etiquette


Nameetiquette JSON
Version 0.0.2 PyPI version JSON
download
home_pageNone
SummarySemaphored background queue for Asynchronous Server Gateway Interface (ASGI) frameworks
upload_time2025-07-29 07:23:40
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords asgi asynchronous asyncio etiquette extension fastapi litestar plugin queue semaphore starlette uvloop
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Etiquette

[![Package version](https://img.shields.io/pypi/v/etiquette)](https://pypi.org/project/etiquette)
[![Format](https://img.shields.io/pypi/format/etiquette)](https://pypi.org/project/etiquette)
[![Python version](https://img.shields.io/pypi/pyversions/etiquette)](https://pypi.org/project/etiquette)
[![License](https://img.shields.io/pypi/l/etiquette)](https://pypi.org/project/etiquette)
[![Top](https://img.shields.io/github/languages/top/aekasitt/etiquette)](.)
[![Languages](https://img.shields.io/github/languages/count/aekasitt/etiquette)](.)
[![Size](https://img.shields.io/github/repo-size/aekasitt/etiquette)](.)
[![Last commit](https://img.shields.io/github/last-commit/aekasitt/etiquette/master)](.)
[![Documentation](https://img.shields.io/badge/pdoc-555?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAkUExURUdwTJHtkZDtkI/vj2rOYFXBQn3deZDukDuzAHDTaFrES4HhfmcEZqoAAAAHdFJOUwCAv0Df788Wv3t3AAACCElEQVQ4y42UsXKcMBCGuVC4Pc6FJuPGRzLDnK/xnAsKP0IeIU+QLhyFEHoAVlJlNxJHlwbBVY4LyeeXs4BLCnQzCTOAZj+0K+3/iyD4zwvhIIiiey/+iVRB8LnJPJBq6QA8eeBRuI8R8UFKTYbc7QGqQRIAOa++KKAD6LW4nQMFwgIB7gMlLaPQz8GVMlhxjD2AoKVcSeKl2jCAyq2K/5yBsiINRqSx+QwUcmhgFNmXOWivzO2CZsWTlyrl2U69qPmMjUi0pBqreY0bvi8BGGHzVaVKJnWHi2o5bwlhOI4JGF9yzeuTPua+5qoHEHvfDAvF398qPz70vboRl4DV8vESCC3HqSfsAAotkNf0sY1HhnR+AZTQruzrBXAHLLe/h9HDtIRwFa+v3XvL4bX8NXpvrLTDb31tImdezvO7dgipAYTOmhyYXAYptOvNuPVR99Ry24BieJlqEX8bdzhUWlBo3JQTMExZ//X7BNplmCgN4t3FFdecnUHJcHLi6ogfEurCujdlMwK38AMItd/FETp0TD2vi6nvW3AHYkje1VmcKJ6humyn46hB1ebkqPwhsYkJs8+jmLYp2Z5AgwkfnlRLPR7BsBTYUH3M7pEVRAMcT2dXbbWLM6d/SEFi6pKayVU75z2Oh3FKoKt7Lv/IkRw6M4mJXKrerP8aII6vz8MVxvsv//gNfQBIOvQNeKr0GQAAAABJRU5ErkJggg==)](https://aekasitt.github.io/etiquette)

[![Etiquette Banner](./static/etiquette-banner.svg)](https://github.com/aekasitt/etiquette/blob/master/static/etiquette-banner.svg)

Plugin designed for `dependency injection` pattern offered by following ASGI frameworks

* [FastAPI](https://fastapi.tiangolo.com)
  framework, high performance, easy to learn, fast to code, ready for production
* [Litestar](https://litestar.dev)
  \- build performant APIs with Litestar; powerful, lightweight & flexible ASGI framework

### Prerequisites

* [python](https://www.python.org) 3.9 and above - High-level general-purpose programming language
* [pip](https://pypi.org/project/pip/) - The PyPA recommended tool for installing Python packages.

### Getting started

You can use `etiquette` simply by installing via `pip` on your terminal emulator of choice.

```sh
pip install etiquette
```

Then you can integrate this library in your ASGI codebase using the lifespan manager
as such:

```python
from asyncio import sleep
from contextlib import asynccontextmanager
from etiquette import Decorum, Etiquette
from fastapi import Depends, FastAPI
from typing import Annotated

@asynccontextmanager
async def lifespan(app):
  Etiquette.initiate(max_concurrent_tasks=16)
  yield
  await Etiquette.release()

app = FastAPI(lifespan=lifespan)

@app.get("/sleep")
async def add_sleep_task_to_queue(decorum: Annotated[Decorum, Depends(Decorum)]) -> str:
  async def sleep_3() -> None:
    await sleep(3)
    print("task done")

  await decorum.add_task(sleep_3)
  return "OK"
```

The above is an example written in FastAPI framework, for Litestar see below:

```python
from asyncio import sleep
from contextlib import asynccontextmanager
from etiquette import Decorum, Etiquette
from litestar import Litestar, get
from litestar.di import Provide
from typing import AsyncGenerator

@asynccontextmanager
async def lifespan(app):
  Etiquette.initiate(max_concurrent_tasks=16)
  yield
  await Etiquette.release()

@get("/sleep", dependencies={"decorum": Provide(Decorum, sync_to_thread=True)})
async def add_sleep_task_to_queue(decorum: Decorum) -> str:
  async def sleep_3() -> None:
    await sleep(3)
    print("task done")

  await decorum.add_task(sleep_3)
  return "OK"

app = Litestar(lifespan=[lifespan], route_handlers=[add_sleep_task_to_queue])
```

## Contributions

### Project structure

```
etiquette/
│
├── src/
│   └── etiquette/
│       ├── __init__.py       # Entrypoint to the etiquette package
│       ├── _types.py         # Defines internal dataclass object `TaskData`
│       ├── core.py           # Defines `Etiquette` core where shared class vars are initiated
│       └── decorum.py        # Defines `Decorum` class utilized by Dependency Injection
│
├── static/
│   ├── etiquette.svg         # Graphics representing project
│   ├── etiquette-banner.svg  # Project banner displayed on README.md
│   └── etiquette-social.svg  # Social media preview banner
│
├── LICENSE                   # Details of MIT License
└── README.md                 # Descriptions and roadmap
```

Notable exemptions: `dotfiles`, `examples` and, `tests`

### Roadmap

* Validates task failing will retry a pre-determined set of times
* Add [testcontainers](https://github.com/testcontainers/testcontainers-python)
  and attempt complex I/O bound tasks
* Experiment with Server-Side Events
* Add an [project.optional-dependencies] for `standard` with `uvloop`

### Prerequisites

* [git](https://git-scm.com/) - --fast-version-control
* [python](https://www.python.org) 3.9 and above - High-level general-purpose programming language
* [uv](https://docs.astral.sh/uv) - Extremely fast Python package & project manager, written in Rust

The following guide walks through setting up your local working environment using `git`
as distributed version control system and `uv` as Python package and version manager.
If you do not have `git` installed, run the following command.

<details>
  <summary> Install using Homebrew (Darwin) </summary>
  
  ```bash
  brew install git
  ```
</details>

<details>
  <summary> Install via binary installer (Linux) </summary>
  
  * Debian-based package management
  ```bash
  sudo apt install git-all
  ```

  * Fedora-based package management
  ```bash
  sudo dnf install git-all
  ```
</details>

If you do not have `uv` installed, run the following command.

<details>
  <summary> Install using Homebrew (Darwin) </summary>

  ```bash
  brew install uv
  ```
</details>

<details>
  <summary> Install using standalone installer (Darwin and Linux) </summary>

  ```bash
  curl -LsSf https://astral.sh/uv/install.sh | sh
  ```
</details>

Once you have `git` distributed version control system installed, you can
clone the current repository and  install any version of Python above version
3.9 for this project. The following commands help you set up and activate a
Python virtual environment where `uv` can download project dependencies from the `PyPI`
open-sourced registry defined under `pyproject.toml` file.

<details>
  <summary> Set up environment and synchronize project dependencies </summary>

  ```bash
  git clone git@github.com:aekasitt/etiquette.git
  cd etiquette
  uv venv --python 3.13.5
  source .venv/bin/activate
  uv sync --dev
  ```
</details>

### Run examples

We need to install a few extra dependencies to run attached examples found
under the `examples` directory on root. First run the following command:

```sh
uv sync --dev --group examples
```

<details>
  <summary> Sample installation output for examples' dependencies </summary>

  ```sh
  $ uv sync --dev --group examples
  > Resolved 48 packages in 1ms
  > Installed 29 packages in 58ms
  >  + annotated-types==0.7.0
  >  + anyio==4.9.0
  >  + certifi==2025.7.14
  >  + click==8.2.1
  >  + faker==37.4.2
  >  + fastapi==0.116.1
  >  + h11==0.16.0
  >  + httpcore==1.0.9
  >  + httpx==0.28.1
  >  + idna==3.10
  >  + litestar==2.16.0
  >  + litestar-htmx==0.5.0
  >  + markdown-it-py==3.0.0
  >  + mdurl==0.1.2
  >  + msgspec==0.19.0
  >  + multidict==6.6.3
  >  + multipart==1.2.1
  >  + polyfactory==2.22.1
  >  + pydantic==2.11.7
  >  + pydantic-core==2.33.2
  >  + pygments==2.19.2
  >  + pyyaml==6.0.2
  >  + rich==14.0.0
  >  + rich-click==1.8.9
  >  + sniffio==1.3.1
  >  + starlette==0.47.2
  >  + typing-inspection==0.4.1
  >  + tzdata==2025.2
  >  + uvicorn==0.35.0
  ```
</details>

Now you can run all four attached examples as such using the `uvicorn` command installed
above: The four examples include:

1. FastAPI using Decorum to add an AsyncIO sleeping task
    ```sh
    uvicorn examples.fastapi_sleeper:app --port 8000 --reload
    ```

2. FastAPI using Decorum to interact with a thread-safe [AtomicCounter](https://gist.github.com/benhoyt/8c8a8d62debe8e5aa5340373f9c509c7)
    ```sh
    uvicorn examples.fastapi_counter:app --port 8000 --reload
    ```
3. Litestar using Decorum to add an AsyncIO sleeping task
    ```sh
    uvicorn examples.litestar_sleeper:app --port 8000 --reload
    ```

4. Litestar using Decorum to interact with a thread-safe [AtomicCounter](https://gist.github.com/benhoyt/8c8a8d62debe8e5aa5340373f9c509c7)
    ```sh
    uvicorn examples.litestar_counter:app --port 8000 --reload
    ```

### Tests

This project uses `pytest` to run automated tests. Install the dependencies with:

```sh
uv sync --dev --group tests
```

<details>
  <summary> Sample installation output for development and test dependencies </summary>

  ```sh
  $ uv sync --dev --group tests
  > Resolved 48 packages in 9ms
  > Installed 40 packages in 104ms
  >  + annotated-types==0.7.0
  >  + anyio==4.9.0
  >  + certifi==2025.7.14
  >  + click==8.2.1
  >  + etiquette==0.0.1 (from file:///.../.../.../etiquette)
  >  + faker==37.4.2
  >  + fastapi==0.116.1
  >  + h11==0.16.0
  >  + httpcore==1.0.9
  >  + httpx==0.28.1
  >  + idna==3.10
  >  + iniconfig==2.1.0
  >  + litestar==2.16.0
  >  + litestar-htmx==0.5.0
  >  + markdown-it-py==3.0.0
  >  + mdurl==0.1.2
  >  + msgspec==0.19.0
  >  + multidict==6.6.3
  >  + multipart==1.2.1
  >  + mypy==1.17.0
  >  + mypy-extensions==1.1.0
  >  + packaging==25.0
  >  + pathspec==0.12.1
  >  + pluggy==1.6.0
  >  + polyfactory==2.22.1
  >  + pydantic==2.11.7
  >  + pydantic-core==2.33.2
  >  + pygments==2.19.2
  >  + pytest==8.4.1
  >  + pytest-asyncio==1.1.0
  >  + pytest-modern==0.7.3
  >  + pyyaml==6.0.2
  >  + rich==14.0.0
  >  + rich-click==1.8.9
  >  + ruff==0.12.4
  >  + sniffio==1.3.1
  >  + starlette==0.47.2
  >  + typing-extensions==4.14.1
  >  + typing-inspection==0.4.1
  >  + tzdata==2025.2
  ```
</details>

Then, run the tests with 

```sh
pytest
```

<details>
  <summary> Sample outputs of successful test running </summary>

  ```sh
  $ pytest
  > ╭─────────────────────────────────── test session starts ──────────────────────────────────╮
  > │ platform darwin pytest 8.4.1 python 3.13.5                                               │
  > │ plugins anyio-4.9.0, modern-0.7.3, asyncio-1.1.0, Faker-37.4.2                           │
  > │ root /Users/mackasitt/workspaces/pypi-etiquette                                          │
  > │ configfile pyproject.toml                                                                │
  > ╰──────────────────────────────────────────────────────────────────────────────────────────╯
  > Collected 14 items (14 selected)
  >       PASS [   1.293ms] tests/fastapi/cancelled.py::test_sure_fail_counter
  >       PASS [   27.17ms] tests/fastapi/race.py::test_safe_counter
  >       PASS [   8.912ms] tests/fastapi/race.py::test_unsafe_counter
  >       PASS [   27.29ms] tests/fastapi/splat.py::test_safe_counter_using_path_parameters
  >       PASS [   26.56ms] tests/fastapi/splat.py::test_safe_counter_using_query_parameters
  >       PASS [   8.681ms] tests/fastapi/splat.py::test_unsafe_counter_using_path_parameters
  >       PASS [   10.18ms] tests/fastapi/splat.py::test_unsafe_counter_using_query_parameters
  >       PASS [   1.919ms] tests/litestar/cancelled.py::test_sure_fail_counter
  >       PASS [   28.65ms] tests/litestar/race.py::test_safe_counter
  >       PASS [   10.62ms] tests/litestar/race.py::test_unsafe_counter
  >       PASS [   29.04ms] tests/litestar/splat.py::test_safe_counter_using_path_parametes
  >       PASS [   31.94ms] tests/litestar/splat.py::test_safe_counter_using_query_parameters
  >       PASS [   10.45ms] tests/litestar/splat.py::test_unsafe_counter_using_path_parameters
  >       PASS [   11.25ms] tests/litestar/splat.py::test_unsafe_counter_using_query_parameters
  > ──────────
  >    Summary [   234.0ms] 14 tests run: 14 passed
  ```
</details>

Refer to the [pytest documentation](https://docs.pytest.org/) for more advanced options.

## Acknowledgements

1. [หมึกหมด - Muekmod](https://www.f0nt.com/release/sov-muekmod/)
  typeface by [uvSOV - Worawut Thanawatanawanich](https://fb.com/worawut.thanawatanawanich)

## License

This project is licensed under the terms of the MIT license.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "etiquette",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "Sitt Guruvanich <aekazitt+github@gmail.com>",
    "keywords": "asgi, asynchronous, asyncio, etiquette, extension, fastapi, litestar, plugin, queue, semaphore, starlette, uvloop",
    "author": null,
    "author_email": "Sitt Guruvanich <aekazitt+github@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/35/4d/1f91ff346d9b495f50cbe3d61c7a4e2f229160f14da0f1015b7287ef9532/etiquette-0.0.2.tar.gz",
    "platform": null,
    "description": "# Etiquette\n\n[![Package version](https://img.shields.io/pypi/v/etiquette)](https://pypi.org/project/etiquette)\n[![Format](https://img.shields.io/pypi/format/etiquette)](https://pypi.org/project/etiquette)\n[![Python version](https://img.shields.io/pypi/pyversions/etiquette)](https://pypi.org/project/etiquette)\n[![License](https://img.shields.io/pypi/l/etiquette)](https://pypi.org/project/etiquette)\n[![Top](https://img.shields.io/github/languages/top/aekasitt/etiquette)](.)\n[![Languages](https://img.shields.io/github/languages/count/aekasitt/etiquette)](.)\n[![Size](https://img.shields.io/github/repo-size/aekasitt/etiquette)](.)\n[![Last commit](https://img.shields.io/github/last-commit/aekasitt/etiquette/master)](.)\n[![Documentation](https://img.shields.io/badge/pdoc-555?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAkUExURUdwTJHtkZDtkI/vj2rOYFXBQn3deZDukDuzAHDTaFrES4HhfmcEZqoAAAAHdFJOUwCAv0Df788Wv3t3AAACCElEQVQ4y42UsXKcMBCGuVC4Pc6FJuPGRzLDnK/xnAsKP0IeIU+QLhyFEHoAVlJlNxJHlwbBVY4LyeeXs4BLCnQzCTOAZj+0K+3/iyD4zwvhIIiiey/+iVRB8LnJPJBq6QA8eeBRuI8R8UFKTYbc7QGqQRIAOa++KKAD6LW4nQMFwgIB7gMlLaPQz8GVMlhxjD2AoKVcSeKl2jCAyq2K/5yBsiINRqSx+QwUcmhgFNmXOWivzO2CZsWTlyrl2U69qPmMjUi0pBqreY0bvi8BGGHzVaVKJnWHi2o5bwlhOI4JGF9yzeuTPua+5qoHEHvfDAvF398qPz70vboRl4DV8vESCC3HqSfsAAotkNf0sY1HhnR+AZTQruzrBXAHLLe/h9HDtIRwFa+v3XvL4bX8NXpvrLTDb31tImdezvO7dgipAYTOmhyYXAYptOvNuPVR99Ry24BieJlqEX8bdzhUWlBo3JQTMExZ//X7BNplmCgN4t3FFdecnUHJcHLi6ogfEurCujdlMwK38AMItd/FETp0TD2vi6nvW3AHYkje1VmcKJ6humyn46hB1ebkqPwhsYkJs8+jmLYp2Z5AgwkfnlRLPR7BsBTYUH3M7pEVRAMcT2dXbbWLM6d/SEFi6pKayVU75z2Oh3FKoKt7Lv/IkRw6M4mJXKrerP8aII6vz8MVxvsv//gNfQBIOvQNeKr0GQAAAABJRU5ErkJggg==)](https://aekasitt.github.io/etiquette)\n\n[![Etiquette Banner](./static/etiquette-banner.svg)](https://github.com/aekasitt/etiquette/blob/master/static/etiquette-banner.svg)\n\nPlugin designed for `dependency injection` pattern offered by following ASGI frameworks\n\n* [FastAPI](https://fastapi.tiangolo.com)\n  framework, high performance, easy to learn, fast to code, ready for production\n* [Litestar](https://litestar.dev)\n  \\- build performant APIs with Litestar; powerful, lightweight & flexible ASGI framework\n\n### Prerequisites\n\n* [python](https://www.python.org) 3.9 and above - High-level general-purpose programming language\n* [pip](https://pypi.org/project/pip/) - The PyPA recommended tool for installing Python packages.\n\n### Getting started\n\nYou can use `etiquette` simply by installing via `pip` on your terminal emulator of choice.\n\n```sh\npip install etiquette\n```\n\nThen you can integrate this library in your ASGI codebase using the lifespan manager\nas such:\n\n```python\nfrom asyncio import sleep\nfrom contextlib import asynccontextmanager\nfrom etiquette import Decorum, Etiquette\nfrom fastapi import Depends, FastAPI\nfrom typing import Annotated\n\n@asynccontextmanager\nasync def lifespan(app):\n  Etiquette.initiate(max_concurrent_tasks=16)\n  yield\n  await Etiquette.release()\n\napp = FastAPI(lifespan=lifespan)\n\n@app.get(\"/sleep\")\nasync def add_sleep_task_to_queue(decorum: Annotated[Decorum, Depends(Decorum)]) -> str:\n  async def sleep_3() -> None:\n    await sleep(3)\n    print(\"task done\")\n\n  await decorum.add_task(sleep_3)\n  return \"OK\"\n```\n\nThe above is an example written in FastAPI framework, for Litestar see below:\n\n```python\nfrom asyncio import sleep\nfrom contextlib import asynccontextmanager\nfrom etiquette import Decorum, Etiquette\nfrom litestar import Litestar, get\nfrom litestar.di import Provide\nfrom typing import AsyncGenerator\n\n@asynccontextmanager\nasync def lifespan(app):\n  Etiquette.initiate(max_concurrent_tasks=16)\n  yield\n  await Etiquette.release()\n\n@get(\"/sleep\", dependencies={\"decorum\": Provide(Decorum, sync_to_thread=True)})\nasync def add_sleep_task_to_queue(decorum: Decorum) -> str:\n  async def sleep_3() -> None:\n    await sleep(3)\n    print(\"task done\")\n\n  await decorum.add_task(sleep_3)\n  return \"OK\"\n\napp = Litestar(lifespan=[lifespan], route_handlers=[add_sleep_task_to_queue])\n```\n\n## Contributions\n\n### Project structure\n\n```\netiquette/\n\u2502\n\u251c\u2500\u2500 src/\n\u2502   \u2514\u2500\u2500 etiquette/\n\u2502       \u251c\u2500\u2500 __init__.py       # Entrypoint to the etiquette package\n\u2502       \u251c\u2500\u2500 _types.py         # Defines internal dataclass object `TaskData`\n\u2502       \u251c\u2500\u2500 core.py           # Defines `Etiquette` core where shared class vars are initiated\n\u2502       \u2514\u2500\u2500 decorum.py        # Defines `Decorum` class utilized by Dependency Injection\n\u2502\n\u251c\u2500\u2500 static/\n\u2502   \u251c\u2500\u2500 etiquette.svg         # Graphics representing project\n\u2502   \u251c\u2500\u2500 etiquette-banner.svg  # Project banner displayed on README.md\n\u2502   \u2514\u2500\u2500 etiquette-social.svg  # Social media preview banner\n\u2502\n\u251c\u2500\u2500 LICENSE                   # Details of MIT License\n\u2514\u2500\u2500 README.md                 # Descriptions and roadmap\n```\n\nNotable exemptions: `dotfiles`, `examples` and, `tests`\n\n### Roadmap\n\n* Validates task failing will retry a pre-determined set of times\n* Add [testcontainers](https://github.com/testcontainers/testcontainers-python)\n  and attempt complex I/O bound tasks\n* Experiment with Server-Side Events\n* Add an [project.optional-dependencies] for `standard` with `uvloop`\n\n### Prerequisites\n\n* [git](https://git-scm.com/) - --fast-version-control\n* [python](https://www.python.org) 3.9 and above - High-level general-purpose programming language\n* [uv](https://docs.astral.sh/uv) - Extremely fast Python package & project manager, written in Rust\n\nThe following guide walks through setting up your local working environment using `git`\nas distributed version control system and `uv` as Python package and version manager.\nIf you do not have `git` installed, run the following command.\n\n<details>\n  <summary> Install using Homebrew (Darwin) </summary>\n  \n  ```bash\n  brew install git\n  ```\n</details>\n\n<details>\n  <summary> Install via binary installer (Linux) </summary>\n  \n  * Debian-based package management\n  ```bash\n  sudo apt install git-all\n  ```\n\n  * Fedora-based package management\n  ```bash\n  sudo dnf install git-all\n  ```\n</details>\n\nIf you do not have `uv` installed, run the following command.\n\n<details>\n  <summary> Install using Homebrew (Darwin) </summary>\n\n  ```bash\n  brew install uv\n  ```\n</details>\n\n<details>\n  <summary> Install using standalone installer (Darwin and Linux) </summary>\n\n  ```bash\n  curl -LsSf https://astral.sh/uv/install.sh | sh\n  ```\n</details>\n\nOnce you have `git` distributed version control system installed, you can\nclone the current repository and  install any version of Python above version\n3.9 for this project. The following commands help you set up and activate a\nPython virtual environment where `uv` can download project dependencies from the `PyPI`\nopen-sourced registry defined under `pyproject.toml` file.\n\n<details>\n  <summary> Set up environment and synchronize project dependencies </summary>\n\n  ```bash\n  git clone git@github.com:aekasitt/etiquette.git\n  cd etiquette\n  uv venv --python 3.13.5\n  source .venv/bin/activate\n  uv sync --dev\n  ```\n</details>\n\n### Run examples\n\nWe need to install a few extra dependencies to run attached examples found\nunder the `examples` directory on root. First run the following command:\n\n```sh\nuv sync --dev --group examples\n```\n\n<details>\n  <summary> Sample installation output for examples' dependencies </summary>\n\n  ```sh\n  $ uv sync --dev --group examples\n  > Resolved 48 packages in 1ms\n  > Installed 29 packages in 58ms\n  >  + annotated-types==0.7.0\n  >  + anyio==4.9.0\n  >  + certifi==2025.7.14\n  >  + click==8.2.1\n  >  + faker==37.4.2\n  >  + fastapi==0.116.1\n  >  + h11==0.16.0\n  >  + httpcore==1.0.9\n  >  + httpx==0.28.1\n  >  + idna==3.10\n  >  + litestar==2.16.0\n  >  + litestar-htmx==0.5.0\n  >  + markdown-it-py==3.0.0\n  >  + mdurl==0.1.2\n  >  + msgspec==0.19.0\n  >  + multidict==6.6.3\n  >  + multipart==1.2.1\n  >  + polyfactory==2.22.1\n  >  + pydantic==2.11.7\n  >  + pydantic-core==2.33.2\n  >  + pygments==2.19.2\n  >  + pyyaml==6.0.2\n  >  + rich==14.0.0\n  >  + rich-click==1.8.9\n  >  + sniffio==1.3.1\n  >  + starlette==0.47.2\n  >  + typing-inspection==0.4.1\n  >  + tzdata==2025.2\n  >  + uvicorn==0.35.0\n  ```\n</details>\n\nNow you can run all four attached examples as such using the `uvicorn` command installed\nabove: The four examples include:\n\n1. FastAPI using Decorum to add an AsyncIO sleeping task\n    ```sh\n    uvicorn examples.fastapi_sleeper:app --port 8000 --reload\n    ```\n\n2. FastAPI using Decorum to interact with a thread-safe [AtomicCounter](https://gist.github.com/benhoyt/8c8a8d62debe8e5aa5340373f9c509c7)\n    ```sh\n    uvicorn examples.fastapi_counter:app --port 8000 --reload\n    ```\n3. Litestar using Decorum to add an AsyncIO sleeping task\n    ```sh\n    uvicorn examples.litestar_sleeper:app --port 8000 --reload\n    ```\n\n4. Litestar using Decorum to interact with a thread-safe [AtomicCounter](https://gist.github.com/benhoyt/8c8a8d62debe8e5aa5340373f9c509c7)\n    ```sh\n    uvicorn examples.litestar_counter:app --port 8000 --reload\n    ```\n\n### Tests\n\nThis project uses `pytest` to run automated tests. Install the dependencies with:\n\n```sh\nuv sync --dev --group tests\n```\n\n<details>\n  <summary> Sample installation output for development and test dependencies </summary>\n\n  ```sh\n  $ uv sync --dev --group tests\n  > Resolved 48 packages in 9ms\n  > Installed 40 packages in 104ms\n  >  + annotated-types==0.7.0\n  >  + anyio==4.9.0\n  >  + certifi==2025.7.14\n  >  + click==8.2.1\n  >  + etiquette==0.0.1 (from file:///.../.../.../etiquette)\n  >  + faker==37.4.2\n  >  + fastapi==0.116.1\n  >  + h11==0.16.0\n  >  + httpcore==1.0.9\n  >  + httpx==0.28.1\n  >  + idna==3.10\n  >  + iniconfig==2.1.0\n  >  + litestar==2.16.0\n  >  + litestar-htmx==0.5.0\n  >  + markdown-it-py==3.0.0\n  >  + mdurl==0.1.2\n  >  + msgspec==0.19.0\n  >  + multidict==6.6.3\n  >  + multipart==1.2.1\n  >  + mypy==1.17.0\n  >  + mypy-extensions==1.1.0\n  >  + packaging==25.0\n  >  + pathspec==0.12.1\n  >  + pluggy==1.6.0\n  >  + polyfactory==2.22.1\n  >  + pydantic==2.11.7\n  >  + pydantic-core==2.33.2\n  >  + pygments==2.19.2\n  >  + pytest==8.4.1\n  >  + pytest-asyncio==1.1.0\n  >  + pytest-modern==0.7.3\n  >  + pyyaml==6.0.2\n  >  + rich==14.0.0\n  >  + rich-click==1.8.9\n  >  + ruff==0.12.4\n  >  + sniffio==1.3.1\n  >  + starlette==0.47.2\n  >  + typing-extensions==4.14.1\n  >  + typing-inspection==0.4.1\n  >  + tzdata==2025.2\n  ```\n</details>\n\nThen, run the tests with \n\n```sh\npytest\n```\n\n<details>\n  <summary> Sample outputs of successful test running </summary>\n\n  ```sh\n  $ pytest\n  > \u256d\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 test session starts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256e\n  > \u2502 platform darwin pytest 8.4.1 python 3.13.5                                               \u2502\n  > \u2502 plugins anyio-4.9.0, modern-0.7.3, asyncio-1.1.0, Faker-37.4.2                           \u2502\n  > \u2502 root /Users/mackasitt/workspaces/pypi-etiquette                                          \u2502\n  > \u2502 configfile pyproject.toml                                                                \u2502\n  > \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256f\n  > Collected 14 items (14 selected)\n  >       PASS [   1.293ms] tests/fastapi/cancelled.py::test_sure_fail_counter\n  >       PASS [   27.17ms] tests/fastapi/race.py::test_safe_counter\n  >       PASS [   8.912ms] tests/fastapi/race.py::test_unsafe_counter\n  >       PASS [   27.29ms] tests/fastapi/splat.py::test_safe_counter_using_path_parameters\n  >       PASS [   26.56ms] tests/fastapi/splat.py::test_safe_counter_using_query_parameters\n  >       PASS [   8.681ms] tests/fastapi/splat.py::test_unsafe_counter_using_path_parameters\n  >       PASS [   10.18ms] tests/fastapi/splat.py::test_unsafe_counter_using_query_parameters\n  >       PASS [   1.919ms] tests/litestar/cancelled.py::test_sure_fail_counter\n  >       PASS [   28.65ms] tests/litestar/race.py::test_safe_counter\n  >       PASS [   10.62ms] tests/litestar/race.py::test_unsafe_counter\n  >       PASS [   29.04ms] tests/litestar/splat.py::test_safe_counter_using_path_parametes\n  >       PASS [   31.94ms] tests/litestar/splat.py::test_safe_counter_using_query_parameters\n  >       PASS [   10.45ms] tests/litestar/splat.py::test_unsafe_counter_using_path_parameters\n  >       PASS [   11.25ms] tests/litestar/splat.py::test_unsafe_counter_using_query_parameters\n  > \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  >    Summary [   234.0ms] 14 tests run: 14 passed\n  ```\n</details>\n\nRefer to the [pytest documentation](https://docs.pytest.org/) for more advanced options.\n\n## Acknowledgements\n\n1. [\u0e2b\u0e21\u0e36\u0e01\u0e2b\u0e21\u0e14 - Muekmod](https://www.f0nt.com/release/sov-muekmod/)\n  typeface by [uvSOV - Worawut Thanawatanawanich](https://fb.com/worawut.thanawatanawanich)\n\n## License\n\nThis project is licensed under the terms of the MIT license.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Semaphored background queue for Asynchronous Server Gateway Interface (ASGI) frameworks",
    "version": "0.0.2",
    "project_urls": null,
    "split_keywords": [
        "asgi",
        " asynchronous",
        " asyncio",
        " etiquette",
        " extension",
        " fastapi",
        " litestar",
        " plugin",
        " queue",
        " semaphore",
        " starlette",
        " uvloop"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "73f7441786aeb8fbb0a78c8c816909d0bac2d6af977eb6591b3e79e2d3f71abd",
                "md5": "f1e0e69059071f43039da5b54ded2abc",
                "sha256": "331b0657b5d52fd6b2e376bb4fd0e10d6864a5b5350d1b697cebbebb3daaff17"
            },
            "downloads": -1,
            "filename": "etiquette-0.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f1e0e69059071f43039da5b54ded2abc",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 10158,
            "upload_time": "2025-07-29T07:23:37",
            "upload_time_iso_8601": "2025-07-29T07:23:37.784024Z",
            "url": "https://files.pythonhosted.org/packages/73/f7/441786aeb8fbb0a78c8c816909d0bac2d6af977eb6591b3e79e2d3f71abd/etiquette-0.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "354d1f91ff346d9b495f50cbe3d61c7a4e2f229160f14da0f1015b7287ef9532",
                "md5": "50561d20b73904e988324f2a035791b1",
                "sha256": "bf5b16bfce244095e96d055fb1c39375b67428be10f426ac819c05052401d0b2"
            },
            "downloads": -1,
            "filename": "etiquette-0.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "50561d20b73904e988324f2a035791b1",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 738608,
            "upload_time": "2025-07-29T07:23:40",
            "upload_time_iso_8601": "2025-07-29T07:23:40.907571Z",
            "url": "https://files.pythonhosted.org/packages/35/4d/1f91ff346d9b495f50cbe3d61c7a4e2f229160f14da0f1015b7287ef9532/etiquette-0.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-29 07:23:40",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "etiquette"
}
        
Elapsed time: 0.46807s