clear-skies 2.0.3__py3-none-any.whl → 2.0.5__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/METADATA +74 -0
- clear_skies-2.0.5.dist-info/RECORD +4 -0
- {clear_skies-2.0.3.dist-info → clear_skies-2.0.5.dist-info}/WHEEL +1 -1
- clear_skies-2.0.3.dist-info/METADATA +0 -46
- clear_skies-2.0.3.dist-info/RECORD +0 -249
- clearskies/__init__.py +0 -59
- clearskies/action.py +0 -7
- clearskies/authentication/__init__.py +0 -15
- clearskies/authentication/authentication.py +0 -46
- clearskies/authentication/authorization.py +0 -16
- clearskies/authentication/authorization_pass_through.py +0 -20
- clearskies/authentication/jwks.py +0 -163
- clearskies/authentication/public.py +0 -5
- clearskies/authentication/secret_bearer.py +0 -553
- clearskies/autodoc/__init__.py +0 -8
- clearskies/autodoc/formats/__init__.py +0 -5
- clearskies/autodoc/formats/oai3_json/__init__.py +0 -7
- clearskies/autodoc/formats/oai3_json/oai3_json.py +0 -87
- clearskies/autodoc/formats/oai3_json/oai3_schema_resolver.py +0 -15
- clearskies/autodoc/formats/oai3_json/parameter.py +0 -35
- clearskies/autodoc/formats/oai3_json/request.py +0 -68
- clearskies/autodoc/formats/oai3_json/response.py +0 -28
- clearskies/autodoc/formats/oai3_json/schema/__init__.py +0 -11
- clearskies/autodoc/formats/oai3_json/schema/array.py +0 -9
- clearskies/autodoc/formats/oai3_json/schema/default.py +0 -13
- clearskies/autodoc/formats/oai3_json/schema/enum.py +0 -7
- clearskies/autodoc/formats/oai3_json/schema/object.py +0 -29
- clearskies/autodoc/formats/oai3_json/test.json +0 -1985
- clearskies/autodoc/py.typed +0 -0
- clearskies/autodoc/request/__init__.py +0 -15
- clearskies/autodoc/request/header.py +0 -6
- clearskies/autodoc/request/json_body.py +0 -6
- clearskies/autodoc/request/parameter.py +0 -8
- clearskies/autodoc/request/request.py +0 -38
- clearskies/autodoc/request/url_parameter.py +0 -6
- clearskies/autodoc/request/url_path.py +0 -6
- clearskies/autodoc/response/__init__.py +0 -5
- clearskies/autodoc/response/response.py +0 -9
- clearskies/autodoc/schema/__init__.py +0 -31
- clearskies/autodoc/schema/array.py +0 -10
- clearskies/autodoc/schema/base64.py +0 -8
- clearskies/autodoc/schema/boolean.py +0 -5
- clearskies/autodoc/schema/date.py +0 -5
- clearskies/autodoc/schema/datetime.py +0 -5
- clearskies/autodoc/schema/double.py +0 -5
- clearskies/autodoc/schema/enum.py +0 -17
- clearskies/autodoc/schema/integer.py +0 -6
- clearskies/autodoc/schema/long.py +0 -5
- clearskies/autodoc/schema/number.py +0 -6
- clearskies/autodoc/schema/object.py +0 -13
- clearskies/autodoc/schema/password.py +0 -5
- clearskies/autodoc/schema/schema.py +0 -11
- clearskies/autodoc/schema/string.py +0 -5
- clearskies/backends/__init__.py +0 -65
- clearskies/backends/api_backend.py +0 -1178
- clearskies/backends/backend.py +0 -136
- clearskies/backends/cursor_backend.py +0 -335
- clearskies/backends/memory_backend.py +0 -797
- clearskies/backends/secrets_backend.py +0 -106
- clearskies/column.py +0 -1233
- clearskies/columns/__init__.py +0 -71
- clearskies/columns/audit.py +0 -206
- clearskies/columns/belongs_to_id.py +0 -483
- clearskies/columns/belongs_to_model.py +0 -132
- clearskies/columns/belongs_to_self.py +0 -105
- clearskies/columns/boolean.py +0 -113
- clearskies/columns/category_tree.py +0 -275
- clearskies/columns/category_tree_ancestors.py +0 -51
- clearskies/columns/category_tree_children.py +0 -127
- clearskies/columns/category_tree_descendants.py +0 -48
- clearskies/columns/created.py +0 -95
- clearskies/columns/created_by_authorization_data.py +0 -116
- clearskies/columns/created_by_header.py +0 -99
- clearskies/columns/created_by_ip.py +0 -92
- clearskies/columns/created_by_routing_data.py +0 -97
- clearskies/columns/created_by_user_agent.py +0 -92
- clearskies/columns/date.py +0 -234
- clearskies/columns/datetime.py +0 -282
- clearskies/columns/email.py +0 -76
- clearskies/columns/float.py +0 -153
- clearskies/columns/has_many.py +0 -505
- clearskies/columns/has_many_self.py +0 -56
- clearskies/columns/has_one.py +0 -14
- clearskies/columns/integer.py +0 -160
- clearskies/columns/json.py +0 -126
- clearskies/columns/many_to_many_ids.py +0 -337
- clearskies/columns/many_to_many_ids_with_data.py +0 -274
- clearskies/columns/many_to_many_models.py +0 -158
- clearskies/columns/many_to_many_pivots.py +0 -134
- clearskies/columns/phone.py +0 -159
- clearskies/columns/select.py +0 -92
- clearskies/columns/string.py +0 -102
- clearskies/columns/timestamp.py +0 -164
- clearskies/columns/updated.py +0 -110
- clearskies/columns/uuid.py +0 -86
- clearskies/configs/README.md +0 -105
- clearskies/configs/__init__.py +0 -162
- clearskies/configs/actions.py +0 -43
- clearskies/configs/any.py +0 -13
- clearskies/configs/any_dict.py +0 -22
- clearskies/configs/any_dict_or_callable.py +0 -23
- clearskies/configs/authentication.py +0 -23
- clearskies/configs/authorization.py +0 -23
- clearskies/configs/boolean.py +0 -16
- clearskies/configs/boolean_or_callable.py +0 -18
- clearskies/configs/callable_config.py +0 -18
- clearskies/configs/columns.py +0 -34
- clearskies/configs/conditions.py +0 -30
- clearskies/configs/config.py +0 -24
- clearskies/configs/datetime.py +0 -18
- clearskies/configs/datetime_or_callable.py +0 -19
- clearskies/configs/endpoint.py +0 -23
- clearskies/configs/endpoint_list.py +0 -28
- clearskies/configs/float.py +0 -16
- clearskies/configs/float_or_callable.py +0 -18
- clearskies/configs/integer.py +0 -16
- clearskies/configs/integer_or_callable.py +0 -18
- clearskies/configs/joins.py +0 -30
- clearskies/configs/list_any_dict.py +0 -30
- clearskies/configs/list_any_dict_or_callable.py +0 -31
- clearskies/configs/model_class.py +0 -35
- clearskies/configs/model_column.py +0 -65
- clearskies/configs/model_columns.py +0 -56
- clearskies/configs/model_destination_name.py +0 -25
- clearskies/configs/model_to_id_column.py +0 -43
- clearskies/configs/readable_model_column.py +0 -9
- clearskies/configs/readable_model_columns.py +0 -9
- clearskies/configs/schema.py +0 -23
- clearskies/configs/searchable_model_columns.py +0 -9
- clearskies/configs/security_headers.py +0 -39
- clearskies/configs/select.py +0 -26
- clearskies/configs/select_list.py +0 -47
- clearskies/configs/string.py +0 -29
- clearskies/configs/string_dict.py +0 -32
- clearskies/configs/string_list.py +0 -32
- clearskies/configs/string_list_or_callable.py +0 -35
- clearskies/configs/string_or_callable.py +0 -18
- clearskies/configs/timedelta.py +0 -18
- clearskies/configs/timezone.py +0 -18
- clearskies/configs/url.py +0 -23
- clearskies/configs/validators.py +0 -45
- clearskies/configs/writeable_model_column.py +0 -9
- clearskies/configs/writeable_model_columns.py +0 -9
- clearskies/configurable.py +0 -76
- clearskies/contexts/__init__.py +0 -11
- clearskies/contexts/cli.py +0 -117
- clearskies/contexts/context.py +0 -98
- clearskies/contexts/wsgi.py +0 -76
- clearskies/contexts/wsgi_ref.py +0 -82
- clearskies/decorators.py +0 -33
- clearskies/di/__init__.py +0 -14
- clearskies/di/additional_config.py +0 -130
- clearskies/di/additional_config_auto_import.py +0 -17
- clearskies/di/di.py +0 -968
- clearskies/di/inject/__init__.py +0 -23
- clearskies/di/inject/by_class.py +0 -21
- clearskies/di/inject/by_name.py +0 -18
- clearskies/di/inject/di.py +0 -13
- clearskies/di/inject/environment.py +0 -14
- clearskies/di/inject/input_output.py +0 -20
- clearskies/di/inject/now.py +0 -13
- clearskies/di/inject/requests.py +0 -13
- clearskies/di/inject/secrets.py +0 -14
- clearskies/di/inject/utcnow.py +0 -13
- clearskies/di/inject/uuid.py +0 -15
- clearskies/di/injectable.py +0 -29
- clearskies/di/injectable_properties.py +0 -131
- clearskies/di/test_module/__init__.py +0 -6
- clearskies/di/test_module/another_module/__init__.py +0 -2
- clearskies/di/test_module/module_class.py +0 -5
- clearskies/end.py +0 -183
- clearskies/endpoint.py +0 -1310
- clearskies/endpoint_group.py +0 -310
- clearskies/endpoints/__init__.py +0 -23
- clearskies/endpoints/advanced_search.py +0 -526
- clearskies/endpoints/callable.py +0 -388
- clearskies/endpoints/create.py +0 -202
- clearskies/endpoints/delete.py +0 -139
- clearskies/endpoints/get.py +0 -275
- clearskies/endpoints/health_check.py +0 -181
- clearskies/endpoints/list.py +0 -573
- clearskies/endpoints/restful_api.py +0 -427
- clearskies/endpoints/simple_search.py +0 -286
- clearskies/endpoints/update.py +0 -190
- clearskies/environment.py +0 -104
- clearskies/exceptions/__init__.py +0 -17
- clearskies/exceptions/authentication.py +0 -2
- clearskies/exceptions/authorization.py +0 -2
- clearskies/exceptions/client_error.py +0 -2
- clearskies/exceptions/input_errors.py +0 -4
- clearskies/exceptions/moved_permanently.py +0 -3
- clearskies/exceptions/moved_temporarily.py +0 -3
- clearskies/exceptions/not_found.py +0 -2
- clearskies/functional/__init__.py +0 -7
- clearskies/functional/routing.py +0 -92
- clearskies/functional/string.py +0 -112
- clearskies/functional/validations.py +0 -76
- clearskies/input_outputs/__init__.py +0 -13
- clearskies/input_outputs/cli.py +0 -171
- clearskies/input_outputs/exceptions/__init__.py +0 -2
- clearskies/input_outputs/exceptions/cli_input_error.py +0 -2
- clearskies/input_outputs/exceptions/cli_not_found.py +0 -2
- clearskies/input_outputs/headers.py +0 -45
- clearskies/input_outputs/input_output.py +0 -138
- clearskies/input_outputs/programmatic.py +0 -69
- clearskies/input_outputs/py.typed +0 -0
- clearskies/input_outputs/wsgi.py +0 -77
- clearskies/model.py +0 -1922
- clearskies/py.typed +0 -0
- clearskies/query/__init__.py +0 -12
- clearskies/query/condition.py +0 -223
- clearskies/query/join.py +0 -136
- clearskies/query/query.py +0 -196
- clearskies/query/sort.py +0 -27
- clearskies/schema.py +0 -82
- clearskies/secrets/__init__.py +0 -6
- clearskies/secrets/additional_configs/__init__.py +0 -32
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +0 -61
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +0 -160
- clearskies/secrets/akeyless.py +0 -182
- clearskies/secrets/exceptions/__init__.py +0 -1
- clearskies/secrets/exceptions/not_found.py +0 -2
- clearskies/secrets/secrets.py +0 -38
- clearskies/security_header.py +0 -15
- clearskies/security_headers/__init__.py +0 -11
- clearskies/security_headers/cache_control.py +0 -67
- clearskies/security_headers/cors.py +0 -50
- clearskies/security_headers/csp.py +0 -94
- clearskies/security_headers/hsts.py +0 -22
- clearskies/security_headers/x_content_type_options.py +0 -0
- clearskies/security_headers/x_frame_options.py +0 -0
- clearskies/test_base.py +0 -8
- clearskies/typing.py +0 -11
- clearskies/validator.py +0 -37
- clearskies/validators/__init__.py +0 -33
- clearskies/validators/after_column.py +0 -62
- clearskies/validators/before_column.py +0 -13
- clearskies/validators/in_the_future.py +0 -32
- clearskies/validators/in_the_future_at_least.py +0 -11
- clearskies/validators/in_the_future_at_most.py +0 -10
- clearskies/validators/in_the_past.py +0 -32
- clearskies/validators/in_the_past_at_least.py +0 -10
- clearskies/validators/in_the_past_at_most.py +0 -10
- clearskies/validators/maximum_length.py +0 -26
- clearskies/validators/maximum_value.py +0 -29
- clearskies/validators/minimum_length.py +0 -26
- clearskies/validators/minimum_value.py +0 -29
- clearskies/validators/required.py +0 -34
- clearskies/validators/timedelta.py +0 -59
- clearskies/validators/unique.py +0 -30
- {clear_skies-2.0.3.dist-info → clear_skies-2.0.5.dist-info/licenses}/LICENSE +0 -0
clearskies/di/inject/__init__.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
from clearskies.di.inject.by_class import ByClass
|
|
2
|
-
from clearskies.di.inject.by_name import ByName
|
|
3
|
-
from clearskies.di.inject.di import Di
|
|
4
|
-
from clearskies.di.inject.environment import Environment
|
|
5
|
-
from clearskies.di.inject.input_output import InputOutput
|
|
6
|
-
from clearskies.di.inject.now import Now
|
|
7
|
-
from clearskies.di.inject.requests import Requests
|
|
8
|
-
from clearskies.di.inject.secrets import Secrets
|
|
9
|
-
from clearskies.di.inject.utcnow import Utcnow
|
|
10
|
-
from clearskies.di.inject.uuid import Uuid
|
|
11
|
-
|
|
12
|
-
__all__ = [
|
|
13
|
-
"ByClass",
|
|
14
|
-
"ByName",
|
|
15
|
-
"Di",
|
|
16
|
-
"Environment",
|
|
17
|
-
"InputOutput",
|
|
18
|
-
"Now",
|
|
19
|
-
"Requests",
|
|
20
|
-
"Secrets",
|
|
21
|
-
"Utcnow",
|
|
22
|
-
"Uuid",
|
|
23
|
-
]
|
clearskies/di/inject/by_class.py
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from clearskies.di.injectable import Injectable
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ByClass(Injectable):
|
|
7
|
-
def __init__(self, cls: type, cache: bool = True):
|
|
8
|
-
if not isinstance(cls, type):
|
|
9
|
-
raise TypeError(
|
|
10
|
-
f"I expected a class for the first argument to clearskies.di.inject.ByClass, but I received an object of type '{cls.__class__.__name__}' instead."
|
|
11
|
-
)
|
|
12
|
-
self.cls = cls
|
|
13
|
-
self.cache = cache
|
|
14
|
-
|
|
15
|
-
def __get__(self, instance, parent) -> Any:
|
|
16
|
-
if instance is None:
|
|
17
|
-
return self # type: ignore
|
|
18
|
-
|
|
19
|
-
if self.cls in self._di._class_overrides_by_class:
|
|
20
|
-
return self._di.build_class(self._di._class_overrides_by_class[self.cls], cache=self.cache)
|
|
21
|
-
return self._di.build_class(self.cls, cache=self.cache)
|
clearskies/di/inject/by_name.py
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from clearskies.di.injectable import Injectable
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ByName(Injectable):
|
|
7
|
-
def __init__(self, name: str, cache: bool = True):
|
|
8
|
-
if not isinstance(name, str):
|
|
9
|
-
raise TypeError(
|
|
10
|
-
f"I expected a string for the first argument to clearskies.di.inject.ByName, but I received an object of type '{name.__class__.__name__}' instead."
|
|
11
|
-
)
|
|
12
|
-
self.name = name
|
|
13
|
-
self.cache = cache
|
|
14
|
-
|
|
15
|
-
def __get__(self, instance, parent) -> Any:
|
|
16
|
-
if instance is None:
|
|
17
|
-
return self # type: ignore
|
|
18
|
-
return self._di.build_from_name(self.name, cache=self.cache)
|
clearskies/di/inject/di.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
|
|
3
|
-
from clearskies.di.injectable import Injectable
|
|
4
|
-
from clearskies.environment import Environment as EnvironmentDependency
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Environment(Injectable):
|
|
8
|
-
def __init__(self, cache: bool = True):
|
|
9
|
-
self.cache = cache
|
|
10
|
-
|
|
11
|
-
def __get__(self, instance, parent) -> EnvironmentDependency:
|
|
12
|
-
if instance is None:
|
|
13
|
-
return self # type: ignore
|
|
14
|
-
return self._di.build_from_name("environment", cache=self.cache)
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
import requests
|
|
6
|
-
|
|
7
|
-
from clearskies.di.injectable import Injectable
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from clearskies.input_outputs.input_output import InputOutput as InputOuputDependency
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class InputOutput(Injectable):
|
|
14
|
-
def __init__(self):
|
|
15
|
-
pass
|
|
16
|
-
|
|
17
|
-
def __get__(self, instance, parent) -> InputOuputDependency:
|
|
18
|
-
if instance is None:
|
|
19
|
-
return self # type: ignore
|
|
20
|
-
return self._di.build_from_name("input_output", cache=True)
|
clearskies/di/inject/now.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
|
|
3
|
-
from clearskies.di.injectable import Injectable
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Now(Injectable):
|
|
7
|
-
def __init__(self, cache: bool = False):
|
|
8
|
-
self.cache = cache
|
|
9
|
-
|
|
10
|
-
def __get__(self, instance, parent) -> datetime.datetime:
|
|
11
|
-
if instance is None:
|
|
12
|
-
return self # type: ignore
|
|
13
|
-
return self._di.build_from_name("now", cache=self.cache)
|
clearskies/di/inject/requests.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
|
|
3
|
-
from clearskies.di.injectable import Injectable
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Requests(Injectable):
|
|
7
|
-
def __init__(self, cache: bool = True):
|
|
8
|
-
self.cache = cache
|
|
9
|
-
|
|
10
|
-
def __get__(self, instance, parent) -> requests.Session:
|
|
11
|
-
if instance is None:
|
|
12
|
-
return self # type: ignore
|
|
13
|
-
return self._di.build_from_name("requests", cache=self.cache)
|
clearskies/di/inject/secrets.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
|
|
3
|
-
from clearskies.di.injectable import Injectable
|
|
4
|
-
from clearskies.secrets import Secrets as SecretsHelper
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Secrets(Injectable):
|
|
8
|
-
def __init__(self, cache: bool = True):
|
|
9
|
-
self.cache = cache
|
|
10
|
-
|
|
11
|
-
def __get__(self, instance, parent) -> SecretsHelper:
|
|
12
|
-
if instance is None:
|
|
13
|
-
return self # type: ignore
|
|
14
|
-
return self._di.build_from_name("secrets", cache=self.cache)
|
clearskies/di/inject/utcnow.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
|
|
3
|
-
from clearskies.di.injectable import Injectable
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class Utcnow(Injectable):
|
|
7
|
-
def __init__(self, cache: bool = False):
|
|
8
|
-
self.cache = cache
|
|
9
|
-
|
|
10
|
-
def __get__(self, instance, parent) -> datetime.datetime:
|
|
11
|
-
if instance is None:
|
|
12
|
-
return self # type: ignore
|
|
13
|
-
return self._di.build_from_name("utcnow", cache=self.cache)
|
clearskies/di/inject/uuid.py
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import types
|
|
3
|
-
import uuid
|
|
4
|
-
|
|
5
|
-
from clearskies.di.injectable import Injectable
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Uuid(Injectable):
|
|
9
|
-
def __init__(self, cache: bool = True):
|
|
10
|
-
self.cache = cache
|
|
11
|
-
|
|
12
|
-
def __get__(self, instance, parent) -> types.ModuleType:
|
|
13
|
-
if instance is None:
|
|
14
|
-
return self # type: ignore
|
|
15
|
-
return self._di.build_from_name("uuid", cache=self.cache) # type: ignore
|
clearskies/di/injectable.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Injectable(ABC):
|
|
6
|
-
_di: Any = None
|
|
7
|
-
|
|
8
|
-
def initiated_guard(self, instance):
|
|
9
|
-
if self._di:
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
-
reference = instance.__class__.__name__ + "."
|
|
13
|
-
my_id = id(self)
|
|
14
|
-
cls = instance.__class__
|
|
15
|
-
for attribute_name in dir(instance):
|
|
16
|
-
if id(getattr(cls, attribute_name)) != my_id:
|
|
17
|
-
continue
|
|
18
|
-
reference += attribute_name
|
|
19
|
-
raise ValueError(
|
|
20
|
-
f"There was an attempt to get a value out of '{reference}' but the injectable hasn't been properly"
|
|
21
|
-
+ "initialized. This usually means that objects are being created outside of the normal Di system."
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
def set_di(self, di):
|
|
25
|
-
self._di = di
|
|
26
|
-
|
|
27
|
-
@abstractmethod
|
|
28
|
-
def __get__(self, instance, parent):
|
|
29
|
-
pass
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Self
|
|
4
|
-
|
|
5
|
-
from clearskies.di.injectable import Injectable
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from clearskies.di import Di
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class InjectableProperties:
|
|
12
|
-
"""
|
|
13
|
-
Fetch dependencies via properties rather than constructor arguments.
|
|
14
|
-
|
|
15
|
-
This class allows you to specify dependencies by setting them as class properties instead of constructor
|
|
16
|
-
arguments. This is common in clearskies as it helps make easily reusable classes - configuration can
|
|
17
|
-
go in the constructor of the class, allowing the developer to directly instantiate it, and then the DI system
|
|
18
|
-
will come by afterwards and provide the necessary dependencies.
|
|
19
|
-
|
|
20
|
-
After adding InjectableProperties as a parent of your class, you have two ways to specify your dependencies:
|
|
21
|
-
|
|
22
|
-
1. By using the classes in the `clearskies.di.inject.*`module.
|
|
23
|
-
2. By directly attaching objects which also use the `InjectableProperties` class.
|
|
24
|
-
|
|
25
|
-
The following table shows the dependencies that can be injected as properties via the clearskies.di.inject module:
|
|
26
|
-
|
|
27
|
-
| Class | Type | Result |
|
|
28
|
-
|----------------------------------|--------------------------------------|-------------------------------------------------|
|
|
29
|
-
| clearskies.di.inject.ByClass | N/A | The specified class will be built |
|
|
30
|
-
| clearskies.di.inject.ByName | N/A | The specified dependnecy name will be built |
|
|
31
|
-
| clearskies.di.inject.Cursor | N/A | The PyMySQL cursor |
|
|
32
|
-
| clearskies.di.inject.Di | N/A | The dependency injection container itself |
|
|
33
|
-
| clearskies.di.inject.Environment | clearskies.Environment | The environment helper |
|
|
34
|
-
| clearskies.di.inject.InputOutput | clearskies.input_outputs.InputOutput | The InputOutput object for the current request |
|
|
35
|
-
| clearskies.di.inject.Now | datetime.datetime | The current time (no timezone) |
|
|
36
|
-
| clearskies.di.inject.Requests | requests.Session | A requests session |
|
|
37
|
-
| clearskies.di.inject.Utcnow | datetime.datetime | The current time (tzinfo=datetime.timezone.utc) |
|
|
38
|
-
|
|
39
|
-
Note: now/utcnow are not cached, so you'll get the current time everytime you get a value out of the class property,
|
|
40
|
-
unless a specific time has been set on the dependency injection container.
|
|
41
|
-
|
|
42
|
-
Here's an example:
|
|
43
|
-
|
|
44
|
-
```python
|
|
45
|
-
import clearskies
|
|
46
|
-
import time
|
|
47
|
-
import clearskies.decorators
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class MyOtherThing(clearskies.di.InjectableProperties):
|
|
51
|
-
now = clearskies.di.inject.Now()
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class ReusableClass(clearskies.Configurable, clearskies.di.InjectableProperties):
|
|
55
|
-
my_int = clearskies.configs.Integer(required=True)
|
|
56
|
-
some_number = clearskies.di.inject.ByName("some_number")
|
|
57
|
-
my_other_thing = clearskies.di.inject.ByClass(MyOtherThing)
|
|
58
|
-
|
|
59
|
-
@clearskies.decorators.parameters_to_properties
|
|
60
|
-
def __init__(self, my_int: int):
|
|
61
|
-
self.finalize_and_validate_configuration()
|
|
62
|
-
|
|
63
|
-
def my_value(self) -> int:
|
|
64
|
-
return self.my_int * self.some_number
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class MyClass(clearskies.di.InjectableProperties):
|
|
68
|
-
reusable = ReusableClass(5)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class MyOtherClass(clearskies.di.InjectableProperties):
|
|
72
|
-
reusable = ReusableClass(10)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
di = clearskies.di.Di(
|
|
76
|
-
bindings={
|
|
77
|
-
"some_number": 10,
|
|
78
|
-
}
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
my_class = di.build(MyClass)
|
|
82
|
-
print(my_class.reusable.my_value()) # prints 50
|
|
83
|
-
|
|
84
|
-
my_other_class = di.build(MyOtherClass)
|
|
85
|
-
print(my_other_class.reusable.my_value()) # prints 100
|
|
86
|
-
|
|
87
|
-
start = my_class.reusable.my_other_thing.now
|
|
88
|
-
time.sleep(1)
|
|
89
|
-
stop = my_class.reusable.my_other_thing.now
|
|
90
|
-
print((stop - start).seconds) # prints 1
|
|
91
|
-
```
|
|
92
|
-
"""
|
|
93
|
-
|
|
94
|
-
_injectables_loaded: str = ""
|
|
95
|
-
|
|
96
|
-
@classmethod
|
|
97
|
-
def injectable_properties(cls, di: Di):
|
|
98
|
-
# you would think that I would be able to just use a simple true/false flag attached to the class,
|
|
99
|
-
# but I'm having this weird issue where (when I tried that) the flag was being shared between classes.
|
|
100
|
-
# It shouldn't happen like that, but it is, so there is probably something subtle going on that I
|
|
101
|
-
# haven't figured out yet, but this also works identitally, so :shrug:.
|
|
102
|
-
# Also, keep track of the id of DI. We use class level caching but tests often use multiple DI containers
|
|
103
|
-
# in one run, which means that we need to re-inject dependencies if we get a new DI container
|
|
104
|
-
cache_name = str(cls) + str(id(di))
|
|
105
|
-
if cache_name == cls._injectables_loaded:
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
injectable_descriptors: list[Any] = []
|
|
109
|
-
injectable_properties: list[Self] = []
|
|
110
|
-
for attribute_name in dir(cls):
|
|
111
|
-
# Per the docs above, we want to inject properties for one of two things: the injectables from clearskies.di.inject,
|
|
112
|
-
# and any object that itself extends this class. This is mildly tricky because the injectables are descriptors, and
|
|
113
|
-
# so we get them using getattr on the class, while if it's not a descriptor, then we want to use getattr on self.
|
|
114
|
-
# The important part here is that we modify descriptors at the class level, so the actual injected values have to
|
|
115
|
-
# be stored in self, and not in the descriptor object. When it's not a descriptor, then we can modify the object
|
|
116
|
-
# directly (since we're operating at the object level, not class level). Either way, while we go, let's keep track
|
|
117
|
-
# of what our dependencies are and which ones are cached, so we only have to list the objects attributes the first time.
|
|
118
|
-
attribute = getattr(cls, attribute_name)
|
|
119
|
-
|
|
120
|
-
if di.has_class_override(attribute.__class__):
|
|
121
|
-
setattr(cls, attribute_name, di.get_override_by_class(attribute))
|
|
122
|
-
continue
|
|
123
|
-
|
|
124
|
-
if issubclass(attribute.__class__, Injectable):
|
|
125
|
-
attribute.set_di(di)
|
|
126
|
-
continue
|
|
127
|
-
|
|
128
|
-
if hasattr(attribute, "injectable_properties"):
|
|
129
|
-
attribute.injectable_properties(di)
|
|
130
|
-
|
|
131
|
-
cls._injectables_loaded = cache_name
|
clearskies/end.py
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
# type: ignore
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
from abc import ABC
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from clearskies.input_output import InputOutput
|
|
9
|
-
|
|
10
|
-
from clearskies import exceptions
|
|
11
|
-
from clearskies.functional import string
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class End(ABC):
|
|
15
|
-
"""
|
|
16
|
-
DRY for endpoint and endpoint groups.
|
|
17
|
-
|
|
18
|
-
This class is just here to hold some common functionality between Endpoints and EndpointGroups.
|
|
19
|
-
The two classes have plenty of overlap but are different enough that I don't want either to inherit
|
|
20
|
-
from the other.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
def add_url_prefix(self, prefix: str) -> None:
|
|
24
|
-
self.url = (prefix.rstrip("/") + "/" + self.url.lstrip("/")).lstrip("/")
|
|
25
|
-
|
|
26
|
-
def top_level_authentication_and_authorization(self, input_output: InputOutput) -> None:
|
|
27
|
-
"""
|
|
28
|
-
Handle authentication and authorization for this endpoint.
|
|
29
|
-
|
|
30
|
-
In the event of an AuthN/AuthZ issue, raise an exception. Otherwise, return None
|
|
31
|
-
"""
|
|
32
|
-
if not self.authentication:
|
|
33
|
-
return
|
|
34
|
-
try:
|
|
35
|
-
if not self.authentication.authenticate(input_output):
|
|
36
|
-
raise exceptions.Authentication("Not Authenticated")
|
|
37
|
-
except exceptions.ClientError as client_error:
|
|
38
|
-
raise exceptions.Authentication(str(client_error))
|
|
39
|
-
if self.authorization:
|
|
40
|
-
try:
|
|
41
|
-
if not self.authorization.gate(input_output.authorization_data, input_output):
|
|
42
|
-
raise exceptions.Authorization("Not Authorized")
|
|
43
|
-
except exceptions.ClientError as client_error:
|
|
44
|
-
raise exception.Authorization(str(client_error))
|
|
45
|
-
|
|
46
|
-
def __call__(self, input_output: InputOutput) -> Any:
|
|
47
|
-
"""
|
|
48
|
-
Execute the endpoint.
|
|
49
|
-
|
|
50
|
-
This function mostly just checks AuthN/AuthZ and then passes along control to the handle method.
|
|
51
|
-
It also checks for all the appropriate exceptions from clearskies.exceptions and turns those into the
|
|
52
|
-
expected response. As a result, when building a new endpoint, you normally modify the handle method
|
|
53
|
-
rather than this one.
|
|
54
|
-
"""
|
|
55
|
-
# these two configs can have arbitrary classes attached, which may use injectable properties. Because they are
|
|
56
|
-
# hiding in configs, the system for automatically discovering these won't work, so we have to manually check them.
|
|
57
|
-
# We can't do this in the constructor because self.di hasn't been populated yet, and we can't do this in
|
|
58
|
-
# our own injectable_properties class method because we need to operate at the instance level
|
|
59
|
-
for config_name in ["authentication", "authorization"]:
|
|
60
|
-
config = getattr(self, config_name)
|
|
61
|
-
if config and hasattr(config, "injectable_properties"):
|
|
62
|
-
config.injectable_properties(self.di)
|
|
63
|
-
|
|
64
|
-
response = self.populate_routing_data(input_output)
|
|
65
|
-
if response:
|
|
66
|
-
return response
|
|
67
|
-
|
|
68
|
-
self.di.add_binding("input_output", input_output)
|
|
69
|
-
|
|
70
|
-
# catch everything when we do an AuthN/AuthZ check because we allow custom-defined classes,
|
|
71
|
-
# and this gives more flexibility (or possibly forgiveness) for how they raise exceptions.
|
|
72
|
-
try:
|
|
73
|
-
self.top_level_authentication_and_authorization(input_output)
|
|
74
|
-
except exceptions.Authentication as auth_error:
|
|
75
|
-
return self.error(input_output, str(auth_error), 401)
|
|
76
|
-
except exceptions.Authorization as auth_error:
|
|
77
|
-
return self.error(input_output, str(auth_error), 403)
|
|
78
|
-
except exceptions.NotFound as not_found:
|
|
79
|
-
return self.error(input_output, str(not_found), 404)
|
|
80
|
-
except exceptions.MovedPermanently as redirect:
|
|
81
|
-
return self.redirect(input_output, str(redirect), 302)
|
|
82
|
-
except exceptions.MovedTemporarily as redirect:
|
|
83
|
-
return self.redirect(input_output, str(redirect), 307)
|
|
84
|
-
|
|
85
|
-
try:
|
|
86
|
-
response = self.handle(input_output)
|
|
87
|
-
except exceptions.ClientError as client_error:
|
|
88
|
-
return self.error(input_output, str(client_error), 400)
|
|
89
|
-
except exceptions.InputErrors as input_errors:
|
|
90
|
-
return self.input_errors(input_output, input_errors.errors)
|
|
91
|
-
except exceptions.Authentication as auth_error:
|
|
92
|
-
return self.error(input_output, str(auth_error), 401)
|
|
93
|
-
except exceptions.Authorization as auth_error:
|
|
94
|
-
return self.error(input_output, str(auth_error), 403)
|
|
95
|
-
except exceptions.NotFound as auth_error:
|
|
96
|
-
return self.error(input_output, str(auth_error), 404)
|
|
97
|
-
except exceptions.MovedPermanently as redirect:
|
|
98
|
-
return self.redirect(input_output, str(redirect), 302)
|
|
99
|
-
except exceptions.MovedTemporarily as redirect:
|
|
100
|
-
return self.redirect(input_output, str(redirect), 307)
|
|
101
|
-
|
|
102
|
-
return response
|
|
103
|
-
|
|
104
|
-
def populate_routing_data(self, input_output: InputOutput) -> Any:
|
|
105
|
-
raise NotImplementedError()
|
|
106
|
-
|
|
107
|
-
def add_response_headers(self, input_output: InputOutput) -> None:
|
|
108
|
-
if self.response_headers:
|
|
109
|
-
if callable(self.response_headers):
|
|
110
|
-
response_headers = self.di.call_function(
|
|
111
|
-
self.response_headers, **input_output.get_context_for_callables()
|
|
112
|
-
)
|
|
113
|
-
else:
|
|
114
|
-
response_headers = self.response_headers
|
|
115
|
-
|
|
116
|
-
for index, response_header in enumerate(response_headers):
|
|
117
|
-
if not isinstance(response_header, str):
|
|
118
|
-
raise TypeError(
|
|
119
|
-
f"Invalid response header in entry #{index + 1}: the header should be a string, but I was given a type of '{header.__class__.__name__}' instead."
|
|
120
|
-
)
|
|
121
|
-
parts = response_header.split(":", 1)
|
|
122
|
-
if len(parts) != 2:
|
|
123
|
-
raise ValueError(
|
|
124
|
-
f"Invalid response header in entry #{index + 1}: the header should be a string in the form of 'key: value' but the given header did not have a colon to separate key and value."
|
|
125
|
-
)
|
|
126
|
-
input_output.response_headers.add(parts[0], parts[1])
|
|
127
|
-
for security_header in self.security_headers:
|
|
128
|
-
security_header.set_headers_for_input_output(input_output)
|
|
129
|
-
|
|
130
|
-
def respond(self, input_output: InputOutput, response: clearskies.typing.response, status_code: int) -> Any:
|
|
131
|
-
self.add_response_headers(input_output)
|
|
132
|
-
return input_output.respond(response, status_code)
|
|
133
|
-
|
|
134
|
-
def respond_json(self, input_output: InputOutput, response_data: dict[str, Any], status_code: int) -> Any:
|
|
135
|
-
if "content-type" not in input_output.response_headers:
|
|
136
|
-
input_output.response_headers.add("content-type", "application/json")
|
|
137
|
-
return self.respond(input_output, self.normalize_response(response_data), status_code)
|
|
138
|
-
|
|
139
|
-
def normalize_response(self, response_data: dict[str, Any]) -> dict[str, Any]:
|
|
140
|
-
if "status" not in response_data:
|
|
141
|
-
raise ValueError("Huh, status got left out somehow")
|
|
142
|
-
return {
|
|
143
|
-
self.auto_case_internal_column_name("status"): self.auto_case_internal_column_name(response_data["status"]),
|
|
144
|
-
self.auto_case_internal_column_name("error"): response_data.get("error", ""),
|
|
145
|
-
self.auto_case_internal_column_name("data"): response_data.get("data", []),
|
|
146
|
-
self.auto_case_internal_column_name("pagination"): self.normalize_pagination(
|
|
147
|
-
response_data.get("pagination", {})
|
|
148
|
-
),
|
|
149
|
-
self.auto_case_internal_column_name("input_errors"): response_data.get("input_errors", {}),
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
def normalize_pagination(self, pagination: dict[str, Any]) -> dict[str, Any]:
|
|
153
|
-
# pagination isn't always relevant so if it is completely empty then leave it that way
|
|
154
|
-
if not pagination:
|
|
155
|
-
return pagination
|
|
156
|
-
return {
|
|
157
|
-
self.auto_case_internal_column_name("number_results"): pagination.get("number_results", 0),
|
|
158
|
-
self.auto_case_internal_column_name("limit"): pagination.get("limit", 0),
|
|
159
|
-
self.auto_case_internal_column_name("next_page"): {
|
|
160
|
-
self.auto_case_internal_column_name(key): value
|
|
161
|
-
for (key, value) in pagination.get("next_page", {}).items()
|
|
162
|
-
},
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
def auto_case_internal_column_name(self, column_name: str) -> str:
|
|
166
|
-
if self.external_casing:
|
|
167
|
-
return string.swap_casing(column_name, "snake_case", self.external_casing)
|
|
168
|
-
return column_name
|
|
169
|
-
|
|
170
|
-
def auto_case_column_name(self, column_name: str, internal_to_external: bool) -> str:
|
|
171
|
-
if not self.internal_casing:
|
|
172
|
-
return column_name
|
|
173
|
-
if internal_to_external:
|
|
174
|
-
return string.swap_casing(
|
|
175
|
-
column_name,
|
|
176
|
-
self.internal_casing,
|
|
177
|
-
self.external_casing,
|
|
178
|
-
)
|
|
179
|
-
return string.swap_casing(
|
|
180
|
-
column_name,
|
|
181
|
-
self.external_casing,
|
|
182
|
-
self.internal_casing,
|
|
183
|
-
)
|