Skip to main content

nodrill

CI Coverage PyPI Python

nodrill gives a call tree a shared, scoped context. Values set in a provider block are visible to any function below it through use(), without being passed through the signatures in between. It is built on contextvars, so lookups are thread-safe and asyncio-task-safe, and it has no dependencies.

from dataclasses import dataclass

from nodrill import provider, use


@dataclass
class RequestScope:
    user_id: int
    db: str


def handle_request():
    with provider(RequestScope(user_id=42, db="postgres://...")):
        render_page()


def render_page():
    return render_sidebar()  # knows nothing about RequestScope


def render_sidebar():
    scope = use(RequestScope)  # inferred as RequestScope
    return f"{scope.user_id} @ {scope.db}"

String names work too, when a typed key is more than the case needs.

with provider("app", db=engine) as ctx:
    ctx.user_id = 42
    handle()  # any callee reads use("app").db

What you get

  • Typed keys: use(Config) is inferred as Config, under both mypy and pyright.
  • String namespaces for the values that do not deserve a class, as above.
  • @inject to declare the dependency in the signature and still pass it explicitly in a test.
  • set_default(Config, factory) for code that has to run outside any provider.
  • Threads and asyncio: tasks inherit the context, wrap and Executor carry it into threads.
  • frozen=True hands consumers a read-only view while the block keeps a writable object.
  • isolate() to give a test fresh context state and roll everything back after it.
  • No dependencies, Python 3.10 and up, and a public API of fifteen names.

Cost

A lookup is one dict read on a single ContextVar, and nothing is constructed, resolved or cached along the way. The first four rows are one function doing one read, reached four ways, so they can be read against each other and against the parameter they replace.

operation ns ×
one read in a function, value passed in as a parameter 25 1.0
the same read through use() 64 2.6
the same read through @inject 78 3.2
the same read through a frozen=True provider 124 5.0
use(Config) on its own, without the call frame 50 2.0
bare ContextVar.get(), for reference 18 0.7
with provider(...), enter and exit 576 23
the same with 8 providers already open 762 31
wrap(fn)(), per call into a thread 654 26

CPython 3.14.5, arm64.

The × column is against handing the value in as a parameter, which is the alternative nodrill removes from the signatures in between. Reading through use() costs a little over the parameter it replaces; @inject costs more, because it fills the argument before the body runs; frozen=True adds a proxy hop to every attribute the consumer touches. A request that reads a provided value a hundred times spends microseconds in nodrill, against hundreds of microseconds for one round trip to a database.

Entering a provider is the expensive end, because it copies the registry so that sibling tasks stay isolated. That copy is proportional to how many providers are open, which the last two rows price at one and at eight, and it happens once per scope rather than once per lookup.

The absolute numbers move with the machine, and the ratios are the part worth reading. Regenerate with make bench ARGS=--write, which measures on your machine and rewrites the block above. A rerun lands within a few percent, so read the digits as approximate; nothing here runs in CI, because timing on a shared runner measures the runner.

Install

pip install nodrill

Python 3.10 or newer.

Documentation

Full documentation is at https://nodrill.readthedocs.io/.

Start with the tutorial, which covers the whole library in about ten minutes, over typed class keys, fallbacks for a miss, @inject, threads and asyncio, frozen providers, and testing.

Contributing

Bug reports and small focused pull requests are welcome. See CONTRIBUTING.md. make install sets up the environment, and make runs the same gate CI does.

Security issues go through a private advisory rather than the issue tracker.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nodrill-0.1.0.tar.gz (39.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nodrill-0.1.0-py3-none-any.whl (21.5 kB view details)

Uploaded Python 3

File details

Details for the file nodrill-0.1.0.tar.gz.

File metadata

  • Download URL: nodrill-0.1.0.tar.gz
  • Upload date:
  • Size: 39.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for nodrill-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fa85b5fc7b49402211a0a25a7c06f7835e1837c6a1c4edee9fabb0d9dae1c204
MD5 56f6f6eaa0e178a702151547635d1d64
BLAKE2b-256 36499e83adaae45b497f73dd4f6b5d62da7edbd85fa6543e743adf2cc7cd43f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for nodrill-0.1.0.tar.gz:

Publisher: publish.yml on paqstd-dev/nodrill

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nodrill-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: nodrill-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for nodrill-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cb83aca6adfa708560240d27ffb7fc3ee4a590c0757470a91d4e23c035963e6d
MD5 13c66bc0af5e399b6bfc4b6894d93c3e
BLAKE2b-256 26201f4320c97f5e68d38fcc5c5a58b96468a7fad6354ba0e46a3a4157912051

See more details on using hashes here.

Provenance

The following attestation bundles were made for nodrill-0.1.0-py3-none-any.whl:

Publisher: publish.yml on paqstd-dev/nodrill

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page