clear-skies 1.22.31__py3-none-any.whl → 2.0.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.

Potentially problematic release.


This version of clear-skies might be problematic. Click here for more details.

Files changed (344) hide show
  1. {clear_skies-1.22.31.dist-info → clear_skies-2.0.0.dist-info}/METADATA +11 -13
  2. clear_skies-2.0.0.dist-info/RECORD +248 -0
  3. {clear_skies-1.22.31.dist-info → clear_skies-2.0.0.dist-info}/WHEEL +1 -1
  4. clearskies/__init__.py +42 -25
  5. clearskies/action.py +7 -0
  6. clearskies/authentication/__init__.py +8 -41
  7. clearskies/authentication/authentication.py +42 -0
  8. clearskies/authentication/authorization.py +4 -9
  9. clearskies/authentication/authorization_pass_through.py +11 -9
  10. clearskies/authentication/jwks.py +128 -58
  11. clearskies/authentication/public.py +3 -38
  12. clearskies/authentication/secret_bearer.py +516 -54
  13. clearskies/autodoc/formats/oai3_json/__init__.py +1 -1
  14. clearskies/autodoc/formats/oai3_json/oai3_json.py +9 -7
  15. clearskies/autodoc/formats/oai3_json/parameter.py +6 -3
  16. clearskies/autodoc/formats/oai3_json/request.py +7 -5
  17. clearskies/autodoc/formats/oai3_json/response.py +7 -4
  18. clearskies/autodoc/formats/oai3_json/schema/object.py +4 -1
  19. clearskies/autodoc/request/__init__.py +2 -0
  20. clearskies/autodoc/request/header.py +4 -6
  21. clearskies/autodoc/request/json_body.py +4 -6
  22. clearskies/autodoc/request/parameter.py +8 -0
  23. clearskies/autodoc/request/request.py +7 -4
  24. clearskies/autodoc/request/url_parameter.py +4 -6
  25. clearskies/autodoc/request/url_path.py +4 -6
  26. clearskies/autodoc/schema/__init__.py +4 -2
  27. clearskies/autodoc/schema/array.py +5 -6
  28. clearskies/autodoc/schema/boolean.py +4 -10
  29. clearskies/autodoc/schema/date.py +0 -3
  30. clearskies/autodoc/schema/datetime.py +1 -4
  31. clearskies/autodoc/schema/double.py +0 -3
  32. clearskies/autodoc/schema/enum.py +4 -2
  33. clearskies/autodoc/schema/integer.py +4 -9
  34. clearskies/autodoc/schema/long.py +0 -3
  35. clearskies/autodoc/schema/number.py +4 -9
  36. clearskies/autodoc/schema/object.py +5 -7
  37. clearskies/autodoc/schema/password.py +0 -3
  38. clearskies/autodoc/schema/schema.py +11 -0
  39. clearskies/autodoc/schema/string.py +4 -10
  40. clearskies/backends/__init__.py +55 -20
  41. clearskies/backends/api_backend.py +1100 -284
  42. clearskies/backends/backend.py +40 -84
  43. clearskies/backends/cursor_backend.py +236 -186
  44. clearskies/backends/memory_backend.py +519 -226
  45. clearskies/backends/secrets_backend.py +75 -31
  46. clearskies/column.py +1232 -0
  47. clearskies/columns/__init__.py +71 -0
  48. clearskies/columns/audit.py +205 -0
  49. clearskies/columns/belongs_to_id.py +483 -0
  50. clearskies/columns/belongs_to_model.py +128 -0
  51. clearskies/columns/belongs_to_self.py +105 -0
  52. clearskies/columns/boolean.py +109 -0
  53. clearskies/columns/category_tree.py +275 -0
  54. clearskies/columns/category_tree_ancestors.py +51 -0
  55. clearskies/columns/category_tree_children.py +127 -0
  56. clearskies/columns/category_tree_descendants.py +48 -0
  57. clearskies/columns/created.py +94 -0
  58. clearskies/columns/created_by_authorization_data.py +116 -0
  59. clearskies/columns/created_by_header.py +99 -0
  60. clearskies/columns/created_by_ip.py +92 -0
  61. clearskies/columns/created_by_routing_data.py +96 -0
  62. clearskies/columns/created_by_user_agent.py +92 -0
  63. clearskies/columns/date.py +230 -0
  64. clearskies/columns/datetime.py +278 -0
  65. clearskies/columns/email.py +76 -0
  66. clearskies/columns/float.py +149 -0
  67. clearskies/columns/has_many.py +505 -0
  68. clearskies/columns/has_many_self.py +56 -0
  69. clearskies/columns/has_one.py +14 -0
  70. clearskies/columns/integer.py +156 -0
  71. clearskies/columns/json.py +122 -0
  72. clearskies/columns/many_to_many_ids.py +333 -0
  73. clearskies/columns/many_to_many_ids_with_data.py +270 -0
  74. clearskies/columns/many_to_many_models.py +154 -0
  75. clearskies/columns/many_to_many_pivots.py +133 -0
  76. clearskies/columns/phone.py +158 -0
  77. clearskies/columns/select.py +91 -0
  78. clearskies/columns/string.py +98 -0
  79. clearskies/columns/timestamp.py +160 -0
  80. clearskies/columns/updated.py +110 -0
  81. clearskies/columns/uuid.py +86 -0
  82. clearskies/configs/README.md +105 -0
  83. clearskies/configs/__init__.py +159 -0
  84. clearskies/configs/actions.py +43 -0
  85. clearskies/configs/any.py +13 -0
  86. clearskies/configs/any_dict.py +22 -0
  87. clearskies/configs/any_dict_or_callable.py +23 -0
  88. clearskies/configs/authentication.py +23 -0
  89. clearskies/configs/authorization.py +23 -0
  90. clearskies/configs/boolean.py +16 -0
  91. clearskies/configs/boolean_or_callable.py +18 -0
  92. clearskies/configs/callable_config.py +18 -0
  93. clearskies/configs/columns.py +34 -0
  94. clearskies/configs/conditions.py +30 -0
  95. clearskies/configs/config.py +21 -0
  96. clearskies/configs/datetime.py +18 -0
  97. clearskies/configs/datetime_or_callable.py +19 -0
  98. clearskies/configs/endpoint.py +23 -0
  99. clearskies/configs/float.py +16 -0
  100. clearskies/configs/float_or_callable.py +18 -0
  101. clearskies/configs/integer.py +16 -0
  102. clearskies/configs/integer_or_callable.py +18 -0
  103. clearskies/configs/joins.py +30 -0
  104. clearskies/configs/list_any_dict.py +30 -0
  105. clearskies/configs/list_any_dict_or_callable.py +31 -0
  106. clearskies/configs/model_class.py +35 -0
  107. clearskies/configs/model_column.py +65 -0
  108. clearskies/configs/model_columns.py +56 -0
  109. clearskies/configs/model_destination_name.py +25 -0
  110. clearskies/configs/model_to_id_column.py +43 -0
  111. clearskies/configs/readable_model_column.py +9 -0
  112. clearskies/configs/readable_model_columns.py +9 -0
  113. clearskies/configs/schema.py +23 -0
  114. clearskies/configs/searchable_model_columns.py +9 -0
  115. clearskies/configs/security_headers.py +39 -0
  116. clearskies/configs/select.py +26 -0
  117. clearskies/configs/select_list.py +47 -0
  118. clearskies/configs/string.py +29 -0
  119. clearskies/configs/string_dict.py +32 -0
  120. clearskies/configs/string_list.py +32 -0
  121. clearskies/configs/string_list_or_callable.py +35 -0
  122. clearskies/configs/string_or_callable.py +18 -0
  123. clearskies/configs/timedelta.py +18 -0
  124. clearskies/configs/timezone.py +18 -0
  125. clearskies/configs/url.py +23 -0
  126. clearskies/configs/validators.py +45 -0
  127. clearskies/configs/writeable_model_column.py +9 -0
  128. clearskies/configs/writeable_model_columns.py +9 -0
  129. clearskies/configurable.py +76 -0
  130. clearskies/contexts/__init__.py +8 -8
  131. clearskies/contexts/cli.py +5 -42
  132. clearskies/contexts/context.py +78 -56
  133. clearskies/contexts/wsgi.py +13 -30
  134. clearskies/contexts/wsgi_ref.py +49 -0
  135. clearskies/di/__init__.py +10 -7
  136. clearskies/di/additional_config.py +115 -4
  137. clearskies/di/additional_config_auto_import.py +12 -0
  138. clearskies/di/di.py +742 -121
  139. clearskies/di/inject/__init__.py +23 -0
  140. clearskies/di/inject/by_class.py +21 -0
  141. clearskies/di/inject/by_name.py +18 -0
  142. clearskies/di/inject/di.py +13 -0
  143. clearskies/di/inject/environment.py +14 -0
  144. clearskies/di/inject/input_output.py +20 -0
  145. clearskies/di/inject/now.py +13 -0
  146. clearskies/di/inject/requests.py +13 -0
  147. clearskies/di/inject/secrets.py +14 -0
  148. clearskies/di/inject/utcnow.py +13 -0
  149. clearskies/di/inject/uuid.py +15 -0
  150. clearskies/di/injectable.py +29 -0
  151. clearskies/di/injectable_properties.py +131 -0
  152. clearskies/end.py +183 -0
  153. clearskies/endpoint.py +1309 -0
  154. clearskies/endpoint_group.py +297 -0
  155. clearskies/endpoints/__init__.py +23 -0
  156. clearskies/endpoints/advanced_search.py +526 -0
  157. clearskies/endpoints/callable.py +387 -0
  158. clearskies/endpoints/create.py +202 -0
  159. clearskies/endpoints/delete.py +139 -0
  160. clearskies/endpoints/get.py +275 -0
  161. clearskies/endpoints/health_check.py +181 -0
  162. clearskies/endpoints/list.py +573 -0
  163. clearskies/endpoints/restful_api.py +427 -0
  164. clearskies/endpoints/simple_search.py +286 -0
  165. clearskies/endpoints/update.py +190 -0
  166. clearskies/environment.py +5 -3
  167. clearskies/exceptions/__init__.py +17 -0
  168. clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
  169. clearskies/exceptions/moved_permanently.py +3 -0
  170. clearskies/exceptions/moved_temporarily.py +3 -0
  171. clearskies/exceptions/not_found.py +2 -0
  172. clearskies/functional/__init__.py +2 -2
  173. clearskies/functional/routing.py +92 -0
  174. clearskies/functional/string.py +19 -11
  175. clearskies/functional/validations.py +61 -9
  176. clearskies/input_outputs/__init__.py +9 -7
  177. clearskies/input_outputs/cli.py +130 -142
  178. clearskies/input_outputs/exceptions/__init__.py +1 -1
  179. clearskies/input_outputs/headers.py +45 -0
  180. clearskies/input_outputs/input_output.py +91 -122
  181. clearskies/input_outputs/programmatic.py +69 -0
  182. clearskies/input_outputs/wsgi.py +23 -38
  183. clearskies/model.py +489 -184
  184. clearskies/parameters_to_properties.py +31 -0
  185. clearskies/query/__init__.py +12 -0
  186. clearskies/query/condition.py +223 -0
  187. clearskies/query/join.py +136 -0
  188. clearskies/query/query.py +196 -0
  189. clearskies/query/sort.py +27 -0
  190. clearskies/schema.py +82 -0
  191. clearskies/secrets/__init__.py +3 -31
  192. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
  193. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
  194. clearskies/secrets/akeyless.py +88 -147
  195. clearskies/secrets/secrets.py +8 -8
  196. clearskies/security_header.py +8 -0
  197. clearskies/security_headers/__init__.py +8 -8
  198. clearskies/security_headers/cache_control.py +47 -110
  199. clearskies/security_headers/cors.py +40 -95
  200. clearskies/security_headers/csp.py +76 -151
  201. clearskies/security_headers/hsts.py +14 -16
  202. clearskies/test_base.py +8 -0
  203. clearskies/typing.py +11 -0
  204. clearskies/validator.py +25 -0
  205. clearskies/validators/__init__.py +33 -0
  206. clearskies/validators/after_column.py +62 -0
  207. clearskies/validators/before_column.py +13 -0
  208. clearskies/validators/in_the_future.py +32 -0
  209. clearskies/validators/in_the_future_at_least.py +11 -0
  210. clearskies/validators/in_the_future_at_most.py +10 -0
  211. clearskies/validators/in_the_past.py +32 -0
  212. clearskies/validators/in_the_past_at_least.py +10 -0
  213. clearskies/validators/in_the_past_at_most.py +10 -0
  214. clearskies/validators/maximum_length.py +26 -0
  215. clearskies/validators/maximum_value.py +29 -0
  216. clearskies/validators/minimum_length.py +26 -0
  217. clearskies/validators/minimum_value.py +29 -0
  218. clearskies/validators/required.py +35 -0
  219. clearskies/validators/timedelta.py +59 -0
  220. clearskies/validators/unique.py +31 -0
  221. clear_skies-1.22.31.dist-info/RECORD +0 -214
  222. clearskies/application.py +0 -29
  223. clearskies/authentication/auth0_jwks.py +0 -118
  224. clearskies/authentication/auth_exception.py +0 -2
  225. clearskies/authentication/jwks_jwcrypto.py +0 -51
  226. clearskies/backends/api_get_only_backend.py +0 -48
  227. clearskies/backends/example_backend.py +0 -43
  228. clearskies/backends/file_backend.py +0 -48
  229. clearskies/backends/json_backend.py +0 -7
  230. clearskies/backends/restful_api_advanced_search_backend.py +0 -103
  231. clearskies/binding_config.py +0 -16
  232. clearskies/column_types/__init__.py +0 -203
  233. clearskies/column_types/audit.py +0 -249
  234. clearskies/column_types/belongs_to.py +0 -271
  235. clearskies/column_types/boolean.py +0 -60
  236. clearskies/column_types/category_tree.py +0 -304
  237. clearskies/column_types/column.py +0 -373
  238. clearskies/column_types/created.py +0 -26
  239. clearskies/column_types/created_by_authorization_data.py +0 -26
  240. clearskies/column_types/created_by_header.py +0 -24
  241. clearskies/column_types/created_by_ip.py +0 -17
  242. clearskies/column_types/created_by_routing_data.py +0 -25
  243. clearskies/column_types/created_by_user_agent.py +0 -17
  244. clearskies/column_types/created_micro.py +0 -26
  245. clearskies/column_types/datetime.py +0 -109
  246. clearskies/column_types/datetime_micro.py +0 -12
  247. clearskies/column_types/email.py +0 -18
  248. clearskies/column_types/float.py +0 -43
  249. clearskies/column_types/has_many.py +0 -179
  250. clearskies/column_types/has_one.py +0 -60
  251. clearskies/column_types/integer.py +0 -41
  252. clearskies/column_types/json.py +0 -25
  253. clearskies/column_types/many_to_many.py +0 -278
  254. clearskies/column_types/many_to_many_with_data.py +0 -162
  255. clearskies/column_types/phone.py +0 -48
  256. clearskies/column_types/select.py +0 -11
  257. clearskies/column_types/string.py +0 -24
  258. clearskies/column_types/timestamp.py +0 -73
  259. clearskies/column_types/updated.py +0 -26
  260. clearskies/column_types/updated_micro.py +0 -26
  261. clearskies/column_types/uuid.py +0 -25
  262. clearskies/columns.py +0 -123
  263. clearskies/condition_parser.py +0 -172
  264. clearskies/contexts/build_context.py +0 -54
  265. clearskies/contexts/convert_to_application.py +0 -190
  266. clearskies/contexts/extract_handler.py +0 -37
  267. clearskies/contexts/test.py +0 -94
  268. clearskies/decorators/__init__.py +0 -41
  269. clearskies/decorators/allow_non_json_bodies.py +0 -9
  270. clearskies/decorators/auth0_jwks.py +0 -22
  271. clearskies/decorators/authorization.py +0 -10
  272. clearskies/decorators/binding_classes.py +0 -9
  273. clearskies/decorators/binding_modules.py +0 -9
  274. clearskies/decorators/bindings.py +0 -9
  275. clearskies/decorators/create.py +0 -10
  276. clearskies/decorators/delete.py +0 -10
  277. clearskies/decorators/docs.py +0 -14
  278. clearskies/decorators/get.py +0 -10
  279. clearskies/decorators/jwks.py +0 -26
  280. clearskies/decorators/merge.py +0 -124
  281. clearskies/decorators/patch.py +0 -10
  282. clearskies/decorators/post.py +0 -10
  283. clearskies/decorators/public.py +0 -11
  284. clearskies/decorators/response_headers.py +0 -10
  285. clearskies/decorators/return_raw_response.py +0 -9
  286. clearskies/decorators/schema.py +0 -10
  287. clearskies/decorators/secret_bearer.py +0 -24
  288. clearskies/decorators/security_headers.py +0 -10
  289. clearskies/di/standard_dependencies.py +0 -151
  290. clearskies/handlers/__init__.py +0 -41
  291. clearskies/handlers/advanced_search.py +0 -271
  292. clearskies/handlers/base.py +0 -479
  293. clearskies/handlers/callable.py +0 -192
  294. clearskies/handlers/create.py +0 -35
  295. clearskies/handlers/crud_by_method.py +0 -18
  296. clearskies/handlers/database_connector.py +0 -32
  297. clearskies/handlers/delete.py +0 -61
  298. clearskies/handlers/exceptions/__init__.py +0 -5
  299. clearskies/handlers/exceptions/not_found.py +0 -3
  300. clearskies/handlers/get.py +0 -156
  301. clearskies/handlers/health_check.py +0 -59
  302. clearskies/handlers/input_processing.py +0 -79
  303. clearskies/handlers/list.py +0 -530
  304. clearskies/handlers/mygrations.py +0 -82
  305. clearskies/handlers/request_method_routing.py +0 -47
  306. clearskies/handlers/restful_api.py +0 -218
  307. clearskies/handlers/routing.py +0 -62
  308. clearskies/handlers/schema_helper.py +0 -128
  309. clearskies/handlers/simple_routing.py +0 -206
  310. clearskies/handlers/simple_routing_route.py +0 -197
  311. clearskies/handlers/simple_search.py +0 -136
  312. clearskies/handlers/update.py +0 -102
  313. clearskies/handlers/write.py +0 -193
  314. clearskies/input_requirements/__init__.py +0 -78
  315. clearskies/input_requirements/after.py +0 -36
  316. clearskies/input_requirements/before.py +0 -36
  317. clearskies/input_requirements/in_the_future_at_least.py +0 -19
  318. clearskies/input_requirements/in_the_future_at_most.py +0 -19
  319. clearskies/input_requirements/in_the_past_at_least.py +0 -19
  320. clearskies/input_requirements/in_the_past_at_most.py +0 -19
  321. clearskies/input_requirements/maximum_length.py +0 -19
  322. clearskies/input_requirements/maximum_value.py +0 -19
  323. clearskies/input_requirements/minimum_length.py +0 -22
  324. clearskies/input_requirements/minimum_value.py +0 -19
  325. clearskies/input_requirements/required.py +0 -23
  326. clearskies/input_requirements/requirement.py +0 -25
  327. clearskies/input_requirements/time_delta.py +0 -38
  328. clearskies/input_requirements/unique.py +0 -18
  329. clearskies/mocks/__init__.py +0 -7
  330. clearskies/mocks/input_output.py +0 -124
  331. clearskies/mocks/models.py +0 -142
  332. clearskies/models.py +0 -350
  333. clearskies/security_headers/base.py +0 -12
  334. clearskies/tests/simple_api/models/__init__.py +0 -2
  335. clearskies/tests/simple_api/models/status.py +0 -23
  336. clearskies/tests/simple_api/models/user.py +0 -21
  337. clearskies/tests/simple_api/users_api.py +0 -64
  338. {clear_skies-1.22.31.dist-info → clear_skies-2.0.0.dist-info}/LICENSE +0 -0
  339. /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
  340. /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
  341. /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
  342. /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
  343. /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
  344. /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
