ae-base 0.3.70__tar.gz → 0.3.71__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.
- {ae_base-0.3.70/ae_base.egg-info → ae_base-0.3.71}/PKG-INFO +4 -4
- {ae_base-0.3.70 → ae_base-0.3.71}/README.md +3 -3
- {ae_base-0.3.70 → ae_base-0.3.71}/ae/base.py +12 -9
- {ae_base-0.3.70 → ae_base-0.3.71/ae_base.egg-info}/PKG-INFO +4 -4
- {ae_base-0.3.70 → ae_base-0.3.71}/setup.py +4 -4
- {ae_base-0.3.70 → ae_base-0.3.71}/tests/test_base.py +45 -22
- {ae_base-0.3.70 → ae_base-0.3.71}/LICENSE.md +0 -0
- {ae_base-0.3.70 → ae_base-0.3.71}/ae_base.egg-info/SOURCES.txt +0 -0
- {ae_base-0.3.70 → ae_base-0.3.71}/ae_base.egg-info/dependency_links.txt +0 -0
- {ae_base-0.3.70 → ae_base-0.3.71}/ae_base.egg-info/requires.txt +0 -0
- {ae_base-0.3.70 → ae_base-0.3.71}/ae_base.egg-info/top_level.txt +0 -0
- {ae_base-0.3.70 → ae_base-0.3.71}/ae_base.egg-info/zip-safe +0 -0
- {ae_base-0.3.70 → ae_base-0.3.71}/pyproject.toml +0 -0
- {ae_base-0.3.70 → ae_base-0.3.71}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ae_base
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.71
|
|
4
4
|
Summary: ae namespace module portion base: basic constants, helper functions and context managers
|
|
5
5
|
Home-page: https://gitlab.com/ae-group/ae_base
|
|
6
6
|
Author: AndiEcker
|
|
@@ -65,13 +65,13 @@ Dynamic: summary
|
|
|
65
65
|
|
|
66
66
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.97 -->
|
|
67
67
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.19 -->
|
|
68
|
-
# base 0.3.
|
|
68
|
+
# base 0.3.71
|
|
69
69
|
|
|
70
70
|
[](
|
|
71
71
|
https://gitlab.com/ae-group/ae_base)
|
|
72
72
|
[](
|
|
74
|
+
https://gitlab.com/ae-group/ae_base/-/tree/release0.3.71)
|
|
75
75
|
[](
|
|
76
76
|
https://pypi.org/project/ae-base/#history)
|
|
77
77
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.97 -->
|
|
2
2
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.19 -->
|
|
3
|
-
# base 0.3.
|
|
3
|
+
# base 0.3.71
|
|
4
4
|
|
|
5
5
|
[](
|
|
6
6
|
https://gitlab.com/ae-group/ae_base)
|
|
7
7
|
[](
|
|
9
|
+
https://gitlab.com/ae-group/ae_base/-/tree/release0.3.71)
|
|
10
10
|
[](
|
|
11
11
|
https://pypi.org/project/ae-base/#history)
|
|
12
12
|
|
|
@@ -246,7 +246,7 @@ from types import ModuleType
|
|
|
246
246
|
from typing import Any, Callable, Generator, Iterable, MutableMapping, Optional, Union, cast
|
|
247
247
|
|
|
248
248
|
|
|
249
|
-
__version__ = '0.3.
|
|
249
|
+
__version__ = '0.3.71'
|
|
250
250
|
|
|
251
251
|
|
|
252
252
|
os_path_abspath = os.path.abspath
|
|
@@ -1362,17 +1362,20 @@ def url_failure(url: str, token: str = "", username: str = "", password: str = "
|
|
|
1362
1362
|
return f"{exception.code} {mask_url(url)} raised HTTPError {exception.reason=}"
|
|
1363
1363
|
|
|
1364
1364
|
except URLError as exception:
|
|
1365
|
-
|
|
1365
|
+
err_msg = f" {mask_url(url)} raised {exception.errno=} {exception.reason=};"
|
|
1366
1366
|
if isinstance(exception.reason, socket.gaierror):
|
|
1367
|
-
return f"{
|
|
1368
|
-
if isinstance(exception.reason, socket.timeout):
|
|
1369
|
-
return f"{err_prefix} connection timed out after {timeout} seconds"
|
|
1367
|
+
return '995' + f"{err_msg} could not resolve hostname"
|
|
1370
1368
|
if isinstance(exception.reason, ssl.SSLCertVerificationError):
|
|
1371
|
-
return f"{
|
|
1372
|
-
|
|
1369
|
+
return '996' + f"{err_msg} SSL certificate verification failed"
|
|
1370
|
+
if isinstance(exception.reason, socket.timeout):
|
|
1371
|
+
return '997' + f"{err_msg} connection timed out after {timeout} seconds"
|
|
1372
|
+
return '998' + f"{err_msg} could not reach the server"
|
|
1373
|
+
|
|
1374
|
+
except socket.timeout as _exception: # noqa: F841 # str(_exception) could contain password|token
|
|
1375
|
+
return '997' + f" {mask_url(url)} raised socket-timeout exception after {timeout} seconds"
|
|
1373
1376
|
|
|
1374
|
-
except Exception:
|
|
1375
|
-
return f"
|
|
1377
|
+
except Exception as _exception: # noqa: F841 # pylint: disable=broad-exception-caught
|
|
1378
|
+
return '999' + f" {mask_url(url)} raised unexpected exception" # str(_exception) COULD contain password
|
|
1376
1379
|
|
|
1377
1380
|
|
|
1378
1381
|
def utc_datetime() -> datetime.datetime:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ae_base
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.71
|
|
4
4
|
Summary: ae namespace module portion base: basic constants, helper functions and context managers
|
|
5
5
|
Home-page: https://gitlab.com/ae-group/ae_base
|
|
6
6
|
Author: AndiEcker
|
|
@@ -65,13 +65,13 @@ Dynamic: summary
|
|
|
65
65
|
|
|
66
66
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.97 -->
|
|
67
67
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.19 -->
|
|
68
|
-
# base 0.3.
|
|
68
|
+
# base 0.3.71
|
|
69
69
|
|
|
70
70
|
[](
|
|
71
71
|
https://gitlab.com/ae-group/ae_base)
|
|
72
72
|
[](
|
|
74
|
+
https://gitlab.com/ae-group/ae_base/-/tree/release0.3.71)
|
|
75
75
|
[](
|
|
76
76
|
https://pypi.org/project/ae-base/#history)
|
|
77
77
|
|
|
@@ -25,13 +25,13 @@ setup_kwargs = {
|
|
|
25
25
|
'license': 'GPL-3.0-or-later',
|
|
26
26
|
'long_description': ('<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.97 -->\n'
|
|
27
27
|
'<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.19 -->\n'
|
|
28
|
-
'# base 0.3.
|
|
28
|
+
'# base 0.3.71\n'
|
|
29
29
|
'\n'
|
|
30
30
|
'[](\n'
|
|
31
31
|
' https://gitlab.com/ae-group/ae_base)\n'
|
|
32
32
|
'[](\n'
|
|
34
|
+
' https://gitlab.com/ae-group/ae_base/-/tree/release0.3.71)\n'
|
|
35
35
|
'[](\n'
|
|
36
36
|
' https://pypi.org/project/ae-base/#history)\n'
|
|
37
37
|
'\n'
|
|
@@ -108,7 +108,7 @@ setup_kwargs = {
|
|
|
108
108
|
'Source': 'https://ae.readthedocs.io/en/latest/_modules/ae/base.html'},
|
|
109
109
|
'python_requires': '>=3.9',
|
|
110
110
|
'url': 'https://gitlab.com/ae-group/ae_base',
|
|
111
|
-
'version': '0.3.
|
|
111
|
+
'version': '0.3.71',
|
|
112
112
|
'zip_safe': True,
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
""" ae.base unit tests """
|
|
2
2
|
import datetime
|
|
3
3
|
import os
|
|
4
|
-
import time
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
4
|
import shutil
|
|
8
5
|
import socket
|
|
9
6
|
import ssl
|
|
@@ -11,16 +8,20 @@ import string
|
|
|
11
8
|
import sys
|
|
12
9
|
import tempfile
|
|
13
10
|
import textwrap
|
|
11
|
+
import time
|
|
12
|
+
import timeit
|
|
14
13
|
|
|
15
14
|
from collections import OrderedDict
|
|
16
15
|
from configparser import ConfigParser
|
|
17
16
|
# noinspection PyProtectedMember
|
|
18
17
|
from http.client import HTTPMessage
|
|
19
18
|
from types import ModuleType
|
|
20
|
-
from typing import cast, Any
|
|
19
|
+
from typing import cast, Any, Optional
|
|
21
20
|
from unittest.mock import patch
|
|
22
21
|
from urllib.error import HTTPError, URLError
|
|
23
22
|
|
|
23
|
+
import pytest
|
|
24
|
+
|
|
24
25
|
# noinspection PyProtectedMember
|
|
25
26
|
from ae.base import (
|
|
26
27
|
ASCII_TO_UNICODE, ASCII_UNICODE, BUILD_CONFIG_FILE, DOTENV_FILE_NAME, PY_EXT, PY_INIT, PY_MAIN, TESTS_FOLDER,
|
|
@@ -69,15 +70,32 @@ def os_env_test_env():
|
|
|
69
70
|
module_test_var = 'module_test_var_val' # used for stack_var()/try_exec() tests
|
|
70
71
|
|
|
71
72
|
|
|
72
|
-
def
|
|
73
|
+
def test_unset_truthiness_and_null_length():
|
|
73
74
|
assert not UNSET
|
|
74
75
|
assert bool(UNSET) is False
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
def test_unset_null_length():
|
|
78
77
|
assert len(UNSET) == 0
|
|
79
78
|
|
|
80
79
|
|
|
80
|
+
def test_proof_os_path_shortcuts_performance_win():
|
|
81
|
+
att_call_setup = textwrap.dedent("""
|
|
82
|
+
import os
|
|
83
|
+
path1, path2, path3 = "folder1", "folder2", "file.tst"
|
|
84
|
+
""")
|
|
85
|
+
sho_call_setup = att_call_setup + textwrap.dedent("""
|
|
86
|
+
os_path_join = os.path.join
|
|
87
|
+
""")
|
|
88
|
+
|
|
89
|
+
att_call_code = "os.path.join(path1, path2, path3)"
|
|
90
|
+
sho_call_code = "os_path_join(path1, path2, path3)"
|
|
91
|
+
|
|
92
|
+
time_att = timeit.timeit(att_call_code, setup=att_call_setup, number=3_000_000)
|
|
93
|
+
time_sho = timeit.timeit(sho_call_code, setup=sho_call_setup, number=3_000_000)
|
|
94
|
+
|
|
95
|
+
assert time_sho < time_att
|
|
96
|
+
print(f"\n¡!¡!¡! os_path_* shortcuts are ~{((time_att - time_sho) / time_att) * 100:.2f}% faster")
|
|
97
|
+
|
|
98
|
+
|
|
81
99
|
class TestErrorMsgMixin:
|
|
82
100
|
def test_instantiation(self):
|
|
83
101
|
ins = ErrorMsgMixin()
|
|
@@ -1069,6 +1087,17 @@ class TestBaseHelpers:
|
|
|
1069
1087
|
assert to_ascii('ß') == 'ss'
|
|
1070
1088
|
assert to_ascii('€') == 'Euro'
|
|
1071
1089
|
|
|
1090
|
+
@staticmethod
|
|
1091
|
+
def url_failure_httpbin_503_retryer(url: str, timeout: Optional[float] = None) -> tuple[str, str]:
|
|
1092
|
+
retries = 9
|
|
1093
|
+
while True:
|
|
1094
|
+
err_msg = url_failure(url, timeout=timeout)
|
|
1095
|
+
if not err_msg or int(err_msg[:3]) != 503 or retries == 0:
|
|
1096
|
+
break
|
|
1097
|
+
time.sleep(3)
|
|
1098
|
+
retries -= 1
|
|
1099
|
+
return err_msg, f"url_failure({url=}, {timeout=}) httpbin is sometimes unavailable/503. retry later; {err_msg=}"
|
|
1100
|
+
|
|
1072
1101
|
def test_url_failure(self):
|
|
1073
1102
|
assert not url_failure("https://gitlab.com/ae-group/ae_base")
|
|
1074
1103
|
|
|
@@ -1078,14 +1107,8 @@ class TestBaseHelpers:
|
|
|
1078
1107
|
|
|
1079
1108
|
assert not url_failure("https://www.google.com")
|
|
1080
1109
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
err_msg = url_failure(f"https://httpbin.org/status/200")
|
|
1084
|
-
if not err_msg or retries == 0:
|
|
1085
|
-
break
|
|
1086
|
-
time.sleep(3)
|
|
1087
|
-
retries -= 1
|
|
1088
|
-
assert not err_msg, f"httpbin is sometimes unavailable with error 503 - retry later; {err_msg=}"
|
|
1110
|
+
ret, message = self.url_failure_httpbin_503_retryer("https://httpbin.org/status/200")
|
|
1111
|
+
assert not ret, message
|
|
1089
1112
|
|
|
1090
1113
|
def test_url_failure_authentication_errors(self):
|
|
1091
1114
|
password, domain, path = "toBeMaskedPassword", "any-not_existing-host_domain.zzz", "any/not/existing/url/path"
|
|
@@ -1215,19 +1238,19 @@ class TestBaseHelpers:
|
|
|
1215
1238
|
assert int(ret[:3]) > 0
|
|
1216
1239
|
|
|
1217
1240
|
def test_url_failure_timeout_errors(self):
|
|
1218
|
-
ret =
|
|
1241
|
+
ret, message = self.url_failure_httpbin_503_retryer("https://httpbin.org/delay/3", timeout=0.9)
|
|
1219
1242
|
|
|
1220
|
-
assert ret
|
|
1221
|
-
assert
|
|
1243
|
+
assert ret, message
|
|
1244
|
+
assert ret[:3] == '997', message
|
|
1222
1245
|
|
|
1223
1246
|
def test_url_failure_url_errors(self):
|
|
1224
1247
|
assert url_failure("")
|
|
1225
1248
|
|
|
1226
|
-
ret =
|
|
1249
|
+
ret, message = self.url_failure_httpbin_503_retryer(url2 := "https://httpbin.org/status/504")
|
|
1227
1250
|
|
|
1228
|
-
assert ret
|
|
1229
|
-
assert
|
|
1230
|
-
assert ret[4:].startswith(mask_url(url2))
|
|
1251
|
+
assert ret, message
|
|
1252
|
+
assert ret[:3] == '504', message
|
|
1253
|
+
assert ret[4:].startswith(mask_url(url2)), message
|
|
1231
1254
|
|
|
1232
1255
|
with pytest.raises(AttributeError):
|
|
1233
1256
|
url_failure(cast(str, 123456))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|