Skip to content

Commit 974c491

Browse files
authored
Documentation review (#54)
* Documentation review
1 parent 47f9938 commit 974c491

File tree

15 files changed

+247
-156
lines changed

15 files changed

+247
-156
lines changed

docs/.pages

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ nav:
33
- Bind manager: manager
44
- Repository: repository
55
- Components life cycle: lifecycle.md
6+
- API Reference: api_reference
67
- ...

docs/api_reference/.pages

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
nav:
2+
- bind_manager.md
3+
- repository.md
4+
- exceptions.md
5+
- ...

docs/api_reference/bind_manager.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
::: sqlalchemy_bind_manager
2+
options:
3+
members:
4+
- SQLAlchemyBindManager
5+
- SQLAlchemyConfig
6+
- SQLAlchemyAsyncConfig

docs/api_reference/exceptions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
::: sqlalchemy_bind_manager.exceptions

docs/api_reference/repository.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
::: sqlalchemy_bind_manager.protocols
2+
options:
3+
members:
4+
- SQLAlchemyRepositoryInterface
5+
- SQLAlchemyAsyncRepositoryInterface
6+
- SortDirection
7+
- PaginatedResult
8+
- CursorPaginatedResult
9+
- CursorReference

docs/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,6 @@ It's not recommended to create long-lived sessions like:
9898
session = sa_manager.get_session()
9999
```
100100

101-
This can create unexpected because of global variables and multi-threading.
102-
More details can be found in the [session page](manager/session/#note-on-multithreaded-applications)
101+
This can create unexpected behaviours because of global variables and multi-threading.
102+
More details can be found in the [session page](manager/session.md#note-on-multithreaded-applications)
103103
///

docs/manager/session.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,18 @@ multiple threads, like spawning a thread per request, then you should not store
2424
initialised session in a global variable, otherwise the state of your models will be shared
2525
among the threads and produce undesired changes in the database.
2626

27-
This is not thread safe:
27+
This is **not** thread safe:
2828

2929
/// note | db.py (a module to have an easy to use session)
3030
```python
31+
sa_manager = SQLAlchemyBindManager(some_config)
32+
3133
session = sa_manager.get_session()
3234
```
3335
///
3436

3537

36-
/// note | some_other_module.py (a module to have an easy-to=use session)
38+
/// note | some_other_module.py
3739
```python
3840
from db import session
3941

@@ -45,9 +47,16 @@ session.commit()
4547

4648
This instead would be thread safe:
4749

48-
/// note | some_other_module.py (a module to have an easy-to=use session)
50+
/// note | db.py (a module to have an easy to use SQLAlchemyBindManager)
51+
```python
52+
sa_manager = SQLAlchemyBindManager(some_config)
53+
```
54+
///
4955

56+
/// note | some_other_module.py
5057
```python
58+
from db import sa_manager
59+
5160
def do_something():
5261
session = sa_manager.get_session()
5362
session.add(model)
@@ -57,8 +66,7 @@ def do_something():
5766
do_something()
5867
```
5968

60-
The `do_something` function can be also in another method, as long as
61-
the `session` variable has no global scope it will be safe.
69+
As long as the `session` variable has no global scope it will be safe.
6270
///
6371

6472
/// tip | Using the `get_session()` context manager is much easier
@@ -77,3 +85,7 @@ session = scoped_session(sa_manager.get_bind().session_class())
7785
Handling the life cycle of scoped sessions is not supported by this documentations.
7886
Please refer to [SQLAlchemy documentation](https://docs.sqlalchemy.org/en/20/orm/contextual.html)
7987
about this.
88+
89+
/// tip | The repository implementation will handle the session life cycle for you
90+
91+
///

docs/repository/uow.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,11 @@ with uow.transaction(read_only=True):
2727
model2 = uow.repository("repo_b").get(2)
2828
```
2929

30-
/// admonition | The unit of work implementation is still experimental.
30+
/// admonition | The unit of work implementation is limited to repositories using the same bind.
3131
type: warning
3232

33-
There are some limitations in the current implementation that could radically change
34-
the implementation:
35-
36-
* Distributed transactions are not yet supported.
37-
* The direct use of `SQLAlchemyRepository` and `SQLAlchemyAsyncRepository` classes is not yet supported.
33+
[Two-phase commits](https://docs.sqlalchemy.org/en/20/orm/session_transaction.html#enabling-two-phase-commit)
34+
are not yet supported.
3835
///
3936

4037
Both the UnitOfWork classes create an internal `scoped_session` or `async_scoped_session`, behaving

mkdocs.yml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,33 @@ plugins:
1111
- search
1212
- awesome-pages
1313
- mike
14-
- gen-files:
15-
scripts:
16-
- scripts/gen_pages.py # or any other name or path
14+
# - gen-files:
15+
# scripts:
16+
# - scripts/gen_pages.py # or any other name or path
1717
- mkdocstrings:
18+
default_handler: python
1819
handlers:
1920
python:
2021
options:
22+
# General options
23+
show_source: true
24+
show_bases: false
25+
# Headings options
26+
show_root_heading: true
27+
show_root_members_full_path: true
28+
# Members options
29+
inherited_members: true
30+
group_by_category: true
31+
members_order: source
32+
# Docstrings options
2133
docstring_style: sphinx
22-
docstring_section_style: spacy
34+
merge_init_into_class: false
35+
show_if_no_docstring: false
36+
# Signature options
37+
show_signature_annotations: true
38+
separate_signature: true
39+
signature_crossrefs: true
40+
unwrap_annotated: true
2341

2442
theme:
2543
name: material

sqlalchemy_bind_manager/_bind_manager.py

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,20 @@
3939

4040

4141
class SQLAlchemyConfig(BaseModel):
42+
"""
43+
Configuration for synchronous engines
44+
"""
45+
4246
engine_url: str
4347
engine_options: Union[dict, None] = None
4448
session_options: Union[dict, None] = None
4549

4650

4751
class SQLAlchemyAsyncConfig(BaseModel):
52+
"""
53+
Configuration for asynchronous engines
54+
"""
55+
4856
engine_url: str
4957
engine_options: Union[dict, None] = None
5058
session_options: Union[dict, None] = None
@@ -68,11 +76,6 @@ class SQLAlchemyAsyncBind(BaseModel):
6876
model_config = ConfigDict(arbitrary_types_allowed=True)
6977

7078

71-
_SQLAlchemyConfig = Union[
72-
Mapping[str, Union[SQLAlchemyConfig, SQLAlchemyAsyncConfig]],
73-
SQLAlchemyConfig,
74-
SQLAlchemyAsyncConfig,
75-
]
7679
DEFAULT_BIND_NAME = "default"
7780

7881

@@ -81,7 +84,11 @@ class SQLAlchemyBindManager:
8184

8285
def __init__(
8386
self,
84-
config: _SQLAlchemyConfig,
87+
config: Union[
88+
Mapping[str, Union[SQLAlchemyConfig, SQLAlchemyAsyncConfig]],
89+
SQLAlchemyConfig,
90+
SQLAlchemyAsyncConfig,
91+
],
8592
) -> None:
8693
self.__binds = {}
8794
if isinstance(config, Mapping):
@@ -162,31 +169,54 @@ def __build_async_bind(
162169
declarative_base=registry_mapper.generate_base(),
163170
)
164171

165-
def get_binds(self) -> Mapping[str, Union[SQLAlchemyBind, SQLAlchemyAsyncBind]]:
166-
return self.__binds
167-
168172
def get_bind_mappers_metadata(self) -> Mapping[str, MetaData]:
169173
"""
170-
Returns the mappers metadata in a format that can be used
171-
in Alembic configuration
174+
Returns the registered mappers metadata in a format
175+
that can be used in Alembic configuration
172176
173177
:returns: mappers metadata
174-
:rtype: dict
175178
"""
176179
return {k: b.registry_mapper.metadata for k, b in self.__binds.items()}
177180

178181
def get_bind(
179182
self, bind_name: str = DEFAULT_BIND_NAME
180183
) -> Union[SQLAlchemyBind, SQLAlchemyAsyncBind]:
184+
"""
185+
Returns a bind object by name.
186+
187+
:param bind_name: A registered bind name
188+
:return: a bind object
189+
"""
181190
try:
182191
return self.__binds[bind_name]
183192
except KeyError:
184193
raise NotInitializedBindError("Bind not initialized")
185194

195+
def get_binds(self) -> Mapping[str, Union[SQLAlchemyBind, SQLAlchemyAsyncBind]]:
196+
"""
197+
Returns all the registered bind objects.
198+
199+
:returns: A mapping containing the registered binds
200+
"""
201+
return self.__binds
202+
203+
def get_mapper(self, bind_name: str = DEFAULT_BIND_NAME) -> registry:
204+
"""
205+
Returns the registered SQLAlchemy registry_mapper for the given bind name
206+
207+
:param bind_name: A registered bind name
208+
:return: the registered registry_mapper
209+
"""
210+
return self.get_bind(bind_name).registry_mapper
211+
186212
def get_session(
187213
self, bind_name: str = DEFAULT_BIND_NAME
188214
) -> Union[Session, AsyncSession]:
189-
return self.get_bind(bind_name).session_class()
215+
"""
216+
Returns a SQLAlchemy Session object, ready to be used either
217+
directly or as a context manager
190218
191-
def get_mapper(self, bind_name: str = DEFAULT_BIND_NAME) -> registry:
192-
return self.get_bind(bind_name).registry_mapper
219+
:param bind_name: A registered bind name
220+
:return: The SQLAlchemy Session object
221+
"""
222+
return self.get_bind(bind_name).session_class()

0 commit comments

Comments
 (0)