Metadata-Version: 2.4
Name: asgi-user-agents
Version: 0.3.0
Summary: User Agent integration for ASGI applications.
Project-URL: Documentation, https://github.com/hasansezertasan/asgi-user-agents#readme
Project-URL: Issues, https://github.com/hasansezertasan/asgi-user-agents/issues
Project-URL: Source, https://github.com/hasansezertasan/asgi-user-agents
Author-email: Hasan Sezer Taşan <hasansezertasan@gmail.com>
Maintainer-email: Hasan Sezer Taşan <hasansezertasan@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: asgi,fastapi,litestar,quart,starlette,user-agent
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Web Environment
Classifier: Framework :: AsyncIO
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Internet
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: typing-extensions>=4.0.0
Requires-Dist: user-agents
Provides-Extra: fastapi
Requires-Dist: fastapi<1.0.0,>=0.110.0; extra == 'fastapi'
Provides-Extra: litestar
Requires-Dist: litestar<3.0.0,>=2.0.0; extra == 'litestar'
Description-Content-Type: text/markdown

# asgi-user-agents

[![CI](https://github.com/hasansezertasan/asgi-user-agents/actions/workflows/ci.yml/badge.svg)](https://github.com/hasansezertasan/asgi-user-agents/actions?query=event%3Apush+branch%3Amain+workflow%3ACI)
[![Coverage](https://img.shields.io/codecov/c/github/hasansezertasan/asgi-user-agents)](https://codecov.io/gh/hasansezertasan/asgi-user-agents)
[![PyPI - Version](https://img.shields.io/pypi/v/asgi-user-agents.svg)](https://pypi.org/project/asgi-user-agents)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/asgi-user-agents.svg)](https://pypi.org/project/asgi-user-agents)
[![License](https://img.shields.io/github/license/hasansezertasan/asgi-user-agents.svg)](https://github.com/hasansezertasan/asgi-user-agents/blob/main/LICENSE)
[![Latest Commit](https://img.shields.io/github/last-commit/hasansezertasan/asgi-user-agents)](https://github.com/hasansezertasan/asgi-user-agents)

[![Downloads](https://pepy.tech/badge/asgi-user-agents)](https://pepy.tech/project/asgi-user-agents)
[![Downloads/Month](https://pepy.tech/badge/asgi-user-agents/month)](https://pepy.tech/project/asgi-user-agents)
[![Downloads/Week](https://pepy.tech/badge/asgi-user-agents/week)](https://pepy.tech/project/asgi-user-agents)

[User Agents][python-user-agents] integration for [ASGI](https://asgi.readthedocs.io/en/latest/) applications. Works with Starlette, FastAPI, Quart, Litestar -- or any other web framework supporting ASGI that exposes the ASGI `scope`.

-----

## Table of Contents

<!-- toc -->

- [Installation](#installation)
- [How does it work?](#how-does-it-work)
- [Usage](#usage)
- [Framework integrations](#framework-integrations)
  - [Litestar](#litestar)
  - [FastAPI](#fastapi)
- [API Reference](#api-reference)
  - [`UAMiddleware`](#uamiddleware)
  - [`UADetails`](#uadetails)
  - [`UARequest`](#uarequest)
- [Development](#development)
  - [`env`](#env)
  - [`hooks`](#hooks)
  - [`test`](#test)
  - [`types`](#types)
  - [`co`](#co)
  - [`pr`](#pr)
- [Author](#author)
- [Credits](#credits)
- [Analysis](#analysis)
- [License](#license)

<!-- tocstop -->

## Installation

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

> Latest Release

```bash
pip install asgi-user-agents
```

> Development Version

```bash
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.

```python
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 and FastAPI, 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:

```bash
pip install asgi-user-agents[litestar]
pip install asgi-user-agents[fastapi]
```

### Litestar

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

```python
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.

```python
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.

## API Reference

### `UAMiddleware`

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

```python
app = UAMiddleware(app)
```

### `UADetails`

A helper that provides shortcuts for accessing [`User-Agent` request header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent).

```python
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:

```python
from asgi_user_agents import UARequest as Request

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

<!-- xc-heading -->
## Development

Clone the repository and cd into the project directory:

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

Install hatch, you can follow the [instructions](https://hatch.pypa.io/latest/install/), or simply run one of the following commands:

```bash
mise use hatch
```

```sh
uv tool install hatch
```

```bash
pipx install hatch
```

The commands below can also be executed using the [xc task runner](https://xcfile.dev/), 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:

```bash
hatch shell
```

### `hooks`

Initialize pre-commit hooks by running the following command:

```bash
pre-commit install
```

### `test`

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

```bash
hatch test -a
```

### `types`

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

```bash
hatch run types:all
```

### `co`

Stage your changes and commit them:

Inputs: MESSAGE

```bash
git add .
git commit -m "$MESSAGE"
```

### `pr`

Create a pull request and wait for the review 🤓.

```sh
gh pr new -B main
```

## Author

- [Hasan Sezer Taşan](https://www.github.com/hasansezertasan), It's me 👋.

## Credits

- This project wouldn't be possible without the [user-agents][python-user-agents] package 🙏.
- The project structure is inspired by the [asgi-htmx](https://github.com/florimondmanca/asgi-htmx) 🚀 package and contains some code snippets from it 😅 (even this file).

## Analysis

- [Snyk Python Package Health Analysis](https://snyk.io/advisor/python/asgi-user-agents)
- [Libraries.io - PyPI](https://libraries.io/pypi/asgi-user-agents)
- [Safety DB](https://data.safetycli.com/packages/pypi/asgi-user-agents)
- [PePy Download Stats](https://www.pepy.tech/projects/asgi-user-agents)
- [PyPI Download Stats](https://pypistats.org/packages/asgi-user-agents)
- [Pip Trends Download Stats](https://piptrends.com/package/asgi-user-agents)

## License

`asgi-user-agents` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.

<!-- Links -->
[python-user-agents]: https://github.com/selwin/python-user-agents