@@ -0,0 +1,18 @@
1
+ from typing import Callable
2
+
3
+ from clearskies.configs import config
4
+
5
+
6
+ class StringOrCallable(config.Config):
7
+ def __set__(self, instance, value: str | Callable[..., str]):
8
+ if not isinstance(value, str) and not callable(value):
9
+ error_prefix = self._error_prefix(instance)
10
+ raise TypeError(
11
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requires a string or a callable."
12
+ )
13
+ instance._set_config(self, value)
14
+
15
+ def __get__(self, instance, parent) -> str | Callable[..., str]:
16
+ if not instance:
17
+ return self # type: ignore
18
+ return instance._get_config(self)
@@ -0,0 +1,18 @@
1
+ import datetime
2
+
3
+ from clearskies.configs import config
4
+
5
+
6
+ class Timedelta(config.Config):
7
+ def __set__(self, instance, value: datetime.timedelta):
8
+ if not isinstance(value, datetime.timedelta):
9
+ error_prefix = self._error_prefix(instance)
10
+ raise TypeError(
11
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requries a datetime.timedelta object."
12
+ )
13
+ instance._set_config(self, value)
14
+
15
+ def __get__(self, instance, parent) -> datetime.timedelta:
16
+ if not instance:
17
+ return self # type: ignore
18
+ return instance._get_config(self)
@@ -0,0 +1,18 @@
1
+ import datetime
2
+
3
+ from clearskies.configs import config
4
+
5
+
6
+ class Timezone(config.Config):
7
+ def __set__(self, instance, value: datetime.timezone | None):
8
+ if value and not isinstance(value, datetime.timezone):
9
+ error_prefix = self._error_prefix(instance)
10
+ raise TypeError(
11
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requries a timezone (datetime.timezone)."
12
+ )
13
+ instance._set_config(self, value)
14
+
15
+ def __get__(self, instance, parent) -> datetime.timezone:
16
+ if not instance:
17
+ return self # type: ignore
18
+ return instance._get_config(self)
@@ -0,0 +1,23 @@
1
+ from clearskies.configs import string
2
+ from clearskies.functional import routing
3
+
4
+
5
+ class Url(string.String):
6
+ def __set__(self, instance, value: str):
7
+ if value is None:
8
+ return
9
+
10
+ if not isinstance(value, str):
11
+ error_prefix = self._error_prefix(instance)
12
+ raise TypeError(
13
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a url parameter"
14
+ )
15
+ value = value.strip("/")
16
+
17
+ if value:
18
+ try:
19
+ routing.extract_url_parameter_name_map(value)
20
+ except ValueError as e:
21
+ error_prefix = self._error_prefix(instance)
22
+ raise ValueError(f"{error_prefix} {e}")
23
+ instance._set_config(self, value)
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from clearskies.configs import config
6
+
7
+ if TYPE_CHECKING:
8
+ from clearskies import typing
9
+
10
+
11
+ class Validators(config.Config):
12
+ """
13
+ Validator config.
14
+
15
+ A config that accepts various things that are accepted as validators in model columns:
16
+
17
+ 1. An instance of clearskies.validators.Validator
18
+ 2. A list of clearskies.validators.Validator
19
+
20
+ Incoming values are normalized to a list so that a list always comes out even if a non-list is provided.
21
+ """
22
+
23
+ def __set__(
24
+ self,
25
+ instance,
26
+ value: typing.validator | list[typing.validator],
27
+ ):
28
+ if not isinstance(value, list):
29
+ value = [value]
30
+
31
+ for index, item in enumerate(value):
32
+ if hasattr(item, "additional_write_columns") and hasattr(item, "check"):
33
+ continue
34
+
35
+ error_prefix = self._error_prefix(instance)
36
+ raise TypeError(
37
+ f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index + 1} when a Validator is required"
38
+ )
39
+
40
+ instance._set_config(self, [*value])
41
+
42
+ def __get__(self, instance, parent) -> list[typing.validator]:
43
+ if not instance:
44
+ return self # type: ignore
45
+ return instance._get_config(self)
@@ -0,0 +1,9 @@
1
+ from clearskies.configs import model_column
2
+
3
+
4
+ class WriteableModelColumn(model_column.ModelColumn):
5
+ def get_allowed_columns(self, model_class, column_configs):
6
+ return [name for (name, column) in column_configs.items() if column.is_writeable]
7
+
8
+ def my_description(self):
9
+ return "writeable column"
@@ -0,0 +1,9 @@
1
+ from clearskies.configs import model_columns
2
+
3
+
4
+ class WriteableModelColumns(model_columns.ModelColumns):
5
+ def get_allowed_columns(self, model_class, column_configs):
6
+ return [name for (name, column) in column_configs.items() if column.is_writeable]
7
+
8
+ def my_description(self):
9
+ return "writeable column"
@@ -0,0 +1,76 @@
1
+ from typing import Any
2
+
3
+ from clearskies.configs import config
4
+
5
+
6
+ class Configurable:
7
+ _config: dict[str, Any] | None = None
8
+ _descriptor_config_map: dict[int, str] | None = None
9
+
10
+ def _set_config(self, descriptor, value):
11
+ if not self._config:
12
+ self._config = {}
13
+
14
+ self._config[self._descriptor_to_name(descriptor)] = value
15
+
16
+ def _get_config(self, descriptor):
17
+ if not self._config:
18
+ self._config = {}
19
+
20
+ name = self._descriptor_to_name(descriptor)
21
+ if name not in self._config:
22
+ raise KeyError(f"Attempt to fetch a config value named '{name}' but no value has been set for this config")
23
+ return self._config[name]
24
+
25
+ @classmethod
26
+ def _get_config_object(cls, attribute_name):
27
+ return getattr(cls, attribute_name)
28
+
29
+ @classmethod
30
+ def get_descriptor_config_map(cls):
31
+ if cls._descriptor_config_map:
32
+ return cls._descriptor_config_map
33
+
34
+ descriptor_config_map = {}
35
+ for attribute_name in dir(cls):
36
+ descriptor = getattr(cls, attribute_name)
37
+ if not isinstance(descriptor, config.Config):
38
+ continue
39
+
40
+ descriptor_config_map[id(descriptor)] = attribute_name
41
+
42
+ cls._descriptor_config_map = descriptor_config_map
43
+ return cls._descriptor_config_map
44
+
45
+ def _descriptor_to_name(self, descriptor):
46
+ descriptor_config_map = self.get_descriptor_config_map()
47
+ if id(descriptor) not in descriptor_config_map:
48
+ raise ValueError(
49
+ f"The reason behind this error is kinda long and complicated, but doens't really matter. To make it go away, just add `_descriptor_config_map = None` to the definition of {self.__class__.__name__}"
50
+ )
51
+ return descriptor_config_map[id(descriptor)]
52
+
53
+ def finalize_and_validate_configuration(self):
54
+ my_class = self.__class__
55
+ if not self._config:
56
+ self._config = {}
57
+
58
+ # now it's time to check for required values and provide defaults
59
+ attribute_names = self.get_descriptor_config_map().values()
60
+ for attribute_name in attribute_names:
61
+ config = getattr(my_class, attribute_name)
62
+ if attribute_name not in self._config:
63
+ self._config[attribute_name] = config.default
64
+
65
+ if config.required and self._config.get(attribute_name) is None:
66
+ raise ValueError(
67
+ f"Missing required configuration property '{attribute_name}' for class '{my_class.__name__}'"
68
+ )
69
+
70
+ # loop through a second time to have the configs check their values
71
+ # we do this as a separate step because we want to make sure required and default
72
+ # values are specified before we have the configs do their validation.
73
+ for attribute_name in attribute_names:
74
+ getattr(my_class, attribute_name).finalize_and_validate_configuration(self)
75
+ if attribute_name not in self._config:
76
+ self._config[attribute_name] = None
@@ -1,11 +1,11 @@
1
- from .build_context import build_context
2
- from .cli import cli
3
- from .test import test
4
- from .wsgi import wsgi
1
+ from clearskies.contexts.cli import Cli
2
+ from clearskies.contexts.context import Context
3
+ from clearskies.contexts.wsgi import Wsgi
4
+ from clearskies.contexts.wsgi_ref import WsgiRef
5
5
 
