clear-skies 2.0.3__py3-none-any.whl → 2.0.5__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 clear-skies might be problematic. Click here for more details.

Files changed (251) hide show
  1. clear_skies-2.0.5.dist-info/METADATA +74 -0
  2. clear_skies-2.0.5.dist-info/RECORD +4 -0
  3. {clear_skies-2.0.3.dist-info → clear_skies-2.0.5.dist-info}/WHEEL +1 -1
  4. clear_skies-2.0.3.dist-info/METADATA +0 -46
  5. clear_skies-2.0.3.dist-info/RECORD +0 -249
  6. clearskies/__init__.py +0 -59
  7. clearskies/action.py +0 -7
  8. clearskies/authentication/__init__.py +0 -15
  9. clearskies/authentication/authentication.py +0 -46
  10. clearskies/authentication/authorization.py +0 -16
  11. clearskies/authentication/authorization_pass_through.py +0 -20
  12. clearskies/authentication/jwks.py +0 -163
  13. clearskies/authentication/public.py +0 -5
  14. clearskies/authentication/secret_bearer.py +0 -553
  15. clearskies/autodoc/__init__.py +0 -8
  16. clearskies/autodoc/formats/__init__.py +0 -5
  17. clearskies/autodoc/formats/oai3_json/__init__.py +0 -7
  18. clearskies/autodoc/formats/oai3_json/oai3_json.py +0 -87
  19. clearskies/autodoc/formats/oai3_json/oai3_schema_resolver.py +0 -15
  20. clearskies/autodoc/formats/oai3_json/parameter.py +0 -35
  21. clearskies/autodoc/formats/oai3_json/request.py +0 -68
  22. clearskies/autodoc/formats/oai3_json/response.py +0 -28
  23. clearskies/autodoc/formats/oai3_json/schema/__init__.py +0 -11
  24. clearskies/autodoc/formats/oai3_json/schema/array.py +0 -9
  25. clearskies/autodoc/formats/oai3_json/schema/default.py +0 -13
  26. clearskies/autodoc/formats/oai3_json/schema/enum.py +0 -7
  27. clearskies/autodoc/formats/oai3_json/schema/object.py +0 -29
  28. clearskies/autodoc/formats/oai3_json/test.json +0 -1985
  29. clearskies/autodoc/py.typed +0 -0
  30. clearskies/autodoc/request/__init__.py +0 -15
  31. clearskies/autodoc/request/header.py +0 -6
  32. clearskies/autodoc/request/json_body.py +0 -6
  33. clearskies/autodoc/request/parameter.py +0 -8
  34. clearskies/autodoc/request/request.py +0 -38
  35. clearskies/autodoc/request/url_parameter.py +0 -6
  36. clearskies/autodoc/request/url_path.py +0 -6
  37. clearskies/autodoc/response/__init__.py +0 -5
  38. clearskies/autodoc/response/response.py +0 -9
  39. clearskies/autodoc/schema/__init__.py +0 -31
  40. clearskies/autodoc/schema/array.py +0 -10
  41. clearskies/autodoc/schema/base64.py +0 -8
  42. clearskies/autodoc/schema/boolean.py +0 -5
  43. clearskies/autodoc/schema/date.py +0 -5
  44. clearskies/autodoc/schema/datetime.py +0 -5
  45. clearskies/autodoc/schema/double.py +0 -5
  46. clearskies/autodoc/schema/enum.py +0 -17
  47. clearskies/autodoc/schema/integer.py +0 -6
  48. clearskies/autodoc/schema/long.py +0 -5
  49. clearskies/autodoc/schema/number.py +0 -6
  50. clearskies/autodoc/schema/object.py +0 -13
  51. clearskies/autodoc/schema/password.py +0 -5
  52. clearskies/autodoc/schema/schema.py +0 -11
  53. clearskies/autodoc/schema/string.py +0 -5
  54. clearskies/backends/__init__.py +0 -65
  55. clearskies/backends/api_backend.py +0 -1178
  56. clearskies/backends/backend.py +0 -136
  57. clearskies/backends/cursor_backend.py +0 -335
  58. clearskies/backends/memory_backend.py +0 -797
  59. clearskies/backends/secrets_backend.py +0 -106
  60. clearskies/column.py +0 -1233
  61. clearskies/columns/__init__.py +0 -71
  62. clearskies/columns/audit.py +0 -206
  63. clearskies/columns/belongs_to_id.py +0 -483
  64. clearskies/columns/belongs_to_model.py +0 -132
  65. clearskies/columns/belongs_to_self.py +0 -105
  66. clearskies/columns/boolean.py +0 -113
  67. clearskies/columns/category_tree.py +0 -275
  68. clearskies/columns/category_tree_ancestors.py +0 -51
  69. clearskies/columns/category_tree_children.py +0 -127
  70. clearskies/columns/category_tree_descendants.py +0 -48
  71. clearskies/columns/created.py +0 -95
  72. clearskies/columns/created_by_authorization_data.py +0 -116
  73. clearskies/columns/created_by_header.py +0 -99
  74. clearskies/columns/created_by_ip.py +0 -92
  75. clearskies/columns/created_by_routing_data.py +0 -97
  76. clearskies/columns/created_by_user_agent.py +0 -92
  77. clearskies/columns/date.py +0 -234
  78. clearskies/columns/datetime.py +0 -282
  79. clearskies/columns/email.py +0 -76
  80. clearskies/columns/float.py +0 -153
  81. clearskies/columns/has_many.py +0 -505
  82. clearskies/columns/has_many_self.py +0 -56
  83. clearskies/columns/has_one.py +0 -14
  84. clearskies/columns/integer.py +0 -160
  85. clearskies/columns/json.py +0 -126
  86. clearskies/columns/many_to_many_ids.py +0 -337
  87. clearskies/columns/many_to_many_ids_with_data.py +0 -274
  88. clearskies/columns/many_to_many_models.py +0 -158
  89. clearskies/columns/many_to_many_pivots.py +0 -134
  90. clearskies/columns/phone.py +0 -159
  91. clearskies/columns/select.py +0 -92
  92. clearskies/columns/string.py +0 -102
  93. clearskies/columns/timestamp.py +0 -164
  94. clearskies/columns/updated.py +0 -110
  95. clearskies/columns/uuid.py +0 -86
  96. clearskies/configs/README.md +0 -105
  97. clearskies/configs/__init__.py +0 -162
  98. clearskies/configs/actions.py +0 -43
  99. clearskies/configs/any.py +0 -13
  100. clearskies/configs/any_dict.py +0 -22
  101. clearskies/configs/any_dict_or_callable.py +0 -23
  102. clearskies/configs/authentication.py +0 -23
  103. clearskies/configs/authorization.py +0 -23
  104. clearskies/configs/boolean.py +0 -16
  105. clearskies/configs/boolean_or_callable.py +0 -18
  106. clearskies/configs/callable_config.py +0 -18
  107. clearskies/configs/columns.py +0 -34
  108. clearskies/configs/conditions.py +0 -30
  109. clearskies/configs/config.py +0 -24
  110. clearskies/configs/datetime.py +0 -18
  111. clearskies/configs/datetime_or_callable.py +0 -19
  112. clearskies/configs/endpoint.py +0 -23
  113. clearskies/configs/endpoint_list.py +0 -28
  114. clearskies/configs/float.py +0 -16
  115. clearskies/configs/float_or_callable.py +0 -18
  116. clearskies/configs/integer.py +0 -16
  117. clearskies/configs/integer_or_callable.py +0 -18
  118. clearskies/configs/joins.py +0 -30
  119. clearskies/configs/list_any_dict.py +0 -30
  120. clearskies/configs/list_any_dict_or_callable.py +0 -31
  121. clearskies/configs/model_class.py +0 -35
  122. clearskies/configs/model_column.py +0 -65
  123. clearskies/configs/model_columns.py +0 -56
  124. clearskies/configs/model_destination_name.py +0 -25
  125. clearskies/configs/model_to_id_column.py +0 -43
  126. clearskies/configs/readable_model_column.py +0 -9
  127. clearskies/configs/readable_model_columns.py +0 -9
  128. clearskies/configs/schema.py +0 -23
  129. clearskies/configs/searchable_model_columns.py +0 -9
  130. clearskies/configs/security_headers.py +0 -39
  131. clearskies/configs/select.py +0 -26
  132. clearskies/configs/select_list.py +0 -47
  133. clearskies/configs/string.py +0 -29
  134. clearskies/configs/string_dict.py +0 -32
  135. clearskies/configs/string_list.py +0 -32
  136. clearskies/configs/string_list_or_callable.py +0 -35
  137. clearskies/configs/string_or_callable.py +0 -18
  138. clearskies/configs/timedelta.py +0 -18
  139. clearskies/configs/timezone.py +0 -18
  140. clearskies/configs/url.py +0 -23
  141. clearskies/configs/validators.py +0 -45
  142. clearskies/configs/writeable_model_column.py +0 -9
  143. clearskies/configs/writeable_model_columns.py +0 -9
  144. clearskies/configurable.py +0 -76
  145. clearskies/contexts/__init__.py +0 -11
  146. clearskies/contexts/cli.py +0 -117
  147. clearskies/contexts/context.py +0 -98
  148. clearskies/contexts/wsgi.py +0 -76
  149. clearskies/contexts/wsgi_ref.py +0 -82
  150. clearskies/decorators.py +0 -33
  151. clearskies/di/__init__.py +0 -14
  152. clearskies/di/additional_config.py +0 -130
  153. clearskies/di/additional_config_auto_import.py +0 -17
  154. clearskies/di/di.py +0 -968
  155. clearskies/di/inject/__init__.py +0 -23
  156. clearskies/di/inject/by_class.py +0 -21
  157. clearskies/di/inject/by_name.py +0 -18
  158. clearskies/di/inject/di.py +0 -13
  159. clearskies/di/inject/environment.py +0 -14
  160. clearskies/di/inject/input_output.py +0 -20
  161. clearskies/di/inject/now.py +0 -13
  162. clearskies/di/inject/requests.py +0 -13
  163. clearskies/di/inject/secrets.py +0 -14
  164. clearskies/di/inject/utcnow.py +0 -13
  165. clearskies/di/inject/uuid.py +0 -15
  166. clearskies/di/injectable.py +0 -29
  167. clearskies/di/injectable_properties.py +0 -131
  168. clearskies/di/test_module/__init__.py +0 -6
  169. clearskies/di/test_module/another_module/__init__.py +0 -2
  170. clearskies/di/test_module/module_class.py +0 -5
  171. clearskies/end.py +0 -183
  172. clearskies/endpoint.py +0 -1310
  173. clearskies/endpoint_group.py +0 -310
  174. clearskies/endpoints/__init__.py +0 -23
  175. clearskies/endpoints/advanced_search.py +0 -526
  176. clearskies/endpoints/callable.py +0 -388
  177. clearskies/endpoints/create.py +0 -202
  178. clearskies/endpoints/delete.py +0 -139
  179. clearskies/endpoints/get.py +0 -275
  180. clearskies/endpoints/health_check.py +0 -181
  181. clearskies/endpoints/list.py +0 -573
  182. clearskies/endpoints/restful_api.py +0 -427
  183. clearskies/endpoints/simple_search.py +0 -286
  184. clearskies/endpoints/update.py +0 -190
  185. clearskies/environment.py +0 -104
  186. clearskies/exceptions/__init__.py +0 -17
  187. clearskies/exceptions/authentication.py +0 -2
  188. clearskies/exceptions/authorization.py +0 -2
  189. clearskies/exceptions/client_error.py +0 -2
  190. clearskies/exceptions/input_errors.py +0 -4
  191. clearskies/exceptions/moved_permanently.py +0 -3
  192. clearskies/exceptions/moved_temporarily.py +0 -3
  193. clearskies/exceptions/not_found.py +0 -2
  194. clearskies/functional/__init__.py +0 -7
  195. clearskies/functional/routing.py +0 -92
  196. clearskies/functional/string.py +0 -112
  197. clearskies/functional/validations.py +0 -76
  198. clearskies/input_outputs/__init__.py +0 -13
  199. clearskies/input_outputs/cli.py +0 -171
  200. clearskies/input_outputs/exceptions/__init__.py +0 -2
  201. clearskies/input_outputs/exceptions/cli_input_error.py +0 -2
  202. clearskies/input_outputs/exceptions/cli_not_found.py +0 -2
  203. clearskies/input_outputs/headers.py +0 -45
  204. clearskies/input_outputs/input_output.py +0 -138
  205. clearskies/input_outputs/programmatic.py +0 -69
  206. clearskies/input_outputs/py.typed +0 -0
  207. clearskies/input_outputs/wsgi.py +0 -77
  208. clearskies/model.py +0 -1922
  209. clearskies/py.typed +0 -0
  210. clearskies/query/__init__.py +0 -12
  211. clearskies/query/condition.py +0 -223
  212. clearskies/query/join.py +0 -136
  213. clearskies/query/query.py +0 -196
  214. clearskies/query/sort.py +0 -27
  215. clearskies/schema.py +0 -82
  216. clearskies/secrets/__init__.py +0 -6
  217. clearskies/secrets/additional_configs/__init__.py +0 -32
  218. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +0 -61
  219. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +0 -160
  220. clearskies/secrets/akeyless.py +0 -182
  221. clearskies/secrets/exceptions/__init__.py +0 -1
  222. clearskies/secrets/exceptions/not_found.py +0 -2
  223. clearskies/secrets/secrets.py +0 -38
  224. clearskies/security_header.py +0 -15
  225. clearskies/security_headers/__init__.py +0 -11
  226. clearskies/security_headers/cache_control.py +0 -67
  227. clearskies/security_headers/cors.py +0 -50
  228. clearskies/security_headers/csp.py +0 -94
  229. clearskies/security_headers/hsts.py +0 -22
  230. clearskies/security_headers/x_content_type_options.py +0 -0
  231. clearskies/security_headers/x_frame_options.py +0 -0
  232. clearskies/test_base.py +0 -8
  233. clearskies/typing.py +0 -11
  234. clearskies/validator.py +0 -37
  235. clearskies/validators/__init__.py +0 -33
  236. clearskies/validators/after_column.py +0 -62
  237. clearskies/validators/before_column.py +0 -13
  238. clearskies/validators/in_the_future.py +0 -32
  239. clearskies/validators/in_the_future_at_least.py +0 -11
  240. clearskies/validators/in_the_future_at_most.py +0 -10
  241. clearskies/validators/in_the_past.py +0 -32
  242. clearskies/validators/in_the_past_at_least.py +0 -10
  243. clearskies/validators/in_the_past_at_most.py +0 -10
  244. clearskies/validators/maximum_length.py +0 -26
  245. clearskies/validators/maximum_value.py +0 -29
  246. clearskies/validators/minimum_length.py +0 -26
  247. clearskies/validators/minimum_value.py +0 -29
  248. clearskies/validators/required.py +0 -34
  249. clearskies/validators/timedelta.py +0 -59
  250. clearskies/validators/unique.py +0 -30
  251. {clear_skies-2.0.3.dist-info → clear_skies-2.0.5.dist-info/licenses}/LICENSE +0 -0
