fastapi-extra 0.1.5__tar.gz → 0.1.7__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.
Files changed (30) hide show
  1. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/PKG-INFO +1 -1
  2. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/__init__.py +1 -1
  3. fastapi_extra-0.1.7/fastapi_extra/cache/__init__.py +8 -0
  4. fastapi_extra-0.1.7/fastapi_extra/cache/redis.py +59 -0
  5. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/database/__init__.py +0 -3
  6. fastapi_extra-0.1.7/fastapi_extra/database/driver.py +83 -0
  7. fastapi_extra-0.1.7/fastapi_extra/database/session.py +24 -0
  8. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/dependency.py +3 -3
  9. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/form.py +8 -1
  10. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/types.py +2 -1
  11. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra.egg-info/PKG-INFO +1 -1
  12. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra.egg-info/SOURCES.txt +1 -1
  13. fastapi_extra-0.1.5/fastapi_extra/cache/__init__.py +0 -10
  14. fastapi_extra-0.1.5/fastapi_extra/cache/redis.py +0 -52
  15. fastapi_extra-0.1.5/fastapi_extra/database/engine.py +0 -50
  16. fastapi_extra-0.1.5/fastapi_extra/database/session.py +0 -39
  17. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/LICENSE +0 -0
  18. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/README.rst +0 -0
  19. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/database/model.py +0 -0
  20. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/database/service.py +0 -0
  21. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/native/cursor.pyx +0 -0
  22. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/native/routing.pyx +0 -0
  23. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/response.py +0 -0
  24. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/settings.py +0 -0
  25. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra/utils.py +0 -0
  26. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra.egg-info/dependency_links.txt +0 -0
  27. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra.egg-info/requires.txt +0 -0
  28. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/fastapi_extra.egg-info/top_level.txt +0 -0
  29. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/pyproject.toml +0 -0
  30. {fastapi_extra-0.1.5 → fastapi_extra-0.1.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fastapi-extra
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: extra package for fastapi.
5
5
  Author-email: Ziyan Yin <408856732@qq.com>
6
6
  License: BSD-3-Clause
@@ -1,4 +1,4 @@
1
- __version__ = "0.1.5"
1
+ __version__ = "0.1.7"
2
2
 
3
3
 
4
4
  from fastapi import FastAPI
@@ -0,0 +1,8 @@
1
+ __author__ = "ziyan.yin"
2
+ __date__ = "2025-01-10"
3
+
4
+ from .redis import DefaultRedis as Redis
5
+
6
+ __all__ = [
7
+ "Redis"
8
+ ]
@@ -0,0 +1,59 @@
1
+ __author__ = "ziyan.yin"
2
+ __date__ = "2025-01-17"
3
+
4
+
5
+ from typing import Annotated, AsyncGenerator
6
+
7
+ from fastapi.params import Depends
8
+ from pydantic import BaseModel, Field, RedisDsn
9
+ from redis.asyncio import ConnectionPool, Redis
10
+
11
+ from fastapi_extra.dependency import AbstractComponent
12
+ from fastapi_extra.settings import Settings
13
+
14
+
15
+ class RedisConfig(BaseModel):
16
+ url: RedisDsn = RedisDsn("redis://localhost:6379/0")
17
+ max_connections: int | None = None
18
+ connection_kwargs: dict = Field(default_factory=dict)
19
+
20
+
21
+ class DefaultRedisSettings(Settings):
22
+ redis: RedisConfig
23
+
24
+
25
+ _settings = DefaultRedisSettings() # type: ignore
26
+ _loaded_pools: list[ConnectionPool] = []
27
+
28
+
29
+ class RedisCli(AbstractComponent):
30
+ default_config = _settings.redis
31
+
32
+
33
+ def __init__(self):
34
+ self._pool = None
35
+
36
+ @property
37
+ def pool(self) -> ConnectionPool:
38
+ if not self._pool:
39
+ self._pool = ConnectionPool.from_url(
40
+ self.default_config.url,
41
+ **self.default_config.model_dump(exclude_defaults=True, exclude={"url", "connection_kwargs"}),
42
+ **self.default_config.connection_kwargs
43
+ )
44
+ _loaded_pools.append(self)
45
+ return self._pool
46
+
47
+
48
+ async def dispose() -> None:
49
+ for redis_pool in _loaded_pools:
50
+ redis_pool.aclose()
51
+
52
+
53
+
54
+ async def get_redis(redis_cli: RedisCli) -> AsyncGenerator[Redis, None]:
55
+ async with Redis(connection_pool=redis_cli.pool) as redis:
56
+ yield redis
57
+
58
+
59
+ DefaultRedis = Annotated[Redis, Depends(get_redis)]
@@ -4,14 +4,11 @@ __date__ = "2025-01-05"
4
4
 
5
5
  from fastapi_extra.database.model import SQLBase
6
6
  from fastapi_extra.database.service import ModelService
7
- from fastapi_extra.database.session import AsyncSessionMaker
8
7
  from fastapi_extra.database.session import DefaultSession as Session
9
- from fastapi_extra.database.session import SessionMaker
10
8
 
11
9
  __all__ = [
12
10
  "AsyncSessionMaker",
13
11
  "ModelService",
14
12
  "Session",
15
- "SessionMaker",
16
13
  "SQLBase"
17
14
  ]
@@ -0,0 +1,83 @@
1
+ __author__ = "ziyan.yin"
2
+ __date__ = "2024-12-26"
3
+
4
+
5
+ from typing import Literal
6
+
7
+ from pydantic import AnyUrl, BaseModel, Field
8
+ from sqlalchemy import Engine, NullPool
9
+ from sqlalchemy.ext.asyncio import AsyncEngine
10
+ from sqlalchemy.util import _concurrency_py3k
11
+ from sqlmodel import Session, create_engine
12
+ from sqlmodel.ext.asyncio.session import AsyncSession
13
+
14
+ from fastapi_extra.dependency import AbstractComponent
15
+ from fastapi_extra.settings import Settings
16
+
17
+
18
+ class DatabaseConfig(BaseModel):
19
+ url: AnyUrl
20
+ echo: bool = False
21
+ echo_pool: bool = False
22
+ isolation_level: Literal[
23
+ "SERIALIZABLE",
24
+ "REPEATABLE READ",
25
+ "READ COMMITTED",
26
+ "READ UNCOMMITTED",
27
+ "AUTOCOMMIT",
28
+ ] | None = None
29
+ options: dict = Field(default_factory=dict)
30
+
31
+
32
+ class DefaultDatabaseSettings(Settings):
33
+ datasource: DatabaseConfig
34
+
35
+
36
+ _settings = DefaultDatabaseSettings() # type: ignore
37
+ _loaded_engines: list[Engine] = []
38
+
39
+
40
+ class DB(AbstractComponent):
41
+ default_config = _settings.datasource
42
+ default_options = {}
43
+
44
+ def __init__(self):
45
+ self._engine = None
46
+
47
+ @property
48
+ def engine(self) -> Engine:
49
+ if not self._engine:
50
+ self._engine = create_engine(
51
+ url=str(self.default_config.url),
52
+ **self.default_config.model_dump(exclude_defaults=True, exclude={"url", "options"}),
53
+ **self.default_config.options,
54
+ **self.default_options
55
+ )
56
+ _loaded_engines.append(self._engine)
57
+ return self._engine
58
+
59
+ @property
60
+ def session(self) -> Session:
61
+ return Session(self.engine)
62
+
63
+
64
+ class AsyncDB(DB):
65
+
66
+ @property
67
+ def engine(self) -> AsyncEngine:
68
+ if not self._engine:
69
+ self._engine = AsyncEngine(super().engine)
70
+ return self._engine
71
+
72
+ @property
73
+ def session(self) -> AsyncSession:
74
+ return AsyncSession(self.engine)
75
+
76
+
77
+ if _settings.mode == "test":
78
+ DB.default_options = {"poolclass": NullPool}
79
+
80
+
81
+ async def dispose() -> None:
82
+ for engine in _loaded_engines:
83
+ await _concurrency_py3k.greenlet_spawn(engine.dispose)
@@ -0,0 +1,24 @@
1
+ __author__ = "ziyan.yin"
2
+ __date__ = "2025-01-05"
3
+
4
+
5
+ from typing import Annotated, AsyncGenerator, Generator
6
+
7
+ from fastapi.params import Depends
8
+ from sqlmodel import Session
9
+ from sqlmodel.ext.asyncio.session import AsyncSession
10
+
11
+ from fastapi_extra.database.driver import DB, AsyncDB
12
+
13
+
14
+ async def get_async_session(db: AsyncDB) -> AsyncGenerator[AsyncSession, None]:
15
+ async with db.session as session:
16
+ yield session
17
+
18
+
19
+ def get_session(db: DB) -> Generator[Session, None, None]:
20
+ with db.session as session:
21
+ yield session
22
+
23
+
24
+ DefaultSession = Annotated[AsyncSession, Depends(get_async_session)]
@@ -8,7 +8,7 @@ from typing import Annotated, Any, Self
8
8
  from fastapi.params import Depends
9
9
 
10
10
 
11
- class AnnotationMetaClass(ABCMeta):
11
+ class DependencyMetaClass(ABCMeta):
12
12
 
13
13
  def __new__(
14
14
  mcs,
@@ -23,11 +23,11 @@ class AnnotationMetaClass(ABCMeta):
23
23
  return new_cls
24
24
 
25
25
 
26
- class AbstractDependency(metaclass=AnnotationMetaClass, annotated=False):
26
+ class AbstractDependency(metaclass=DependencyMetaClass, annotated=False):
27
27
  __slot__ = ()
28
28
 
29
29
 
30
- class AbstractWidget(AbstractDependency, annotated=False):
30
+ class AbstractComponent(AbstractDependency, annotated=False):
31
31
  __slot__ = ()
32
32
  __instance__: Any = None
33
33
 
@@ -6,7 +6,7 @@ from typing import Generic, Literal
6
6
 
7
7
  from pydantic import BaseModel, Field, model_validator
8
8
 
9
- from fastapi_extra.types import C, S
9
+ from fastapi_extra.types import C, S, Schema
10
10
 
11
11
 
12
12
  class DataRange(BaseModel, Generic[C]):
@@ -28,3 +28,10 @@ class ColumnExpression(BaseModel, Generic[S]):
28
28
  class WhereClause(BaseModel):
29
29
  option: Literal["and", "or"] = Field(default="and", title="关系")
30
30
  column_clauses: list[ColumnExpression | "WhereClause"]
31
+
32
+
33
+ class Page(BaseModel, Generic[Schema]):
34
+ items: list[Schema] = Field(default_factory=list, title="列表")
35
+ total: int = Field(default=0, title="总量")
36
+ page_num: int = Field(default=0, title="页码")
37
+ page_size: int = Field(default=0, title="单页大小")
@@ -6,7 +6,7 @@ import datetime
6
6
  import decimal
7
7
  from typing import Annotated, Any, TypeVar, Union
8
8
 
9
- from pydantic import PlainSerializer
9
+ from pydantic import BaseModel, PlainSerializer
10
10
  from sqlmodel import SQLModel
11
11
 
12
12
  Comparable = Union[int, float, decimal.Decimal, datetime.datetime, datetime.date, datetime.time]
@@ -17,6 +17,7 @@ T = TypeVar("T", bound=Any)
17
17
  E = TypeVar("E", bound=Exception)
18
18
  C = TypeVar("C", bound=Comparable)
19
19
  S = TypeVar("S", bound=Serializable)
20
+ Schema = TypeVar("Schema", bound=BaseModel)
20
21
  Model = TypeVar("Model", bound=SQLModel)
21
22
 
22
23
  Cursor = Annotated[
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fastapi-extra
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: extra package for fastapi.
5
5
  Author-email: Ziyan Yin <408856732@qq.com>
6
6
  License: BSD-3-Clause
@@ -18,7 +18,7 @@ fastapi_extra/native/routing.pyx
18
18
  fastapi_extra/cache/__init__.py
19
19
  fastapi_extra/cache/redis.py
20
20
  fastapi_extra/database/__init__.py
21
- fastapi_extra/database/engine.py
21
+ fastapi_extra/database/driver.py
22
22
  fastapi_extra/database/model.py
23
23
  fastapi_extra/database/service.py
24
24
  fastapi_extra/database/session.py
@@ -1,10 +0,0 @@
1
- __author__ = "ziyan.yin"
2
- __date__ = "2025-01-10"
3
-
4
- from .redis import Redis, RedisCli, RedisMaker
5
-
6
- __all__ = [
7
- "Redis",
8
- "RedisMaker",
9
- "RedisCli"
10
- ]
@@ -1,52 +0,0 @@
1
- __author__ = "ziyan.yin"
2
- __date__ = "2025-01-17"
3
-
4
-
5
- from typing import Annotated, AsyncGenerator
6
-
7
- from fastapi.params import Depends
8
- from pydantic import BaseModel, Field, RedisDsn
9
- from redis.asyncio import ConnectionPool, Redis
10
-
11
- from fastapi_extra.settings import Settings
12
-
13
-
14
- class RedisConfig(BaseModel):
15
- url: RedisDsn = RedisDsn("redis://localhost:6379/0")
16
- max_connections: int | None = None
17
- connection_kwargs: dict = Field(default_factory=dict)
18
-
19
-
20
- class DefaultRedisSettings(Settings):
21
- redis: RedisConfig
22
-
23
-
24
- def load_pool(config: RedisConfig) -> ConnectionPool:
25
- return ConnectionPool.from_url(
26
- config.url,
27
- **config.model_dump(exclude_defaults=True, exclude={"url", "connection_kwargs"}),
28
- **config.connection_kwargs
29
- )
30
-
31
-
32
- _settings = DefaultRedisSettings() # type: ignore
33
- DEFAULT_POOL = load_pool(_settings.redis)
34
-
35
-
36
- async def dispose() -> None:
37
- await DEFAULT_POOL.aclose()
38
-
39
-
40
- class RedisMaker(Depends):
41
-
42
- def __init__(self, pool: ConnectionPool):
43
- super().__init__()
44
- self.dependency = self
45
- self.pool = pool
46
-
47
- async def __call__(self) -> AsyncGenerator[Redis, None]:
48
- async with Redis(connection_pool=self.pool) as session:
49
- yield session
50
-
51
-
52
- RedisCli = Annotated[Redis, RedisMaker(DEFAULT_POOL)]
@@ -1,50 +0,0 @@
1
- __author__ = "ziyan.yin"
2
- __date__ = "2024-12-26"
3
-
4
-
5
- from typing import Any, Literal
6
-
7
- from pydantic import AnyUrl, BaseModel, Field
8
- from sqlalchemy import Engine, NullPool
9
- from sqlalchemy.util import _concurrency_py3k
10
- from sqlmodel import create_engine
11
-
12
- from fastapi_extra.settings import Settings
13
-
14
-
15
- class DatabaseConfig(BaseModel):
16
- url: AnyUrl
17
- echo: bool = False
18
- echo_pool: bool = False
19
- isolation_level: Literal[
20
- "SERIALIZABLE",
21
- "REPEATABLE READ",
22
- "READ COMMITTED",
23
- "READ UNCOMMITTED",
24
- "AUTOCOMMIT",
25
- ] | None = None
26
- options: dict = Field(default_factory=dict)
27
-
28
-
29
- class DefaultDatabaseSettings(Settings):
30
- datasource: DatabaseConfig
31
-
32
-
33
- def load_engine(config: DatabaseConfig, **kw: Any) -> Engine:
34
- return create_engine(
35
- url=str(config.url),
36
- **config.model_dump(exclude_defaults=True, exclude={"url", "options"}),
37
- **config.options,
38
- **kw
39
- )
40
-
41
-
42
- _settings = DefaultDatabaseSettings() # type: ignore
43
- if _settings.mode == "test":
44
- DEFAULT_ENGINE: Engine = load_engine(_settings.datasource, poolclass=NullPool)
45
- else:
46
- DEFAULT_ENGINE: Engine = load_engine(_settings.datasource)
47
-
48
-
49
- async def dispose() -> None:
50
- await _concurrency_py3k.greenlet_spawn(DEFAULT_ENGINE.dispose)
@@ -1,39 +0,0 @@
1
- __author__ = "ziyan.yin"
2
- __date__ = "2025-01-05"
3
-
4
-
5
- from typing import Annotated, AsyncGenerator, Generator
6
-
7
- from fastapi.params import Depends
8
- from sqlalchemy.ext.asyncio import AsyncEngine
9
- from sqlmodel import Session as _Session
10
- from sqlmodel.ext.asyncio.session import AsyncSession
11
-
12
- from fastapi_extra.database.engine import DEFAULT_ENGINE, Engine
13
-
14
-
15
- class SessionMaker(Depends):
16
- __slots__ = ("engine", )
17
-
18
- def __init__(self, engine: Engine):
19
- super().__init__()
20
- self.engine = engine
21
- self.dependency = self
22
-
23
- def __call__(self) -> Generator[_Session, None, None]:
24
- with _Session(self.engine) as session:
25
- yield session
26
-
27
-
28
- class AsyncSessionMaker(SessionMaker):
29
-
30
- def __init__(self, engine: Engine):
31
- super().__init__(engine)
32
- self.async_engine: AsyncEngine = AsyncEngine(self.engine)
33
-
34
- async def __call__(self) -> AsyncGenerator[AsyncSession, None]:
35
- async with AsyncSession(self.async_engine) as session:
36
- yield session
37
-
38
-
39
- DefaultSession = Annotated[AsyncSession, AsyncSessionMaker(DEFAULT_ENGINE)]
File without changes
File without changes
File without changes