Skip to main content

asgi-user-agents

CI Coverage PyPI - Version PyPI - Python Version License Latest Commit

Downloads Downloads/Month Downloads/Week

User Agents integration for ASGI applications. Works with Starlette, FastAPI, Quart, Litestar, Django -- or any other web framework supporting ASGI that exposes the ASGI scope.


Table of Contents

Installation

NOTE: This is alpha software. Please be sure to pin your dependencies.

Latest Release

pip install asgi-user-agents

Development Version

pip install git+https://github.com/hasansezertasan/asgi-user-agents.git

How does it work?

It simply adds a ua attribute to the request scope. This attribute is an instance of the UADetails class which abstracts the UserAgent class from the user-agents package 📄.

Usage

It's pretty simple. Just add the middleware to your ASGI application and access the ua attribute from the request scope.

from asgi_user_agents import UAMiddleware
from asgi_user_agents import UARequest as Request
from fastapi.applications import FastAPI
from starlette.middleware import Middleware
from starlette.responses import JSONResponse, Response


app = FastAPI(middleware=[Middleware(UAMiddleware)])


@app.get("/")
async def index(request: Request) -> Response:
    ua = request.scope["ua"]
    data = {
        "ua_string": ua.ua_string,
        "os": ua.os,
        "os.family": ua.os.family,
        "os.version": ua.os.version,
        "os.version_string": ua.os.version_string,
        "browser": ua.browser,
        "browser.family": ua.ua.browser.family,
        "browser.version": ua.ua.browser.version,
        "browser.version_string": ua.ua.browser.version_string,
        "device": ua.device,
        "device.family": ua.device.family,
        "device.brand": ua.device.brand,
        "device.model": ua.device.model,
        "is_provided": ua.is_provided,
        "is_tablet": ua.is_tablet,
        "is_mobile": ua.is_mobile,
        "is_touch_capable": ua.is_touch_capable,
        "is_pc": ua.is_pc,
        "is_bot": ua.is_bot,
        "is_email_client": ua.is_email_client,
    }
    return JSONResponse(data)

Framework integrations

For Litestar, FastAPI, and Django, optional contrib subpackages provide framework-idiomatic access. The core UAMiddleware still works for any ASGI framework — contrib is additive convenience.

Install with the relevant extra:

pip install asgi-user-agents[litestar]
pip install asgi-user-agents[fastapi]
pip install asgi-user-agents[django]

Litestar

Use UAPlugin to inject ua: UADetails and user_agent: UserAgent into route handlers. No middleware needed.

from litestar import Litestar, get

from asgi_user_agents import UADetails
from asgi_user_agents.contrib.litestar import UAPlugin


@get("/")
async def index(ua: UADetails) -> dict:
    return {"is_bot": ua.is_bot, "browser": ua.browser.family}


app = Litestar(route_handlers=[index], plugins=[UAPlugin()])

If you already registered a dependency named ua or user_agent, UAPlugin will not overwrite it.

FastAPI

Use install_ua(app) plus the prebuilt UADep / UserAgentDep annotated dependencies.

from fastapi import FastAPI

from asgi_user_agents.contrib.fastapi import UADep, install_ua

app = install_ua(FastAPI())


@app.get("/")
async def index(ua: UADep) -> dict:
    return {"is_bot": ua.is_bot, "browser": ua.browser.family}

install_ua is idempotent. The plain dependency functions get_ua and get_user_agent are also exported if you prefer to wire them yourself.

Django

Built on UADetails. Add the app and the middleware:

# settings.py
INSTALLED_APPS = [
    # ...
    "asgi_user_agents.contrib.django",  # enables the template filters
]

MIDDLEWARE = [
    # ...
    "asgi_user_agents.contrib.django.UserAgentMiddleware",
]

The middleware attaches a lazy request.user_agent (a UADetails):

def my_view(request):
    if request.user_agent.is_mobile:
        ...
    browser = request.user_agent.browser.family

…and the same data is available as template filters:

{% load asgi_user_agents %}
{% if request|is_mobile %}Mobile{% elif request|is_pc %}Desktop{% endif %}

Resolution is scope-first: under ASGI, if the core UAMiddleware already ran, request.user_agent reuses the parsed request.scope["ua"] with no re-parse. Under WSGI (no scope) it falls back to parsing request.headers, so the integration works under both deployment models. The middleware is sync- and async-capable.

