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
@@ -1,172 +0,0 @@
1
- import re
2
-
3
-
4
- class ConditionParser:
5
- operators = ["<=>", "!=", "<=", ">=", ">", "<", "=", "in", "is not null", "is null", "is not", "is", "like"]
6
-
7
- operator_lengths = {
8
- "<=>": 3,
9
- "<=": 2,
10
- ">=": 2,
11
- "!=": 2,
12
- ">": 1,
13
- "<": 1,
14
- "=": 1,
15
- "in": 4,
16
- "is not null": 12,
17
- "is null": 8,
18
- "is not": 8,
19
- "is": 4,
20
- "like": 6,
21
- }
22
-
23
- # some operators require spaces around them
24
- operators_for_matching = {
25
- "like": " like ",
26
- "in": " in ",
27
- "is not null": " is not null",
28
- "is null": " is null",
29
- "is": " is ",
30
- "is not": " is not ",
31
- }
32
-
33
- operators_with_simple_placeholders = {
34
- "<=>": True,
35
- "<=": True,
36
- ">=": True,
37
- "!=": True,
38
- "=": True,
39
- "<": True,
40
- ">": True,
41
- }
42
-
43
- operators_without_placeholders = {
44
- "is not null",
45
- "is null",
46
- }
47
-
48
- def parse_condition(self, condition):
49
- lowercase_condition = condition.lower()
50
- matching_operator = None
51
- matching_index = len(condition)
52
- # figure out which operator comes earliest in the string: make sure and check all so we match the
53
- # earliest operator so we don't get weird results for things like 'age=name<=5'.
54
- for operator in self.operators:
55
- try:
56
- operator_for_match = self.operators_for_matching.get(operator, operator)
57
- index = lowercase_condition.index(operator_for_match)
58
- except ValueError:
59
- continue
60
- if index < matching_index:
61
- matching_index = index
62
- matching_operator = operator
63
-
64
- if matching_operator is None:
65
- raise ValueError(f"No supported operators found in condition {condition}")
66
-
67
- column = condition[:matching_index].strip().replace("`", "")
68
- value = condition[matching_index + self.operator_lengths[matching_operator] :].strip()
69
- if value and (value[0] == "'" and value[-1] == "'"):
70
- value = value.strip("'")
71
- values = self._parse_condition_list(value) if matching_operator == "in" else [value]
72
- table = ""
73
- if "." in column:
74
- [table, column] = column.split(".")
75
- column_for_parsed = f"{table}.{column}" if table else column
76
- return {
77
- "table": table,
78
- "column": column,
79
- "operator": matching_operator.upper(),
80
- "values": [] if matching_operator in self.operators_without_placeholders else values,
81
- "parsed": self._with_placeholders(
82
- column_for_parsed, matching_operator, values, escape=False if table else True
83
- ),
84
- }
85
-
86
- def _parse_condition_list(self, value):
87
- if value[0] != "(" and value[-1] != ")":
88
- raise ValueError(f"Invalid search value {value} for condition. For IN operator use `IN (value1,value2)`")
89
-
90
- # note: this is not very smart and will mess things up if there are single quotes/commas in the data
91
- return list(map(lambda value: value.strip().strip("'"), value[1:-1].split(",")))
92
-
93
- def _with_placeholders(self, column, operator, values, escape=True, escape_character="`"):
94
- quote = escape_character if escape else ""
95
- column = column.replace("`", "")
96
- upper_case_operator = operator.upper()
97
- lower_case_operator = operator.lower()
98
- if lower_case_operator in self.operators_with_simple_placeholders:
99
- return f"{quote}{column}{quote}{upper_case_operator}%s"
100
- if lower_case_operator in self.operators_without_placeholders:
101
- return f"{quote}{column}{quote} {upper_case_operator}"
102
- if lower_case_operator == "is" or lower_case_operator == "is not" or lower_case_operator == "like":
103
- return f"{quote}{column}{quote} {upper_case_operator} %s"
104
-
105
- # the only thing left is "in" which has a variable number of placeholders
106
- return f"{quote}{column}{quote} IN (" + ", ".join(["%s" for i in range(len(values))]) + ")"
107
-
108
- def parse_join(self, join):
109
- # doing this the simple and stupid way, until that doesn't work. Yes, it is ugly.
110
- # Splitting this into two regexps for simplicity: this one does not check for an alias
111
- matches = re.match(
112
- "(\\w+\\s+)?join\\s+`?([^\\s`]+)`?\\s+on\\s+`?([^`]+)`?\\.`?([^`]+)`?\\s*=\\s*`?([^`]+)`?\\.`?([^`]+)`?",
113
- join,
114
- re.IGNORECASE,
115
- )
116
- if matches:
117
- groups = matches.groups()
118
- alias = ""
119
- join_type = groups[0]
120
- right_table = groups[1]
121
- first_table = groups[2]
122
- first_column = groups[3]
123
- second_table = groups[4]
124
- second_column = groups[5]
125
- else:
126
- matches = re.match(
127
- "(\\w+\\s+)?join\\s+`?([^\\s`]+)`?\\s+(as\\s+)?(\\S+)\\s+on\\s+`?([^`]+)`?\\.`?([^`]+)`?\\s*=\\s*`?([^`]+)`?\\.`?([^`]+)`?",
128
- join,
129
- re.IGNORECASE,
130
- )
131
- if not matches:
132
- raise ValueError(f"Specified join condition, '{join}' does not appear to be a valid join statement")
133
- groups = matches.groups()
134
- join_type = groups[0]
135
- right_table = groups[1]
136
- alias = groups[3]
137
- first_table = groups[4]
138
- first_column = groups[5]
139
- second_table = groups[6]
140
- second_column = groups[7]
141
-
142
- # which is the left table and which is the right table?
143
- match_by = alias if alias else right_table
144
- if first_table == match_by:
145
- join_data = {
146
- "left_table": second_table,
147
- "left_column": second_column,
148
- "right_table": first_table,
149
- "right_column": first_column,
150
- }
151
- elif second_table == match_by:
152
- join_data = {
153
- "left_table": first_table,
154
- "left_column": first_column,
155
- "right_table": second_table,
156
- "right_column": second_column,
157
- }
158
- else:
159
- raise ValueError(
160
- f"Specified join condition, '{join}' was not understandable because the joined table "
161
- + "is not referenced in the 'on' clause"
162
- )
163
-
164
- return {
165
- **join_data,
166
- **{
167
- "type": groups[0].strip().upper() if groups[0] else "INNER",
168
- "table": right_table,
169
- "alias": alias,
170
- "raw": join,
171
- },
172
- }
@@ -1,54 +0,0 @@
1
- from ..application import Application
2
- from ..di import StandardDependencies
3
- from ..handlers.callable import Callable
4
- from .convert_to_application import convert_to_application
5
- import sys
6
-
7
-
8
- def build_context(
9
- context_class,
10
- application,
11
- di_class=None,
12
- bindings=None,
13
- binding_classes=None,
14
- binding_modules=None,
15
- additional_configs=None,
16
- additional_kwargs=None,
17
- ):
18
- application = convert_to_application(application)
19
-
20
- if di_class is None:
21
- di_class = application.di_class if application.di_class is not None else StandardDependencies
22
-
23
- if bindings is None:
24
- bindings = {}
25
- if binding_classes is None:
26
- binding_classes = []
27
- if binding_modules is None:
28
- binding_modules = []
29
- if additional_configs is None:
30
- additional_configs = []
31
- if additional_kwargs is None:
32
- additional_kwargs = {}
33
-
34
- bindings = {
35
- **application.bindings,
36
- **bindings,
37
- }
38
- binding_classes = [
39
- *application.binding_classes,
40
- *binding_classes,
41
- ]
42
- binding_modules = [
43
- *application.binding_modules,
44
- *binding_modules,
45
- ]
46
- additional_configs = [
47
- *application.additional_configs,
48
- *additional_configs,
49
- ]
50
-
51
- di = di_class.init(*binding_classes, **bindings, modules=binding_modules, additional_configs=additional_configs)
52
- context = di.build(context_class, cache=False)
53
- context.configure(application, **additional_kwargs)
54
- return context
@@ -1,190 +0,0 @@
1
- import inspect
2
- import os
3
- import types
4
- from ..application import Application
5
- from ..handlers.callable import Callable as CallableHandler
6
- from ..handlers.simple_routing import SimpleRouting as SimpleRoutingHandler
7
- from typing import Any, Callable, Dict, Union, List
8
-
9
-
10
- def convert_to_application(
11
- application: Union[Application, Dict[str, Any], Callable, List[Callable], types.ModuleType]
12
- ) -> Application:
13
- """
14
- Converts a variety of allowed inputs into an application which the context can run.
15
-
16
- This is called by the `build_context` method which is in turn used by all of the
17
- clearskies.context.* functions. The goal is to provide flexibility to the developer.
18
- The context itself always runs an application. However, we want to give the developer
19
- additional options. This function marrys the two. This function can specifically handle:
20
-
21
- 1. A module
22
- 2. A function
23
- 3. A function with one or more clearskies.decorators.* applied to it
24
- 4. A list containing any number of the above
25
- 5. A clearskies.Application object
26
- 6. A dictionary containing two keys: 'handler_class' and 'handler_config'
27
-
28
- Note that if more than one function is provided they will all be wrapped in a SimpleRouting
29
- handler. In addition, if any of the functions don't have routing information provided
30
- by a decorator, then the the function name will be used as the route for the function
31
- and only the GET method will be allowed.
32
-
33
- Finally, if passing in a module, note that only functions with routing decorators will be
34
- imported. This stops you from accidentally taking helper functions and exposing them as endpoints.
35
- """
36
- if type(application) == types.ModuleType:
37
- return convert_module_to_application(application)
38
-
39
- # Lists need some special processing, although they just end up back here in the end
40
- if type(application) == list:
41
- return convert_list_to_application(application)
42
-
43
- # if it has the handler_class attribute, then just assume it is an application object
44
- if hasattr(application, "handler_class"):
45
- return application
46
-
47
- # check for a dictionary with the same thing (in case the developer doesn't want to bother with
48
- # an application)
49
- if hasattr(application, "__getitem__") and "handler_class" in application:
50
- if not "handler_config" in application:
51
- raise ValueError(
52
- "build_context was passed a dictionary-like object with 'handler_class', but not "
53
- + "'handler_config'. Both are required to build an application"
54
- )
55
- return Application(application["handler_class"], application["handler_config"])
56
-
57
- # if we have a wrapped application, then it's a callable with decorators and we can invoke
58
- # it to return an application
59
- if getattr(application, "is_wrapped_application", False):
60
- return application if type(application) == Application else application()
61
-
62
- # if we get a callable, then use the callable handler class
63
- if callable(application):
64
- return Application(CallableHandler, {"callable": application})
65
-
66
- raise ValueError(
67
- "A context was passed something but I'm not smart enough to figure out what it is :( In general you want to pass in an Application, a callable, or a callable with decorators from the clearskies.decorators module. You can also try a dictionary with `handler_class` and `handler_config` options. I'll link to the docs eventually."
68
- )
69
-
70
-
71
- def convert_module_to_application(module: types.ModuleType) -> Application:
72
- """
73
- Take a module, find any routing applications in it, and convert it to a single application
74
- """
75
- if not hasattr(module, "__file__") or not module.__file__:
76
- raise ValueError("I'm trying to find routed functions in a module but I was passed a python-native module")
77
- root = os.path.dirname(module.__file__)
78
- checked = {}
79
- routes = convert_list_to_application(return_routed_functions(module, root, len(root), checked))
80
- return routes
81
-
82
-
83
- def return_routed_functions(
84
- module: types.ModuleType, root: str, root_len: int, checked: [str, str]
85
- ) -> List[Application]:
86
- routed_functions = []
87
- for name, item in module.__dict__.items():
88
- if type(item) == Application:
89
- routed_functions.append(item)
90
- continue
91
- if getattr(item, "is_wrapped_application", False):
92
- routed_functions.append(item())
93
- continue
94
- if inspect.ismodule(item):
95
- if not hasattr(item, "__file__") or not item.__file__:
96
- continue
97
- child_root = os.path.dirname(item.__file__)
98
- if child_root[:root_len] != root:
99
- continue
100
- if item.__file__ in checked:
101
- continue
102
- checked[item.__file__] = True
103
- routed_functions.extend(return_routed_functions(item, root, root_len, checked))
104
- return routed_functions
105
-
106
-
107
- def convert_list_to_application(applications: List[Union[Application, Callable]]) -> Application:
108
- """
109
- Take a list of applications and convert it into a single application.
110
- """
111
- # First check the items. Only callables or SimpleRouting handlers are allowed. Everything else is too complicated.
112
- for index, application in enumerate(applications):
113
- if not callable(application) and application.handler_class not in [CallableHandler, SimpleRoutingHandler]:
114
- raise ValueError(
115
- f"A context was provided with a list of applications, but in this case all the applications must be callables or wrapped in a SimpleRouting function. Item #{index+1} was not either of these things."
116
- )
117
-
118
- # so this is... less than ideal. In their current iteration, the simple router ignores any DI configuration
119
- # on inner applications (bindings, imported modules, imported classes, etc...). To some extent this is
120
- # a short coming of how DI is handled, since all the applications share a single DI container. If needed,
121
- # we can adjust things in the future so that each application may be called with its own DI container.
122
- # I'm avoiding that at the moment because it's not clear if it is necessary, and it will impact performance.
123
- # In the meantime, we need to collect DI configuration from our inner applications and combine those
124
- # in the outer one.
125
- di_config = {}
126
- routes = []
127
- # also, our top level router will need something for authentication. It's a requirement. It doesn't
128
- # actually matter exactly _what_ it is because it won't overwrite authentication for the children.
129
- authentication = None
130
- for application in applications:
131
- converted = convert_to_application(application)
132
- if not authentication and converted.handler_config.get("authentication"):
133
- authentication = converted.handler_config.get("authentication")
134
- routes.append(ensure_routing(converted))
135
- for di_key in ["bindings", "binding_classes", "binding_modules", "additional_configs"]:
136
- di_value = getattr(application, di_key, None)
137
- if di_value:
138
- if di_key == "bindings":
139
- di_config["bindings"] = {**di_config.get("bindings", {}), **di_value}
140
- else:
141
- di_config[di_key] = [*di_config.get(di_key, []), *di_value]
142
-
143
- if not authentication:
144
- raise ValueError(
145
- "I couldn't find any authentication rules while auto-importing your routes. Make sure an add an authentication decorator to your routes, even if it's just @clearskies.decorators.public"
146
- )
147
-
148
- return Application(
149
- SimpleRoutingHandler,
150
- {
151
- "authentication": authentication,
152
- "routes": [ensure_routing(convert_to_application(application)) for application in applications],
153
- },
154
- **di_config,
155
- )
156
-
157
-
158
- def ensure_routing(application: Application) -> Application:
159
- """
160
- Add routing to an application by wrapping it in a SimpleRouting handler if it isn't already.
161
- """
162
- if application.handler_class == SimpleRoutingHandler:
163
- return application
164
-
165
- # We're making assumptions which is bad, but at this point in time the `application` should literally be
166
- # an application and it should either be a SimpleRoutingHnadler (which we took care of above) or it
167
- # should be a callable application. To add routing we wrap it in a SimpleRouting handler and use the callable
168
- # name as the route. Still, let's just check even if it isn't the most meanginful error message.
169
- if not application.handler_config.get("callable"):
170
- raise ValueError(
171
- "Huh, I should have an application with a callable handler class but it doesn't have a callable so I don't know what went wrong :("
172
- )
173
- name = application.handler_config["callable"].__name__
174
- if name == "<lambda>":
175
- raise ValueError(
176
- "A lambda was sent to the application for auto-routing, but since lambdas don't have names, I can't create the route for it. To fix this, switch it out for a regular function, attach a decorator with a path, or manually wrap it in a SimpleRouting handler"
177
- )
178
- return Application(
179
- SimpleRoutingHandler,
180
- {
181
- "authentication": application.handler_config.get("authentication", None),
182
- "routes": [
183
- {
184
- "path": name,
185
- "handler_class": application.handler_class,
186
- "handler_config": application.handler_config,
187
- }
188
- ],
189
- },
190
- )
@@ -1,37 +0,0 @@
1
- def extract_handler(application):
2
- """
3
- This accepts the application passed in to the context and returns the handler
4
-
5
- Most importantly, it doesn't technically have to be an application: if passed a simple function
6
- it will assume you mean to use the callable handler, and if passed a dictionary with 'handler_class'
7
- and 'handler_config' keys, it will build a handler from that.
8
- """
9
- # applications will have a handler_class property
10
- if hasattr(application, "handler_class"):
11
- handler = self.di.build(application.handler_class, cache=False)
12
- handler.configure(self.finalize_handler_config(application.handler_config))
13
- return handler
14
-
15
- # check for a dictionary with the same thing (in case the developer doesn't want to bother with
16
- # an application
17
- if hasattr(application, "__getitem__") and "handler_class" in application:
18
- if not "handler_config" in application:
19
- raise ValueError(
20
- "context was passed a dictionary-like object with 'handler_class', but not "
21
- + "'handler_config'. Both are required to execute the handler"
22
- )
23
- handler = self.di.build(application["handler_class"], cache=False)
24
- handler.configure(self.finalize_handler_config(application["handler_config"]))
25
- return handler
26
-
27
- # if we get a callable, then use the callable handler class
28
- if callable(application):
29
- handler = self.di.build(callable_module.Callable, cache=False)
30
- handler.configure(self.finalize_handler_config({"callable": application}))
31
- return handler
32
-
33
- raise ValueError(
34
- "The context received an object it did not know how to handle! You should pass in either an instance "
35
- + "of clearskies.Application, a dictionary with 'handler_class' and 'handler_config' keys, or a "
36
- + "function/lambda to be executed"
37
- )
@@ -1,94 +0,0 @@
1
- from ..authentication import public
2
- from ..mocks import InputOutput
3
- from ..backends import MemoryBackend
4
- from datetime import datetime, timezone
5
- from .build_context import build_context
6
- from .context import Context
7
- from .convert_to_application import convert_to_application
8
-
9
-
10
- class Test(Context):
11
- application = None
12
- input_output = None
13
- memory_backend = None
14
- now = None
15
-
16
- def __init__(self, di):
17
- super().__init__(di)
18
-
19
- def configure(self, application, cursor_backend_to_memory_backend=True):
20
- # so for the other contexts, the application is just a way to manage configuration,
21
- # and so gets promptly thrown away. We actually want it though
22
- self.application = convert_to_application(application)
23
- self.memory_backend = self.di.build(MemoryBackend, cache=False)
24
- self.memory_backend.silent_on_missing_tables(silent=True)
25
-
26
- if "now" not in self.di._prepared:
27
- self.now = datetime.now().replace(tzinfo=timezone.utc, microsecond=0)
28
- self.di.bind("now", self.now)
29
- if cursor_backend_to_memory_backend:
30
- self.di.bind("cursor_backend", self.memory_backend)
31
-
32
- def __call__(
33
- self,
34
- method=None,
35
- body=None,
36
- headers=None,
37
- url=None,
38
- routing_data=None,
39
- input_output=None,
40
- query_parameters=None,
41
- authorization_data=None,
42
- context_specifics=None,
43
- ):
44
- if self.application is None:
45
- raise ValueError("Cannot call the test context without an application")
46
-
47
- if input_output is None:
48
- input_output = InputOutput()
49
- if body is not None:
50
- input_output.set_body(body)
51
- if headers is not None:
52
- input_output.set_request_headers(headers)
53
- if method is not None:
54
- input_output.set_request_method(method)
55
- if url is not None:
56
- input_output.set_request_url(url)
57
- if routing_data is not None:
58
- input_output.set_routing_data(routing_data)
59
- if query_parameters is not None:
60
- input_output.set_query_parameters(query_parameters)
61
- if authorization_data is not None:
62
- input_output.set_authorization_data(authorization_data)
63
- if context_specifics is not None:
64
- input_output.set_context_specifics(context_specifics)
65
-
66
- self.handler = self.di.build(self.application.handler_class, cache=False)
67
- self.handler.configure(
68
- {
69
- **{"authentication": public()},
70
- **self.application.handler_config,
71
- }
72
- )
73
- return self.handler(input_output)
74
-
75
-
76
- def test(
77
- application,
78
- di_class=None,
79
- bindings=None,
80
- binding_classes=None,
81
- binding_modules=None,
82
- additional_configs=None,
83
- cursor_backend_to_memory_backend=True,
84
- ):
85
- return build_context(
86
- Test,
87
- application,
88
- di_class=di_class,
89
- bindings=bindings,
90
- binding_classes=binding_classes,
91
- binding_modules=binding_modules,
92
- additional_configs=additional_configs,
93
- additional_kwargs={"cursor_backend_to_memory_backend": cursor_backend_to_memory_backend},
94
- )
@@ -1,39 +0,0 @@
1
- from .auth0_jwks import auth0_jwks
2
- from .authorization import authorization
3
- from .bindings import bindings
4
- from .binding_classes import binding_classes
5
- from .binding_modules import binding_modules
6
- from .create import create
7
- from .delete import delete
8
- from .docs import docs
9
- from .get import get
10
- from .jwks import jwks
11
- from .patch import patch
12
- from .post import post
13
- from .public import public
14
- from .response_headers import response_headers
15
- from .return_raw_response import return_raw_response
16
- from .schema import schema
17
- from .secret_bearer import secret_bearer
18
- from .security_headers import security_headers
19
-
20
- __all__ = [
21
- "auth0_jwks",
22
- "authorization",
23
- "bindings",
24
- "binding_classes",
25
- "binding_modules",
26
- "create",
27
- "delete",
28
- "docs",
29
- "get",
30
- "jwks",
31
- "patch",
32
- "post",
33
- "public",
34
- "response_headers",
35
- "return_raw_response",
36
- "schema",
37
- "secret_bearer",
38
- "security_headers",
39
- ]
@@ -1,22 +0,0 @@
1
- from .merge import merge
2
- from typing import List, Optional
3
- from ..authentication import auth0_jwks as auth0_jwks_binding
4
-
5
-
6
- def auth0_jwks(
7
- auth0_domain: str,
8
- audience: str,
9
- algorithms: Optional[List[str]] = None,
10
- documentation_security_name: Optional[str] = None,
11
- ):
12
- def wrap_in_application(function):
13
- auth0 = auth0_jwks_binding(
14
- auth0_domain=auth0_domain,
15
- audience=audience,
16
- algorithms=algorithms,
17
- documentation_security_name=documentation_security_name,
18
- )
19
- return merge(function, authentication=auth0)
20
-
21
- wrap_in_application.is_wrapped_application = True
22
- return wrap_in_application
@@ -1,10 +0,0 @@
1
- from .merge import merge
2
- from typing import List, Optional
3
-
4
-
5
- def authorization(auth_rules):
6
- def wrap_in_application(function):
7
- return merge(function, authorization=auth_rules)
8
-
9
- wrap_in_application.is_wrapped_application = True
10
- return wrap_in_application
@@ -1,9 +0,0 @@
1
- from .merge import merge
2
-
3
-
4
- def binding_classes(*args):
5
- def wrap_in_application(function):
6
- return merge(function, binding_classes=args)
7
-
8
- wrap_in_application.is_wrapped_application = True
9
- return wrap_in_application
@@ -1,9 +0,0 @@
1
- from .merge import merge
2
-
3
-
4
- def binding_modules(*args):
5
- def wrap_in_application(function):
6
- return merge(function, binding_modules=args)
7
-
8
- wrap_in_application.is_wrapped_application = True
9
- return wrap_in_application
@@ -1,9 +0,0 @@
1
- from .merge import merge
2
-
3
-
4
- def bindings(**kwargs):
5
- def wrap_in_application(function):
6
- return merge(function, bindings=kwargs)
7
-
8
- wrap_in_application.is_wrapped_application = True
9
- return wrap_in_application
@@ -1,10 +0,0 @@
1
- from .merge import merge
2
-
3
-
4
- def create(path, **kwargs):
5
- def wrap_in_application(function):
6
- kwargs["path"] = path
7
- return merge(function, **{**kwargs, "methods": "CREATE"})
8
-
9
- wrap_in_application.is_wrapped_application = True
10
- return wrap_in_application
@@ -1,10 +0,0 @@
1
- from .merge import merge
2
-
3
-
4
- def delete(path, **kwargs):
5
- def wrap_in_application(function):
6
- kwargs["path"] = path
7
- return merge(function, **{**kwargs, "methods": "DELETE"})
8
-
9
- wrap_in_application.is_wrapped_application = True
10
- return wrap_in_application