6
6
  __all__ = [
7
- "build_context",
8
- "cli",
9
- "test",
10
- "wsgi",
7
+ "Cli",
8
+ "Context",
9
+ "Wsgi",
10
+ "WsgiRef",
11
11
  ]
@@ -1,44 +1,7 @@
1
- from ..authentication import public
2
- from ..input_outputs import CLI as CLIInputOutput
3
- from ..input_outputs import exceptions
4
- from .build_context import build_context
5
- from .context import Context
1
+ from clearskies.contexts.context import Context
2
+ from clearskies.input_outputs import Cli as CliInputOutput
6
3
 
7
4
 
8
- class CLI(Context):
9
- def __init__(self, di):
10
- super().__init__(di)
11
-
12
- def finalize_handler_config(self, config):
13
- return {
14
- "authentication": public(),
15
- **config,
16
- }
17
-
18
- def __call__(self):
19
- if self.handler is None:
20
- raise ValueError("Cannot execute CLI context without first configuring it")
21
-
22
- try:
23
- return self.handler(self.di.build(CLIInputOutput))
24
- except exceptions.CLINotFound:
25
- print("help (aka 404 not found)!")
26
-
27
-
28
- def cli(
29
- application,
30
- di_class=None,
31
- bindings=None,
32
- binding_classes=None,
33
- binding_modules=None,
34
- additional_configs=None,
35
- ):
36
- return build_context(
37
- CLI,
38
- application,
39
- di_class=di_class,
40
- bindings=bindings,
41
- binding_classes=binding_classes,
42
- binding_modules=binding_modules,
43
- additional_configs=additional_configs,
44
- )
5
+ class Cli(Context):
6
+ def __call__(self): # type: ignore
7
+ return self.execute_application(CliInputOutput())
@@ -1,62 +1,84 @@
1
- from ..handlers import callable as callable_module
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+ from types import ModuleType
5
+ from typing import TYPE_CHECKING, Any, Callable
6
+
7
+ import clearskies.endpoint
8
+ import clearskies.endpoint_group
9
+ from clearskies.di import Di
10
+ from clearskies.di.additional_config import AdditionalConfig
11
+ from clearskies.input_outputs import Programmatic
12
+
13
+ if TYPE_CHECKING:
14
+ from clearskies.input_outputs import InputOutput
2
15
 
