Skip to content

Commit

Permalink
Merge pull request #300 from Eclips4/improve-example
Browse files Browse the repository at this point in the history
Run code from README in tests
  • Loading branch information
Tishka17 authored Nov 11, 2024
2 parents c87a367 + 1fb5ac3 commit 39b646a
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 88 deletions.
23 changes: 8 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,9 @@ class SomeClient:
...
```

4. Create Provider instance. It is only used to setup all factories providing your objects.
3. Create Provider instance and setup how to provide dependencies.

```python
from dishka import Provider

provider = Provider()
```


5. Setup how to provide dependencies.
Providers are only used to setup all factories providing your objects.

We use `scope=Scope.APP` for dependencies which are created only once in application lifetime,
and `scope=Scope.REQUEST` for those which should be recreated for each processing request/event/etc.
Expand All @@ -89,28 +82,28 @@ from dishka import Provider, provide, Scope
class ConnectionProvider(Provider):
@provide(scope=Scope.REQUEST)
def new_connection(self) -> Iterable[Connection]:
conn = sqlite3.connect()
conn = sqlite3.connect(":memory:")
yield conn
conn.close()
```

6. Create main `Container` instance passing providers, and step into `APP` scope.
4. Create main `Container` instance passing providers, and step into `APP` scope.

```python
from dishka import make_container

container = make_container(service_provider, ConnectionProvider())
```

7. Container holds dependencies cache and is used to retrieve them. Here, you can use `.get` method to access APP-scoped dependencies:
5. Container holds dependencies cache and is used to retrieve them. Here, you can use `.get` method to access APP-scoped dependencies:

```python
client = container.get(SomeClient) # `SomeClient` has Scope.APP, so it is accessible here
client = container.get(SomeClient) # same instance of `SomeClient`
```


8. You can enter and exit `REQUEST` scope multiple times after that using context manager:
6. You can enter and exit `REQUEST` scope multiple times after that using context manager:

```python
# subcontainer to access more short-living objects
Expand All @@ -125,13 +118,13 @@ with container() as request_container:
```


9. Close container in the end:
7. Close container in the end:

```python
container.close()
```

10. If you are using supported framework add decorators and middleware for it.
8. If you are using supported framework add decorators and middleware for it.
For more details see [integrations doc](https://dishka.readthedocs.io/en/latest/integrations/index.html)

```python
Expand Down
101 changes: 28 additions & 73 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,99 +9,54 @@ Quickstart
2. Write your classes, fill type hints. Imagine, you have two classes: Service (kind of business logic) and DAO (kind of data access) and some external api client:

.. code-block:: python
class DAO(Protocol):
...
class Service:
def __init__(self, dao: DAO):
...
class DAOImpl(DAO):
def __init__(self, connection: Connection):
...
class SomeClient:
...
4. Create Provider instance. It is only used to setup all factories providing your objects.

.. code-block:: python
from dishka import Provider
provider = Provider()
.. literalinclude:: ./quickstart_example.py
:language: python
:lines: 6-18

3. Create Provider instance and setup how to provide dependencies.

5. Setup how to provide dependencies.
Providers are only used to setup all factories providing your objects.

We use ``scope=Scope.APP`` for dependencies which are created only once in application lifetime,
and ``scope=Scope.REQUEST`` for those which should be recreated for each processing request/event/etc.
To read more about scopes, refer :ref:`scopes`

.. code-block:: python
from dishka import Provider, Scope
service_provider = Provider(scope=Scope.REQUEST)
service_provider.provide(Service)
service_provider.provide(DAOImpl, provides=DAO)
service_provider.provide(SomeClient, scope=Scope.APP) # override provider scope
.. literalinclude:: ./quickstart_example.py
:language: python
:lines: 20-25

To provide connection we might need to write some custom code:

.. code-block:: python
.. literalinclude:: ./quickstart_example.py
:language: python
:lines: 27-34

from dishka import Provider, provide, Scope
4. Create main ``Container`` instance passing providers, and step into ``APP`` scope.

class ConnectionProvider(Provider):
@provide(scope=Scope.REQUEST)
def new_connection(self) -> Iterable[Connection]:
conn = sqlite3.connect()
yield conn
conn.close()
.. literalinclude:: ./quickstart_example.py
:language: python
:lines: 37-39

5. Container holds dependencies cache and is used to retrieve them. Here, you can use ``.get`` method to access APP-scoped dependencies:

6. Create main ``Container`` instance passing providers, and step into ``APP`` scope.

.. code-block:: python
from dishka import make_container
container = make_container(service_provider, ConnectionProvider())
7. Container holds dependencies cache and is used to retrieve them. Here, you can use ``.get`` method to access APP-scoped dependencies:

.. code-block:: python
client = container.get(SomeClient) # `SomeClient` has Scope.APP, so it is accessible here
client = container.get(SomeClient) # same instance of `SomeClient`
.. literalinclude:: ./quickstart_example.py
:language: python
:lines: 41-42


8. You can enter and exit ``REQUEST`` scope multiple times after that using context manager:
6. You can enter and exit ``REQUEST`` scope multiple times after that using context manager:

.. code-block:: python
# subcontainer to access more short-living objects
with container() as request_container:
service = request_container.get(Service)
service = request_container.get(Service) # same service instance
# at this point connection will be closed as we exited context manager
# new subcontainer to have a new lifespan for request processing
with container() as request_container:
service = request_container.get(Service) # new service instance
9. Close container in the end:

.. code-block:: python
.. literalinclude:: ./quickstart_example.py
:language: python
:lines: 45-53

container.close()
7. Close container in the end:

.. literalinclude:: ./quickstart_example.py
:language: python
:lines: 55

10. If you are using supported framework add decorators and middleware for it.
8. If you are using supported framework add decorators and middleware for it.
For more details see :ref:`integrations`

.. code-block:: python
Expand Down
55 changes: 55 additions & 0 deletions docs/quickstart_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import sqlite3

from typing import Protocol, Iterable
from sqlite3 import Connection

class DAO(Protocol):
...

class Service:
def __init__(self, dao: DAO):
...

class DAOImpl(DAO):
def __init__(self, connection: Connection):
...

class SomeClient:
...

from dishka import Provider, Scope

service_provider = Provider(scope=Scope.REQUEST)
service_provider.provide(Service)
service_provider.provide(DAOImpl, provides=DAO)
service_provider.provide(SomeClient, scope=Scope.APP) # override provider scope

from dishka import Provider, provide, Scope

class ConnectionProvider(Provider):
@provide(scope=Scope.REQUEST)
def new_connection(self) -> Iterable[Connection]:
conn = sqlite3.connect(":memory:")
yield conn
conn.close()


from dishka import make_container

container = make_container(service_provider, ConnectionProvider())

client = container.get(SomeClient) # `SomeClient` has Scope.APP, so it is accessible here
client = container.get(SomeClient) # same instace of `SomeClient`


# subcotaniner to access more short-living objects
with container() as request_container:
service = request_container.get(Service)
service = request_container.get(Service) # same service instance
# at this point connection will be closed as we exited context manager

# new subcontainer to have a new lifespan for request processing
with container() as request_container:
service = request_container.get(Service) # new service instance

container.close()
8 changes: 8 additions & 0 deletions tests/unit/test_quickstart_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import runpy
from pathlib import Path

ROOT = Path(__file__).parent.parent.parent.resolve()
QUICKSTART_EXAMPLE_PATH = ROOT / "docs/quickstart_example.py"

def test_readme_example():
runpy.run_path(QUICKSTART_EXAMPLE_PATH)

0 comments on commit 39b646a

Please sign in to comment.