Skip to content

Commit

Permalink
doc: update examples to reflect dependency scope inference (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
adriangb committed Sep 9, 2022
1 parent 3065295 commit 54b4572
Show file tree
Hide file tree
Showing 8 changed files with 18 additions and 28 deletions.
2 changes: 1 addition & 1 deletion docs/tutorial/dependencies/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Once again, nothing will change from the application user's perspective, but our
The order of execution here is `get_client() -> echo_headers() -> get_client()` and is roughly equivalent to:

```python
async with asynccontextmanager(get_client(HttpBinConfigModel())) as client:
async with asynccontextmanager(get_client(HttpBinConfig())) as client:
await echo_headers(client)
```

Expand Down
11 changes: 7 additions & 4 deletions docs/tutorial/dependencies/scopes.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ So for our use case, we'll be wanting to use the `"app"` scope for `httpx.AsyncC

Everything else can stay the same, this is all we need!

!!! attention
You may notice we also had to change the `HttpBinConfig` marker to the `"app"` scope.
Just like in Pytest, where a `"session"` scoped fixture can't depend on a `"function"` scoped fixture, in Xpresso an `"app"` scoped fixture can't depend on an `"endpoint"` scoped fixture, so we are forced to make `HttpBinConfig` an `"app"` scoped fixture.

If you run this and navigate to [http://127.0.0.1:8000/echo/url](http://127.0.0.1:8000/echo/url) the response will be the same, but you will probably notice reduced latency if you refresh to make several requests.

## Scope inference

In Pytest a `"session"` scoped fixture can't depend on a `"function"` scoped fixture, and in Xpresso an `"app"` scoped fixture can't depend on an `"endpoint"` scoped fixture.

Unlike Pytest which forces you to hardcode all of the scopes, Xpresso is able to infer scopes from the dependency graph.
So in this case it says "oh, I see that `get_client` depends on `HttpBinConfig` and `HttpBinConfig` was not explicitly assigned a scope; I'll give `HttpBinConfig` and `"app"` scope then so that it is compatible with `get_client`".

[pytest]: https://docs.pytest.org/en/6.2.x/fixture.html
[dependency lifecycles]: lifecycle.md
3 changes: 3 additions & 0 deletions docs/tutorial/lifespan.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ The main difference vs. Starlette is that the lifespan context manager is allowe
You don't need `app.state` or `request.state` in Xpresso.
Instead, you can create you own strongly typed mutable or immutable state object and inject it into your lifespan and/or endpoints like in the example above.

!!! tip Tip
Lifespan dependencies are automatically assigned the `"app"` scope, you don't need to explicitly set it.

## Router lifespans

Routers can also have lifespans, and these lifespans will be executed when the top level `App`'s lifespan executes:
Expand Down
7 changes: 1 addition & 6 deletions docs_src/tutorial/dependencies/tutorial_003.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@
from xpresso.typing import Annotated


class HttpBinConfigModel(BaseSettings):
class HttpBinConfig(BaseSettings):
url: str = "https://httpbin.org"

class Config(BaseSettings.Config):
env_prefix = "HTTPBIN_"


HttpBinConfig = Annotated[
HttpBinConfigModel, Depends(lambda: HttpBinConfigModel())
]


def get_client(config: HttpBinConfig) -> httpx.AsyncClient:
return httpx.AsyncClient(base_url=config.url)

Expand Down
7 changes: 1 addition & 6 deletions docs_src/tutorial/dependencies/tutorial_004.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,13 @@
from xpresso.typing import Annotated


class HttpBinConfigModel(BaseSettings):
class HttpBinConfig(BaseSettings):
url: str = "https://httpbin.org"

class Config(BaseSettings.Config):
env_prefix = "HTTPBIN_"


HttpBinConfig = Annotated[
HttpBinConfigModel, Depends(lambda: HttpBinConfigModel())
]


async def get_client(
config: HttpBinConfig,
) -> AsyncGenerator[httpx.AsyncClient, None]:
Expand Down
8 changes: 1 addition & 7 deletions docs_src/tutorial/dependencies/tutorial_006.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,13 @@
from xpresso.typing import Annotated


class HttpBinConfigModel(BaseSettings):
class HttpBinConfig(BaseSettings):
url: str = "https://httpbin.org"

class Config(BaseSettings.Config):
env_prefix = "HTTPBIN_"


HttpBinConfig = Annotated[
HttpBinConfigModel,
Depends(lambda: HttpBinConfigModel(), scope="app"),
]


async def get_client(
config: HttpBinConfig,
) -> AsyncGenerator[httpx.AsyncClient, None]:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "xpresso"
version = "0.42.0"
version = "0.42.1"
description = "A developer centric, performant Python web framework"
authors = ["Adrian Garcia Badaracco <adrian@adriangb.com>"]
readme = "README.md"
Expand Down
6 changes: 3 additions & 3 deletions tests/test_docs/tutorial/dependencies/test_tutorial_003.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import httpx

from docs_src.tutorial.dependencies.tutorial_003 import HttpBinConfigModel, app
from docs_src.tutorial.dependencies.tutorial_003 import HttpBinConfig, app
from xpresso.testclient import TestClient


Expand All @@ -15,14 +15,14 @@ async def handler(request: httpx.Request) -> httpx.Response:
# This dependency becomes the provider for the client
# It will get auto-wired with the config, so we can use it to assert that the config
# Was successfully injected
def get_client(config: HttpBinConfigModel) -> httpx.AsyncClient:
def get_client(config: HttpBinConfig) -> httpx.AsyncClient:
assert config.url == test_url
return httpx.AsyncClient(
transport=httpx.MockTransport(handler), base_url=config.url
)

with app.dependency_overrides as overrides:
overrides[HttpBinConfigModel] = lambda: HttpBinConfigModel(url=test_url)
overrides[HttpBinConfig] = lambda: HttpBinConfig(url=test_url)
overrides[httpx.AsyncClient] = get_client
client = TestClient(app)
response = client.get("/echo/url")
Expand Down

0 comments on commit 54b4572

Please sign in to comment.