Skip to main content

Touchstone

What is Touchstone?

Touchstone is an annotations-driven Inversion of Control container for Python 3.6 and above.

Links:

Learn by Example

Auto Wiring

from touchstone import Container

class Child:
    pass

class Parent:
    def __init__(self, child: Child) -> None:
        self.child = child


container = Container()
parent = container.make(Parent)

assert isinstance(parent.child, Child)

Interface Binding

from touchstone import Container

class AbstractChild:
    pass

class Child(AbstractChild):
    pass

class Parent:
    def __init__(self, child: AbstractChild) -> None:
        self.child = child


container = Container()
container.bind(AbstractChild, Child)
parent = container.make(Parent)

assert isinstance(parent.child, Child)

Binding with Factory Methods

from touchstone import Container

class Child:
    def __init__(self, name: str) -> None:
        self.name = name

class Parent:
    def __init__(self, child: Child) -> None:
        self.child = child


container = Container()
container.bind(Child, lambda: Child('them'))
parent = container.make(Parent)

assert isinstance(parent.child, Child)
assert parent.child.name == 'them'

Binding Singletons

from touchstone import Container, SINGLETON

class Child:
    def __init__(self, name: str) -> None:
        self.name = name

class Parent:
    def __init__(self, child: Child) -> None:
        self.child = child


container = Container()
them_child = Child('them')
container.bind_instance(Child, them_child)
# Or...
container.bind(Child, lambda: them_child, lifetime_strategy=SINGLETON)
parent = container.make(Parent)

assert isinstance(parent.child, Child)
assert parent.child is them_child

Contextual Binding

from touchstone import Container

class Child:
    def __init__(self, name: str) -> None:
        self.name = name

class Parent:
    def __init__(self, child1: Child, child2: Child) -> None:
        self.child1 = child1
        self.child2 = child2


container = Container()
container.bind_contextual(when=Parent, wants=Child, wants_name='child1', give=lambda: Child('her'))
container.bind_contextual(when=Parent, wants=Child, wants_name='child2', give=lambda: Child('him'))
parent = container.make(Parent)

assert isinstance(parent.child1, Child)
assert isinstance(parent.child2, Child)
assert parent.child1.name == 'her'
assert parent.child2.name == 'him'

Django Support

Now featuring Django support! New in v0.3.0

  • Configure your instance of touchstone.Container however you see fit.

  • In your main settings.py, set TOUCHSTONE_CONTAINER_GETTER to the path to a callable that will return the singleton instance of touchstone.Container your app uses.

    • Note that your getter should build the Container precisely once, and then return that same Container on all subsequent calls. Build it as a singleton.

To get injected properties in your class-based views:

  • In your main settings.py, add touchstone.django.InjectViewsMiddleware to your MIDDLEWARE list.

  • Use class annotations on your class-based views. Cached Properties will be added to your view classes so that they automatically resolve using your configured touchstone container. For example:

class MyView(View):
    something: MyObject
    def get(self, request):
        # You can now access self.something!

To get injected properties in your middleware, you’ll need to do a little more work because we haven’t found a good way to hook into Django’s middleware instantiation logic.

from touchstone.django import inject_magic_properties

@inject_magic_properties
class MyMixin:
    something: MyObject
    # define your mixin here...
    # You'll be able to use `self.something` from within every instance method.

Celery Tasks

Celery tasks called can be applied to any callable with a decorator. For example:

class MyLogger:
    def __init__(self, another_logger: AnotherLogger):
        self.another_logger = another_logger

    def log(self, msg):
        print(msg)
        self.another_logger.log(msg)

@app.task
def log_messages(msg):
    another_logger = AnotherLogger()
    my_logger = MyLogger(another_logger)

    my_logger.log(msg)

log_messages.apply_async(args=['hello world'])

However, if we want to refactor this code to make use of touchstone as a service locator:

class MyLogger:
    def __init__(self, another_logger: AnotherLogger):
        self.another_logger = another_logger

    def log(self, msg):
        print(msg)
        self.another_logger.log(msg)

@app.task
def log_messages(msg):
    container = get_container()
    my_logger = container.make(MyLogger)

    my_logger.log(msg)

log_messages.apply_async(args=['hello world'])

Each task would then have quite a bit of repeated code to create the container for each task and then make class instance we need.

class MyLogger:
    def __init__(self, another_logger: AnotherLogger):
        self.another_logger = another_logger

    def log(self, msg):
        print(msg)
        self.another_logger.log(msg)

@touchstone_task
class LogMessagesTask:
    my_logger: MyLogger

    def run(self, msg):
        self.my_logger.log(msg)

LogMessagesTask.apply_async(args=['hello world'])

Metadata

Release files for touchstone 2.0.3

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

Source distribution (sdist)

Source distribution for touchstone 2.0.3
File Size Uploaded Provenance Actions
touchstone-2.0.3.tar.gz 9.4 kB Without provenance Details for touchstone-2.0.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for touchstone 2.0.3
File Interpreter ABI Platform Actions
touchstone-2.0.3-py3-none-any.whl Python 3 none any Details for touchstone-2.0.3-py3-none-any.whl

Total release size: 33.0 kB

Release files / touchstone-2.0.3.tar.gz

Download URL touchstone-2.0.3.tar.gz
Size 9.4 kB
Tags Source
SHA-256 checksum
How to use checksums
a92cc3d265736644e2906b95ec9888a2b763726ce784a1cdea7906f504c520a8
BLAKE2b-256 checksum
How to use checksums
f813fa9e3421377d83c161eb8adc1c46924a87dc8a393ba559436cd01d38e148
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.1.1 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.41.1 CPython/3.6.9

Release files / touchstone-2.0.3-py3-none-any.whl

Download URL touchstone-2.0.3-py3-none-any.whl
Size 23.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
54a61f654af99f830314a77a93036d7c13b53a7529d6c808ec28a112ce6af9b2
BLAKE2b-256 checksum
How to use checksums
4edb6f8dbad069af2f1ccba3876c16bf13069dc02cd0c8d4defe435183c09dfb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.1.1 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.41.1 CPython/3.6.9

Security for touchstone 2.0.3

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