clear-skies 1.19.22__py3-none-any.whl → 2.0.23__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 (362) hide show
  1. clear_skies-2.0.23.dist-info/METADATA +76 -0
  2. clear_skies-2.0.23.dist-info/RECORD +265 -0
  3. {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info}/WHEEL +1 -1
  4. clearskies/__init__.py +37 -21
  5. clearskies/action.py +7 -0
  6. clearskies/authentication/__init__.py +9 -38
  7. clearskies/authentication/authentication.py +44 -0
  8. clearskies/authentication/authorization.py +14 -8
  9. clearskies/authentication/authorization_pass_through.py +22 -0
  10. clearskies/authentication/jwks.py +135 -58
  11. clearskies/authentication/public.py +3 -26
  12. clearskies/authentication/secret_bearer.py +515 -44
  13. clearskies/autodoc/formats/oai3_json/__init__.py +2 -2
  14. clearskies/autodoc/formats/oai3_json/oai3_json.py +11 -9
  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 +10 -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 +16 -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 +56 -17
  41. clearskies/backends/api_backend.py +1128 -166
  42. clearskies/backends/backend.py +54 -85
  43. clearskies/backends/cursor_backend.py +246 -191
  44. clearskies/backends/memory_backend.py +514 -208
  45. clearskies/backends/secrets_backend.py +68 -31
  46. clearskies/column.py +1221 -0
  47. clearskies/columns/__init__.py +71 -0
  48. clearskies/columns/audit.py +306 -0
  49. clearskies/columns/belongs_to_id.py +478 -0
  50. clearskies/columns/belongs_to_model.py +129 -0
  51. clearskies/columns/belongs_to_self.py +109 -0
  52. clearskies/columns/boolean.py +110 -0
  53. clearskies/columns/category_tree.py +273 -0
  54. clearskies/columns/category_tree_ancestors.py +51 -0
  55. clearskies/columns/category_tree_children.py +126 -0
  56. clearskies/columns/category_tree_descendants.py +48 -0
  57. clearskies/columns/created.py +92 -0
  58. clearskies/columns/created_by_authorization_data.py +114 -0
  59. clearskies/columns/created_by_header.py +103 -0
  60. clearskies/columns/created_by_ip.py +90 -0
  61. clearskies/columns/created_by_routing_data.py +102 -0
  62. clearskies/columns/created_by_user_agent.py +89 -0
  63. clearskies/columns/date.py +232 -0
  64. clearskies/columns/datetime.py +284 -0
  65. clearskies/columns/email.py +78 -0
  66. clearskies/columns/float.py +149 -0
  67. clearskies/columns/has_many.py +529 -0
  68. clearskies/columns/has_many_self.py +62 -0
  69. clearskies/columns/has_one.py +21 -0
  70. clearskies/columns/integer.py +158 -0
  71. clearskies/columns/json.py +126 -0
  72. clearskies/columns/many_to_many_ids.py +335 -0
  73. clearskies/columns/many_to_many_ids_with_data.py +274 -0
  74. clearskies/columns/many_to_many_models.py +156 -0
  75. clearskies/columns/many_to_many_pivots.py +132 -0
  76. clearskies/columns/phone.py +162 -0
  77. clearskies/columns/select.py +95 -0
  78. clearskies/columns/string.py +102 -0
  79. clearskies/columns/timestamp.py +164 -0
  80. clearskies/columns/updated.py +107 -0
  81. clearskies/columns/uuid.py +83 -0
  82. clearskies/configs/README.md +105 -0
  83. clearskies/configs/__init__.py +170 -0
  84. clearskies/configs/actions.py +43 -0
  85. clearskies/configs/any.py +15 -0
  86. clearskies/configs/any_dict.py +24 -0
  87. clearskies/configs/any_dict_or_callable.py +25 -0
  88. clearskies/configs/authentication.py +23 -0
  89. clearskies/configs/authorization.py +23 -0
  90. clearskies/configs/boolean.py +18 -0
  91. clearskies/configs/boolean_or_callable.py +20 -0
  92. clearskies/configs/callable_config.py +20 -0
  93. clearskies/configs/columns.py +34 -0
  94. clearskies/configs/conditions.py +30 -0
  95. clearskies/configs/config.py +26 -0
  96. clearskies/configs/datetime.py +20 -0
  97. clearskies/configs/datetime_or_callable.py +21 -0
  98. clearskies/configs/email.py +10 -0
  99. clearskies/configs/email_list.py +17 -0
  100. clearskies/configs/email_list_or_callable.py +17 -0
  101. clearskies/configs/email_or_email_list_or_callable.py +59 -0
  102. clearskies/configs/endpoint.py +23 -0
  103. clearskies/configs/endpoint_list.py +29 -0
  104. clearskies/configs/float.py +18 -0
  105. clearskies/configs/float_or_callable.py +20 -0
  106. clearskies/configs/headers.py +28 -0
  107. clearskies/configs/integer.py +18 -0
  108. clearskies/configs/integer_or_callable.py +20 -0
  109. clearskies/configs/joins.py +30 -0
  110. clearskies/configs/list_any_dict.py +32 -0
  111. clearskies/configs/list_any_dict_or_callable.py +33 -0
  112. clearskies/configs/model_class.py +35 -0
  113. clearskies/configs/model_column.py +67 -0
  114. clearskies/configs/model_columns.py +58 -0
  115. clearskies/configs/model_destination_name.py +26 -0
  116. clearskies/configs/model_to_id_column.py +45 -0
  117. clearskies/configs/readable_model_column.py +11 -0
  118. clearskies/configs/readable_model_columns.py +11 -0
  119. clearskies/configs/schema.py +23 -0
  120. clearskies/configs/searchable_model_columns.py +11 -0
  121. clearskies/configs/security_headers.py +39 -0
  122. clearskies/configs/select.py +28 -0
  123. clearskies/configs/select_list.py +49 -0
  124. clearskies/configs/string.py +31 -0
  125. clearskies/configs/string_dict.py +34 -0
  126. clearskies/configs/string_list.py +47 -0
  127. clearskies/configs/string_list_or_callable.py +48 -0
  128. clearskies/configs/string_or_callable.py +18 -0
  129. clearskies/configs/timedelta.py +20 -0
  130. clearskies/configs/timezone.py +20 -0
  131. clearskies/configs/url.py +25 -0
  132. clearskies/configs/validators.py +45 -0
  133. clearskies/configs/writeable_model_column.py +11 -0
  134. clearskies/configs/writeable_model_columns.py +11 -0
  135. clearskies/configurable.py +78 -0
  136. clearskies/contexts/__init__.py +8 -8
  137. clearskies/contexts/cli.py +129 -43
  138. clearskies/contexts/context.py +93 -56
  139. clearskies/contexts/wsgi.py +79 -33
  140. clearskies/contexts/wsgi_ref.py +87 -0
  141. clearskies/cursors/__init__.py +7 -0
  142. clearskies/cursors/cursor.py +166 -0
  143. clearskies/cursors/from_environment/__init__.py +5 -0
  144. clearskies/cursors/from_environment/mysql.py +51 -0
  145. clearskies/cursors/from_environment/postgresql.py +49 -0
  146. clearskies/cursors/from_environment/sqlite.py +35 -0
  147. clearskies/cursors/mysql.py +61 -0
  148. clearskies/cursors/postgresql.py +61 -0
  149. clearskies/cursors/sqlite.py +62 -0
  150. clearskies/decorators.py +33 -0
  151. clearskies/decorators.pyi +10 -0
  152. clearskies/di/__init__.py +11 -7
  153. clearskies/di/additional_config.py +117 -3
  154. clearskies/di/additional_config_auto_import.py +12 -0
  155. clearskies/di/di.py +717 -126
  156. clearskies/di/inject/__init__.py +23 -0
  157. clearskies/di/inject/akeyless_sdk.py +16 -0
  158. clearskies/di/inject/by_class.py +24 -0
  159. clearskies/di/inject/by_name.py +22 -0
  160. clearskies/di/inject/di.py +16 -0
  161. clearskies/di/inject/environment.py +15 -0
  162. clearskies/di/inject/input_output.py +19 -0
  163. clearskies/di/inject/now.py +16 -0
  164. clearskies/di/inject/requests.py +16 -0
  165. clearskies/di/inject/secrets.py +15 -0
  166. clearskies/di/inject/utcnow.py +16 -0
  167. clearskies/di/inject/uuid.py +16 -0
  168. clearskies/di/injectable.py +32 -0
  169. clearskies/di/injectable_properties.py +131 -0
  170. clearskies/end.py +219 -0
  171. clearskies/endpoint.py +1303 -0
  172. clearskies/endpoint_group.py +333 -0
  173. clearskies/endpoints/__init__.py +25 -0
  174. clearskies/endpoints/advanced_search.py +519 -0
  175. clearskies/endpoints/callable.py +382 -0
  176. clearskies/endpoints/create.py +201 -0
  177. clearskies/endpoints/delete.py +133 -0
  178. clearskies/endpoints/get.py +267 -0
  179. clearskies/endpoints/health_check.py +181 -0
  180. clearskies/endpoints/list.py +567 -0
  181. clearskies/endpoints/restful_api.py +417 -0
  182. clearskies/endpoints/schema.py +185 -0
  183. clearskies/endpoints/simple_search.py +279 -0
  184. clearskies/endpoints/update.py +188 -0
  185. clearskies/environment.py +7 -3
  186. clearskies/exceptions/__init__.py +19 -0
  187. clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
  188. clearskies/exceptions/missing_dependency.py +2 -0
  189. clearskies/exceptions/moved_permanently.py +3 -0
  190. clearskies/exceptions/moved_temporarily.py +3 -0
  191. clearskies/functional/__init__.py +2 -2
  192. clearskies/functional/json.py +47 -0
  193. clearskies/functional/routing.py +92 -0
  194. clearskies/functional/string.py +19 -11
  195. clearskies/functional/validations.py +61 -9
  196. clearskies/input_outputs/__init__.py +9 -7
  197. clearskies/input_outputs/cli.py +135 -152
  198. clearskies/input_outputs/exceptions/__init__.py +6 -1
  199. clearskies/input_outputs/headers.py +54 -0
  200. clearskies/input_outputs/input_output.py +77 -123
  201. clearskies/input_outputs/programmatic.py +62 -0
  202. clearskies/input_outputs/wsgi.py +36 -48
  203. clearskies/model.py +1894 -199
  204. clearskies/query/__init__.py +12 -0
  205. clearskies/query/condition.py +228 -0
  206. clearskies/query/join.py +136 -0
  207. clearskies/query/query.py +193 -0
  208. clearskies/query/sort.py +27 -0
  209. clearskies/schema.py +82 -0
  210. clearskies/secrets/__init__.py +4 -31
  211. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
  212. clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
  213. clearskies/secrets/akeyless.py +421 -155
  214. clearskies/secrets/exceptions/__init__.py +7 -1
  215. clearskies/secrets/exceptions/not_found_error.py +2 -0
  216. clearskies/secrets/exceptions/permissions_error.py +2 -0
  217. clearskies/secrets/secrets.py +12 -11
  218. clearskies/security_header.py +17 -0
  219. clearskies/security_headers/__init__.py +8 -8
  220. clearskies/security_headers/cache_control.py +47 -109
  221. clearskies/security_headers/cors.py +38 -92
  222. clearskies/security_headers/csp.py +76 -150
  223. clearskies/security_headers/hsts.py +14 -15
  224. clearskies/typing.py +11 -0
  225. clearskies/validator.py +36 -0
  226. clearskies/validators/__init__.py +33 -0
  227. clearskies/validators/after_column.py +61 -0
  228. clearskies/validators/before_column.py +15 -0
  229. clearskies/validators/in_the_future.py +29 -0
  230. clearskies/validators/in_the_future_at_least.py +13 -0
  231. clearskies/validators/in_the_future_at_most.py +12 -0
  232. clearskies/validators/in_the_past.py +29 -0
  233. clearskies/validators/in_the_past_at_least.py +12 -0
  234. clearskies/validators/in_the_past_at_most.py +12 -0
  235. clearskies/validators/maximum_length.py +25 -0
  236. clearskies/validators/maximum_value.py +28 -0
  237. clearskies/validators/minimum_length.py +25 -0
  238. clearskies/validators/minimum_value.py +28 -0
  239. clearskies/{input_requirements → validators}/required.py +18 -9
  240. clearskies/validators/timedelta.py +58 -0
  241. clearskies/validators/unique.py +28 -0
  242. clear_skies-1.19.22.dist-info/METADATA +0 -46
  243. clear_skies-1.19.22.dist-info/RECORD +0 -206
  244. clearskies/application.py +0 -29
  245. clearskies/authentication/auth0_jwks.py +0 -118
  246. clearskies/authentication/auth_exception.py +0 -2
  247. clearskies/authentication/jwks_jwcrypto.py +0 -39
  248. clearskies/backends/example_backend.py +0 -43
  249. clearskies/backends/file_backend.py +0 -48
  250. clearskies/backends/json_backend.py +0 -7
  251. clearskies/backends/restful_api_advanced_search_backend.py +0 -138
  252. clearskies/binding_config.py +0 -16
  253. clearskies/column_types/__init__.py +0 -184
  254. clearskies/column_types/audit.py +0 -235
  255. clearskies/column_types/belongs_to.py +0 -250
  256. clearskies/column_types/boolean.py +0 -60
  257. clearskies/column_types/category_tree.py +0 -226
  258. clearskies/column_types/column.py +0 -373
  259. clearskies/column_types/created.py +0 -26
  260. clearskies/column_types/created_by_authorization_data.py +0 -26
  261. clearskies/column_types/created_by_header.py +0 -24
  262. clearskies/column_types/created_by_ip.py +0 -17
  263. clearskies/column_types/created_by_routing_data.py +0 -25
  264. clearskies/column_types/created_by_user_agent.py +0 -17
  265. clearskies/column_types/created_micro.py +0 -26
  266. clearskies/column_types/datetime.py +0 -108
  267. clearskies/column_types/datetime_micro.py +0 -12
  268. clearskies/column_types/email.py +0 -18
  269. clearskies/column_types/float.py +0 -43
  270. clearskies/column_types/has_many.py +0 -139
  271. clearskies/column_types/integer.py +0 -41
  272. clearskies/column_types/json.py +0 -25
  273. clearskies/column_types/many_to_many.py +0 -278
  274. clearskies/column_types/many_to_many_with_data.py +0 -162
  275. clearskies/column_types/select.py +0 -11
  276. clearskies/column_types/string.py +0 -24
  277. clearskies/column_types/updated.py +0 -24
  278. clearskies/column_types/updated_micro.py +0 -24
  279. clearskies/column_types/uuid.py +0 -25
  280. clearskies/columns.py +0 -123
  281. clearskies/condition_parser.py +0 -172
  282. clearskies/contexts/build_context.py +0 -54
  283. clearskies/contexts/convert_to_application.py +0 -190
  284. clearskies/contexts/extract_handler.py +0 -37
  285. clearskies/contexts/test.py +0 -94
  286. clearskies/decorators/__init__.py +0 -39
  287. clearskies/decorators/auth0_jwks.py +0 -22
  288. clearskies/decorators/authorization.py +0 -10
  289. clearskies/decorators/binding_classes.py +0 -9
  290. clearskies/decorators/binding_modules.py +0 -9
  291. clearskies/decorators/bindings.py +0 -9
  292. clearskies/decorators/create.py +0 -10
  293. clearskies/decorators/delete.py +0 -10
  294. clearskies/decorators/docs.py +0 -14
  295. clearskies/decorators/get.py +0 -10
  296. clearskies/decorators/jwks.py +0 -26
  297. clearskies/decorators/merge.py +0 -124
  298. clearskies/decorators/patch.py +0 -10
  299. clearskies/decorators/post.py +0 -10
  300. clearskies/decorators/public.py +0 -11
  301. clearskies/decorators/response_headers.py +0 -10
  302. clearskies/decorators/return_raw_response.py +0 -9
  303. clearskies/decorators/schema.py +0 -10
  304. clearskies/decorators/secret_bearer.py +0 -24
  305. clearskies/decorators/security_headers.py +0 -10
  306. clearskies/di/standard_dependencies.py +0 -140
  307. clearskies/di/test_module/__init__.py +0 -6
  308. clearskies/di/test_module/another_module/__init__.py +0 -2
  309. clearskies/di/test_module/module_class.py +0 -5
  310. clearskies/handlers/__init__.py +0 -41
  311. clearskies/handlers/advanced_search.py +0 -271
  312. clearskies/handlers/base.py +0 -473
  313. clearskies/handlers/callable.py +0 -189
  314. clearskies/handlers/create.py +0 -35
  315. clearskies/handlers/crud_by_method.py +0 -18
  316. clearskies/handlers/database_connector.py +0 -32
  317. clearskies/handlers/delete.py +0 -61
  318. clearskies/handlers/exceptions/__init__.py +0 -5
  319. clearskies/handlers/exceptions/not_found.py +0 -3
  320. clearskies/handlers/get.py +0 -156
  321. clearskies/handlers/health_check.py +0 -59
  322. clearskies/handlers/input_processing.py +0 -79
  323. clearskies/handlers/list.py +0 -530
  324. clearskies/handlers/mygrations.py +0 -82
  325. clearskies/handlers/request_method_routing.py +0 -47
  326. clearskies/handlers/restful_api.py +0 -218
  327. clearskies/handlers/routing.py +0 -62
  328. clearskies/handlers/schema_helper.py +0 -128
  329. clearskies/handlers/simple_routing.py +0 -204
  330. clearskies/handlers/simple_routing_route.py +0 -192
  331. clearskies/handlers/simple_search.py +0 -136
  332. clearskies/handlers/update.py +0 -96
  333. clearskies/handlers/write.py +0 -193
  334. clearskies/input_requirements/__init__.py +0 -68
  335. clearskies/input_requirements/after.py +0 -36
  336. clearskies/input_requirements/before.py +0 -36
  337. clearskies/input_requirements/in_the_future_at_least.py +0 -19
  338. clearskies/input_requirements/in_the_future_at_most.py +0 -19
  339. clearskies/input_requirements/in_the_past_at_least.py +0 -19
  340. clearskies/input_requirements/in_the_past_at_most.py +0 -19
  341. clearskies/input_requirements/maximum_length.py +0 -19
  342. clearskies/input_requirements/minimum_length.py +0 -22
  343. clearskies/input_requirements/requirement.py +0 -25
  344. clearskies/input_requirements/time_delta.py +0 -38
  345. clearskies/input_requirements/unique.py +0 -18
  346. clearskies/mocks/__init__.py +0 -7
  347. clearskies/mocks/input_output.py +0 -124
  348. clearskies/mocks/models.py +0 -142
  349. clearskies/models.py +0 -345
  350. clearskies/security_headers/base.py +0 -12
  351. clearskies/tests/simple_api/models/__init__.py +0 -2
  352. clearskies/tests/simple_api/models/status.py +0 -23
  353. clearskies/tests/simple_api/models/user.py +0 -21
  354. clearskies/tests/simple_api/users_api.py +0 -64
  355. {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info/licenses}/LICENSE +0 -0
  356. /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
  357. /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
  358. /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
  359. /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
  360. /clearskies/{secrets/exceptions → exceptions}/not_found.py +0 -0
  361. /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
  362. /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable
