alpha-python 0.1.0__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.
Files changed (62) hide show
  1. alpha/__init__.py +0 -0
  2. alpha/adapters/__init__.py +0 -0
  3. alpha/adapters/sqla_unit_of_work.py +120 -0
  4. alpha/domain/__init__.py +0 -0
  5. alpha/domain/models/__init__.py +0 -0
  6. alpha/domain/models/base_model.py +25 -0
  7. alpha/encoder.py +62 -0
  8. alpha/exceptions.py +99 -0
  9. alpha/factories/__init__.py +0 -0
  10. alpha/factories/_type_conversion_matrix.py +233 -0
  11. alpha/factories/_type_mapping.py +29 -0
  12. alpha/factories/class_factories.py +496 -0
  13. alpha/factories/default_field_factory.py +50 -0
  14. alpha/factories/field_iterator.py +188 -0
  15. alpha/factories/logging_handler_factory.py +86 -0
  16. alpha/factories/model_class_factory.py +176 -0
  17. alpha/factories/models/__init__.py +0 -0
  18. alpha/factories/models/factory_classes.py +20 -0
  19. alpha/factories/request_factory.py +211 -0
  20. alpha/factories/response_factory.py +186 -0
  21. alpha/factories/type_factories.py +204 -0
  22. alpha/infra/__init__.py +0 -0
  23. alpha/infra/database/__init__.py +0 -0
  24. alpha/infra/database/sql_alchemy_database.py +159 -0
  25. alpha/infra/database/sql_alchemy_view.py +48 -0
  26. alpha/infra/models/__init__.py +0 -0
  27. alpha/infra/models/filter_operators.py +98 -0
  28. alpha/infra/models/json_patch.py +21 -0
  29. alpha/infra/models/order_by.py +69 -0
  30. alpha/infra/models/query_clause.py +45 -0
  31. alpha/infra/models/search_filter.py +586 -0
  32. alpha/interfaces/__init__.py +0 -0
  33. alpha/interfaces/attrs_instance.py +10 -0
  34. alpha/interfaces/dataclass_instance.py +11 -0
  35. alpha/interfaces/factories.py +102 -0
  36. alpha/interfaces/openapi_model.py +21 -0
  37. alpha/interfaces/patchable.py +8 -0
  38. alpha/interfaces/sql_database.py +36 -0
  39. alpha/interfaces/sql_mapper.py +23 -0
  40. alpha/interfaces/sql_repository.py +380 -0
  41. alpha/interfaces/token_factory.py +56 -0
  42. alpha/interfaces/unit_of_work.py +53 -0
  43. alpha/interfaces/updateable.py +7 -0
  44. alpha/py.typed +0 -0
  45. alpha/repositories/__init__.py +0 -0
  46. alpha/repositories/default_sql_repository.py +679 -0
  47. alpha/repositories/models/__init__.py +0 -0
  48. alpha/repositories/models/repository_model.py +16 -0
  49. alpha/services/__init__.py +0 -0
  50. alpha/services/authentication_service.py +71 -0
  51. alpha/utils/__init__.py +0 -0
  52. alpha/utils/_http_codes.py +148 -0
  53. alpha/utils/is_attrs.py +18 -0
  54. alpha/utils/logging_configurator.py +133 -0
  55. alpha/utils/logging_level_checker.py +26 -0
  56. alpha/utils/response_object.py +26 -0
  57. alpha/utils/version_check.py +17 -0
  58. alpha_python-0.1.0.dist-info/METADATA +22 -0
  59. alpha_python-0.1.0.dist-info/RECORD +62 -0
  60. alpha_python-0.1.0.dist-info/WHEEL +5 -0
  61. alpha_python-0.1.0.dist-info/licenses/LICENSE +21 -0
  62. alpha_python-0.1.0.dist-info/top_level.txt +1 -0
