everysk-lib 1.10.2__cp312-cp312-win_amd64.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.
- everysk/__init__.py +30 -0
- everysk/_version.py +683 -0
- everysk/api/__init__.py +61 -0
- everysk/api/api_requestor.py +167 -0
- everysk/api/api_resources/__init__.py +23 -0
- everysk/api/api_resources/api_resource.py +371 -0
- everysk/api/api_resources/calculation.py +779 -0
- everysk/api/api_resources/custom_index.py +42 -0
- everysk/api/api_resources/datastore.py +81 -0
- everysk/api/api_resources/file.py +42 -0
- everysk/api/api_resources/market_data.py +223 -0
- everysk/api/api_resources/parser.py +66 -0
- everysk/api/api_resources/portfolio.py +43 -0
- everysk/api/api_resources/private_security.py +42 -0
- everysk/api/api_resources/report.py +65 -0
- everysk/api/api_resources/report_template.py +39 -0
- everysk/api/api_resources/tests.py +115 -0
- everysk/api/api_resources/worker_execution.py +64 -0
- everysk/api/api_resources/workflow.py +65 -0
- everysk/api/api_resources/workflow_execution.py +93 -0
- everysk/api/api_resources/workspace.py +42 -0
- everysk/api/http_client.py +63 -0
- everysk/api/tests.py +32 -0
- everysk/api/utils.py +262 -0
- everysk/config.py +451 -0
- everysk/core/_tests/serialize/test_json.py +336 -0
- everysk/core/_tests/serialize/test_orjson.py +295 -0
- everysk/core/_tests/serialize/test_pickle.py +48 -0
- everysk/core/cloud_function/main.py +78 -0
- everysk/core/cloud_function/tests.py +86 -0
- everysk/core/compress.py +245 -0
- everysk/core/datetime/__init__.py +12 -0
- everysk/core/datetime/calendar.py +144 -0
- everysk/core/datetime/date.py +424 -0
- everysk/core/datetime/date_expression.py +299 -0
- everysk/core/datetime/date_mixin.py +1475 -0
- everysk/core/datetime/date_settings.py +30 -0
- everysk/core/datetime/datetime.py +713 -0
- everysk/core/exceptions.py +435 -0
- everysk/core/fields.py +1176 -0
- everysk/core/firestore.py +555 -0
- everysk/core/fixtures/_settings.py +29 -0
- everysk/core/fixtures/other/_settings.py +18 -0
- everysk/core/fixtures/user_agents.json +88 -0
- everysk/core/http.py +691 -0
- everysk/core/lists.py +92 -0
- everysk/core/log.py +709 -0
- everysk/core/number.py +37 -0
- everysk/core/object.py +1469 -0
- everysk/core/redis.py +1021 -0
- everysk/core/retry.py +51 -0
- everysk/core/serialize.py +674 -0
- everysk/core/sftp.py +414 -0
- everysk/core/signing.py +53 -0
- everysk/core/slack.py +127 -0
- everysk/core/string.py +199 -0
- everysk/core/tests.py +240 -0
- everysk/core/threads.py +199 -0
- everysk/core/undefined.py +70 -0
- everysk/core/unittests.py +73 -0
- everysk/core/workers.py +241 -0
- everysk/sdk/__init__.py +23 -0
- everysk/sdk/base.py +98 -0
- everysk/sdk/brutils/cnpj.py +391 -0
- everysk/sdk/brutils/cnpj_pd.py +129 -0
- everysk/sdk/engines/__init__.py +26 -0
- everysk/sdk/engines/cache.py +185 -0
- everysk/sdk/engines/compliance.py +37 -0
- everysk/sdk/engines/cryptography.py +69 -0
- everysk/sdk/engines/expression.cp312-win_amd64.pyd +0 -0
- everysk/sdk/engines/expression.pyi +55 -0
- everysk/sdk/engines/helpers.cp312-win_amd64.pyd +0 -0
- everysk/sdk/engines/helpers.pyi +26 -0
- everysk/sdk/engines/lock.py +120 -0
- everysk/sdk/engines/market_data.py +244 -0
- everysk/sdk/engines/settings.py +19 -0
- everysk/sdk/entities/__init__.py +23 -0
- everysk/sdk/entities/base.py +784 -0
- everysk/sdk/entities/base_list.py +131 -0
- everysk/sdk/entities/custom_index/base.py +209 -0
- everysk/sdk/entities/custom_index/settings.py +29 -0
- everysk/sdk/entities/datastore/base.py +160 -0
- everysk/sdk/entities/datastore/settings.py +17 -0
- everysk/sdk/entities/fields.py +375 -0
- everysk/sdk/entities/file/base.py +215 -0
- everysk/sdk/entities/file/settings.py +63 -0
- everysk/sdk/entities/portfolio/base.py +248 -0
- everysk/sdk/entities/portfolio/securities.py +241 -0
- everysk/sdk/entities/portfolio/security.py +580 -0
- everysk/sdk/entities/portfolio/settings.py +97 -0
- everysk/sdk/entities/private_security/base.py +226 -0
- everysk/sdk/entities/private_security/settings.py +17 -0
- everysk/sdk/entities/query.py +603 -0
- everysk/sdk/entities/report/base.py +214 -0
- everysk/sdk/entities/report/settings.py +23 -0
- everysk/sdk/entities/script.py +310 -0
- everysk/sdk/entities/secrets/base.py +128 -0
- everysk/sdk/entities/secrets/script.py +119 -0
- everysk/sdk/entities/secrets/settings.py +17 -0
- everysk/sdk/entities/settings.py +48 -0
- everysk/sdk/entities/tags.py +174 -0
- everysk/sdk/entities/worker_execution/base.py +307 -0
- everysk/sdk/entities/worker_execution/settings.py +63 -0
- everysk/sdk/entities/workflow_execution/base.py +113 -0
- everysk/sdk/entities/workflow_execution/settings.py +32 -0
- everysk/sdk/entities/workspace/base.py +99 -0
- everysk/sdk/entities/workspace/settings.py +27 -0
- everysk/sdk/settings.py +67 -0
- everysk/sdk/tests.py +105 -0
- everysk/sdk/worker_base.py +47 -0
- everysk/server/__init__.py +9 -0
- everysk/server/applications.py +63 -0
- everysk/server/endpoints.py +516 -0
- everysk/server/example_api.py +69 -0
- everysk/server/middlewares.py +80 -0
- everysk/server/requests.py +62 -0
- everysk/server/responses.py +119 -0
- everysk/server/routing.py +64 -0
- everysk/server/settings.py +36 -0
- everysk/server/tests.py +36 -0
- everysk/settings.py +98 -0
- everysk/sql/__init__.py +9 -0
- everysk/sql/connection.py +232 -0
- everysk/sql/model.py +376 -0
- everysk/sql/query.py +417 -0
- everysk/sql/row_factory.py +63 -0
- everysk/sql/settings.py +49 -0
- everysk/sql/utils.py +129 -0
- everysk/tests.py +23 -0
- everysk/utils.py +81 -0
- everysk/version.py +15 -0
- everysk_lib-1.10.2.dist-info/.gitignore +5 -0
- everysk_lib-1.10.2.dist-info/METADATA +326 -0
- everysk_lib-1.10.2.dist-info/RECORD +137 -0
- everysk_lib-1.10.2.dist-info/WHEEL +5 -0
- everysk_lib-1.10.2.dist-info/licenses/LICENSE.txt +9 -0
- everysk_lib-1.10.2.dist-info/top_level.txt +2 -0
everysk/config.py
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
#
|
|
3
|
+
# (C) Copyright 2023 EVERYSK TECHNOLOGIES
|
|
4
|
+
#
|
|
5
|
+
# This is an unpublished work containing confidential and proprietary
|
|
6
|
+
# information of EVERYSK TECHNOLOGIES. Disclosure, use, or reproduction
|
|
7
|
+
# without authorization of EVERYSK TECHNOLOGIES is prohibited.
|
|
8
|
+
#
|
|
9
|
+
###############################################################################
|
|
10
|
+
from contextlib import AbstractContextManager
|
|
11
|
+
from contextvars import ContextVar
|
|
12
|
+
from copy import deepcopy
|
|
13
|
+
from importlib import import_module
|
|
14
|
+
from inspect import getmembers, isroutine, isclass
|
|
15
|
+
from os import getenv
|
|
16
|
+
from os.path import dirname, exists, sep
|
|
17
|
+
from pathlib import Path, PosixPath
|
|
18
|
+
from re import findall
|
|
19
|
+
from types import TracebackType, UnionType
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from everysk.core.object import BaseObject, MetaClass
|
|
23
|
+
from everysk.utils import bool_convert
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# The root path of the everysk lib we need this to create the correct stub file
|
|
27
|
+
EVERYSK_ROOT = dirname(__file__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
###############################################################################
|
|
31
|
+
# Private Functions Implementation
|
|
32
|
+
###############################################################################
|
|
33
|
+
def _get_context_value(name: str) -> Any:
|
|
34
|
+
"""
|
|
35
|
+
If we are in a python context we return the value
|
|
36
|
+
that is in the context setting.
|
|
37
|
+
If the settings is not found we raise a KeyError.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
name (str): The settings name.
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
KeyError: If the name is not found.
|
|
44
|
+
"""
|
|
45
|
+
context_settings = SettingsManager.settings.get()
|
|
46
|
+
# We only care for the values that are explicit set in this settings
|
|
47
|
+
if context_settings:
|
|
48
|
+
return context_settings.__dict__.get(name, Undefined)
|
|
49
|
+
|
|
50
|
+
return Undefined
|
|
51
|
+
|
|
52
|
+
def _get_env_value__(name: str) -> Any:
|
|
53
|
+
"""
|
|
54
|
+
Get the value from the environment and clean it.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
name (str): The var name to search.
|
|
58
|
+
"""
|
|
59
|
+
env_value = getenv(name)
|
|
60
|
+
|
|
61
|
+
# This is a check to always get the correct value
|
|
62
|
+
# that we received from the environment
|
|
63
|
+
# https://everysk.atlassian.net/browse/COD-907
|
|
64
|
+
if env_value is not None:
|
|
65
|
+
attributes = getattr(Settings, SettingsMetaClass._attr_name, {}) # pylint: disable=protected-access
|
|
66
|
+
try:
|
|
67
|
+
attr_type = attributes[name]
|
|
68
|
+
try:
|
|
69
|
+
# Our fields have the clean_value method
|
|
70
|
+
env_value = attr_type.clean_value(env_value)
|
|
71
|
+
|
|
72
|
+
except AttributeError:
|
|
73
|
+
# For the other types we try to convert the
|
|
74
|
+
# value to the specific type that was declared
|
|
75
|
+
# For bool types we use a specific function
|
|
76
|
+
if attr_type != bool:
|
|
77
|
+
env_value = attr_type(env_value)
|
|
78
|
+
else:
|
|
79
|
+
env_value = bool_convert(env_value)
|
|
80
|
+
|
|
81
|
+
except KeyError:
|
|
82
|
+
pass
|
|
83
|
+
else:
|
|
84
|
+
# If env_value is None then the var was not set
|
|
85
|
+
env_value = Undefined
|
|
86
|
+
|
|
87
|
+
return env_value
|
|
88
|
+
|
|
89
|
+
def _is_valid_path(path: str) -> bool:
|
|
90
|
+
"""
|
|
91
|
+
Function that checks if a path is valid to load the settings module.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
path (str): The full path that need be checked.
|
|
95
|
+
"""
|
|
96
|
+
result = True
|
|
97
|
+
# Python installation normally happens on venvs that are in the project path
|
|
98
|
+
if 'site-packages/' in path:
|
|
99
|
+
result = False
|
|
100
|
+
|
|
101
|
+
# Git files could have copies of python files
|
|
102
|
+
elif '.git/' in path:
|
|
103
|
+
result = False
|
|
104
|
+
|
|
105
|
+
return result
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
###############################################################################
|
|
109
|
+
# Public Functions Implementation
|
|
110
|
+
###############################################################################
|
|
111
|
+
def get_full_dotted_path_module(entry: PosixPath, root: str) -> str:
|
|
112
|
+
"""
|
|
113
|
+
From the full path we need to generate the module that is valid for import.
|
|
114
|
+
Changes '/var/app/src/everysk/settings.py' to 'everysk.settings'.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
entry (PosixPath): The posix path object.
|
|
118
|
+
root (str): The root path that will be removed.
|
|
119
|
+
"""
|
|
120
|
+
# /var/app/src/everysk/settings.py
|
|
121
|
+
module = str(entry)
|
|
122
|
+
|
|
123
|
+
# everysk/settings.py
|
|
124
|
+
module = module.replace(f'{root}{sep}', '')
|
|
125
|
+
|
|
126
|
+
# everysk/settings
|
|
127
|
+
module = module.replace('.py', '')
|
|
128
|
+
|
|
129
|
+
# everysk.settings
|
|
130
|
+
module = module.replace(sep, '.')
|
|
131
|
+
|
|
132
|
+
return module
|
|
133
|
+
|
|
134
|
+
def get_all_modules() -> list:
|
|
135
|
+
"""
|
|
136
|
+
We search inside EVERYSK_ROOT and PROJECT_ROOT for 'settings.py'
|
|
137
|
+
files and convert this files into python modules.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
list: A list of Python modules
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
>>> all_modules = get_all_modules()
|
|
144
|
+
>>> print(all_modules)
|
|
145
|
+
... ['module1', 'module2']
|
|
146
|
+
"""
|
|
147
|
+
search_name = 'settings.py'
|
|
148
|
+
modules = []
|
|
149
|
+
if EVERYSK_ROOT and exists(EVERYSK_ROOT):
|
|
150
|
+
# we need to remove the '/everysk' from the path to create the correct module path
|
|
151
|
+
root = EVERYSK_ROOT.replace(f'{sep}everysk', '')
|
|
152
|
+
for entry in Path(EVERYSK_ROOT).rglob(search_name):
|
|
153
|
+
modules.append(get_full_dotted_path_module(entry=entry, root=root))
|
|
154
|
+
|
|
155
|
+
project_root = getenv('PROJECT_ROOT')
|
|
156
|
+
if project_root and exists(project_root):
|
|
157
|
+
for entry in Path(project_root).rglob(search_name):
|
|
158
|
+
# The rglob function does not have a way to remove some paths
|
|
159
|
+
# so we need to manually exclude the ones that are not required
|
|
160
|
+
if _is_valid_path(path=entry.as_posix()):
|
|
161
|
+
modules.append(get_full_dotted_path_module(entry=entry, root=project_root))
|
|
162
|
+
|
|
163
|
+
return modules
|
|
164
|
+
|
|
165
|
+
def get_all_attributes() -> list[tuple[str, Any, Any]]:
|
|
166
|
+
"""
|
|
167
|
+
Get all attributes from the settings modules except ones that starts with '_'
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
Consider a settings module 'my_settings.py' containing the following attributes:
|
|
171
|
+
|
|
172
|
+
...
|
|
173
|
+
MY_SETTING_1 = 42
|
|
174
|
+
MY_SETTING_2 = 'Hello, World!'
|
|
175
|
+
_MY_SETTING_3 = 'Foo Bar'
|
|
176
|
+
...
|
|
177
|
+
|
|
178
|
+
Calling get_all_attributes() will return:
|
|
179
|
+
|
|
180
|
+
[
|
|
181
|
+
('MY_SETTING_1', int, 42),
|
|
182
|
+
('MY_SETTING_2', str, 'Hello, World!')
|
|
183
|
+
]
|
|
184
|
+
"""
|
|
185
|
+
result = []
|
|
186
|
+
|
|
187
|
+
for module in get_all_modules():
|
|
188
|
+
# Import the module
|
|
189
|
+
module = import_module(module)
|
|
190
|
+
|
|
191
|
+
for attr, value in getmembers(module, predicate=lambda x: not isroutine(x) and not isclass(x)):
|
|
192
|
+
if not attr.startswith('_'):
|
|
193
|
+
try:
|
|
194
|
+
attr_value = value.default
|
|
195
|
+
attr_type = value
|
|
196
|
+
except AttributeError:
|
|
197
|
+
attr_value = value
|
|
198
|
+
# If this attribute already has an annotation on the module we use it
|
|
199
|
+
# otherwise we get the class of the value as the annotation
|
|
200
|
+
# https://everysk.atlassian.net/browse/COD-3833
|
|
201
|
+
attr_type = module.__annotations__.get(attr, type(value))
|
|
202
|
+
|
|
203
|
+
result.append((attr, attr_type, attr_value))
|
|
204
|
+
return result
|
|
205
|
+
|
|
206
|
+
def update_settings_attributes(obj: 'Settings'):
|
|
207
|
+
"""
|
|
208
|
+
Updates the attributes of the Settings class in the config.pyi file.
|
|
209
|
+
|
|
210
|
+
This function updates the attribute definitions of the Settings class based on the attributes retrieved from the `get_all_attributes` function.
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
>>> from everysk.config import update_settings_attributes
|
|
214
|
+
>>> update_settings_attribute()
|
|
215
|
+
...
|
|
216
|
+
"""
|
|
217
|
+
with open(f'{EVERYSK_ROOT}/config.pyi', 'w', encoding='utf-8') as stubs:
|
|
218
|
+
# This fix the correct type of SettingsManager.settings
|
|
219
|
+
stubs.write('from contextvars import ContextVar\n')
|
|
220
|
+
|
|
221
|
+
# This fix the stub file for the RegexField
|
|
222
|
+
stubs.write('from re import Pattern\n\n')
|
|
223
|
+
|
|
224
|
+
# Now we have dict and BaseDict in the stub file too
|
|
225
|
+
stubs.write('from everysk.core.object import BaseDict\n\n')
|
|
226
|
+
|
|
227
|
+
# Write the SettingsManager class
|
|
228
|
+
stubs.write('class SettingsManager:\n')
|
|
229
|
+
stubs.write(' settings: ContextVar\n')
|
|
230
|
+
stubs.write(' token: str\n\n')
|
|
231
|
+
|
|
232
|
+
# Write the Settings class
|
|
233
|
+
stubs.write('class Settings:\n')
|
|
234
|
+
|
|
235
|
+
attributes = set()
|
|
236
|
+
for attr_name, attr_type, attr_value in get_all_attributes():
|
|
237
|
+
obj.__set_attribute__(attr_name, attr_type, attr_value)
|
|
238
|
+
try:
|
|
239
|
+
# If attr_type is a Everysk Field the real value will be in the attr_type attribute
|
|
240
|
+
attr_type = attr_type.attr_type
|
|
241
|
+
except AttributeError:
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
# For Union types we need to get the real types inside __args__
|
|
245
|
+
if not isinstance(attr_type, UnionType):
|
|
246
|
+
attr_type = attr_type.__name__
|
|
247
|
+
else:
|
|
248
|
+
attr_type = ' | '.join([t.__name__ for t in attr_type.__args__])
|
|
249
|
+
|
|
250
|
+
# We could have some duplicated attributes so we keep then in a set
|
|
251
|
+
attributes.add(f' {attr_name}: {attr_type}\n')
|
|
252
|
+
|
|
253
|
+
# Then we write all attributes in the stub file
|
|
254
|
+
stubs.writelines(sorted(attributes))
|
|
255
|
+
# To autocomplete the init params
|
|
256
|
+
stubs.write(' def __init__(self, singleton: bool = True, **kwargs) -> None: ...\n')
|
|
257
|
+
# Fix for the autocomplete works if a context var is created
|
|
258
|
+
stubs.write(' def __enter__(self) -> \'Settings\': ...\n')
|
|
259
|
+
|
|
260
|
+
# This is needed to VS Code understand the instance
|
|
261
|
+
stubs.write('\nsettings: Settings\n')
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
###############################################################################
|
|
265
|
+
# SettingsManager Class Implementation
|
|
266
|
+
###############################################################################
|
|
267
|
+
class SettingsManager(AbstractContextManager):
|
|
268
|
+
settings: ContextVar = ContextVar('everysk-settings', default=None)
|
|
269
|
+
token: str = None
|
|
270
|
+
|
|
271
|
+
def __init__(self, context_settings: 'Settings' = Undefined) -> None:
|
|
272
|
+
if context_settings:
|
|
273
|
+
# We make a copy to not change the original
|
|
274
|
+
context_settings = deepcopy(context_settings)
|
|
275
|
+
else:
|
|
276
|
+
# We create a fresh one
|
|
277
|
+
context_settings = Settings(singleton=False)
|
|
278
|
+
|
|
279
|
+
# Store the value inside the context var
|
|
280
|
+
self.token = self.settings.set(context_settings)
|
|
281
|
+
|
|
282
|
+
def __exit__(self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None) -> bool | None:
|
|
283
|
+
"""
|
|
284
|
+
https://docs.python.org/3/library/stdtypes.html#contextmanager.__exit__
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
bool | None: If return is False any exception will be raised.
|
|
288
|
+
"""
|
|
289
|
+
# Clear all settings config exiting the context
|
|
290
|
+
self.settings.reset(self.token)
|
|
291
|
+
|
|
292
|
+
# return False to raise any errors if they happened
|
|
293
|
+
return False
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
###############################################################################
|
|
297
|
+
# SettingsMetaClass Class Implementation
|
|
298
|
+
###############################################################################
|
|
299
|
+
class SettingsMetaClass(MetaClass):
|
|
300
|
+
|
|
301
|
+
def __new__(mcs, name: str, bases: tuple, attrs: dict) -> type:
|
|
302
|
+
"""
|
|
303
|
+
Method that create the class object.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
mcs (_type_): This class.
|
|
307
|
+
name (str): The name of the new class -> Settings.
|
|
308
|
+
bases (tuple): Parent for the new class -> BaseObject.
|
|
309
|
+
attrs (dict): A list of attributes tha the new class will have.
|
|
310
|
+
"""
|
|
311
|
+
obj = super().__new__(mcs, name, bases, attrs)
|
|
312
|
+
|
|
313
|
+
# This is executed only one time for every python process
|
|
314
|
+
# before the initialization of the instance, we update the attributes list
|
|
315
|
+
update_settings_attributes(obj)
|
|
316
|
+
|
|
317
|
+
return obj
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
###############################################################################
|
|
321
|
+
# Settings Class Implementation
|
|
322
|
+
###############################################################################
|
|
323
|
+
class Settings(BaseObject, metaclass=SettingsMetaClass):
|
|
324
|
+
|
|
325
|
+
def __new__(cls, singleton: bool = True, **kwargs) -> 'Settings':
|
|
326
|
+
"""
|
|
327
|
+
Changed to keep only one instance for the class.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
singleton (bool): Used to keep the same instance. Defaults to True.
|
|
331
|
+
"""
|
|
332
|
+
if singleton:
|
|
333
|
+
try:
|
|
334
|
+
return settings
|
|
335
|
+
except NameError:
|
|
336
|
+
pass
|
|
337
|
+
|
|
338
|
+
return object.__new__(cls)
|
|
339
|
+
|
|
340
|
+
def __init__(self, **kwargs) -> None:
|
|
341
|
+
# remove the singleton keyword
|
|
342
|
+
if 'singleton' in kwargs:
|
|
343
|
+
kwargs.pop('singleton')
|
|
344
|
+
|
|
345
|
+
super().__init__(**kwargs)
|
|
346
|
+
|
|
347
|
+
def __deepcopy__(self, memo: dict = None) -> 'Settings':
|
|
348
|
+
"""
|
|
349
|
+
A deep copy constructs a new compound object and then, recursively,
|
|
350
|
+
inserts copies into it of the objects found in the original.
|
|
351
|
+
This method is used when we call deepcopy(obj).
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
memo (dict, optional): A memory object to avoid copy twice. Defaults to None.
|
|
355
|
+
"""
|
|
356
|
+
# We need to copy the __dict__
|
|
357
|
+
obj = deepcopy(self.__dict__, memo)
|
|
358
|
+
|
|
359
|
+
# We create a new obj
|
|
360
|
+
obj = type(self)(singleton=False, **obj)
|
|
361
|
+
return obj
|
|
362
|
+
|
|
363
|
+
def __enter__(self) -> 'Settings':
|
|
364
|
+
"""
|
|
365
|
+
https://docs.python.org/3/library/stdtypes.html#contextmanager.__enter__
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Settings: A copy of the settings object.
|
|
369
|
+
"""
|
|
370
|
+
# We must use deepcopy to keep all values that are already set
|
|
371
|
+
return deepcopy(settings)
|
|
372
|
+
|
|
373
|
+
def __exit__(self, __exc_type: type[BaseException] | None, __exc_value: BaseException | None, __traceback: TracebackType | None) -> bool | None:
|
|
374
|
+
"""
|
|
375
|
+
https://docs.python.org/3/library/stdtypes.html#contextmanager.__exit__
|
|
376
|
+
This method is required even if it only returns False
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
bool | None: If return is False any exception will be raised.
|
|
380
|
+
"""
|
|
381
|
+
return False
|
|
382
|
+
|
|
383
|
+
def __getattribute__(self, name: str) -> Any:
|
|
384
|
+
"""
|
|
385
|
+
This method try to first get the value from the environment
|
|
386
|
+
if this does not exists get from the class.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
name (str): The setting name.
|
|
390
|
+
"""
|
|
391
|
+
## For performance and to avoid some errors because
|
|
392
|
+
## we are trying to get other things that does not
|
|
393
|
+
## are attributes of this class
|
|
394
|
+
if name.startswith('__') or name == '_config':
|
|
395
|
+
return super().__getattribute__(name)
|
|
396
|
+
|
|
397
|
+
# First we try to get the value from the context
|
|
398
|
+
value = _get_context_value(name)
|
|
399
|
+
if value is Undefined:
|
|
400
|
+
# Second we try to get the value from the instance
|
|
401
|
+
value = self.__dict__.get(name, Undefined)
|
|
402
|
+
if value is Undefined:
|
|
403
|
+
# Third we try to get from the environment
|
|
404
|
+
value = _get_env_value__(name)
|
|
405
|
+
if value is Undefined:
|
|
406
|
+
# Fourth we try to get from the class that is the default value
|
|
407
|
+
value = super().__getattribute__(name)
|
|
408
|
+
|
|
409
|
+
if isinstance(value, str):
|
|
410
|
+
try:
|
|
411
|
+
# If is a normal string nothing happen
|
|
412
|
+
value = value.format()
|
|
413
|
+
except KeyError:
|
|
414
|
+
# Otherwise is a string in this format:
|
|
415
|
+
# '{SOME_SETTING}-rest-string' so we need to change SOME_SETTING
|
|
416
|
+
# for the real value that is a settings
|
|
417
|
+
|
|
418
|
+
# We get all {*} that appears
|
|
419
|
+
keys = findall(r'{(.*?)}', value)
|
|
420
|
+
# Then we get the real value of each key
|
|
421
|
+
kwargs = {key: getattr(self, key) for key in keys}
|
|
422
|
+
# Then we try to format the real string
|
|
423
|
+
value = value.format(**kwargs)
|
|
424
|
+
|
|
425
|
+
return value
|
|
426
|
+
|
|
427
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
428
|
+
"""
|
|
429
|
+
This method set the value of every attribute inside the Settings object,
|
|
430
|
+
we use it to choose if we set on the normal object or in the context object.
|
|
431
|
+
If the context_settings exists it sets the value inside the context_settings.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
name (str): The name of the attribute.
|
|
435
|
+
value (Any): The value that needs to be stored.
|
|
436
|
+
"""
|
|
437
|
+
context_settings = SettingsManager.settings.get()
|
|
438
|
+
# If context settings does not exist or it is a context_settings
|
|
439
|
+
# that is trying to store the value for this setting we use the default behavior
|
|
440
|
+
if not context_settings or context_settings == self:
|
|
441
|
+
super().__setattr__(name, value)
|
|
442
|
+
else:
|
|
443
|
+
# Otherwise we call the set for context_settings
|
|
444
|
+
setattr(context_settings, name, value)
|
|
445
|
+
SettingsManager.settings.set(context_settings)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
###############################################################################
|
|
449
|
+
# Here we load all computed settings on one var
|
|
450
|
+
###############################################################################
|
|
451
|
+
settings: Settings = Settings()
|