everysk-lib 1.9.8__tar.gz → 1.9.9__tar.gz
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_lib-1.9.8/src/everysk_lib.egg-info → everysk_lib-1.9.9}/PKG-INFO +4 -2
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/pyproject.toml +3 -1
- everysk_lib-1.9.9/src/everysk/core/retry.py +51 -0
- everysk_lib-1.9.9/src/everysk/core/tests.py +240 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/settings.py +3 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sql/connection.py +81 -33
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sql/model.py +1 -6
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sql/query.py +30 -14
- everysk_lib-1.9.9/src/everysk/sql/row_factory.py +63 -0
- everysk_lib-1.9.9/src/everysk/sql/settings.py +45 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sql/utils.py +8 -7
- {everysk_lib-1.9.8 → everysk_lib-1.9.9/src/everysk_lib.egg-info}/PKG-INFO +4 -2
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk_lib.egg-info/SOURCES.txt +2 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk_lib.egg-info/requires.txt +3 -1
- everysk_lib-1.9.8/src/everysk/core/tests.py +0 -259
- everysk_lib-1.9.8/src/everysk/sql/settings.py +0 -22
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/LICENSE.txt +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/MANIFEST.in +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/README.md +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/setup.cfg +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/setup.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/_version.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_requestor.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/api_resource.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/calculation.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/custom_index.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/datastore.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/file.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/market_data.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/parser.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/portfolio.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/private_security.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/report.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/report_template.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/tests.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/worker_execution.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/workflow.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/workflow_execution.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/api_resources/workspace.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/http_client.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/tests.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/api/utils.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/config.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/_tests/serialize/test_json.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/_tests/serialize/test_orjson.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/_tests/serialize/test_pickle.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/cloud_function/main.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/cloud_function/tests.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/compress.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/datetime/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/datetime/calendar.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/datetime/date.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/datetime/date_expression.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/datetime/date_mixin.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/datetime/date_settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/datetime/datetime.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/exceptions.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/fields.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/firestore.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/fixtures/_settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/fixtures/other/_settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/fixtures/user_agents.json +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/http.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/lists.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/log.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/number.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/object.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/redis.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/serialize.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/sftp.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/signing.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/slack.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/string.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/threads.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/undefined.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/unittests.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/core/workers.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/engines/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/engines/cache.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/engines/compliance.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/engines/cryptography.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/engines/lock.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/engines/market_data.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/engines/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/base_list.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/custom_index/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/custom_index/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/datastore/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/datastore/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/fields.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/file/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/file/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/portfolio/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/portfolio/securities.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/portfolio/security.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/portfolio/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/private_security/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/private_security/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/query.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/report/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/report/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/script.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/secrets/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/secrets/script.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/secrets/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/tags.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/worker_execution/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/worker_execution/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/workflow_execution/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/workflow_execution/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/workspace/base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/entities/workspace/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/tests.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sdk/worker_base.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/applications.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/endpoints.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/example_api.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/middlewares.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/requests.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/responses.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/routing.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/settings.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/server/tests.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/sql/__init__.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/tests.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/utils.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk/version.py +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk_lib.egg-info/.gitignore +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk_lib.egg-info/dependency_links.txt +0 -0
- {everysk_lib-1.9.8 → everysk_lib-1.9.9}/src/everysk_lib.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: everysk-lib
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.9
|
|
4
4
|
Summary: Generic lib to share python code on Everysk.
|
|
5
5
|
License-Expression: LicenseRef-Proprietary
|
|
6
6
|
Project-URL: Homepage, https://everysk.com/
|
|
@@ -115,7 +115,9 @@ Requires-Dist: lark==1.2.2; extra == "expression"
|
|
|
115
115
|
Requires-Dist: numpy==1.26.4; extra == "expression"
|
|
116
116
|
Requires-Dist: pandas==2.1.4; extra == "expression"
|
|
117
117
|
Provides-Extra: postgresql
|
|
118
|
-
Requires-Dist:
|
|
118
|
+
Requires-Dist: psycopg-binary==3.3.0; extra == "postgresql"
|
|
119
|
+
Requires-Dist: psycopg-pool==3.3.0; extra == "postgresql"
|
|
120
|
+
Requires-Dist: psycopg==3.3.0; extra == "postgresql"
|
|
119
121
|
Dynamic: license-file
|
|
120
122
|
|
|
121
123
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
###############################################################################
|
|
2
|
+
#
|
|
3
|
+
# (C) Copyright 2025 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 typing import Any
|
|
11
|
+
|
|
12
|
+
from everysk.config import settings
|
|
13
|
+
from everysk.core.log import Logger
|
|
14
|
+
|
|
15
|
+
log = Logger('everysk-lib-core-retry')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def retry(
|
|
19
|
+
func: callable, params: dict, retry_count: int = 0, retries: int = 5, exceptions: tuple | Exception = Exception
|
|
20
|
+
) -> Any:
|
|
21
|
+
"""
|
|
22
|
+
Retries a function call a number of times if it raises an exception.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
func (callable): The function to be called.
|
|
26
|
+
params (dict): The parameters to be passed to the function.
|
|
27
|
+
retry_count (int, optional): The current retry count. Defaults to 0.
|
|
28
|
+
retries (int, optional): The maximum number of retries. Defaults to 5.
|
|
29
|
+
exceptions (tuple | Exception, optional): The exceptions to catch. Defaults to Exception.
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
Exception: If the maximum number of retries is reached.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
return func(**params)
|
|
36
|
+
except exceptions:
|
|
37
|
+
if retry_count < retries:
|
|
38
|
+
if settings.RETRY_SHOW_LOGS:
|
|
39
|
+
msg = f'Retry {retry_count + 1} of {retries} for function {func.__name__} due to exception.'
|
|
40
|
+
log.warning(
|
|
41
|
+
msg,
|
|
42
|
+
extra={
|
|
43
|
+
'function': func.__name__,
|
|
44
|
+
'params': params,
|
|
45
|
+
'retry_count': retry_count + 1,
|
|
46
|
+
'max_retries': retries,
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
return retry(func, params, retry_count + 1, retries, exceptions)
|
|
50
|
+
|
|
51
|
+
raise
|
|
@@ -0,0 +1,240 @@
|
|
|
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
|
+
# ruff: noqa: F401
|
|
11
|
+
|
|
12
|
+
## Remember to prefix all import with EveryskLib to avoid clash with other tests
|
|
13
|
+
|
|
14
|
+
## Cloud function Test Cases
|
|
15
|
+
try:
|
|
16
|
+
from everysk.core.cloud_function.tests import CloudFunctionTestCase as EveryskLibCloudFunctionTestCase
|
|
17
|
+
except ModuleNotFoundError as error:
|
|
18
|
+
# This will prevent running these tests if redis is not installed
|
|
19
|
+
if not error.args[0].startswith("No module named 'redis'"):
|
|
20
|
+
raise
|
|
21
|
+
|
|
22
|
+
## Compress Test Cases
|
|
23
|
+
from everysk.core._tests.compress import CompressGzipJsonTestCase as EveryskLibCompressGzipJsonTestCase
|
|
24
|
+
from everysk.core._tests.compress import CompressGzipPickleTestCase as EveryskLibCompressGzipPickleTestCase
|
|
25
|
+
from everysk.core._tests.compress import CompressTestCase as EveryskLibCompressTestCase
|
|
26
|
+
from everysk.core._tests.compress import CompressZlibJsonTestCase as EveryskLibCompressZlibJsonTestCase
|
|
27
|
+
from everysk.core._tests.compress import CompressZlibPickleTestCase as EveryskLibCompressZlibPickleTestCase
|
|
28
|
+
from everysk.core._tests.compress import FileHandlingTestCase as EveryskLibFileHandlingTestCase
|
|
29
|
+
|
|
30
|
+
## Config Test Cases
|
|
31
|
+
from everysk.core._tests.config import SettingsManagerTestCase as EveryskLibSettingsManagerTestCase
|
|
32
|
+
from everysk.core._tests.config import SettingsModulesTestCase as EveryskLibSettingsModulesTestCase
|
|
33
|
+
from everysk.core._tests.config import SettingsTestCase as EveryskLibSettingsTestCase
|
|
34
|
+
|
|
35
|
+
## Exceptions Test Cases
|
|
36
|
+
from everysk.core._tests.exceptions import BaseExceptionTestCase as EveryskLibBaseExceptionTestCase
|
|
37
|
+
from everysk.core._tests.exceptions import DefaultErrorTestCase as EveryskLibDefaultErrorTestCase
|
|
38
|
+
from everysk.core._tests.exceptions import FieldValueErrorTestCase as EveryskLibFieldValueErrorTestCase
|
|
39
|
+
from everysk.core._tests.exceptions import HandledExceptionTestCase as EveryskLibHandledExceptionTestCase
|
|
40
|
+
from everysk.core._tests.exceptions import HttpErrorTestCase as EveryskLibHttpErrorTestCase
|
|
41
|
+
from everysk.core._tests.exceptions import ReadonlyErrorTestCase as EveryskLibReadonlyErrorTestCase
|
|
42
|
+
from everysk.core._tests.exceptions import RequiredErrorTestCase as EveryskLibRequiredErrorTestCase
|
|
43
|
+
from everysk.core._tests.exceptions import SDKExceptionsTestCase as EveryskLibSDKExceptionsTestCase
|
|
44
|
+
from everysk.core._tests.exceptions import TestAPIError as EveryskLibTestAPIError
|
|
45
|
+
|
|
46
|
+
## Fields Test Cases
|
|
47
|
+
from everysk.core._tests.fields import BoolFieldTestCase as EveryskLibBoolFieldTestCase
|
|
48
|
+
from everysk.core._tests.fields import ChoiceFieldTestCase as EveryskLibChoiceFieldTestCase
|
|
49
|
+
from everysk.core._tests.fields import COD3770TestCase as EveryskLibCOD3770TestCase
|
|
50
|
+
from everysk.core._tests.fields import DateFieldTestCase as EveryskLibDateFieldTestCase
|
|
51
|
+
from everysk.core._tests.fields import DateTimeFieldTestCase as EveryskLibDateTimeFieldTestCase
|
|
52
|
+
from everysk.core._tests.fields import DictFieldTestCase as EveryskLibDictFieldTestCase
|
|
53
|
+
from everysk.core._tests.fields import EmailFieldTestCase as EveryskLibEmailFieldTestCase
|
|
54
|
+
from everysk.core._tests.fields import FieldTestCase as EveryskLibFieldTestCase
|
|
55
|
+
from everysk.core._tests.fields import FieldUndefinedTestCase as EveryskLibFieldUndefinedTestCase
|
|
56
|
+
from everysk.core._tests.fields import FloatFieldTestCase as EveryskLibFloatFieldTestCase
|
|
57
|
+
from everysk.core._tests.fields import IntFieldTestCase as EveryskLibIntFieldTestCase
|
|
58
|
+
from everysk.core._tests.fields import IteratorFieldTestCase as EveryskLibIteratorFieldTestCase
|
|
59
|
+
from everysk.core._tests.fields import ListFieldTestCase as EveryskLibListFieldTestCase
|
|
60
|
+
from everysk.core._tests.fields import ObjectInitPropertyTestCase as EveryskLibObjectInitPropertyTestCase
|
|
61
|
+
from everysk.core._tests.fields import SetFieldTestCase as EveryskLibSetFieldTestCase
|
|
62
|
+
from everysk.core._tests.fields import StrFieldTestCase as EveryskLibStrFieldTestCase
|
|
63
|
+
from everysk.core._tests.fields import TupleFieldTestCase as EveryskLibTupleFieldTestCase
|
|
64
|
+
from everysk.core._tests.fields import URLFieldTestCase as EveryskLibURLFieldTestCase
|
|
65
|
+
|
|
66
|
+
## Date, DateTime Test Cases
|
|
67
|
+
from everysk.core.datetime.tests.calendar import CalendarTestCase as EveryskLibCalendarTestCase
|
|
68
|
+
from everysk.core.datetime.tests.date import DateTestCase as EveryskLibDateTestCase
|
|
69
|
+
from everysk.core.datetime.tests.date_mixin import GetHolidaysTestCase as EveryskLibDateMixinGetHolidaysTestCase
|
|
70
|
+
from everysk.core.datetime.tests.datetime import DateTimeTestCase as EveryskLibDateTimeTestCase
|
|
71
|
+
|
|
72
|
+
## Firestore Test Cases
|
|
73
|
+
try:
|
|
74
|
+
from everysk.core._tests.firestore import (
|
|
75
|
+
BaseDocumentCachedConfigTestCase as EveryskLibBaseDocumentCachedConfigTestCase,
|
|
76
|
+
)
|
|
77
|
+
from everysk.core._tests.firestore import BaseDocumentConfigTestCase as EveryskLibBaseDocumentConfigTestCase
|
|
78
|
+
from everysk.core._tests.firestore import DocumentCachedTestCase as EveryskLibDocumentCachedTestCase
|
|
79
|
+
from everysk.core._tests.firestore import DocumentTestCase as EveryskLibDocumentTestCase
|
|
80
|
+
from everysk.core._tests.firestore import FirestoreClientTestCase as EveryskLibFirestoreClientTestCase
|
|
81
|
+
from everysk.core._tests.firestore import LoadsPaginatedTestCase as EveryskLibLoadsPaginatedTestCase
|
|
82
|
+
except ModuleNotFoundError as error:
|
|
83
|
+
# This will prevent running these tests if google-cloud-firestore is not installed
|
|
84
|
+
if not error.args[0].startswith("No module named 'google"):
|
|
85
|
+
raise
|
|
86
|
+
|
|
87
|
+
## Http Test Cases
|
|
88
|
+
try:
|
|
89
|
+
from everysk.core._tests.http import HttpConnectionConfigTestCase as EveryskLibHttpConnectionConfigTestCase
|
|
90
|
+
from everysk.core._tests.http import HttpConnectionTestCase as EveryskLibHttpConnectionTestCase
|
|
91
|
+
from everysk.core._tests.http import HttpDELETEConnectionTestCase as EveryskLibHttpDELETEConnectioNTestCase
|
|
92
|
+
from everysk.core._tests.http import HttpGETConnectionTestCase as EveryskLibHttpGETConnectionTestCase
|
|
93
|
+
from everysk.core._tests.http import HttpHEADConnectionTestCase as EveryskLibHttpHEADConnectionTestCase
|
|
94
|
+
from everysk.core._tests.http import HttpOPTIONSConnectionTestCase as EveryskLibHttpOPTIONSConnectionTestCase
|
|
95
|
+
from everysk.core._tests.http import HttpPATCHConnectionTestCase as EveryskLibHttpPATCHConnectionTestCase
|
|
96
|
+
from everysk.core._tests.http import (
|
|
97
|
+
HttpPOSTCompressedConnectionTestCase as EveryskLibHttpPOSTCompressedConnectionTestCase,
|
|
98
|
+
)
|
|
99
|
+
from everysk.core._tests.http import HttpPOSTConnectionTestCase as EveryskLibHttpPOSTConnectionTestCase
|
|
100
|
+
from everysk.core._tests.http import HttpPUTConnectionTestCase as EveryskLibHttpPUTCompressedConnectionTestCase
|
|
101
|
+
from everysk.core._tests.http import HttpSDKPOSTConnectionTestCase as EveryskLibHttpSDKPOSTConnectionTestCase
|
|
102
|
+
except ModuleNotFoundError as error:
|
|
103
|
+
# This will prevent running these tests if requests is not installed
|
|
104
|
+
if not error.args[0].startswith("No module named 'requests'"):
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
## Lists Test Cases
|
|
108
|
+
from everysk.core._tests.lists import SlicesTestCase as EveryskLibSlicesTestCase
|
|
109
|
+
from everysk.core._tests.lists import SortListDictTestCase as EveryskLibSortListDictTestCase
|
|
110
|
+
from everysk.core._tests.lists import SplitInSlicesTestCase as EveryskLibSplitInSlicesTestCase
|
|
111
|
+
|
|
112
|
+
## Log Test Cases
|
|
113
|
+
from everysk.core._tests.log import LoggerExtraDataTestCase as EveryskLibLoggerExtraDataTestCase
|
|
114
|
+
from everysk.core._tests.log import LoggerFormatterTestCase as EveryskLibLoggerFormatterTestCase
|
|
115
|
+
from everysk.core._tests.log import LoggerJsonTestCase as EveryskLibLoggerJsonTestCase
|
|
116
|
+
from everysk.core._tests.log import LoggerManagerTestCase as EveryskLibLoggerManagerTestCase
|
|
117
|
+
from everysk.core._tests.log import LoggerMethodsTestCase as EveryskLibLoggerMethodsTestCase
|
|
118
|
+
from everysk.core._tests.log import LoggerStackLevelTestCase as EveryskLibLoggerStackLevelTestCase
|
|
119
|
+
from everysk.core._tests.log import LoggerStdoutTestCase as EveryskLibLoggerStdoutTestCase
|
|
120
|
+
from everysk.core._tests.log import LoggerTestCase as EveryskLibLoggerTestCase
|
|
121
|
+
from everysk.core._tests.log import LoggerTraceTestCase as EveryskLibLogTraceTestCase
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
# We need requests to run this test
|
|
125
|
+
from everysk.core._tests.log import LoggerSlackTestCase as EveryskLibLoggerSlackTestCase
|
|
126
|
+
except ModuleNotFoundError as error:
|
|
127
|
+
# This will prevent running these tests if requests is not installed
|
|
128
|
+
if not error.args[0].startswith("No module named 'requests'"):
|
|
129
|
+
raise
|
|
130
|
+
|
|
131
|
+
## Number Test Cases
|
|
132
|
+
from everysk.core._tests.number import NumberTestCase as EveryskLibNumberTestCase
|
|
133
|
+
|
|
134
|
+
## Object Test Cases
|
|
135
|
+
from everysk.core._tests.object import AfterInitTestCase as EveryskLibAfterInitTestCase
|
|
136
|
+
from everysk.core._tests.object import BaseDictPropertyTestCase as EveryskLibBaseDictPropertyTestCase
|
|
137
|
+
from everysk.core._tests.object import BaseDictSuperTestCase as EveryskLibBaseDictSuperTestCase
|
|
138
|
+
from everysk.core._tests.object import BaseDictTestCase as EveryskLibBaseDictTestCase
|
|
139
|
+
from everysk.core._tests.object import BaseFieldTestCase as EveryskLibBaseFieldTestCase
|
|
140
|
+
from everysk.core._tests.object import BaseObjectTestCase as EveryskLibBaseObjectTestCase
|
|
141
|
+
from everysk.core._tests.object import BeforeInitTestCase as EveryskLibBeforeInitTestCase
|
|
142
|
+
from everysk.core._tests.object import ConfigHashTestCase as EveryskLibConfigHashTestCase
|
|
143
|
+
from everysk.core._tests.object import FrozenDictTestCase as EveryskLibFrozenDictTestCase
|
|
144
|
+
from everysk.core._tests.object import FrozenObjectTestCase as EveryskLibFrozenObjectTestCase
|
|
145
|
+
from everysk.core._tests.object import MetaClassAttributesTestCase as EveryskLibMetaClassAttributesTestCase
|
|
146
|
+
from everysk.core._tests.object import MetaClassConfigTestCase as EveryskLibMetaClassConfigTestCase
|
|
147
|
+
from everysk.core._tests.object import NpArrayTestCase as EveryskLibNpArrayTestCase
|
|
148
|
+
from everysk.core._tests.object import RequiredTestCase as EveryskLibRequiredTestCase
|
|
149
|
+
from everysk.core._tests.object import SilentTestCase as EveryskLibSilentTestCase
|
|
150
|
+
from everysk.core._tests.object import TypingCheckingTestCase as EveryskLibTypingCheckingTestCase
|
|
151
|
+
from everysk.core._tests.object import ValidateTestCase as EveryskLibValidateTestCase
|
|
152
|
+
|
|
153
|
+
## Redis Test Cases
|
|
154
|
+
try:
|
|
155
|
+
from everysk.core._tests.redis import CacheDecoratorTestCase as EveryskLibCacheDecoratorTestCase
|
|
156
|
+
from everysk.core._tests.redis import RedisCacheCompressedTestCase as EveryskLibRedisCacheCompressedTestCase
|
|
157
|
+
from everysk.core._tests.redis import RedisCacheGetSetTestCase as EveryskLibRedisCacheGetSetTestCase
|
|
158
|
+
from everysk.core._tests.redis import RedisCacheTestCase as EveryskLibRedisCacheTestCase
|
|
159
|
+
from everysk.core._tests.redis import RedisChannelTestCase as EveryskLibRedisChannelTestCase
|
|
160
|
+
from everysk.core._tests.redis import RedisClientTestCase as EveryskLibRedisClientTestCase
|
|
161
|
+
from everysk.core._tests.redis import RedisListTestCase as EveryskLibRedisListTestCase
|
|
162
|
+
from everysk.core._tests.redis import RedisLockTestCase as EveryskLibRedisLockTestCase
|
|
163
|
+
except ModuleNotFoundError as error:
|
|
164
|
+
# This will prevent running these tests if redis is not installed
|
|
165
|
+
if not error.args[0].startswith("No module named 'redis'"):
|
|
166
|
+
raise
|
|
167
|
+
|
|
168
|
+
## Retry Test Cases
|
|
169
|
+
from everysk.core._tests.retry import RetryTestCase as EveryskLibRetryTestCase
|
|
170
|
+
|
|
171
|
+
## Serialize Test Cases
|
|
172
|
+
from everysk.core._tests.serialize.test_json import SerializeJsonDumpsTestCase as EveryskLibSerializeJsonDumpsTestCase
|
|
173
|
+
from everysk.core._tests.serialize.test_json import SerializeJsonLoadsTestCase as EveryskLibSerializeJsonLoadsTestCase
|
|
174
|
+
from everysk.core._tests.serialize.test_pickle import (
|
|
175
|
+
SerializePickleDumpsTestCase as EveryskLibSerializePickleDumpsTestCase,
|
|
176
|
+
)
|
|
177
|
+
from everysk.core._tests.serialize.test_pickle import (
|
|
178
|
+
SerializePickleLoadsTestCase as EveryskLibSerializePickleLoadsTestCase,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
from everysk.core._tests.serialize.test_orjson import (
|
|
183
|
+
SerializeOrjsonDumpsTestCase as EveryskLibSerializeOrjsonDumpsTestCase,
|
|
184
|
+
)
|
|
185
|
+
from everysk.core._tests.serialize.test_orjson import (
|
|
186
|
+
SerializeOrjsonLoadsTestCase as EveryskLibSerializeOrjsonLoadsTestCase,
|
|
187
|
+
)
|
|
188
|
+
except ModuleNotFoundError as error:
|
|
189
|
+
# This will prevent running these tests if orjson is not installed
|
|
190
|
+
if not error.args[0].startswith("No module named 'orjson'"):
|
|
191
|
+
raise
|
|
192
|
+
|
|
193
|
+
## SFTP Test Cases
|
|
194
|
+
try:
|
|
195
|
+
from everysk.core._tests.sftp import KnownHostsTestCase as EveryskLibKnownHostsTestCase
|
|
196
|
+
from everysk.core._tests.sftp import SFTPTestCase as EveryskLibSFTPTestCase
|
|
197
|
+
except ModuleNotFoundError as error:
|
|
198
|
+
# This will prevent running these tests if Paramiko is not installed
|
|
199
|
+
if not error.args[0].startswith("No module named 'paramiko'"):
|
|
200
|
+
raise
|
|
201
|
+
|
|
202
|
+
## Signing Test Cases
|
|
203
|
+
from everysk.core._tests.signing import SignTestCase as EveryskLibSignTestCase
|
|
204
|
+
from everysk.core._tests.signing import UnsignTestCase as EveryskLibUnsignTestCase
|
|
205
|
+
|
|
206
|
+
## String Test Cases
|
|
207
|
+
from everysk.core._tests.string import StringTestCase as EveryskLibStringTestCase
|
|
208
|
+
|
|
209
|
+
## Slack Test Cases
|
|
210
|
+
try:
|
|
211
|
+
from everysk.core._tests.slack import SlackTestCase as EveryskLibSlackTestCase
|
|
212
|
+
except ModuleNotFoundError as error:
|
|
213
|
+
# This will prevent running these tests if requests is not installed
|
|
214
|
+
if not error.args[0].startswith("No module named 'requests'"):
|
|
215
|
+
raise
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
## Thread Test Cases
|
|
219
|
+
from everysk.core._tests.threads import ThreadPoolTestCase as EveryskLibThreadPoolTestCase
|
|
220
|
+
from everysk.core._tests.threads import ThreadTestCase as EveryskLibThreadTestCase
|
|
221
|
+
|
|
222
|
+
## Undefined Test Cases
|
|
223
|
+
from everysk.core._tests.undefined import UndefinedTestCase as EveryskLibUndefinedTestCase
|
|
224
|
+
|
|
225
|
+
## Unittest Test Cases
|
|
226
|
+
from everysk.core._tests.unittests import SDKUnittestTestCase as EveryskLibSDKUnittestTestCase
|
|
227
|
+
|
|
228
|
+
## Utils Test Cases
|
|
229
|
+
from everysk.core._tests.utils import BoolConverterTestCase as EveryskLibBoolConverterTestCase
|
|
230
|
+
from everysk.core._tests.utils import SearchKeyTestCase as EveryskLibSearchKeyTestCase
|
|
231
|
+
|
|
232
|
+
## Workers Test Cases
|
|
233
|
+
try:
|
|
234
|
+
from everysk.core._tests.workers import BaseGoogleTestCase as EveryskLibBaseGoogleTestCase
|
|
235
|
+
from everysk.core._tests.workers import TaskGoogleTestCase as EveryskLibTaskGoogleTestCase
|
|
236
|
+
from everysk.core._tests.workers import WorkerGoogleTestCase as EveryskLibWorkerGoogleTestCase
|
|
237
|
+
except ModuleNotFoundError as error:
|
|
238
|
+
# This will prevent running these tests if google-cloud-tasks is not installed
|
|
239
|
+
if not error.args[0].startswith("No module named 'google"):
|
|
240
|
+
raise
|
|
@@ -93,3 +93,6 @@ HTTP_USE_RANDOM_USER_AGENT = BoolField(default=False)
|
|
|
93
93
|
|
|
94
94
|
# Default directory to use the known_hosts file and other SFTP configurations
|
|
95
95
|
EVERYSK_SFTP_DIR = StrField(default=f'{tempfile.gettempdir()}/sftp')
|
|
96
|
+
|
|
97
|
+
# Enable to show retry logs
|
|
98
|
+
RETRY_SHOW_LOGS = BoolField(default=False)
|
|
@@ -7,18 +7,21 @@
|
|
|
7
7
|
# without authorization of EVERYSK TECHNOLOGIES is prohibited.
|
|
8
8
|
#
|
|
9
9
|
###############################################################################
|
|
10
|
+
from collections.abc import Callable
|
|
10
11
|
from contextvars import ContextVar, Token
|
|
11
12
|
from os import getpid
|
|
12
13
|
from types import TracebackType
|
|
13
14
|
from typing import Literal
|
|
14
15
|
|
|
15
|
-
from
|
|
16
|
-
from
|
|
16
|
+
from psycopg import Connection, OperationalError
|
|
17
|
+
from psycopg_pool import ConnectionPool as _ConnectionPool
|
|
17
18
|
|
|
18
19
|
from everysk.config import settings
|
|
19
20
|
from everysk.core.log import Logger
|
|
21
|
+
from everysk.core.retry import retry
|
|
22
|
+
from everysk.sql.row_factory import cls_row, dict_row
|
|
20
23
|
|
|
21
|
-
_CONNECTIONS: dict[str, ConnectionPool] = {}
|
|
24
|
+
_CONNECTIONS: dict[str, 'ConnectionPool'] = {}
|
|
22
25
|
log = Logger('everysk-lib-sql-query')
|
|
23
26
|
|
|
24
27
|
|
|
@@ -27,37 +30,49 @@ def _log(message: str, extra: dict | None = None) -> None:
|
|
|
27
30
|
log.debug(message, extra=extra)
|
|
28
31
|
|
|
29
32
|
|
|
33
|
+
class ConnectionPool(_ConnectionPool):
|
|
34
|
+
def __del__(self) -> None:
|
|
35
|
+
# To close the connections when the pool is deleted
|
|
36
|
+
# https://everysk.atlassian.net/browse/COD-8885
|
|
37
|
+
try:
|
|
38
|
+
return super().__del__()
|
|
39
|
+
except RuntimeError:
|
|
40
|
+
# The connection is already closed or discarded because we cannot join the current thread
|
|
41
|
+
# RuntimeError: cannot join current thread
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
30
47
|
class Transaction:
|
|
31
48
|
## Private attributes
|
|
32
49
|
_connection: Connection
|
|
33
50
|
_pool: ConnectionPool
|
|
34
51
|
_token: Token
|
|
35
|
-
_transaction: _Transaction
|
|
36
52
|
|
|
37
53
|
## Public attributes
|
|
38
|
-
connection: ContextVar[
|
|
54
|
+
connection: ContextVar[Connection] = ContextVar('postgresql-psqlpy-transaction', default=None)
|
|
39
55
|
|
|
40
56
|
def __init__(self, dsn: str | None = None) -> None:
|
|
41
57
|
self._pool: ConnectionPool = get_pool(dsn=dsn)
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
self._connection =
|
|
45
|
-
self.
|
|
46
|
-
await self._transaction.begin()
|
|
47
|
-
self._token = self.connection.set(self._transaction)
|
|
59
|
+
def __enter__(self) -> None:
|
|
60
|
+
self._connection = self._pool.getconn()
|
|
61
|
+
self._token = self.connection.set(self._connection)
|
|
48
62
|
|
|
49
63
|
return self
|
|
50
64
|
|
|
51
|
-
|
|
65
|
+
def __exit__(
|
|
52
66
|
self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
|
|
53
67
|
) -> None:
|
|
54
68
|
if exc_type is None:
|
|
55
|
-
|
|
69
|
+
self._connection.commit()
|
|
56
70
|
else:
|
|
57
|
-
|
|
71
|
+
self._connection.rollback()
|
|
58
72
|
|
|
59
73
|
self.connection.reset(self._token)
|
|
60
|
-
|
|
74
|
+
# Return the connection to the pool
|
|
75
|
+
self._pool.putconn(self._connection)
|
|
61
76
|
|
|
62
77
|
return False
|
|
63
78
|
|
|
@@ -89,7 +104,7 @@ def make_connection_dsn(
|
|
|
89
104
|
return 'postgresql://{user}:{password}@{host}:{port}/{database}'.format(**options)
|
|
90
105
|
|
|
91
106
|
|
|
92
|
-
def get_pool(dsn: str | None = None) -> ConnectionPool:
|
|
107
|
+
def get_pool(dsn: str | None = None, **kwargs) -> ConnectionPool:
|
|
93
108
|
"""
|
|
94
109
|
Retrieve a database connection pool for the given DSN.
|
|
95
110
|
|
|
@@ -102,28 +117,38 @@ def get_pool(dsn: str | None = None) -> ConnectionPool:
|
|
|
102
117
|
|
|
103
118
|
Args:
|
|
104
119
|
dsn (str | None): The Data Source Name for the database connection. If None, a default DSN is used.
|
|
120
|
+
**kwargs: Additional keyword arguments to configure the connection pool.
|
|
105
121
|
|
|
106
122
|
Returns:
|
|
107
123
|
ConnectionPool: The connection pool associated with the given DSN.
|
|
108
124
|
"""
|
|
109
125
|
dsn = dsn or make_connection_dsn()
|
|
126
|
+
# https://www.psycopg.org/psycopg3/docs/api/pool.html
|
|
127
|
+
kwargs['check'] = ConnectionPool.check_connection
|
|
128
|
+
kwargs['min_size'] = kwargs.get('min_size', settings.POSTGRESQL_POOL_MIN_SIZE)
|
|
129
|
+
kwargs['max_size'] = kwargs.get('max_size', settings.POSTGRESQL_POOL_MAX_SIZE)
|
|
130
|
+
kwargs['max_idle'] = kwargs.get('max_idle', settings.POSTGRESQL_POOL_MAX_IDLE)
|
|
131
|
+
kwargs['max_lifetime'] = kwargs.get('max_lifetime', settings.POSTGRESQL_POOL_MAX_LIFETIME)
|
|
132
|
+
kwargs['max_waiting'] = kwargs.get('max_waiting', settings.POSTGRESQL_POOL_MAX_WAITING)
|
|
133
|
+
kwargs['reconnect_timeout'] = kwargs.get('reconnect_timeout', settings.POSTGRESQL_POOL_RECONNECT_TIMEOUT)
|
|
134
|
+
kwargs['timeout'] = kwargs.get('timeout', settings.POSTGRESQL_POOL_TIMEOUT)
|
|
135
|
+
kwargs['open'] = kwargs.get('open', settings.POSTGRESQL_POOL_OPEN)
|
|
136
|
+
|
|
110
137
|
key = f'{getpid()}:{hash(dsn)}'
|
|
111
138
|
if key not in _CONNECTIONS:
|
|
112
|
-
_CONNECTIONS[key] = ConnectionPool(
|
|
113
|
-
|
|
114
|
-
max_db_pool_size=settings.POSTGRESQL_POOL_MAX_SIZE,
|
|
115
|
-
ssl_mode=getattr(SslMode, settings.POSTGRESQL_CONNECTION_ENCRYPTION, None),
|
|
116
|
-
)
|
|
139
|
+
_CONNECTIONS[key] = ConnectionPool(conninfo=dsn, **kwargs)
|
|
140
|
+
|
|
117
141
|
return _CONNECTIONS[key]
|
|
118
142
|
|
|
119
143
|
|
|
120
|
-
|
|
144
|
+
def execute(
|
|
121
145
|
query: str,
|
|
122
146
|
params: dict | None = None,
|
|
123
147
|
return_type: Literal['dict', 'list'] = 'list',
|
|
124
148
|
dsn: str | None = None,
|
|
125
149
|
cls: type | None = None,
|
|
126
|
-
|
|
150
|
+
loads: Callable | None = None,
|
|
151
|
+
) -> list[dict] | list[object] | dict | None:
|
|
127
152
|
"""
|
|
128
153
|
Execute a query and return the results.
|
|
129
154
|
If return_type is a class, return a list of instances of that class.
|
|
@@ -136,25 +161,48 @@ async def execute(
|
|
|
136
161
|
return_type (Literal['dict', 'list'], optional): The type of return value. Defaults to 'list'.
|
|
137
162
|
dsn (str | None, optional): The DSN to use for the connection. Defaults to None.
|
|
138
163
|
cls (type | None, optional): The class to map the results to. Defaults to None.
|
|
164
|
+
loads (Callable | None, optional): Optional function to process each value. Defaults to None.
|
|
165
|
+
retry (int, optional): The current retry count. Defaults to 0.
|
|
139
166
|
"""
|
|
140
|
-
conn = Transaction.connection.get()
|
|
167
|
+
conn: Connection = Transaction.connection.get()
|
|
141
168
|
if not conn:
|
|
142
169
|
pool: ConnectionPool = get_pool(dsn=dsn)
|
|
143
|
-
conn =
|
|
170
|
+
conn: Connection = pool.getconn()
|
|
171
|
+
is_transactional = False
|
|
144
172
|
log_message = 'PostgreSQL query executed.'
|
|
145
173
|
else:
|
|
174
|
+
is_transactional = True
|
|
146
175
|
log_message = 'PostgreSQL query executed within transaction.'
|
|
147
176
|
|
|
148
177
|
_log(log_message, extra={'labels': {'query': query, 'params': params}})
|
|
149
|
-
result: QueryResult = await conn.execute(query, params)
|
|
150
|
-
if not Transaction.connection.get():
|
|
151
|
-
conn.close()
|
|
152
178
|
|
|
153
|
-
if cls
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
179
|
+
row_factory = cls_row(cls, loads) if cls else dict_row(loads)
|
|
180
|
+
# For transactions we let it be controlled externally by the context manager
|
|
181
|
+
try:
|
|
182
|
+
with conn.cursor(row_factory=row_factory) as cur:
|
|
183
|
+
result = retry(cur.execute, {'query': query, 'params': params}, retries=3, exceptions=OperationalError)
|
|
184
|
+
|
|
185
|
+
if result.description:
|
|
186
|
+
result = cur.fetchall()
|
|
187
|
+
else:
|
|
188
|
+
result = None
|
|
189
|
+
except Exception:
|
|
190
|
+
# On error we need to rollback
|
|
191
|
+
if not is_transactional:
|
|
192
|
+
conn.rollback()
|
|
193
|
+
raise
|
|
194
|
+
|
|
195
|
+
else:
|
|
196
|
+
# Block that only executes if no exception was raised in the try block
|
|
197
|
+
if not is_transactional:
|
|
198
|
+
conn.commit()
|
|
199
|
+
|
|
200
|
+
finally:
|
|
201
|
+
# We only return the connection to the pool if we are not in a transaction
|
|
202
|
+
if not is_transactional:
|
|
203
|
+
pool.putconn(conn)
|
|
157
204
|
|
|
158
|
-
|
|
205
|
+
if result and cls and return_type == 'dict':
|
|
206
|
+
return {row[cls._primary_key]: row for row in result}
|
|
159
207
|
|
|
160
|
-
return result
|
|
208
|
+
return result
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
# without authorization of EVERYSK TECHNOLOGIES is prohibited.
|
|
8
8
|
#
|
|
9
9
|
###############################################################################
|
|
10
|
-
import asyncio
|
|
11
10
|
import inspect
|
|
12
11
|
from copy import deepcopy
|
|
13
12
|
from types import GenericAlias, UnionType
|
|
@@ -260,12 +259,8 @@ class BaseModel(dict, metaclass=BaseModelMetaClass):
|
|
|
260
259
|
return_type: Literal['dict', 'list'] = 'list',
|
|
261
260
|
klass: type | None = None,
|
|
262
261
|
) -> Any:
|
|
263
|
-
loop = asyncio.get_event_loop()
|
|
264
262
|
kwargs = {'query': query, 'params': params, 'return_type': return_type, 'dsn': cls._dsn, 'cls': klass}
|
|
265
|
-
|
|
266
|
-
return asyncio.run_coroutine_threadsafe(execute(**kwargs), loop).result()
|
|
267
|
-
|
|
268
|
-
return loop.run_until_complete(execute(**kwargs))
|
|
263
|
+
return execute(**kwargs)
|
|
269
264
|
|
|
270
265
|
@classmethod
|
|
271
266
|
def _generate_attributes(cls) -> None:
|
|
@@ -8,12 +8,29 @@
|
|
|
8
8
|
#
|
|
9
9
|
###############################################################################
|
|
10
10
|
import hashlib
|
|
11
|
+
from functools import partial
|
|
11
12
|
|
|
12
|
-
from
|
|
13
|
+
from psycopg.types.json import Jsonb, set_json_dumps, set_json_loads
|
|
13
14
|
|
|
14
15
|
from everysk.core.object import BaseDict
|
|
16
|
+
from everysk.core.serialize import dumps, loads
|
|
15
17
|
from everysk.sql.utils import ConditionOperator
|
|
16
18
|
|
|
19
|
+
# https://www.psycopg.org/psycopg3/docs/basic/adapt.html#json-adaptation
|
|
20
|
+
set_json_dumps(
|
|
21
|
+
partial(
|
|
22
|
+
dumps,
|
|
23
|
+
add_class_path=True,
|
|
24
|
+
date_format='%Y-%m-%d',
|
|
25
|
+
datetime_format='%Y-%m-%dT%H:%M:%S',
|
|
26
|
+
indent=None,
|
|
27
|
+
separators=(',', ':'),
|
|
28
|
+
use_undefined=True,
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
set_json_loads(partial(loads, use_undefined=True, instantiate_object=True))
|
|
32
|
+
|
|
33
|
+
|
|
17
34
|
## Constants
|
|
18
35
|
_SQL_FIELDS = {
|
|
19
36
|
'bool': 'BOOLEAN',
|
|
@@ -42,7 +59,7 @@ _SQL_CREATE_SCHEMA = 'CREATE SCHEMA IF NOT EXISTS "{schema}"'
|
|
|
42
59
|
_SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS "{schema}"."{table}" ({fields})'
|
|
43
60
|
|
|
44
61
|
# https://www.postgresqltutorial.com/postgresql-delete/
|
|
45
|
-
_SQL_DELETE = 'DELETE FROM "{schema}"."{table}" WHERE "{primary_key}" = ANY(
|
|
62
|
+
_SQL_DELETE = 'DELETE FROM "{schema}"."{table}" WHERE "{primary_key}" = ANY(%(ids)s)'
|
|
46
63
|
|
|
47
64
|
# https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-upsert/
|
|
48
65
|
# https://stackoverflow.com/a/30917361
|
|
@@ -201,11 +218,13 @@ class Query:
|
|
|
201
218
|
|
|
202
219
|
try:
|
|
203
220
|
offset = int(offset)
|
|
204
|
-
except (ValueError, TypeError):
|
|
205
|
-
|
|
221
|
+
except (ValueError, TypeError) as error:
|
|
222
|
+
msg = 'Offset must be an integer or a string representing it.'
|
|
223
|
+
raise TypeError(msg) from error
|
|
206
224
|
|
|
207
225
|
if offset < 0:
|
|
208
|
-
|
|
226
|
+
msg = 'Offset must not be negative.'
|
|
227
|
+
raise TypeError(msg)
|
|
209
228
|
|
|
210
229
|
if offset == 0:
|
|
211
230
|
return ''
|
|
@@ -296,7 +315,7 @@ class Query:
|
|
|
296
315
|
"""
|
|
297
316
|
# Create the values string
|
|
298
317
|
# We do not use " here because we are using the values as placeholders
|
|
299
|
-
values = ', '.join(f'
|
|
318
|
+
values = ', '.join(f'%({field})s' for field in fields)
|
|
300
319
|
|
|
301
320
|
# Create the SQL query
|
|
302
321
|
update = ', '.join(f'"{field}" = EXCLUDED."{field}"' for field in fields)
|
|
@@ -320,17 +339,14 @@ class Query:
|
|
|
320
339
|
Args:
|
|
321
340
|
params (dict): A dictionary of parameters to prepare.
|
|
322
341
|
"""
|
|
323
|
-
# https://
|
|
342
|
+
# https://www.psycopg.org/psycopg3/docs/basic/adapt.html#json-adaptation
|
|
324
343
|
for key, value in params.items():
|
|
325
344
|
if isinstance(value, (set, tuple)):
|
|
326
|
-
params[key] =
|
|
327
|
-
elif isinstance(value, list):
|
|
328
|
-
params[key] =
|
|
345
|
+
params[key] = Jsonb(list(value))
|
|
346
|
+
elif isinstance(value, (dict, list)):
|
|
347
|
+
params[key] = Jsonb(value)
|
|
329
348
|
elif isinstance(value, BaseDict):
|
|
330
|
-
params[key] =
|
|
331
|
-
elif isinstance(value, str):
|
|
332
|
-
# Text is needed to differentiate between VARCHAR and TEXT
|
|
333
|
-
params[key] = Text(value)
|
|
349
|
+
params[key] = Jsonb(value.to_dict())
|
|
334
350
|
|
|
335
351
|
return params
|
|
336
352
|
|