everysk-lib 1.9.7__cp312-cp312-macosx_11_0_arm64.whl → 1.9.9__cp312-cp312-macosx_11_0_arm64.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/core/retry.py ADDED
@@ -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
everysk/core/tests.py CHANGED
@@ -7,7 +7,7 @@
7
7
  # without authorization of EVERYSK TECHNOLOGIES is prohibited.
8
8
  #
9
9
  ###############################################################################
10
- # pylint: disable=unused-import
10
+ # ruff: noqa: F401
11
11
 
12
12
  ## Remember to prefix all import with EveryskLib to avoid clash with other tests
13
13
 
@@ -17,200 +17,194 @@ try:
17
17
  except ModuleNotFoundError as error:
18
18
  # This will prevent running these tests if redis is not installed
19
19
  if not error.args[0].startswith("No module named 'redis'"):
20
- raise error
20
+ raise
21
21
 
22
22
  ## Compress Test Cases
23
- from everysk.core._tests.compress import (
24
- CompressTestCase as EveryskLibCompressTestCase,
25
- CompressGzipJsonTestCase as EveryskLibCompressGzipJsonTestCase,
26
- CompressGzipPickleTestCase as EveryskLibCompressGzipPickleTestCase,
27
- CompressZlibJsonTestCase as EveryskLibCompressZlibJsonTestCase,
28
- CompressZlibPickleTestCase as EveryskLibCompressZlibPickleTestCase,
29
- FileHandlingTestCase as EveryskLibFileHandlingTestCase
30
- )
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
31
29
 
32
30
  ## Config Test Cases
33
- from everysk.core._tests.config import (
34
- SettingsModulesTestCase as EveryskLibSettingsModulesTestCase,
35
- SettingsTestCase as EveryskLibSettingsTestCase,
36
- SettingsManagerTestCase as EveryskLibSettingsManagerTestCase
37
- )
38
-
39
- ## Date, DateTime Test Cases
40
- from everysk.core.datetime.tests.date import DateTestCase as EveryskLibDateTestCase
41
- from everysk.core.datetime.tests.datetime import DateTimeTestCase as EveryskLibDateTimeTestCase
42
- from everysk.core.datetime.tests.date_mixin import GetHolidaysTestCase as EveryskLibDateMixinGetHolidaysTestCase
43
- from everysk.core.datetime.tests.calendar import CalendarTestCase as EveryskLibCalendarTestCase
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
44
34
 
45
35
  ## Exceptions Test Cases
46
- from everysk.core._tests.exceptions import (
47
- BaseExceptionTestCase as EveryskLibBaseExceptionTestCase,
48
- DefaultErrorTestCase as EveryskLibDefaultErrorTestCase,
49
- HttpErrorTestCase as EveryskLibHttpErrorTestCase,
50
- FieldValueErrorTestCase as EveryskLibFieldValueErrorTestCase,
51
- ReadonlyErrorTestCase as EveryskLibReadonlyErrorTestCase,
52
- RequiredErrorTestCase as EveryskLibRequiredErrorTestCase,
53
- TestAPIError as EveryskLibTestAPIError,
54
- HandledExceptionTestCase as EveryskLibHandledExceptionTestCase,
55
- SDKExceptionsTestCase as EveryskLibSDKExceptionsTestCase
56
- )
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
57
45
 
58
46
  ## Fields Test Cases
59
- from everysk.core._tests.fields import (
60
- BoolFieldTestCase as EveryskLibBoolFieldTestCase,
61
- ChoiceFieldTestCase as EveryskLibChoiceFieldTestCase,
62
- DateFieldTestCase as EveryskLibDateFieldTestCase,
63
- DateTimeFieldTestCase as EveryskLibDateTimeFieldTestCase,
64
- DictFieldTestCase as EveryskLibDictFieldTestCase,
65
- FieldTestCase as EveryskLibFieldTestCase,
66
- FieldUndefinedTestCase as EveryskLibFieldUndefinedTestCase,
67
- FloatFieldTestCase as EveryskLibFloatFieldTestCase,
68
- IntFieldTestCase as EveryskLibIntFieldTestCase,
69
- IteratorFieldTestCase as EveryskLibIteratorFieldTestCase,
70
- ListFieldTestCase as EveryskLibListFieldTestCase,
71
- StrFieldTestCase as EveryskLibStrFieldTestCase,
72
- TupleFieldTestCase as EveryskLibTupleFieldTestCase,
73
- ObjectInitPropertyTestCase as EveryskLibObjectInitPropertyTestCase,
74
- COD3770TestCase as EveryskLibCOD3770TestCase,
75
- URLFieldTestCase as EveryskLibURLFieldTestCase,
76
- SetFieldTestCase as EveryskLibSetFieldTestCase,
77
- EmailFieldTestCase as EveryskLibEmailFieldTestCase
78
- )
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
79
71
 
