clear-skies 2.0.14__tar.gz → 2.0.16__tar.gz

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 (278) hide show
  1. {clear_skies-2.0.14 → clear_skies-2.0.16}/CHANGELOG.md +24 -0
  2. clear_skies-2.0.16/LATEST_CHANGELOG.md +11 -0
  3. {clear_skies-2.0.14 → clear_skies-2.0.16}/PKG-INFO +1 -1
  4. {clear_skies-2.0.14 → clear_skies-2.0.16}/pyproject.toml +1 -1
  5. {clear_skies-2.0.14 → clear_skies-2.0.16}/ruff.toml +1 -1
  6. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/authentication/jwks.py +1 -1
  7. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/request.py +1 -1
  8. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/backends/api_backend.py +31 -19
  9. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/backends/secrets_backend.py +0 -1
  10. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/belongs_to_model.py +1 -1
  11. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/updated.py +0 -1
  12. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/__init__.py +0 -2
  13. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/email_list_or_callable.py +0 -2
  14. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/headers.py +1 -3
  15. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/contexts/context.py +1 -2
  16. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/contexts/wsgi.py +0 -2
  17. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/contexts/wsgi_ref.py +0 -1
  18. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/di.py +0 -2
  19. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/input_output.py +0 -2
  20. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/test_module/__init__.py +3 -0
  21. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/test_module/module_class.py +0 -3
  22. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoint.py +1 -1
  23. clear_skies-2.0.16/src/clearskies/functional/json.py +47 -0
  24. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/exceptions/__init__.py +5 -0
  25. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/input_output.py +0 -1
  26. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/model.py +0 -1
  27. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/akeyless.py +4 -26
  28. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/__init__.py +1 -1
  29. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/maximum_value.py +0 -1
  30. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/minimum_length.py +0 -1
  31. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/minimum_value.py +0 -1
  32. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/unique.py +0 -1
  33. {clear_skies-2.0.14 → clear_skies-2.0.16}/uv.lock +1 -1
  34. clear_skies-2.0.14/LATEST_CHANGELOG.md +0 -5
  35. {clear_skies-2.0.14 → clear_skies-2.0.16}/.editorconfig +0 -0
  36. {clear_skies-2.0.14 → clear_skies-2.0.16}/.github/workflows/create-version.yaml +0 -0
  37. {clear_skies-2.0.14 → clear_skies-2.0.16}/.github/workflows/docs.yaml +0 -0
  38. {clear_skies-2.0.14 → clear_skies-2.0.16}/.github/workflows/run-tests.yml +0 -0
  39. {clear_skies-2.0.14 → clear_skies-2.0.16}/.github/workflows/tests-matrix.yaml +0 -0
  40. {clear_skies-2.0.14 → clear_skies-2.0.16}/.github/workflows/tests.yaml +0 -0
  41. {clear_skies-2.0.14 → clear_skies-2.0.16}/.gitignore +0 -0
  42. {clear_skies-2.0.14 → clear_skies-2.0.16}/.pre-commit-config.yaml +0 -0
  43. {clear_skies-2.0.14 → clear_skies-2.0.16}/.python-version +0 -0
  44. {clear_skies-2.0.14 → clear_skies-2.0.16}/.vscode/extensions.json +0 -0
  45. {clear_skies-2.0.14 → clear_skies-2.0.16}/.vscode/settings.json +0 -0
  46. {clear_skies-2.0.14 → clear_skies-2.0.16}/LICENSE +0 -0
  47. {clear_skies-2.0.14 → clear_skies-2.0.16}/README.md +0 -0
  48. {clear_skies-2.0.14 → clear_skies-2.0.16}/build.sh +0 -0
  49. {clear_skies-2.0.14 → clear_skies-2.0.16}/cliff.toml +0 -0
  50. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/__init__.py +0 -0
  51. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/action.py +0 -0
  52. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/authentication/__init__.py +0 -0
  53. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/authentication/authentication.py +0 -0
  54. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/authentication/authorization.py +0 -0
  55. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/authentication/authorization_pass_through.py +0 -0
  56. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/authentication/public.py +0 -0
  57. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/authentication/secret_bearer.py +0 -0
  58. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/__init__.py +0 -0
  59. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/__init__.py +0 -0
  60. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/__init__.py +0 -0
  61. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/oai3_json.py +0 -0
  62. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/oai3_schema_resolver.py +0 -0
  63. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/parameter.py +0 -0
  64. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/response.py +0 -0
  65. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/schema/__init__.py +0 -0
  66. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/schema/array.py +0 -0
  67. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/schema/default.py +0 -0
  68. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/schema/enum.py +0 -0
  69. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/schema/object.py +0 -0
  70. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/formats/oai3_json/test.json +0 -0
  71. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/py.typed +0 -0
  72. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/request/__init__.py +0 -0
  73. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/request/header.py +0 -0
  74. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/request/json_body.py +0 -0
  75. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/request/parameter.py +0 -0
  76. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/request/request.py +0 -0
  77. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/request/url_parameter.py +0 -0
  78. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/request/url_path.py +0 -0
  79. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/response/__init__.py +0 -0
  80. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/response/response.py +0 -0
  81. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/__init__.py +0 -0
  82. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/array.py +0 -0
  83. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/base64.py +0 -0
  84. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/boolean.py +0 -0
  85. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/date.py +0 -0
  86. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/datetime.py +0 -0
  87. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/double.py +0 -0
  88. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/enum.py +0 -0
  89. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/integer.py +0 -0
  90. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/long.py +0 -0
  91. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/number.py +0 -0
  92. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/object.py +0 -0
  93. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/password.py +0 -0
  94. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/schema.py +0 -0
  95. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/autodoc/schema/string.py +0 -0
  96. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/backends/__init__.py +0 -0
  97. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/backends/backend.py +0 -0
  98. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/backends/cursor_backend.py +0 -0
  99. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/backends/memory_backend.py +0 -0
  100. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/column.py +0 -0
  101. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/__init__.py +0 -0
  102. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/audit.py +0 -0
  103. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/belongs_to_id.py +0 -0
  104. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/belongs_to_self.py +0 -0
  105. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/boolean.py +0 -0
  106. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/category_tree.py +0 -0
  107. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/category_tree_ancestors.py +0 -0
  108. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/category_tree_children.py +0 -0
  109. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/category_tree_descendants.py +0 -0
  110. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/created.py +0 -0
  111. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/created_by_authorization_data.py +0 -0
  112. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/created_by_header.py +0 -0
  113. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/created_by_ip.py +0 -0
  114. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/created_by_routing_data.py +0 -0
  115. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/created_by_user_agent.py +0 -0
  116. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/date.py +0 -0
  117. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/datetime.py +0 -0
  118. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/email.py +0 -0
  119. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/float.py +0 -0
  120. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/has_many.py +0 -0
  121. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/has_many_self.py +0 -0
  122. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/has_one.py +0 -0
  123. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/integer.py +0 -0
  124. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/json.py +0 -0
  125. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/many_to_many_ids.py +0 -0
  126. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/many_to_many_ids_with_data.py +0 -0
  127. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/many_to_many_models.py +0 -0
  128. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/many_to_many_pivots.py +0 -0
  129. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/phone.py +0 -0
  130. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/select.py +0 -0
  131. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/string.py +0 -0
  132. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/timestamp.py +0 -0
  133. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/columns/uuid.py +0 -0
  134. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/README.md +0 -0
  135. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/actions.py +0 -0
  136. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/any.py +0 -0
  137. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/any_dict.py +0 -0
  138. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/any_dict_or_callable.py +0 -0
  139. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/authentication.py +0 -0
  140. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/authorization.py +0 -0
  141. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/boolean.py +0 -0
  142. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/boolean_or_callable.py +0 -0
  143. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/callable_config.py +0 -0
  144. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/columns.py +0 -0
  145. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/conditions.py +0 -0
  146. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/config.py +0 -0
  147. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/datetime.py +0 -0
  148. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/datetime_or_callable.py +0 -0
  149. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/email.py +0 -0
  150. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/email_list.py +0 -0
  151. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/email_or_email_list_or_callable.py +0 -0
  152. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/endpoint.py +0 -0
  153. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/endpoint_list.py +0 -0
  154. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/float.py +0 -0
  155. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/float_or_callable.py +0 -0
  156. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/integer.py +0 -0
  157. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/integer_or_callable.py +0 -0
  158. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/joins.py +0 -0
  159. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/list_any_dict.py +0 -0
  160. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/list_any_dict_or_callable.py +0 -0
  161. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/model_class.py +0 -0
  162. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/model_column.py +0 -0
  163. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/model_columns.py +0 -0
  164. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/model_destination_name.py +0 -0
  165. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/model_to_id_column.py +0 -0
  166. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/readable_model_column.py +0 -0
  167. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/readable_model_columns.py +0 -0
  168. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/schema.py +0 -0
  169. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/searchable_model_columns.py +0 -0
  170. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/security_headers.py +0 -0
  171. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/select.py +0 -0
  172. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/select_list.py +0 -0
  173. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/string.py +0 -0
  174. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/string_dict.py +0 -0
  175. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/string_list.py +0 -0
  176. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/string_list_or_callable.py +0 -0
  177. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/string_or_callable.py +0 -0
  178. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/timedelta.py +0 -0
  179. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/timezone.py +0 -0
  180. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/url.py +0 -0
  181. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/validators.py +0 -0
  182. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/writeable_model_column.py +0 -0
  183. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configs/writeable_model_columns.py +0 -0
  184. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/configurable.py +0 -0
  185. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/contexts/__init__.py +0 -0
  186. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/contexts/cli.py +0 -0
  187. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/decorators.py +0 -0
  188. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/decorators.pyi +0 -0
  189. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/__init__.py +0 -0
  190. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/additional_config.py +0 -0
  191. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/additional_config_auto_import.py +0 -0
  192. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/__init__.py +0 -0
  193. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/akeyless_sdk.py +0 -0
  194. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/by_class.py +0 -0
  195. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/by_name.py +0 -0
  196. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/di.py +0 -0
  197. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/environment.py +0 -0
  198. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/now.py +0 -0
  199. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/requests.py +0 -0
  200. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/secrets.py +0 -0
  201. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/utcnow.py +0 -0
  202. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/inject/uuid.py +0 -0
  203. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/injectable.py +0 -0
  204. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/injectable_properties.py +0 -0
  205. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/di/test_module/another_module/__init__.py +0 -0
  206. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/end.py +0 -0
  207. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoint_group.py +0 -0
  208. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/__init__.py +0 -0
  209. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/advanced_search.py +0 -0
  210. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/callable.py +0 -0
  211. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/create.py +0 -0
  212. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/delete.py +0 -0
  213. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/get.py +0 -0
  214. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/health_check.py +0 -0
  215. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/list.py +0 -0
  216. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/restful_api.py +0 -0
  217. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/schema.py +0 -0
  218. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/simple_search.py +0 -0
  219. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/endpoints/update.py +0 -0
  220. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/environment.py +0 -0
  221. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/__init__.py +0 -0
  222. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/authentication.py +0 -0
  223. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/authorization.py +0 -0
  224. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/client_error.py +0 -0
  225. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/input_errors.py +0 -0
  226. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/missing_dependency.py +0 -0
  227. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/moved_permanently.py +0 -0
  228. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/moved_temporarily.py +0 -0
  229. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/exceptions/not_found.py +0 -0
  230. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/functional/__init__.py +0 -0
  231. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/functional/routing.py +0 -0
  232. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/functional/string.py +0 -0
  233. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/functional/validations.py +0 -0
  234. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/__init__.py +0 -0
  235. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/cli.py +0 -0
  236. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/exceptions/cli_input_error.py +0 -0
  237. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/exceptions/cli_not_found.py +0 -0
  238. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/headers.py +0 -0
  239. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/programmatic.py +0 -0
  240. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/py.typed +0 -0
  241. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/input_outputs/wsgi.py +1 -1
  242. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/py.typed +0 -0
  243. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/query/__init__.py +0 -0
  244. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/query/condition.py +0 -0
  245. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/query/join.py +0 -0
  246. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/query/query.py +0 -0
  247. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/query/sort.py +0 -0
  248. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/schema.py +0 -0
  249. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/__init__.py +0 -0
  250. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/additional_configs/__init__.py +0 -0
  251. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +0 -0
  252. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +0 -0
  253. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/exceptions/__init__.py +0 -0
  254. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/exceptions/not_found_error.py +0 -0
  255. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/exceptions/permissions_error.py +0 -0
  256. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/secrets/secrets.py +0 -0
  257. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/security_header.py +0 -0
  258. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/security_headers/__init__.py +0 -0
  259. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/security_headers/cache_control.py +0 -0
  260. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/security_headers/cors.py +0 -0
  261. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/security_headers/csp.py +0 -0
  262. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/security_headers/hsts.py +0 -0
  263. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/security_headers/x_content_type_options.py +0 -0
  264. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/security_headers/x_frame_options.py +0 -0
  265. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/test_base.py +0 -0
  266. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/typing.py +0 -0
  267. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validator.py +0 -0
  268. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/after_column.py +0 -0
  269. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/before_column.py +0 -0
  270. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/in_the_future.py +0 -0
  271. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/in_the_future_at_least.py +0 -0
  272. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/in_the_future_at_most.py +0 -0
  273. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/in_the_past.py +0 -0
  274. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/in_the_past_at_least.py +0 -0
  275. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/in_the_past_at_most.py +0 -0
  276. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/maximum_length.py +0 -0
  277. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/required.py +0 -0
  278. {clear_skies-2.0.14 → clear_skies-2.0.16}/src/clearskies/validators/timedelta.py +0 -0
