diff --git a/MANIFEST.in b/MANIFEST.in
index eb21927..973e7b1 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -13,3 +13,4 @@ include .coveragerc .flake8
include tox.ini readthedocs.yml
recursive-exclude * __pycache__ *.py[cod]
+recursive-include examples *.py
diff --git a/docs/api.rst b/docs/api.rst
index 6303651..81e74c4 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -10,6 +10,9 @@
.. autoclass:: ServiceRegistry
:members:
+.. autoclass:: service_factory
+ :members:
+
:mod:`wired.dataclasses` API
============================
diff --git a/docs/examples.rst b/docs/examples.rst
new file mode 100644
index 0000000..760ff96
--- /dev/null
+++ b/docs/examples.rst
@@ -0,0 +1,97 @@
+========
+Examples
+========
+
+.. _examples-decorators:
+
+Decorators
+~~~~~~~~~~
+
+Let's show the use of `venusian `_ and the :class:`wired.service_factory` decorator in building an app that scans for factories.
+We'll do it piece-by-piece, starting with a regular ``wired`` app.
+
+Basic ``wired`` app
+-------------------
+
+As a starting point we use an app with *no* decorators.
+In this app we have a ``Greeting`` class that depends on a ``Greeter`` class.
+As such, we register a factory for each.
+
+.. literalinclude:: ../examples/decorators/no_decorator.py
+
+This is the basics of a simple, pluggable application.
+As a note, everything in the ``app`` function would typically be done once as part of your app.
+
+Class as factory
+----------------
+
+Before getting to decorators, just to emphasize...the first argument to :meth:`wired.ServiceRegistry.register_factory` can be the class itself.
+
+.. literalinclude:: ../examples/decorators/no_decorator_class.py
+ :emphasize-lines: 25
+
+``venusian`` scanner
+--------------------
+
+We will now add ``venusian`` and its ``Scanner``.
+We make a ``Scanner`` instance and include the ``registry``.
+When we call ``scan`` on a module -- in this case, the same module -- it looks for the ``@service_factory`` decorator.
+The decorator then extracts the ``registry`` instance we stored in the ``Scanner`` and does the registration.
+
+.. literalinclude:: ../examples/decorators/basic_class.py
+
+What's nice about this venusian approach: no module-level state globals stuff.
+
+Another decorator plus ``__wired_factory__``
+--------------------------------------------
+
+We'll now move the ``Greeter`` class to also use the ``@service_factory`` decorator instead of a manual registration.
+Since it hard-codes ``Marie`` as a value to the constructor, we use the ``__wired_factory__`` protocol as a class method to generate the instance.
+This means any code that does ``container.get(Greeter)`` will run this class method to construct the ``Greeter``.
+
+.. literalinclude:: ../examples/decorators/decorator_with_wired_factory.py
+
+We also add a ``__wired_factory__`` class method to ``Greeting`` to make it nicer.
+Now its constructor no longer uses the ``container``, which is a huge surface area.
+Instead, the class is constructed just with the data it needs, which is nice for testing.
+The class method acts as an "adapter", getting stuff out of the container that is needed for the class.
+
+Decorator arguments
+-------------------
+
+The ``@service_factory`` acts as a replacement for ``register_factory``.
+Thus it needs to support the other arguments beyond the first one:
+
+- The ``service_or_iface``, if not provided, defaults to the class the decorator is decorating
+- If you pass ``for_=`` to the decorator, it will be used as the ``service_or_iface`` argument to
+- You can also pass ``context=`` and ``name=``
+
+Imagine our app now has a ``Customer`` and ``FrenchCustomer`` as container contexts.
+Here is an example of registering different ``Greeter`` classes that are unique to those contexts:
+
+.. literalinclude:: ../examples/decorators/decorator_args.py
+
+.. _examples-wired-factory:
+
+Wired Factory
+~~~~~~~~~~~~~
+
+Registering a factory means two things: a callable that constructs and returns an object, then the "kind" of thing the factory is registered for.
+You can eliminate the callable as a separate function by providing a ``__wired_factory__`` callable *on*, for example, the class that gets constructed.
+
+This is the wired factory "protocol" and the callable acts as an adapter.
+It is handed the container, extracts what it needs, then constructs and returns an object.
+
+Basic wired factory callable
+----------------------------
+
+We start again with our simple app, with a ``Greeting`` that uses a ``Greeter``.
+In this case, we do two things:
+
+- Both classes have a ``classmethod`` that manages construction of instances
+- The ``register_factory`` first argument is, thus, the class itself
+
+.. literalinclude:: ../examples/wired_factory/register_wired_factory.py
+
+With this, when some application code calls ``container.get(Greeter)``, the construction is done by ``Greeter.__wired_factory__``.
+
diff --git a/docs/index.rst b/docs/index.rst
index 6672ccf..7d7d5f1 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -108,6 +108,7 @@ More Information
:maxdepth: 1
usage
+ examples
tutorial/index
dc/index
dc/usage
diff --git a/docs/tutorial/context/app.py b/docs/tutorial/context/app.py
index 6089c4f..1649638 100644
--- a/docs/tutorial/context/app.py
+++ b/docs/tutorial/context/app.py
@@ -111,7 +111,3 @@ def main():
# then test the result.
french_customer = FrenchCustomer(name='Henri')
assert 'Bonjour Henri !!' == greet_customer(registry, french_customer)
-
-
-if __name__ == '__main__':
- main()
diff --git a/docs/tutorial/datastore/__init__.py b/docs/tutorial/datastore/__init__.py
index 0f900e4..2478021 100644
--- a/docs/tutorial/datastore/__init__.py
+++ b/docs/tutorial/datastore/__init__.py
@@ -122,7 +122,3 @@ def main():
registry = app_bootstrap(settings)
greetings = sample_interactions(registry)
assert greetings == ['Hello Mary !!', 'Bonjour Henri !!']
-
-
-if __name__ == '__main__':
- main()
diff --git a/docs/tutorial/decoupled/__init__.py b/docs/tutorial/decoupled/__init__.py
index a0239f0..f81413d 100644
--- a/docs/tutorial/decoupled/__init__.py
+++ b/docs/tutorial/decoupled/__init__.py
@@ -94,7 +94,3 @@ def main():
french_customer = FrenchCustomer(name='Henri')
assert 'Bonjour Henri !!' == greet_customer(registry, french_customer)
-
-
-if __name__ == '__main__':
- main()
diff --git a/docs/tutorial/factory/app.py b/docs/tutorial/factory/app.py
index f1bee06..a57b2af 100644
--- a/docs/tutorial/factory/app.py
+++ b/docs/tutorial/factory/app.py
@@ -57,7 +57,3 @@ def main():
registry = setup()
greeting = greet_a_customer(registry)
assert greeting == 'Hello !!'
-
-
-if __name__ == '__main__':
- main()
diff --git a/docs/tutorial/overrides/__init__.py b/docs/tutorial/overrides/__init__.py
index 7933814..a0545b1 100644
--- a/docs/tutorial/overrides/__init__.py
+++ b/docs/tutorial/overrides/__init__.py
@@ -36,7 +36,7 @@ def setup(registry: ServiceRegistry, settings: Settings):
# Make the greeter factory, using punctuation from settings
punctuation = settings.punctuation
- def default_greeter_factory(container) -> Greeter:
+ def default_greeter_factory(container) -> Greeter: # pragma: no cover
# Use the dataclass default for greeting
return Greeter(punctuation=punctuation)
@@ -59,7 +59,6 @@ def app_bootstrap(settings: Settings) -> ServiceRegistry:
from .custom import setup as addon_setup
addon_setup(registry, settings)
-
return registry
@@ -97,8 +96,4 @@ def main():
settings = Settings(punctuation='!!')
registry = app_bootstrap(settings)
greetings = sample_interactions(registry)
- assert greetings == ['Hello Mary !!', 'Bonjour Henri !!']
-
-
-if __name__ == '__main__':
- main()
+ assert ['Override Mary !!', 'Bonjour Henri !!'] == greetings
diff --git a/docs/tutorial/requests_views/__init__.py b/docs/tutorial/requests_views/__init__.py
index 8fcc8a8..925edf2 100644
--- a/docs/tutorial/requests_views/__init__.py
+++ b/docs/tutorial/requests_views/__init__.py
@@ -128,7 +128,3 @@ def main():
registry = app_bootstrap(settings)
greetings = sample_interactions(registry)
assert greetings == ['Hello Mary !!', 'Bonjour Henri !!']
-
-
-if __name__ == '__main__':
- main()
diff --git a/docs/tutorial/settings/app.py b/docs/tutorial/settings/app.py
index dab6f8c..3bb2f82 100644
--- a/docs/tutorial/settings/app.py
+++ b/docs/tutorial/settings/app.py
@@ -70,7 +70,3 @@ def main():
registry = setup(settings)
greeting = greet_a_customer(registry)
assert greeting == 'Hello !!'
-
-
-if __name__ == '__main__':
- main()
diff --git a/docs/tutorial/simple/app.py b/docs/tutorial/simple/app.py
index f4795e2..9899d85 100644
--- a/docs/tutorial/simple/app.py
+++ b/docs/tutorial/simple/app.py
@@ -56,7 +56,3 @@ def main():
registry = setup()
greeting = greet_a_customer(registry)
assert greeting == 'Hello !!'
-
-
-if __name__ == '__main__':
- main()
diff --git a/docs/usage.rst b/docs/usage.rst
index 838c2bf..c46b548 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -41,10 +41,53 @@ However, a service factory that is registered to provide instances of the ``Logi
Anyone else registering such a service factory is directly competing for control of that type.
It is possible to register for both type and name.
+As a note, when calling :meth:`wired.ServiceRegistry.register_factory`, the first argument is a callable.
+It doesn't have to be a function: it could be a class that accepts the container as an argument.
+In such a case, the class might be *both* arguments.
+This kind of usage is helpful when combined with the ``__wired_factory__`` protocol and the ``@service_factory`` decorator discussed below.
+
Service factories accept one argument, a :class:`wired.ServiceContainer` instance.
The container may be used to get any dependencies required to create the service and return it from the factory.
The service is then cached on the container, available for any other factories or code to get.
+The ``@service_factory`` decorator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It's quite convenient to register your factories with decorators, as you can just point at a module or package and not manually do each registration.
+``wired`` has optional support for the `venusian `_ package for deferred scanning and evaluation of decorators.
+You can then register your services with the :class:`wired.service_factory` decorator:
+
+.. literalinclude:: ../examples/decorators/basic_class.py
+ :start-at: import service_factory
+ :end-at: Hello from
+
+The decorator can take arguments of ``for_``, ``context``, and ``name``, to mimic the arguments to ``register_factory``.
+
+You can find more variations, including setup of the scanner, on this in the :ref:`examples-decorators` examples.
+
+The ``__wired_factory__`` protocol
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you call :meth:`wired.ServiceRegistry.register_factory` the first argument is the factory "function".
+It takes a :class:`wired.ServiceContainer`, makes the desired object, and returns it.
+This means a separation: a "function" that makes the class instance, and the class.
+
+As we saw above, the first argument is really a callable that returns an instance, and that means the class itself can be the first argument.
+But what if you want custom logic to pick apart the container then construct the class?
+
+Enter the ``__wired_factory__`` protocol.
+This is an attribute -- for example, a ``classmethod`` -- on the factory callable.
+It is passed the container and returns the class.
+
+.. literalinclude:: ../examples/wired_factory/register_wired_factory.py
+ :start-at: @classmethod
+ :end-at: return cls
+
+This class method is then used as the first argument to :meth:`wired.ServiceRegistry.register_factory`.
+It doesn't have to be just for classes and class methods: a function/class/instance could have a ``__wired_factory__`` attribute stamped on it, possibly via an intermediate decorator.
+
+More examples are available in :ref:`examples-wired-factory`.
+
Example
~~~~~~~
@@ -130,3 +173,4 @@ For example, imagine binding the web request itself as a service, or the active
# later ...
user = container.get(IUser)
+
diff --git a/examples/__init__.py b/examples/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/examples/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/examples/decorators/__init__.py b/examples/decorators/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/examples/decorators/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/examples/decorators/basic_class.py b/examples/decorators/basic_class.py
new file mode 100644
index 0000000..3a7c621
--- /dev/null
+++ b/examples/decorators/basic_class.py
@@ -0,0 +1,42 @@
+"""
+Simplest example for ``@service_factory`` decorator: a basic class.
+"""
+from venusian import Scanner
+
+from wired import service_factory, ServiceRegistry
+from .. import decorators
+
+
+@service_factory()
+class Greeting:
+ def __init__(self, container):
+ self.greeter = container.get(Greeter)
+
+ def greet(self):
+ return f'Hello from {self.greeter.name}'
+
+
+class Greeter:
+ def __init__(self, name):
+ self.name = name
+
+
+def greeter_factory(container):
+ return Greeter('Marie')
+
+
+def app():
+ # Do this once at startup
+ registry = ServiceRegistry()
+ scanner = Scanner(registry=registry)
+ # Point the scanner at a package/module and scan
+ scanner.scan(decorators.basic_class)
+
+ registry.register_factory(greeter_factory, Greeter)
+ # No longer need this line
+ # registry.register_factory(Greeting, Greeting)
+
+ # Do this for every "request" or operation
+ container = registry.create_container()
+ greeting: Greeting = container.get(Greeting)
+ assert 'Hello from Marie' == greeting.greet()
diff --git a/examples/decorators/decorator_args.py b/examples/decorators/decorator_args.py
new file mode 100644
index 0000000..abe2d29
--- /dev/null
+++ b/examples/decorators/decorator_args.py
@@ -0,0 +1,77 @@
+"""
+Decorators for both plus usage of the ``__wired_factory__ protocol.
+"""
+from venusian import Scanner
+
+from wired import service_factory, ServiceRegistry
+from .. import decorators
+
+
+class Customer:
+ def __init__(self):
+ self.name = 'Jill'
+
+
+class FrenchCustomer(Customer):
+ def __init__(self):
+ super().__init__()
+ self.name = 'Juliette'
+
+
+@service_factory(context=Customer)
+class Greeter:
+ def __init__(self, name):
+ self.name = name
+
+ @classmethod
+ def __wired_factory__(cls, container):
+ return cls('Susan')
+
+
+@service_factory(for_=Greeter, context=FrenchCustomer)
+class FrenchGreeter:
+ """ Serves as Greeter when container.context is FrenchCustomer """
+ def __init__(self, name):
+ self.name = name
+
+ @classmethod
+ def __wired_factory__(cls, container):
+ return cls('Marie')
+
+
+@service_factory(context=Customer)
+class Greeting:
+ greeter: Greeter
+
+ def __init__(self, greeter: Greeter, customer):
+ self.greeter = greeter
+ self.customer = customer
+
+ def greet(self):
+ return f'Hello from {self.greeter.name} to {self.customer.name}'
+
+ @classmethod
+ def __wired_factory__(cls, container):
+ greeter = container.get(Greeter)
+ customer = container.context
+ return cls(greeter, customer)
+
+
+def app():
+ # Do this once at startup
+ registry = ServiceRegistry()
+ scanner = Scanner(registry=registry)
+ # Point the scanner at a package/module and scan
+ scanner.scan(decorators.decorator_args)
+
+ # First request, for a regular Customer
+ customer1 = Customer()
+ container1 = registry.create_container(context=customer1)
+ greeting1: Greeting = container1.get(Greeting)
+ assert 'Hello from Susan to Jill' == greeting1.greet()
+
+ # Second request, for a FrenchCustomer
+ customer2 = FrenchCustomer()
+ container2 = registry.create_container(context=customer2)
+ greeting2: Greeting = container2.get(Greeting)
+ assert 'Hello from Marie to Juliette' == greeting2.greet()
diff --git a/examples/decorators/decorator_with_wired_factory.py b/examples/decorators/decorator_with_wired_factory.py
new file mode 100644
index 0000000..0729ea4
--- /dev/null
+++ b/examples/decorators/decorator_with_wired_factory.py
@@ -0,0 +1,46 @@
+"""
+Decorators for both plus usage of the ``__wired_factory__ protocol.
+"""
+from venusian import Scanner
+
+from wired import service_factory, ServiceRegistry
+from .. import decorators
+
+
+@service_factory()
+class Greeter:
+ def __init__(self, name):
+ self.name = name
+
+ @classmethod
+ def __wired_factory__(cls, container):
+ return cls('Marie')
+
+
+@service_factory()
+class Greeting:
+ greeter: Greeter
+
+ def __init__(self, greeter: Greeter):
+ self.greeter = greeter
+
+ def greet(self):
+ return f'Hello from {self.greeter.name}'
+
+ @classmethod
+ def __wired_factory__(cls, container):
+ greeter = container.get(Greeter)
+ return cls(greeter)
+
+
+def app():
+ # Do this once at startup
+ registry = ServiceRegistry()
+ scanner = Scanner(registry=registry)
+ # Point the scanner at a package/module and scan
+ scanner.scan(decorators.decorator_with_wired_factory)
+
+ # Do this for every "request" or operation
+ container = registry.create_container()
+ greeting: Greeting = container.get(Greeting)
+ assert 'Hello from Marie' == greeting.greet()
diff --git a/examples/decorators/no_decorator.py b/examples/decorators/no_decorator.py
new file mode 100644
index 0000000..1049fe6
--- /dev/null
+++ b/examples/decorators/no_decorator.py
@@ -0,0 +1,35 @@
+from wired import ServiceRegistry, ServiceContainer
+
+
+class Greeter:
+ def __init__(self, name):
+ self.name = name
+
+
+def greeter_factory(container):
+ return Greeter('Marie')
+
+
+class Greeting:
+ def __init__(self, greeter: Greeter):
+ self.greeter = greeter
+
+ def greet(self):
+ return f'Hello from {self.greeter.name}'
+
+
+def greeting_factory(container: ServiceContainer):
+ greeter = container.get(Greeter)
+ return Greeting(greeter)
+
+
+def app():
+ # Do this once at startup
+ registry = ServiceRegistry()
+ registry.register_factory(greeter_factory, Greeter)
+ registry.register_factory(greeting_factory, Greeting)
+
+ # Do this for every "request" or operation
+ container = registry.create_container()
+ greeting: Greeting = container.get(Greeting)
+ assert 'Hello from Marie' == greeting.greet()
diff --git a/examples/decorators/no_decorator_class.py b/examples/decorators/no_decorator_class.py
new file mode 100644
index 0000000..edc13a2
--- /dev/null
+++ b/examples/decorators/no_decorator_class.py
@@ -0,0 +1,30 @@
+from wired import ServiceContainer, ServiceRegistry
+
+
+class Greeter:
+ def __init__(self, name):
+ self.name = name
+
+
+class Greeting:
+ def __init__(self, container: ServiceContainer):
+ self.greeter = container.get(Greeter)
+
+ def greet(self):
+ return f'Hello from {self.greeter.name}'
+
+
+def greeter_factory(container):
+ return Greeter('Marie')
+
+
+def app():
+ # Do this once at startup
+ registry = ServiceRegistry()
+ registry.register_factory(greeter_factory, Greeter)
+ registry.register_factory(Greeting, Greeting)
+
+ # Do this for every "request" or operation
+ container = registry.create_container()
+ greeting: Greeting = container.get(Greeting)
+ assert 'Hello from Marie' == greeting.greet()
diff --git a/examples/wired_factory/register_wired_factory.py b/examples/wired_factory/register_wired_factory.py
new file mode 100644
index 0000000..7020df0
--- /dev/null
+++ b/examples/wired_factory/register_wired_factory.py
@@ -0,0 +1,40 @@
+"""
+A class is a callable that can be the factory supplied to
+:func:`wired.ServiceRegistry.register_factory`.
+"""
+
+from wired import ServiceContainer, ServiceRegistry
+
+
+class Greeter:
+ def __init__(self, name):
+ self.name = name
+
+ @classmethod
+ def __wired_factory__(cls, container: ServiceContainer):
+ return cls('Marie')
+
+
+class Greeting:
+ def __init__(self, greeter: Greeter):
+ self.greeter = greeter
+
+ def greet(self):
+ return f'Hello from {self.greeter.name}'
+
+ @classmethod
+ def __wired_factory__(cls, container):
+ greeter = container.get(Greeter)
+ return cls(greeter)
+
+
+def app():
+ # Do this once at startup
+ registry = ServiceRegistry()
+ registry.register_factory(Greeter, Greeter)
+ registry.register_factory(Greeting, Greeting)
+
+ # Do this for every "request" or operation
+ container = registry.create_container()
+ greeting: Greeting = container.get(Greeting)
+ assert 'Hello from Marie' == greeting.greet()
diff --git a/src/wired/__init__.py b/src/wired/__init__.py
index bb6fd40..579387e 100644
--- a/src/wired/__init__.py
+++ b/src/wired/__init__.py
@@ -1,4 +1,5 @@
-__all__ = ['ServiceContainer', 'ServiceRegistry']
+__all__ = ['ServiceContainer', 'ServiceRegistry', 'service_factory']
from .container import ServiceContainer
from .container import ServiceRegistry
+from .decorators import service_factory
diff --git a/src/wired/container.py b/src/wired/container.py
index 02f0230..4a056d9 100644
--- a/src/wired/container.py
+++ b/src/wired/container.py
@@ -33,7 +33,9 @@ class IContextFinalizer(Interface):
class ServiceFactoryInfo:
def __init__(self, factory, service_iface, context_iface, wants_context):
- self.factory = factory
+ # Use the __wired_factory__ protocol if present
+ _factory = getattr(factory, '__wired_factory__', factory)
+ self.factory = _factory
self.service_iface = service_iface
self.context_iface = context_iface
self.wants_context = wants_context
diff --git a/src/wired/dataclasses/__init__.py b/src/wired/dataclasses/__init__.py
index f4f1b9a..41d19c0 100644
--- a/src/wired/dataclasses/__init__.py
+++ b/src/wired/dataclasses/__init__.py
@@ -4,4 +4,3 @@
from .field_types import injected
from .models import Context
from .registration import register_dataclass
-
diff --git a/src/wired/decorators.py b/src/wired/decorators.py
new file mode 100644
index 0000000..b44d82a
--- /dev/null
+++ b/src/wired/decorators.py
@@ -0,0 +1,39 @@
+# wired is usable without venusian
+try:
+ import venusian
+except ImportError: # pragma: no cover
+ venusian = None
+
+
+# noinspection PyPep8Naming
+class service_factory:
+ """
+ Register a factory for a class that can sniff dependencies.
+
+ The factory will be registered with a :class:`wired.ServiceRegistry` when
+ performing a venusian scan.
+
+ .. seealso::
+
+ - :func:`wired.ServiceRegistry.register_factory`
+
+ """
+
+ def __init__(self, for_=None, context=None, name: str = ''):
+ self.for_ = for_
+ self.context = context
+ self.name = name
+
+ def __call__(self, wrapped):
+ def callback(scanner: venusian.Scanner, name: str, cls):
+ registry = getattr(scanner, 'registry')
+ # If there is a for_ use it, otherwise, register for the same
+ # class as the instance
+ for_ = self.for_ if self.for_ else cls
+
+ registry.register_factory(
+ cls, for_, context=self.context, name=self.name
+ )
+
+ venusian.attach(wrapped, callback, category='wired')
+ return wrapped
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..f665893
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,13 @@
+import os
+import sys
+
+import pytest
+
+
+@pytest.fixture(scope="session", autouse=True)
+def examples_path():
+ """ Automatically add the root of the repo to path """
+ tutorial_path = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..')
+ )
+ sys.path.insert(0, tutorial_path)
diff --git a/tests/dataclasses/integration/conftest.py b/tests/dataclasses/integration/conftest.py
index 02a740b..7ebcb78 100644
--- a/tests/dataclasses/integration/conftest.py
+++ b/tests/dataclasses/integration/conftest.py
@@ -3,9 +3,6 @@
import pytest
-if sys.version_info < (3, 7): # pragma: no cover
- collect_ignore_glob = ['*.py']
-
@pytest.fixture(scope="session", autouse=True)
def docs_path():
diff --git a/tests/examples/conftest.py b/tests/examples/conftest.py
new file mode 100644
index 0000000..5a9e729
--- /dev/null
+++ b/tests/examples/conftest.py
@@ -0,0 +1,6 @@
+import sys
+
+if sys.version_info < (3, 7): # pragma: no cover
+ collect_ignore_glob = ['*.py']
+else:
+ pass
diff --git a/tests/examples/test_examples.py b/tests/examples/test_examples.py
new file mode 100644
index 0000000..db93ebb
--- /dev/null
+++ b/tests/examples/test_examples.py
@@ -0,0 +1,25 @@
+import pytest
+
+from examples.decorators import (
+ no_decorator,
+ no_decorator_class,
+ basic_class,
+ decorator_with_wired_factory,
+ decorator_args,
+)
+from examples.wired_factory import register_wired_factory
+
+
+@pytest.mark.parametrize(
+ 'target',
+ (
+ no_decorator,
+ no_decorator_class,
+ basic_class,
+ decorator_with_wired_factory,
+ register_wired_factory,
+ decorator_args,
+ ),
+)
+def test_greeter(target):
+ target.app()
diff --git a/tests/test_container.py b/tests/test_container.py
index 73cb7f1..438e504 100644
--- a/tests/test_container.py
+++ b/tests/test_container.py
@@ -1,6 +1,5 @@
import pytest
-from zope.interface import Interface
-from zope.interface import implementer
+from zope.interface import Interface, implementer
class IFooService(Interface):
@@ -48,6 +47,20 @@ def __call__(self, container):
return self.result
+class DummyWiredFactory:
+ def __init__(self, result=None):
+ if result is None:
+ result = DummyService()
+ self.result = result
+ self.calls = []
+
+ @classmethod
+ def __wired_factory__(cls, container):
+ inst = cls()
+ inst.calls.append(container)
+ return inst
+
+
@pytest.fixture
def registry():
from wired import ServiceRegistry
@@ -83,6 +96,15 @@ def test_various_params(registry, iface, contexts, name):
assert len(factory.calls) == 1
+def test_wired_factory(registry):
+ registry.register_factory(DummyWiredFactory, DummyWiredFactory)
+ c1 = registry.create_container()
+ result = c1.get(DummyWiredFactory)
+ assert isinstance(result, DummyWiredFactory)
+ assert result.calls[0] is c1
+ assert len(result.calls) == 1
+
+
def test_basic_caching(registry):
factory = DummyFactory()
registry.register_factory(factory, name='foo')
diff --git a/tests/tutorial/test_context.py b/tests/tutorial/test_context.py
index 4076a37..8943d6a 100644
--- a/tests/tutorial/test_context.py
+++ b/tests/tutorial/test_context.py
@@ -45,3 +45,9 @@ def test_greet_french_customer(registry, french_customer):
actual = greet_customer(registry, french_customer)
assert 'Bonjour Henri !!' == actual
+
+
+def test_main():
+ from tutorial.context.app import main
+
+ assert None is main()
diff --git a/tests/tutorial/test_datastore.py b/tests/tutorial/test_datastore.py
index e63caac..e14154a 100644
--- a/tests/tutorial/test_datastore.py
+++ b/tests/tutorial/test_datastore.py
@@ -25,3 +25,9 @@ def test_sample_interactions(registry):
greetings = sample_interactions(registry)
assert 'Hello Mary !!' == greetings[0]
assert 'Bonjour Henri !!' == greetings[1]
+
+
+def test_main():
+ from tutorial.datastore import main
+
+ assert None is main()
diff --git a/tests/tutorial/test_decoupled.py b/tests/tutorial/test_decoupled.py
index 81e9d6c..5960465 100644
--- a/tests/tutorial/test_decoupled.py
+++ b/tests/tutorial/test_decoupled.py
@@ -45,3 +45,9 @@ def test_greet_french_customer(registry, french_customer):
actual = greet_customer(registry, french_customer)
assert 'Bonjour Henri !!' == actual
+
+
+def test_main():
+ from tutorial.decoupled import main
+
+ assert None is main()
diff --git a/tests/tutorial/test_factory.py b/tests/tutorial/test_factory.py
index e36f0d5..4fcdc6b 100644
--- a/tests/tutorial/test_factory.py
+++ b/tests/tutorial/test_factory.py
@@ -16,3 +16,9 @@ def test_greet_a_customer(registry):
actual = greet_a_customer(registry)
assert 'Hello !!' == actual
+
+
+def test_main():
+ from tutorial.factory.app import main
+
+ assert None is main()
diff --git a/tests/tutorial/test_overrides.py b/tests/tutorial/test_overrides.py
index 6412fe2..2563b7a 100644
--- a/tests/tutorial/test_overrides.py
+++ b/tests/tutorial/test_overrides.py
@@ -25,3 +25,9 @@ def test_sample_interactions(registry):
greetings = sample_interactions(registry)
assert 'Override Mary !!' == greetings[0]
assert 'Bonjour Henri !!' == greetings[1]
+
+
+def test_main():
+ from tutorial.overrides import main
+
+ assert None is main()
diff --git a/tests/tutorial/test_requests_views.py b/tests/tutorial/test_requests_views.py
index adfc35b..033aff5 100644
--- a/tests/tutorial/test_requests_views.py
+++ b/tests/tutorial/test_requests_views.py
@@ -25,3 +25,9 @@ def test_sample_interactions(registry):
greetings = sample_interactions(registry)
assert 'Hello Mary !!' == greetings[0]
assert 'Bonjour Henri !!' == greetings[1]
+
+
+def test_main():
+ from tutorial.requests_views import main
+
+ assert None is main()
diff --git a/tests/tutorial/test_settings.py b/tests/tutorial/test_settings.py
index d625ce5..b038d75 100644
--- a/tests/tutorial/test_settings.py
+++ b/tests/tutorial/test_settings.py
@@ -24,3 +24,9 @@ def test_greet_a_customer(registry):
actual = greet_a_customer(registry)
assert 'Hello !!' == actual
+
+
+def test_main():
+ from tutorial.settings.app import main
+
+ assert None is main()
diff --git a/tests/tutorial/test_simple.py b/tests/tutorial/test_simple.py
index 6eb601f..2350369 100644
--- a/tests/tutorial/test_simple.py
+++ b/tests/tutorial/test_simple.py
@@ -16,3 +16,9 @@ def test_greet_a_customer(registry):
actual = greet_a_customer(registry)
assert 'Hello !!' == actual
+
+
+def test_main():
+ from tutorial.simple.app import main
+
+ assert None is main()