3
16
 
4
17
  class Context:
5
- di = None
6
- handler = None
7
-
8
- def __init__(self, di):
9
- self.di = di
10
-
11
- def configure(self, application):
12
- self.handler = self.extract_handler(application)
13
-
14
- def bind(self, key, value):
15
- self.di.bind(key, value)
16
-
17
- def build(self, key):
18
- return self.di.build(key)
19
-
20
- def mock_class(self, class_or_name, replacement):
21
- self.di.mock_class(class_or_name, replacement)
22
-
23
- def extract_handler(self, application):
24
- """
25
- This accepts the application passed in to the context and returns the handler
26
-
27
- Most importantly, it doesn't technically have to be an application: if passed a simple function
28
- it will assume you mean to use the callable handler, and if passed a dictionary with 'handler_class'
29
- and 'handler_config' keys, it will build a handler from that.
30
- """
31
- # applications will have a handler_class property
32
- if hasattr(application, "handler_class"):
33
- handler = self.di.build(application.handler_class, cache=False)
34
- handler.configure(self.finalize_handler_config(application.handler_config))
35
- return handler
36
-
37
- # check for a dictionary with the same thing (in case the developer doesn't want to bother with
38
- # an application
39
- if hasattr(application, "__getitem__") and "handler_class" in application:
40
- if not "handler_config" in application:
41
- raise ValueError(
42
- "context was passed a dictionary-like object with 'handler_class', but not "
43
- + "'handler_config'. Both are required to execute the handler"
18
+ di: Di = None # type: ignore
19
+
20
+ def __init__(
21
+ self,
22
+ application: Callable | clearskies.endpoint.Endpoint | clearskies.endpoint_group.EndpointGroup,
23
+ classes: type | list[type] = [],
24
+ modules: ModuleType | list[ModuleType] = [],
25
+ bindings: dict[str, Any] = {},
26
+ additional_configs: AdditionalConfig | list[AdditionalConfig] = [],
27
+ class_overrides: dict[type, Any] = {},
28
+ overrides: dict[str, type] = {},
29
+ now: datetime.datetime | None = None,
30
+ utcnow: datetime.datetime | None = None,
31
+ ):
32
+ self.di = Di(
33
+ classes=classes,
34
+ modules=modules,
35
+ bindings=bindings,
36
+ additional_configs=additional_configs,
37
+ class_overrides=class_overrides,
38
+ now=now,
39
+ utcnow=utcnow,
40
+ )
41
+ self.application = application
42
+
43
+ def execute_application(self, input_output: InputOutput):
44
+ if hasattr(self.application, "injectable_properties"):
45
+ self.application.injectable_properties(self.di)
46
+ return self.application(input_output)
47
+ elif callable(self.application):
48
+ try:
49
+ return input_output.respond(
50
+ self.di.call_function(self.application, **input_output.get_context_for_callables())
44
51
  )
45
- handler = self.di.build(application["handler_class"], cache=False)
46
- handler.configure(self.finalize_handler_config(application["handler_config"]))
47
- return handler
48
-
49
- # if we get a callable, then use the callable handler class
50
- if callable(application):
51
- handler = self.di.build(callable_module.Callable, cache=False)
52
- handler.configure(self.finalize_handler_config({"callable": application}))
53
- return handler
54
-
55
- raise ValueError(
56
- "The context received an object it did not know how to handle! You should pass in either an instance "
57
- + "of clearskies.Application, a dictionary with 'handler_class' and 'handler_config' keys, or a "
58
- + "function/lambda to be executed"
52
+ except clearskies.exceptions.ClientError as e:
53
+ return input_output.respond(str(e), 400)
54
+ except clearskies.exceptions.Authentication as e:
55
+ return input_output.respond(str(e), 401)
56
+ except clearskies.exceptions.Authorization as e:
57
+ return input_output.respond(str(e), 403)
58
+ except clearskies.exceptions.NotFound as e:
59
+ return input_output.respond(str(e), 404)
60
+ except clearskies.exceptions.MovedPermanently as e:
61
+ return input_output.respond(str(e), 302)
62
+ except clearskies.exceptions.MovedTemporarily as e:
63
+ return input_output.respond(str(e), 307)
64
+
65
+ def __call__(
66
+ self,
67
+ url: str = "",
68
+ request_method: str = "GET",
69
+ body: str | dict[str, Any] | list[Any] = "",
70
+ query_parameters: dict[str, str] = {},
71
+ request_headers: dict[str, str] = {},
72
+ ):
73
+ return self.execute_application(
74
+ Programmatic(
75
+ url=url,
76
+ request_method=request_method,
77
+ body=body,
78
+ query_parameters=query_parameters,
79
+ request_headers=request_headers,
80
+ )
59
81
  )
60
82
 
61
- def finalize_handler_config(self, config):
62
- return config
83
+ def build(self, thing: Any, cache: bool = False) -> Any:
84
+ return self.di.build(thing, cache=cache)
@@ -1,33 +1,16 @@
1
- from ..input_outputs import WSGI as WSGIInputOutput
2
- from .build_context import build_context
3
- from .context import Context
1
+ import datetime
2
+ from types import ModuleType
3
+ from typing import Any, Callable
4
+ from wsgiref.simple_server import make_server
5
+ from wsgiref.util import setup_testing_defaults
4
6
 
7
+ import clearskies.endpoint
8
+ import clearskies.endpoint_group
9
+ from clearskies.contexts.context import Context
10
+ from clearskies.di import AdditionalConfig
11
+ from clearskies.input_outputs import Wsgi as WsgiInputOutput
5
12
 
6
- class WSGI(Context):
7
- def __init__(self, di):
8
- super().__init__(di)
9
13
 
10
- def __call__(self, env, start_response):
11
- if self.handler is None:
12
- raise ValueError("Cannot execute WSGI context without first configuring it")
13
-
14
- return self.handler(WSGIInputOutput(env, start_response))
15
-
16
-
17
- def wsgi(
18
- application,
19
- di_class=None,
20
- bindings=None,
21
- binding_classes=None,
22
- binding_modules=None,
23
- additional_configs=None,
24
- ):
25
- return build_context(
26
- WSGI,
27
- application,
28
- di_class=None,
29
- bindings=bindings,
30
- binding_classes=binding_classes,
31
- binding_modules=binding_modules,
32
- additional_configs=additional_configs,
33
- )
14
+ class Wsgi(Context):
15
+ def __call__(self, env, start_response): # type: ignore
16
+ return self.execute_application(WsgiInputOutput(env, start_response))
@@ -0,0 +1,49 @@
1
+ import datetime
2
+ from types import ModuleType
3
+ from typing import Any, Callable
4
+ from wsgiref.simple_server import make_server
5
+ from wsgiref.util import setup_testing_defaults
6
+
7
+ import clearskies.endpoint
8
+ import clearskies.endpoint_group
9
+ from clearskies.contexts.context import Context
10
+ from clearskies.di import AdditionalConfig
11
+ from clearskies.input_outputs import Wsgi as WsgiInputOutput
12
+
13
+
14
+ class WsgiRef(Context):
15
+ port: int = 8080
16
+
17
+ def __init__(
18
+ self,
19
+ application: Callable | clearskies.endpoint.Endpoint | clearskies.endpoint_group.EndpointGroup,
20
+ port: int = 8080,
21
+ classes: type | list[type] = [],
22
+ modules: ModuleType | list[ModuleType] = [],
23
+ bindings: dict[str, Any] = {},
24
+ additional_configs: AdditionalConfig | list[AdditionalConfig] = [],
25
+ class_overrides: dict[type, type] = {},
26
+ overrides: dict[str, type] = {},
27
+ now: datetime.datetime | None = None,
28
+ utcnow: datetime.datetime | None = None,
29
+ ):
30
+ super().__init__(
31
+ application,
32
+ classes=classes,
33
+ modules=modules,
34
+ bindings=bindings,
35
+ additional_configs=additional_configs,
36
+ class_overrides=class_overrides,
37
+ overrides=overrides,
38
+ now=now,
39
+ utcnow=utcnow,
40
+ )
41
+ self.port = port
42
+
43
+ def __call__(self): # type: ignore
44
+ with make_server("", self.port, self.handler) as httpd:
45
+ print(f"Starting WSGI server on port {self.port}. This is NOT intended for production usage.")
46
+ httpd.serve_forever()
47
+
48
+ def handler(self, environment, start_response):
49
+ return self.execute_application(WsgiInputOutput(environment, start_response))
clearskies/di/__init__.py CHANGED
@@ -1,11 +1,14 @@
1
- from .additional_config_auto_import import AdditionalConfigAutoImport
2
- from .di import DI
3
- from .standard_dependencies import StandardDependencies
4
- from .additional_config import AdditionalConfig
1
+ import clearskies.di.inject as inject
2
+ from clearskies.di.additional_config import AdditionalConfig
3
+ from clearskies.di.additional_config_auto_import import AdditionalConfigAutoImport
4
+ from clearskies.di.di import Di
5
+ from clearskies.di.injectable import Injectable
6
+ from clearskies.di.injectable_properties import InjectableProperties
5
7
 
6
8
  __all__ = [
7
- "AdditionalConfigAutoImport",
8
- "DI",
9
- "StandardDependencies",
10
9
  "AdditionalConfig",
10
+ "AdditionalConfigAutoImport",
11
+ "Di",
12
+ "InjectableProperties",
13
+ "injectInjectable",
11
14
  ]