fastapi-factory-utilities 0.2.0__py3-none-any.whl → 0.7.1__py3-none-any.whl

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.

Potentially problematic release.


This version of fastapi-factory-utilities might be problematic. Click here for more details.

Files changed (72) hide show
  1. fastapi_factory_utilities/core/api/__init__.py +1 -1
  2. fastapi_factory_utilities/core/api/v1/sys/health.py +1 -1
  3. fastapi_factory_utilities/core/app/__init__.py +12 -3
  4. fastapi_factory_utilities/core/app/application.py +24 -26
  5. fastapi_factory_utilities/core/app/builder.py +23 -37
  6. fastapi_factory_utilities/core/app/config.py +22 -1
  7. fastapi_factory_utilities/core/app/fastapi_builder.py +3 -2
  8. fastapi_factory_utilities/core/exceptions.py +58 -22
  9. fastapi_factory_utilities/core/plugins/__init__.py +2 -31
  10. fastapi_factory_utilities/core/plugins/abstracts.py +40 -0
  11. fastapi_factory_utilities/core/plugins/aiopika/__init__.py +25 -0
  12. fastapi_factory_utilities/core/plugins/aiopika/abstract.py +48 -0
  13. fastapi_factory_utilities/core/plugins/aiopika/configs.py +85 -0
  14. fastapi_factory_utilities/core/plugins/aiopika/depends.py +20 -0
  15. fastapi_factory_utilities/core/plugins/aiopika/exceptions.py +29 -0
  16. fastapi_factory_utilities/core/plugins/aiopika/exchange.py +70 -0
  17. fastapi_factory_utilities/core/plugins/aiopika/listener/__init__.py +7 -0
  18. fastapi_factory_utilities/core/plugins/aiopika/listener/abstract.py +72 -0
  19. fastapi_factory_utilities/core/plugins/aiopika/message.py +86 -0
  20. fastapi_factory_utilities/core/plugins/aiopika/plugins.py +84 -0
  21. fastapi_factory_utilities/core/plugins/aiopika/publisher/__init__.py +7 -0
  22. fastapi_factory_utilities/core/plugins/aiopika/publisher/abstract.py +66 -0
  23. fastapi_factory_utilities/core/plugins/aiopika/queue.py +86 -0
  24. fastapi_factory_utilities/core/plugins/odm_plugin/__init__.py +25 -153
  25. fastapi_factory_utilities/core/plugins/odm_plugin/builder.py +59 -31
  26. fastapi_factory_utilities/core/plugins/odm_plugin/configs.py +1 -1
  27. fastapi_factory_utilities/core/plugins/odm_plugin/documents.py +2 -1
  28. fastapi_factory_utilities/core/plugins/odm_plugin/helpers.py +16 -0
  29. fastapi_factory_utilities/core/plugins/odm_plugin/plugins.py +155 -0
  30. fastapi_factory_utilities/core/plugins/odm_plugin/repositories.py +112 -3
  31. fastapi_factory_utilities/core/plugins/opentelemetry_plugin/__init__.py +8 -115
  32. fastapi_factory_utilities/core/plugins/opentelemetry_plugin/builder.py +65 -14
  33. fastapi_factory_utilities/core/plugins/opentelemetry_plugin/configs.py +13 -0
  34. fastapi_factory_utilities/core/plugins/opentelemetry_plugin/instruments/__init__.py +85 -0
  35. fastapi_factory_utilities/core/plugins/opentelemetry_plugin/plugins.py +137 -0
  36. fastapi_factory_utilities/core/plugins/taskiq_plugins/__init__.py +29 -0
  37. fastapi_factory_utilities/core/plugins/taskiq_plugins/configs.py +12 -0
  38. fastapi_factory_utilities/core/plugins/taskiq_plugins/depends.py +51 -0
  39. fastapi_factory_utilities/core/plugins/taskiq_plugins/exceptions.py +13 -0
  40. fastapi_factory_utilities/core/plugins/taskiq_plugins/plugin.py +41 -0
  41. fastapi_factory_utilities/core/plugins/taskiq_plugins/schedulers.py +187 -0
  42. fastapi_factory_utilities/core/protocols.py +1 -54
  43. fastapi_factory_utilities/core/security/jwt.py +159 -0
  44. fastapi_factory_utilities/core/security/kratos.py +98 -0
  45. fastapi_factory_utilities/core/services/hydra/__init__.py +13 -0
  46. fastapi_factory_utilities/core/services/hydra/exceptions.py +15 -0
  47. fastapi_factory_utilities/core/services/hydra/objects.py +26 -0
  48. fastapi_factory_utilities/core/services/hydra/services.py +122 -0
  49. fastapi_factory_utilities/core/services/kratos/__init__.py +13 -0
  50. fastapi_factory_utilities/core/services/kratos/enums.py +11 -0
  51. fastapi_factory_utilities/core/services/kratos/exceptions.py +15 -0
  52. fastapi_factory_utilities/core/services/kratos/objects.py +43 -0
  53. fastapi_factory_utilities/core/services/kratos/services.py +86 -0
  54. fastapi_factory_utilities/core/services/status/__init__.py +2 -2
  55. fastapi_factory_utilities/core/utils/status.py +2 -1
  56. fastapi_factory_utilities/core/utils/uvicorn.py +36 -0
  57. fastapi_factory_utilities/core/utils/yaml_reader.py +2 -2
  58. fastapi_factory_utilities/example/app.py +15 -5
  59. fastapi_factory_utilities/example/entities/books/__init__.py +1 -1
  60. fastapi_factory_utilities/example/models/books/__init__.py +1 -1
  61. fastapi_factory_utilities/py.typed +0 -0
  62. {fastapi_factory_utilities-0.2.0.dist-info → fastapi_factory_utilities-0.7.1.dist-info}/METADATA +23 -14
  63. fastapi_factory_utilities-0.7.1.dist-info/RECORD +101 -0
  64. {fastapi_factory_utilities-0.2.0.dist-info → fastapi_factory_utilities-0.7.1.dist-info}/WHEEL +1 -1
  65. fastapi_factory_utilities/core/app/plugin_manager/__init__.py +0 -15
  66. fastapi_factory_utilities/core/app/plugin_manager/exceptions.py +0 -33
  67. fastapi_factory_utilities/core/app/plugin_manager/plugin_manager.py +0 -190
  68. fastapi_factory_utilities/core/plugins/example/__init__.py +0 -31
  69. fastapi_factory_utilities/core/plugins/httpx_plugin/__init__.py +0 -31
  70. fastapi_factory_utilities-0.2.0.dist-info/RECORD +0 -70
  71. {fastapi_factory_utilities-0.2.0.dist-info → fastapi_factory_utilities-0.7.1.dist-info}/entry_points.txt +0 -0
  72. {fastapi_factory_utilities-0.2.0.dist-info → fastapi_factory_utilities-0.7.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,122 @@
1
+ """Provides a service to interact with the Hydra service."""
2
+
3
+ from base64 import b64encode
4
+ from http import HTTPStatus
5
+ from typing import Annotated
6
+
7
+ import aiohttp
8
+ from fastapi import Depends
9
+ from pydantic import ValidationError
10
+
11
+ from fastapi_factory_utilities.core.app import (
12
+ DependencyConfig,
13
+ HttpServiceDependencyConfig,
14
+ depends_dependency_config,
15
+ )
16
+
17
+ from .exceptions import HydraOperationError, HydraTokenInvalidError
18
+ from .objects import HydraTokenIntrospectObject
19
+
20
+
21
+ class HydraService:
22
+ """Service to interact with the Hydra service."""
23
+
24
+ INTROSPECT_ENDPOINT: str = "/admin/oauth2/introspect"
25
+ CLIENT_CREDENTIALS_ENDPOINT: str = "/oauth2/token"
26
+
27
+ def __init__(
28
+ self,
29
+ hydra_admin_http_config: HttpServiceDependencyConfig,
30
+ hydra_public_http_config: HttpServiceDependencyConfig,
31
+ ) -> None:
32
+ """Instanciate the Hydra service.
33
+
34
+ Args:
35
+ hydra_admin_http_config (HttpServiceDependencyConfig): The Hydra admin HTTP configuration.
36
+ hydra_public_http_config (HttpServiceDependencyConfig): The Hydra public HTTP configuration.
37
+ """
38
+ self._hydra_admin_http_config: HttpServiceDependencyConfig = hydra_admin_http_config
39
+ self._hydra_public_http_config: HttpServiceDependencyConfig = hydra_public_http_config
40
+
41
+ async def introspect(self, token: str) -> HydraTokenIntrospectObject:
42
+ """Introspects a token using the Hydra service.
43
+
44
+ Args:
45
+ token (str): The token to introspect.
46
+
47
+ Raises:
48
+ HydraOperationError: If the introspection fails.
49
+ HydraTokenInvalidError: If the token is invalid.
50
+ """
51
+ async with aiohttp.ClientSession(
52
+ base_url=str(self._hydra_admin_http_config.url),
53
+ ) as session:
54
+ async with session.post(
55
+ url=self.INTROSPECT_ENDPOINT,
56
+ data={"token": token},
57
+ ) as response:
58
+ if response.status != HTTPStatus.OK:
59
+ raise HydraTokenInvalidError()
60
+
61
+ try:
62
+ instrospect: HydraTokenIntrospectObject = HydraTokenIntrospectObject(**await response.json())
63
+ except ValidationError as error:
64
+ raise HydraOperationError() from error
65
+
66
+ return instrospect
67
+
68
+ async def oauth2_client_credentials(self, client_id: str, client_secret: str, scope: str) -> str:
69
+ """Get the OAuth2 client credentials.
70
+
71
+ Args:
72
+ client_id (str): The client ID.
73
+ client_secret (str): The client secret.
74
+ scope (str): The scope.
75
+
76
+ Returns:
77
+ str: The access token.
78
+
79
+ Raises:
80
+ HydraOperationError: If the client credentials request fails.
81
+ """
82
+ # Create base64 encoded Basic Auth header
83
+ auth_string = f"{client_id}:{client_secret}"
84
+ auth_bytes = auth_string.encode("utf-8")
85
+ auth_b64 = b64encode(auth_bytes).decode("utf-8")
86
+
87
+ async with aiohttp.ClientSession(
88
+ base_url=str(self._hydra_public_http_config.url),
89
+ ) as session:
90
+ async with session.post(
91
+ url=self.CLIENT_CREDENTIALS_ENDPOINT,
92
+ headers={"Authorization": f"Basic {auth_b64}"},
93
+ data={"grant_type": "client_credentials", "scope": scope},
94
+ ) as response:
95
+ response_data = await response.json()
96
+ if response.status != HTTPStatus.OK:
97
+ raise HydraOperationError(f"Failed to get client credentials: {response_data}")
98
+
99
+ return response_data["access_token"]
100
+
101
+
102
+ def depends_hydra_service(
103
+ dependency_config: Annotated[DependencyConfig, Depends(depends_dependency_config)],
104
+ ) -> HydraService:
105
+ """Dependency injection for the Hydra service.
106
+
107
+ Args:
108
+ dependency_config (DependencyConfig): The dependency configuration.
109
+
110
+ Returns:
111
+ HydraService: The Hydra service instance.
112
+
113
+ Raises:
114
+ HydraOperationError: If the Hydra admin or public dependency is not configured.
115
+ """
116
+ if dependency_config.hydra_admin is None or dependency_config.hydra_public is None:
117
+ raise HydraOperationError(message="Hydra admin or public dependency not configured")
118
+
119
+ return HydraService(
120
+ hydra_admin_http_config=dependency_config.hydra_admin,
121
+ hydra_public_http_config=dependency_config.hydra_public,
122
+ )
@@ -0,0 +1,13 @@
1
+ """Kratos service module."""
2
+
3
+ from .exceptions import KratosOperationError, KratosSessionInvalidError
4
+ from .objects import KratosSessionObject
5
+ from .services import KratosService, depends_kratos_service
6
+
7
+ __all__: list[str] = [
8
+ "KratosOperationError",
9
+ "KratosService",
10
+ "KratosSessionInvalidError",
11
+ "KratosSessionObject",
12
+ "depends_kratos_service",
13
+ ]
@@ -0,0 +1,11 @@
1
+ """Provides enums for Kratos."""
2
+
3
+ from enum import StrEnum
4
+
5
+
6
+ class AuthenticatorAssuranceLevelEnum(StrEnum):
7
+ """Enum for Authenticator Assurance Level (AAL)."""
8
+
9
+ AAL1 = "aal1"
10
+ AAL2 = "aal2"
11
+ AAL3 = "aal3"
@@ -0,0 +1,15 @@
1
+ """Python exceptions for the Kratos service."""
2
+
3
+ from fastapi_factory_utilities.core.exceptions import FastAPIFactoryUtilitiesError
4
+
5
+
6
+ class KratosError(FastAPIFactoryUtilitiesError):
7
+ """Base class for all exceptions raised by the Kratos service."""
8
+
9
+
10
+ class KratosOperationError(KratosError):
11
+ """Exception raised when a Kratos operation fails."""
12
+
13
+
14
+ class KratosSessionInvalidError(KratosOperationError):
15
+ """Exception raised when a Kratos session is invalid."""
@@ -0,0 +1,43 @@
1
+ """Provides the Kratos Objects."""
2
+
3
+ import datetime
4
+ import uuid
5
+ from typing import ClassVar
6
+
7
+ from pydantic import BaseModel, ConfigDict
8
+
9
+ from .enums import AuthenticatorAssuranceLevelEnum
10
+
11
+
12
+ class KratosTraitsObject(BaseModel):
13
+ """Traits for Kratos."""
14
+
15
+ model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
16
+
17
+ email: str
18
+ username: str
19
+ realm_id: uuid.UUID
20
+
21
+
22
+ class KratosIdentityObject(BaseModel):
23
+ """Identity for Kratos."""
24
+
25
+ model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
26
+
27
+ id: uuid.UUID
28
+ state: str
29
+ traits: KratosTraitsObject
30
+
31
+
32
+ class KratosSessionObject(BaseModel):
33
+ """Session object for Kratos."""
34
+
35
+ model_config: ClassVar[ConfigDict] = ConfigDict(extra="ignore")
36
+
37
+ id: uuid.UUID
38
+ active: bool
39
+ issued_at: datetime.datetime
40
+ expires_at: datetime.datetime
41
+ authenticated_at: datetime.datetime
42
+ authenticator_assurance_level: AuthenticatorAssuranceLevelEnum
43
+ identity: KratosIdentityObject
@@ -0,0 +1,86 @@
1
+ """Provides the KratosService class for handling Kratos operations."""
2
+
3
+ from http import HTTPStatus
4
+ from typing import Annotated
5
+
6
+ import aiohttp
7
+ from fastapi import Depends
8
+ from pydantic import ValidationError
9
+
10
+ from fastapi_factory_utilities.core.app import (
11
+ DependencyConfig,
12
+ HttpServiceDependencyConfig,
13
+ depends_dependency_config,
14
+ )
15
+
16
+ from .exceptions import KratosOperationError, KratosSessionInvalidError
17
+ from .objects import KratosSessionObject
18
+
19
+
20
+ class KratosService:
21
+ """Service class for handling Kratos operations."""
22
+
23
+ COOKIE_NAME: str = "ory_kratos_session"
24
+
25
+ def __init__(self, kratos_http_config: HttpServiceDependencyConfig) -> None:
26
+ """Initialize the KratosService class.
27
+
28
+ Args:
29
+ kratos_http_config (HttpServiceDependencyConfig): Kratos HTTP configuration.
30
+ """
31
+ self._http_config: HttpServiceDependencyConfig = kratos_http_config
32
+
33
+ async def whoami(self, cookie_value: str) -> KratosSessionObject:
34
+ """Get the current user session.
35
+
36
+ Args:
37
+ cookie_value (str): Cookie value.
38
+
39
+ Returns:
40
+ KratosSessionObject: Kratos session object.
41
+
42
+ Raises:
43
+ KratosOperationError: If the Kratos service returns an error.
44
+ KratosSessionInvalidError: If the Kratos session is invalid.
45
+ """
46
+ cookies: dict[str, str] = {self.COOKIE_NAME: cookie_value}
47
+ async with aiohttp.ClientSession(base_url=str(self._http_config.url), cookies=cookies) as session:
48
+ async with session.get(
49
+ url="/sessions/whoami",
50
+ ) as response:
51
+ if response.status >= HTTPStatus.INTERNAL_SERVER_ERROR.value:
52
+ raise KratosOperationError(message=f"Kratos service error: {response.status} - {response.reason}")
53
+ if response.status == HTTPStatus.UNAUTHORIZED:
54
+ raise KratosSessionInvalidError(
55
+ message=f"Kratos session invalid: {response.status} - {response.reason}"
56
+ )
57
+ if response.status != HTTPStatus.OK:
58
+ raise KratosOperationError(message=f"Kratos service error: {response.status} - {response.reason}")
59
+
60
+ try:
61
+ kratos_session: KratosSessionObject = KratosSessionObject(**await response.json())
62
+ except ValidationError as e:
63
+ raise KratosOperationError(message=f"Kratos service error: {e}") from e
64
+
65
+ return kratos_session
66
+
67
+
68
+ def depends_kratos_service(
69
+ dependency_config: Annotated[DependencyConfig, Depends(depends_dependency_config)],
70
+ ) -> KratosService:
71
+ """Dependency function to get the Kratos service instance.
72
+
73
+ Args:
74
+ dependency_config (DependencyConfig): Dependency configuration.
75
+
76
+ Returns:
77
+ KratosService: Kratos service instance.
78
+
79
+ Raises:
80
+ KratosOperationError: If the Kratos dependency is not configured.
81
+ """
82
+ if dependency_config.kratos is None:
83
+ raise KratosOperationError(message="Kratos dependency not configured")
84
+ return KratosService(
85
+ kratos_http_config=dependency_config.kratos,
86
+ )
@@ -5,10 +5,10 @@ from .services import StatusService
5
5
  from .types import ComponentInstanceType, Status
6
6
 
7
7
  __all__: list[str] = [
8
- "ComponentTypeEnum",
9
8
  "ComponentInstanceType",
9
+ "ComponentTypeEnum",
10
10
  "HealthStatusEnum",
11
11
  "ReadinessStatusEnum",
12
- "StatusService",
13
12
  "Status",
13
+ "StatusService",
14
14
  ]
@@ -3,6 +3,7 @@
3
3
  ```python
4
4
  # Example of using the MonitoredAbstract
5
5
 
6
+
6
7
  class MyMonitored(MonitoredAbstract):
7
8
  def __init__(self, status_service: StatusService) -> None:
8
9
  super().__init__(
@@ -63,9 +64,9 @@ class MonitoredAbstract(ABC):
63
64
 
64
65
 
65
66
  __all__: list[str] = [
66
- "MonitoredAbstract",
67
67
  "ComponentInstanceType",
68
68
  "ComponentTypeEnum",
69
+ "MonitoredAbstract",
69
70
  "Status",
70
71
  "StatusService",
71
72
  ]
@@ -1,5 +1,8 @@
1
1
  """Provides utilities for the application."""
2
2
 
3
+ import os
4
+ from typing import Any
5
+
3
6
  import uvicorn
4
7
  import uvicorn.server
5
8
 
@@ -20,6 +23,29 @@ class UvicornUtils:
20
23
  None
21
24
  """
22
25
  self._app: ApplicationAbstractProtocol = app
26
+ self._ssl_keyfile: str | os.PathLike[str] | None = None
27
+ self._ssl_certfile: str | os.PathLike[str] | None = None
28
+ self._ssl_keyfile_password: str | None = None
29
+
30
+ def add_ssl_certificates(
31
+ self,
32
+ ssl_keyfile: str | os.PathLike[str] | None = None,
33
+ ssl_certfile: str | os.PathLike[str] | None = None,
34
+ ssl_keyfile_password: str | None = None,
35
+ ) -> None:
36
+ """Add SSL certificates to the application.
37
+
38
+ Args:
39
+ ssl_keyfile (str | os.PathLike[str] | None): The SSL key file.
40
+ ssl_certfile (str | os.PathLike[str] | None): The SSL certificate file.
41
+ ssl_keyfile_password (str | None): The SSL key file password.
42
+
43
+ Returns:
44
+ None
45
+ """
46
+ self._ssl_keyfile = ssl_keyfile
47
+ self._ssl_certfile = ssl_certfile
48
+ self._ssl_keyfile_password = ssl_keyfile_password
23
49
 
24
50
  def build_uvicorn_config(self) -> uvicorn.Config:
25
51
  """Build the Uvicorn configuration.
@@ -27,12 +53,22 @@ class UvicornUtils:
27
53
  Returns:
28
54
  uvicorn.Config: The Uvicorn configuration.
29
55
  """
56
+ kwargs: dict[str, Any] = {}
57
+
58
+ if self._ssl_keyfile:
59
+ kwargs["ssl_keyfile"] = self._ssl_keyfile
60
+ if self._ssl_certfile:
61
+ kwargs["ssl_certfile"] = self._ssl_certfile
62
+ if self._ssl_keyfile_password:
63
+ kwargs["ssl_keyfile_password"] = self._ssl_keyfile_password
64
+
30
65
  config = uvicorn.Config(
31
66
  app=self._app.get_asgi_app(),
32
67
  host=self._app.get_config().server.host,
33
68
  port=self._app.get_config().server.port,
34
69
  reload=self._app.get_config().development.reload,
35
70
  workers=self._app.get_config().server.workers,
71
+ **kwargs,
36
72
  )
37
73
  clean_uvicorn_logger()
38
74
  return config
@@ -29,7 +29,7 @@ class UnableToReadYamlFileError(Exception):
29
29
  class YamlFileReader:
30
30
  """Handles reading YAML files and converting them to Pydantic models."""
31
31
 
32
- re_pattern: re.Pattern[str] = re.compile(r"\${([A-Za-z0-9\-\_]+):?([A-Za-z0-9\-\_\/\:]*)?}")
32
+ re_pattern: re.Pattern[str] = re.compile(r"\${([A-Za-z0-9\-\_]+):?([A-Za-z0-9\-\_\/\:\.]*)?}")
33
33
 
34
34
  def __init__(
35
35
  self,
@@ -72,7 +72,7 @@ class YamlFileReader:
72
72
  try:
73
73
  yaml_data = yaml_data[key]
74
74
  except KeyError:
75
- logger.warning(f"Base key {key}" " not found in YAML file" + " from {self._yaml_base_key}")
75
+ logger.warning(f"Base key {key} not found in YAML file" + " from {self._yaml_base_key}")
76
76
  return dict()
77
77
  return yaml_data
78
78
 
@@ -7,7 +7,9 @@ from beanie import Document
7
7
  from fastapi_factory_utilities.core.app.application import ApplicationAbstract
8
8
  from fastapi_factory_utilities.core.app.builder import ApplicationGenericBuilder
9
9
  from fastapi_factory_utilities.core.app.config import RootConfig
10
- from fastapi_factory_utilities.core.plugins import PluginsEnum
10
+ from fastapi_factory_utilities.core.plugins.abstracts import PluginAbstract
11
+ from fastapi_factory_utilities.core.plugins.odm_plugin import ODMPlugin
12
+ from fastapi_factory_utilities.core.plugins.opentelemetry_plugin import OpenTelemetryPlugin
11
13
  from fastapi_factory_utilities.example.models.books.document import BookDocument
12
14
 
13
15
 
@@ -26,12 +28,11 @@ class App(ApplicationAbstract):
26
28
 
27
29
  ODM_DOCUMENT_MODELS: ClassVar[list[type[Document]]] = [BookDocument]
28
30
 
29
- DEFAULT_PLUGINS_ACTIVATED: ClassVar[list[PluginsEnum]] = [PluginsEnum.OPENTELEMETRY_PLUGIN, PluginsEnum.ODM_PLUGIN]
30
-
31
31
  def configure(self) -> None:
32
32
  """Configure the application."""
33
33
  # Prevent circular import
34
- from .api import api_router # pylint: disable=import-outside-toplevel
34
+ # pylint: disable=import-outside-toplevel
35
+ from .api import api_router # noqa: PLC0415
35
36
 
36
37
  self.get_asgi_app().include_router(router=api_router)
37
38
 
@@ -47,4 +48,13 @@ class App(ApplicationAbstract):
47
48
  class AppBuilder(ApplicationGenericBuilder[App]):
48
49
  """Application builder for the App application."""
49
50
 
50
- pass
51
+ def get_default_plugins(self) -> list[PluginAbstract]:
52
+ """Get the default plugins."""
53
+ return [ODMPlugin(), OpenTelemetryPlugin()]
54
+
55
+ def __init__(self, plugins: list[PluginAbstract] | None = None) -> None:
56
+ """Initialize the AppBuilder."""
57
+ # If no plugins are provided, use the default plugins
58
+ if plugins is None:
59
+ plugins = self.get_default_plugins()
60
+ super().__init__(plugins=plugins)
@@ -4,4 +4,4 @@ from .entities import BookEntity
4
4
  from .enums import BookType
5
5
  from .types import BookName
6
6
 
7
- __all__: list[str] = ["BookEntity", "BookType", "BookName"]
7
+ __all__: list[str] = ["BookEntity", "BookName", "BookType"]
@@ -3,4 +3,4 @@
3
3
  from .document import BookDocument
4
4
  from .repository import BookRepository
5
5
 
6
- __all__: list[str] = ["BookRepository", "BookDocument"]
6
+ __all__: list[str] = ["BookDocument", "BookRepository"]
File without changes
@@ -1,9 +1,10 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: fastapi_factory_utilities
3
- Version: 0.2.0
4
- Summary: Consolidate libraries and utilities to create microservices in Python with FastAPI, Beanie, Httpx, AioPika and OpenTelemetry.
3
+ Version: 0.7.1
4
+ Summary: Consolidate libraries and utilities to create microservices in Python with FastAPI, Beanie, Taskiq, AioPika and OpenTelemetry.
5
5
  License: MIT
6
- Keywords: python,fastapi,beanie,httpx,opentelemetry,microservices
6
+ License-File: LICENSE
7
+ Keywords: python,fastapi,beanie,taskiq,opentelemetry,microservices
7
8
  Author: miragecentury
8
9
  Author-email: victorien.vanroye@gmail.com
9
10
  Maintainer: miragecentury
@@ -18,23 +19,31 @@ Classifier: Programming Language :: Python :: 3.12
18
19
  Classifier: Topic :: Software Development :: Libraries
19
20
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
20
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
- Requires-Dist: beanie (>=1.27.0,<2.0.0)
22
- Requires-Dist: fastapi (>=0.115.4,<0.116.0)
23
- Requires-Dist: httpx (>=0.28.1,<0.29.0)
22
+ Requires-Dist: aio-pika (>=9.5.7,<10.0.0)
23
+ Requires-Dist: aiohttp[speedups] (>=3.12.13,<4.0.0)
24
+ Requires-Dist: beanie (>=1.30.0,<2.0.0)
25
+ Requires-Dist: fastapi (>=0.115.13,<1)
26
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (>=1.26.0,<2.0.0)
24
27
  Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.26.0,<2.0.0)
25
- Requires-Dist: opentelemetry-instrumentation-fastapi (>=0.49b1,<0.50)
26
- Requires-Dist: opentelemetry-instrumentation-pymongo (>=0.49b2,<0.50)
28
+ Requires-Dist: opentelemetry-instrumentation-aio-pika (>=0.59b0,<0.60)
29
+ Requires-Dist: opentelemetry-instrumentation-aiohttp-client (>=0,<1)
30
+ Requires-Dist: opentelemetry-instrumentation-fastapi (>=0,<1)
31
+ Requires-Dist: opentelemetry-instrumentation-pymongo (>=0,<1)
27
32
  Requires-Dist: opentelemetry-propagator-b3 (>=1.26.0,<2.0.0)
28
33
  Requires-Dist: opentelemetry-sdk (>=1.26.0,<2.0.0)
29
34
  Requires-Dist: pyaml (>=25.1.0,<26.0.0)
30
35
  Requires-Dist: pydantic (>=2.8.2,<3.0.0)
31
- Requires-Dist: pymongo (>=4.9.2,<4.10.0)
36
+ Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
37
+ Requires-Dist: pymongo (>=4.9.2,<4.16.0)
32
38
  Requires-Dist: reactivex (>=4.0.4,<5.0.0)
33
39
  Requires-Dist: structlog (>=24.1,<26.0)
34
- Requires-Dist: typer (>=0.15.1,<0.16.0)
35
- Requires-Dist: uvicorn (>=0.32.0,<0.33.0)
36
- Project-URL: Homepage, https://github.com/miragecentury/fastapi_factory_utilities
37
- Project-URL: Repository, https://github.com/miragecentury/fastapi_factory_utilities
40
+ Requires-Dist: taskiq-dependencies (>=1.5.8,<2.0.0)
41
+ Requires-Dist: taskiq-fastapi (>=0.3.5,<0.4.0)
42
+ Requires-Dist: taskiq-redis (>=1.0.9,<2.0.0)
43
+ Requires-Dist: typer (>=0,<1)
44
+ Requires-Dist: uvicorn (>=0.34.3,<1)
45
+ Project-URL: Homepage, https://github.com/DeerHide/fastapi_factory_utilities
46
+ Project-URL: Repository, https://github.com/DeerHide/fastapi_factory_utilities
38
47
  Description-Content-Type: text/markdown
39
48
 
40
49
  # fastapi_factory_utilities
@@ -0,0 +1,101 @@
1
+ fastapi_factory_utilities/__main__.py,sha256=w8o9KpyHcxGyLHmTK8ixbIqJIsB3NbIGuAMY7OfnxrA,147
2
+ fastapi_factory_utilities/core/__init__.py,sha256=tt5a-MgeFt_oACkc9K5xl2rynIbca9DGqsKBDEqGzto,34
3
+ fastapi_factory_utilities/core/api/__init__.py,sha256=fAwgRcRry1nIyAL7CsCbpwR87n5tWnBWKEPH6QZCpp8,542
4
+ fastapi_factory_utilities/core/api/tags.py,sha256=3hQcTeW0FS78sPTJ2PB44dMDTSkoW-xKj7rrfKX2Lk0,154
5
+ fastapi_factory_utilities/core/api/v1/sys/__init__.py,sha256=mTXhpn3_KgQ1snt0-0PFmGvFr4n5srQRRADEdRSGFJM,345
6
+ fastapi_factory_utilities/core/api/v1/sys/health.py,sha256=LILUwfyqeBDlf4GSpkU_3bxMjZPCLanOBkMGDKtQufY,2867
7
+ fastapi_factory_utilities/core/api/v1/sys/readiness.py,sha256=xIY8pQLShU7KWRtlOUK5gTDyZ8aB1KBvLczC6boT-tg,1711
8
+ fastapi_factory_utilities/core/app/__init__.py,sha256=6Ee_zK-KzJWoOElsAnGdBz8giRJlhAPMaEzs0I3gvrA,596
9
+ fastapi_factory_utilities/core/app/application.py,sha256=fJTKgfqBWD1xzE7Y9ToZEBDrd1PxHtnZ7SHLmkS1S1M,4737
10
+ fastapi_factory_utilities/core/app/builder.py,sha256=gkmhGVYFu2zbW5sxWyh6Pn5ysdCIvn1_WqNJMKls54s,3820
11
+ fastapi_factory_utilities/core/app/config.py,sha256=MuV4G_M4QgZWYHoulusJLv_m4Qr2php-Cg9Jum4qkNA,7303
12
+ fastapi_factory_utilities/core/app/enums.py,sha256=X1upnaehYU0eHExXTde5xsH-pI9q7HZDNsOEF5PApdg,226
13
+ fastapi_factory_utilities/core/app/exceptions.py,sha256=tQDf0_4j5xgCbku7TL7JaZGs3_bjsWG2YLBCydQJpPw,664
14
+ fastapi_factory_utilities/core/app/fastapi_builder.py,sha256=7egWkS98nTiVBe2Bw5dQzDBryQCkz4w7gnY9HA24NFg,2855
15
+ fastapi_factory_utilities/core/exceptions.py,sha256=9s-i_RHPCsOswQPRvK8tyy-smE1118pytxWDJMNEl0M,2670
16
+ fastapi_factory_utilities/core/plugins/__init__.py,sha256=7ctIv2Jx2uOUpgm8ITFRuZxHi6OXvlVS0VcbszVzvis,114
17
+ fastapi_factory_utilities/core/plugins/abstracts.py,sha256=p5iXmeOVD737G73I2keKV0Y-jLGQf2vYbTSR1LgrA14,1165
18
+ fastapi_factory_utilities/core/plugins/aiopika/__init__.py,sha256=flXanbhbjFaOsgx7jZvtW0bVLT_NXHypIPFO1tIHoCM,705
19
+ fastapi_factory_utilities/core/plugins/aiopika/abstract.py,sha256=TcvDwdaLV0UqFHQy6Sn3Y5Fp-1463dhL7VNB9LTKL5I,1682
20
+ fastapi_factory_utilities/core/plugins/aiopika/configs.py,sha256=BEPE8Ss7q_yCyuii4nywmIgh67iNLdhKxUm8tC0PdCY,3706
21
+ fastapi_factory_utilities/core/plugins/aiopika/depends.py,sha256=5xna571hCIdqzbo0t1CW-yzmJMSpEw_KUeJh5nBXjwQ,761
22
+ fastapi_factory_utilities/core/plugins/aiopika/exceptions.py,sha256=AkBKT8HVPpJDAntLNlOLp6lpQpKI-k8gxNzU24FsOF0,985
23
+ fastapi_factory_utilities/core/plugins/aiopika/exchange.py,sha256=tQfrv7-mX4lrHY1OTXB_aTkbg_brYniIuFisfFDSTT8,2509
24
+ fastapi_factory_utilities/core/plugins/aiopika/listener/__init__.py,sha256=LsTyu6kGoUm7c5OvSQTDJL7d8CG9TeARBHHVe_HGsHY,148
25
+ fastapi_factory_utilities/core/plugins/aiopika/listener/abstract.py,sha256=cMRQwqCwa1kGs2PHXR5ws0-HLzqHC_XFZtu43Y7iTpA,2577
26
+ fastapi_factory_utilities/core/plugins/aiopika/message.py,sha256=Tn9pLZlkx238GA6SEav15Npq3TWu_dQ0NtzFWvPcPTM,2861
27
+ fastapi_factory_utilities/core/plugins/aiopika/plugins.py,sha256=0lLYt-ZG3g3m8wi1tCCPEhTUBqaXuEJOwHEcrvP7FYc,3284
28
+ fastapi_factory_utilities/core/plugins/aiopika/publisher/__init__.py,sha256=MGGdygH72xHZ4QAwvbUZKQt-_mPzLmMxHyAACSNnZ_c,151
29
+ fastapi_factory_utilities/core/plugins/aiopika/publisher/abstract.py,sha256=PmIG5zlx-tFUAneHtbknR0Ik5flz8pMTzxcreY2hZ7s,2494
30
+ fastapi_factory_utilities/core/plugins/aiopika/queue.py,sha256=u7iOOnv8EHO7q2qM6jDlYimajrjlQN8R3ftL61XgQIE,2794
31
+ fastapi_factory_utilities/core/plugins/odm_plugin/__init__.py,sha256=JsVz4GBiZRmm4qpUD1-Wsg2tRXTn-VO5fU-W1whZo4E,683
32
+ fastapi_factory_utilities/core/plugins/odm_plugin/builder.py,sha256=MdO1D0C9LQpoqtNFqgPwSYedLzXR6PArhkoJw6wzykg,8761
33
+ fastapi_factory_utilities/core/plugins/odm_plugin/configs.py,sha256=5bVbtsLwJhuIvt8FCzOvk002G8qFmZumWkN75bPKatc,331
34
+ fastapi_factory_utilities/core/plugins/odm_plugin/depends.py,sha256=OcLsfTLzMBk_xFV6qsMy_-qFkiphEbbEuaHUooagxg8,730
35
+ fastapi_factory_utilities/core/plugins/odm_plugin/documents.py,sha256=4aQNrD26d0S542_LYmxxm07Q8SWzbGe3PCpho6iWY5Q,1102
36
+ fastapi_factory_utilities/core/plugins/odm_plugin/exceptions.py,sha256=acnKJB0lGAzDs-7-LjBap8shjP3iV1a7dw7ouPVF27o,551
37
+ fastapi_factory_utilities/core/plugins/odm_plugin/helpers.py,sha256=s9iEujDZbTmTb9FByJAVUzLkjQpsciQT9dytkeosKuE,463
38
+ fastapi_factory_utilities/core/plugins/odm_plugin/plugins.py,sha256=0jsLtw4NxZB6bDcBLBvWHqB-RELoewsSXTJdAOGunSY,6443
39
+ fastapi_factory_utilities/core/plugins/odm_plugin/repositories.py,sha256=GJ3ovlxzTpiJ2_XlgARtwn6j0SbQxxAray_r8QWvGok,11313
40
+ fastapi_factory_utilities/core/plugins/opentelemetry_plugin/__init__.py,sha256=xXHn5dUxhgdDDQMpTHf3voolzS0E3zE2RFbtFHJzb38,641
41
+ fastapi_factory_utilities/core/plugins/opentelemetry_plugin/builder.py,sha256=9npQImifYAbEg0lFG7KwZ8V78SNrPoaINgd8vKitdMw,12509
42
+ fastapi_factory_utilities/core/plugins/opentelemetry_plugin/configs.py,sha256=pMG9leMB7rtdkdGFLIxXflV7bf9epGrrYPt2N97KZcM,3750
43
+ fastapi_factory_utilities/core/plugins/opentelemetry_plugin/exceptions.py,sha256=CpsHayfQpP0zghN8y5PP6TBy-cXhHoNxBR--I86gAdE,327
44
+ fastapi_factory_utilities/core/plugins/opentelemetry_plugin/helpers.py,sha256=qpTIzX67orJz7vy6SBIwRs24omMBoToJkhpurZRjPuk,1533
45
+ fastapi_factory_utilities/core/plugins/opentelemetry_plugin/instruments/__init__.py,sha256=lMtdd1DSDrFcXggf0qMpB1RJC7aBqWMpOBXxC8-bqPY,3307
46
+ fastapi_factory_utilities/core/plugins/opentelemetry_plugin/plugins.py,sha256=v9W4bqEljcRgeSL8pf-4yZ7SGXOFmxVoljthvcLdy6Q,5356
47
+ fastapi_factory_utilities/core/plugins/taskiq_plugins/__init__.py,sha256=6rjQJXYEsOOSP8Fhlc1oJJVnHBOAxkapUKTpbB2X-K0,776
48
+ fastapi_factory_utilities/core/plugins/taskiq_plugins/configs.py,sha256=O2rgFFWJNBUZ7ozfhM0Lj5Te-58qBNrQtw_N0mosAu4,304
49
+ fastapi_factory_utilities/core/plugins/taskiq_plugins/depends.py,sha256=XBxC1uUdMhYtqZDqmUEKI80y5L7mDoXXjN8dZjEvVAo,1602
50
+ fastapi_factory_utilities/core/plugins/taskiq_plugins/exceptions.py,sha256=1Xq_DqDFiQm2YU-2pGxziy-EeMhHiUsUwV0XdY_rQls,456
51
+ fastapi_factory_utilities/core/plugins/taskiq_plugins/plugin.py,sha256=gUlU2s7aLB2lkAAPdLqVc-BY16PessSYTGMGgNcKCSA,1602
52
+ fastapi_factory_utilities/core/plugins/taskiq_plugins/schedulers.py,sha256=82Yh7Y_MUxiWjrHPnsW_Ax-CWREAT6eC_bVl-HW5a3E,7208
53
+ fastapi_factory_utilities/core/protocols.py,sha256=w5FQqMAZ_OulG5hQWkoOapNSnxsjBPq4EjvWNXXdI_c,859
54
+ fastapi_factory_utilities/core/security/jwt.py,sha256=BrP9EmtuOxjafHKluYzRtGdZYankv72iQgG49t9TgJw,5640
55
+ fastapi_factory_utilities/core/security/kratos.py,sha256=yP9-TkELeXRPRYE9aQRlOPlwvaUJ7VQpyAea8ucWUfg,3364
56
+ fastapi_factory_utilities/core/services/hydra/__init__.py,sha256=AKBYNc3zW5OsCLLeFSXqmDbWbQefHUJxstLpsXLQEUQ,369
57
+ fastapi_factory_utilities/core/services/hydra/exceptions.py,sha256=ePMrfZturU2IVcxOebR0CbVKKqprce_fK-4UXbPPgNI,450
58
+ fastapi_factory_utilities/core/services/hydra/objects.py,sha256=2CeV_4zmwwpfbXQ0TM9B_UnNkZuIRXweFP_VALBo57c,601
59
+ fastapi_factory_utilities/core/services/hydra/services.py,sha256=Urc98pM23OatHr_WdgrpK1U4E2qsjF1fpXo0xnNjZ60,4411
60
+ fastapi_factory_utilities/core/services/kratos/__init__.py,sha256=DaC29-Ol0WR5vX56IHLGDXP9UrhISq0Juhg_sJTasw4,368
61
+ fastapi_factory_utilities/core/services/kratos/enums.py,sha256=ULJppowlZbOjdnUIXQyI4_nHmHZoNnv7-M1CYQBYXFY,220
62
+ fastapi_factory_utilities/core/services/kratos/exceptions.py,sha256=xAX01-lQvPpADgcwhB5YWSy1UqAxG38s2rlU9AJBJd8,472
63
+ fastapi_factory_utilities/core/services/kratos/objects.py,sha256=a0npt4Q6d9UbF0w3e6EvyKLwMWO2l639_dFjruA8_2c,980
64
+ fastapi_factory_utilities/core/services/kratos/services.py,sha256=RFKrME6M6omWiHORMBCSyvIG0BsimQaCfnmtsOdFfsg,3158
65
+ fastapi_factory_utilities/core/services/status/__init__.py,sha256=kkR2Xx-kbHb_QXkkBSUNIK5y_eNM4YCElFftBAooIY0,370
66
+ fastapi_factory_utilities/core/services/status/enums.py,sha256=IUxWAd0Ecknri4BqzaqoDRRhT_8LdcgtQcNqgNVDXGE,599
67
+ fastapi_factory_utilities/core/services/status/exceptions.py,sha256=_fQFGqHKnG54Hs-ZtC4gs0xwzSH246_WwQOonraoGKw,856
68
+ fastapi_factory_utilities/core/services/status/health_calculator_strategies.py,sha256=p2KKJo-dq1j9iWHT0mvlBKtldH9m8l31aytLkUsb9nQ,1634
69
+ fastapi_factory_utilities/core/services/status/readiness_calculator_strategies.py,sha256=hA1LX2pSxva-7bVaQ0dw8NpTbl1ZWDtXd9xzqQUGUsI,1354
70
+ fastapi_factory_utilities/core/services/status/services.py,sha256=qtgYdnxNQMbb6q6G9uGasRO3lZcpiWNmw1padPkh6jA,8490
71
+ fastapi_factory_utilities/core/services/status/types.py,sha256=GJOGRra6NtpUS4q1cr4cdWR0BbIUtEwP7vQX-sXX5jQ,3297
72
+ fastapi_factory_utilities/core/utils/configs.py,sha256=qM0pCrsK8ZyfCoyova_VrhR4eUX3LSPCbWunGMWcSVg,2581
73
+ fastapi_factory_utilities/core/utils/importlib.py,sha256=DYcPo7K0s95WV5xxtucpufWsTj8Pxv25sWunDmmNUYI,797
74
+ fastapi_factory_utilities/core/utils/log.py,sha256=6V9CL3bQio4e47YxcSXM2JQRGhVxuBfmcEbcF4RtCfQ,6393
75
+ fastapi_factory_utilities/core/utils/status.py,sha256=ADdi4NzkJy0qNVC86tKV09wdpuPhuCKSrvYn9tzNVn8,1934
76
+ fastapi_factory_utilities/core/utils/uvicorn.py,sha256=XThylG-nOPVL00w6MIWGODnweoM7VxmpSFcyoPcmqns,2609
77
+ fastapi_factory_utilities/core/utils/yaml_reader.py,sha256=2XenSHqymZsgff70upPZtyx0-BsiUdTENKmrxaOBLeE,6100
78
+ fastapi_factory_utilities/example/__init__.py,sha256=LEKnPTBcgDyfHeOjlVxjK5lFdFqS-7-mHDuVuM2Jh_Y,206
79
+ fastapi_factory_utilities/example/__main__.py,sha256=Iwp_6rK7Lcv2F-XAKn6xjxQHOWjx2OjgwKAr91tfUfk,135
80
+ fastapi_factory_utilities/example/api/__init__.py,sha256=qI82eeSwVjR6jSkX1pxm8ALv9WPQ_iHurFY4G2K7VzE,554
81
+ fastapi_factory_utilities/example/api/books/__init__.py,sha256=zXARBnjywJwg1XsLbcixYWcHH4uC9mF-kbbX4P8cVgA,160
82
+ fastapi_factory_utilities/example/api/books/responses.py,sha256=21WeD6bdg0MCD_0vRHwmsL4W79iDcG9NnDLemXysc84,540
83
+ fastapi_factory_utilities/example/api/books/routes.py,sha256=bwg8Bhvoj9vx2SdwunzFS-Z3cHECtjl_yGdo8MfiGEM,1529
84
+ fastapi_factory_utilities/example/app.py,sha256=MInt71wnDEC9yqVJeT3hChj7DRmg7sI3Ta0KkfubdyU,2076
85
+ fastapi_factory_utilities/example/application.yaml,sha256=5xRyFSuMxmgZ5Mikai94UqKYJ7PxJp8omlXobTjv14M,485
86
+ fastapi_factory_utilities/example/entities/books/__init__.py,sha256=cpTMhLpenb359lOT9HHUv6DzKX8fKl4WCzcsB3ddk6Q,185
87
+ fastapi_factory_utilities/example/entities/books/entities.py,sha256=rLE01lE7U6WizrD5ZHMRwkynd8_dWF6DltBFH61f-Do,405
88
+ fastapi_factory_utilities/example/entities/books/enums.py,sha256=lXYUvhIkT1pi0teflMpnqeafeiBZMokyWxoFLgzV6a8,330
89
+ fastapi_factory_utilities/example/entities/books/types.py,sha256=7LYGPu-CcI3noIORJyIZlVF-CBugWPXEqgDzWrO3XmQ,1558
90
+ fastapi_factory_utilities/example/models/__init__.py,sha256=RJmp3R9bhbQv7n0WOlsHP65LqbEs_DjF9hzYKwYTRGo,22
91
+ fastapi_factory_utilities/example/models/books/__init__.py,sha256=IPS3rLLnZncV-VAxgsipxUD7l4r04ewo_Hx2Naj2lT0,181
92
+ fastapi_factory_utilities/example/models/books/document.py,sha256=lYJfMGr5GqEEsn7L--PFs75hC2q-jQx77wl7EhTrp5U,568
93
+ fastapi_factory_utilities/example/models/books/repository.py,sha256=7K63uAsSEGZ2EXqufU4Tc8KpymgXK8JX8WjAE2Sw8ok,387
94
+ fastapi_factory_utilities/example/services/books/__init__.py,sha256=Z06yNRoA7Zg3TGN-Q9rrvJg6Bbx-qJw661MVwukV6vQ,148
95
+ fastapi_factory_utilities/example/services/books/services.py,sha256=-x7d4hotUWLzWo5uImMjFmtNcSTHwWv2bfttIbYYKbA,5380
96
+ fastapi_factory_utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
+ fastapi_factory_utilities-0.7.1.dist-info/METADATA,sha256=Qa2ulopX-2Fb4an_JoqjlEMZRwyfeMPKAv0GfCvHv7g,3741
98
+ fastapi_factory_utilities-0.7.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
99
+ fastapi_factory_utilities-0.7.1.dist-info/entry_points.txt,sha256=IK0VcBexXo4uXQmTrbfhhnnfq4GmXPRn0GBB8hzlsq4,101
100
+ fastapi_factory_utilities-0.7.1.dist-info/licenses/LICENSE,sha256=iO1nLzMMst6vEiqgSUrfrbetM7b0bvdzXhbed5tqG8o,1074
101
+ fastapi_factory_utilities-0.7.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.1
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,15 +0,0 @@
1
- """Provide PluginManager."""
2
-
3
- from .exceptions import (
4
- InvalidPluginError,
5
- PluginManagerError,
6
- PluginPreConditionNotMetError,
7
- )
8
- from .plugin_manager import PluginManager
9
-
10
- __all__: list[str] = [
11
- "PluginManager",
12
- "PluginManagerError",
13
- "InvalidPluginError",
14
- "PluginPreConditionNotMetError",
15
- ]