From 2cff4c4c7045f17b3f795cf18c9419a4e7b4bae2 Mon Sep 17 00:00:00 2001 From: Hynek Schlawack Date: Mon, 24 Jul 2023 18:16:45 +0200 Subject: [PATCH] Re-brand while we can --- .github/CONTRIBUTING.md | 2 +- .github/workflows/ci.yml | 4 +- CHANGELOG.md | 18 +++---- README.md | 61 ++++++++++++--------- conftest.py | 6 +-- docs/_static/logo.svg | 1 + pyproject.toml | 16 +++--- src/{svc_reg => svcs}/__init__.py | 0 src/{svc_reg => svcs}/_core.py | 0 src/{svc_reg => svcs}/exceptions.py | 0 src/{svc_reg => svcs}/flask.py | 8 +-- src/{svc_reg => svcs}/py.typed | 0 tests/test_core.py | 16 +++--- tests/test_flask.py | 84 ++++++++++++++--------------- tests/typing/core.py | 10 ++-- tests/typing/flask_.py | 30 +++++------ 16 files changed, 133 insertions(+), 123 deletions(-) create mode 100644 docs/_static/logo.svg rename src/{svc_reg => svcs}/__init__.py (100%) rename src/{svc_reg => svcs}/_core.py (100%) rename src/{svc_reg => svcs}/exceptions.py (100%) rename src/{svc_reg => svcs}/flask.py (89%) rename src/{svc_reg => svcs}/py.typed (100%) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ccc11ea..b1edebc 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing Guide -Please, for now, only contribute in the form of [feedback](https://github.com/hynek/svc-reg/discussions/1) and/or [issues](https://github.com/hynek/svc-reg/issues). +Please, for now, only contribute in the form of [feedback](https://github.com/hynek/svcs/discussions/1) and/or [issues](https://github.com/hynek/svcs/issues). The project is not ready for code contributions yet. Yes, I still use `git push --force` occasionally. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8710e19..910dc59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,8 +151,8 @@ jobs: python-version-file: .python-version-default - run: python -Im pip install -e .[dev] - - run: python -Ic 'import svc_reg' - - run: python -Ic 'import svc_reg.flask' + - run: python -Ic 'import svcs' + - run: python -Ic 'import svcs.flask' # Ensure everything required is passing for branch protection. required-checks-pass: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7890376..fe55978 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,15 +8,15 @@ 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](https://github.com/hynek/svc-reg/blob/main/.github/SECURITY.md). +You can find our backwards-compatibility policy [here](https://github.com/hynek/svcs/blob/main/.github/SECURITY.md). -## [Unreleased](https://github.com/hynek/svc-reg/compare/23.3.0...HEAD) +## [Unreleased](https://github.com/hynek/svcs/compare/23.3.0...HEAD) -## [23.3.0](https://github.com/hynek/svc-reg/compare/23.2.0...23.3.0) - 2023-07-20 +## [23.3.0](https://github.com/hynek/svcs/compare/23.2.0...23.3.0) - 2023-07-20 ### Added @@ -25,29 +25,29 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/ 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](https://github.com/hynek/svc-reg/pull/4) + [#4](https://github.com/hynek/svcs/pull/4) ### Changed - Switched the cleanup mechanism from passing a function to allowing the factory to be a generator that yields the resource and can clean up after the `yield`. Just like Pytest fixtures. - [#3](https://github.com/hynek/svc-reg/pull/3) + [#3](https://github.com/hynek/svcs/pull/3) -## [23.2.0](https://github.com/hynek/svc-reg/compare/23.1.0...23.2.0) - 2023-07-13 +## [23.2.0](https://github.com/hynek/svcs/compare/23.1.0...23.2.0) - 2023-07-13 ### Changed - `Container.cleanup()` and `Container.acleanup` have been renamed to `close()` and `aclose()` 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. -- `svc_reg.flask.register_(factory|value)` now take the current Flask application as first argument. - The old behavior moved to `svc_reg.flask.replace_(factory|value)`. +- `svcs.flask.register_(factory|value)` now take the current Flask application as first argument. + The old behavior moved to `svcs.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](https://github.com/hynek/svc-reg/tree/23.1.0) - 2023-07-12 +## [23.1.0](https://github.com/hynek/svcs/tree/23.1.0) - 2023-07-12 - Initial release. diff --git a/README.md b/README.md index 8ce16f1..c3d8864 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,34 @@ -# A Service Locator for Python +

+

+ + svcs + +

+

+ + +## A Service Locator for Python > **Warning** > ☠️ Not ready yet! ☠️ > -> This project is only public to [gather feedback](https://github.com/hynek/svc-reg/discussions), and everything can and will change until the project is proclaimed stable. +> This project is only public to [gather feedback](https://github.com/hynek/svcs/discussions), and everything can and will change until the project is proclaimed stable. > > Currently only [**Flask** support](#flask) is production-ready, but API details can still change. > > At this point, it's unclear whether this project will become a "proper Hynek project". > I will keep using it for my work projects, but whether this will grow beyond my personal needs depends on community interest. -*svc-reg* is a [service locator](https://en.wikipedia.org/wiki/Service_locator_pattern) for Python. +*svcs* (pronounced *services*) is a [service locator](https://en.wikipedia.org/wiki/Service_locator_pattern) for Python. It provides you with a central place to register factories for types/interfaces and then imperatively request instances of those types with **automatic cleanup** and **health checks**. **This allows you to configure and manage resources in *one central place* and access them all in a *consistent* way.** --- -In practice that means that at runtime, you say "*Give me a database connection*!", and *svc-reg* will give you whatever you've configured it to return when asked for a database connection. +In practice that means that at runtime, you say "*Give me a database connection*!", and *svcs* will give you whatever you've configured it to return when asked for a database connection. This can be an actual database connection or it can be a mock object for testing. If you like the [*Dependency Inversion Principle*](https://en.wikipedia.org/wiki/Dependency_inversion_principle) (aka "*program against interfaces, not implementations*"), you would register concrete factories for abstract interfaces; in Python usually a [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol) or an [Abstract Base Class](https://docs.python.org/3.11/library/abc.html). @@ -63,7 +72,7 @@ def engine_factory(): with engine.connect() as conn: yield conn -registry = svc_reg.Registry() +registry = svcs.Registry() registry.register_factory(Connection, engine_factory) ``` @@ -71,7 +80,7 @@ The generator-based setup and cleanup may remind you of [Pytest fixtures](https: Unlike typical dependency injection that passes your dependencies as arguments, the active obtainment of resources by calling `get()` when you *know* you're going to need it avoids the conundrum of either having to pass a factory (e.g., a connection pool -- which also puts the onus of cleanup on you), or eagerly creating resources that are never used. -*svc-reg* comes with **full async** support via a-prefixed methods (i.e. `aget()` instead of `get()`, et cetera). +*svcs* comes with **full async** support via a-prefixed methods (i.e. `aget()` instead of `get()`, et cetera). @@ -80,7 +89,7 @@ Unlike typical dependency injection that passes your dependencies as arguments, You're unlikely to use the core API directly, but knowing what's happening underneath is good to dispel any concerns about magic. -*svc-reg* has two essential concepts: +*svcs* has two essential concepts: ### Registries @@ -92,10 +101,10 @@ Its only job is to store and retrieve factories. It is possible to register either factory callables or values: ```python ->>> import svc_reg +>>> import svcs >>> import uuid ->>> reg = svc_reg.Registry() +>>> reg = svcs.Registry() >>> reg.register_factory(uuid.UUID, uuid.uuid4) >>> reg.register_value(str, "Hello World") @@ -111,7 +120,7 @@ But the types must be *hashable* because they're used as keys in a lookup dictio A **`Container`** belongs to a Registry and allows to create instances of the registered types, taking care of their life-cycle: ```python ->>> container = svc_reg.Container(reg) +>>> container = svcs.Container(reg) >>> u = container.get(uuid.UUID) >>> u @@ -172,19 +181,19 @@ On the other hand, the `Container` object should live on a request-scoped object ## Flask -*svc-reg* has grown from my frustration with the repetitiveness of using the `get_x` that creates an `x` and then stores it on the `g` object [pattern](https://flask.palletsprojects.com/en/latest/appcontext/#storing-data). +*svcs* has grown from my frustration with the repetitiveness of using the `get_x` that creates an `x` and then stores it on the `g` object [pattern](https://flask.palletsprojects.com/en/latest/appcontext/#storing-data). -Therefore it comes with Flask support out of the box in the form of the `svc_reg.flask` module. +Therefore it comes with Flask support out of the box in the form of the `svcs.flask` module. It: -- puts the registry into `app.config["svc_registry"]`, +- puts the registry into `app.config["svcsistry"]`, - unifies the putting and caching of services on the `g` object by putting a container into `g.svc_container`, - transparently retrieves them from there for you, - and installs a [`teardown_appcontext()`](http://flask.pocoo.org/docs/latest/api#flask.Flask.teardown_appcontext) handler that calls `close()` on the container when a request is done. --- -You can add support for *svc-reg* by calling `svc_reg.flask.init_app(app)` in your [*application factory*](https://flask.palletsprojects.com/en/latest/patterns/appfactories/). +You can add support for *svcs* by calling `svcs.flask.init_app(app)` in your [*application factory*](https://flask.palletsprojects.com/en/latest/patterns/appfactories/). For instance, to create a factory that uses a SQLAlchemy engine to produce connections, you could do this: ```python @@ -192,7 +201,7 @@ from flask import Flask from sqlalchemy import Connection, create_engine from sqlalchemy.sql import text -import svc_reg +import svcs def create_app(config_filename): @@ -202,7 +211,7 @@ def create_app(config_filename): ########################################################################## # Set up the registry using Flask integration. - app = svc_reg.flask.init_app(app) + app = svcs.flask.init_app(app) # Now, register a factory that calls `engine.connect()` if you ask for a # `Connection`. Since we use yield inside of a context manager, the @@ -215,7 +224,7 @@ def create_app(config_filename): yield conn ping = text("SELECT 1") - svc_reg_flask.register_factory( + svcs_flask.register_factory( # The app argument makes it good for custom init_app() functions. app, Connection, @@ -223,13 +232,13 @@ def create_app(config_filename): ping=lambda conn: conn.execute(ping) ) - # You also use svc_reg WITHIN factories: - svc_reg_flask.register_factory( + # You also use svcs WITHIN factories: + svcs_flask.register_factory( app, # <--- AbstractRepository, # No cleanup, so we just return an object using a lambda lambda: Repository.from_connection( - svc_reg.flask.get(Connection) + svcs.flask.get(Connection) ), ) ########################################################################## @@ -244,7 +253,7 @@ Now you can request the `Connection` object in your views: ```python @app.get("/") def index() -> flask.ResponseValue: - conn: Connection = svc_reg.flask.get(Connection) + conn: Connection = svcs.flask.get(Connection) ``` If you have a [health endpoint](https://kubernetes.io/docs/reference/using-api/health-checks/), it could look like this: @@ -259,7 +268,7 @@ def healthy() -> flask.ResponseValue: failing: list[dict[str, str]] = [] code = 200 - for svc in svc_reg.flask.get_pings(): + for svc in svcs.flask.get_pings(): try: svc.ping() ok.append(svc.name) @@ -312,7 +321,7 @@ In practice, you can simplify/beautify the code within your views by creating a Say this is `app/services.py`: ```python -from svc_reg.flask import ( +from svcs.flask import ( get, get_pings, init_app, @@ -373,19 +382,19 @@ If types are more important to you than a unified interface, you can always wrap ```python def get_connection() -> Connection: - return svc_reg.flask.get(Connection) + return svcs.flask.get(Connection) ``` Or, if you don't care about `Protocols`: ```python def get(svc_type: type[T]) -> T: - return svc_reg.flask.get(svc_type) + return svcs.flask.get(svc_type) ``` ## Credits -*svc-reg* is written by [Hynek Schlawack](https://hynek.me/) and distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. +*svcs* is written by [Hynek Schlawack](https://hynek.me/) and distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. The development is kindly supported by my employer [Variomedia AG](https://www.variomedia.de/) and all my amazing [GitHub Sponsors](https://github.com/sponsors/hynek). diff --git a/conftest.py b/conftest.py index c704a59..de0f06e 100644 --- a/conftest.py +++ b/conftest.py @@ -9,7 +9,7 @@ from sybil import Sybil from sybil.parsers.rest import DocTestParser, PythonCodeBlockParser -import svc_reg +import svcs pytest_collect_file = Sybil( @@ -23,9 +23,9 @@ @pytest.fixture(name="registry") def _registry(): - return svc_reg.Registry() + return svcs.Registry() @pytest.fixture(name="container") def _container(registry): - return svc_reg.Container(registry) + return svcs.Container(registry) diff --git a/docs/_static/logo.svg b/docs/_static/logo.svg new file mode 100644 index 0000000..097add5 --- /dev/null +++ b/docs/_static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 5041065..20ecb1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] dynamic = ["version", "readme"] -name = "svc-reg" +name = "svcs" description = "A Service Locator for Python" requires-python = ">=3.8" license = "MIT" @@ -26,12 +26,12 @@ dependencies = ["attrs"] [project.optional-dependencies] tests = ["pytest", "pytest-asyncio", "sybil"] typing = ["mypy>=1.4", "flask"] -dev = ["svc-reg[tests,typing]", "tox>4", "flask"] +dev = ["svcs[tests,typing]", "tox>4", "flask"] [project.urls] -Changelog = "https://github.com/hynek/svc-reg/blob/main/CHANGELOG.md" -Documentation = "https://github.com/hynek/svc-reg/blob/main/README.md" -Source = "https://github.com/hynek/svc-reg" +Changelog = "https://github.com/hynek/svcs/blob/main/CHANGELOG.md" +Documentation = "https://github.com/hynek/svcs/blob/main/README.md" +Source = "https://github.com/hynek/svcs" Funding = "https://github.com/sponsors/hynek" @@ -52,7 +52,7 @@ text = """ --- -For now, please refer to the [GitHub README](https://github.com/hynek/svc-reg/blob/main/README.md) for latest documentation. +For now, please refer to the [GitHub README](https://github.com/hynek/svcs/blob/main/README.md) for latest documentation. ## Release Information @@ -68,7 +68,7 @@ pattern = "\n(###.+?\n)## " text = """ --- -[→ Full Changelog](https://github.com/hynek/svc-reg/blob/main/CHANGELOG.md) +[→ Full Changelog](https://github.com/hynek/svcs/blob/main/CHANGELOG.md) """ @@ -87,7 +87,7 @@ filterwarnings = ["once::Warning"] [tool.coverage.run] branch = true parallel = true -source = ["svc_reg"] +source = ["svcs"] [tool.coverage.paths] source = ["src", ".tox/py*/**/site-packages"] diff --git a/src/svc_reg/__init__.py b/src/svcs/__init__.py similarity index 100% rename from src/svc_reg/__init__.py rename to src/svcs/__init__.py diff --git a/src/svc_reg/_core.py b/src/svcs/_core.py similarity index 100% rename from src/svc_reg/_core.py rename to src/svcs/_core.py diff --git a/src/svc_reg/exceptions.py b/src/svcs/exceptions.py similarity index 100% rename from src/svc_reg/exceptions.py rename to src/svcs/exceptions.py diff --git a/src/svc_reg/flask.py b/src/svcs/flask.py similarity index 89% rename from src/svc_reg/flask.py rename to src/svcs/flask.py index f5a707f..99a0a72 100644 --- a/src/svc_reg/flask.py +++ b/src/svcs/flask.py @@ -16,7 +16,7 @@ def init_app(app: Flask, registry: Registry | None = None) -> Flask: if registry is None: registry = Registry() - app.config["svc_registry"] = registry + app.config["svcsistry"] = registry app.teardown_appcontext(teardown) return app @@ -40,7 +40,7 @@ def register_factory( *, ping: Callable | None = None, ) -> None: - app.config["svc_registry"].register_factory(svc_type, factory, ping=ping) + app.config["svcsistry"].register_factory(svc_type, factory, ping=ping) def register_value( @@ -50,7 +50,7 @@ def register_value( *, ping: Callable | None = None, ) -> None: - app.config["svc_registry"].register_value(svc_type, instance, ping=ping) + app.config["svcsistry"].register_value(svc_type, instance, ping=ping) def replace_factory( @@ -95,7 +95,7 @@ def teardown(exc: BaseException | None) -> None: def _ensure_req_data() -> tuple[Registry, Container]: - registry: Registry = current_app.config["svc_registry"] + registry: Registry = current_app.config["svcsistry"] if "svc_container" not in g: g.svc_container = Container(registry) diff --git a/src/svc_reg/py.typed b/src/svcs/py.typed similarity index 100% rename from src/svc_reg/py.typed rename to src/svcs/py.typed diff --git a/tests/test_core.py b/tests/test_core.py index 6542185..b01ed80 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8,7 +8,7 @@ import pytest -import svc_reg +import svcs class Service: @@ -25,7 +25,7 @@ class YetAnotherService: @pytest.fixture(name="rs") def _rs(svc): - return svc_reg.RegisteredService(Service, Service, None) + return svcs.RegisteredService(Service, Service, None) @pytest.fixture(name="svc") @@ -60,7 +60,7 @@ def test_get_not_found(self, container): """ Asking for a service that isn't registered raises a ServiceNotFoundError. """ - with pytest.raises(svc_reg.exceptions.ServiceNotFoundError) as ei: + with pytest.raises(svcs.exceptions.ServiceNotFoundError) as ei: container.get(Service) assert Service is ei.value.args[0] @@ -226,8 +226,8 @@ async def factory_cleanup(): await asyncio.sleep(0) yield 42 - assert svc_reg.RegisteredService(object, factory, None).is_async - assert svc_reg.RegisteredService( + assert svcs.RegisteredService(object, factory, None).is_async + assert svcs.RegisteredService( object, factory_cleanup, None ).is_async @@ -242,8 +242,8 @@ def factory(): def factory_cleanup(): yield 42 - assert not svc_reg.RegisteredService(object, factory, None).is_async - assert not svc_reg.RegisteredService( + assert not svcs.RegisteredService(object, factory, None).is_async + assert not svcs.RegisteredService( object, factory_cleanup, None ).is_async @@ -254,7 +254,7 @@ def test_name(self, rs): The name property proxies the correct class name. """ - assert "Service" == svc_reg.ServicePing(None, rs).name + assert "Service" == svcs.ServicePing(None, rs).name def test_ping(self, registry, container): """ diff --git a/tests/test_flask.py b/tests/test_flask.py index ce52b17..692f3bd 100644 --- a/tests/test_flask.py +++ b/tests/test_flask.py @@ -6,13 +6,13 @@ import pytest -import svc_reg +import svcs try: import flask - from svc_reg.flask import teardown + from svcs.flask import teardown except ImportError: pytest.skip("Flask not installed", allow_module_level=True) @@ -24,14 +24,14 @@ def _app(): @pytest.fixture(name="clean_app_ctx") def _clean_app_ctx(registry, app): - svc_reg.flask.init_app(app, registry) + svcs.flask.init_app(app, registry) with app.app_context() as ctx: yield ctx @pytest.fixture(name="container") def _container(clean_app_ctx): - return svc_reg.flask._ensure_req_data()[1] + return svcs.flask._ensure_req_data()[1] class Interface: @@ -65,10 +65,10 @@ def factory2(): cleanup2() registry.register_factory(Service1, factory1) - svc_reg.flask.replace_factory(Service2, factory2) + svcs.flask.replace_factory(Service2, factory2) - svc1 = svc_reg.flask.get(Service1) - svc2 = svc_reg.flask.get(Service2) + svc1 = svcs.flask.get(Service1) + svc2 = svcs.flask.get(Service2) assert isinstance(svc1, Service1) assert isinstance(svc2, Service2) @@ -85,59 +85,59 @@ def test_overwrite_value(self, registry): """ registry.register_value(Interface, Interface(), ping=lambda _: None) - assert isinstance(svc_reg.flask.get(Interface), Interface) + assert isinstance(svcs.flask.get(Interface), Interface) - svc_reg.flask.replace_value(Interface, Service2()) + svcs.flask.replace_value(Interface, Service2()) - assert isinstance(svc_reg.flask.get(Interface), Service2) - assert [] == svc_reg.flask.get_pings() + assert isinstance(svcs.flask.get(Interface), Service2) + assert [] == svcs.flask.get_pings() def test_overwrite_factory(self, container): """ It's possible to overwrite an already registered type using a factory. """ - svc_reg.flask.replace_value( + svcs.flask.replace_value( Interface, Interface(), ping=lambda _: None ) - assert isinstance(svc_reg.flask.get(Interface), Interface) + assert isinstance(svcs.flask.get(Interface), Interface) - svc_reg.flask.replace_factory(Interface, Service2) + svcs.flask.replace_factory(Interface, Service2) - assert isinstance(svc_reg.flask.get(Interface), Service2) - assert [] == svc_reg.flask.get_pings() + assert isinstance(svcs.flask.get(Interface), Service2) + assert [] == svcs.flask.get_pings() def test_cache(self): """ Getting a service twice within the same request returns the same object. """ - svc_reg.flask.replace_factory(Interface, Service1) + svcs.flask.replace_factory(Interface, Service1) - assert svc_reg.flask.get(Interface) is svc_reg.flask.get(Interface) + assert svcs.flask.get(Interface) is svcs.flask.get(Interface) def test_not_found(self): """ Trying to get a service that hasn't been registered raises a ServiceNotFoundError. """ - with pytest.raises(svc_reg.exceptions.ServiceNotFoundError): - svc_reg.flask.get(Interface) + with pytest.raises(svcs.exceptions.ServiceNotFoundError): + svcs.flask.get(Interface) def test_get_pingeable(self): """ - get_pingable returns only pingable svc_reg. + get_pingable returns only pingable svcs. """ - svc_reg.flask.replace_factory(Service1, Service1) - svc_reg.flask.replace_factory(Service2, Service2, ping=lambda _: None) + svcs.flask.replace_factory(Service1, Service1) + svcs.flask.replace_factory(Service2, Service2, ping=lambda _: None) assert [Service2] == [ - ping._rs.svc_type for ping in svc_reg.flask.get_pings() + ping._rs.svc_type for ping in svcs.flask.get_pings() ] def test_cleanup_purge_tolerant(self, container): """ - If other svc_reg are registered, they are ignored by the cleanup + If other svcs are registered, they are ignored by the cleanup purge. """ @@ -147,18 +147,18 @@ def factory1(): def factory2(): yield Service2() - svc_reg.flask.replace_factory(Service1, factory1) - svc_reg.flask.replace_factory(Service2, factory2) + svcs.flask.replace_factory(Service1, factory1) + svcs.flask.replace_factory(Service2, factory2) - svc_reg.flask.get(Service1) - svc_reg.flask.get(Service2) + svcs.flask.get(Service1) + svcs.flask.get(Service2) assert 2 == len(container.cleanups) - svc_reg.flask.replace_factory(Service1, Interface) + svcs.flask.replace_factory(Service1, Interface) - svc_reg.flask.get(Service1) - svc_reg.flask.get(Service2) + svcs.flask.get(Service1) + svcs.flask.get(Service2) assert 2 == len(container.cleanups) @@ -171,7 +171,7 @@ async def test_teardown_warns_on_async_cleanups(self, container): async def factory(): yield Service1() - svc_reg.flask.replace_factory(Service1, factory) + svcs.flask.replace_factory(Service1, factory) await container.aget(Service1) @@ -193,9 +193,9 @@ def test_register_factory_helper(self, registry, app): """ register_factory() registers a factory to the app that is passed. """ - svc_reg.flask.init_app(app, registry) + svcs.flask.init_app(app, registry) - svc_reg.flask.register_factory(app, Interface, Service1) + svcs.flask.register_factory(app, Interface, Service1) assert Interface in registry.services @@ -203,9 +203,9 @@ def test_register_value_helper(self, registry, app): """ register_value() registers a value to the app that is passed. """ - svc_reg.flask.init_app(app, registry) + svcs.flask.init_app(app, registry) - svc_reg.flask.register_value(app, Interface, 42) + svcs.flask.register_value(app, Interface, 42) assert Interface in registry.services @@ -216,16 +216,16 @@ def test_implicit_registry(self): init_app() creates a registry if one isn't provided. """ app = flask.Flask("tests") - svc_reg.flask.init_app(app) + svcs.flask.init_app(app) - assert isinstance(app.config["svc_registry"], svc_reg.Registry) + assert isinstance(app.config["svcsistry"], svcs.Registry) def test_explicit_registry(self): """ If a registry is passsed to init_app(), it's used. """ - registry = svc_reg.Registry() + registry = svcs.Registry() app = flask.Flask("tests") - svc_reg.flask.init_app(app, registry) + svcs.flask.init_app(app, registry) - assert registry is app.config["svc_registry"] + assert registry is app.config["svcsistry"] diff --git a/tests/typing/core.py b/tests/typing/core.py index d6bab0d..acbae71 100644 --- a/tests/typing/core.py +++ b/tests/typing/core.py @@ -7,10 +7,10 @@ from typing import AsyncGenerator, Generator -import svc_reg +import svcs -reg = svc_reg.Registry() +reg = svcs.Registry() def gen() -> Generator: @@ -40,7 +40,7 @@ async def async_ping() -> None: reg.register_value(str, str, ping=lambda: None) reg.register_value(str, async_gen) -con = svc_reg.Container(reg) +con = svcs.Container(reg) # The type checker believes whatever we tell it. o1: object = con.get(object) @@ -48,11 +48,11 @@ async def async_ping() -> None: con.close() -with contextlib.closing(svc_reg.Container(reg)) as con: +with contextlib.closing(svcs.Container(reg)) as con: ... if sys.version_info >= (3, 10): async def f() -> None: - async with contextlib.aclosing(svc_reg.Container(reg)): + async with contextlib.aclosing(svcs.Container(reg)): ... diff --git a/tests/typing/flask_.py b/tests/typing/flask_.py index 6ffd1cd..f986f03 100644 --- a/tests/typing/flask_.py +++ b/tests/typing/flask_.py @@ -6,32 +6,32 @@ from flask import Flask -import svc_reg +import svcs def factory_with_cleanup() -> Generator[int, None, None]: yield 1 -reg = svc_reg.Registry() +reg = svcs.Registry() app = Flask("tests") -app = svc_reg.flask.init_app(app, reg) -app = svc_reg.flask.init_app(app) +app = svcs.flask.init_app(app, reg) +app = svcs.flask.init_app(app) -svc_reg.flask.replace_value(int, 1) -svc_reg.flask.replace_value(int, 1, ping=lambda: None) +svcs.flask.replace_value(int, 1) +svcs.flask.replace_value(int, 1, ping=lambda: None) -svc_reg.flask.register_value(app, int, 1) -svc_reg.flask.register_value(app, int, 1, ping=lambda: None) +svcs.flask.register_value(app, int, 1) +svcs.flask.register_value(app, int, 1, ping=lambda: None) -svc_reg.flask.replace_factory(str, str) -svc_reg.flask.replace_value(str, str, ping=lambda: None) +svcs.flask.replace_factory(str, str) +svcs.flask.replace_value(str, str, ping=lambda: None) -svc_reg.flask.register_factory(app, str, str) -svc_reg.flask.register_factory(app, int, factory_with_cleanup) -svc_reg.flask.register_value(app, str, str, ping=lambda: None) +svcs.flask.register_factory(app, str, str) +svcs.flask.register_factory(app, int, factory_with_cleanup) +svcs.flask.register_value(app, str, str, ping=lambda: None) # The type checker believes whatever we tell it. -o1: object = svc_reg.flask.get(object) -o2: int = svc_reg.flask.get(object) +o1: object = svcs.flask.get(object) +o2: int = svcs.flask.get(object)