80
72
  ## Firestore Test Cases
81
73
  try:
82
74
  from everysk.core._tests.firestore import (
83
75
  BaseDocumentCachedConfigTestCase as EveryskLibBaseDocumentCachedConfigTestCase,
84
- BaseDocumentConfigTestCase as EveryskLibBaseDocumentConfigTestCase,
85
- DocumentCachedTestCase as EveryskLibDocumentCachedTestCase,
86
- DocumentTestCase as EveryskLibDocumentTestCase,
87
- FirestoreClientTestCase as EveryskLibFirestoreClientTestCase,
88
- LoadsPaginatedTestCase as EveryskLibLoadsPaginatedTestCase
89
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
90
82
  except ModuleNotFoundError as error:
91
83
  # This will prevent running these tests if google-cloud-firestore is not installed
92
84
  if not error.args[0].startswith("No module named 'google"):
93
- raise error
85
+ raise
94
86
 
95
87
  ## Http Test Cases
96
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
97
96
  from everysk.core._tests.http import (
98
- HttpConnectionTestCase as EveryskLibHttpConnectionTestCase,
99
- HttpConnectionConfigTestCase as EveryskLibHttpConnectionConfigTestCase,
100
- HttpGETConnectionTestCase as EveryskLibHttpGETConnectionTestCase,
101
- HttpPOSTConnectionTestCase as EveryskLibHttpPOSTConnectionTestCase,
102
- HttpSDKPOSTConnectionTestCase as EveryskLibHttpSDKPOSTConnectionTestCase,
103
97
  HttpPOSTCompressedConnectionTestCase as EveryskLibHttpPOSTCompressedConnectionTestCase,
104
- HttpDELETEConnectionTestCase as EveryskLibHttpDELETEConnectioNTestCase,
105
- HttpHEADConnectionTestCase as EveryskLibHttpHEADConnectionTestCase,
106
- HttpOPTIONSConnectionTestCase as EveryskLibHttpOPTIONSConnectionTestCase,
107
- HttpPATCHConnectionTestCase as EveryskLibHttpPATCHConnectionTestCase,
108
- HttpPUTConnectionTestCase as EveryskLibHttpPUTCompressedConnectionTestCase
109
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
110
102
  except ModuleNotFoundError as error:
111
103
  # This will prevent running these tests if requests is not installed
112
104
  if not error.args[0].startswith("No module named 'requests'"):
113
- raise error
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
114
111
 
115
112
  ## Log Test Cases
116
- from everysk.core._tests.log import (
117
- LoggerExtraDataTestCase as EveryskLibLoggerExtraDataTestCase,
118
- LoggerFormatterTestCase as EveryskLibLoggerFormatterTestCase,
119
- LoggerJsonTestCase as EveryskLibLoggerJsonTestCase,
120
- LoggerManagerTestCase as EveryskLibLoggerManagerTestCase,
121
- LoggerMethodsTestCase as EveryskLibLoggerMethodsTestCase,
122
- LoggerStackLevelTestCase as EveryskLibLoggerStackLevelTestCase,
123
- LoggerStdoutTestCase as EveryskLibLoggerStdoutTestCase,
124
- LoggerTestCase as EveryskLibLoggerTestCase,
125
- LoggerTraceTestCase as EveryskLibLogTraceTestCase,
126
- )
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
+
127
123
  try:
128
- from everysk.core._tests.log import LoggerSlackTestCase as EveryskLibLoggerSlackTestCase # We need requests to run this test
124
+ # We need requests to run this test
125
+ from everysk.core._tests.log import LoggerSlackTestCase as EveryskLibLoggerSlackTestCase
129
126
  except ModuleNotFoundError as error:
130
127
  # This will prevent running these tests if requests is not installed
131
128
  if not error.args[0].startswith("No module named 'requests'"):
132
- raise error
133
-
134
-
135
- ## Object Test Cases
136
- from everysk.core._tests.object import (
137
- BaseDictPropertyTestCase as EveryskLibBaseDictPropertyTestCase,
138
- BaseDictTestCase as EveryskLibBaseDictTestCase,
139
- BaseFieldTestCase as EveryskLibBaseFieldTestCase,
140
- BaseObjectTestCase as EveryskLibBaseObjectTestCase,
141
- ConfigHashTestCase as EveryskLibConfigHashTestCase,
142
- FrozenDictTestCase as EveryskLibFrozenDictTestCase,
143
- FrozenObjectTestCase as EveryskLibFrozenObjectTestCase,
144
- MetaClassConfigTestCase as EveryskLibMetaClassConfigTestCase,
145
- RequiredTestCase as EveryskLibRequiredTestCase,
146
- ValidateTestCase as EveryskLibValidateTestCase,
147
- MetaClassAttributesTestCase as EveryskLibMetaClassAttributesTestCase,
148
- NpArrayTestCase as EveryskLibNpArrayTestCase,
149
- AfterInitTestCase as EveryskLibAfterInitTestCase,
150
- BeforeInitTestCase as EveryskLibBeforeInitTestCase,
151
- SilentTestCase as EveryskLibSilentTestCase,
152
- TypingCheckingTestCase as EveryskLibTypingCheckingTestCase,
153
- BaseDictSuperTestCase as EveryskLibBaseDictSuperTestCase
154
- )
129
+ raise
155
130
 
156
131
  ## Number Test Cases
157
132
  from everysk.core._tests.number import NumberTestCase as EveryskLibNumberTestCase
158
133
 
159
- ## Signing Test Cases
160
- from everysk.core._tests.signing import (
161
- SignTestCase as EveryskLibSignTestCase,
162
- UnsignTestCase as EveryskLibUnsignTestCase
163
- )
164
-
165
- ## String Test Cases
166
- from everysk.core._tests.string import StringTestCase as EveryskLibStringTestCase
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
167
152
 
168
153
  ## Redis Test Cases
169
154
  try:
170
- from everysk.core._tests.redis import (
171
- RedisCacheCompressedTestCase as EveryskLibRedisCacheCompressedTestCase,
172
- RedisCacheTestCase as EveryskLibRedisCacheTestCase,
173
- RedisChannelTestCase as EveryskLibRedisChannelTestCase,
174
- RedisClientTestCase as EveryskLibRedisClientTestCase,
175
- RedisListTestCase as EveryskLibRedisListTestCase,
176
- RedisLockTestCase as EveryskLibRedisLockTestCase,
177
- RedisCacheGetSetTestCase as EveryskLibRedisCacheGetSetTestCase,
178
- CacheDecoratorTestCase as EveryskLibCacheDecoratorTestCase
179
- )
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
180
163
  except ModuleNotFoundError as error:
181
164
  # This will prevent running these tests if redis is not installed
182
165
  if not error.args[0].startswith("No module named 'redis'"):
183
- raise error
166
+ raise
167
+
168
+ ## Retry Test Cases
169
+ from everysk.core._tests.retry import RetryTestCase as EveryskLibRetryTestCase
184
170
 
185
171
  ## Serialize Test Cases
186
- from everysk.core._tests.serialize.test_json import (
187
- SerializeJsonDumpsTestCase as EveryskLibSerializeJsonDumpsTestCase,
188
- SerializeJsonLoadsTestCase as EveryskLibSerializeJsonLoadsTestCase
189
- )
172
+ from everysk.core._tests.serialize.test_json import SerializeJsonDumpsTestCase as EveryskLibSerializeJsonDumpsTestCase
173
+ from everysk.core._tests.serialize.test_json import SerializeJsonLoadsTestCase as EveryskLibSerializeJsonLoadsTestCase
190
174
  from everysk.core._tests.serialize.test_pickle import (
191
175
  SerializePickleDumpsTestCase as EveryskLibSerializePickleDumpsTestCase,
192
- SerializePickleLoadsTestCase as EveryskLibSerializePickleLoadsTestCase
193
176
  )
177
+ from everysk.core._tests.serialize.test_pickle import (
178
+ SerializePickleLoadsTestCase as EveryskLibSerializePickleLoadsTestCase,
179
+ )
180
+
194
181
  try:
195
182
  from everysk.core._tests.serialize.test_orjson import (
196
183
  SerializeOrjsonDumpsTestCase as EveryskLibSerializeOrjsonDumpsTestCase,
197
- SerializeOrjsonLoadsTestCase as EveryskLibSerializeOrjsonLoadsTestCase
184
+ )
185
+ from everysk.core._tests.serialize.test_orjson import (
186
+ SerializeOrjsonLoadsTestCase as EveryskLibSerializeOrjsonLoadsTestCase,
198
187
  )
199
188
  except ModuleNotFoundError as error:
200
189
  # This will prevent running these tests if orjson is not installed
201
190
  if not error.args[0].startswith("No module named 'orjson'"):
202
- raise error
191
+ raise
203
192
 
204
193
  ## SFTP Test Cases
205
194
  try:
206
- from everysk.core._tests.sftp import (
207
- KnownHostsTestCase as EveryskLibKnownHostsTestCase,
208
- SFTPTestCase as EveryskLibSFTPTestCase
209
- )
195
+ from everysk.core._tests.sftp import KnownHostsTestCase as EveryskLibKnownHostsTestCase
196
+ from everysk.core._tests.sftp import SFTPTestCase as EveryskLibSFTPTestCase
210
197
  except ModuleNotFoundError as error:
211
198
  # This will prevent running these tests if Paramiko is not installed
212
199
  if not error.args[0].startswith("No module named 'paramiko'"):
213
- raise error
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
214
208
 
215
209
  ## Slack Test Cases
216
210
  try:
@@ -218,14 +212,12 @@ try:
218
212
  except ModuleNotFoundError as error:
219
213
  # This will prevent running these tests if requests is not installed
220
214
  if not error.args[0].startswith("No module named 'requests'"):
221
- raise error
215
+ raise
222
216
 
223
217
 
224
218
  ## Thread Test Cases
225
- from everysk.core._tests.threads import (
226
- ThreadPoolTestCase as EveryskLibThreadPoolTestCase,
227
- ThreadTestCase as EveryskLibThreadTestCase
228
- )
219
+ from everysk.core._tests.threads import ThreadPoolTestCase as EveryskLibThreadPoolTestCase
220
+ from everysk.core._tests.threads import ThreadTestCase as EveryskLibThreadTestCase
229
221
 
230
222
  ## Undefined Test Cases
231
223
  from everysk.core._tests.undefined import UndefinedTestCase as EveryskLibUndefinedTestCase
@@ -234,26 +226,15 @@ from everysk.core._tests.undefined import UndefinedTestCase as EveryskLibUndefin
234
226
  from everysk.core._tests.unittests import SDKUnittestTestCase as EveryskLibSDKUnittestTestCase
235
227
 
236
228
  ## Utils Test Cases
237
- from everysk.core._tests.utils import (
238
- BoolConverterTestCase as EveryskLibBoolConverterTestCase,
239
- SearchKeyTestCase as EveryskLibSearchKeyTestCase
240
- )
241
-
242
- ## Lists Test Cases
243
- from everysk.core._tests.lists import (
244
- SplitInSlicesTestCase as EveryskLibSplitInSlicesTestCase,
245
- SlicesTestCase as EveryskLibSlicesTestCase,
246
- SortListDictTestCase as EveryskLibSortListDictTestCase
247
- )
229
+ from everysk.core._tests.utils import BoolConverterTestCase as EveryskLibBoolConverterTestCase
230
+ from everysk.core._tests.utils import SearchKeyTestCase as EveryskLibSearchKeyTestCase
248
231
 
249
232
  ## Workers Test Cases
250
233
  try:
251
- from everysk.core._tests.workers import (
252
- BaseGoogleTestCase as EveryskLibBaseGoogleTestCase,
253
- TaskGoogleTestCase as EveryskLibTaskGoogleTestCase,
254
- WorkerGoogleTestCase as EveryskLibWorkerGoogleTestCase
255
- )
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
256
237
  except ModuleNotFoundError as error:
257
238
  # This will prevent running these tests if google-cloud-tasks is not installed
258
239
  if not error.args[0].startswith("No module named 'google"):
259
- raise error
240
+ raise
@@ -119,7 +119,10 @@ class Secrets(BaseEntity, metaclass=SecretsScriptMetaClass):
119
119
  path (str): The path to retrieve the value from.
120
120
 
121
121
  Returns:
122
- Any: The value retrieved from the path.
122
+ Any | None: The value retrieved from the path or None if the path is empty.
123
123
  """
124
+ if not path:
125
+ return None
126
+
124
127
  _, _ = cls._split_path(path)
125
128
  return cls.get_response(params={'path': path})
everysk/settings.py CHANGED
@@ -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)
everysk/sql/connection.py CHANGED
@@ -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 psqlpy import Connection, ConnectionPool, QueryResult, SslMode
16
- from psqlpy import Transaction as _Transaction
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[_Transaction] = ContextVar('postgresql-psqlpy-transaction', default=None)
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
- async def __aenter__(self) -> None:
44
- self._connection = await self._pool.connection()
45
- self._transaction = self._connection.transaction()
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
- async def __aexit__(
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
- await self._transaction.commit()
69
+ self._connection.commit()
56
70
  else:
57
- await self._transaction.rollback()
71
+ self._connection.rollback()
58
72
 
59
73
  self.connection.reset(self._token)
60
- self._connection.close()
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
- dsn=dsn,
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
- async def execute(
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
- ) -> list[dict] | list[object] | dict:
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 = await pool.connection()
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
- result = result.as_class(cls)
155
- if return_type == 'dict':
156
- return {row[cls._primary_key]: row for row in result}
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
- return result
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.result()
208
+ return result
everysk/sql/model.py CHANGED
@@ -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
- if loop.is_running():
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:
everysk/sql/query.py CHANGED
@@ -8,12 +8,29 @@
8
8
  #
9
9
  ###############################################################################
10
10
  import hashlib
11
+ from functools import partial
11
12
 
12
- from psqlpy.extra_types import JSONB, Text
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($(ids)p)'
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
- raise TypeError('Offset must be an integer or a string representing it.')
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
- raise TypeError('Offset must not be negative.')
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'$({field})p' for field in fields)
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://psqlpy-python.github.io/usage/types/extra_types.html
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] = JSONB(list(value))
327
- elif isinstance(value, list):
328
- params[key] = JSONB(value)
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] = JSONB(value.to_dict())
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
 
@@ -0,0 +1,63 @@
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 collections.abc import Callable, Iterable
11
+ from typing import Any
12
+
13
+ from psycopg import Cursor
14
+ from psycopg.rows import _get_names, no_result
15
+
16
+
17
+ def cls_row(cls: type, loads: Callable | None = None) -> Callable:
18
+ """
19
+ Function to convert a row from a cursor to an instance of the specified class.
20
+
21
+ Args:
22
+ cls (type): The class to instantiate for each row.
23
+ loads (callable | None, optional): Optional function to process each value. Defaults to None.
24
+ """
25
+
26
+ def inner(cursor: Cursor) -> Callable:
27
+ names = _get_names(cursor)
28
+ if names is None:
29
+ return no_result
30
+
31
+ def cls_row_(values: Iterable) -> Any:
32
+ if loads is None:
33
+ return cls(**dict(zip(names, values, strict=True)))
34
+
35
+ return cls(**dict(zip(names, map(loads, values), strict=True)))
36
+
37
+ return cls_row_
38
+
39
+ return inner
40
+
41
+
42
+ def dict_row(loads: Callable | None = None) -> Callable:
43
+ """
44
+ Function to convert a row from a cursor to a dictionary.
45
+
46
+ Args:
47
+ loads (Callable | None): Optional function to process each value. Defaults to None.
48
+ """
49
+
50
+ def inner(cursor: Cursor) -> Callable:
51
+ names = _get_names(cursor)
52
+ if names is None:
53
+ return no_result
54
+
55
+ def dict_row_(values: Iterable) -> dict[str, Any]:
56
+ if loads is None:
57
+ return dict(zip(names, values, strict=True))
58
+
59
+ return dict(zip(names, map(loads, values), strict=True))
60
+
61
+ return dict_row_
62
+
63
+ return inner
everysk/sql/settings.py CHANGED
@@ -17,6 +17,29 @@ POSTGRESQL_CONNECTION_PASSWORD: str = None
17
17
  POSTGRESQL_CONNECTION_PORT: int = 5432
18
18
  POSTGRESQL_CONNECTION_HOST: str = None
19
19
  POSTGRESQL_CONNECTION_USER: str = None
20
- # Disable, Allow, Prefer, Require, VerifyCA, VerifyFull
21
- POSTGRESQL_CONNECTION_ENCRYPTION: str = 'Prefer'
22
20
  POSTGRESQL_POOL_MAX_SIZE: int = 10
21
+ POSTGRESQL_POOL_MIN_SIZE: int = 1
22
+
23
+ # https://www.psycopg.org/psycopg3/docs/api/pool.html
24
+ # Maximum time, in seconds, that a connection can stay unused in the pool before being closed, and the pool shrunk.
25
+ # This only happens to connections more than min_size, if max_size allowed the pool to grow.
26
+ POSTGRESQL_POOL_MAX_IDLE: int = 60 * 5 # 5 minutes
27
+
28
+ # The maximum lifetime of a connection in the pool, in seconds. Connections used for longer get closed and replaced by
29
+ # a new one. The amount is reduced by a random 10% to avoid mass eviction.
30
+ POSTGRESQL_POOL_MAX_LIFETIME: int = 60 * 30 # 30 minutes
31
+
32
+ # Maximum number of requests that can be queued to the pool, after which new requests will fail.
33
+ # Raising TooManyRequests, 0 means no queue limit.
34
+ POSTGRESQL_POOL_MAX_WAITING: int = 0
35
+
36
+ # If the connections are opened on init or later.
37
+ POSTGRESQL_POOL_OPEN: bool = True
38
+
39
+ # Maximum time, in seconds, the pool will try to create a connection. If a connection attempt fails, the pool will try
40
+ # to reconnect a few times, using an exponential backoff and some random factor to avoid mass attempts.
41
+ POSTGRESQL_POOL_RECONNECT_TIMEOUT: int = 60 * 2 # 2 minutes
42
+
43
+ # The default maximum time in seconds that a client can wait to receive a connection
44
+ # from the pool (using connection() or getconn()).
45
+ POSTGRESQL_POOL_TIMEOUT: int = 30 # 30 seconds
everysk/sql/utils.py CHANGED
@@ -17,6 +17,7 @@ _SQL_OPERATORS = {
17
17
  'ilike': 'ILIKE',
18
18
  'in': 'IN',
19
19
  'inside': '?',
20
+ 'insidebinary': '?|',
20
21
  'isnotnull': 'IS NOT NULL',
21
22
  'isnull': 'IS NULL',
22
23
  'like': 'LIKE',
@@ -58,8 +59,8 @@ class ConditionOperator:
58
59
 
59
60
  ## Operators methods
60
61
  def _operator_default(self) -> tuple[str, Any]:
61
- """Default operator method "field" <operator> $(field)p."""
62
- operation = f'{self.sql_operator} $({self.field_operator})p'
62
+ """Default operator method "field" <operator> %(field)s."""
63
+ operation = f'{self.sql_operator} %({self.field_operator})s'
63
64
  sql = f'"{self.field}" {operation}'
64
65
  return sql, self.value
65
66
 
@@ -69,9 +70,9 @@ class ConditionOperator:
69
70
  return sql, f'%{value}'
70
71
 
71
72
  def _operator_in(self) -> tuple[str, list]:
72
- """Operator method for in: "field" = ANY($(field__in)p)."""
73
+ """Operator method for in: "field" = ANY(%(field__in)s)."""
73
74
  # https://www.psycopg.org/psycopg3/docs/basic/from_pg2.html#you-cannot-use-in-s-with-a-tuple
74
- operation = f'ANY($({self.field_operator})p)'
75
+ operation = f'ANY(%({self.field_operator})s)'
75
76
  sql = f'"{self.field}" = {operation}'
76
77
  if isinstance(self.value, str):
77
78
  return sql, self.value.split(',')
@@ -98,8 +99,8 @@ class ConditionOperator:
98
99
  return self._operator_like()
99
100
 
100
101
  def _operator_nin(self) -> tuple[str, list]:
101
- """Operator method for nin: "field" != ALL($(field__nin)p)."""
102
- operation = f'ALL($({self.field_operator})p)'
102
+ """Operator method for nin: "field" != ALL(%(field__nin)s)."""
103
+ operation = f'ALL(%({self.field_operator})s)'
103
104
  sql = f'"{self.field}" != {operation}'
104
105
  if isinstance(self.value, str):
105
106
  return sql, self.value.split(',')
@@ -119,7 +120,7 @@ class ConditionOperator:
119
120
  Example:
120
121
  field__operator = 'age__gt'
121
122
  value = 30
122
- returns: ('"age" > $(age__gt)p', 30)
123
+ returns: ('"age" > %(age__gt)s', 30)
123
124
  """
124
125
  method_name = f'_operator_{self.operator}'
125
126
  if hasattr(self, method_name):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: everysk-lib
3
- Version: 1.9.7
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/
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.11
12
12
  Requires-Python: >=3.11
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE.txt
15
- Requires-Dist: python-dateutil<=2.9.0,>=2.8.2
15
+ Requires-Dist: python-dateutil<=3.0.0,>=2.8.2
16
16
  Requires-Dist: holidays==0.51
17
17
  Requires-Dist: tzdata<=2025.2,>=2024.1
18
18
  Provides-Extra: requests
@@ -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: psqlpy==0.11.9; extra == "postgresql"
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
 
@@ -3,7 +3,7 @@ everysk/version.py,sha256=q8j2yDrdm4lEtGtOIt4tY4TUlctwQYJJcVqscDabikA,617
3
3
  everysk/_version.py,sha256=_OPcym8X5j0fiafJLlKp8ADhMP5pHKqD2YySbH9f1PY,24501
4
4
  everysk/__init__.py,sha256=JeZGK9kmTaBRR6H4et-3UYE6x8D7xsFtrTnJ_tQXG_Q,1039
5
5
  everysk/utils.py,sha256=nejrDn5n75YBk3BinEiDY5JhhdB7BaHX_9wpJ20RvXs,2654
6
- everysk/settings.py,sha256=sU051hpzszM0SgZzn4vAOhEuEU_j9rEU7Gv9nWx1aEg,3936
6
+ everysk/settings.py,sha256=Z_wPLDcAxjd6gW2lH6J8n6Q2j-x3V_AJGJmR5NapzBo,4008
7
7
  everysk/tests.py,sha256=-btpbhDDF2CGIIDr4vra38HrSu7kXrV3o9Hgci90lNc,844
8
8
  everysk/core/serialize.py,sha256=8P5C7V98tB9vAQBEdu0NqMt8lktsD-Ol5REpTG1vNlw,25155
9
9
  everysk/core/signing.py,sha256=vImkFEiunqD-oO3e9Oi88z0x0jEEfO1lDIbC9RYxTn8,1956
@@ -13,6 +13,7 @@ everysk/core/object.py,sha256=9XMSPDlgLtGTKJLVimRIND4a7U1e_rZrERJGZw2j_lQ,53924
13
13
  everysk/core/log.py,sha256=qFRW9bO6jaELwrJ0MIaEUm2-uYFIyTbH2bQ3j0XpPio,30239
14
14
  everysk/core/number.py,sha256=Al_9LLu7hj_4fTLPTiN3dzQvxctmMxB5i9I4Z4X5Lro,1204
15
15
  everysk/core/unittests.py,sha256=ujGqGHCUAI25P4UgwHn9GpZAMBdqZ-lxxGERrckthIM,3289
16
+ everysk/core/retry.py,sha256=wLU0fNKP7cNYKkYxMTjR--YFYM0ahXWP-39O_FObwfo,1897
16
17
  everysk/core/threads.py,sha256=CcL4Nv2Zzdxt1xjo0CbLSttSU29xHgSRiWF_JXr3GAA,7167
17
18
  everysk/core/redis.py,sha256=HrG58dc_3EHnti2sBaijcyN8Hq4u3p1zst6-CSK9ySQ,36664
18
19
  everysk/core/compress.py,sha256=TRk-ME_UYfD5OQyhISYvwAZLT7IYfjbqWHYEgOuyaFU,10858
@@ -22,7 +23,7 @@ everysk/core/exceptions.py,sha256=HTMKauc7vSeP77UqgPGw6rmwhEqWAKcGS0wk4oofgEY,14
22
23
  everysk/core/sftp.py,sha256=01_iDxqhcAO3FFKUp-8myV7qtf3I-Mi_cLZ6KrAfsGw,15600
23
24
  everysk/core/string.py,sha256=K1BRlRNuhqlI7gdqOi40hjTHuxm1fF-RLqIAwm74eHc,5456
24
25
  everysk/core/workers.py,sha256=alPhC1hEhI-I1l1Vnt0OrpxPDfi7R-xodo_qjJRVHvk,9037
25
- everysk/core/tests.py,sha256=8zTef-BgIeMkV8QxWh-OEZy3wgJhVIRLNq9zj0VRg9E,11486
26
+ everysk/core/tests.py,sha256=qrfAfUDmgCvBn99X73fL5MUTo1P33gq0ftP5CTJetCc,14491
26
27
  everysk/core/slack.py,sha256=r9QH6RRe74Jz4-8XWLqbYq21msIlsVuC4fr_VUMFZPE,4394
27
28
  everysk/core/firestore.py,sha256=S5NWH9CS0by_Xk19UPX_455nM05Y-UG-rExgQDTf5zY,21567
28
29
  everysk/core/_tests/serialize/test_orjson.py,sha256=VSwkwv_omT4D_o38Ldnbw0Mj-hLYQfa0loEwYQ9C5q8,12415
@@ -82,7 +83,7 @@ everysk/sdk/entities/file/settings.py,sha256=gDTLsbAvVoVdRLoQD1gkDYIJuKZUCdgS0AQ
82
83
  everysk/sdk/entities/file/base.py,sha256=gInM4XUvW1qxopVf7iDek76pGeo9on4U1gEo_JpGZSw,7951
83
84
  everysk/sdk/entities/secrets/settings.py,sha256=rN6QUdRO86e_5_Gg_nByKGRSrrNfKfv-PjjAPGh6OGg,910
84
85
  everysk/sdk/entities/secrets/script.py,sha256=kg2T4ptBhy_R4tPYq50_JBCNSgsdjKQzVzvzFNLcYaI,4038
85
- everysk/sdk/entities/secrets/base.py,sha256=oElnSxS9bTFdFnQYTECfs_gVwdkHGiVnoiL5_WmhCkY,4328
86
+ everysk/sdk/entities/secrets/base.py,sha256=On9tvQFQN4sENArlABKktc3mqx4kynaVpxCnuYAi5Bk,4410
86
87
  everysk/sdk/entities/private_security/settings.py,sha256=Lgcl_olxdUztYrVs7_tQ4WLlZtqk2IgPtdKZfIQ8o-E,875
87
88
  everysk/sdk/entities/private_security/base.py,sha256=nfVEPAhNjw160WMIrvHtdKVtuve0e6-9UNiRPzxVgTo,7860
88
89
  everysk/sdk/entities/workspace/settings.py,sha256=i2VFTCaIaza_wmWdAAkFeHMhVAv8fA-YafRVQbuYiuY,1314
@@ -119,15 +120,16 @@ everysk/api/api_resources/portfolio.py,sha256=51_rOn92-tNo28fcryFoQ0Q69iPNK3FN7d
119
120
  everysk/api/api_resources/report.py,sha256=FBEh4uEXd_OxE4BJmrQnbC9nBjGGSYt33HsKvQvsVL8,2529
120
121
  everysk/api/api_resources/datastore.py,sha256=ZFXkON1DH6M-Kui6nViwitH7L8vACRq1PKFLr8NtLdM,3250
121
122
  everysk/api/api_resources/custom_index.py,sha256=Lk5ms54MLPs-vlKwkw-kyDjyFwsk-AD3OOYa-HHRc48,1382
122
- everysk/sql/query.py,sha256=vybpvTI4oieI6b9uGCPkRnb1YgZ3sNKOyzemKOHroaA,13757
123
+ everysk/sql/row_factory.py,sha256=FEtB4d12ACT3Bp6g2k_Ty2p2HpTkPoVqODUS1_kSXys,1936
124
+ everysk/sql/query.py,sha256=yO2EoGoUoGace_9BNKN8jeyFV30yCrAhefAvGHqIFE4,14174
123
125
  everysk/sql/__init__.py,sha256=lXZAWXrI2tlzw9uvJyThaWnksZqdbemQ2tqe-e7wUiU,413
124
- everysk/sql/model.py,sha256=AzhGSYq9o_ekmpKmDcD6-9FEghd3_JzOiqGbapqhm_A,14756
125
- everysk/sql/connection.py,sha256=uFp4EEsc4fX2rnmvQsHTpLZnWiUf1fpt4FSSZvPYhdk,5718
126
- everysk/sql/utils.py,sha256=Or2Cs4wVp6fKQXuGP7e_bPvc88pP-PZ3AoSEDT7G_dA,4301
127
- everysk/sql/settings.py,sha256=aQ6z1ZYtulmqwA4G7u-jx_OV_4IWPNgv5r9sfNWNh3A,864
128
- everysk_lib-1.9.7.dist-info/RECORD,,
129
- everysk_lib-1.9.7.dist-info/WHEEL,sha256=V1loQ6TpxABu1APUg0MoTRBOzSKT5xVc3skizX-ovCU,136
130
- everysk_lib-1.9.7.dist-info/.gitignore,sha256=0A1r9HzLhR7IQ1rGPjbaW5HC6oIQ7xzVYZ1z1ZaVfmw,183
131
- everysk_lib-1.9.7.dist-info/top_level.txt,sha256=1s1Lfhd4gXolqzkh-ay3yy-EZKPiKnJfbZwx2fybxyk,14
132
- everysk_lib-1.9.7.dist-info/METADATA,sha256=o8009mvpZRKVhS5fLELVAhngx7fQCjrSVHRhscIhEpQ,12962
133
- everysk_lib-1.9.7.dist-info/licenses/LICENSE.txt,sha256=Q5YxWA62m0TsmpEmHeoRHg4oPu_8ektkZ3FWWm1pQWo,311
126
+ everysk/sql/model.py,sha256=syHHTTS4JRTj_2Mu9ugmMu4b6qUY12t3VgfE3hSxYIc,14559
127
+ everysk/sql/connection.py,sha256=PujKm1i5gAvxU_FVT7IDuMb38D52dII52Gk8RI0vC_0,7928
128
+ everysk/sql/utils.py,sha256=Hbk9INEcz12FoBeDvHh9qh0dTSmTCWAn_S1anJOJb1o,4327
129
+ everysk/sql/settings.py,sha256=g5ZVm4Y3fhWGeXgTW6D309waGRBx2S6Ima4nY6ByTZw,2103
130
+ everysk_lib-1.9.9.dist-info/RECORD,,
131
+ everysk_lib-1.9.9.dist-info/WHEEL,sha256=V1loQ6TpxABu1APUg0MoTRBOzSKT5xVc3skizX-ovCU,136
132
+ everysk_lib-1.9.9.dist-info/.gitignore,sha256=0A1r9HzLhR7IQ1rGPjbaW5HC6oIQ7xzVYZ1z1ZaVfmw,183
133
+ everysk_lib-1.9.9.dist-info/top_level.txt,sha256=1s1Lfhd4gXolqzkh-ay3yy-EZKPiKnJfbZwx2fybxyk,14
134
+ everysk_lib-1.9.9.dist-info/METADATA,sha256=LrZks9TN21b8JcX5EiJg2oIzB_o8mwCbusCghhXkYz4,13080
135
+ everysk_lib-1.9.9.dist-info/licenses/LICENSE.txt,sha256=Q5YxWA62m0TsmpEmHeoRHg4oPu_8ektkZ3FWWm1pQWo,311