4
+
5
+ from clearskies.configs import config
6
+
7
+
8
+ class ListAnyDictOrCallable(config.Config):
9
+ def __set__(self, instance, value: list[dict[str, Any]] | Callable[..., list[dict[str, Any]]]):
10
+ if not isinstance(value, list) and not callable(value):
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 parameter that requries a list or a callable."
14
+ )
15
+ if isinstance(value, list):
16
+ for index, list_item in enumerate(value):
17
+ if not isinstance(list_item, dict):
18
+ error_prefix = self._error_prefix(instance)
19
+ raise TypeError(
20
+ f"{error_prefix} I was expecting a list of dictionaries, but item # {index + 1} has type '{list_item.__class__.__name__}."
21
+ )
22
+ for key, val in list_item.items():
23
+ if not isinstance(key, str):
24
+ error_prefix = self._error_prefix(instance)
25
+ raise TypeError(
26
+ f"{error_prefix} attempt to set a dictionary with a non-string key for item #{index + 1}."
27
+ )
28
+ instance._set_config(self, value)
29
+
30
+ def __get__(self, instance, parent) -> list[dict[str, Any]] | Callable[..., list[dict[str, Any]]]:
31
+ if not instance:
32
+ return self # type: ignore
33
+ return instance._get_config(self)
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from typing import TYPE_CHECKING
5
+
6
+ from clearskies.configs import config
7
+ from clearskies.functional import validations
8
+
9
+ if TYPE_CHECKING:
10
+ from clearskies.model import Model, ModelClassReference
11
+
12
+
13
+ class ModelClass(config.Config):
14
+ """A config that accepts a model class."""
15
+
16
+ def __set__(self, instance, value: type[Model | ModelClassReference]):
17
+ try:
18
+ validations.is_model_class_or_reference(value, raise_error_message=True, strict=False)
19
+ except TypeError as e:
20
+ error_prefix = self._error_prefix(instance)
21
+ raise TypeError(f"{error_prefix} {str(e)}")
22
+
23
+ # reference or model class?
24
+ instance._set_config(self, value) # type: ignore
25
+
26
+ def __get__(self, instance, parent) -> type[Model]:
27
+ if not instance:
28
+ return self # type: ignore
29
+
30
+ value = instance._get_config(self)
31
+ if validations.is_model_class_reference(value):
32
+ class_reference = value() if inspect.isclass(value) else value
33
+ instance._set_config(self, class_reference.get_model_class())
34
+
35
+ return instance._get_config(self)
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import select
4
+
5
+
6
+ class ModelColumn(select.Select):
7
+ def __init__(self, model_column_config_name="", required=False, default=None):
8
+ self.required = required
9
+ self.default = default
10
+ self.model_column_config_name = model_column_config_name
11
+ self.model_class = None
12
+
13
+ def __set__(self, instance, value: str):
14
+ if value is None:
15
+ return
16
+
17
+ if not isinstance(value, str):
18
+ error_prefix = self._error_prefix(instance)
19
+ raise TypeError(
20
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a string parameter"
21
+ )
22
+
23
+ instance._set_config(self, value)
24
+ # unlike select, we won't validate the value currently because we won't be able to
25
+ # do that until the finalize_and_validate_configuration phase
26
+
27
+ def get_allowed_columns(self, model_class, column_configs):
28
+ return [name for name in column_configs.keys()]
29
+
30
+ def my_description(self):
31
+ return "column"
32
+
33
+ def get_model_class(self, instance):
34
+ model_class = self.model_class
35
+ if not model_class and self.model_column_config_name:
36
+ model_class = getattr(instance, self.model_column_config_name)
37
+ return model_class
38
+
39
+ def set_model_class(self, model_class):
40
+ self.model_class = model_class
41
+
42
+ def finalize_and_validate_configuration(self, instance):
43
+ super().finalize_and_validate_configuration(instance)
44
+
45
+ # to check for a valid column we need to know the name of the model class.
46
+ # This can be either provided to us directly or we may be given the name of a configuration
47
+ # from which we fetch the model class
48
+ model_class = self.get_model_class(instance)
49
+
50
+ # if we don't have one though, no worries - some classes have to provide it later and
51
+ # can trigger validation then.
52
+ if not model_class:
53
+ return
54
+
55
+ allowed_columns = self.get_allowed_columns(model_class, model_class.get_columns())
56
+
57
+ value = instance._get_config(self)
58
+ if not value:
59
+ return
60
+ if value not in allowed_columns:
61
+ error_prefix = self._error_prefix(instance)
62
+ my_description = self.my_description()
63
+ raise ValueError(
64
+ f"{error_prefix} attempt to set a value of '{value}' but this is not a {my_description} in the specified model class, '{model_class.__name__}'. Expected values are: '"
65
+ + "', '".join(allowed_columns)
66
+ + "'"
67
+ )
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import select_list
4
+
5
+
6
+ class ModelColumns(select_list.SelectList):
7
+ def __init__(self, model_column_config_name="", allow_relationship_references=False, required=False, default=None):
8
+ self.required = required
9
+ self.default = default
10
+ self.model_column_config_name = model_column_config_name
11
+ self.allow_relationship_references = allow_relationship_references
12
+
13
+ def __set__(self, instance, value: list[str]):
14
+ if value is None:
15
+ return
16
+
17
+ if not isinstance(value, list):
18
+ error_prefix = self._error_prefix(instance)
19
+ raise TypeError(
20
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a list parameter"
21
+ )
22
+
23
+ instance._set_config(self, value)
24
+ # unlike select_list, we won't validate the value currently because we won't be able to
25
+ # do that until the finalize_and_validate_configuration phase
26
+
27
+ def get_allowed_columns(self, model_class, column_configs):
28
+ return [name for name in column_configs.keys()]
29
+
30
+ def my_description(self):
31
+ return "column"
32
+
33
+ def finalize_and_validate_configuration(self, instance):
34
+ super().finalize_and_validate_configuration(instance)
35
+
36
+ # when we were created we were told the name of the config that stores
37
+ # the model class we're getting readable columns from. Let's use that
38
+ # and fetch the model class. Note that this is an optional feature though
39
+ if not self.model_column_config_name:
40
+ return
41
+
42
+ model_class = getattr(instance, self.model_column_config_name)
43
+ values = instance._get_config(self)
44
+ if not values or not model_class:
45
+ return
46
+
47
+ allowed_columns = self.get_allowed_columns(model_class, model_class.get_columns())
48
+ for value in values:
49
+ if self.allow_relationship_references and "." in value:
50
+ value = value.split(".")[0]
51
+ if value not in allowed_columns:
52
+ error_prefix = self._error_prefix(instance)
53
+ my_description = self.my_description()
54
+ raise ValueError(
55
+ f"{error_prefix} attempt to set a value of '{value}' but this is not a {my_description} in the specified model class, '{model_class.__name__}. Expected values are: '"
56
+ + "', '".join(allowed_columns)
57
+ + "'"
58
+ )
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs.string import String
4
+
5
+
6
+ class ModelDestinationName(String):
7
+ def __init__(self, model_column_config_name, required=False, default=None):
8
+ self.required = required
9
+ self.default = default
10
+ self.model_column_config_name = model_column_config_name
11
+
12
+ def get_model_class(self, instance):
13
+ if self.model_column_config_name:
14
+ return getattr(instance, self.model_column_config_name)
15
+ return None
16
+
17
+ def finalize_and_validate_configuration(self, instance):
18
+ # we use the model class itself to decide on our value. However,
19
+ # if we don't have it yet, don't worry. Someone will eventually tell us
20
+ # what we need to know
21
+ model_class = self.get_model_class(instance)
22
+ if not model_class:
23
+ return
24
+
25
+ instance._set_config(self, model_class.destination_name())
26
+ super().finalize_and_validate_configuration(instance)
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import model_column
4
+ from clearskies.functional import string
5
+
6
+
7
+ class ModelToIdColumn(model_column.ModelColumn):
8
+ def __init__(
9
+ self,
10
+ model_column_config_name="",
11
+ source_model_class=None,
12
+ source_model_class_config_name="",
13
+ required=False,
14
+ default=None,
15
+ ):
16
+ self.required = required
17
+ self.default = default
18
+ self.model_column_config_name = model_column_config_name
19
+ self.source_model_class_config_name = source_model_class_config_name
20
+ self.model_class = None
21
+ self.source_model_class = source_model_class
22
+
23
+ def finalize_and_validate_configuration(self, instance):
24
+ # we use the model class itself to decide on our value. However,
25
+ # if we don't have it yet, don't worry. Someone will eventually tell us
26
+ # what we need to know
27
+ model_class = self.get_model_class(instance)
28
+ if not model_class:
29
+ return
30
+ if self.source_model_class:
31
+ model_class = self.source_model_class
32
+ elif self.source_model_class_config_name:
33
+ model_class = getattr(instance, self.source_model_class_config_name)
34
+
35
+ has_config = False
36
+ try:
37
+ if instance._get_config(self):
38
+ has_config = True
39
+ except KeyError:
40
+ pass
41
+
42
+ if not has_config:
43
+ instance._set_config(self, string.camel_case_to_snake_case(model_class.__name__) + "_id")
44
+
45
+ super().finalize_and_validate_configuration(instance)
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import model_column
4
+
5
+
6
+ class ReadableModelColumn(model_column.ModelColumn):
7
+ def get_allowed_columns(self, model_class, column_configs):
8
+ return [name for (name, column) in column_configs.items() if column.is_readable]
9
+
10
+ def my_description(self):
11
+ return "readable column"
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import model_columns
4
+
5
+
6
+ class ReadableModelColumns(model_columns.ModelColumns):
7
+ def get_allowed_columns(self, model_class, column_configs):
8
+ return [name for (name, column) in column_configs.items() if column.is_readable]
9
+
10
+ def my_description(self):
11
+ return "readable column"
@@ -0,0 +1,23 @@
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.schema import Schema as SchemaType
9
+
10
+
11
+ class Schema(config.Config):
12
+ def __set__(self, instance, value: type[SchemaType]) -> None:
13
+ if not hasattr(value, "get_columns"):
14
+ error_prefix = self._error_prefix(instance)
15
+ raise TypeError(
16
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to parameter that requires a Schema."
17
+ )
18
+ instance._set_config(self, value)
19
+
20
+ def __get__(self, instance, parent) -> type[SchemaType]:
21
+ if not instance:
22
+ return self # type: ignore
23
+ return instance._get_config(self)
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import model_columns
4
+
5
+
6
+ class SearchableModelColumns(model_columns.ModelColumns):
7
+ def get_allowed_columns(self, model_class, column_configs):
8
+ return [name for (name, column) in column_configs.items() if column.is_searchable]
9
+
10
+ def my_description(self):
11
+ return "searchable column"
@@ -0,0 +1,39 @@
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 SecurityHeader
9
+
10
+
11
+ class SecurityHeaders(config.Config):
12
+ """
13
+ This is for a configuration that should be a list of strings.
14
+
15
+ This is different than SelectList, which also accepts a list of strings, but
16
+ valdiates that all of those values match against an allow list.
17
+ """
18
+
19
+ def __set__(self, instance, value: list[SecurityHeader]):
20
+ if value is None:
21
+ return
22
+
23
+ if not isinstance(value, list):
24
+ error_prefix = self._error_prefix(instance)
25
+ raise TypeError(
26
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a list parameter"
27
+ )
28
+ for index, item in enumerate(value):
29
+ if not hasattr(item, "set_headers_for_input_output"):
30
+ error_prefix = self._error_prefix(instance)
31
+ raise TypeError(
32
+ f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index + 1}. A clearskies.SecurityHeader was expected."
33
+ )
34
+ instance._set_config(self, value)
35
+
36
+ def __get__(self, instance, parent) -> list[SecurityHeader]:
37
+ if not instance:
38
+ return self # type: ignore
39
+ return instance._get_config(self)
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import string
4
+
5
+
6
+ class Select(string.String):
7
+ def __init__(self, allowed_values: list[str], required=False, default=None):
8
+ self.allowed_values = allowed_values
9
+ self.required = required
10
+ self.default = default
11
+
12
+ def __set__(self, instance, value: str):
13
+ if value is None:
14
+ return
15
+
16
+ if not isinstance(value, str):
17
+ error_prefix = self._error_prefix(instance)
18
+ raise TypeError(
19
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a string parameter"
20
+ )
21
+ if value not in self.allowed_values:
22
+ error_prefix = self._error_prefix(instance)
23
+ raise ValueError(
24
+ f"{error_prefix} attempt to set a value of type '{value}' which is not in the list of allowed values. It must be one of '" # type: ignore
25
+ + "', '".join(self.allowed_values)
26
+ + "'"
27
+ )
28
+ instance._set_config(self, value)
@@ -0,0 +1,49 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import config
4
+
5
+
6
+ class SelectList(config.Config):
7
+ """
8
+ This is for a configuration that should be a list of strings matching some list of allowed values.
9
+
10
+ The allowed values are set when you create the config, and when values are set for the config
11
+ they must match.
12
+
13
+ This is different than StringList, which also accepts a list of any strings.
14
+ """
15
+
16
+ def __init__(self, allowed_values: list[str], required=False, default=None):
17
+ self.allowed_values = allowed_values
18
+ self.required = required
19
+ self.default = default
20
+
21
+ def __set__(self, instance, value: list[str]):
22
+ if value is None:
23
+ return
24
+
25
+ if not isinstance(value, list):
26
+ error_prefix = self._error_prefix(instance)
27
+ raise TypeError(
28
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a list parameter"
29
+ )
30
+ for index, item in enumerate(value):
31
+ if not isinstance(item, str):
32
+ error_prefix = self._error_prefix(instance)
33
+ raise TypeError(
34
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' for item #{index + 1}. A string was expected."
35
+ )
36
+
37
+ if item not in self.allowed_values:
38
+ error_prefix = self._error_prefix(instance)
39
+ raise ValueError(
40
+ f"{error_prefix} attempt to set a value of '{item}' for item #{index + 1}. This is not in the list of allowed values. It must be one of '"
41
+ + "', '".join(self.allowed_values)
42
+ + "'"
43
+ )
44
+ instance._set_config(self, [*value])
45
+
46
+ def __get__(self, instance, parent) -> list[str]:
47
+ if not instance:
48
+ return self # type: ignore
49
+ return instance._get_config(self)
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ from clearskies.configs import config
6
+
7
+
8
+ class String(config.Config):
9
+ def __init__(self, required=False, default=None, regexp: str = ""):
10
+ self.required = required
11
+ self.default = default
12
+ self.regexp = regexp
13
+
14
+ def __set__(self, instance, value: str):
15
+ if not isinstance(value, str):
16
+ error_prefix = self._error_prefix(instance)
17
+ raise TypeError(
18
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requires a string."
19
+ )
20
+ if self.regexp:
21
+ if not re.match(self.regexp, value):
22
+ error_prefix = self._error_prefix(instance)
23
+ raise ValueError(
24
+ f"{error_prefix} attempt to set a value of '{value}' but this does not match the required regexp: '{self.regexp}'."
25
+ )
26
+ instance._set_config(self, value)
27
+
28
+ def __get__(self, instance, parent) -> str:
29
+ if not instance:
30
+ return self # type: ignore
31
+ return instance._get_config(self)
@@ -0,0 +1,34 @@
1
+ from __future__ import annotations
2
+
3
+ from clearskies.configs import config
4
+
5
+
6
+ class StringDict(config.Config):
7
+ """This is for a configuration that should be a dictionary with keys and values that are all strings."""
8
+
9
+ def __set__(self, instance, value: dict[str, str]):
10
+ if value is None:
11
+ return
12
+
13
+ if not isinstance(value, dict):
14
+ error_prefix = self._error_prefix(instance)
15
+ raise TypeError(
16
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a dict parameter"
17
+ )
18
+ for key, val in value.items():
19
+ if not isinstance(key, str):
20
+ error_prefix = self._error_prefix(instance)
21
+ raise TypeError(
22
+ f"{error_prefix} attempt to set a key of type '{key.__class__.__name__}' when only string keys are allowed."
23
+ )
24
+ if not isinstance(val, str):
25
+ error_prefix = self._error_prefix(instance)
26
+ raise TypeError(
27
+ f"{error_prefix} attempt to set a value of type '{val.__class__.__name__}' for key '{key}'. A string was expected."
28
+ )
29
+ instance._set_config(self, value)
30
+
31
+ def __get__(self, instance, parent) -> dict[str, str]:
32
+ if not instance:
33
+ return self # type: ignore
34
+ return instance._get_config(self)
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ from clearskies.configs import config
6
+
7
+
8
+ class StringList(config.Config):
9
+ """
10
+ This is for a configuration that should be a list of strings.
11
+
12
+ This is different than SelectList, which also accepts a list of strings, but
13
+ valdiates that all of those values match against an allow list.
14
+ """
15
+
16
+ def __init__(self, required=False, default=None, regexp: str = ""):
17
+ self.required = required
18
+ self.default = default
19
+ self.regexp = regexp
20
+
21
+ def __set__(self, instance, value: list[str]):
22
+ if value is None:
23
+ return
24
+
25
+ if not isinstance(value, list):
26
+ error_prefix = self._error_prefix(instance)
27
+ raise TypeError(
28
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a list of strings parameter."
29
+ )
30
+
31
+ for index, item in enumerate(value):
32
+ if not isinstance(item, str):
33
+ error_prefix = self._error_prefix(instance)
34
+ raise TypeError(
35
+ f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index + 1}. A string was expected."
36
+ )
37
+ if self.regexp and not re.match(self.regexp, item):
38
+ error_prefix = self._error_prefix(instance)
39
+ raise ValueError(
40
+ f"{error_prefix} attempt to set a value of '{item}' for item #{index + 1} but this does not match the required regexp: '{self.regexp}'."
41
+ )
42
+ instance._set_config(self, value)
43
+
44
+ def __get__(self, instance, parent) -> list[str]:
45
+ if not instance:
46
+ return self # type: ignore
47
+ return instance._get_config(self)
@@ -0,0 +1,48 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Callable
5
+
6
+ from clearskies.configs import config
7
+
8
+
9
+ class StringListOrCallable(config.Config):
10
+ """
11
+ This is for a configuration that should be a list of strings or a callable that returns a list of strings.
12
+
13
+ This is different than SelectList, which also accepts a list of strings, but
14
+ valdiates that all of those values match against an allow list.
15
+ """
16
+
17
+ def __init__(self, required=False, default=None, regexp: str = ""):
18
+ self.required = required
19
+ self.default = default
20
+ self.regexp = regexp
21
+
22
+ def __set__(self, instance, value: list[str] | Callable[..., list[str]]):
23
+ if value is None:
24
+ return
25
+
26
+ if not isinstance(value, list) and not callable(value):
27
+ error_prefix = self._error_prefix(instance)
28
+ raise TypeError(
29
+ f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that should be a list or a callable"
30
+ )
31
+ if isinstance(value, list):
32
+ for index, item in enumerate(value):
33
+ if not isinstance(item, str):
34
+ error_prefix = self._error_prefix(instance)
35
+ raise TypeError(
36
+ f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index + 1}. A string was expected."
37
+ )
38
+ if self.regexp and not re.match(self.regexp, item):
39
+ error_prefix = self._error_prefix(instance)
40
+ raise ValueError(
41
+ f"{error_prefix} attempt to set a value of '{item}' for item #{index + 1} but this does not match the required regexp: '{self.regexp}'."
42
+ )
43
+ instance._set_config(self, value)
44
+
45
+ def __get__(self, instance, parent) -> list[str] | Callable[..., list[str]]:
46
+ if not instance:
47
+ return self # type: ignore
48
+ return instance._get_config(self)
@@ -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,20 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+
5
+ from clearskies.configs import config
6
+
7
+
8
+ class Timedelta(config.Config):
9
+ def __set__(self, instance, value: datetime.timedelta):
10
+ if not isinstance(value, datetime.timedelta):
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 parameter that requries a datetime.timedelta object."
14
+ )
15
+ instance._set_config(self, value)
16
+
17
+ def __get__(self, instance, parent) -> datetime.timedelta:
18
+ if not instance:
19
+ return self # type: ignore
20
+ return instance._get_config(self)