clear-skies 2.0.5__py3-none-any.whl → 2.0.7__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-2.0.5.dist-info → clear_skies-2.0.7.dist-info}/METADATA +1 -1
- clear_skies-2.0.7.dist-info/RECORD +251 -0
- clearskies/__init__.py +61 -0
- clearskies/action.py +7 -0
- clearskies/authentication/__init__.py +15 -0
- clearskies/authentication/authentication.py +46 -0
- clearskies/authentication/authorization.py +16 -0
- clearskies/authentication/authorization_pass_through.py +20 -0
- clearskies/authentication/jwks.py +163 -0
- clearskies/authentication/public.py +5 -0
- clearskies/authentication/secret_bearer.py +553 -0
- clearskies/autodoc/__init__.py +8 -0
- clearskies/autodoc/formats/__init__.py +5 -0
- clearskies/autodoc/formats/oai3_json/__init__.py +7 -0
- clearskies/autodoc/formats/oai3_json/oai3_json.py +87 -0
- clearskies/autodoc/formats/oai3_json/oai3_schema_resolver.py +15 -0
- clearskies/autodoc/formats/oai3_json/parameter.py +35 -0
- clearskies/autodoc/formats/oai3_json/request.py +68 -0
- clearskies/autodoc/formats/oai3_json/response.py +28 -0
- clearskies/autodoc/formats/oai3_json/schema/__init__.py +11 -0
- clearskies/autodoc/formats/oai3_json/schema/array.py +9 -0
- clearskies/autodoc/formats/oai3_json/schema/default.py +13 -0
- clearskies/autodoc/formats/oai3_json/schema/enum.py +7 -0
- clearskies/autodoc/formats/oai3_json/schema/object.py +35 -0
- clearskies/autodoc/formats/oai3_json/test.json +1985 -0
- clearskies/autodoc/py.typed +0 -0
- clearskies/autodoc/request/__init__.py +15 -0
- clearskies/autodoc/request/header.py +6 -0
- clearskies/autodoc/request/json_body.py +6 -0
- clearskies/autodoc/request/parameter.py +8 -0
- clearskies/autodoc/request/request.py +47 -0
- clearskies/autodoc/request/url_parameter.py +6 -0
- clearskies/autodoc/request/url_path.py +6 -0
- clearskies/autodoc/response/__init__.py +5 -0
- clearskies/autodoc/response/response.py +9 -0
- clearskies/autodoc/schema/__init__.py +31 -0
- clearskies/autodoc/schema/array.py +10 -0
- clearskies/autodoc/schema/base64.py +8 -0
- clearskies/autodoc/schema/boolean.py +5 -0
- clearskies/autodoc/schema/date.py +5 -0
- clearskies/autodoc/schema/datetime.py +5 -0
- clearskies/autodoc/schema/double.py +5 -0
- clearskies/autodoc/schema/enum.py +17 -0
- clearskies/autodoc/schema/integer.py +6 -0
- clearskies/autodoc/schema/long.py +5 -0
- clearskies/autodoc/schema/number.py +6 -0
- clearskies/autodoc/schema/object.py +13 -0
- clearskies/autodoc/schema/password.py +5 -0
- clearskies/autodoc/schema/schema.py +11 -0
- clearskies/autodoc/schema/string.py +5 -0
- clearskies/backends/__init__.py +65 -0
- clearskies/backends/api_backend.py +1178 -0
- clearskies/backends/backend.py +136 -0
- clearskies/backends/cursor_backend.py +335 -0
- clearskies/backends/memory_backend.py +797 -0
- clearskies/backends/secrets_backend.py +106 -0
- clearskies/column.py +1233 -0
- clearskies/columns/__init__.py +71 -0
- clearskies/columns/audit.py +206 -0
- clearskies/columns/belongs_to_id.py +483 -0
- clearskies/columns/belongs_to_model.py +132 -0
- clearskies/columns/belongs_to_self.py +105 -0
- clearskies/columns/boolean.py +113 -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 +95 -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 +97 -0
- clearskies/columns/created_by_user_agent.py +92 -0
- clearskies/columns/date.py +234 -0
- clearskies/columns/datetime.py +282 -0
- clearskies/columns/email.py +76 -0
- clearskies/columns/float.py +153 -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 +160 -0
- clearskies/columns/json.py +128 -0
- clearskies/columns/many_to_many_ids.py +337 -0
- clearskies/columns/many_to_many_ids_with_data.py +274 -0
- clearskies/columns/many_to_many_models.py +158 -0
- clearskies/columns/many_to_many_pivots.py +134 -0
- clearskies/columns/phone.py +159 -0
- clearskies/columns/select.py +92 -0
- clearskies/columns/string.py +102 -0
- clearskies/columns/timestamp.py +164 -0
- clearskies/columns/updated.py +110 -0
- clearskies/columns/uuid.py +86 -0
- clearskies/configs/README.md +105 -0
- clearskies/configs/__init__.py +162 -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 +24 -0
- clearskies/configs/datetime.py +18 -0
- clearskies/configs/datetime_or_callable.py +19 -0
- clearskies/configs/endpoint.py +23 -0
- clearskies/configs/endpoint_list.py +29 -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 +11 -0
- clearskies/contexts/cli.py +117 -0
- clearskies/contexts/context.py +98 -0
- clearskies/contexts/wsgi.py +76 -0
- clearskies/contexts/wsgi_ref.py +82 -0
- clearskies/decorators.py +33 -0
- clearskies/di/__init__.py +15 -0
- clearskies/di/additional_config.py +130 -0
- clearskies/di/additional_config_auto_import.py +17 -0
- clearskies/di/di.py +973 -0
- 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/di/test_module/__init__.py +6 -0
- clearskies/di/test_module/another_module/__init__.py +2 -0
- clearskies/di/test_module/module_class.py +5 -0
- clearskies/end.py +183 -0
- clearskies/endpoint.py +1314 -0
- clearskies/endpoint_group.py +336 -0
- clearskies/endpoints/__init__.py +25 -0
- clearskies/endpoints/advanced_search.py +526 -0
- clearskies/endpoints/callable.py +388 -0
- clearskies/endpoints/create.py +205 -0
- clearskies/endpoints/delete.py +139 -0
- clearskies/endpoints/get.py +271 -0
- clearskies/endpoints/health_check.py +183 -0
- clearskies/endpoints/list.py +574 -0
- clearskies/endpoints/restful_api.py +427 -0
- clearskies/endpoints/schema.py +189 -0
- clearskies/endpoints/simple_search.py +286 -0
- clearskies/endpoints/update.py +193 -0
- clearskies/environment.py +104 -0
- clearskies/exceptions/__init__.py +19 -0
- clearskies/exceptions/authentication.py +2 -0
- clearskies/exceptions/authorization.py +2 -0
- clearskies/exceptions/client_error.py +2 -0
- clearskies/exceptions/input_errors.py +4 -0
- clearskies/exceptions/missing_dependency.py +2 -0
- 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 +7 -0
- clearskies/functional/routing.py +92 -0
- clearskies/functional/string.py +112 -0
- clearskies/functional/validations.py +76 -0
- clearskies/input_outputs/__init__.py +13 -0
- clearskies/input_outputs/cli.py +171 -0
- clearskies/input_outputs/exceptions/__init__.py +2 -0
- clearskies/input_outputs/exceptions/cli_input_error.py +2 -0
- clearskies/input_outputs/exceptions/cli_not_found.py +2 -0
- clearskies/input_outputs/headers.py +45 -0
- clearskies/input_outputs/input_output.py +138 -0
- clearskies/input_outputs/programmatic.py +69 -0
- clearskies/input_outputs/py.typed +0 -0
- clearskies/input_outputs/wsgi.py +77 -0
- clearskies/model.py +1922 -0
- clearskies/py.typed +0 -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 +6 -0
- clearskies/secrets/additional_configs/__init__.py +32 -0
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +61 -0
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +160 -0
- clearskies/secrets/akeyless.py +182 -0
- clearskies/secrets/exceptions/__init__.py +1 -0
- clearskies/secrets/exceptions/not_found.py +2 -0
- clearskies/secrets/secrets.py +38 -0
- clearskies/security_header.py +15 -0
- clearskies/security_headers/__init__.py +11 -0
- clearskies/security_headers/cache_control.py +67 -0
- clearskies/security_headers/cors.py +50 -0
- clearskies/security_headers/csp.py +94 -0
- clearskies/security_headers/hsts.py +22 -0
- clearskies/security_headers/x_content_type_options.py +0 -0
- clearskies/security_headers/x_frame_options.py +0 -0
- clearskies/test_base.py +8 -0
- clearskies/typing.py +11 -0
- clearskies/validator.py +37 -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 +34 -0
- clearskies/validators/timedelta.py +59 -0
- clearskies/validators/unique.py +30 -0
- clear_skies-2.0.5.dist-info/RECORD +0 -4
- {clear_skies-2.0.5.dist-info → clear_skies-2.0.7.dist-info}/WHEEL +0 -0
- {clear_skies-2.0.5.dist-info → clear_skies-2.0.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from typing import Callable
|
|
3
|
+
|
|
4
|
+
from clearskies.configs import config
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DatetimeOrCallable(config.Config):
|
|
8
|
+
def __set__(self, instance, value: datetime.datetime | Callable[..., datetime.datetime]):
|
|
9
|
+
if not isinstance(value, datetime.datetime) and not callable(value):
|
|
10
|
+
error_prefix = self._error_prefix(instance)
|
|
11
|
+
raise TypeError(
|
|
12
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requries a datetime object or a callable."
|
|
13
|
+
)
|
|
14
|
+
instance._set_config(self, value)
|
|
15
|
+
|
|
16
|
+
def __get__(self, instance, parent) -> datetime.datetime | Callable[..., datetime.datetime]:
|
|
17
|
+
if not instance:
|
|
18
|
+
return self # type: ignore
|
|
19
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from clearskies.configs import config
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from clearskies.endpoint import Endpoint as EndpointBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Endpoint(config.Config):
|
|
12
|
+
def __set__(self, instance, value: EndpointBase):
|
|
13
|
+
if not hasattr(value, "success"):
|
|
14
|
+
error_prefix = self._error_prefix(instance)
|
|
15
|
+
raise TypeError(
|
|
16
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requries an endpoint."
|
|
17
|
+
)
|
|
18
|
+
instance._set_config(self, value)
|
|
19
|
+
|
|
20
|
+
def __get__(self, instance, parent) -> EndpointBase:
|
|
21
|
+
if not instance:
|
|
22
|
+
return self # type: ignore
|
|
23
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,29 @@
|
|
|
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.endpoint import Endpoint as EndpointBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EndpointList(config.Config):
|
|
12
|
+
def __set__(self, instance, value: list[EndpointBase]):
|
|
13
|
+
if not isinstance(value, list):
|
|
14
|
+
error_prefix = self._error_prefix(instance)
|
|
15
|
+
raise TypeError(
|
|
16
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a parameter that requries a list of endpoints."
|
|
17
|
+
)
|
|
18
|
+
for index, item in enumerate(value):
|
|
19
|
+
if not hasattr(item, "top_level_authentication_and_authorization"):
|
|
20
|
+
error_prefix = self._error_prefix(instance)
|
|
21
|
+
raise TypeError(
|
|
22
|
+
f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index+1} when all items in the list should be instances of clearskies.End."
|
|
23
|
+
)
|
|
24
|
+
instance._set_config(self, value)
|
|
25
|
+
|
|
26
|
+
def __get__(self, instance, parent) -> list[EndpointBase]:
|
|
27
|
+
if not instance:
|
|
28
|
+
return self # type: ignore
|
|
29
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from clearskies.configs import config
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Float(config.Config):
|
|
5
|
+
def __set__(self, instance, value: float):
|
|
6
|
+
if not isinstance(value, float):
|
|
7
|
+
error_prefix = self._error_prefix(instance)
|
|
8
|
+
raise TypeError(
|
|
9
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to parameter that requires a float."
|
|
10
|
+
)
|
|
11
|
+
instance._set_config(self, value)
|
|
12
|
+
|
|
13
|
+
def __get__(self, instance, parent) -> float:
|
|
14
|
+
if not instance:
|
|
15
|
+
return self # type: ignore
|
|
16
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
from clearskies.configs import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FloatOrCallable(config.Config):
|
|
7
|
+
def __set__(self, instance, value: float | Callable[..., float]):
|
|
8
|
+
if not isinstance(value, float) 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 parameter that requires a float or a callable."
|
|
12
|
+
)
|
|
13
|
+
instance._set_config(self, value)
|
|
14
|
+
|
|
15
|
+
def __get__(self, instance, parent) -> float | Callable[..., float]:
|
|
16
|
+
if not instance:
|
|
17
|
+
return self # type: ignore
|
|
18
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from clearskies.configs import config
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Integer(config.Config):
|
|
5
|
+
def __set__(self, instance, value: int):
|
|
6
|
+
if not isinstance(value, int):
|
|
7
|
+
error_prefix = self._error_prefix(instance)
|
|
8
|
+
raise TypeError(
|
|
9
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to parameter that requires an integer."
|
|
10
|
+
)
|
|
11
|
+
instance._set_config(self, value)
|
|
12
|
+
|
|
13
|
+
def __get__(self, instance, parent) -> int:
|
|
14
|
+
if not instance:
|
|
15
|
+
return self # type: ignore
|
|
16
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
from clearskies.configs import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IntegerOrCallable(config.Config):
|
|
7
|
+
def __set__(self, instance, value: int | Callable[..., int]):
|
|
8
|
+
if not isinstance(value, int) 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 parameter that requires an integer or a callable."
|
|
12
|
+
)
|
|
13
|
+
instance._set_config(self, value)
|
|
14
|
+
|
|
15
|
+
def __get__(self, instance, parent) -> int | Callable[..., int]:
|
|
16
|
+
if not instance:
|
|
17
|
+
return self # type: ignore
|
|
18
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,30 @@
|
|
|
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 Joins(config.Config):
|
|
12
|
+
def __set__(self, instance, value: typing.join | list[typing.join]):
|
|
13
|
+
if not isinstance(value, list):
|
|
14
|
+
value = [value]
|
|
15
|
+
|
|
16
|
+
for index, item in enumerate(value):
|
|
17
|
+
if callable(item) or isinstance(item, str):
|
|
18
|
+
continue
|
|
19
|
+
|
|
20
|
+
error_prefix = self._error_prefix(instance)
|
|
21
|
+
raise TypeError(
|
|
22
|
+
f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index + 1} when a string or callable is required" # type: ignore
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
instance._set_config(self, [*value])
|
|
26
|
+
|
|
27
|
+
def __get__(self, instance, parent) -> list[typing.join]:
|
|
28
|
+
if not instance:
|
|
29
|
+
return self # type: ignore
|
|
30
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from clearskies.configs import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ListAnyDict(config.Config):
|
|
7
|
+
def __set__(self, instance, value: list[dict[str, Any]]):
|
|
8
|
+
if not isinstance(value, list):
|
|
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 list."
|
|
12
|
+
)
|
|
13
|
+
for index, list_item in enumerate(value):
|
|
14
|
+
if not isinstance(list_item, dict):
|
|
15
|
+
error_prefix = self._error_prefix(instance)
|
|
16
|
+
raise TypeError(
|
|
17
|
+
f"{error_prefix} I was expecting a list of dictionaries, but item # {index + 1} has type '{list_item.__class__.__name__}."
|
|
18
|
+
)
|
|
19
|
+
for key, val in list_item.items():
|
|
20
|
+
if not isinstance(key, str):
|
|
21
|
+
error_prefix = self._error_prefix(instance)
|
|
22
|
+
raise TypeError(
|
|
23
|
+
f"{error_prefix} attempt to set a dictionary with a non-string key for item #{index + 1}."
|
|
24
|
+
)
|
|
25
|
+
instance._set_config(self, value)
|
|
26
|
+
|
|
27
|
+
def __get__(self, instance, parent) -> list[dict[str, Any]]:
|
|
28
|
+
if not instance:
|
|
29
|
+
return self # type: ignore
|
|
30
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Any, Callable
|
|
2
|
+
|
|
3
|
+
from clearskies.configs import config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ListAnyDictOrCallable(config.Config):
|
|
7
|
+
def __set__(self, instance, value: list[dict[str, Any]] | Callable[..., list[dict[str, Any]]]):
|
|
8
|
+
if not isinstance(value, list) 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 requries a list or a callable."
|
|
12
|
+
)
|
|
13
|
+
if isinstance(value, list):
|
|
14
|
+
for index, list_item in enumerate(value):
|
|
15
|
+
if not isinstance(list_item, dict):
|
|
16
|
+
error_prefix = self._error_prefix(instance)
|
|
17
|
+
raise TypeError(
|
|
18
|
+
f"{error_prefix} I was expecting a list of dictionaries, but item # {index + 1} has type '{list_item.__class__.__name__}."
|
|
19
|
+
)
|
|
20
|
+
for key, val in list_item.items():
|
|
21
|
+
if not isinstance(key, str):
|
|
22
|
+
error_prefix = self._error_prefix(instance)
|
|
23
|
+
raise TypeError(
|
|
24
|
+
f"{error_prefix} attempt to set a dictionary with a non-string key for item #{index + 1}."
|
|
25
|
+
)
|
|
26
|
+
instance._set_config(self, value)
|
|
27
|
+
|
|
28
|
+
def __get__(self, instance, parent) -> list[dict[str, Any]] | Callable[..., list[dict[str, Any]]]:
|
|
29
|
+
if not instance:
|
|
30
|
+
return self # type: ignore
|
|
31
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from clearskies.configs import config
|
|
7
|
+
from clearskies.functional import validations
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from clearskies.model import Model, ModelClassReference
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ModelClass(config.Config):
|
|
14
|
+
"""A config that accepts a model class."""
|
|
15
|
+
|
|
16
|
+
def __set__(self, instance, value: type[Model | ModelClassReference]):
|
|
17
|
+
try:
|
|
18
|
+
validations.is_model_class_or_reference(value, raise_error_message=True, strict=False)
|
|
19
|
+
except TypeError as e:
|
|
20
|
+
error_prefix = self._error_prefix(instance)
|
|
21
|
+
raise TypeError(f"{error_prefix} {str(e)}")
|
|
22
|
+
|
|
23
|
+
# reference or model class?
|
|
24
|
+
instance._set_config(self, value) # type: ignore
|
|
25
|
+
|
|
26
|
+
def __get__(self, instance, parent) -> type[Model]:
|
|
27
|
+
if not instance:
|
|
28
|
+
return self # type: ignore
|
|
29
|
+
|
|
30
|
+
value = instance._get_config(self)
|
|
31
|
+
if validations.is_model_class_reference(value):
|
|
32
|
+
class_reference = value() if inspect.isclass(value) else value
|
|
33
|
+
instance._set_config(self, class_reference.get_model_class())
|
|
34
|
+
|
|
35
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from clearskies.configs import select
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ModelColumn(select.Select):
|
|
5
|
+
def __init__(self, model_column_config_name="", required=False, default=None):
|
|
6
|
+
self.required = required
|
|
7
|
+
self.default = default
|
|
8
|
+
self.model_column_config_name = model_column_config_name
|
|
9
|
+
self.model_class = None
|
|
10
|
+
|
|
11
|
+
def __set__(self, instance, value: str):
|
|
12
|
+
if value is None:
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
if not isinstance(value, str):
|
|
16
|
+
error_prefix = self._error_prefix(instance)
|
|
17
|
+
raise TypeError(
|
|
18
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a string parameter"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
instance._set_config(self, value)
|
|
22
|
+
# unlike select, we won't validate the value currently because we won't be able to
|
|
23
|
+
# do that until the finalize_and_validate_configuration phase
|
|
24
|
+
|
|
25
|
+
def get_allowed_columns(self, model_class, column_configs):
|
|
26
|
+
return [name for name in column_configs.keys()]
|
|
27
|
+
|
|
28
|
+
def my_description(self):
|
|
29
|
+
return "column"
|
|
30
|
+
|
|
31
|
+
def get_model_class(self, instance):
|
|
32
|
+
model_class = self.model_class
|
|
33
|
+
if not model_class and self.model_column_config_name:
|
|
34
|
+
model_class = getattr(instance, self.model_column_config_name)
|
|
35
|
+
return model_class
|
|
36
|
+
|
|
37
|
+
def set_model_class(self, model_class):
|
|
38
|
+
self.model_class = model_class
|
|
39
|
+
|
|
40
|
+
def finalize_and_validate_configuration(self, instance):
|
|
41
|
+
super().finalize_and_validate_configuration(instance)
|
|
42
|
+
|
|
43
|
+
# to check for a valid column we need to know the name of the model class.
|
|
44
|
+
# This can be either provided to us directly or we may be given the name of a configuration
|
|
45
|
+
# from which we fetch the model class
|
|
46
|
+
model_class = self.get_model_class(instance)
|
|
47
|
+
|
|
48
|
+
# if we don't have one though, no worries - some classes have to provide it later and
|
|
49
|
+
# can trigger validation then.
|
|
50
|
+
if not model_class:
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
allowed_columns = self.get_allowed_columns(model_class, model_class.get_columns())
|
|
54
|
+
|
|
55
|
+
value = instance._get_config(self)
|
|
56
|
+
if not value:
|
|
57
|
+
return
|
|
58
|
+
if value not in allowed_columns:
|
|
59
|
+
error_prefix = self._error_prefix(instance)
|
|
60
|
+
my_description = self.my_description()
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"{error_prefix} attempt to set a value of '{value}' but this is not a {my_description} in the specified model class, '{model_class.__name__}'. Expected values are: '"
|
|
63
|
+
+ "', '".join(allowed_columns)
|
|
64
|
+
+ "'"
|
|
65
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from clearskies.configs import select_list
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ModelColumns(select_list.SelectList):
|
|
5
|
+
def __init__(self, model_column_config_name="", allow_relationship_references=False, required=False, default=None):
|
|
6
|
+
self.required = required
|
|
7
|
+
self.default = default
|
|
8
|
+
self.model_column_config_name = model_column_config_name
|
|
9
|
+
self.allow_relationship_references = allow_relationship_references
|
|
10
|
+
|
|
11
|
+
def __set__(self, instance, value: list[str]):
|
|
12
|
+
if value is None:
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
if not isinstance(value, list):
|
|
16
|
+
error_prefix = self._error_prefix(instance)
|
|
17
|
+
raise TypeError(
|
|
18
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a list parameter"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
instance._set_config(self, value)
|
|
22
|
+
# unlike select_list, we won't validate the value currently because we won't be able to
|
|
23
|
+
# do that until the finalize_and_validate_configuration phase
|
|
24
|
+
|
|
25
|
+
def get_allowed_columns(self, model_class, column_configs):
|
|
26
|
+
return [name for name in column_configs.keys()]
|
|
27
|
+
|
|
28
|
+
def my_description(self):
|
|
29
|
+
return "column"
|
|
30
|
+
|
|
31
|
+
def finalize_and_validate_configuration(self, instance):
|
|
32
|
+
super().finalize_and_validate_configuration(instance)
|
|
33
|
+
|
|
34
|
+
# when we were created we were told the name of the config that stores
|
|
35
|
+
# the model class we're getting readable columns from. Let's use that
|
|
36
|
+
# and fetch the model class. Note that this is an optional feature though
|
|
37
|
+
if not self.model_column_config_name:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
model_class = getattr(instance, self.model_column_config_name)
|
|
41
|
+
values = instance._get_config(self)
|
|
42
|
+
if not values or not model_class:
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
allowed_columns = self.get_allowed_columns(model_class, model_class.get_columns())
|
|
46
|
+
for value in values:
|
|
47
|
+
if self.allow_relationship_references and "." in value:
|
|
48
|
+
value = value.split(".")[0]
|
|
49
|
+
if value not in allowed_columns:
|
|
50
|
+
error_prefix = self._error_prefix(instance)
|
|
51
|
+
my_description = self.my_description()
|
|
52
|
+
raise ValueError(
|
|
53
|
+
f"{error_prefix} attempt to set a value of '{value}' but this is not a {my_description} in the specified model class, '{model_class.__name__}. Expected values are: '"
|
|
54
|
+
+ "', '".join(allowed_columns)
|
|
55
|
+
+ "'"
|
|
56
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from clearskies.configs.string import String
|
|
2
|
+
from clearskies.functional import string
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ModelDestinationName(String):
|
|
6
|
+
def __init__(self, model_column_config_name, required=False, default=None):
|
|
7
|
+
self.required = required
|
|
8
|
+
self.default = default
|
|
9
|
+
self.model_column_config_name = model_column_config_name
|
|
10
|
+
|
|
11
|
+
def get_model_class(self, instance):
|
|
12
|
+
if self.model_column_config_name:
|
|
13
|
+
return getattr(instance, self.model_column_config_name)
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
def finalize_and_validate_configuration(self, instance):
|
|
17
|
+
# we use the model class itself to decide on our value. However,
|
|
18
|
+
# if we don't have it yet, don't worry. Someone will eventually tell us
|
|
19
|
+
# what we need to know
|
|
20
|
+
model_class = self.get_model_class(instance)
|
|
21
|
+
if not model_class:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
instance._set_config(self, model_class.destination_name())
|
|
25
|
+
super().finalize_and_validate_configuration(instance)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from clearskies.configs import model_column
|
|
2
|
+
from clearskies.functional import string
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ModelToIdColumn(model_column.ModelColumn):
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
model_column_config_name="",
|
|
9
|
+
source_model_class=None,
|
|
10
|
+
source_model_class_config_name="",
|
|
11
|
+
required=False,
|
|
12
|
+
default=None,
|
|
13
|
+
):
|
|
14
|
+
self.required = required
|
|
15
|
+
self.default = default
|
|
16
|
+
self.model_column_config_name = model_column_config_name
|
|
17
|
+
self.source_model_class_config_name = source_model_class_config_name
|
|
18
|
+
self.model_class = None
|
|
19
|
+
self.source_model_class = source_model_class
|
|
20
|
+
|
|
21
|
+
def finalize_and_validate_configuration(self, instance):
|
|
22
|
+
# we use the model class itself to decide on our value. However,
|
|
23
|
+
# if we don't have it yet, don't worry. Someone will eventually tell us
|
|
24
|
+
# what we need to know
|
|
25
|
+
model_class = self.get_model_class(instance)
|
|
26
|
+
if not model_class:
|
|
27
|
+
return
|
|
28
|
+
if self.source_model_class:
|
|
29
|
+
model_class = self.source_model_class
|
|
30
|
+
elif self.source_model_class_config_name:
|
|
31
|
+
model_class = getattr(instance, self.source_model_class_config_name)
|
|
32
|
+
|
|
33
|
+
has_config = False
|
|
34
|
+
try:
|
|
35
|
+
if instance._get_config(self):
|
|
36
|
+
has_config = True
|
|
37
|
+
except KeyError:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
if not has_config:
|
|
41
|
+
instance._set_config(self, string.camel_case_to_snake_case(model_class.__name__) + "_id")
|
|
42
|
+
|
|
43
|
+
super().finalize_and_validate_configuration(instance)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from clearskies.configs import model_column
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ReadableModelColumn(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_readable]
|
|
7
|
+
|
|
8
|
+
def my_description(self):
|
|
9
|
+
return "readable column"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from clearskies.configs import model_columns
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ReadableModelColumns(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_readable]
|
|
7
|
+
|
|
8
|
+
def my_description(self):
|
|
9
|
+
return "readable column"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from clearskies.configs import config
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from clearskies.schema import Schema as SchemaType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Schema(config.Config):
|
|
12
|
+
def __set__(self, instance, value: type[SchemaType]) -> None:
|
|
13
|
+
if not hasattr(value, "get_columns"):
|
|
14
|
+
error_prefix = self._error_prefix(instance)
|
|
15
|
+
raise TypeError(
|
|
16
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to parameter that requires a Schema."
|
|
17
|
+
)
|
|
18
|
+
instance._set_config(self, value)
|
|
19
|
+
|
|
20
|
+
def __get__(self, instance, parent) -> type[SchemaType]:
|
|
21
|
+
if not instance:
|
|
22
|
+
return self # type: ignore
|
|
23
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from clearskies.configs import model_columns
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SearchableModelColumns(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_searchable]
|
|
7
|
+
|
|
8
|
+
def my_description(self):
|
|
9
|
+
return "searchable column"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from clearskies.configs import config
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from clearskies import SecurityHeader
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SecurityHeaders(config.Config):
|
|
12
|
+
"""
|
|
13
|
+
This is for a configuration that should be a list of strings.
|
|
14
|
+
|
|
15
|
+
This is different than SelectList, which also accepts a list of strings, but
|
|
16
|
+
valdiates that all of those values match against an allow list.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __set__(self, instance, value: list[SecurityHeader]):
|
|
20
|
+
if value is None:
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
if not isinstance(value, list):
|
|
24
|
+
error_prefix = self._error_prefix(instance)
|
|
25
|
+
raise TypeError(
|
|
26
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a list parameter"
|
|
27
|
+
)
|
|
28
|
+
for index, item in enumerate(value):
|
|
29
|
+
if not hasattr(item, "set_headers_for_input_output"):
|
|
30
|
+
error_prefix = self._error_prefix(instance)
|
|
31
|
+
raise TypeError(
|
|
32
|
+
f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index + 1}. A clearskies.SecurityHeader was expected."
|
|
33
|
+
)
|
|
34
|
+
instance._set_config(self, value)
|
|
35
|
+
|
|
36
|
+
def __get__(self, instance, parent) -> list[SecurityHeader]:
|
|
37
|
+
if not instance:
|
|
38
|
+
return self # type: ignore
|
|
39
|
+
return instance._get_config(self)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from clearskies.configs import string
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Select(string.String):
|
|
5
|
+
def __init__(self, allowed_values: list[str], required=False, default=None):
|
|
6
|
+
self.allowed_values = allowed_values
|
|
7
|
+
self.required = required
|
|
8
|
+
self.default = default
|
|
9
|
+
|
|
10
|
+
def __set__(self, instance, value: str):
|
|
11
|
+
if value is None:
|
|
12
|
+
return
|
|
13
|
+
|
|
14
|
+
if not isinstance(value, str):
|
|
15
|
+
error_prefix = self._error_prefix(instance)
|
|
16
|
+
raise TypeError(
|
|
17
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a string parameter"
|
|
18
|
+
)
|
|
19
|
+
if value not in self.allowed_values:
|
|
20
|
+
error_prefix = self._error_prefix(instance)
|
|
21
|
+
raise ValueError(
|
|
22
|
+
f"{error_prefix} attempt to set a value of type '{value}' which is not in the list of allowed values. It must be one of '" # type: ignore
|
|
23
|
+
+ "', '".join(self.allowed_values)
|
|
24
|
+
+ "'"
|
|
25
|
+
)
|
|
26
|
+
instance._set_config(self, value)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from clearskies.configs import config
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SelectList(config.Config):
|
|
5
|
+
"""
|
|
6
|
+
This is for a configuration that should be a list of strings matching some list of allowed values.
|
|
7
|
+
|
|
8
|
+
The allowed values are set when you create the config, and when values are set for the config
|
|
9
|
+
they must match.
|
|
10
|
+
|
|
11
|
+
This is different than StringList, which also accepts a list of any strings.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, allowed_values: list[str], required=False, default=None):
|
|
15
|
+
self.allowed_values = allowed_values
|
|
16
|
+
self.required = required
|
|
17
|
+
self.default = default
|
|
18
|
+
|
|
19
|
+
def __set__(self, instance, value: list[str]):
|
|
20
|
+
if value is None:
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
if not isinstance(value, list):
|
|
24
|
+
error_prefix = self._error_prefix(instance)
|
|
25
|
+
raise TypeError(
|
|
26
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a list parameter"
|
|
27
|
+
)
|
|
28
|
+
for index, item in enumerate(value):
|
|
29
|
+
if not isinstance(item, str):
|
|
30
|
+
error_prefix = self._error_prefix(instance)
|
|
31
|
+
raise TypeError(
|
|
32
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' for item #{index + 1}. A string was expected."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if item not in self.allowed_values:
|
|
36
|
+
error_prefix = self._error_prefix(instance)
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"{error_prefix} attempt to set a value of '{item}' for item #{index + 1}. This is not in the list of allowed values. It must be one of '"
|
|
39
|
+
+ "', '".join(self.allowed_values)
|
|
40
|
+
+ "'"
|
|
41
|
+
)
|
|
42
|
+
instance._set_config(self, [*value])
|
|
43
|
+
|
|
44
|
+
def __get__(self, instance, parent) -> list[str]:
|
|
45
|
+
if not instance:
|
|
46
|
+
return self # type: ignore
|
|
47
|
+
return instance._get_config(self)
|