NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #1867 most downloaded on PyPI
A Flexible Service Locator
Last release 1 months ago
24 Aug 2026
Ships unpredictably
gaps range from 8 days to 1.5 years
Nearly every release is documented
notes for 22 of 22 stable releases
Nothing withdrawn
no release was ever pulled
3 years old
24 releases · first in 2023
One column per quarter.
This one drops direct Pyramid support in lieu of the svcs-pyramid package (migration is just adding a dependency and replacing a dot with an underscor
This one drops direct Pyramid support in lieu of the svcs-pyramid package (migration is just adding a dependency and replacing a dot with an underscore), adds registry access to FastAPI integration (which turns out to be very useful for testing), and makes auto-wiring of ForwardRefs on Python 3.14+ more robust.
Full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), Kraken Tech (@kraken-tech), Klaviyo (@klaviyo), Privacy Solutions GmbH (@privacy-solutions), FilePreviews (@filepreviews), TestMu AI Open Source Office (Formerly LambdaTest) (@LambdaTest-Inc), Todoist Inc (@Doist), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Buttondown (@buttondown), Christopher Dignam (@chdsbd), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Rivo Laks (@rivol), Polar (@polarsource), Mike Fiedler (@miketheman), Duncan Hill (@cricalix), Colin Marquardt (@cmarqu), Nick Libertini (@libertininick), Brian M. Dennis (@crossjam), Al Sweigart (@asweigart), Celebrity News AG (@celebritynewsag), The Westervelt Company (@westerveltco), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Thomas Klinger (@thmsklngr), Andreas Poehlmann (@ap--), August Trapper Bigelow (@atbigelow), Carlton Gibson (@carltongibson), and Roboflow (@roboflow).
The Pyramid integration. Maintenance within the main package has become too much of a burden, so it is now lives in its own package called svcs-pyramid.
Migrating is a pure rename: install svcs-pyramid, then replace import svcs with import svcs_pyramid and svcs.pyramid.foo() with svcs_pyramid.foo(). The registry and the container are still stored under the same keys, so nothing else changes. We will try to continue its maintenance for as long as its feasible.
This is in line with our backwards-compatibility policy that has a provision for this exact case. #180
FastAPI: svcs.fastapi.get_registry() returns the registry of a running application, either from the application object itself or from a test client wrapping it. The new svcs.fastapi.DepRegistry dependency (based on svcs.fastapi.registry()) injects it into views. #186
Starlette: svcs.starlette.get_registry() returns the registry of a running application, either from the application object itself or from a test client wrapping it. #186
typing.ForwardRef annotations before service lookup. #187This release contains contributions from @hynek, and @wbolster.
You can verify this release's artifact attestions using GitHub's CLI tool by downloading the sdist and wheel from PyPI and running:
$ gh attestation verify --owner hynek svcs-26.2.0.tar.gzand
$ gh attestation verify --owner hynek svcs-26.2.0-py3-none-any.whlsvcs.get_abstract() and all its *_abstract() siblings. Thanks to PEP 747 they are no longer necessary. No deprecation warnings or plans to remove them…
In the spirit of "we do these things not because they are easy, but because we thought they were going to be easy" this release brings a great new feature: autowiring!
It resolves dependencies based on type annotations and allows you to shed boilerplate when registering nested services. Check out its docs for details!
Since this release took way too long (to the point of leaving another great feature for later 😢), there's a too many features and bug fixes to list, so check out the full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), Kraken Tech (@kraken-tech), Klaviyo (@klaviyo), Privacy Solutions GmbH (@privacy-solutions), FilePreviews (@filepreviews), Ecosystems (@ecosyste-ms), Doist (@Doist), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Buttondown (@buttondown), Christopher Dignam (@chdsbd), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Rivo Laks (@rivol), Polar (@polarsource), Mike Fiedler (@miketheman), Duncan Hill (@cricalix), Colin Marquardt (@cmarqu), Pieter Swinkels (@swinkels), Nick Libertini (@libertininick), Brian M. Dennis (@crossjam), Al Sweigart (@asweigart), Celebrity News AG (@celebritynewsag), The Westervelt Company (@westerveltco), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Thomas Klinger (@thmsklngr), Andreas Poehlmann (@ap--), August Trapper Bigelow (@atbigelow), Carlton Gibson (@carltongibson), and Roboflow (@roboflow).
svcs.get_abstract() and all its *_abstract() siblings. Thanks to PEP 747 they are no longer necessary. No deprecation warnings or plans to remove them for now.Python 3.14 and 3.15 support.
PEP 747 support, aka typing.TypeForm. This means that it's now possible use abstract types like Protocols or abstract base classes for registered services, removing an important typing caveat. This change introduces a dependency on typing-extensions for Python 3.14 and earlier.
Note: On Mypy versions older than 2.2, users that want to take advantage of this must pass the --enable-incomplete-feature=TypeForm argument.
New svcs.autowire() and svcs.aautowire() that can be used to automatically resolve dependencies based on type annotations. #167
New suppress_context_exit argument to svcs.register_(factory|value)(). If set to False, errors in the container context will be passed into the factory cleanup context manager and allow you to act on them there.
You can't stop the exception from bubbling out of the container context, though. #129
Debug logs don't contain stack information anymore since it leads to excessive output. If you miss them, please open an issue and we make it an option on svcs.Registry. #135 #139
Python 3.9 support. #152
Container.get_pings() now includes registry-local services. Locally defined services overwrite global ones if they are registered for the same type. This includes that a local service without a ping disables a global service's ping. #83Factories now can return MagicMocks without crashing with a TypeError. #137
AIOHTTP: The container is now stored using aiohttp.web.RequestKeys on the application. This is an implementation detail and shouldn't matter, but it fixes a warning on AIOHTTP 3.14 and later.
This release contains contributions from @g0di, @hynek, @lewicki, and @vladfedoriuk.
You can verify this release's artifact attestions using GitHub's CLI tool by downloading the sdist and wheel from PyPI and running:
$ gh attestation verify --owner hynek svcs-26.1.0.tar.gzand
$ gh attestation verify --owner hynek svcs-26.1.0-py3-none-any.whlThis is an implementation detail. If you are directly accessing the registry via app.config , this is a breaking change, though you should ideally mov…
This is mostly a grab bag of smaller features and bug fixes that I've been carrying around forever, because I was getting ready to tackle a bunch of bigger projects like multiple scopes soon™. I'm still getting ready, but I don't want to withhold these improvements any further.
Full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), Klaviyo (@klaviyo), FilePreviews (@filepreviews), Privacy Solutions GmbH (@privacy-solutions), Gedankenspieler (@Gedankenspieler), Daniel Fortunov (@asqui), Kevin P. Fleming (@kpfleming), and Polar (@polarsource).
Buttondown (@buttondown), Christopher Dignam (@chdsbd), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Chris Withers (@cjw296), Mike Fiedler (@miketheman), Duncan Hill (@cricalix), Colin Marquardt (@cmarqu), Pieter Swinkels (@swinkels), Nick Libertini (@libertininick), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), The Westervelt Company (@westerveltco), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Thomas Klinger (@thmsklngr), Andreas Poehlmann (@ap--), August Bigelow (@atbigelow), Carlton Gibson (@carltongibson), and Roboflow (@roboflow).
Not to forget 11 more amazing humans who chose to be generous but anonymous!
Python 3.13 support.
svcs.Registry now implements a __iter__ method that allows to iterate over its registered services. #106
Flask: The registry is now stored on app.extensions, not app.config. This is an implementation detail. If you are directly accessing the registry via app.config, this is a breaking change, though you should ideally move to svcs.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
This release contains contributions from @alexrudy, @hynek, and @wbolster.
You can verify this release's artifact attestions using GitHub's CLI tool by downloading the sdist and wheel from PyPI and running:
$ gh attestation verify --owner hynek svcs-25.1.0.tar.gzand
$ gh attestation verify --owner hynek svcs-25.1.0-py3-none-any.whlThis is just a quick fix for a warning in AIOHTTP 3.9+.
This is just a quick fix for a warning in AIOHTTP 3.9+.
Full changelog below!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), FilePreviews (@filepreviews), Daniel Fortunov (@asqui), Kevin P. Fleming (@kpfleming), and Sören Weber (@SoerenWeber).
Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), Stefan Hagen (@sthagen), Sławomir Ehlert (@slafs), Mostafa Khalil (@khadrawy), Filip Mularczyk (@mukiblejlok), Mike Fiedler (@miketheman), and Michel Vittória (@michelvittoria).
Not to forget 5 more amazing humans who chose to be generous but anonymous!
aiohttp.web.AppKeys on the application. This is an implementation detail and shouldn't matter, but it fixes a warning on AIOHTTP 3.9 and later.The main feature of this release are container-local registries that allow you to define factories that are only valid for a container. For example, b
The main feature of this release are container-local registries that allow you to define factories that are only valid for a container. For example, based on request metadata! Check out the docs for details.
There's one minor backward-incompatible change regarding the handling of values and context managers – please refer to #50 for the reasons.
Finally there's a bunch of fixes around typing.
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), FilePreviews (@filepreviews), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), Stefan Hagen (@sthagen), Sławomir Ehlert (@slafs), and Mostafa Khalil (@khadrawy).
Not to forget 5 more amazing humans who chose to be generous but anonymous!
Backwards-Incompatible: Since multiple people have been bit by the enter=True default for Registry.register_value(), and it's very early in svcs life, we're changing the default to enter=False for all versions of register_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() and svcs.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 a werkzeug.local.LocalProxy for the currently active registry on flask.current_app.
I'm very proud to hereby announce the first stable version of svcs .
I'm very proud to hereby announce the first stable version of svcs.
I'm both excited and anxious how it's going to be received.
Please give it a shot and let me know!
This release would not be possible without my generous sponsors! Thank you to all of you making sustainable maintenance possible! If you would like to join them, go to https://github.com/sponsors/hynek and check out the sweet perks!
Variomedia AG (@variomedia), Tidelift (@tidelift), Sentry (@getsentry), HiredScore (@HiredScore), FilePreviews (@filepreviews), Daniel Fortunov (@asqui), and Kevin P. Fleming (@kpfleming).
Adam Hill (@adamghill), Dan Groshev (@si14), Magnus Watn (@magnuswatn), David Cramer (@dcramer), Moving Content AG (@moving-content), Stein Magnus Jodal (@jodal), ProteinQure (@ProteinQure), Jesse Snyder (@jessesnyder), Rivo Laks (@rivol), Tom Ballinger (@thomasballinger), Ionel Cristian Mărieș (@ionelmc), The Westervelt Company (@westerveltco), Philippe Galvan (@PhilippeGalvan), Birk Jernström (@birkjernstrom), Tim Schilling (@tim-schilling), Chris Withers (@cjw296), Christopher Dignam (@chdsbd), and Stefan Hagen (@sthagen).
Not to forget 3 more amazing humans who chose to be generous but anonymous!
Backwards-compatibility! svcs is pronounced stable now -- no more rug-pulls planned! #36
Flask: svcs.flask.container which is a werkzeug.local.LocalProxy (like, for example, flask.current_app) and is the currently active container when accessed within a request context.
Just internal re-jiggerings.
Just internal re-jiggerings.
With Starlette successfully added, I hope svcs is now feature-complete and won't need any bigger changes in the future.
With Starlette successfully added, I hope svcs is now feature-complete and won't need any bigger changes in the future.
I suspect it can be proclaimed stable soon, just when I realize how to market it…
Flask: svcs.flask.get_registry().
Starlette integration. #31
FastAPI integration has landed in #30 !
FastAPI integration has landed in #30!
enter keyword argument to all register_(value|factory)() . It prevents svcs from entering context managers if the factory returns one. This is useful
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 named svcs_container or that is annotated as being svcs.Container and has any name.
A ResourceWarning is now raised when a container or a registry are garbage-collected with pending cleanups.
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. #92
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 use svcs.pyramid.from_svcs(None) to obtain the currently active container.
Flask: replace_(value|factory)() is now called overwrite_(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 using svcs.Container.(a)close().aiohttp: missing aget_abstract() function.
aget_abstract() function.get_pings() function.AIOHTTP integration has landed!
AIOHTTP integration has landed!
*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 le
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 of Any.
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).
Factory results of None are now treated like every other result and cached. \#22
Proper documentation at ! I guess it's getting serious. #17
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.
This is a huge release as far as breaking changes go and I hope the last one of such sorts.
This is a huge release as far as breaking changes go and I hope the last one of such sorts.
There has always been the conundrum imposed by Python typing whether we a) support automatic type-deduction of the services or b) support abstract classes (ABCs, Protocols).
After lots of waffling, I've gone for a compromise: Container.(a)get() are now properly typed as type[T] -> T (for up to 10 services at once 😅). If you want T to be abstract (until Python typing changes its stance), you'll have to use Container.(a)get_abstract(). The nice thing is that this separation serves more people and will be fully backwards-compatible, if we ever don't need it anymore.
Please see https://github.com/hynek/svcs#typing-caveats for more details and a more verbose explanation of the problem if you don't understand what any of the above means.
Container.get() and Container.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 like typing.Protocol or ABCs, you can use Container.get_abstract() and Container.aget_abstract() instead.Container.get_abstract() and Container.aget_abstract().
They behave like Container.get() and Container.aget() before.
It is now possible to check if a service type is registered with a Registry by using in.
It is now possible to check if a service type has a cached instance within a Container by using in.
Registry and Container are now also an (async) context managers that call close() / aclose() on exit automatically.
It's now possible to request multiple services at once by passing multiple types to Container.get() and Container.aget(). #15
Container.get() and Container.aget().
#15Factories now may take a parameter called svcs_container or that is annotated to be svcs.Container. In this case the factory will receive the current
svcs_container or that is annotated to be svcs.Container.
In this case the factory will receive the current container as a first positional argument.
This allows for recursive factories without global state.
#10Renamed Container.forget_service_type() to Container.forget_about().
Warning Still in RFC mode!
Container.forget_service_type() to Container.forget_about().svcs.flask.init_app()'s type hints now take into account custom flask.Flask subclasses.Still, RFC-mode, added registry-level cleanups which allows to clean up pools automatically.
Still, RFC-mode, added registry-level cleanups which allows to clean up pools automatically.
on_registry_close hook that is called once the Registry's (a)close() method is called.The only difference is that we've rebranded ourselves to *svcs* which is easier to type and leaves back the "registry" baggage.
The only difference is that we've rebranded ourselves to svcs which is easier to type and leaves back the "registry" baggage.
I'm still open to better names before we go official tho!
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →