Skip to main content

mainline

mainline logo

Simple yet powerful python dependency injection.

coverage-badge docs-badge

Why

  • Pure Python, so it basically works everywhere. Tested against cPython 3.11. PyPy/PyPy3 are also fully supported.

  • Only external dependencies are six and wrapt, both of which you’re likely to already have.

  • Supports using function annotations in Python 3.x.

  • Your method signature is fully preserved, maintaining introspection ability. (Minus any injected arguments of course.)

  • Scope is fully configurable (per injectable), giving you tight control over where an object should be shared and where it should not.

  • Supports auto injection”, where your argument names are used to determine what gets injected. It’s also fully optional, as it’s slightly less performant due to it’s dynamic nature.

  • Provider keys tend to be strings, but really any hashable object is supported, so if you prefer to use classes, go for it.

    Just keep in mind that you can’t use a class as an argument name (rightfully so) in python. This means you can’t auto inject it, for instance. You can simply make an alias to get both worlds, however. The world is your oyster.

  • Check out that sweet syringe.

Installation

pip install mainline

Quickstart

Make sure to check the docs for more use cases!

"""
Initialize your Di instance.
"""

>>> from mainline import Di
>>> di = Di()

"""
Feed it your delicious factories, optionally scoped.
"""

>>> @di.register_factory('apple')
... def apple():
...    return 'apple'

"""
Factories can of course be injected themselves.
"""

>>> @di.f('banana', scope='global')     # f is syntactic sugar for register_factory
... def banana():
...    return 'banana'

"""
Let's verify that our factories above do what they're supposed to.
"""

>>> di.resolve('apple') == 'apple' and di.resolve('banana') == 'banana'
True

"""
Positional arguments are injected in the order given:
"""

>>> @di.inject('apple')
... def injected(apple):
...     return apple

>>> injected() == apple()   # verify
True

"""
Injecting keyword arguments is straight forward, you simply hand them as keyword arguments:
"""

>>> @di.f('orange')     # alias for register_factory
... @di.i('apple')      # alias for inject
... def orange(apple):
...     return 'banana', apple

>>> @di.i('apple', an_orange='orange')
... def injected(apple, arg1, an_orange=None):
...     return apple, arg1, an_orange

>>> injected('arg1') == (apple(), 'arg1', orange())  # verify
True

"""
Arguments that are not injected work as expected:
"""

>>> @di.inject('apple')
... def injected(apple, arg1):
...     return apple, arg1

>>> injected('arg1') == (apple(), 'arg1')
True

"""
Injection on a class injects upon it's `__init__` method:
"""

>>> @di.inject('apple')
... class Injectee(object):
...     def __init__(self, apple):
...         self.apple = apple

>>> Injectee().apple == apple()
True

"""
You can inject class-level properties using `di.inject_classproperty()`:
"""

>>> @di.inject_classproperty('apple')
... class Injectee(object):
...     pass

>>> Injectee.apple == apple()
True

"""
Injecting providers based upon the argpsec can be done with `di.auto_inject`, or it's shortened alias `di.ai()`:
"""

>>> @di.auto_inject()
... def injected(apple):
...     return apple

>>> injected() == apple()
True

>>> @di.ai('apple')             # alias for auto_inject
... def injected(apple, arg1):
...     return apple, arg1

>>> injected('arg1') == (apple(), 'arg1')
True

>>> @di.auto_inject()
... def injected(apple, arg1, banana=None):
...     return apple, arg1, banana

>>> injected('arg1') == (apple(), 'arg1', banana())
True

>>> @di.auto_inject(renamed_banana='banana')
... def injected(apple, arg1, renamed_banana):
...     return apple, arg1, renamed_banana

>>> injected('arg1') == (apple(), 'arg1', banana())
True

Running tests

Tox is used to handle testing multiple python versions.

tox

Metadata

Release files for mainline 1.2.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 mainline 1.2.0
File Size Uploaded Provenance Actions
mainline-1.2.0.tar.gz 56.5 kB Without provenance Details for mainline-1.2.0.tar.gz

Release files / mainline-1.2.0.tar.gz

Download URL mainline-1.2.0.tar.gz
Size 56.5 kB
Tags Source
SHA-256 checksum
How to use checksums
31610c2cae101dcbe5a79b76ffda6bf658a054c4ad245cc6b15b535480edf99c
BLAKE2b-256 checksum
How to use checksums
2a0912cf09f10f43cd2162651fc74c1edaf31b756ac25e8b4e1c3b9f563781b2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.0.1 CPython/3.11.2

Security for mainline 1.2.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