API Reference

UAMiddleware

An ASGI middleware that sets scope["ua"] to an instance of UADetails (scope refers to the ASGI scope).

app = UAMiddleware(app)

UADetails

A helper that provides shortcuts for accessing User-Agent request header.

ua = UADetails(scope)
  • ua: UserAgent - The UserAgent instance from the user-agents package.
  • ua_string: str - The user agent string.
  • is_provided: bool - True if the user agent string is provided.
  • os: OperatingSystem - The operating system details of the user agent. It's a named tuple with the following fields:
    • family: str - The family of the operating system.
    • version: str - The version of the operating system.
    • version_string: str - The version of the operating system as a string.
  • browser: Browser - The browser details of the user agent. It's a named tuple with the following fields:
    • family: str - The family of the browser.
    • version: str - The version of the browser.
    • version_string: str - The version of the browser as a string.
  • device: Device - The device details of the user agent. It's a named tuple with the following fields:
    • family: str - The family of the device.
    • brand: str - The brand of the device.
    • model: str - The model of the device.
  • is_tablet: bool - True if the request was made by a tablet.
  • is_mobile: bool - True if the request was made by a mobile device.
  • is_touch_capable: bool - True if the request was made by a touch-capable device.
  • is_pc: bool - True if the request was made by a PC.
  • is_bot: bool - True if the request was made by a bot.
  • is_email_client: bool - True if the request was made by an email client.

UARequest

For Starlette-based frameworks, use this instead of the standard starlette.requests.Request so that code editors understand that request.scope["ua"] contains an UADetails instance:

from asgi_user_agents import UARequest as Request

async def home(request: Request):
    reveal_type(request.scope["ua"])  # Revealed type is 'UADetails'

Development

Clone the repository and cd into the project directory:

git clone https://github.com/hasansezertasan/asgi-user-agents
cd asgi-user-agents

Install hatch, you can follow the instructions, or simply run one of the following commands:

mise use hatch
uv tool install hatch
pipx install hatch

The commands below can also be executed using the xc task runner, which combines the usage instructions with the actual commands. Simply run xc, it will popup an interactive menu with all available tasks.

env

Initialize the environment and install the dependencies:

hatch shell

hooks

Initialize pre-commit hooks by running the following command:

pre-commit install

test

Make your changes on a new branch and run the tests:

hatch test -a

types

Make sure that the code is typed, linted, and formatted correctly:

hatch run types:all

co

Stage your changes and commit them:

Inputs: MESSAGE

git add .
git commit -m "$MESSAGE"

pr

Create a pull request and wait for the review 🤓.

gh pr new -B main

Author

Credits

  • This project wouldn't be possible without the user-agents package 🙏.
  • The project structure is inspired by the asgi-htmx 🚀 package and contains some code snippets from it 😅 (even this file).

Analysis

License

asgi-user-agents is distributed under the terms of the MIT license.

Metadata

Release files for asgi-user-agents 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for asgi-user-agents 0.4.0
File Size Uploaded Provenance
asgi_user_agents-0.4.0.tar.gz 80.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for asgi-user-agents 0.4.0
File Interpreter ABI Platform
asgi_user_agents-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 96.3 kB

Release files / asgi_user_agents-0.4.0.tar.gz

Download URL asgi_user_agents-0.4.0.tar.gz
Size 80.0 kB
Tags Source
SHA-256 checksum
How to use checksums
093554979a9c8a126434c77e7465d8d28fa2ade3139add94d47e64a56cb155b4
BLAKE2b-256 checksum
How to use checksums
90c7a905bb2a1cf8ed0e7b4dbd57026e05ac7a9fafa564267336ba747f2d0a30
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / asgi_user_agents-0.4.0-py3-none-any.whl

Download URL asgi_user_agents-0.4.0-py3-none-any.whl
Size 16.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
72dc34f8bea52e417f05000534bd50dd6d35ac44bd0d61a440aef849d7211a5c
BLAKE2b-256 checksum
How to use checksums
cab28ae113d7c6f47f37c6e2fae74b762ae9694551c8578fa6384d09d87475f8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.23 {"installer":{"name":"uv","version":"0.11.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for asgi-user-agents 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page