alpha/__init__.py ADDED
File without changes
File without changes
@@ -0,0 +1,120 @@
1
+ """Contains the SQLAlchemy Unit of Work implementation."""
2
+
3
+ from typing import Any, TypeVar
4
+
5
+ from sqlalchemy.orm.session import Session
6
+
7
+ from alpha import exceptions
8
+ from alpha.interfaces.sql_database import SqlDatabase
9
+ from alpha.repositories.models.repository_model import RepositoryModel
10
+
11
+ UOW = TypeVar("UOW", bound="SqlAlchemyUnitOfWork")
12
+
13
+
14
+ class SqlAlchemyUnitOfWork:
15
+ """Unit of Work implementation for SQLAlchemy databases."""
16
+
17
+ def __init__(self, db: SqlDatabase, repos: list[RepositoryModel]) -> None:
18
+ """Initialize the Unit of Work with a database and repositories.
19
+
20
+ Parameters
21
+ ----------
22
+ db : SqlDatabase
23
+ The database instance to use.
24
+ repos : list[RepositoryModel]
25
+ The list of repository models to use.
26
+
27
+ Raises
28
+ ------
29
+ TypeError
30
+ If the provided database is not a valid SqlDatabase instance.
31
+ TypeError
32
+ If the provided repositories list is empty or contains invalid
33
+ models.
34
+ """
35
+ if not isinstance(db, SqlDatabase): # type: ignore
36
+ raise TypeError("No valid database provided")
37
+
38
+ self._db = db
39
+ self._repositories = repos
40
+ self._session: Session | None = None
41
+
42
+ def __enter__(self: UOW) -> UOW:
43
+ """Initialize the Unit of Work context.
44
+
45
+ Returns
46
+ -------
47
+ The Unit of Work instance.
48
+
49
+ Raises
50
+ ------
51
+ TypeError
52
+ If the provided repositories list is empty or contains invalid
53
+ models.
54
+ """
55
+ self._session = self._db.get_session()
56
+
57
+ for repo in self._repositories:
58
+ session = self._session
59
+ model = repo.default_model
60
+
61
+ name: str = repo.name
62
+ repository = repo.repository
63
+ interface: Any = repo.interface
64
+
65
+ self.__setattr__(
66
+ name,
67
+ repository(session=session, default_model=model), # type: ignore
68
+ )
69
+ if interface:
70
+ if not isinstance(getattr(self, name), interface):
71
+ raise TypeError(f"Repository for {name} has no interface")
72
+
73
+ return self
74
+
75
+ def __exit__(self, *args: Any) -> None:
76
+ """Finalize the Unit of Work context."""
77
+ if not self._session:
78
+ raise exceptions.DatabaseSessionError(
79
+ "No active database session is defined"
80
+ )
81
+ self._session.close()
82
+ self.rollback()
83
+ self._session = None # type: ignore
84
+
85
+ def commit(self) -> None:
86
+ """Commit the current transaction."""
87
+ if not self._session:
88
+ raise exceptions.DatabaseSessionError(
89
+ "No active database session is defined"
90
+ )
91
+ self._session.commit()
92
+
93
+ def flush(self) -> None:
94
+ """Flush the current transaction."""
95
+ if not self._session:
96
+ raise exceptions.DatabaseSessionError(
97
+ "No active database session is defined"
98
+ )
99
+ self._session.flush()
100
+
101
+ def rollback(self) -> None:
102
+ """Rollback the current transaction."""
103
+ if not self._session:
104
+ raise exceptions.DatabaseSessionError(
105
+ "No active database session is defined"
106
+ )
107
+ self._session.rollback()
108
+
109
+ def refresh(self, obj: object) -> None:
110
+ """Refresh the state of a given object."""
111
+ if not self._session:
112
+ raise exceptions.DatabaseSessionError(
113
+ "No active database session is defined"
114
+ )
115
+ self._session.refresh(obj)
116
+
117
+ @property
118
+ def session(self) -> Session | None:
119
+ """Get the current database session."""
120
+ return self._session
File without changes
File without changes
@@ -0,0 +1,25 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, TypeVar
3
+
4
+ DomainModel = TypeVar("DomainModel", bound="BaseDomainModel")
5
+ DomainModelCovariant = TypeVar(
6
+ "DomainModelCovariant", bound="BaseDomainModel", covariant=True
7
+ )
8
+ DomainModelContravariant = TypeVar(
9
+ "DomainModelContravariant", bound="BaseDomainModel", contravariant=True
10
+ )
11
+
12
+
13
+ @dataclass
14
+ class BaseDomainModel:
15
+ def to_dict(self) -> dict[str, Any]:
16
+ obj: dict[str, Any] = {}
17
+ for attr in self.__dataclass_fields__.keys():
18
+ if not attr.startswith("_"):
19
+ obj[attr] = getattr(self, attr)
20
+ if attr == "_id":
21
+ obj[attr] = str(getattr(self, attr))
22
+ return obj
23
+
24
+ def update(self, obj: DomainModel) -> DomainModel:
25
+ raise NotImplementedError("Subclasses must implement the update method")
alpha/encoder.py ADDED
@@ -0,0 +1,62 @@
1
+ import json
2
+ from dataclasses import asdict, is_dataclass
3
+ from datetime import date, datetime, time
4
+ from enum import Enum
5
+ from json import encoder
6
+ from typing import Any
7
+ from uuid import UUID
8
+
9
+ import numpy as np
10
+ import pandas as pd
11
+ import six
12
+
13
+ from alpha.interfaces.openapi_model import OpenAPIModel
14
+
15
+
16
+ class JSONEncoder(encoder.JSONEncoder):
17
+ include_nulls = False
18
+
19
+ def default(self, o: Any) -> Any:
20
+ if isinstance(o, list):
21
+ return [self.default(item) for item in o] # type: ignore
22
+ if isinstance(o, OpenAPIModel):
23
+ dikt: dict[str, Any] = {}
24
+ for attr, _ in six.iteritems(o.openapi_types):
25
+ value = getattr(o, attr)
26
+ if value is None and not self.include_nulls:
27
+ continue
28
+ attr = o.attribute_map[attr]
29
+ dikt[attr] = value
30
+ return dikt
31
+ if hasattr(o, "to_dict"):
32
+ return o.to_dict()
33
+ if hasattr(o, "to_list"):
34
+ return o.to_list()
35
+ if isinstance(o, set):
36
+ return list(o) # type: ignore
37
+ if isinstance(o, Enum):
38
+ return o.name
39
+ if isinstance(o, UUID):
40
+ return str(o)
41
+ if isinstance(o, np.int64): # type: ignore
42
+ return int(o)
43
+ if isinstance(o, np.float32): # type: ignore
44
+ return float(o)
45
+ if isinstance(o, pd.Timestamp):
46
+ return o.isoformat()
47
+ if isinstance(o, datetime):
48
+ return o.isoformat()
49
+ if isinstance(o, date):
50
+ return o.isoformat()
51
+ if isinstance(o, time):
52
+ return o.isoformat()
53
+ if is_dataclass(o):
54
+ if isinstance(o, type):
55
+ cls = getattr(o, "__class__")
56
+ return cls.__name__
57
+ return asdict(o)
58
+
59
+ try:
60
+ return json.JSONEncoder.default(self, o)
61
+ except Exception:
62
+ return None
alpha/exceptions.py ADDED
@@ -0,0 +1,99 @@
1
+ # HTTP Exceptions
2
+ class BadRequestException(Exception):
3
+ """Equivalent to HTTP code 400"""
4
+
5
+
6
+ class UnauthorizedException(Exception):
7
+ """Equivalent to HTTP code 401"""
8
+
9
+
10
+ class ForbiddenException(Exception):
11
+ """Equivalent to HTTP code 403"""
12
+
13
+
14
+ class NotFoundException(Exception):
15
+ """Equivalent to HTTP code 404"""
16
+
17
+
18
+ class NotAcceptableException(Exception):
19
+ """Equivalent to HTTP code 406"""
20
+
21
+
22
+ class ConflictException(Exception):
23
+ """Equivalent to HTTP code 409"""
24
+
25
+
26
+ class PayloadTooLargeException(Exception):
27
+ """Equivalent to HTTP code 413"""
28
+
29
+
30
+ class UnprocessableContentException(Exception):
31
+ """Equivalent to HTTP code 422"""
32
+
33
+
34
+ class ErrorException(Exception): ...
35
+
36
+
37
+ class InternalServerErrorException(ErrorException):
38
+ """Equivalent to HTTP code 500"""
39
+
40
+
41
+ class NotImplementedException(ErrorException):
42
+ """Equivalent to HTTP code 501"""
43
+
44
+
45
+ class BadGatewayException(ErrorException):
46
+ """Equivalent to HTTP code 502"""
47
+
48
+
49
+ class ServiceUnavailableException(ErrorException):
50
+ """Equivalent to HTTP code 503"""
51
+
52
+
53
+ ## Common exceptions
54
+ class DatabaseMapperError(Exception): ...
55
+
56
+
57
+ class DatabaseSessionError(InternalServerErrorException): ...
58
+
59
+
60
+ class InstrumentedAttributeMissing(Exception): ...
61
+
62
+
63
+ class AlreadyExistsException(ForbiddenException): ...
64
+
65
+
66
+ class TypingFactoryException(Exception): ...
67
+
68
+
69
+ class ModelClassFactoryException(Exception): ...
70
+
71
+
72
+ class DefaultFactoryException(Exception): ...
73
+
74
+
75
+ class ObjectConversionNotSupported(Exception): ...
76
+
77
+
78
+ class ObjectConversionNotAllowed(Exception): ...
79
+
80
+
81
+ class ObjectConversionError(Exception): ...
82
+
83
+
84
+ class UnionArgumentError(Exception): ...
85
+
86
+
87
+ class MixedArgumentTypesError(Exception): ...
88
+
89
+
90
+ class MissingAttributeError(Exception): ...
91
+
92
+
93
+ class ClassMismatchException(Exception): ...
94
+
95
+
96
+ class LoggingHandlerException(Exception): ...
97
+
98
+
99
+ class ClassFactoryException(Exception): ...
File without changes
@@ -0,0 +1,233 @@
1
+ """Contains the TYPE_CONVERSION_MATRIX constant which can be used to determine
2
+ if it is allowed to cast an object to a different type.
3
+ """
4
+
5
+ import datetime
6
+ import uuid
7
+
8
+ TYPE_CONVERSION_MATRIX: dict[type, dict[type, bool]] = {
9
+ str: {
10
+ str: True,
11
+ complex: True,
12
+ int: True,
13
+ float: True,
14
+ bytes: True,
15
+ bytearray: True,
16
+ list: True,
17
+ dict: False,
18
+ set: True,
19
+ tuple: True,
20
+ bool: True,
21
+ datetime.datetime: True,
22
+ datetime.date: True,
23
+ uuid.UUID: True,
24
+ },
25
+ complex: {
26
+ str: True,
27
+ complex: True,
28
+ int: False,
29
+ float: False,
30
+ bytes: False,
31
+ bytearray: False,
32
+ list: False,
33
+ dict: False,
34
+ set: False,
35
+ tuple: False,
36
+ bool: True,
37
+ datetime.datetime: False,
38
+ datetime.date: False,
39
+ uuid.UUID: False,
40
+ },
41
+ int: {
42
+ str: True,
43
+ complex: True,
44
+ int: True,
45
+ float: True,
46
+ bytes: True,
47
+ bytearray: True,
48
+ list: False,
49
+ dict: False,
50
+ set: False,
51
+ tuple: False,
52
+ bool: True,
53
+ datetime.datetime: True,
54
+ datetime.date: True,
55
+ uuid.UUID: False,
56
+ },
57
+ float: {
58
+ str: True,
59
+ complex: True,
60
+ int: True,
61
+ float: True,
62
+ bytes: False,
63
+ bytearray: False,
64
+ list: False,
65
+ dict: False,
66
+ set: False,
67
+ tuple: False,
68
+ bool: True,
69
+ datetime.datetime: False,
70
+ datetime.date: False,
71
+ uuid.UUID: False,
72
+ },
73
+ bytes: {
74
+ str: False,
75
+ complex: False,
76
+ int: False,
77
+ float: False,
78
+ bytes: True,
79
+ bytearray: True,
80
+ list: False,
81
+ dict: False,
82
+ set: False,
83
+ tuple: False,
84
+ bool: True,
85
+ datetime.datetime: False,
86
+ datetime.date: False,
87
+ uuid.UUID: False,
88
+ },
89
+ bytearray: {
90
+ str: False,
91
+ complex: False,
92
+ int: False,
93
+ float: False,
94
+ bytes: True,
95
+ bytearray: True,
96
+ list: False,
97
+ dict: False,
98
+ set: False,
99
+ tuple: False,
100
+ bool: True,
101
+ datetime.datetime: False,
102
+ datetime.date: False,
103
+ uuid.UUID: False,
104
+ },
105
+ list: {
106
+ str: True,
107
+ complex: False,
108
+ int: False,
109
+ float: False,
110
+ bytes: True,
111
+ bytearray: True,
112
+ list: True,
113
+ dict: False,
114
+ set: True,
115
+ tuple: True,
116
+ bool: True,
117
+ datetime.datetime: False,
118
+ datetime.date: False,
119
+ uuid.UUID: False,
120
+ },
121
+ dict: {
122
+ str: True,
123
+ complex: False,
124
+ int: False,
125
+ float: False,
126
+ bytes: False,
127
+ bytearray: False,
128
+ list: True,
129
+ dict: True,
130
+ set: True,
131
+ tuple: True,
132
+ bool: True,
133
+ datetime.datetime: False,
134
+ datetime.date: False,
135
+ uuid.UUID: False,
136
+ },
137
+ set: {
138
+ str: True,
139
+ complex: False,
140
+ int: False,
141
+ float: False,
142
+ bytes: True,
143
+ bytearray: True,
144
+ list: True,
145
+ dict: False,
146
+ set: True,
147
+ tuple: True,
148
+ bool: True,
149
+ datetime.datetime: False,
150
+ datetime.date: False,
151
+ uuid.UUID: False,
152
+ },
153
+ tuple: {
154
+ str: True,
155
+ complex: False,
156
+ int: False,
157
+ float: False,
158
+ bytes: True,
159
+ bytearray: True,
160
+ list: True,
161
+ dict: False,
162
+ set: True,
163
+ tuple: True,
164
+ bool: True,
165
+ datetime.datetime: False,
166
+ datetime.date: False,
167
+ uuid.UUID: False,
168
+ },
169
+ bool: {
170
+ str: True,
171
+ complex: True,
172
+ int: True,
173
+ float: True,
174
+ bytes: True,
175
+ bytearray: True,
176
+ list: False,
177
+ dict: False,
178
+ set: False,
179
+ tuple: False,
180
+ bool: True,
181
+ datetime.datetime: False,
182
+ datetime.date: False,
183
+ uuid.UUID: False,
184
+ },
185
+ datetime.datetime: {
186
+ str: True,
187
+ complex: False,
188
+ int: False,
189
+ float: False,
190
+ bytes: False,
191
+ bytearray: False,
192
+ list: False,
193
+ dict: False,
194
+ set: False,
195
+ tuple: False,
196
+ bool: True,
197
+ datetime.datetime: True,
198
+ datetime.date: True,
199
+ uuid.UUID: False,
200
+ },
201
+ datetime.date: {
202
+ str: True,
203
+ complex: False,
204
+ int: False,
205
+ float: False,
206
+ bytes: False,
207
+ bytearray: False,
208
+ list: False,
209
+ dict: False,
210
+ set: False,
211
+ tuple: False,
212
+ bool: True,
213
+ datetime.datetime: True,
214
+ datetime.date: True,
215
+ uuid.UUID: False,
216
+ },
217
+ uuid.UUID: {
218
+ str: True,
219
+ complex: False,
220
+ int: True,
221
+ float: False,
222
+ bytes: False,
223
+ bytearray: False,
224
+ list: False,
225
+ dict: False,
226
+ set: False,
227
+ tuple: False,
228
+ bool: False,
229
+ datetime.datetime: False,
230
+ datetime.date: False,
231
+ uuid.UUID: False,
232
+ },
233
+ }
@@ -0,0 +1,29 @@
1
+ """Contains the TYPES constant to determine which TypeFactory can be used to
2
+ process a certain object type
3
+ """
4
+
5
+ import datetime
6
+ import uuid
7
+
8
+ from alpha.factories.type_factories import (
9
+ DatetimeTypeFactory,
10
+ GenericTypeFactory,
11
+ )
12
+ from alpha.interfaces.factories import TypeFactory
13
+
14
+ TYPES: dict[type, type[TypeFactory]] = {
15
+ str: GenericTypeFactory,
16
+ complex: GenericTypeFactory,
17
+ int: GenericTypeFactory,
18
+ float: GenericTypeFactory,
19
+ bytes: GenericTypeFactory,
20
+ bytearray: GenericTypeFactory,
21
+ list: GenericTypeFactory,
22
+ dict: GenericTypeFactory,
23
+ set: GenericTypeFactory,
24
+ tuple: GenericTypeFactory,
25
+ bool: GenericTypeFactory,
26
+ uuid.UUID: GenericTypeFactory,
27
+ datetime.datetime: DatetimeTypeFactory,
28
+ datetime.date: DatetimeTypeFactory,
29
+ }