clear-skies 1.22.31__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of clear-skies might be problematic. Click here for more details.
- {clear_skies-1.22.31.dist-info → clear_skies-2.0.0.dist-info}/METADATA +11 -13
- clear_skies-2.0.0.dist-info/RECORD +248 -0
- {clear_skies-1.22.31.dist-info → clear_skies-2.0.0.dist-info}/WHEEL +1 -1
- clearskies/__init__.py +42 -25
- clearskies/action.py +7 -0
- clearskies/authentication/__init__.py +8 -41
- clearskies/authentication/authentication.py +42 -0
- clearskies/authentication/authorization.py +4 -9
- clearskies/authentication/authorization_pass_through.py +11 -9
- clearskies/authentication/jwks.py +128 -58
- clearskies/authentication/public.py +3 -38
- clearskies/authentication/secret_bearer.py +516 -54
- clearskies/autodoc/formats/oai3_json/__init__.py +1 -1
- clearskies/autodoc/formats/oai3_json/oai3_json.py +9 -7
- clearskies/autodoc/formats/oai3_json/parameter.py +6 -3
- clearskies/autodoc/formats/oai3_json/request.py +7 -5
- clearskies/autodoc/formats/oai3_json/response.py +7 -4
- clearskies/autodoc/formats/oai3_json/schema/object.py +4 -1
- clearskies/autodoc/request/__init__.py +2 -0
- clearskies/autodoc/request/header.py +4 -6
- clearskies/autodoc/request/json_body.py +4 -6
- clearskies/autodoc/request/parameter.py +8 -0
- clearskies/autodoc/request/request.py +7 -4
- clearskies/autodoc/request/url_parameter.py +4 -6
- clearskies/autodoc/request/url_path.py +4 -6
- clearskies/autodoc/schema/__init__.py +4 -2
- clearskies/autodoc/schema/array.py +5 -6
- clearskies/autodoc/schema/boolean.py +4 -10
- clearskies/autodoc/schema/date.py +0 -3
- clearskies/autodoc/schema/datetime.py +1 -4
- clearskies/autodoc/schema/double.py +0 -3
- clearskies/autodoc/schema/enum.py +4 -2
- clearskies/autodoc/schema/integer.py +4 -9
- clearskies/autodoc/schema/long.py +0 -3
- clearskies/autodoc/schema/number.py +4 -9
- clearskies/autodoc/schema/object.py +5 -7
- clearskies/autodoc/schema/password.py +0 -3
- clearskies/autodoc/schema/schema.py +11 -0
- clearskies/autodoc/schema/string.py +4 -10
- clearskies/backends/__init__.py +55 -20
- clearskies/backends/api_backend.py +1100 -284
- clearskies/backends/backend.py +40 -84
- clearskies/backends/cursor_backend.py +236 -186
- clearskies/backends/memory_backend.py +519 -226
- clearskies/backends/secrets_backend.py +75 -31
- clearskies/column.py +1232 -0
- clearskies/columns/__init__.py +71 -0
- clearskies/columns/audit.py +205 -0
- clearskies/columns/belongs_to_id.py +483 -0
- clearskies/columns/belongs_to_model.py +128 -0
- clearskies/columns/belongs_to_self.py +105 -0
- clearskies/columns/boolean.py +109 -0
- clearskies/columns/category_tree.py +275 -0
- clearskies/columns/category_tree_ancestors.py +51 -0
- clearskies/columns/category_tree_children.py +127 -0
- clearskies/columns/category_tree_descendants.py +48 -0
- clearskies/columns/created.py +94 -0
- clearskies/columns/created_by_authorization_data.py +116 -0
- clearskies/columns/created_by_header.py +99 -0
- clearskies/columns/created_by_ip.py +92 -0
- clearskies/columns/created_by_routing_data.py +96 -0
- clearskies/columns/created_by_user_agent.py +92 -0
- clearskies/columns/date.py +230 -0
- clearskies/columns/datetime.py +278 -0
- clearskies/columns/email.py +76 -0
- clearskies/columns/float.py +149 -0
- clearskies/columns/has_many.py +505 -0
- clearskies/columns/has_many_self.py +56 -0
- clearskies/columns/has_one.py +14 -0
- clearskies/columns/integer.py +156 -0
- clearskies/columns/json.py +122 -0
- clearskies/columns/many_to_many_ids.py +333 -0
- clearskies/columns/many_to_many_ids_with_data.py +270 -0
- clearskies/columns/many_to_many_models.py +154 -0
- clearskies/columns/many_to_many_pivots.py +133 -0
- clearskies/columns/phone.py +158 -0
- clearskies/columns/select.py +91 -0
- clearskies/columns/string.py +98 -0
- clearskies/columns/timestamp.py +160 -0
- clearskies/columns/updated.py +110 -0
- clearskies/columns/uuid.py +86 -0
- clearskies/configs/README.md +105 -0
- clearskies/configs/__init__.py +159 -0
- clearskies/configs/actions.py +43 -0
- clearskies/configs/any.py +13 -0
- clearskies/configs/any_dict.py +22 -0
- clearskies/configs/any_dict_or_callable.py +23 -0
- clearskies/configs/authentication.py +23 -0
- clearskies/configs/authorization.py +23 -0
- clearskies/configs/boolean.py +16 -0
- clearskies/configs/boolean_or_callable.py +18 -0
- clearskies/configs/callable_config.py +18 -0
- clearskies/configs/columns.py +34 -0
- clearskies/configs/conditions.py +30 -0
- clearskies/configs/config.py +21 -0
- clearskies/configs/datetime.py +18 -0
- clearskies/configs/datetime_or_callable.py +19 -0
- clearskies/configs/endpoint.py +23 -0
- clearskies/configs/float.py +16 -0
- clearskies/configs/float_or_callable.py +18 -0
- clearskies/configs/integer.py +16 -0
- clearskies/configs/integer_or_callable.py +18 -0
- clearskies/configs/joins.py +30 -0
- clearskies/configs/list_any_dict.py +30 -0
- clearskies/configs/list_any_dict_or_callable.py +31 -0
- clearskies/configs/model_class.py +35 -0
- clearskies/configs/model_column.py +65 -0
- clearskies/configs/model_columns.py +56 -0
- clearskies/configs/model_destination_name.py +25 -0
- clearskies/configs/model_to_id_column.py +43 -0
- clearskies/configs/readable_model_column.py +9 -0
- clearskies/configs/readable_model_columns.py +9 -0
- clearskies/configs/schema.py +23 -0
- clearskies/configs/searchable_model_columns.py +9 -0
- clearskies/configs/security_headers.py +39 -0
- clearskies/configs/select.py +26 -0
- clearskies/configs/select_list.py +47 -0
- clearskies/configs/string.py +29 -0
- clearskies/configs/string_dict.py +32 -0
- clearskies/configs/string_list.py +32 -0
- clearskies/configs/string_list_or_callable.py +35 -0
- clearskies/configs/string_or_callable.py +18 -0
- clearskies/configs/timedelta.py +18 -0
- clearskies/configs/timezone.py +18 -0
- clearskies/configs/url.py +23 -0
- clearskies/configs/validators.py +45 -0
- clearskies/configs/writeable_model_column.py +9 -0
- clearskies/configs/writeable_model_columns.py +9 -0
- clearskies/configurable.py +76 -0
- clearskies/contexts/__init__.py +8 -8
- clearskies/contexts/cli.py +5 -42
- clearskies/contexts/context.py +78 -56
- clearskies/contexts/wsgi.py +13 -30
- clearskies/contexts/wsgi_ref.py +49 -0
- clearskies/di/__init__.py +10 -7
- clearskies/di/additional_config.py +115 -4
- clearskies/di/additional_config_auto_import.py +12 -0
- clearskies/di/di.py +742 -121
- clearskies/di/inject/__init__.py +23 -0
- clearskies/di/inject/by_class.py +21 -0
- clearskies/di/inject/by_name.py +18 -0
- clearskies/di/inject/di.py +13 -0
- clearskies/di/inject/environment.py +14 -0
- clearskies/di/inject/input_output.py +20 -0
- clearskies/di/inject/now.py +13 -0
- clearskies/di/inject/requests.py +13 -0
- clearskies/di/inject/secrets.py +14 -0
- clearskies/di/inject/utcnow.py +13 -0
- clearskies/di/inject/uuid.py +15 -0
- clearskies/di/injectable.py +29 -0
- clearskies/di/injectable_properties.py +131 -0
- clearskies/end.py +183 -0
- clearskies/endpoint.py +1309 -0
- clearskies/endpoint_group.py +297 -0
- clearskies/endpoints/__init__.py +23 -0
- clearskies/endpoints/advanced_search.py +526 -0
- clearskies/endpoints/callable.py +387 -0
- clearskies/endpoints/create.py +202 -0
- clearskies/endpoints/delete.py +139 -0
- clearskies/endpoints/get.py +275 -0
- clearskies/endpoints/health_check.py +181 -0
- clearskies/endpoints/list.py +573 -0
- clearskies/endpoints/restful_api.py +427 -0
- clearskies/endpoints/simple_search.py +286 -0
- clearskies/endpoints/update.py +190 -0
- clearskies/environment.py +5 -3
- clearskies/exceptions/__init__.py +17 -0
- clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
- clearskies/exceptions/moved_permanently.py +3 -0
- clearskies/exceptions/moved_temporarily.py +3 -0
- clearskies/exceptions/not_found.py +2 -0
- clearskies/functional/__init__.py +2 -2
- clearskies/functional/routing.py +92 -0
- clearskies/functional/string.py +19 -11
- clearskies/functional/validations.py +61 -9
- clearskies/input_outputs/__init__.py +9 -7
- clearskies/input_outputs/cli.py +130 -142
- clearskies/input_outputs/exceptions/__init__.py +1 -1
- clearskies/input_outputs/headers.py +45 -0
- clearskies/input_outputs/input_output.py +91 -122
- clearskies/input_outputs/programmatic.py +69 -0
- clearskies/input_outputs/wsgi.py +23 -38
- clearskies/model.py +489 -184
- clearskies/parameters_to_properties.py +31 -0
- clearskies/query/__init__.py +12 -0
- clearskies/query/condition.py +223 -0
- clearskies/query/join.py +136 -0
- clearskies/query/query.py +196 -0
- clearskies/query/sort.py +27 -0
- clearskies/schema.py +82 -0
- clearskies/secrets/__init__.py +3 -31
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
- clearskies/secrets/akeyless.py +88 -147
- clearskies/secrets/secrets.py +8 -8
- clearskies/security_header.py +8 -0
- clearskies/security_headers/__init__.py +8 -8
- clearskies/security_headers/cache_control.py +47 -110
- clearskies/security_headers/cors.py +40 -95
- clearskies/security_headers/csp.py +76 -151
- clearskies/security_headers/hsts.py +14 -16
- clearskies/test_base.py +8 -0
- clearskies/typing.py +11 -0
- clearskies/validator.py +25 -0
- clearskies/validators/__init__.py +33 -0
- clearskies/validators/after_column.py +62 -0
- clearskies/validators/before_column.py +13 -0
- clearskies/validators/in_the_future.py +32 -0
- clearskies/validators/in_the_future_at_least.py +11 -0
- clearskies/validators/in_the_future_at_most.py +10 -0
- clearskies/validators/in_the_past.py +32 -0
- clearskies/validators/in_the_past_at_least.py +10 -0
- clearskies/validators/in_the_past_at_most.py +10 -0
- clearskies/validators/maximum_length.py +26 -0
- clearskies/validators/maximum_value.py +29 -0
- clearskies/validators/minimum_length.py +26 -0
- clearskies/validators/minimum_value.py +29 -0
- clearskies/validators/required.py +35 -0
- clearskies/validators/timedelta.py +59 -0
- clearskies/validators/unique.py +31 -0
- clear_skies-1.22.31.dist-info/RECORD +0 -214
- clearskies/application.py +0 -29
- clearskies/authentication/auth0_jwks.py +0 -118
- clearskies/authentication/auth_exception.py +0 -2
- clearskies/authentication/jwks_jwcrypto.py +0 -51
- clearskies/backends/api_get_only_backend.py +0 -48
- clearskies/backends/example_backend.py +0 -43
- clearskies/backends/file_backend.py +0 -48
- clearskies/backends/json_backend.py +0 -7
- clearskies/backends/restful_api_advanced_search_backend.py +0 -103
- clearskies/binding_config.py +0 -16
- clearskies/column_types/__init__.py +0 -203
- clearskies/column_types/audit.py +0 -249
- clearskies/column_types/belongs_to.py +0 -271
- clearskies/column_types/boolean.py +0 -60
- clearskies/column_types/category_tree.py +0 -304
- clearskies/column_types/column.py +0 -373
- clearskies/column_types/created.py +0 -26
- clearskies/column_types/created_by_authorization_data.py +0 -26
- clearskies/column_types/created_by_header.py +0 -24
- clearskies/column_types/created_by_ip.py +0 -17
- clearskies/column_types/created_by_routing_data.py +0 -25
- clearskies/column_types/created_by_user_agent.py +0 -17
- clearskies/column_types/created_micro.py +0 -26
- clearskies/column_types/datetime.py +0 -109
- clearskies/column_types/datetime_micro.py +0 -12
- clearskies/column_types/email.py +0 -18
- clearskies/column_types/float.py +0 -43
- clearskies/column_types/has_many.py +0 -179
- clearskies/column_types/has_one.py +0 -60
- clearskies/column_types/integer.py +0 -41
- clearskies/column_types/json.py +0 -25
- clearskies/column_types/many_to_many.py +0 -278
- clearskies/column_types/many_to_many_with_data.py +0 -162
- clearskies/column_types/phone.py +0 -48
- clearskies/column_types/select.py +0 -11
- clearskies/column_types/string.py +0 -24
- clearskies/column_types/timestamp.py +0 -73
- clearskies/column_types/updated.py +0 -26
- clearskies/column_types/updated_micro.py +0 -26
- clearskies/column_types/uuid.py +0 -25
- clearskies/columns.py +0 -123
- clearskies/condition_parser.py +0 -172
- clearskies/contexts/build_context.py +0 -54
- clearskies/contexts/convert_to_application.py +0 -190
- clearskies/contexts/extract_handler.py +0 -37
- clearskies/contexts/test.py +0 -94
- clearskies/decorators/__init__.py +0 -41
- clearskies/decorators/allow_non_json_bodies.py +0 -9
- clearskies/decorators/auth0_jwks.py +0 -22
- clearskies/decorators/authorization.py +0 -10
- clearskies/decorators/binding_classes.py +0 -9
- clearskies/decorators/binding_modules.py +0 -9
- clearskies/decorators/bindings.py +0 -9
- clearskies/decorators/create.py +0 -10
- clearskies/decorators/delete.py +0 -10
- clearskies/decorators/docs.py +0 -14
- clearskies/decorators/get.py +0 -10
- clearskies/decorators/jwks.py +0 -26
- clearskies/decorators/merge.py +0 -124
- clearskies/decorators/patch.py +0 -10
- clearskies/decorators/post.py +0 -10
- clearskies/decorators/public.py +0 -11
- clearskies/decorators/response_headers.py +0 -10
- clearskies/decorators/return_raw_response.py +0 -9
- clearskies/decorators/schema.py +0 -10
- clearskies/decorators/secret_bearer.py +0 -24
- clearskies/decorators/security_headers.py +0 -10
- clearskies/di/standard_dependencies.py +0 -151
- clearskies/handlers/__init__.py +0 -41
- clearskies/handlers/advanced_search.py +0 -271
- clearskies/handlers/base.py +0 -479
- clearskies/handlers/callable.py +0 -192
- clearskies/handlers/create.py +0 -35
- clearskies/handlers/crud_by_method.py +0 -18
- clearskies/handlers/database_connector.py +0 -32
- clearskies/handlers/delete.py +0 -61
- clearskies/handlers/exceptions/__init__.py +0 -5
- clearskies/handlers/exceptions/not_found.py +0 -3
- clearskies/handlers/get.py +0 -156
- clearskies/handlers/health_check.py +0 -59
- clearskies/handlers/input_processing.py +0 -79
- clearskies/handlers/list.py +0 -530
- clearskies/handlers/mygrations.py +0 -82
- clearskies/handlers/request_method_routing.py +0 -47
- clearskies/handlers/restful_api.py +0 -218
- clearskies/handlers/routing.py +0 -62
- clearskies/handlers/schema_helper.py +0 -128
- clearskies/handlers/simple_routing.py +0 -206
- clearskies/handlers/simple_routing_route.py +0 -197
- clearskies/handlers/simple_search.py +0 -136
- clearskies/handlers/update.py +0 -102
- clearskies/handlers/write.py +0 -193
- clearskies/input_requirements/__init__.py +0 -78
- clearskies/input_requirements/after.py +0 -36
- clearskies/input_requirements/before.py +0 -36
- clearskies/input_requirements/in_the_future_at_least.py +0 -19
- clearskies/input_requirements/in_the_future_at_most.py +0 -19
- clearskies/input_requirements/in_the_past_at_least.py +0 -19
- clearskies/input_requirements/in_the_past_at_most.py +0 -19
- clearskies/input_requirements/maximum_length.py +0 -19
- clearskies/input_requirements/maximum_value.py +0 -19
- clearskies/input_requirements/minimum_length.py +0 -22
- clearskies/input_requirements/minimum_value.py +0 -19
- clearskies/input_requirements/required.py +0 -23
- clearskies/input_requirements/requirement.py +0 -25
- clearskies/input_requirements/time_delta.py +0 -38
- clearskies/input_requirements/unique.py +0 -18
- clearskies/mocks/__init__.py +0 -7
- clearskies/mocks/input_output.py +0 -124
- clearskies/mocks/models.py +0 -142
- clearskies/models.py +0 -350
- clearskies/security_headers/base.py +0 -12
- clearskies/tests/simple_api/models/__init__.py +0 -2
- clearskies/tests/simple_api/models/status.py +0 -23
- clearskies/tests/simple_api/models/user.py +0 -21
- clearskies/tests/simple_api/users_api.py +0 -64
- {clear_skies-1.22.31.dist-info → clear_skies-2.0.0.dist-info}/LICENSE +0 -0
- /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
- /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
- /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
- /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
- /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
- /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
from clearskies.configs import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class StringOrCallable(config.Config):
|
|
7
|
+
def __set__(self, instance, value: str | Callable[..., str]):
|
|
8
|
+
if not isinstance(value, str) and not callable(value):
|
|
9
|
+
error_prefix = self._error_prefix(instance)
|
|
10
|
+
raise TypeError(
|
|
11
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requires a string or a callable."
|
|
12
|
+
)
|
|
13
|
+
instance._set_config(self, value)
|
|
14
|
+
|
|
15
|
+
def __get__(self, instance, parent) -> str | Callable[..., str]:
|
|
16
|
+
if not instance:
|
|
17
|
+
return self # type: ignore
|
|
18
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
from clearskies.configs import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Timedelta(config.Config):
|
|
7
|
+
def __set__(self, instance, value: datetime.timedelta):
|
|
8
|
+
if not isinstance(value, datetime.timedelta):
|
|
9
|
+
error_prefix = self._error_prefix(instance)
|
|
10
|
+
raise TypeError(
|
|
11
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requries a datetime.timedelta object."
|
|
12
|
+
)
|
|
13
|
+
instance._set_config(self, value)
|
|
14
|
+
|
|
15
|
+
def __get__(self, instance, parent) -> datetime.timedelta:
|
|
16
|
+
if not instance:
|
|
17
|
+
return self # type: ignore
|
|
18
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
from clearskies.configs import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Timezone(config.Config):
|
|
7
|
+
def __set__(self, instance, value: datetime.timezone | None):
|
|
8
|
+
if value and not isinstance(value, datetime.timezone):
|
|
9
|
+
error_prefix = self._error_prefix(instance)
|
|
10
|
+
raise TypeError(
|
|
11
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requries a timezone (datetime.timezone)."
|
|
12
|
+
)
|
|
13
|
+
instance._set_config(self, value)
|
|
14
|
+
|
|
15
|
+
def __get__(self, instance, parent) -> datetime.timezone:
|
|
16
|
+
if not instance:
|
|
17
|
+
return self # type: ignore
|
|
18
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from clearskies.configs import string
|
|
2
|
+
from clearskies.functional import routing
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Url(string.String):
|
|
6
|
+
def __set__(self, instance, value: str):
|
|
7
|
+
if value is None:
|
|
8
|
+
return
|
|
9
|
+
|
|
10
|
+
if not isinstance(value, str):
|
|
11
|
+
error_prefix = self._error_prefix(instance)
|
|
12
|
+
raise TypeError(
|
|
13
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a url parameter"
|
|
14
|
+
)
|
|
15
|
+
value = value.strip("/")
|
|
16
|
+
|
|
17
|
+
if value:
|
|
18
|
+
try:
|
|
19
|
+
routing.extract_url_parameter_name_map(value)
|
|
20
|
+
except ValueError as e:
|
|
21
|
+
error_prefix = self._error_prefix(instance)
|
|
22
|
+
raise ValueError(f"{error_prefix} {e}")
|
|
23
|
+
instance._set_config(self, value)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from clearskies.configs import config
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from clearskies import typing
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Validators(config.Config):
|
|
12
|
+
"""
|
|
13
|
+
Validator config.
|
|
14
|
+
|
|
15
|
+
A config that accepts various things that are accepted as validators in model columns:
|
|
16
|
+
|
|
17
|
+
1. An instance of clearskies.validators.Validator
|
|
18
|
+
2. A list of clearskies.validators.Validator
|
|
19
|
+
|
|
20
|
+
Incoming values are normalized to a list so that a list always comes out even if a non-list is provided.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __set__(
|
|
24
|
+
self,
|
|
25
|
+
instance,
|
|
26
|
+
value: typing.validator | list[typing.validator],
|
|
27
|
+
):
|
|
28
|
+
if not isinstance(value, list):
|
|
29
|
+
value = [value]
|
|
30
|
+
|
|
31
|
+
for index, item in enumerate(value):
|
|
32
|
+
if hasattr(item, "additional_write_columns") and hasattr(item, "check"):
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
error_prefix = self._error_prefix(instance)
|
|
36
|
+
raise TypeError(
|
|
37
|
+
f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index + 1} when a Validator is required"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
instance._set_config(self, [*value])
|
|
41
|
+
|
|
42
|
+
def __get__(self, instance, parent) -> list[typing.validator]:
|
|
43
|
+
if not instance:
|
|
44
|
+
return self # type: ignore
|
|
45
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from clearskies.configs import model_column
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WriteableModelColumn(model_column.ModelColumn):
|
|
5
|
+
def get_allowed_columns(self, model_class, column_configs):
|
|
6
|
+
return [name for (name, column) in column_configs.items() if column.is_writeable]
|
|
7
|
+
|
|
8
|
+
def my_description(self):
|
|
9
|
+
return "writeable column"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from clearskies.configs import model_columns
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WriteableModelColumns(model_columns.ModelColumns):
|
|
5
|
+
def get_allowed_columns(self, model_class, column_configs):
|
|
6
|
+
return [name for (name, column) in column_configs.items() if column.is_writeable]
|
|
7
|
+
|
|
8
|
+
def my_description(self):
|
|
9
|
+
return "writeable column"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from clearskies.configs import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Configurable:
|
|
7
|
+
_config: dict[str, Any] | None = None
|
|
8
|
+
_descriptor_config_map: dict[int, str] | None = None
|
|
9
|
+
|
|
10
|
+
def _set_config(self, descriptor, value):
|
|
11
|
+
if not self._config:
|
|
12
|
+
self._config = {}
|
|
13
|
+
|
|
14
|
+
self._config[self._descriptor_to_name(descriptor)] = value
|
|
15
|
+
|
|
16
|
+
def _get_config(self, descriptor):
|
|
17
|
+
if not self._config:
|
|
18
|
+
self._config = {}
|
|
19
|
+
|
|
20
|
+
name = self._descriptor_to_name(descriptor)
|
|
21
|
+
if name not in self._config:
|
|
22
|
+
raise KeyError(f"Attempt to fetch a config value named '{name}' but no value has been set for this config")
|
|
23
|
+
return self._config[name]
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def _get_config_object(cls, attribute_name):
|
|
27
|
+
return getattr(cls, attribute_name)
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_descriptor_config_map(cls):
|
|
31
|
+
if cls._descriptor_config_map:
|
|
32
|
+
return cls._descriptor_config_map
|
|
33
|
+
|
|
34
|
+
descriptor_config_map = {}
|
|
35
|
+
for attribute_name in dir(cls):
|
|
36
|
+
descriptor = getattr(cls, attribute_name)
|
|
37
|
+
if not isinstance(descriptor, config.Config):
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
descriptor_config_map[id(descriptor)] = attribute_name
|
|
41
|
+
|
|
42
|
+
cls._descriptor_config_map = descriptor_config_map
|
|
43
|
+
return cls._descriptor_config_map
|
|
44
|
+
|
|
45
|
+
def _descriptor_to_name(self, descriptor):
|
|
46
|
+
descriptor_config_map = self.get_descriptor_config_map()
|
|
47
|
+
if id(descriptor) not in descriptor_config_map:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"The reason behind this error is kinda long and complicated, but doens't really matter. To make it go away, just add `_descriptor_config_map = None` to the definition of {self.__class__.__name__}"
|
|
50
|
+
)
|
|
51
|
+
return descriptor_config_map[id(descriptor)]
|
|
52
|
+
|
|
53
|
+
def finalize_and_validate_configuration(self):
|
|
54
|
+
my_class = self.__class__
|
|
55
|
+
if not self._config:
|
|
56
|
+
self._config = {}
|
|
57
|
+
|
|
58
|
+
# now it's time to check for required values and provide defaults
|
|
59
|
+
attribute_names = self.get_descriptor_config_map().values()
|
|
60
|
+
for attribute_name in attribute_names:
|
|
61
|
+
config = getattr(my_class, attribute_name)
|
|
62
|
+
if attribute_name not in self._config:
|
|
63
|
+
self._config[attribute_name] = config.default
|
|
64
|
+
|
|
65
|
+
if config.required and self._config.get(attribute_name) is None:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"Missing required configuration property '{attribute_name}' for class '{my_class.__name__}'"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# loop through a second time to have the configs check their values
|
|
71
|
+
# we do this as a separate step because we want to make sure required and default
|
|
72
|
+
# values are specified before we have the configs do their validation.
|
|
73
|
+
for attribute_name in attribute_names:
|
|
74
|
+
getattr(my_class, attribute_name).finalize_and_validate_configuration(self)
|
|
75
|
+
if attribute_name not in self._config:
|
|
76
|
+
self._config[attribute_name] = None
|
clearskies/contexts/__init__.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .
|
|
3
|
-
from .
|
|
4
|
-
from .
|
|
1
|
+
from clearskies.contexts.cli import Cli
|
|
2
|
+
from clearskies.contexts.context import Context
|
|
3
|
+
from clearskies.contexts.wsgi import Wsgi
|
|
4
|
+
from clearskies.contexts.wsgi_ref import WsgiRef
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
7
|
+
"Cli",
|
|
8
|
+
"Context",
|
|
9
|
+
"Wsgi",
|
|
10
|
+
"WsgiRef",
|
|
11
11
|
]
|
clearskies/contexts/cli.py
CHANGED
|
@@ -1,44 +1,7 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
3
|
-
from ..input_outputs import exceptions
|
|
4
|
-
from .build_context import build_context
|
|
5
|
-
from .context import Context
|
|
1
|
+
from clearskies.contexts.context import Context
|
|
2
|
+
from clearskies.input_outputs import Cli as CliInputOutput
|
|
6
3
|
|
|
7
4
|
|
|
8
|
-
class
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def finalize_handler_config(self, config):
|
|
13
|
-
return {
|
|
14
|
-
"authentication": public(),
|
|
15
|
-
**config,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
def __call__(self):
|
|
19
|
-
if self.handler is None:
|
|
20
|
-
raise ValueError("Cannot execute CLI context without first configuring it")
|
|
21
|
-
|
|
22
|
-
try:
|
|
23
|
-
return self.handler(self.di.build(CLIInputOutput))
|
|
24
|
-
except exceptions.CLINotFound:
|
|
25
|
-
print("help (aka 404 not found)!")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def cli(
|
|
29
|
-
application,
|
|
30
|
-
di_class=None,
|
|
31
|
-
bindings=None,
|
|
32
|
-
binding_classes=None,
|
|
33
|
-
binding_modules=None,
|
|
34
|
-
additional_configs=None,
|
|
35
|
-
):
|
|
36
|
-
return build_context(
|
|
37
|
-
CLI,
|
|
38
|
-
application,
|
|
39
|
-
di_class=di_class,
|
|
40
|
-
bindings=bindings,
|
|
41
|
-
binding_classes=binding_classes,
|
|
42
|
-
binding_modules=binding_modules,
|
|
43
|
-
additional_configs=additional_configs,
|
|
44
|
-
)
|
|
5
|
+
class Cli(Context):
|
|
6
|
+
def __call__(self): # type: ignore
|
|
7
|
+
return self.execute_application(CliInputOutput())
|
clearskies/contexts/context.py
CHANGED
|
@@ -1,62 +1,84 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
6
|
+
|
|
7
|
+
import clearskies.endpoint
|
|
8
|
+
import clearskies.endpoint_group
|
|
9
|
+
from clearskies.di import Di
|
|
10
|
+
from clearskies.di.additional_config import AdditionalConfig
|
|
11
|
+
from clearskies.input_outputs import Programmatic
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from clearskies.input_outputs import InputOutput
|
|
2
15
|
|
|
3
16
|
|
|
4
17
|
class Context:
|
|
5
|
-
di = None
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# an application
|
|
39
|
-
if hasattr(application, "__getitem__") and "handler_class" in application:
|
|
40
|
-
if not "handler_config" in application:
|
|
41
|
-
raise ValueError(
|
|
42
|
-
"context was passed a dictionary-like object with 'handler_class', but not "
|
|
43
|
-
+ "'handler_config'. Both are required to execute the handler"
|
|
18
|
+
di: Di = None # type: ignore
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
application: Callable | clearskies.endpoint.Endpoint | clearskies.endpoint_group.EndpointGroup,
|
|
23
|
+
classes: type | list[type] = [],
|
|
24
|
+
modules: ModuleType | list[ModuleType] = [],
|
|
25
|
+
bindings: dict[str, Any] = {},
|
|
26
|
+
additional_configs: AdditionalConfig | list[AdditionalConfig] = [],
|
|
27
|
+
class_overrides: dict[type, Any] = {},
|
|
28
|
+
overrides: dict[str, type] = {},
|
|
29
|
+
now: datetime.datetime | None = None,
|
|
30
|
+
utcnow: datetime.datetime | None = None,
|
|
31
|
+
):
|
|
32
|
+
self.di = Di(
|
|
33
|
+
classes=classes,
|
|
34
|
+
modules=modules,
|
|
35
|
+
bindings=bindings,
|
|
36
|
+
additional_configs=additional_configs,
|
|
37
|
+
class_overrides=class_overrides,
|
|
38
|
+
now=now,
|
|
39
|
+
utcnow=utcnow,
|
|
40
|
+
)
|
|
41
|
+
self.application = application
|
|
42
|
+
|
|
43
|
+
def execute_application(self, input_output: InputOutput):
|
|
44
|
+
if hasattr(self.application, "injectable_properties"):
|
|
45
|
+
self.application.injectable_properties(self.di)
|
|
46
|
+
return self.application(input_output)
|
|
47
|
+
elif callable(self.application):
|
|
48
|
+
try:
|
|
49
|
+
return input_output.respond(
|
|
50
|
+
self.di.call_function(self.application, **input_output.get_context_for_callables())
|
|
44
51
|
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
except clearskies.exceptions.ClientError as e:
|
|
53
|
+
return input_output.respond(str(e), 400)
|
|
54
|
+
except clearskies.exceptions.Authentication as e:
|
|
55
|
+
return input_output.respond(str(e), 401)
|
|
56
|
+
except clearskies.exceptions.Authorization as e:
|
|
57
|
+
return input_output.respond(str(e), 403)
|
|
58
|
+
except clearskies.exceptions.NotFound as e:
|
|
59
|
+
return input_output.respond(str(e), 404)
|
|
60
|
+
except clearskies.exceptions.MovedPermanently as e:
|
|
61
|
+
return input_output.respond(str(e), 302)
|
|
62
|
+
except clearskies.exceptions.MovedTemporarily as e:
|
|
63
|
+
return input_output.respond(str(e), 307)
|
|
64
|
+
|
|
65
|
+
def __call__(
|
|
66
|
+
self,
|
|
67
|
+
url: str = "",
|
|
68
|
+
request_method: str = "GET",
|
|
69
|
+
body: str | dict[str, Any] | list[Any] = "",
|
|
70
|
+
query_parameters: dict[str, str] = {},
|
|
71
|
+
request_headers: dict[str, str] = {},
|
|
72
|
+
):
|
|
73
|
+
return self.execute_application(
|
|
74
|
+
Programmatic(
|
|
75
|
+
url=url,
|
|
76
|
+
request_method=request_method,
|
|
77
|
+
body=body,
|
|
78
|
+
query_parameters=query_parameters,
|
|
79
|
+
request_headers=request_headers,
|
|
80
|
+
)
|
|
59
81
|
)
|
|
60
82
|
|
|
61
|
-
def
|
|
62
|
-
return
|
|
83
|
+
def build(self, thing: Any, cache: bool = False) -> Any:
|
|
84
|
+
return self.di.build(thing, cache=cache)
|
clearskies/contexts/wsgi.py
CHANGED
|
@@ -1,33 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
from
|
|
3
|
-
from
|
|
1
|
+
import datetime
|
|
2
|
+
from types import ModuleType
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
from wsgiref.simple_server import make_server
|
|
5
|
+
from wsgiref.util import setup_testing_defaults
|
|
4
6
|
|
|
7
|
+
import clearskies.endpoint
|
|
8
|
+
import clearskies.endpoint_group
|
|
9
|
+
from clearskies.contexts.context import Context
|
|
10
|
+
from clearskies.di import AdditionalConfig
|
|
11
|
+
from clearskies.input_outputs import Wsgi as WsgiInputOutput
|
|
5
12
|
|
|
6
|
-
class WSGI(Context):
|
|
7
|
-
def __init__(self, di):
|
|
8
|
-
super().__init__(di)
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return self.handler(WSGIInputOutput(env, start_response))
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def wsgi(
|
|
18
|
-
application,
|
|
19
|
-
di_class=None,
|
|
20
|
-
bindings=None,
|
|
21
|
-
binding_classes=None,
|
|
22
|
-
binding_modules=None,
|
|
23
|
-
additional_configs=None,
|
|
24
|
-
):
|
|
25
|
-
return build_context(
|
|
26
|
-
WSGI,
|
|
27
|
-
application,
|
|
28
|
-
di_class=None,
|
|
29
|
-
bindings=bindings,
|
|
30
|
-
binding_classes=binding_classes,
|
|
31
|
-
binding_modules=binding_modules,
|
|
32
|
-
additional_configs=additional_configs,
|
|
33
|
-
)
|
|
14
|
+
class Wsgi(Context):
|
|
15
|
+
def __call__(self, env, start_response): # type: ignore
|
|
16
|
+
return self.execute_application(WsgiInputOutput(env, start_response))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from types import ModuleType
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
from wsgiref.simple_server import make_server
|
|
5
|
+
from wsgiref.util import setup_testing_defaults
|
|
6
|
+
|
|
7
|
+
import clearskies.endpoint
|
|
8
|
+
import clearskies.endpoint_group
|
|
9
|
+
from clearskies.contexts.context import Context
|
|
10
|
+
from clearskies.di import AdditionalConfig
|
|
11
|
+
from clearskies.input_outputs import Wsgi as WsgiInputOutput
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WsgiRef(Context):
|
|
15
|
+
port: int = 8080
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
application: Callable | clearskies.endpoint.Endpoint | clearskies.endpoint_group.EndpointGroup,
|
|
20
|
+
port: int = 8080,
|
|
21
|
+
classes: type | list[type] = [],
|
|
22
|
+
modules: ModuleType | list[ModuleType] = [],
|
|
23
|
+
bindings: dict[str, Any] = {},
|
|
24
|
+
additional_configs: AdditionalConfig | list[AdditionalConfig] = [],
|
|
25
|
+
class_overrides: dict[type, type] = {},
|
|
26
|
+
overrides: dict[str, type] = {},
|
|
27
|
+
now: datetime.datetime | None = None,
|
|
28
|
+
utcnow: datetime.datetime | None = None,
|
|
29
|
+
):
|
|
30
|
+
super().__init__(
|
|
31
|
+
application,
|
|
32
|
+
classes=classes,
|
|
33
|
+
modules=modules,
|
|
34
|
+
bindings=bindings,
|
|
35
|
+
additional_configs=additional_configs,
|
|
36
|
+
class_overrides=class_overrides,
|
|
37
|
+
overrides=overrides,
|
|
38
|
+
now=now,
|
|
39
|
+
utcnow=utcnow,
|
|
40
|
+
)
|
|
41
|
+
self.port = port
|
|
42
|
+
|
|
43
|
+
def __call__(self): # type: ignore
|
|
44
|
+
with make_server("", self.port, self.handler) as httpd:
|
|
45
|
+
print(f"Starting WSGI server on port {self.port}. This is NOT intended for production usage.")
|
|
46
|
+
httpd.serve_forever()
|
|
47
|
+
|
|
48
|
+
def handler(self, environment, start_response):
|
|
49
|
+
return self.execute_application(WsgiInputOutput(environment, start_response))
|
clearskies/di/__init__.py
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
from .di import
|
|
3
|
-
from .
|
|
4
|
-
from .
|
|
1
|
+
import clearskies.di.inject as inject
|
|
2
|
+
from clearskies.di.additional_config import AdditionalConfig
|
|
3
|
+
from clearskies.di.additional_config_auto_import import AdditionalConfigAutoImport
|
|
4
|
+
from clearskies.di.di import Di
|
|
5
|
+
from clearskies.di.injectable import Injectable
|
|
6
|
+
from clearskies.di.injectable_properties import InjectableProperties
|
|
5
7
|
|
|
6
8
|
__all__ = [
|
|
7
|
-
"AdditionalConfigAutoImport",
|
|
8
|
-
"DI",
|
|
9
|
-
"StandardDependencies",
|
|
10
9
|
"AdditionalConfig",
|
|
10
|
+
"AdditionalConfigAutoImport",
|
|
11
|
+
"Di",
|
|
12
|
+
"InjectableProperties",
|
|
13
|
+
"injectInjectable",
|
|
11
14
|
]
|