All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project adheres to Calendar Versioning.
The first number of the version is the year. The second number is incremented with each release, starting at 1 for each year. The third number is for emergencies when we need to start branches for older releases.
You can find our backwards-compatibility policy here.
-
Python 3.13 support.
-
svcs.Registry
now implements a__iter__
method that allows to iterate over its registered services. #106
- Python 3.8 support.
-
Flask: The registry is now stored on
app.extensions
, notapp.config
. This is an implementation detail. If you are directly accessing the registry viaapp.config
, this is a breaking change, though you should ideally move tosvcs.flask.registry
anyway. #71 #72 #73 -
Registry.register_factory()
is now more lenient regarding the arguments of the factory. It only looks at the first argument (if present) and ignores the rest. #110
-
Container.aget()
now also enters and exits synchronous context managers. #93 -
Container.aget()
now also enters and exits context managers that are returned by async factories. #105
24.1.0 - 2024-01-25
- AIOHTTP: The registry is now stored using
aiohttp.web.AppKey
s on the application. This is an implementation detail and shouldn't matter, but it fixes a warning on AIOHTTP 3.9 and later.
23.21.0 - 2023-11-21
-
Backwards-Incompatible: Since multiple people have been bit by the
enter=True
default forRegistry.register_value()
, and it's very early in svcs life, we're changing the default toenter=False
for all versions ofregister_value()
.This means that you have to explicitly opt-in to context manager behavior which makes a lot more sense for singletons like connection pools which are the most common candidates for registered values.
(The irony of shipping a backwards-incompatible change in the release directly following the adoption of a backwards-compatibility policy not lost on me.) #50 #51
-
Container-local registries! Sometimes it's useful to bind a value or factory only to a container. For example, request metadata or authentication information.
You can now achieve that with
svcs.Container.register_local_factory()
andsvcs.Container.register_local_value()
. Once something local is registered, a registry is transparently created and it takes precedence over the global one when a service is requested. The local registry is closed together with the container. #56 -
Flask:
svcs.flask.registry
which is awerkzeug.local.LocalProxy
for the currently active registry onflask.current_app
.
-
We've stopped rewriting the public names of our objects and
typing.get_type_hints()
now works on them as expected for Python 3.10 and later. #52 #53 -
The detection of container arguments in
svcs.Registry()
when using string-based type annotations is more robust now. #55
23.20.0 - 2023-09-05
-
Backwards-compatibility! svcs is pronounced stable now -- no more rug-pulls planned! #36
-
Flask:
svcs.flask.container
which is awerkzeug.local.LocalProxy
(like, for example,flask.current_app
) and is the currently active container when accessed within a request context.
23.19.0 - 2023-08-21
- Various optimizations & cleanups.
23.18.0 - 2023-08-17
-
Flask:
svcs.flask.get_registry()
. -
Starlette integration. #31
23.17.0 - 2023-08-15
- FastAPI integration. #30
23.16.0 - 2023-08-14
-
enter keyword argument to all
register_(value|factory)()
. It prevents svcs from entering context managers if the factory returns one. This is useful for context managers like database transactions that you want to start manually. -
Services acquired using
aget()
now also can receive the current container if they take one argument that is namedsvcs_container
or that is annotated as beingsvcs.Container
and has any name.
23.15.0 - 2023-08-14
- A
ResourceWarning
is now raised when a container or a registry are garbage-collected with pending cleanups.
-
Cleanups for services are internally context managers now. For your convenience, if you pass an (async) generator function for a factory, the registry will automatically wrap it for you into an (async) context manager. #29
-
Pyramid:
svcs.pyramid.get()
now takes a Pyramid request as the first argument.svcs.pyramid.get_pings()
also doesn't look at thread locals anymore. If you still want to use thread locals, you can usesvcs.pyramid.from_svcs(None)
to obtain the currently active container. -
Flask:
replace_(value|factory)()
is now calledoverwrite_(value|factory())
to be consistent with the docs lingo. They also completely reset the instantiation cache now (practically speaking: they close the container).
svcs.Container.forget_about()
. It doesn't make any sense in a world of recursive dependencies. Just reset the container usingsvcs.Container.(a)close()
.
23.14.0 - 2023-08-11
- AIOHTTP: missing
aget_abstract()
function. - Pyramid: missing
get_pings()
function.
23.13.0 - 2023-08-11
- AIOHTTP integration.
23.12.0 - 2023-08-09
- svcs now logs registrations at debug level along with a stacktrace. So if you ever get confused where your factories are coming from, set the log level to debug and trace your registrations!
-
Ooof. It's obvious in hindsight, but accessing anything directly on a request object like in the
request.svcs.get()
examples erases type information and everything becomes a big soup ofAny
.Therefore, we've added a new "best practice" for integrations to have a
svcs_from()
function that extracts containers from request objects (or from thread locals in the case of Flask).
23.11.0 - 2023-08-08
-
Factory results of None are now treated like every other result and cached. #22
-
Calling
Container.get()
on a service that has an async factory now raises aTypeError
. #21
23.10.0 - 2023-08-07
-
Proper documentation at https://svcs.hynek.me/! I guess it's getting serious. #17
-
Pyramid integration.
Please note that not all integrations will be shipped with svcs proper once it is stable. Some will be moved to separate packages and Pyramid is a prime contender for that.
23.9.0 - 2023-08-06
Container.get()
andContainer.aget()
now have type hints that only work with concrete classes but allow for type checking without repeating yourself. If you want to use abstract classes liketyping.Protocol
or ABCs, you can useContainer.get_abstract()
andContainer.aget_abstract()
instead.
-
Container.get_abstract()
andContainer.aget_abstract()
. They behave likeContainer.get()
andContainer.aget()
before. -
It is now possible to check if a service type is registered with a
Registry
by usingin
. -
It is now possible to check if a service type has a cached instance within a
Container
by usingin
. -
Registry
andContainer
are now also an (async) context managers that callclose()
/aclose()
on exit automatically.
23.8.0 - 2023-08-04
- It's now possible to request multiple services at once by passing multiple types to
Container.get()
andContainer.aget()
. #15
23.7.0 - 2023-08-02
- Factories now may take a parameter called
svcs_container
or that is annotated to besvcs.Container
. In this case the factory will receive the current container as a first positional argument. This allows for recursive factories without global state. #10
23.6.0 - 2023-07-31
- Renamed
Container.forget_service_type()
toContainer.forget_about()
.
svcs.flask.init_app()
's type hints now take into account customflask.Flask
subclasses.
23.5.0 - 2023-07-26
- Registered factory/value clean up!
It is now possible to register an
on_registry_close
hook that is called once theRegistry
's(a)close()
method is called.
23.4.0 - 2023-07-24
- Renamed from svc-reg to svcs. Sadly the more obvious names are all taken.
23.3.0 - 2023-07-20
- Async method
Container.aget()
. This was necessary for generator-based cleanups. It works with sync factories too, so you can use it universally in async code. - Async method
ServicePing.aping()
. It works with sync factories and pings too, so you can use it universally in async code. #4
- Switched the cleanup mechanism from passing a function to allowing the factory to be a generator that yields the service and can clean up after the
yield
. Just like Pytest fixtures. #3
23.2.0 - 2023-07-13
-
Container.cleanup()
andContainer.acleanup
have been renamed toclose()
andaclose()
respectively. -
The clean up methods are now more resilient by catching and logging exceptions at
warning
level. That means that if the first clean up method fails, the second one will still be called. -
svcs.flask.register_(factory|value)
now take the current Flask application as first argument. The old behavior moved tosvcs.flask.replace_(factory|value)
.The former requires no application context (and thusly be used in
init_app()
-style initializers) while the latter does require an application context and can be used to "monkey-patch" an existing application in tests.
23.1.0 - 2023-07-12
- Initial release.