tiny-service-core 0.1.2__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/PKG-INFO +8 -9
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/README.md +2 -2
- tiny_service_core-0.2.1/pyproject.toml +130 -0
- tiny_service_core-0.1.2/pyproject.toml → tiny_service_core-0.2.1/pyproject.toml.orig +5 -6
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/config.py +9 -1
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/deps/db.py +5 -5
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/deps/http_client.py +1 -1
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/lifespan.py +19 -6
- tiny_service_core-0.2.1/src/core/typing.py +8 -0
- tiny_service_core-0.2.1/src/core/utils.py +7 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/__init__.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/app.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/crypto.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/deps/__init__.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/deps/auth.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/deps/cache.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/deps/user_ip.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/excs.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/lock.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/main.py +0 -0
- {tiny_service_core-0.1.2 → tiny_service_core-0.2.1}/src/core/sentry.py +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: tiny-service-core
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: 🪐 Shared functionality for tiny microservices
|
|
5
5
|
Keywords: async,fastapi,microservice,core
|
|
6
6
|
Author: Aliaksei Urbanski
|
|
7
7
|
Author-email: Aliaksei Urbanski <aliaksei.urbanski@gmail.com>
|
|
8
|
-
License: MIT
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
License-Expression: MIT
|
|
10
9
|
Classifier: Development Status :: 4 - Beta
|
|
11
10
|
Classifier: Environment :: Web Environment
|
|
12
11
|
Classifier: Framework :: AsyncIO
|
|
@@ -36,14 +35,14 @@ Requires-Dist: asyncpg ; extra == 'db'
|
|
|
36
35
|
Requires-Dist: sentry-sdk[asyncpg,sqlalchemy] ; extra == 'db'
|
|
37
36
|
Requires-Dist: sqlmodel ; extra == 'db'
|
|
38
37
|
Requires-Dist: fastapi[standard] ; extra == 'dev'
|
|
39
|
-
Requires-Dist:
|
|
40
|
-
Requires-Dist:
|
|
38
|
+
Requires-Dist: freezegun ; extra == 'dev'
|
|
39
|
+
Requires-Dist: httpx2 ; extra == 'http-client'
|
|
41
40
|
Requires-Dist: python-multipart ; extra == 'multipart'
|
|
42
41
|
Maintainer: Aliaksei Urbanski
|
|
43
42
|
Maintainer-email: Aliaksei Urbanski <aliaksei.urbanski@gmail.com>
|
|
44
43
|
Requires-Python: >=3.12
|
|
45
|
-
Project-URL: Issues, https://github.com/Jamim/tiny-service-core/issues
|
|
46
44
|
Project-URL: Repository, https://github.com/Jamim/tiny-service-core
|
|
45
|
+
Project-URL: Issues, https://github.com/Jamim/tiny-service-core/issues
|
|
47
46
|
Provides-Extra: cache
|
|
48
47
|
Provides-Extra: db
|
|
49
48
|
Provides-Extra: dev
|
|
@@ -103,7 +102,7 @@ Current version glues together these components:
|
|
|
103
102
|
* [asyncpg][asyncpg]
|
|
104
103
|
* [Alembic][alembic]
|
|
105
104
|
- `http-client`
|
|
106
|
-
* [
|
|
105
|
+
* [HTTPX2][httpx2]
|
|
107
106
|
|
|
108
107
|
[fastapi]: https://fastapi.tiangolo.com
|
|
109
108
|
[pydantic-settings]: https://docs.pydantic.dev/latest/concepts/pydantic_settings/
|
|
@@ -112,6 +111,6 @@ Current version glues together these components:
|
|
|
112
111
|
[sqlmodel]: https://sqlmodel.tiangolo.com
|
|
113
112
|
[asyncpg]: https://github.com/MagicStack/asyncpg
|
|
114
113
|
[alembic]: https://alembic.sqlalchemy.org
|
|
115
|
-
[
|
|
114
|
+
[httpx2]: https://httpx2.pydantic.dev
|
|
116
115
|
|
|
117
116
|
[installing-extras]: https://packaging.python.org/en/latest/tutorials/installing-packages/#installing-extras
|
|
@@ -50,7 +50,7 @@ Current version glues together these components:
|
|
|
50
50
|
* [asyncpg][asyncpg]
|
|
51
51
|
* [Alembic][alembic]
|
|
52
52
|
- `http-client`
|
|
53
|
-
* [
|
|
53
|
+
* [HTTPX2][httpx2]
|
|
54
54
|
|
|
55
55
|
[fastapi]: https://fastapi.tiangolo.com
|
|
56
56
|
[pydantic-settings]: https://docs.pydantic.dev/latest/concepts/pydantic_settings/
|
|
@@ -59,6 +59,6 @@ Current version glues together these components:
|
|
|
59
59
|
[sqlmodel]: https://sqlmodel.tiangolo.com
|
|
60
60
|
[asyncpg]: https://github.com/MagicStack/asyncpg
|
|
61
61
|
[alembic]: https://alembic.sqlalchemy.org
|
|
62
|
-
[
|
|
62
|
+
[httpx2]: https://httpx2.pydantic.dev
|
|
63
63
|
|
|
64
64
|
[installing-extras]: https://packaging.python.org/en/latest/tutorials/installing-packages/#installing-extras
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tiny-service-core"
|
|
3
|
+
description = "🪐 Shared functionality for tiny microservices"
|
|
4
|
+
version = "0.2.1"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
classifiers = [
|
|
8
|
+
"Development Status :: 4 - Beta",
|
|
9
|
+
"Environment :: Web Environment",
|
|
10
|
+
"Framework :: AsyncIO",
|
|
11
|
+
"Framework :: FastAPI",
|
|
12
|
+
"Framework :: Pydantic",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Topic :: Internet",
|
|
16
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
17
|
+
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
|
|
18
|
+
"Topic :: Software Development",
|
|
19
|
+
"Topic :: Software Development :: Libraries",
|
|
20
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
"Typing :: Typed",
|
|
23
|
+
"Programming Language :: Python",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
28
|
+
]
|
|
29
|
+
keywords = [
|
|
30
|
+
"async",
|
|
31
|
+
"fastapi",
|
|
32
|
+
"microservice",
|
|
33
|
+
"core",
|
|
34
|
+
]
|
|
35
|
+
requires-python = ">=3.12"
|
|
36
|
+
dependencies = [
|
|
37
|
+
"fastapi",
|
|
38
|
+
"pydantic-settings",
|
|
39
|
+
"sentry-sdk[fastapi]",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[[project.authors]]
|
|
43
|
+
name = "Aliaksei Urbanski"
|
|
44
|
+
email = "aliaksei.urbanski@gmail.com"
|
|
45
|
+
|
|
46
|
+
[[project.maintainers]]
|
|
47
|
+
name = "Aliaksei Urbanski"
|
|
48
|
+
email = "aliaksei.urbanski@gmail.com"
|
|
49
|
+
|
|
50
|
+
[project.optional-dependencies]
|
|
51
|
+
dev = [
|
|
52
|
+
"fastapi[standard]",
|
|
53
|
+
"freezegun",
|
|
54
|
+
]
|
|
55
|
+
cache = ["redis"]
|
|
56
|
+
db = [
|
|
57
|
+
"alembic",
|
|
58
|
+
"asyncpg",
|
|
59
|
+
"sentry-sdk[asyncpg,sqlalchemy]",
|
|
60
|
+
"sqlmodel",
|
|
61
|
+
]
|
|
62
|
+
http-client = ["httpx2"]
|
|
63
|
+
multipart = ["python-multipart"]
|
|
64
|
+
|
|
65
|
+
[project.urls]
|
|
66
|
+
Repository = "https://github.com/Jamim/tiny-service-core"
|
|
67
|
+
Issues = "https://github.com/Jamim/tiny-service-core/issues"
|
|
68
|
+
|
|
69
|
+
[dependency-groups]
|
|
70
|
+
lint = [
|
|
71
|
+
"mypy",
|
|
72
|
+
"ruff",
|
|
73
|
+
]
|
|
74
|
+
test = [
|
|
75
|
+
"pytest",
|
|
76
|
+
"pytest-cov",
|
|
77
|
+
"pytest-env",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
[build-system]
|
|
81
|
+
requires = ["uv_build<1.0"]
|
|
82
|
+
build-backend = "uv_build"
|
|
83
|
+
|
|
84
|
+
[tool.uv.build-backend]
|
|
85
|
+
module-name = "core"
|
|
86
|
+
|
|
87
|
+
[tool.ruff]
|
|
88
|
+
line-length = 79
|
|
89
|
+
|
|
90
|
+
[tool.ruff.format]
|
|
91
|
+
quote-style = "single"
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint]
|
|
94
|
+
select = [
|
|
95
|
+
"E",
|
|
96
|
+
"F",
|
|
97
|
+
"UP",
|
|
98
|
+
"B",
|
|
99
|
+
"SIM",
|
|
100
|
+
"I",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[tool.ruff.lint.per-file-ignores]
|
|
104
|
+
"src/core/__init__.py" = [
|
|
105
|
+
"F401",
|
|
106
|
+
"E402",
|
|
107
|
+
]
|
|
108
|
+
"src/core/deps/__init__.py" = ["F401"]
|
|
109
|
+
|
|
110
|
+
[tool.ruff.lint.isort]
|
|
111
|
+
known-local-folder = ["core"]
|
|
112
|
+
|
|
113
|
+
[tool.mypy]
|
|
114
|
+
mypy_path = "stubs"
|
|
115
|
+
files = "src/**/*.py"
|
|
116
|
+
plugins = "pydantic.mypy"
|
|
117
|
+
strict = true
|
|
118
|
+
|
|
119
|
+
[tool.pytest.ini_options]
|
|
120
|
+
addopts = "--cov core"
|
|
121
|
+
|
|
122
|
+
[tool.pytest_env]
|
|
123
|
+
APP_PROJ = "tiny-service"
|
|
124
|
+
APP_NAME = "core"
|
|
125
|
+
APP_ENV = "test"
|
|
126
|
+
APP_KEY = "42"
|
|
127
|
+
DB_URL = "postgresql+asyncpg://foo:bar@127.0.0.1/dummy"
|
|
128
|
+
|
|
129
|
+
[tool.coverage.run]
|
|
130
|
+
branch = true
|
|
@@ -3,10 +3,10 @@ name = "tiny-service-core"
|
|
|
3
3
|
description = "🪐 Shared functionality for tiny microservices"
|
|
4
4
|
|
|
5
5
|
# https://github.com/astral-sh/uv/issues/11718
|
|
6
|
-
version = "0.1
|
|
6
|
+
version = "0.2.1"
|
|
7
7
|
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
license =
|
|
9
|
+
license = "MIT"
|
|
10
10
|
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "Aliaksei Urbanski", email = "aliaksei.urbanski@gmail.com" },
|
|
@@ -16,7 +16,6 @@ maintainers = [
|
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
classifiers = [
|
|
19
|
-
"License :: OSI Approved :: MIT License",
|
|
20
19
|
"Development Status :: 4 - Beta",
|
|
21
20
|
"Environment :: Web Environment",
|
|
22
21
|
"Framework :: AsyncIO",
|
|
@@ -50,6 +49,7 @@ dependencies = [
|
|
|
50
49
|
[project.optional-dependencies]
|
|
51
50
|
dev = [
|
|
52
51
|
"fastapi[standard]",
|
|
52
|
+
"freezegun",
|
|
53
53
|
]
|
|
54
54
|
cache = [
|
|
55
55
|
"redis",
|
|
@@ -61,8 +61,7 @@ db = [
|
|
|
61
61
|
"sqlmodel",
|
|
62
62
|
]
|
|
63
63
|
http-client = [
|
|
64
|
-
"
|
|
65
|
-
"sentry-sdk[httpx]",
|
|
64
|
+
"httpx2",
|
|
66
65
|
]
|
|
67
66
|
multipart = [
|
|
68
67
|
"python-multipart",
|
|
@@ -84,7 +83,7 @@ Repository = "https://github.com/Jamim/tiny-service-core"
|
|
|
84
83
|
Issues = "https://github.com/Jamim/tiny-service-core/issues"
|
|
85
84
|
|
|
86
85
|
[build-system]
|
|
87
|
-
requires = ["uv_build
|
|
86
|
+
requires = ["uv_build<1.0"]
|
|
88
87
|
build-backend = "uv_build"
|
|
89
88
|
|
|
90
89
|
[tool.uv.build-backend]
|
|
@@ -41,6 +41,14 @@ class CacheSettings(BaseSettings):
|
|
|
41
41
|
|
|
42
42
|
class DBSettings(BaseSettings):
|
|
43
43
|
db_url: str
|
|
44
|
+
db_isolation_level: Literal[
|
|
45
|
+
'SERIALIZABLE',
|
|
46
|
+
'REPEATABLE READ',
|
|
47
|
+
'READ COMMITTED',
|
|
48
|
+
'READ UNCOMMITTED',
|
|
49
|
+
'AUTOCOMMIT',
|
|
50
|
+
None,
|
|
51
|
+
] = None
|
|
44
52
|
|
|
45
53
|
|
|
46
54
|
class HTTPClientSettings(BaseSettings):
|
|
@@ -50,7 +58,7 @@ class HTTPClientSettings(BaseSettings):
|
|
|
50
58
|
OPTIONAL_SETTINGS: dict[str, type[BaseSettings]] = {
|
|
51
59
|
'redis': CacheSettings,
|
|
52
60
|
'sqlmodel': DBSettings,
|
|
53
|
-
'
|
|
61
|
+
'httpx2': HTTPClientSettings,
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
settings_bases = [AppSettings, SentrySettings]
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
from collections.abc import AsyncGenerator
|
|
2
|
-
from typing import Annotated
|
|
2
|
+
from typing import Annotated
|
|
3
3
|
|
|
4
4
|
from fastapi import Depends, Request
|
|
5
|
-
from sqlalchemy.
|
|
5
|
+
from sqlalchemy.ext.asyncio import async_sessionmaker
|
|
6
6
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
async def get_session(request: Request) -> AsyncGenerator[AsyncSession]:
|
|
10
|
-
async_session =
|
|
10
|
+
async_session = async_sessionmaker(
|
|
11
11
|
request.app.state.db,
|
|
12
12
|
class_=AsyncSession,
|
|
13
13
|
expire_on_commit=False,
|
|
14
|
+
autoflush=False,
|
|
14
15
|
)
|
|
15
|
-
|
|
16
|
-
async with session:
|
|
16
|
+
async with async_session() as session:
|
|
17
17
|
yield session
|
|
18
18
|
|
|
19
19
|
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from collections.abc import AsyncGenerator
|
|
2
|
+
from collections.abc import AsyncGenerator
|
|
3
3
|
from contextlib import asynccontextmanager
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import cast
|
|
5
5
|
|
|
6
6
|
from fastapi import FastAPI
|
|
7
7
|
from starlette.datastructures import State
|
|
8
8
|
|
|
9
9
|
from . import settings
|
|
10
|
+
from .typing import CleanUpFunc, CoreInitFunc
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
try:
|
|
13
|
+
from app.lifespan import INIT_FUNCS as APP_INIT_FUNCS
|
|
14
|
+
except ImportError:
|
|
15
|
+
APP_INIT_FUNCS = []
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
def init_cache(state: State, cache_url: str) -> CleanUpFunc:
|
|
@@ -21,12 +25,16 @@ def init_cache(state: State, cache_url: str) -> CleanUpFunc:
|
|
|
21
25
|
def init_db(state: State, db_url: str) -> CleanUpFunc:
|
|
22
26
|
from sqlalchemy.ext.asyncio import create_async_engine
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
kwargs = {}
|
|
29
|
+
if settings.db_isolation_level:
|
|
30
|
+
kwargs['isolation_level'] = settings.db_isolation_level
|
|
31
|
+
|
|
32
|
+
state.db = create_async_engine(db_url, **kwargs)
|
|
25
33
|
return cast(CleanUpFunc, state.db.dispose)
|
|
26
34
|
|
|
27
35
|
|
|
28
36
|
def init_http_client(state: State, retries: int) -> CleanUpFunc:
|
|
29
|
-
from
|
|
37
|
+
from httpx2 import AsyncClient, AsyncHTTPTransport
|
|
30
38
|
|
|
31
39
|
state.http_client = AsyncClient(
|
|
32
40
|
transport=AsyncHTTPTransport(retries=retries)
|
|
@@ -34,7 +42,7 @@ def init_http_client(state: State, retries: int) -> CleanUpFunc:
|
|
|
34
42
|
return cast(CleanUpFunc, state.http_client.aclose)
|
|
35
43
|
|
|
36
44
|
|
|
37
|
-
INIT_FUNCS: dict[str,
|
|
45
|
+
INIT_FUNCS: dict[str, CoreInitFunc] = {
|
|
38
46
|
'cache_url': init_cache,
|
|
39
47
|
'db_url': init_db,
|
|
40
48
|
'http_retries': init_http_client,
|
|
@@ -50,6 +58,11 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None]:
|
|
|
50
58
|
if value:
|
|
51
59
|
clean_up_funcs.append(init_func(app.state, value))
|
|
52
60
|
|
|
61
|
+
for app_init_func in APP_INIT_FUNCS:
|
|
62
|
+
clean_up = app_init_func(app.state)
|
|
63
|
+
if clean_up:
|
|
64
|
+
clean_up_funcs.append(clean_up)
|
|
65
|
+
|
|
53
66
|
yield
|
|
54
67
|
|
|
55
68
|
await asyncio.gather(*(func() for func in clean_up_funcs))
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from starlette.datastructures import State
|
|
5
|
+
|
|
6
|
+
type CleanUpFunc = Callable[[], Awaitable[None]]
|
|
7
|
+
type CoreInitFunc = Callable[[State, Any], CleanUpFunc]
|
|
8
|
+
type AppInitFunc = Callable[[State], CleanUpFunc | None]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|