@@ -5,11 +5,33 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0.16] - 2025-10-16
9
+
10
+ ### Added
11
+ - Add json attribute to api backend by @cmancone in [#30](https://github.com/clearskies-py/clearskies/pull/30)
12
+ - Add json attribute to api backend
13
+
14
+ ### Fixed
15
+ - Reorder map_records_response
16
+ - Reorder mapping steps
17
+ - Use empty instead of empty_model
18
+
19
+ ## [2.0.15] - 2025-10-16
20
+
21
+ ### Changed
22
+ - Bump version to v2.0.15 by @github-actions[bot]
23
+
24
+ ### Removed
25
+ - Remove unused imports by @tnijboer in [#29](https://github.com/clearskies-py/clearskies/pull/29)
26
+
8
27
  ## [2.0.14] - 2025-10-16
9
28
 
10
29
  ### Added
11
30
  - Add auto guess for akeyless secrets by @tnijboer in [#27](https://github.com/clearskies-py/clearskies/pull/27)
12
31
 
32
+ ### Changed
33
+ - Bump version to v2.0.14 by @github-actions[bot]
34
+
13
35
  ## [2.0.13] - 2025-10-10
14
36
 
15
37
  ### Changed
@@ -830,6 +852,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
830
852
  * @ made their first contribution
831
853
  * @tnijboer made their first contribution
832
854
  * @conormancone-cimpress made their first contribution
855
+ [2.0.16]: https://github.com/clearskies-py/clearskies/compare/v2.0.15..v2.0.16
856
+ [2.0.15]: https://github.com/clearskies-py/clearskies/compare/v2.0.14..v2.0.15
833
857
  [2.0.14]: https://github.com/clearskies-py/clearskies/compare/v2.0.13..v2.0.14
834
858
  [2.0.13]: https://github.com/clearskies-py/clearskies/compare/v2.0.12..v2.0.13
835
859
  [2.0.12]: https://github.com/clearskies-py/clearskies/compare/v2.0.11..v2.0.12
@@ -0,0 +1,11 @@
1
+ ## [2.0.16] - 2025-10-16
2
+
3
+ ### Added
4
+ - Add json attribute to api backend by @cmancone in [#30](https://github.com/clearskies-py/clearskies/pull/30)
5
+ - Add json attribute to api backend
6
+
7
+ ### Fixed
8
+ - Reorder map_records_response
9
+ - Reorder mapping steps
10
+ - Use empty instead of empty_model
11
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clear-skies
3
- Version: 2.0.14
3
+ Version: 2.0.16
4
4
  Summary: A framework for building backends in the cloud
5
5
  Project-URL: Documentation, https://clearskies.io/
6
6
  Project-URL: Repository, https://github.com/clearskies-py/clearskies
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "clear-skies"
3
3
  description = "A framework for building backends in the cloud"
4
- version = "2.0.14"
4
+ version = "2.0.16"
5
5
  license = "MIT"
6
6
  readme = "./README.md"
7
7
  authors = [{name = "Conor Mancone", email = "cmancone@gmail.com"}]
@@ -14,7 +14,7 @@ line-ending = "lf"
14
14
 
15
15
  [lint]
16
16
  # D - Enable all `pydocstyle` rules, limiting to those that adhere to the Pep257 convention enabled via `convention = "pep257"` below
17
- select = ["I", "D"]
17
+ select = ["I", "D", "F401"]
18
18
  ignore = [
19
19
  "D100", # pydocstyle: Missing docstring in public module.
20
20
  "D101", # pydocstyle: Missing docstring in public class.
@@ -106,7 +106,7 @@ class Jwks(Authentication, di.InjectableProperties):
106
106
 
107
107
  def validate_jwt(self, raw_jwt):
108
108
  try:
109
- from jwcrypto import jwk, jws, jwt # type: ignore
109
+ from jwcrypto import jwk, jwt # type: ignore
110
110
  from jwcrypto.common import JWException # type: ignore
111
111
  except:
112
112
  raise ValueError(
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from ...schema import Array, Object
3
+ from ...schema import Object
4
4
  from .parameter import Parameter
5
5
  from .response import Response
6
6
 
@@ -11,6 +11,7 @@ from clearskies.autodoc.schema import Schema as AutoDocSchema
11
11
  from clearskies.autodoc.schema import String as AutoDocString
12
12
  from clearskies.backends.backend import Backend
13
13
  from clearskies.di import InjectableProperties, inject
14
+ from clearskies.functional import json as json_functional
14
15
  from clearskies.functional import routing, string
15
16
 
16
17
  if TYPE_CHECKING:
@@ -550,7 +551,7 @@ class ApiBackend(configurable.Configurable, Backend, InjectableProperties):
550
551
  }
551
552
  ```
552
553
  """
553
- api_to_model_map = configs.StringDict(default={})
554
+ api_to_model_map = configs.AnyDict(default={})
554
555
 
555
556
  """
556
557
  The name of the pagination parameter
@@ -589,7 +590,7 @@ class ApiBackend(configurable.Configurable, Backend, InjectableProperties):
589
590
  authentication: Authentication | None = None,
590
591
  model_casing: str = "snake_case",
591
592
  api_casing: str = "snake_case",
592
- api_to_model_map: dict[str, str] = {},
593
+ api_to_model_map: dict[str, str | list[str]] = {},
593
594
  pagination_parameter_name: str = "start",
594
595
  pagination_parameter_type: str = "str",
595
596
  limit_parameter_name: str = "limit",
@@ -897,16 +898,16 @@ class ApiBackend(configurable.Configurable, Backend, InjectableProperties):
897
898
  f"The response from a records request returned a variable of type {response_data.__class__.__name__}, which is just confusing. To do automatic introspection, I need a list or a dictionary. I'm afraid you'll have to extend the API backend and override the map_record_response method to deal with this."
898
899
  )
899
900
 
900
- for key, value in response_data.items():
901
- if not isinstance(value, list):
902
- continue
903
- return self.map_records_response(value, query, query_data)
904
-
905
901
  # a records request may only return a single record, so before we fail, let's check for that
906
902
  record = self.check_dict_and_map_to_model(response_data, columns, query_data)
907
903
  if record is not None:
908
904
  return [record]
909
905
 
906
+ for key, value in response_data.items():
907
+ if not isinstance(value, list):
908
+ continue
909
+ return self.map_records_response(value, query, query_data)
910
+
910
911
  raise ValueError(
911
912
  "The response from a records request returned a dictionary, but none of the items in the dictionary was a list, so I don't know where to find the records. I only ever check one level deep in dictionaries. I'm afraid you'll have to extend the API backend and override the map_records_response method to deal with this."
912
913
  )
@@ -958,27 +959,38 @@ class ApiBackend(configurable.Configurable, Backend, InjectableProperties):
958
959
  map_keys = set(response_to_model_map.keys())
959
960
  matching = response_keys.intersection(map_keys)
960
961
 
961
- # if nothing matches then clearly this isn't what we're looking for: repeat on all the children
962
- if not matching:
963
- for key, value in response_data.items():
964
- if not isinstance(value, dict):
965
- continue
966
- mapped = self.check_dict_and_map_to_model(value, columns)
967
- if mapped:
968
- return {**query_data, **mapped}
969
-
970
- # no match anywhere :(
971
- return None
972
-
973
962
  # we may need to be smarter about whether or not we think we found a match, but for now let's
974
963
  # ignore that possibility. If any columns match between the keys in our response dictionary and
975
964
  # the keys that we are expecting to find data in, then just assume that we have found a record.
976
965
  mapped = {response_to_model_map[key]: response_data[key] for key in matching}
977
966
 
967
+ for api_key, column_name in self.api_to_model_map.items():
968
+ if not "." in api_key:
969
+ continue
970
+ value = json_functional.get_nested_attribute(response_data, api_key)
971
+ if value is None:
972
+ continue
973
+ if isinstance(column_name, list):
974
+ for column in column_name:
975
+ mapped[column] = value
976
+ else:
977
+ mapped[column_name] = value
978
978
  # finally, move over anything not mentioned in the map
979
979
  for key in response_keys.difference(map_keys):
980
980
  mapped[string.swap_casing(key, self.api_casing, self.model_casing)] = response_data[key]
981
981
 
982
+ # if nothing matches then clearly this isn't what we're looking for: repeat on all the children
983
+ if not mapped:
984
+ for key, value in response_data.items():
985
+ if not isinstance(value, dict):
986
+ continue
987
+ remapped = self.check_dict_and_map_to_model(value, columns)
988
+ if remapped:
989
+ return {**query_data, **remapped}
990
+
991
+ # no match anywhere :(
992
+ return None
993
+
982
994
  return {**query_data, **mapped}
983
995
 
984
996
  def build_response_to_model_map(self, columns: dict[str, Column]) -> dict[str, str]:
@@ -7,7 +7,6 @@ from clearskies.query import Condition, Query
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from clearskies import Model
10
- from clearskies.authentication import Authentication
11
10
 
12
11
 
13
12
  class SecretsBackend(Backend):
@@ -65,7 +65,7 @@ class BelongsToModel(Column):
65
65
  parent_class = belongs_to_column.parent_model_class
66
66
  parent_model = self.di.build(parent_class, cache=False)
67
67
  if not parent_id:
68
- return parent_model.empty_model()
68
+ return parent_model.empty()
69
69
 
70
70
  parent_id_column_name = parent_model.id_column_name
71
71
  join_alias = belongs_to_column.join_table_alias()
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime
4
3
  from typing import TYPE_CHECKING, Any
5
4
 
6
5
  from clearskies import configs, decorators
@@ -63,8 +63,6 @@ class MyConfigurableClass(configs.Configurable):
63
63
 
64
64
  """
65
65
 
66
- import inspect
67
-
68
66
  from .actions import Actions
69
67
  from .any import Any
70
68
  from .any_dict import AnyDict
@@ -1,7 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Callable
4
-
5
3
  from clearskies.configs import string_list_or_callable
6
4
 
7
5
 
@@ -9,9 +9,7 @@ if TYPE_CHECKING:
9
9
 
10
10
 
11
11
  class Headers(config.Config):
12
- """
13
- This is for a configuration that should be an instance of type clearskies.input_outputs.Headers.
14
- """
12
+ """This is for a configuration that should be an instance of type clearskies.input_outputs.Headers."""
15
13
 
16
14
  def __set__(self, instance, value: headers.Headers):
17
15
  if value is None:
@@ -7,8 +7,7 @@ from typing import TYPE_CHECKING, Any, Callable
7
7
  from clearskies import exceptions
8
8
  from clearskies.di import Di
9
9
  from clearskies.di.additional_config import AdditionalConfig
10
- from clearskies.input_outputs import InputOutput
11
- from clearskies.input_outputs import Programmatic
10
+ from clearskies.input_outputs import InputOutput, Programmatic
12
11
 
13
12
  if TYPE_CHECKING:
14
13
  from clearskies.endpoint import Endpoint
@@ -1,7 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
4
-
5
3
  from clearskies.contexts.context import Context
6
4
  from clearskies.input_outputs import Wsgi as WsgiInputOutput
7
5
 
@@ -4,7 +4,6 @@ import datetime
4
4
  from types import ModuleType
5
5
  from typing import TYPE_CHECKING, Any, Callable
6
6
  from wsgiref.simple_server import make_server
7
- from wsgiref.util import setup_testing_defaults
8
7
 
9
8
  from clearskies.contexts.context import Context
10
9
  from clearskies.input_outputs import Wsgi as WsgiInputOutput
@@ -1,8 +1,6 @@
1
1
  import datetime
2
2
  import inspect
3
3
  import os
4
- import re
5
- import sys
6
4
  from types import ModuleType
7
5
  from typing import Any, Callable
8
6
 
@@ -2,8 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- import requests
6
-
7
5
  from clearskies.di.injectable import Injectable
8
6
 
9
7
  if TYPE_CHECKING:
@@ -4,3 +4,6 @@ from .module_class import ModuleClass
4
4
 
5
5
  class Hi:
6
6
  pass
7
+
8
+
9
+ __all__ = ["another_module", "ModuleClass", "Hi"]
@@ -1,5 +1,2 @@
1
- import datetime
2
-
3
-
4
1
  class ModuleClass:
5
2
  pass
@@ -10,7 +10,7 @@ from clearskies.authentication import Authentication, Authorization, Public
10
10
  from clearskies.autodoc import schema
11
11
  from clearskies.autodoc.request import Parameter, Request
12
12
  from clearskies.autodoc.response import Response
13
- from clearskies.functional import routing, string, validations
13
+ from clearskies.functional import routing, string
14
14
 
15
15
  if TYPE_CHECKING:
16
16
  from clearskies import Column, Model, SecurityHeader
@@ -0,0 +1,47 @@
1
+ from typing import Any, cast
2
+
3
+
4
+ def get_nested_attribute(data: dict[str, Any] | str, attr_path: str) -> Any:
5
+ """
6
+ Extract a nested attribute from JSON data using dot notation.
7
+
8
+ This function navigates through a nested JSON structure using a dot-separated path
9
+ to retrieve a specific attribute. If the input is a string, it will attempt to parse
10
+ it as JSON first.
11
+
12
+ Example:
13
+ ```
14
+ data = {"database": {"credentials": {"username": "admin", "password": "secret"}}}
15
+ username = get_nested_attribute(data, "database.credentials.username")
16
+ # Returns "admin"
17
+ ```
18
+
19
+ Args:
20
+ data: The JSON data as a dictionary or a JSON string
21
+ attr_path: The path to the attribute using dot notation (e.g., "database.username")
22
+
23
+ Returns:
24
+ The value at the specified path
25
+
26
+ Raises:
27
+ ValueError: If the data cannot be parsed as JSON
28
+ KeyError: If the attribute path doesn't exist in the data
29
+ """
30
+ keys = attr_path.split(".", 1)
31
+ if not isinstance(data, dict):
32
+ try:
33
+ import json
34
+
35
+ data = json.loads(data)
36
+ except Exception:
37
+ raise ValueError(f"Could not parse data as JSON to get attribute '{attr_path}'")
38
+
39
+ # At this point, we know data is a dictionary
40
+ data_dict = cast(dict[str, Any], data) # Help type checker understand data is a dict
41
+
42
+ if len(keys) == 1:
43
+ if keys[0] not in data_dict:
44
+ raise KeyError(f"Data does not contain attribute '{attr_path}'")
45
+ return data_dict[keys[0]]
46
+
47
+ return get_nested_attribute(data_dict[keys[0]], keys[1])
@@ -1,2 +1,7 @@
1
1
  from .cli_input_error import CLIInputError
2
2
  from .cli_not_found import CLINotFound
3
+
4
+ __all__ = [
5
+ "CLIInputError",
6
+ "CLINotFound",
7
+ ]
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import json
4
4
  from abc import ABC, abstractmethod
5
5
  from typing import TYPE_CHECKING, Any
6
- from urllib.parse import parse_qs
7
6
 
8
7
  from clearskies import configs, configurable
9
8
 
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import re
4
3
  from abc import abstractmethod
5
4
  from typing import TYPE_CHECKING, Any, Callable, Iterator, Self
6
5
 
@@ -1,16 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime
4
- import json
5
4
  import logging
6
5
  from types import ModuleType
7
6
  from typing import TYPE_CHECKING, Any
8
7
 
9
- from typing_extensions import override
10
-
11
8
  from clearskies import configs, secrets
12
9
  from clearskies.decorators import parameters_to_properties
13
10
  from clearskies.di import inject
11
+ from clearskies.functional.json import get_nested_attribute
14
12
  from clearskies.secrets.exceptions import PermissionsError
15
13
 
16
14
  if TYPE_CHECKING:
@@ -226,7 +224,7 @@ class Akeyless(secrets.Secrets):
226
224
  raise KeyError(f"Secret '{path}' not found")
227
225
  raise e
228
226
  if json_attribute:
229
- return self._get_nested_attribute(res[path], json_attribute) # type: ignore
227
+ return get_nested_attribute(res[path], json_attribute) # type: ignore
230
228
  return str(res[path])
231
229
 
232
230
  def get_dynamic_secret(
@@ -251,7 +249,7 @@ class Akeyless(secrets.Secrets):
251
249
  kwargs["args"] = args # type: ignore
252
250
  res: dict[str, Any] = self.api.get_dynamic_secret_value(self.akeyless.GetDynamicSecretValue(**kwargs)) # type: ignore
253
251
  if json_attribute:
254
- return self._get_nested_attribute(res, json_attribute)
252
+ return get_nested_attribute(res, json_attribute)
255
253
  return res
256
254
 
257
255
  def get_rotated_secret(
@@ -278,7 +276,7 @@ class Akeyless(secrets.Secrets):
278
276
 
279
277
  res: dict[str, str] = self._api.get_rotated_secret_value(self.akeyless.GetRotatedSecretValue(**kwargs))["value"] # type: ignore
280
278
  if json_attribute:
281
- return self._get_nested_attribute(res, json_attribute)
279
+ return get_nested_attribute(res, json_attribute)
282
280
  return res
283
281
 
284
282
  def describe_secret(self, path: str) -> Any:
@@ -472,26 +470,6 @@ class Akeyless(secrets.Secrets):
472
470
  self.akeyless.DescribePermissions(token=self._get_token(), path=path, type=type)
473
471
  ).client_permissions # type: ignore
474
472
 
475
- def _get_nested_attribute(self, data: dict[str, Any] | str, attr_path: str) -> Any:
476
- """
477
- Extract a nested attribute from JSON data.
478
-
479
- Parses the provided data as JSON if it's a string. Traverses the nested structure using
480
- the dot-separated path (e.g., "database.username"). Raises ValueError if the data cannot
481
- be parsed as JSON, or KeyError if the attribute path doesn't exist in the data.
482
- """
483
- keys = attr_path.split(".", 1)
484
- if not isinstance(data, dict):
485
- try:
486
- data = json.loads(data)
487
- except Exception:
488
- raise ValueError(f"Could not parse secret as JSON to get attribute '{attr_path}'")
489
- if len(keys) == 1:
490
- if not isinstance(data, dict) or keys[0] not in data:
491
- raise KeyError(f"Secret does not contain attribute '{attr_path}'")
492
- return data[keys[0]] # type: ignore
493
- return self._get_nested_attribute(data[keys[0]], keys[1]) # type: ignore
494
-
495
473
 
496
474
  class AkeylessSaml(Akeyless):
497
475
  """Convenience class for SAML authentication with Akeyless."""
@@ -25,8 +25,8 @@ __all__ = [
25
25
  "InThePastAtMost",
26
26
  "MaximumLength",
27
27
  "MaximumValue",
28
+ "MinimumValue",
28
29
  "MinimumLength",
29
- "MaximumLength",
30
30
  "Required",
31
31
  "Timedelta",
32
32
  "Unique",
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime
4
3
  from typing import TYPE_CHECKING, Any
5
4
 
6
5
  from clearskies import configs
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime
4
3
  from typing import TYPE_CHECKING, Any
5
4
 
6
5
  from clearskies import configs
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime
4
3
  from typing import TYPE_CHECKING, Any
5
4
 
6
5
  from clearskies import configs
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime
4
3
  from typing import TYPE_CHECKING, Any
5
4
 
6
5
  from clearskies.validator import Validator
@@ -202,7 +202,7 @@ wheels = [
202
202
 
203
203
  [[package]]
204
204
  name = "clear-skies"
205
- version = "2.0.14"
205
+ version = "2.0.16"
206
206
  source = { editable = "." }
207
207
  dependencies = [
208
208
  { name = "dateparser" },
@@ -1,5 +0,0 @@
1
- ## [2.0.14] - 2025-10-16
2
-
3
- ### Added
4
- - Add auto guess for akeyless secrets by @tnijboer in [#27](https://github.com/clearskies-py/clearskies/pull/27)
5
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes