Skip to content

Commit

Permalink
Use flask app.extensions for storing registry (#73)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Hynek Schlawack <[email protected]>
  • Loading branch information
3 people authored Feb 26, 2024
1 parent 85827a1 commit 012b6a9
Show file tree
Hide file tree
Showing 6 changed files with 19 additions and 11 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/

## [Unreleased](https://github.com/hynek/svcs/compare/24.1.0...HEAD)

### Changed

- 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](https://github.com/hynek/svcs/discussions/71)
[#72](https://github.com/hynek/svcs/issues/72)
[#73](https://github.com/hynek/svcs/pull/73)

## [24.1.0](https://github.com/hynek/svcs/compare/23.21.0...24.1.0) - 2024-01-25

Expand Down
2 changes: 1 addition & 1 deletion docs/core-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ Now, you can point your monitoring tool of choice -- like Prometheus's [Blackbox

While *svcs*'s core is entirely agnostic on how you use the registry and the container, all our {doc}`integrations/index` follow the same life cycle:

- The {class}`svcs.Registry` objects live on **application-scoped** objects like {attr}`flask.Flask.config`.
- The {class}`svcs.Registry` objects live on **application-scoped** objects like {attr}`flask.Flask.extensions`.
- The {class}`svcs.Container` objects live on **request-scoped** objects like {data}`flask.g`.

You're free to structure your own integrations as you want, though.
Expand Down
2 changes: 1 addition & 1 deletion docs/integrations/flask.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Flask

*svcs*'s [Flask](https://flask.palletsprojects.com/) integration uses the {attr}`flask.Flask.config` object to store the {class}`svcs.Registry` and the {obj}`~flask.g` object to store the {class}`svcs.Container`.
*svcs*'s [Flask](https://flask.palletsprojects.com/) integration uses the {attr}`flask.Flask.extensions` object to store the {class}`svcs.Registry` and the {obj}`~flask.g` object to store the {class}`svcs.Container`.
It also installs a {meth}`flask.Flask.teardown_appcontext` handler to close the container when the request is done.

---
Expand Down
2 changes: 1 addition & 1 deletion docs/why.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ If you're curious, check the [glossary](glossary) entries for {term}`Service Loc
While it may take a moment to realize, all of this comes with many benefits:

**Reduction in boilerplate.**
Every web framework has some way to store data on long-lived objects like the application, and short-lived objects like requests (for example, in Flask it's {attr}`flask.Flask.config` and {obj}`flask.g` – Starlette uses `request.state` for both).
Every web framework has some way to store data on long-lived objects like the application, and short-lived objects like requests (for example, in Flask it's {attr}`flask.Flask.extensions` and {obj}`flask.g` – Starlette uses `request.state` for both).
Some frameworks also have helpers to control the lifecycle of those objects (like {meth}`flask.Flask.teardown_appcontext` or {meth}`pyramid.request.Request.add_finished_callback`).
But they work subtly differently and you accumulate a lot of repetitive boilerplate code.
In fact, Hynek started this project because of the [repetitiveness of Flask's `get_X` pattern](#flask-get-x).
Expand Down
12 changes: 6 additions & 6 deletions src/svcs/flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def svcs_from(g: _AppCtxGlobals = g) -> Container:
Get the current container from *g*.
"""
if (con := g.get(_KEY_CONTAINER, None)) is None:
con = Container(current_app.config[_KEY_REGISTRY])
con = Container(current_app.extensions[_KEY_REGISTRY])
setattr(g, _KEY_CONTAINER, con)

return con # type: ignore[no-any-return]
Expand All @@ -53,7 +53,7 @@ def get_registry(app: Flask | None = None) -> Registry:
"""
if app is None:
app = current_app
return app.config[_KEY_REGISTRY] # type: ignore[no-any-return]
return app.extensions[_KEY_REGISTRY] # type: ignore[no-any-return]


registry = cast(Registry, LocalProxy(get_registry))
Expand All @@ -68,7 +68,7 @@ def init_app(app: FlaskAppT, *, registry: Registry | None = None) -> FlaskAppT:
Creates a registry for you if you don't provide one.
"""
app.config[_KEY_REGISTRY] = registry or Registry()
app.extensions[_KEY_REGISTRY] = registry or Registry()
app.teardown_appcontext(teardown)

return app
Expand All @@ -95,7 +95,7 @@ def register_factory(
Same as :meth:`svcs.Registry.register_factory()`, but uses registry on
*app* that has been put there by :func:`init_app()`.
"""
app.config[_KEY_REGISTRY].register_factory(
app.extensions[_KEY_REGISTRY].register_factory(
svc_type,
factory,
enter=enter,
Expand All @@ -117,7 +117,7 @@ def register_value(
Same as :meth:`svcs.Registry.register_value()`, but uses registry on *app*
that has been put there by :func:`init_app()`.
"""
app.config[_KEY_REGISTRY].register_value(
app.extensions[_KEY_REGISTRY].register_value(
svc_type,
value,
enter=enter,
Expand Down Expand Up @@ -209,7 +209,7 @@ def close_registry(app: Flask) -> None:
"""
Close the registry on *app*, if present.
"""
if reg := app.config.pop(_KEY_REGISTRY, None):
if reg := app.extensions.pop(_KEY_REGISTRY, None):
reg.close()


Expand Down
4 changes: 2 additions & 2 deletions tests/integrations/test_flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def test_implicit_registry(self):
app = flask.Flask("tests")
svcs.flask.init_app(app)

assert isinstance(app.config["svcs_registry"], svcs.Registry)
assert isinstance(app.extensions["svcs_registry"], svcs.Registry)

def test_explicit_registry(self):
"""
Expand All @@ -273,7 +273,7 @@ def test_explicit_registry(self):
app = flask.Flask("tests")
svcs.flask.init_app(app, registry=registry)

assert registry is app.config["svcs_registry"]
assert registry is app.extensions["svcs_registry"]


class TestCloseRegistry:
Expand Down

0 comments on commit 012b6a9

Please sign in to comment.