ae-base 0.3.39__tar.gz → 0.3.41__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.39 → ae_base-0.3.41}/LICENSE.md +1 -1
- {ae_base-0.3.39/ae_base.egg-info → ae_base-0.3.41}/PKG-INFO +7 -5
- {ae_base-0.3.39 → ae_base-0.3.41}/README.md +4 -4
- {ae_base-0.3.39 → ae_base-0.3.41}/ae/base.py +42 -10
- {ae_base-0.3.39 → ae_base-0.3.41/ae_base.egg-info}/PKG-INFO +7 -5
- {ae_base-0.3.39 → ae_base-0.3.41}/ae_base.egg-info/requires.txt +2 -0
- {ae_base-0.3.39 → ae_base-0.3.41}/setup.py +1 -1
- {ae_base-0.3.39 → ae_base-0.3.41}/tests/test_base.py +19 -12
- {ae_base-0.3.39 → ae_base-0.3.41}/ae_base.egg-info/SOURCES.txt +0 -0
- {ae_base-0.3.39 → ae_base-0.3.41}/ae_base.egg-info/dependency_links.txt +0 -0
- {ae_base-0.3.39 → ae_base-0.3.41}/ae_base.egg-info/top_level.txt +0 -0
- {ae_base-0.3.39 → ae_base-0.3.41}/ae_base.egg-info/zip-safe +0 -0
- {ae_base-0.3.39 → ae_base-0.3.41}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ae_base
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.41
|
|
4
4
|
Summary: ae namespace module portion base: basic constants, helper functions and context manager
|
|
5
5
|
Home-page: https://gitlab.com/ae-group/ae_base
|
|
6
6
|
Author: AndiEcker
|
|
@@ -32,6 +32,7 @@ Requires-Dist: mypy; extra == "dev"
|
|
|
32
32
|
Requires-Dist: pylint; extra == "dev"
|
|
33
33
|
Requires-Dist: pytest; extra == "dev"
|
|
34
34
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-django; extra == "dev"
|
|
35
36
|
Requires-Dist: typing; extra == "dev"
|
|
36
37
|
Requires-Dist: types-setuptools; extra == "dev"
|
|
37
38
|
Requires-Dist: wheel; extra == "dev"
|
|
@@ -46,6 +47,7 @@ Requires-Dist: mypy; extra == "tests"
|
|
|
46
47
|
Requires-Dist: pylint; extra == "tests"
|
|
47
48
|
Requires-Dist: pytest; extra == "tests"
|
|
48
49
|
Requires-Dist: pytest-cov; extra == "tests"
|
|
50
|
+
Requires-Dist: pytest-django; extra == "tests"
|
|
49
51
|
Requires-Dist: typing; extra == "tests"
|
|
50
52
|
Requires-Dist: types-setuptools; extra == "tests"
|
|
51
53
|
Requires-Dist: wheel; extra == "tests"
|
|
@@ -53,17 +55,17 @@ Requires-Dist: twine; extra == "tests"
|
|
|
53
55
|
|
|
54
56
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.94 -->
|
|
55
57
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_namespace_root V0.3.14 -->
|
|
56
|
-
# base 0.3.
|
|
58
|
+
# base 0.3.41
|
|
57
59
|
|
|
58
60
|
[](
|
|
59
61
|
https://gitlab.com/ae-group/ae_base)
|
|
60
62
|
[](
|
|
64
|
+
https://gitlab.com/ae-group/ae_base/-/tree/release0.3.40)
|
|
63
65
|
[](
|
|
64
66
|
https://pypi.org/project/ae-base/#history)
|
|
65
67
|
|
|
66
|
-
>ae_base module 0.3.
|
|
68
|
+
>ae_base module 0.3.41.
|
|
67
69
|
|
|
68
70
|
[](
|
|
69
71
|
https://ae-group.gitlab.io/ae_base/coverage/index.html)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.94 -->
|
|
2
2
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_namespace_root V0.3.14 -->
|
|
3
|
-
# base 0.3.
|
|
3
|
+
# base 0.3.41
|
|
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.40)
|
|
10
10
|
[](
|
|
11
11
|
https://pypi.org/project/ae-base/#history)
|
|
12
12
|
|
|
13
|
-
>ae_base module 0.3.
|
|
13
|
+
>ae_base module 0.3.41.
|
|
14
14
|
|
|
15
15
|
[](
|
|
16
16
|
https://ae-group.gitlab.io/ae_base/coverage/index.html)
|
|
@@ -3,7 +3,7 @@ basic constants, helper functions and context manager
|
|
|
3
3
|
=====================================================
|
|
4
4
|
|
|
5
5
|
this module is pure python, has no external dependencies, and is providing base constants, common helper
|
|
6
|
-
functions and context managers.
|
|
6
|
+
functions, useful classes and context managers.
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
base constants
|
|
@@ -24,10 +24,16 @@ the constants :data:`PACKAGE_NAME`, :data:`PACKAGE_DOMAIN` and :data:`PERMISSION
|
|
|
24
24
|
on mobile devices. to avoid redundancies, these values get loaded from the
|
|
25
25
|
:data:`build config file <BUILD_CONFIG_FILE>` - if it exists in the current working directory.
|
|
26
26
|
|
|
27
|
+
with the help of the format string constant :data:`NOW_STR_FORMAT` and the function :func:`now_str` you can create a
|
|
28
|
+
sortable and compact string from a timestamp.
|
|
29
|
+
|
|
27
30
|
|
|
28
31
|
base helper functions
|
|
29
32
|
---------------------
|
|
30
33
|
|
|
34
|
+
:func:`now_str` creates a timestamp string with the actual UTC date and time. the :func:`utc_datetime` provides the
|
|
35
|
+
actual UTC date and time as datetime object.
|
|
36
|
+
|
|
31
37
|
to write more compact and readable code for the most common file I/O operations, the helper functions :func:`read_file`
|
|
32
38
|
and :func:`write_file` are wrapping Python's built-in :func:`open` function and its context manager.
|
|
33
39
|
|
|
@@ -40,6 +46,9 @@ the function :func:`duplicates` returns the duplicates of an iterable type.
|
|
|
40
46
|
to normalize a file path, in order to remove `.`, `..` placeholders, to resolve symbolic links or to make it relative or
|
|
41
47
|
absolute, call the function :func:`norm_path`.
|
|
42
48
|
|
|
49
|
+
:func:`uri2filename` converts special characters of a URI/URL resulting in a string that can be used as a file name.
|
|
50
|
+
use the function :func:`filename2uri` to convert this string back to the corresponding URL/URI.
|
|
51
|
+
|
|
43
52
|
:func:`camel_to_snake` and :func:`snake_to_camel` providing name conversions of class and method names.
|
|
44
53
|
|
|
45
54
|
to encode Unicode strings to other codecs the functions :func:`force_encoding` and :func:`to_ascii` can be used.
|
|
@@ -99,6 +108,15 @@ code and build Kivy apps for the Android OS, and to `Gabriel Pettier <https://gi
|
|
|
99
108
|
osc example.
|
|
100
109
|
|
|
101
110
|
|
|
111
|
+
types, classes and mixins
|
|
112
|
+
-------------------------
|
|
113
|
+
|
|
114
|
+
the :class:`UnsetType` class can be used e.g. for the declaration of optional function and method parameters,
|
|
115
|
+
allowing also `None` is an accepted argument value.
|
|
116
|
+
|
|
117
|
+
to extend any class with an intelligent error message property, add the mixin :class:`ErrorMsgMixin` to it.
|
|
118
|
+
|
|
119
|
+
|
|
102
120
|
generic context manager
|
|
103
121
|
-----------------------
|
|
104
122
|
|
|
@@ -148,7 +166,7 @@ from types import ModuleType
|
|
|
148
166
|
from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union, cast
|
|
149
167
|
|
|
150
168
|
|
|
151
|
-
__version__ = '0.3.
|
|
169
|
+
__version__ = '0.3.41'
|
|
152
170
|
|
|
153
171
|
|
|
154
172
|
DOCS_FOLDER = 'docs' #: project documentation root folder name
|
|
@@ -202,7 +220,9 @@ _env_variable = re.compile(r"""
|
|
|
202
220
|
""", re.IGNORECASE | re.VERBOSE)
|
|
203
221
|
|
|
204
222
|
|
|
205
|
-
NAME_PARTS_SEP = '_'
|
|
223
|
+
NAME_PARTS_SEP = '_' #: name parts separator character, e.g. for :func:`norm_name`
|
|
224
|
+
|
|
225
|
+
NOW_STR_FORMAT = "{sep}%Y%m%d{sep}%H%M%S{sep}%f" #: timestamp format of :func:`now_str`
|
|
206
226
|
|
|
207
227
|
SKIPPED_MODULES = ('ae.base', 'ae.paths', 'ae.dynamicod', 'ae.core', 'ae.console', 'ae.gui_app', 'ae.gui_help',
|
|
208
228
|
'ae.kivy', 'ae.kivy.apps', 'ae.kivy.behaviors', 'ae.kivy.i18n', 'ae.kivy.tours', 'ae.kivy.widgets',
|
|
@@ -338,6 +358,17 @@ def env_str(name: str, convert_name: bool = False) -> Optional[str]:
|
|
|
338
358
|
return os.environ.get(name)
|
|
339
359
|
|
|
340
360
|
|
|
361
|
+
def filename2uri(file_name: str) -> str:
|
|
362
|
+
""" convert a file name converted by :func:`uri2filename` back to its representation as a URI
|
|
363
|
+
|
|
364
|
+
:param file_name: name of the file/folder to convert back to its URI representation.
|
|
365
|
+
:return: URI string.
|
|
366
|
+
|
|
367
|
+
.. hint:: to ensure proper conversion the specified file name has to be created by :func:`uri2filename`.
|
|
368
|
+
"""
|
|
369
|
+
return urllib.parse.unquote(file_name)
|
|
370
|
+
|
|
371
|
+
|
|
341
372
|
def force_encoding(text: Union[str, bytes], encoding: str = DEF_ENCODING, errors: str = DEF_ENCODE_ERRORS) -> str:
|
|
342
373
|
""" force/ensure the encoding of text (str or bytes) without any UnicodeDecodeError/UnicodeEncodeError.
|
|
343
374
|
|
|
@@ -612,9 +643,9 @@ def now_str(sep: str = "") -> str:
|
|
|
612
643
|
|
|
613
644
|
:param sep: optional prefix and separator character (separating date from time and in time part
|
|
614
645
|
the seconds from the microseconds).
|
|
615
|
-
:return: UTC timestamp as string (length=20 + 3 * len(sep)).
|
|
646
|
+
:return: naive UTC timestamp (without timezone info) as string (length=20 + 3 * len(sep)).
|
|
616
647
|
"""
|
|
617
|
-
return
|
|
648
|
+
return utc_datetime().strftime(NOW_STR_FORMAT.format(sep=sep))
|
|
618
649
|
|
|
619
650
|
|
|
620
651
|
def os_host_name() -> str:
|
|
@@ -969,19 +1000,20 @@ def uri2filename(uri: str) -> str:
|
|
|
969
1000
|
|
|
970
1001
|
more on allowed characters in file names in the answers of RedGrittyBrick on https://superuser.com/questions/358855
|
|
971
1002
|
and of Christopher Oezbek on https://stackoverflow.com/questions/1976007.
|
|
1003
|
+
|
|
1004
|
+
.. hint:: use :func:`filename2uri` to convert the resulting file name back to the corresponding URO
|
|
972
1005
|
"""
|
|
973
1006
|
# using urllib.parse.quote(uri, safe="") instead would convert also any non-ascii (e.g. umlaut) characters into hex
|
|
974
1007
|
# added [] to str.join() argument because List comprehensions are faster than generator expressions
|
|
975
1008
|
return "".join([f"%{hex(ord(_))[2:].upper()}" if _ in '/|\\:*?"<>%' else _ for _ in uri])
|
|
976
1009
|
|
|
977
1010
|
|
|
978
|
-
def
|
|
979
|
-
"""
|
|
1011
|
+
def utc_datetime() -> datetime.datetime:
|
|
1012
|
+
""" return the current UTC timestamp as string (to use as suffix for file and variable/attribute names).
|
|
980
1013
|
|
|
981
|
-
:
|
|
982
|
-
:return: URI string.
|
|
1014
|
+
:return: timestamp string of the actual UTC date and time.
|
|
983
1015
|
"""
|
|
984
|
-
return
|
|
1016
|
+
return datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
|
985
1017
|
|
|
986
1018
|
|
|
987
1019
|
def write_file(file_path: str, content: Union[str, bytes], extra_mode: str = "", encoding: Optional[str] = None):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ae_base
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.41
|
|
4
4
|
Summary: ae namespace module portion base: basic constants, helper functions and context manager
|
|
5
5
|
Home-page: https://gitlab.com/ae-group/ae_base
|
|
6
6
|
Author: AndiEcker
|
|
@@ -32,6 +32,7 @@ Requires-Dist: mypy; extra == "dev"
|
|
|
32
32
|
Requires-Dist: pylint; extra == "dev"
|
|
33
33
|
Requires-Dist: pytest; extra == "dev"
|
|
34
34
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-django; extra == "dev"
|
|
35
36
|
Requires-Dist: typing; extra == "dev"
|
|
36
37
|
Requires-Dist: types-setuptools; extra == "dev"
|
|
37
38
|
Requires-Dist: wheel; extra == "dev"
|
|
@@ -46,6 +47,7 @@ Requires-Dist: mypy; extra == "tests"
|
|
|
46
47
|
Requires-Dist: pylint; extra == "tests"
|
|
47
48
|
Requires-Dist: pytest; extra == "tests"
|
|
48
49
|
Requires-Dist: pytest-cov; extra == "tests"
|
|
50
|
+
Requires-Dist: pytest-django; extra == "tests"
|
|
49
51
|
Requires-Dist: typing; extra == "tests"
|
|
50
52
|
Requires-Dist: types-setuptools; extra == "tests"
|
|
51
53
|
Requires-Dist: wheel; extra == "tests"
|
|
@@ -53,17 +55,17 @@ Requires-Dist: twine; extra == "tests"
|
|
|
53
55
|
|
|
54
56
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.94 -->
|
|
55
57
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_namespace_root V0.3.14 -->
|
|
56
|
-
# base 0.3.
|
|
58
|
+
# base 0.3.41
|
|
57
59
|
|
|
58
60
|
[](
|
|
59
61
|
https://gitlab.com/ae-group/ae_base)
|
|
60
62
|
[](
|
|
64
|
+
https://gitlab.com/ae-group/ae_base/-/tree/release0.3.40)
|
|
63
65
|
[](
|
|
64
66
|
https://pypi.org/project/ae-base/#history)
|
|
65
67
|
|
|
66
|
-
>ae_base module 0.3.
|
|
68
|
+
>ae_base module 0.3.41.
|
|
67
69
|
|
|
68
70
|
[](
|
|
69
71
|
https://ae-group.gitlab.io/ae_base/coverage/index.html)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
""" ae.base unit tests """
|
|
2
|
+
import datetime
|
|
2
3
|
import os
|
|
3
4
|
import tempfile
|
|
4
5
|
|
|
@@ -16,11 +17,11 @@ from typing import cast
|
|
|
16
17
|
from ae.base import (
|
|
17
18
|
BUILD_CONFIG_FILE, DOTENV_FILE_NAME, PY_EXT, PY_INIT, PY_MAIN, TESTS_FOLDER, UNSET,
|
|
18
19
|
app_name_guess, build_config_variable_values, camel_to_snake, deep_dict_update, dummy_function, duplicates, env_str,
|
|
19
|
-
force_encoding, full_stack_trace, import_module, instantiate_config_parser, in_wd,
|
|
20
|
+
filename2uri, force_encoding, full_stack_trace, import_module, instantiate_config_parser, in_wd,
|
|
20
21
|
load_env_var_defaults, load_dotenvs, main_file_paths_parts, module_attr, module_file_path, module_name,
|
|
21
22
|
norm_line_sep, norm_name, norm_path, now_str, os_host_name, os_local_ip, _os_platform, os_user_name,
|
|
22
23
|
parse_dotenv, project_main_file, read_file, round_traditional, snake_to_camel, stack_frames, stack_var, stack_vars,
|
|
23
|
-
sys_env_dict, sys_env_text, to_ascii,
|
|
24
|
+
sys_env_dict, sys_env_text, to_ascii, uri2filename, utc_datetime, write_file, ErrorMsgMixin)
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
tst_uri1 = "schema://user:pwd@domain/path_root/path_sub\\path+file% Üml?ä|ït.path_ext*\"<>"
|
|
@@ -203,6 +204,10 @@ class TestBaseHelpers:
|
|
|
203
204
|
os.environ['NON_ALPHA_NUM_CHARS_69'] = vv
|
|
204
205
|
assert env_str(ev, convert_name=True) == vv
|
|
205
206
|
|
|
207
|
+
def test_filename2uri(self):
|
|
208
|
+
assert filename2uri(tst_fna1) == tst_uri1
|
|
209
|
+
assert uri2filename(filename2uri(tst_fna1)) == tst_fna1
|
|
210
|
+
|
|
206
211
|
def test_force_encoding_bytes(self):
|
|
207
212
|
s = 'äöü'
|
|
208
213
|
|
|
@@ -245,7 +250,7 @@ class TestBaseHelpers:
|
|
|
245
250
|
|
|
246
251
|
def test_import_module_local_module(self):
|
|
247
252
|
module = "mod_2_tst"
|
|
248
|
-
mod_file = os.path.join(TESTS_FOLDER, module + PY_EXT)
|
|
253
|
+
mod_file = cast(str, os.path.join(TESTS_FOLDER, module + PY_EXT))
|
|
249
254
|
cur_dir = os.getcwd()
|
|
250
255
|
try:
|
|
251
256
|
write_file(mod_file, "mod_var = 'mod_var_val'")
|
|
@@ -676,7 +681,8 @@ class TestBaseHelpers:
|
|
|
676
681
|
assert sys_env_dict().get('bundle_dir') is None
|
|
677
682
|
sys.frozen = True
|
|
678
683
|
assert sys_env_dict().get('bundle_dir')
|
|
679
|
-
|
|
684
|
+
# noinspection PyUnresolvedReferences
|
|
685
|
+
del sys.__dict__['frozen'] # sys.__dict__.pop('frozen')
|
|
680
686
|
assert sys_env_dict().get('bundle_dir') is None
|
|
681
687
|
|
|
682
688
|
def test_sys_env_text(self):
|
|
@@ -702,14 +708,15 @@ class TestBaseHelpers:
|
|
|
702
708
|
assert to_ascii('ß') == 'ss'
|
|
703
709
|
assert to_ascii('€') == 'Euro'
|
|
704
710
|
|
|
705
|
-
def test_filename2uri(self):
|
|
706
|
-
assert filename2uri(tst_fna1) == tst_uri1
|
|
707
|
-
assert uri2filename(filename2uri(tst_fna1)) == tst_fna1
|
|
708
|
-
|
|
709
711
|
def test_uri2filename(self):
|
|
710
712
|
assert uri2filename(tst_uri1) == tst_fna1
|
|
711
713
|
assert filename2uri(uri2filename(tst_uri1)) == tst_uri1
|
|
712
714
|
|
|
715
|
+
def test_utc_datetime(self):
|
|
716
|
+
dt1 = utc_datetime()
|
|
717
|
+
dt2 = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
|
718
|
+
assert dt2 - dt1 < datetime.timedelta(seconds=1)
|
|
719
|
+
|
|
713
720
|
def test_uri_file_name(self):
|
|
714
721
|
try:
|
|
715
722
|
write_file(tst_fna1, "tst uri file content")
|
|
@@ -749,7 +756,7 @@ class TestModuleHelpers:
|
|
|
749
756
|
namespace = TESTS_FOLDER
|
|
750
757
|
mod_name = 'test_module_name'
|
|
751
758
|
att_name = 'test_module_func'
|
|
752
|
-
module_file = os.path.join(namespace, mod_name + PY_EXT)
|
|
759
|
+
module_file = cast(str, os.path.join(namespace, mod_name + PY_EXT))
|
|
753
760
|
try:
|
|
754
761
|
write_file(module_file, f"def {att_name}(*args, **kwargs):\n return args, kwargs\n")
|
|
755
762
|
args = (1, '2')
|
|
@@ -777,7 +784,7 @@ class TestModuleHelpers:
|
|
|
777
784
|
namespace = TESTS_FOLDER
|
|
778
785
|
mod_name = 'test_module_name'
|
|
779
786
|
att_name = 'test_module_func'
|
|
780
|
-
module_file = os.path.join(namespace, mod_name + PY_EXT)
|
|
787
|
+
module_file = cast(str, os.path.join(namespace, mod_name + PY_EXT))
|
|
781
788
|
try:
|
|
782
789
|
write_file(module_file, f"def {att_name}(arg1, args2, kwarg1='default'):\n return arg1, arg2, kwarg1\n")
|
|
783
790
|
|
|
@@ -802,7 +809,7 @@ class TestModuleHelpers:
|
|
|
802
809
|
def test_module_attr_module_ref(self):
|
|
803
810
|
namespace = TESTS_FOLDER
|
|
804
811
|
mod_name = 'test_module_name'
|
|
805
|
-
module_file = os.path.join(namespace, mod_name + PY_EXT)
|
|
812
|
+
module_file = cast(str, os.path.join(namespace, mod_name + PY_EXT))
|
|
806
813
|
cur_dir = os.getcwd()
|
|
807
814
|
try:
|
|
808
815
|
write_file(module_file, "# empty module")
|
|
@@ -825,7 +832,7 @@ class TestModuleHelpers:
|
|
|
825
832
|
namespace = TESTS_FOLDER
|
|
826
833
|
mod_name = 'test_module_name'
|
|
827
834
|
att_name = 'test_module_func'
|
|
828
|
-
module_file = os.path.join(namespace, mod_name + PY_EXT)
|
|
835
|
+
module_file = cast(str, os.path.join(namespace, mod_name + PY_EXT))
|
|
829
836
|
cur_dir = os.getcwd()
|
|
830
837
|
try:
|
|
831
838
|
write_file(module_file, f"""def {att_name}(*args, **kwargs):\n pass\n""")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|