@@ -1,23 +0,0 @@
1
- from clearskies.di.inject.by_class import ByClass
2
- from clearskies.di.inject.by_name import ByName
3
- from clearskies.di.inject.di import Di
4
- from clearskies.di.inject.environment import Environment
5
- from clearskies.di.inject.input_output import InputOutput
6
- from clearskies.di.inject.now import Now
7
- from clearskies.di.inject.requests import Requests
8
- from clearskies.di.inject.secrets import Secrets
9
- from clearskies.di.inject.utcnow import Utcnow
10
- from clearskies.di.inject.uuid import Uuid
11
-
12
- __all__ = [
13
- "ByClass",
14
- "ByName",
15
- "Di",
16
- "Environment",
17
- "InputOutput",
18
- "Now",
19
- "Requests",
20
- "Secrets",
21
- "Utcnow",
22
- "Uuid",
23
- ]
@@ -1,21 +0,0 @@
1
- from typing import Any
2
-
3
- from clearskies.di.injectable import Injectable
4
-
5
-
6
- class ByClass(Injectable):
7
- def __init__(self, cls: type, cache: bool = True):
8
- if not isinstance(cls, type):
9
- raise TypeError(
10
- f"I expected a class for the first argument to clearskies.di.inject.ByClass, but I received an object of type '{cls.__class__.__name__}' instead."
11
- )
12
- self.cls = cls
13
- self.cache = cache
14
-
15
- def __get__(self, instance, parent) -> Any:
16
- if instance is None:
17
- return self # type: ignore
18
-
19
- if self.cls in self._di._class_overrides_by_class:
20
- return self._di.build_class(self._di._class_overrides_by_class[self.cls], cache=self.cache)
21
- return self._di.build_class(self.cls, cache=self.cache)
@@ -1,18 +0,0 @@
1
- from typing import Any
2
-
3
- from clearskies.di.injectable import Injectable
4
-
5
-
6
- class ByName(Injectable):
7
- def __init__(self, name: str, cache: bool = True):
8
- if not isinstance(name, str):
9
- raise TypeError(
10
- f"I expected a string for the first argument to clearskies.di.inject.ByName, but I received an object of type '{name.__class__.__name__}' instead."
11
- )
12
- self.name = name
13
- self.cache = cache
14
-
15
- def __get__(self, instance, parent) -> Any:
16
- if instance is None:
17
- return self # type: ignore
18
- return self._di.build_from_name(self.name, cache=self.cache)
@@ -1,13 +0,0 @@
1
- from typing import Any
2
-
3
- from clearskies.di.injectable import Injectable
4
-
5
-
6
- class Di(Injectable):
7
- def __init__(self):
8
- pass
9
-
10
- def __get__(self, instance, parent) -> Any:
11
- if instance is None:
12
- return self # type: ignore
13
- return self._di
@@ -1,14 +0,0 @@
1
- import requests
2
-
3
- from clearskies.di.injectable import Injectable
4
- from clearskies.environment import Environment as EnvironmentDependency
5
-
6
-
7
- class Environment(Injectable):
8
- def __init__(self, cache: bool = True):
9
- self.cache = cache
10
-
11
- def __get__(self, instance, parent) -> EnvironmentDependency:
12
- if instance is None:
13
- return self # type: ignore
14
- return self._di.build_from_name("environment", cache=self.cache)
@@ -1,20 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING
4
-
5
- import requests
6
-
7
- from clearskies.di.injectable import Injectable
8
-
9
- if TYPE_CHECKING:
10
- from clearskies.input_outputs.input_output import InputOutput as InputOuputDependency
11
-
12
-
13
- class InputOutput(Injectable):
14
- def __init__(self):
15
- pass
16
-
17
- def __get__(self, instance, parent) -> InputOuputDependency:
18
- if instance is None:
19
- return self # type: ignore
20
- return self._di.build_from_name("input_output", cache=True)
@@ -1,13 +0,0 @@
1
- import datetime
2
-
3
- from clearskies.di.injectable import Injectable
4
-
5
-
6
- class Now(Injectable):
7
- def __init__(self, cache: bool = False):
8
- self.cache = cache
9
-
10
- def __get__(self, instance, parent) -> datetime.datetime:
11
- if instance is None:
12
- return self # type: ignore
13
- return self._di.build_from_name("now", cache=self.cache)
@@ -1,13 +0,0 @@
1
- import requests
2
-
3
- from clearskies.di.injectable import Injectable
4
-
5
-
6
- class Requests(Injectable):
7
- def __init__(self, cache: bool = True):
8
- self.cache = cache
9
-
10
- def __get__(self, instance, parent) -> requests.Session:
11
- if instance is None:
12
- return self # type: ignore
13
- return self._di.build_from_name("requests", cache=self.cache)
@@ -1,14 +0,0 @@
1
- import datetime
2
-
3
- from clearskies.di.injectable import Injectable
4
- from clearskies.secrets import Secrets as SecretsHelper
5
-
6
-
7
- class Secrets(Injectable):
8
- def __init__(self, cache: bool = True):
9
- self.cache = cache
10
-
11
- def __get__(self, instance, parent) -> SecretsHelper:
12
- if instance is None:
13
- return self # type: ignore
14
- return self._di.build_from_name("secrets", cache=self.cache)
@@ -1,13 +0,0 @@
1
- import datetime
2
-
3
- from clearskies.di.injectable import Injectable
4
-
5
-
6
- class Utcnow(Injectable):
7
- def __init__(self, cache: bool = False):
8
- self.cache = cache
9
-
10
- def __get__(self, instance, parent) -> datetime.datetime:
11
- if instance is None:
12
- return self # type: ignore
13
- return self._di.build_from_name("utcnow", cache=self.cache)
@@ -1,15 +0,0 @@
1
- import datetime
2
- import types
3
- import uuid
4
-
5
- from clearskies.di.injectable import Injectable
6
-
7
-
8
- class Uuid(Injectable):
9
- def __init__(self, cache: bool = True):
10
- self.cache = cache
11
-
12
- def __get__(self, instance, parent) -> types.ModuleType:
13
- if instance is None:
14
- return self # type: ignore
15
- return self._di.build_from_name("uuid", cache=self.cache) # type: ignore
@@ -1,29 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Any
3
-
4
-
5
- class Injectable(ABC):
6
- _di: Any = None
7
-
8
- def initiated_guard(self, instance):
9
- if self._di:
10
- return
11
-
12
- reference = instance.__class__.__name__ + "."
13
- my_id = id(self)
14
- cls = instance.__class__
15
- for attribute_name in dir(instance):
16
- if id(getattr(cls, attribute_name)) != my_id:
17
- continue
18
- reference += attribute_name
19
- raise ValueError(
20
- f"There was an attempt to get a value out of '{reference}' but the injectable hasn't been properly"
21
- + "initialized. This usually means that objects are being created outside of the normal Di system."
22
- )
23
-
24
- def set_di(self, di):
25
- self._di = di
26
-
27
- @abstractmethod
28
- def __get__(self, instance, parent):
29
- pass
@@ -1,131 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING, Any, Self
4
-
5
- from clearskies.di.injectable import Injectable
6
-
7
- if TYPE_CHECKING:
8
- from clearskies.di import Di
9
-
10
-
11
- class InjectableProperties:
12
- """
13
- Fetch dependencies via properties rather than constructor arguments.
14
-
15
- This class allows you to specify dependencies by setting them as class properties instead of constructor
16
- arguments. This is common in clearskies as it helps make easily reusable classes - configuration can
17
- go in the constructor of the class, allowing the developer to directly instantiate it, and then the DI system
18
- will come by afterwards and provide the necessary dependencies.
19
-
20
- After adding InjectableProperties as a parent of your class, you have two ways to specify your dependencies:
21
-
22
- 1. By using the classes in the `clearskies.di.inject.*`module.
23
- 2. By directly attaching objects which also use the `InjectableProperties` class.
24
-
25
- The following table shows the dependencies that can be injected as properties via the clearskies.di.inject module:
26
-
27
- | Class | Type | Result |
28
- |----------------------------------|--------------------------------------|-------------------------------------------------|
29
- | clearskies.di.inject.ByClass | N/A | The specified class will be built |
30
- | clearskies.di.inject.ByName | N/A | The specified dependnecy name will be built |
31
- | clearskies.di.inject.Cursor | N/A | The PyMySQL cursor |
32
- | clearskies.di.inject.Di | N/A | The dependency injection container itself |
33
- | clearskies.di.inject.Environment | clearskies.Environment | The environment helper |
34
- | clearskies.di.inject.InputOutput | clearskies.input_outputs.InputOutput | The InputOutput object for the current request |
35
- | clearskies.di.inject.Now | datetime.datetime | The current time (no timezone) |
36
- | clearskies.di.inject.Requests | requests.Session | A requests session |
37
- | clearskies.di.inject.Utcnow | datetime.datetime | The current time (tzinfo=datetime.timezone.utc) |
38
-
39
- Note: now/utcnow are not cached, so you'll get the current time everytime you get a value out of the class property,
40
- unless a specific time has been set on the dependency injection container.
41
-
42
- Here's an example:
43
-
44
- ```python
45
- import clearskies
46
- import time
47
- import clearskies.decorators
48
-
49
-
50
- class MyOtherThing(clearskies.di.InjectableProperties):
51
- now = clearskies.di.inject.Now()
52
-
53
-
54
- class ReusableClass(clearskies.Configurable, clearskies.di.InjectableProperties):
55
- my_int = clearskies.configs.Integer(required=True)
56
- some_number = clearskies.di.inject.ByName("some_number")
57
- my_other_thing = clearskies.di.inject.ByClass(MyOtherThing)
58
-
59
- @clearskies.decorators.parameters_to_properties
60
- def __init__(self, my_int: int):
61
- self.finalize_and_validate_configuration()
62
-
63
- def my_value(self) -> int:
64
- return self.my_int * self.some_number
65
-
66
-
67
- class MyClass(clearskies.di.InjectableProperties):
68
- reusable = ReusableClass(5)
69
-
70
-
71
- class MyOtherClass(clearskies.di.InjectableProperties):
72
- reusable = ReusableClass(10)
73
-
74
-
75
- di = clearskies.di.Di(
76
- bindings={
77
- "some_number": 10,
78
- }
79
- )
80
-
81
- my_class = di.build(MyClass)
82
- print(my_class.reusable.my_value()) # prints 50
83
-
84
- my_other_class = di.build(MyOtherClass)
85
- print(my_other_class.reusable.my_value()) # prints 100
86
-
87
- start = my_class.reusable.my_other_thing.now
88
- time.sleep(1)
89
- stop = my_class.reusable.my_other_thing.now
90
- print((stop - start).seconds) # prints 1
91
- ```
92
- """
93
-
94
- _injectables_loaded: str = ""
95
-
96
- @classmethod
97
- def injectable_properties(cls, di: Di):
98
- # you would think that I would be able to just use a simple true/false flag attached to the class,
99
- # but I'm having this weird issue where (when I tried that) the flag was being shared between classes.
100
- # It shouldn't happen like that, but it is, so there is probably something subtle going on that I
101
- # haven't figured out yet, but this also works identitally, so :shrug:.
102
- # Also, keep track of the id of DI. We use class level caching but tests often use multiple DI containers
103
- # in one run, which means that we need to re-inject dependencies if we get a new DI container
104
- cache_name = str(cls) + str(id(di))
105
- if cache_name == cls._injectables_loaded:
106
- return
107
-
108
- injectable_descriptors: list[Any] = []
109
- injectable_properties: list[Self] = []
110
- for attribute_name in dir(cls):
111
- # Per the docs above, we want to inject properties for one of two things: the injectables from clearskies.di.inject,
112
- # and any object that itself extends this class. This is mildly tricky because the injectables are descriptors, and
113
- # so we get them using getattr on the class, while if it's not a descriptor, then we want to use getattr on self.
114
- # The important part here is that we modify descriptors at the class level, so the actual injected values have to
115
- # be stored in self, and not in the descriptor object. When it's not a descriptor, then we can modify the object
116
- # directly (since we're operating at the object level, not class level). Either way, while we go, let's keep track
117
- # of what our dependencies are and which ones are cached, so we only have to list the objects attributes the first time.
118
- attribute = getattr(cls, attribute_name)
119
-
120
- if di.has_class_override(attribute.__class__):
121
- setattr(cls, attribute_name, di.get_override_by_class(attribute))
122
- continue
123
-
124
- if issubclass(attribute.__class__, Injectable):
125
- attribute.set_di(di)
126
- continue
127
-
128
- if hasattr(attribute, "injectable_properties"):
129
- attribute.injectable_properties(di)
130
-
131
- cls._injectables_loaded = cache_name
@@ -1,6 +0,0 @@
1
- from . import another_module
2
- from .module_class import ModuleClass
3
-
4
-
5
- class Hi:
6
- pass
@@ -1,2 +0,0 @@
1
- class AnotherModuleClass:
2
- pass
@@ -1,5 +0,0 @@
1
- import datetime
2
-
3
-
4
- class ModuleClass:
5
- pass
clearskies/end.py DELETED
@@ -1,183 +0,0 @@
1
- # type: ignore
2
- from __future__ import annotations
3
-
4
- from abc import ABC
5
- from typing import TYPE_CHECKING, Any
6
-
7
- if TYPE_CHECKING:
8
- from clearskies.input_output import InputOutput
9
-
10
- from clearskies import exceptions
11
- from clearskies.functional import string
12
-
13
-
14
- class End(ABC):
15
- """
16
- DRY for endpoint and endpoint groups.
17
-
18
- This class is just here to hold some common functionality between Endpoints and EndpointGroups.
19
- The two classes have plenty of overlap but are different enough that I don't want either to inherit
20
- from the other.
21
- """
22
-
23
- def add_url_prefix(self, prefix: str) -> None:
24
- self.url = (prefix.rstrip("/") + "/" + self.url.lstrip("/")).lstrip("/")
25
-
26
- def top_level_authentication_and_authorization(self, input_output: InputOutput) -> None:
27
- """
28
- Handle authentication and authorization for this endpoint.
29
-
30
- In the event of an AuthN/AuthZ issue, raise an exception. Otherwise, return None
31
- """
32
- if not self.authentication:
33
- return
34
- try:
35
- if not self.authentication.authenticate(input_output):
36
- raise exceptions.Authentication("Not Authenticated")
37
- except exceptions.ClientError as client_error:
38
- raise exceptions.Authentication(str(client_error))
39
- if self.authorization:
40
- try:
41
- if not self.authorization.gate(input_output.authorization_data, input_output):
42
- raise exceptions.Authorization("Not Authorized")
43
- except exceptions.ClientError as client_error:
44
- raise exception.Authorization(str(client_error))
45
-
46
- def __call__(self, input_output: InputOutput) -> Any:
47
- """
48
- Execute the endpoint.
49
-
50
- This function mostly just checks AuthN/AuthZ and then passes along control to the handle method.
51
- It also checks for all the appropriate exceptions from clearskies.exceptions and turns those into the
52
- expected response. As a result, when building a new endpoint, you normally modify the handle method
53
- rather than this one.
54
- """
55
- # these two configs can have arbitrary classes attached, which may use injectable properties. Because they are
56
- # hiding in configs, the system for automatically discovering these won't work, so we have to manually check them.
57
- # We can't do this in the constructor because self.di hasn't been populated yet, and we can't do this in
58
- # our own injectable_properties class method because we need to operate at the instance level
59
- for config_name in ["authentication", "authorization"]:
60
- config = getattr(self, config_name)
61
- if config and hasattr(config, "injectable_properties"):
62
- config.injectable_properties(self.di)
63
-
64
- response = self.populate_routing_data(input_output)
65
- if response:
66
- return response
67
-
68
- self.di.add_binding("input_output", input_output)
69
-
70
- # catch everything when we do an AuthN/AuthZ check because we allow custom-defined classes,
71
- # and this gives more flexibility (or possibly forgiveness) for how they raise exceptions.
72
- try:
73
- self.top_level_authentication_and_authorization(input_output)
74
- except exceptions.Authentication as auth_error:
75
- return self.error(input_output, str(auth_error), 401)
76
- except exceptions.Authorization as auth_error:
77
- return self.error(input_output, str(auth_error), 403)
78
- except exceptions.NotFound as not_found:
79
- return self.error(input_output, str(not_found), 404)
80
- except exceptions.MovedPermanently as redirect:
81
- return self.redirect(input_output, str(redirect), 302)
82
- except exceptions.MovedTemporarily as redirect:
83
- return self.redirect(input_output, str(redirect), 307)
84
-
85
- try:
86
- response = self.handle(input_output)
87
- except exceptions.ClientError as client_error:
88
- return self.error(input_output, str(client_error), 400)
89
- except exceptions.InputErrors as input_errors:
90
- return self.input_errors(input_output, input_errors.errors)
91
- except exceptions.Authentication as auth_error:
92
- return self.error(input_output, str(auth_error), 401)
93
- except exceptions.Authorization as auth_error:
94
- return self.error(input_output, str(auth_error), 403)
95
- except exceptions.NotFound as auth_error:
96
- return self.error(input_output, str(auth_error), 404)
97
- except exceptions.MovedPermanently as redirect:
98
- return self.redirect(input_output, str(redirect), 302)
99
- except exceptions.MovedTemporarily as redirect:
100
- return self.redirect(input_output, str(redirect), 307)
101
-
102
- return response
103
-
104
- def populate_routing_data(self, input_output: InputOutput) -> Any:
105
- raise NotImplementedError()
106
-
107
- def add_response_headers(self, input_output: InputOutput) -> None:
108
- if self.response_headers:
109
- if callable(self.response_headers):
110
- response_headers = self.di.call_function(
111
- self.response_headers, **input_output.get_context_for_callables()
112
- )
113
- else:
114
- response_headers = self.response_headers
115
-
116
- for index, response_header in enumerate(response_headers):
117
- if not isinstance(response_header, str):
118
- raise TypeError(
119
- f"Invalid response header in entry #{index + 1}: the header should be a string, but I was given a type of '{header.__class__.__name__}' instead."
120
- )
121
- parts = response_header.split(":", 1)
122
- if len(parts) != 2:
123
- raise ValueError(
124
- f"Invalid response header in entry #{index + 1}: the header should be a string in the form of 'key: value' but the given header did not have a colon to separate key and value."
125
- )
126
- input_output.response_headers.add(parts[0], parts[1])
127
- for security_header in self.security_headers:
128
- security_header.set_headers_for_input_output(input_output)
129
-
130
- def respond(self, input_output: InputOutput, response: clearskies.typing.response, status_code: int) -> Any:
131
- self.add_response_headers(input_output)
132
- return input_output.respond(response, status_code)
133
-
134
- def respond_json(self, input_output: InputOutput, response_data: dict[str, Any], status_code: int) -> Any:
135
- if "content-type" not in input_output.response_headers:
136
- input_output.response_headers.add("content-type", "application/json")
137
- return self.respond(input_output, self.normalize_response(response_data), status_code)
138
-
139
- def normalize_response(self, response_data: dict[str, Any]) -> dict[str, Any]:
140
- if "status" not in response_data:
141
- raise ValueError("Huh, status got left out somehow")
142
- return {
143
- self.auto_case_internal_column_name("status"): self.auto_case_internal_column_name(response_data["status"]),
144
- self.auto_case_internal_column_name("error"): response_data.get("error", ""),
145
- self.auto_case_internal_column_name("data"): response_data.get("data", []),
146
- self.auto_case_internal_column_name("pagination"): self.normalize_pagination(
147
- response_data.get("pagination", {})
148
- ),
149
- self.auto_case_internal_column_name("input_errors"): response_data.get("input_errors", {}),
150
- }
151
-
152
- def normalize_pagination(self, pagination: dict[str, Any]) -> dict[str, Any]:
153
- # pagination isn't always relevant so if it is completely empty then leave it that way
154
- if not pagination:
155
- return pagination
156
- return {
157
- self.auto_case_internal_column_name("number_results"): pagination.get("number_results", 0),
158
- self.auto_case_internal_column_name("limit"): pagination.get("limit", 0),
159
- self.auto_case_internal_column_name("next_page"): {
160
- self.auto_case_internal_column_name(key): value
161
- for (key, value) in pagination.get("next_page", {}).items()
162
- },
163
- }
164
-
165
- def auto_case_internal_column_name(self, column_name: str) -> str:
166
- if self.external_casing:
167
- return string.swap_casing(column_name, "snake_case", self.external_casing)
168
- return column_name
169
-
170
- def auto_case_column_name(self, column_name: str, internal_to_external: bool) -> str:
171
- if not self.internal_casing:
172
- return column_name
173
- if internal_to_external:
174
- return string.swap_casing(
175
- column_name,
176
- self.internal_casing,
177
- self.external_casing,
178
- )
179
- return string.swap_casing(
180
- column_name,
181
- self.external_casing,
182
- self.internal_casing,
183
- )