clear-skies 2.0.1__py3-none-any.whl → 2.0.3__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.1.dist-info → clear_skies-2.0.3.dist-info}/METADATA +1 -1
- {clear_skies-2.0.1.dist-info → clear_skies-2.0.3.dist-info}/RECORD +70 -70
- clearskies/__init__.py +5 -8
- clearskies/authentication/jwks.py +2 -2
- clearskies/authentication/secret_bearer.py +2 -2
- clearskies/backends/api_backend.py +2 -2
- clearskies/backends/secrets_backend.py +0 -1
- clearskies/column.py +6 -2
- clearskies/columns/audit.py +3 -2
- clearskies/columns/belongs_to_id.py +5 -5
- clearskies/columns/belongs_to_model.py +6 -2
- clearskies/columns/belongs_to_self.py +2 -2
- clearskies/columns/boolean.py +6 -2
- clearskies/columns/category_tree.py +2 -2
- clearskies/columns/category_tree_children.py +2 -2
- clearskies/columns/created.py +3 -2
- clearskies/columns/created_by_authorization_data.py +2 -2
- clearskies/columns/created_by_header.py +2 -2
- clearskies/columns/created_by_ip.py +2 -2
- clearskies/columns/created_by_routing_data.py +3 -2
- clearskies/columns/created_by_user_agent.py +2 -2
- clearskies/columns/date.py +6 -2
- clearskies/columns/datetime.py +6 -2
- clearskies/columns/float.py +6 -2
- clearskies/columns/has_many.py +2 -2
- clearskies/columns/has_many_self.py +2 -2
- clearskies/columns/integer.py +6 -2
- clearskies/columns/json.py +6 -2
- clearskies/columns/many_to_many_ids.py +6 -2
- clearskies/columns/many_to_many_ids_with_data.py +6 -2
- clearskies/columns/many_to_many_models.py +6 -2
- clearskies/columns/many_to_many_pivots.py +3 -2
- clearskies/columns/phone.py +3 -2
- clearskies/columns/select.py +3 -2
- clearskies/columns/string.py +4 -0
- clearskies/columns/timestamp.py +6 -2
- clearskies/columns/updated.py +2 -2
- clearskies/columns/uuid.py +2 -2
- clearskies/configs/__init__.py +1 -1
- clearskies/contexts/cli.py +106 -0
- clearskies/contexts/context.py +1 -0
- clearskies/contexts/wsgi.py +56 -0
- clearskies/contexts/wsgi_ref.py +29 -0
- clearskies/{parameters_to_properties.py → decorators.py} +2 -0
- clearskies/di/injectable_properties.py +2 -2
- clearskies/endpoint.py +4 -4
- clearskies/endpoint_group.py +1 -1
- clearskies/endpoints/callable.py +1 -1
- clearskies/endpoints/create.py +1 -1
- clearskies/endpoints/delete.py +1 -1
- clearskies/endpoints/get.py +1 -1
- clearskies/endpoints/health_check.py +1 -1
- clearskies/endpoints/list.py +1 -1
- clearskies/endpoints/restful_api.py +2 -2
- clearskies/endpoints/simple_search.py +1 -1
- clearskies/endpoints/update.py +1 -1
- clearskies/input_outputs/cli.py +5 -4
- clearskies/model.py +807 -43
- clearskies/security_headers/cache_control.py +2 -2
- clearskies/security_headers/cors.py +2 -2
- clearskies/security_headers/csp.py +2 -2
- clearskies/security_headers/hsts.py +2 -2
- clearskies/validators/after_column.py +2 -2
- clearskies/validators/in_the_future.py +1 -1
- clearskies/validators/in_the_past.py +1 -1
- clearskies/validators/required.py +0 -1
- clearskies/validators/timedelta.py +2 -2
- clearskies/validators/unique.py +0 -1
- {clear_skies-2.0.1.dist-info → clear_skies-2.0.3.dist-info}/LICENSE +0 -0
- {clear_skies-2.0.1.dist-info → clear_skies-2.0.3.dist-info}/WHEEL +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import clearskies.decorators
|
|
1
2
|
import clearskies.typing
|
|
2
|
-
from clearskies import configs
|
|
3
|
+
from clearskies import configs
|
|
3
4
|
from clearskies.columns.string import String
|
|
4
5
|
|
|
5
6
|
|
|
@@ -80,7 +81,7 @@ class CreatedByRoutingData(String):
|
|
|
80
81
|
|
|
81
82
|
_allowed_search_operators = ["=", "in", "is not null", "is null", "like"]
|
|
82
83
|
|
|
83
|
-
@
|
|
84
|
+
@clearskies.decorators.parameters_to_properties
|
|
84
85
|
def __init__(
|
|
85
86
|
self,
|
|
86
87
|
routing_path_name: str,
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import datetime
|
|
4
4
|
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
|
-
import clearskies.
|
|
6
|
+
import clearskies.decorators
|
|
7
7
|
import clearskies.typing
|
|
8
8
|
from clearskies import configs
|
|
9
9
|
from clearskies.columns.string import String
|
|
@@ -70,7 +70,7 @@ class CreatedByUserAgent(String):
|
|
|
70
70
|
|
|
71
71
|
_allowed_search_operators = ["=", "in", "is not null", "is null", "like"]
|
|
72
72
|
|
|
73
|
-
@clearskies.
|
|
73
|
+
@clearskies.decorators.parameters_to_properties
|
|
74
74
|
def __init__(
|
|
75
75
|
self,
|
|
76
76
|
is_readable: bool = True,
|
clearskies/columns/date.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Callable, Self, overload
|
|
|
5
5
|
|
|
6
6
|
import dateparser # type: ignore
|
|
7
7
|
|
|
8
|
-
import clearskies.
|
|
8
|
+
import clearskies.decorators
|
|
9
9
|
import clearskies.typing
|
|
10
10
|
from clearskies import configs
|
|
11
11
|
from clearskies.autodoc.schema import Datetime as AutoDocDatetime
|
|
@@ -102,7 +102,7 @@ class Date(Datetime):
|
|
|
102
102
|
auto_doc_class: type[AutoDocSchema] = AutoDocDatetime
|
|
103
103
|
_descriptor_config_map = None
|
|
104
104
|
|
|
105
|
-
@clearskies.
|
|
105
|
+
@clearskies.decorators.parameters_to_properties
|
|
106
106
|
def __init__(
|
|
107
107
|
self,
|
|
108
108
|
date_format: str = "%Y-%m-%d",
|
|
@@ -162,6 +162,10 @@ class Date(Datetime):
|
|
|
162
162
|
return super().__get__(instance, cls)
|
|
163
163
|
|
|
164
164
|
def __set__(self, instance, value: datetime.datetime | datetime.date) -> None:
|
|
165
|
+
# this makes sure we're initialized
|
|
166
|
+
if "name" not in self._config: # type: ignore
|
|
167
|
+
instance.get_columns()
|
|
168
|
+
|
|
165
169
|
instance._next_data[self.name] = value
|
|
166
170
|
|
|
167
171
|
def equals(self, value: str | datetime.datetime | datetime.date) -> Condition:
|
clearskies/columns/datetime.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Callable, Self, overload
|
|
|
5
5
|
|
|
6
6
|
import dateparser # type: ignore
|
|
7
7
|
|
|
8
|
-
import clearskies.
|
|
8
|
+
import clearskies.decorators
|
|
9
9
|
import clearskies.typing
|
|
10
10
|
from clearskies import configs
|
|
11
11
|
from clearskies.autodoc.schema import Datetime as AutoDocDatetime
|
|
@@ -127,7 +127,7 @@ class Datetime(Column):
|
|
|
127
127
|
auto_doc_class: type[AutoDocSchema] = AutoDocDatetime
|
|
128
128
|
_descriptor_config_map = None
|
|
129
129
|
|
|
130
|
-
@clearskies.
|
|
130
|
+
@clearskies.decorators.parameters_to_properties
|
|
131
131
|
def __init__(
|
|
132
132
|
self,
|
|
133
133
|
date_format: str = "%Y-%m-%d %H:%M:%S",
|
|
@@ -204,6 +204,10 @@ class Datetime(Column):
|
|
|
204
204
|
return super().__get__(instance, cls)
|
|
205
205
|
|
|
206
206
|
def __set__(self, instance, value: datetime.datetime) -> None:
|
|
207
|
+
# this makes sure we're initialized
|
|
208
|
+
if "name" not in self._config: # type: ignore
|
|
209
|
+
instance.get_columns()
|
|
210
|
+
|
|
207
211
|
instance._next_data[self.name] = value
|
|
208
212
|
|
|
209
213
|
def equals(self, value: str | datetime.datetime) -> Condition:
|
clearskies/columns/float.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Callable, Self, overload
|
|
4
4
|
|
|
5
|
-
import clearskies.
|
|
5
|
+
import clearskies.decorators
|
|
6
6
|
import clearskies.typing
|
|
7
7
|
from clearskies import configs
|
|
8
8
|
from clearskies.autodoc.schema import Number as AutoDocNumber
|
|
@@ -75,7 +75,7 @@ class Float(Column):
|
|
|
75
75
|
auto_doc_class: type[AutoDocSchema] = AutoDocNumber
|
|
76
76
|
_descriptor_config_map = None
|
|
77
77
|
|
|
78
|
-
@clearskies.
|
|
78
|
+
@clearskies.decorators.parameters_to_properties
|
|
79
79
|
def __init__(
|
|
80
80
|
self,
|
|
81
81
|
default: float | None = None,
|
|
@@ -106,6 +106,10 @@ class Float(Column):
|
|
|
106
106
|
return super().__get__(instance, cls)
|
|
107
107
|
|
|
108
108
|
def __set__(self, instance, value: float) -> None:
|
|
109
|
+
# this makes sure we're initialized
|
|
110
|
+
if "name" not in self._config: # type: ignore
|
|
111
|
+
instance.get_columns()
|
|
112
|
+
|
|
109
113
|
instance._next_data[self.name] = float(value)
|
|
110
114
|
|
|
111
115
|
def from_backend(self, value) -> float:
|
clearskies/columns/has_many.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any, Self, overload
|
|
4
4
|
|
|
5
|
-
import clearskies.
|
|
5
|
+
import clearskies.decorators
|
|
6
6
|
import clearskies.typing
|
|
7
7
|
from clearskies import configs
|
|
8
8
|
from clearskies.autodoc.schema import Array as AutoDocArray
|
|
@@ -382,7 +382,7 @@ class HasMany(Column):
|
|
|
382
382
|
|
|
383
383
|
input_output = InputOutput()
|
|
384
384
|
|
|
385
|
-
@clearskies.
|
|
385
|
+
@clearskies.decorators.parameters_to_properties
|
|
386
386
|
def __init__(
|
|
387
387
|
self,
|
|
388
388
|
child_model_class,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import clearskies.decorators
|
|
1
2
|
import clearskies.typing
|
|
2
|
-
from clearskies import parameters_to_properties
|
|
3
3
|
from clearskies.columns.has_many import HasMany
|
|
4
4
|
|
|
5
5
|
|
|
@@ -20,7 +20,7 @@ class HasManySelf(HasMany):
|
|
|
20
20
|
|
|
21
21
|
_descriptor_config_map = None
|
|
22
22
|
|
|
23
|
-
@
|
|
23
|
+
@clearskies.decorators.parameters_to_properties
|
|
24
24
|
def __init__(
|
|
25
25
|
self,
|
|
26
26
|
foreign_column_name: str | None = None,
|
clearskies/columns/integer.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Callable, Self, overload
|
|
4
4
|
|
|
5
|
-
import clearskies.
|
|
5
|
+
import clearskies.decorators
|
|
6
6
|
import clearskies.typing
|
|
7
7
|
from clearskies import configs
|
|
8
8
|
from clearskies.autodoc.schema import Integer as AutoDocInteger
|
|
@@ -77,7 +77,7 @@ class Integer(Column):
|
|
|
77
77
|
|
|
78
78
|
_descriptor_config_map = None
|
|
79
79
|
|
|
80
|
-
@clearskies.
|
|
80
|
+
@clearskies.decorators.parameters_to_properties
|
|
81
81
|
def __init__(
|
|
82
82
|
self,
|
|
83
83
|
default: int | None = None,
|
|
@@ -113,6 +113,10 @@ class Integer(Column):
|
|
|
113
113
|
return None if value is None else int(value)
|
|
114
114
|
|
|
115
115
|
def __set__(self, instance, value: int) -> None:
|
|
116
|
+
# this makes sure we're initialized
|
|
117
|
+
if "name" not in self._config: # type: ignore
|
|
118
|
+
instance.get_columns()
|
|
119
|
+
|
|
116
120
|
instance._next_data[self.name] = value
|
|
117
121
|
|
|
118
122
|
def from_backend(self, value) -> int | None:
|
clearskies/columns/json.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Callable, Self, overload
|
|
5
5
|
|
|
6
|
-
import clearskies.
|
|
6
|
+
import clearskies.decorators
|
|
7
7
|
import clearskies.typing
|
|
8
8
|
from clearskies import configs
|
|
9
9
|
from clearskies.column import Column
|
|
@@ -72,7 +72,7 @@ class Json(Column):
|
|
|
72
72
|
is_searchable = configs.Boolean(default=False)
|
|
73
73
|
_descriptor_config_map = None
|
|
74
74
|
|
|
75
|
-
@clearskies.
|
|
75
|
+
@clearskies.decorators.parameters_to_properties
|
|
76
76
|
def __init__(
|
|
77
77
|
self,
|
|
78
78
|
default: dict[str, Any] | None = None,
|
|
@@ -102,6 +102,10 @@ class Json(Column):
|
|
|
102
102
|
return super().__get__(instance, cls)
|
|
103
103
|
|
|
104
104
|
def __set__(self, instance, value: dict[str, Any]) -> None:
|
|
105
|
+
# this makes sure we're initialized
|
|
106
|
+
if "name" not in self._config: # type: ignore
|
|
107
|
+
instance.get_columns()
|
|
108
|
+
|
|
105
109
|
instance._next_data[self.name] = value
|
|
106
110
|
|
|
107
111
|
def from_backend(self, value) -> dict[str, Any] | list[Any] | None:
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from collections import OrderedDict
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Callable, Self, overload
|
|
5
5
|
|
|
6
|
-
import clearskies.
|
|
6
|
+
import clearskies.decorators
|
|
7
7
|
import clearskies.typing
|
|
8
8
|
from clearskies import configs
|
|
9
9
|
from clearskies.autodoc.schema import Array as AutoDocArray
|
|
@@ -173,7 +173,7 @@ class ManyToManyIds(Column):
|
|
|
173
173
|
is_searchable = configs.Boolean(default=False)
|
|
174
174
|
_descriptor_config_map = None
|
|
175
175
|
|
|
176
|
-
@clearskies.
|
|
176
|
+
@clearskies.decorators.parameters_to_properties
|
|
177
177
|
def __init__(
|
|
178
178
|
self,
|
|
179
179
|
related_model_class,
|
|
@@ -258,6 +258,10 @@ class ManyToManyIds(Column):
|
|
|
258
258
|
return [getattr(model, related_id_column_name) for model in self.get_related_models(instance)]
|
|
259
259
|
|
|
260
260
|
def __set__(self, instance, value: list[str | int]) -> None:
|
|
261
|
+
# this makes sure we're initialized
|
|
262
|
+
if "name" not in self._config: # type: ignore
|
|
263
|
+
instance.get_columns()
|
|
264
|
+
|
|
261
265
|
instance._next_data[self.name] = value
|
|
262
266
|
|
|
263
267
|
def get_related_models(self, model: Model) -> Model:
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any, Callable, Self, overload
|
|
4
4
|
|
|
5
|
-
import clearskies.
|
|
5
|
+
import clearskies.decorators
|
|
6
6
|
import clearskies.typing
|
|
7
7
|
from clearskies import configs
|
|
8
8
|
from clearskies.columns.many_to_many_ids import ManyToManyIds
|
|
@@ -149,7 +149,7 @@ class ManyToManyIdsWithData(ManyToManyIds):
|
|
|
149
149
|
setable = configs.ListAnyDictOrCallable(default=None) # type: ignore
|
|
150
150
|
_descriptor_config_map = None
|
|
151
151
|
|
|
152
|
-
@clearskies.
|
|
152
|
+
@clearskies.decorators.parameters_to_properties
|
|
153
153
|
def __init__(
|
|
154
154
|
self,
|
|
155
155
|
related_model_class,
|
|
@@ -187,6 +187,10 @@ class ManyToManyIdsWithData(ManyToManyIds):
|
|
|
187
187
|
return super().__get__(instance, cls)
|
|
188
188
|
|
|
189
189
|
def __set__(self, instance, value: list[dict[str, Any]]) -> None: # type: ignore
|
|
190
|
+
# this makes sure we're initialized
|
|
191
|
+
if "name" not in self._config: # type: ignore
|
|
192
|
+
instance.get_columns()
|
|
193
|
+
|
|
190
194
|
instance._next_data[self.name] = value
|
|
191
195
|
|
|
192
196
|
def post_save(self, data, model, id):
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from collections import OrderedDict
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Callable, Self, overload
|
|
5
5
|
|
|
6
|
-
import clearskies.
|
|
6
|
+
import clearskies.decorators
|
|
7
7
|
import clearskies.typing
|
|
8
8
|
from clearskies import configs
|
|
9
9
|
from clearskies.autodoc.schema import Array as AutoDocArray
|
|
@@ -30,7 +30,7 @@ class ManyToManyModels(Column):
|
|
|
30
30
|
is_searchable = configs.Boolean(default=False)
|
|
31
31
|
_descriptor_config_map = None
|
|
32
32
|
|
|
33
|
-
@clearskies.
|
|
33
|
+
@clearskies.decorators.parameters_to_properties
|
|
34
34
|
def __init__(
|
|
35
35
|
self,
|
|
36
36
|
many_to_many_column_name,
|
|
@@ -87,6 +87,10 @@ class ManyToManyModels(Column):
|
|
|
87
87
|
return self.many_to_many_column.get_related_models(instance) # type: ignore
|
|
88
88
|
|
|
89
89
|
def __set__(self, instance, value: Model | list[Model] | list[dict[str, Any]]) -> None:
|
|
90
|
+
# this makes sure we're initialized
|
|
91
|
+
if "name" not in self._config: # type: ignore
|
|
92
|
+
instance.get_columns()
|
|
93
|
+
|
|
90
94
|
# we allow a list of models or a model, but if it's a model it may represent a single record or a query.
|
|
91
95
|
# if it's a single record then we want to wrap it in a list so we can iterate over it.
|
|
92
96
|
if hasattr(value, "_data") and value._data:
|
|
@@ -3,8 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
from collections import OrderedDict
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Callable, Self, overload
|
|
5
5
|
|
|
6
|
+
import clearskies.decorators
|
|
6
7
|
import clearskies.typing
|
|
7
|
-
from clearskies import configs
|
|
8
|
+
from clearskies import configs
|
|
8
9
|
from clearskies.autodoc.schema import Array as AutoDocArray
|
|
9
10
|
from clearskies.autodoc.schema import Object as AutoDocObject
|
|
10
11
|
from clearskies.column import Column
|
|
@@ -30,7 +31,7 @@ class ManyToManyPivots(Column):
|
|
|
30
31
|
is_searchable = configs.Boolean(default=False)
|
|
31
32
|
_descriptor_config_map = None
|
|
32
33
|
|
|
33
|
-
@
|
|
34
|
+
@clearskies.decorators.parameters_to_properties
|
|
34
35
|
def __init__(
|
|
35
36
|
self,
|
|
36
37
|
many_to_many_column_name,
|
clearskies/columns/phone.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from typing import Any, Callable
|
|
3
3
|
|
|
4
|
+
import clearskies.decorators
|
|
4
5
|
import clearskies.typing
|
|
5
|
-
from clearskies import configs
|
|
6
|
+
from clearskies import configs
|
|
6
7
|
from clearskies.columns.string import String
|
|
7
8
|
|
|
8
9
|
|
|
@@ -98,7 +99,7 @@ class Phone(String):
|
|
|
98
99
|
usa_only = configs.Boolean(default=True)
|
|
99
100
|
_descriptor_config_map = None
|
|
100
101
|
|
|
101
|
-
@
|
|
102
|
+
@clearskies.decorators.parameters_to_properties
|
|
102
103
|
def __init__(
|
|
103
104
|
self,
|
|
104
105
|
usa_only: bool = True,
|
clearskies/columns/select.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from typing import Callable
|
|
2
2
|
|
|
3
|
+
import clearskies.decorators
|
|
3
4
|
import clearskies.typing
|
|
4
|
-
from clearskies import configs
|
|
5
|
+
from clearskies import configs
|
|
5
6
|
from clearskies.columns.string import String
|
|
6
7
|
|
|
7
8
|
|
|
@@ -67,7 +68,7 @@ class Select(String):
|
|
|
67
68
|
allowed_values = configs.StringList(required=True)
|
|
68
69
|
_descriptor_config_map = None
|
|
69
70
|
|
|
70
|
-
@
|
|
71
|
+
@clearskies.decorators.parameters_to_properties
|
|
71
72
|
def __init__(
|
|
72
73
|
self,
|
|
73
74
|
allowed_values: list[str],
|
clearskies/columns/string.py
CHANGED
|
@@ -92,6 +92,10 @@ class String(Column):
|
|
|
92
92
|
return instance._transformed_data[self.name]
|
|
93
93
|
|
|
94
94
|
def __set__(self, instance: Model, value: str) -> None:
|
|
95
|
+
# this makes sure we're initialized
|
|
96
|
+
if "name" not in self._config: # type: ignore
|
|
97
|
+
instance.get_columns()
|
|
98
|
+
|
|
95
99
|
instance._next_data[self.name] = value
|
|
96
100
|
|
|
97
101
|
def input_error_for_value(self, value: str, operator: str | None = None) -> str:
|
clearskies/columns/timestamp.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import datetime
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Callable, Self, Type, overload
|
|
5
5
|
|
|
6
|
-
import clearskies.
|
|
6
|
+
import clearskies.decorators
|
|
7
7
|
import clearskies.typing
|
|
8
8
|
from clearskies import configs
|
|
9
9
|
from clearskies.columns.datetime import Datetime
|
|
@@ -76,7 +76,7 @@ class Timestamp(Datetime):
|
|
|
76
76
|
include_microseconds = configs.Boolean(default=False)
|
|
77
77
|
_descriptor_config_map = None
|
|
78
78
|
|
|
79
|
-
@clearskies.
|
|
79
|
+
@clearskies.decorators.parameters_to_properties
|
|
80
80
|
def __init__(
|
|
81
81
|
self,
|
|
82
82
|
include_microseconds: bool = False,
|
|
@@ -148,6 +148,10 @@ class Timestamp(Datetime):
|
|
|
148
148
|
return super().__get__(instance, cls)
|
|
149
149
|
|
|
150
150
|
def __set__(self, instance, value: datetime.datetime) -> None:
|
|
151
|
+
# this makes sure we're initialized
|
|
152
|
+
if "name" not in self._config: # type: ignore
|
|
153
|
+
instance.get_columns()
|
|
154
|
+
|
|
151
155
|
instance._next_data[self.name] = value
|
|
152
156
|
|
|
153
157
|
def input_error_for_value(self, value: str, operator: str | None = None) -> str:
|
clearskies/columns/updated.py
CHANGED
|
@@ -3,8 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import datetime
|
|
4
4
|
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
|
+
import clearskies.decorators
|
|
6
7
|
import clearskies.di
|
|
7
|
-
import clearskies.parameters_to_properties
|
|
8
8
|
import clearskies.typing
|
|
9
9
|
from clearskies import configs
|
|
10
10
|
from clearskies.columns.datetime import Datetime
|
|
@@ -85,7 +85,7 @@ class Updated(Datetime):
|
|
|
85
85
|
|
|
86
86
|
now = clearskies.di.inject.Now()
|
|
87
87
|
|
|
88
|
-
@clearskies.
|
|
88
|
+
@clearskies.decorators.parameters_to_properties
|
|
89
89
|
def __init__(
|
|
90
90
|
self,
|
|
91
91
|
in_utc: bool = True,
|
clearskies/columns/uuid.py
CHANGED
|
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
|
+
import clearskies.decorators
|
|
5
6
|
import clearskies.di
|
|
6
|
-
import clearskies.parameters_to_properties
|
|
7
7
|
import clearskies.typing
|
|
8
8
|
from clearskies import configs
|
|
9
9
|
from clearskies.columns.string import String
|
|
@@ -65,7 +65,7 @@ class Uuid(String):
|
|
|
65
65
|
|
|
66
66
|
uuid = clearskies.di.inject.Uuid()
|
|
67
67
|
|
|
68
|
-
@clearskies.
|
|
68
|
+
@clearskies.decorators.parameters_to_properties
|
|
69
69
|
def __init__(
|
|
70
70
|
self,
|
|
71
71
|
is_readable: bool = True,
|
clearskies/configs/__init__.py
CHANGED
|
@@ -56,7 +56,7 @@ class MyConfigurableClass(configs.Configurable):
|
|
|
56
56
|
age = configs.Integer(required=True)
|
|
57
57
|
property_with_default = configs.String(default="some value")
|
|
58
58
|
|
|
59
|
-
@clearskies.
|
|
59
|
+
@clearskies.decorators()
|
|
60
60
|
def __init__(self, name: str, age: int, optional: string = None):
|
|
61
61
|
self.finalize_and_validate_configuration()
|
|
62
62
|
```
|
clearskies/contexts/cli.py
CHANGED
|
@@ -5,6 +5,112 @@ from clearskies.input_outputs import Cli as CliInputOutput
|
|
|
5
5
|
class Cli(Context):
|
|
6
6
|
"""
|
|
7
7
|
Run an application via a CLI command
|
|
8
|
+
|
|
9
|
+
This context converts a clearskies application into a CLI command. Here's a simple example:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
#!/usr/bin/env python
|
|
13
|
+
import clearskies
|
|
14
|
+
|
|
15
|
+
def my_function():
|
|
16
|
+
return "Hello World!"
|
|
17
|
+
|
|
18
|
+
cli = clearskies.contexts.Cli(my_function)
|
|
19
|
+
cli()
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Which you can then run as expected:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
$ ./example.py
|
|
26
|
+
Hello World!
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Routing is still supported, with routes and route parameters becoming CLI args:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
#!/usr/bin/env python
|
|
33
|
+
import clearskies
|
|
34
|
+
|
|
35
|
+
def my_function(name):
|
|
36
|
+
return f"Hello {name}!"
|
|
37
|
+
|
|
38
|
+
cli = clearskies.contexts.Cli(
|
|
39
|
+
clearskies.endpoints.Callable(
|
|
40
|
+
my_function,
|
|
41
|
+
url="/hello/:name",
|
|
42
|
+
return_standard_response=False,
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
cli()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
With a url of `/hello/:name` you would invoke like so:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
./example.py hello Bob
|
|
52
|
+
Hello Bob!
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
If the endpoint expects a request method you can provide it by setting the `-X` or `--request_method=`
|
|
56
|
+
kwargs. So for tihs example:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
#!/usr/bin/env python
|
|
60
|
+
import clearskies
|
|
61
|
+
|
|
62
|
+
def my_function(name):
|
|
63
|
+
return f"Hello {name}!"
|
|
64
|
+
|
|
65
|
+
cli = clearskies.contexts.Cli(
|
|
66
|
+
clearskies.endpoints.Callable(
|
|
67
|
+
my_function,
|
|
68
|
+
url="/hello/:name",
|
|
69
|
+
request_methods=["POST"],
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
cli()
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
And then calling it successfully:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
./example.py hello Bob --request_method=POST
|
|
79
|
+
|
|
80
|
+
./example.py hello Bob -X POST
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
You can pass data as a json string with the -d flag or set individual named arguments. The following
|
|
84
|
+
example just reflects the request data back to the client:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
#!/usr/bin/env python
|
|
88
|
+
import clearskies
|
|
89
|
+
|
|
90
|
+
def my_function(request_data):
|
|
91
|
+
return request_data
|
|
92
|
+
|
|
93
|
+
cli = clearskies.contexts.Cli(
|
|
94
|
+
clearskies.endpoints.Callable(
|
|
95
|
+
my_function,
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
cli()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
And these three calls are identical:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
./example.py -d '{"hello": "world"}'
|
|
105
|
+
|
|
106
|
+
echo '{"hello": "world"}' | ./test.py
|
|
107
|
+
|
|
108
|
+
./test.py --hello=world
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Although note that the first two are going to be preferred over the third, simply because with the
|
|
112
|
+
third there's simply no way to specify the type of a variable. As a result, you may run into issues
|
|
113
|
+
with strict type checking on endpoints.
|
|
8
114
|
"""
|
|
9
115
|
|
|
10
116
|
def __call__(self): # type: ignore
|
clearskies/contexts/context.py
CHANGED
clearskies/contexts/wsgi.py
CHANGED
|
@@ -14,6 +14,62 @@ from clearskies.input_outputs import Wsgi as WsgiInputOutput
|
|
|
14
14
|
class Wsgi(Context):
|
|
15
15
|
"""
|
|
16
16
|
Connect your application to a WSGI server.
|
|
17
|
+
|
|
18
|
+
The Wsgi context is used to connect a clearskies application to a WSGI server of your choice. As with all
|
|
19
|
+
contexts, you first create it and pass in the application (a callable, endpoint, or endpoint group) as well
|
|
20
|
+
as any dependency injection parameters. Then, you call the context from inside of the function invoked by
|
|
21
|
+
your WSGI server, passing along the `environment` and `start_response` variables, and returning the response
|
|
22
|
+
from the context. Here's a simple example:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
import clearskies
|
|
26
|
+
|
|
27
|
+
def hello_world():
|
|
28
|
+
return "Hello World!"
|
|
29
|
+
|
|
30
|
+
wsgi = clearskies.contexts.Wsgi(hello_world)
|
|
31
|
+
|
|
32
|
+
def application(environment, start_response):
|
|
33
|
+
return wsgi(environment, start_response)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
You would then launch your WSGI server. For instance, here's how to launch it with uwsgi, which automatically
|
|
37
|
+
looks for a function called `application` and treats that as the WSGI starting point:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
uwsgi --http :9090 --wsgi-file test.py
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
You could then:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
curl 'http://localhost:9090'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
And see the response from this "hello world" app. Note than in the above example I create the context outside
|
|
50
|
+
of the application function. Of course, you can do the opposite:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
import clearskies
|
|
54
|
+
|
|
55
|
+
def hello_world():
|
|
56
|
+
return "Hello World!"
|
|
57
|
+
|
|
58
|
+
def application(environment, start_response):
|
|
59
|
+
wsgi = clearskies.contexts.Wsgi(hello_world)
|
|
60
|
+
return wsgi(environment, start_response)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The difference is that most wsgi servers will cache any objects created outside of the handler function (e.g. `application`
|
|
64
|
+
in this case). When you first create the context clearskies will configure and validate any endpoints attached.
|
|
65
|
+
Also, it will create an instance of the dependency injection container and cache it. If the context object is created
|
|
66
|
+
outside of the handler, and the server caches objects in this csae, then this validation will only happen once and
|
|
67
|
+
the DI cache will store objects in between HTTP calls. If you create your context inside the handler function, then
|
|
68
|
+
you'll end up with an empty cache everytime and you'll have slower responses because of clearskies checking the
|
|
69
|
+
application configuration everytime. Note that the DI system for clearskies grants you full cache control, so
|
|
70
|
+
by and large it's normal and expected that you'll persist the cache between requests by creating the context outside
|
|
71
|
+
of any handler functions.
|
|
72
|
+
|
|
17
73
|
"""
|
|
18
74
|
|
|
19
75
|
def __call__(self, env, start_response): # type: ignore
|
clearskies/contexts/wsgi_ref.py
CHANGED
|
@@ -14,6 +14,35 @@ from clearskies.input_outputs import Wsgi as WsgiInputOutput
|
|
|
14
14
|
class WsgiRef(Context):
|
|
15
15
|
"""
|
|
16
16
|
Use a built in WSGI server (for development purposes only).
|
|
17
|
+
|
|
18
|
+
This context will launch a built-in HTTP server for you, so you can run applications locally
|
|
19
|
+
without having to install extra dependencies. Note that this server is not intended for production
|
|
20
|
+
usage, so this is best used for simple tests/demonstration purposes. Unlike the WSGI context, where
|
|
21
|
+
you define the application handler and invoke the context from inside of it (passing along the
|
|
22
|
+
environment and start_response variables), in this case you simply directly invoke the context to
|
|
23
|
+
launch the server. The default port is 8080:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
#!/usr/bin/env python
|
|
27
|
+
import clearskies
|
|
28
|
+
|
|
29
|
+
def hello_world(name):
|
|
30
|
+
return f"Hello {name}!"
|
|
31
|
+
|
|
32
|
+
wsgi = clearskies.contexts.WsgiRef(
|
|
33
|
+
clearskies.endpoints.Callable(
|
|
34
|
+
hello_world,
|
|
35
|
+
url="/hello/:name",
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
wsgi()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
And to invoke it:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
curl 'http://localhost:8080/hello/Friend'
|
|
45
|
+
```
|
|
17
46
|
"""
|
|
18
47
|
|
|
19
48
|
port: int = 8080
|