ae-base 0.3.40__tar.gz → 0.3.42__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.40 → ae_base-0.3.42}/LICENSE.md +1 -1
- {ae_base-0.3.40/ae_base.egg-info → ae_base-0.3.42}/PKG-INFO +7 -5
- {ae_base-0.3.40 → ae_base-0.3.42}/README.md +4 -4
- {ae_base-0.3.40 → ae_base-0.3.42}/ae/base.py +89 -44
- {ae_base-0.3.40 → ae_base-0.3.42/ae_base.egg-info}/PKG-INFO +7 -5
- {ae_base-0.3.40 → ae_base-0.3.42}/ae_base.egg-info/requires.txt +2 -0
- {ae_base-0.3.40 → ae_base-0.3.42}/setup.py +1 -1
- {ae_base-0.3.40 → ae_base-0.3.42}/tests/test_base.py +24 -12
- {ae_base-0.3.40 → ae_base-0.3.42}/ae_base.egg-info/SOURCES.txt +0 -0
- {ae_base-0.3.40 → ae_base-0.3.42}/ae_base.egg-info/dependency_links.txt +0 -0
- {ae_base-0.3.40 → ae_base-0.3.42}/ae_base.egg-info/top_level.txt +0 -0
- {ae_base-0.3.40 → ae_base-0.3.42}/ae_base.egg-info/zip-safe +0 -0
- {ae_base-0.3.40 → ae_base-0.3.42}/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.42
|
|
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.42
|
|
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.41)
|
|
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.42.
|
|
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.42
|
|
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.41)
|
|
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.42.
|
|
14
14
|
|
|
15
15
|
[](
|
|
16
16
|
https://ae-group.gitlab.io/ae_base/coverage/index.html)
|
|
@@ -31,6 +31,9 @@ sortable and compact string from a timestamp.
|
|
|
31
31
|
base helper functions
|
|
32
32
|
---------------------
|
|
33
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
|
+
|
|
34
37
|
to write more compact and readable code for the most common file I/O operations, the helper functions :func:`read_file`
|
|
35
38
|
and :func:`write_file` are wrapping Python's built-in :func:`open` function and its context manager.
|
|
36
39
|
|
|
@@ -43,6 +46,9 @@ the function :func:`duplicates` returns the duplicates of an iterable type.
|
|
|
43
46
|
to normalize a file path, in order to remove `.`, `..` placeholders, to resolve symbolic links or to make it relative or
|
|
44
47
|
absolute, call the function :func:`norm_path`.
|
|
45
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
|
+
|
|
46
52
|
:func:`camel_to_snake` and :func:`snake_to_camel` providing name conversions of class and method names.
|
|
47
53
|
|
|
48
54
|
to encode Unicode strings to other codecs the functions :func:`force_encoding` and :func:`to_ascii` can be used.
|
|
@@ -137,6 +143,13 @@ to determine e.g. variable values of the callers of a function/method.
|
|
|
137
143
|
:attr:`title <AppBase.app_title>` of an application, if these values are not specified in the instance initializer.
|
|
138
144
|
|
|
139
145
|
another useful helper function provided by this portion to inspect and debug your code is :func:`full_stack_trace`.
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
os.path shortcuts
|
|
149
|
+
-----------------
|
|
150
|
+
|
|
151
|
+
the following data items are pointers to shortcut the lookup to their related functions in the
|
|
152
|
+
Python module :mod:`os.path`:
|
|
140
153
|
"""
|
|
141
154
|
import datetime
|
|
142
155
|
import getpass
|
|
@@ -160,7 +173,21 @@ from types import ModuleType
|
|
|
160
173
|
from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union, cast
|
|
161
174
|
|
|
162
175
|
|
|
163
|
-
__version__ = '0.3.
|
|
176
|
+
__version__ = '0.3.42'
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
os_path_abspath = os.path.abspath
|
|
180
|
+
os_path_basename = os.path.basename
|
|
181
|
+
os_path_dirname = os.path.dirname
|
|
182
|
+
os_path_expanduser = os.path.expanduser
|
|
183
|
+
os_path_isdir = os.path.isdir
|
|
184
|
+
os_path_isfile = os.path.isfile
|
|
185
|
+
os_path_join = os.path.join
|
|
186
|
+
os_path_normpath = os.path.normpath
|
|
187
|
+
os_path_realpath = os.path.realpath
|
|
188
|
+
os_path_relpath = os.path.relpath
|
|
189
|
+
os_path_sep = os.path.sep
|
|
190
|
+
os_path_splitext = os.path.splitext
|
|
164
191
|
|
|
165
192
|
|
|
166
193
|
DOCS_FOLDER = 'docs' #: project documentation root folder name
|
|
@@ -250,10 +277,10 @@ def app_name_guess() -> str:
|
|
|
250
277
|
if not app_name:
|
|
251
278
|
unspecified_app_names = ('ae_base', 'app', '_jb_pytest_runner', 'main', '__main__', 'pydevconsole', 'src')
|
|
252
279
|
path = sys.argv[0]
|
|
253
|
-
app_name =
|
|
280
|
+
app_name = os_path_splitext(os_path_basename(path))[0]
|
|
254
281
|
if app_name.lower() in unspecified_app_names:
|
|
255
282
|
path = os.getcwd()
|
|
256
|
-
app_name =
|
|
283
|
+
app_name = os_path_basename(path)
|
|
257
284
|
if app_name.lower() in unspecified_app_names:
|
|
258
285
|
app_name = "unguessable"
|
|
259
286
|
return app_name
|
|
@@ -267,7 +294,7 @@ def build_config_variable_values(*names_defaults: Tuple[str, Any], section: str
|
|
|
267
294
|
:return: tuple of build config variable values (using the passed default value if not specified
|
|
268
295
|
in the :data:`BUILD_CONFIG_FILE` spec file or if the spec file does not exist in cwd).
|
|
269
296
|
"""
|
|
270
|
-
if not
|
|
297
|
+
if not os_path_isfile(BUILD_CONFIG_FILE):
|
|
271
298
|
return tuple(def_val for name, def_val in names_defaults)
|
|
272
299
|
|
|
273
300
|
config = instantiate_config_parser()
|
|
@@ -352,6 +379,17 @@ def env_str(name: str, convert_name: bool = False) -> Optional[str]:
|
|
|
352
379
|
return os.environ.get(name)
|
|
353
380
|
|
|
354
381
|
|
|
382
|
+
def filename2uri(file_name: str) -> str:
|
|
383
|
+
""" convert a file name converted by :func:`uri2filename` back to its representation as a URI
|
|
384
|
+
|
|
385
|
+
:param file_name: name of the file/folder to convert back to its URI representation.
|
|
386
|
+
:return: URI string.
|
|
387
|
+
|
|
388
|
+
.. hint:: to ensure proper conversion the specified file name has to be created by :func:`uri2filename`.
|
|
389
|
+
"""
|
|
390
|
+
return urllib.parse.unquote(file_name)
|
|
391
|
+
|
|
392
|
+
|
|
355
393
|
def force_encoding(text: Union[str, bytes], encoding: str = DEF_ENCODING, errors: str = DEF_ENCODE_ERRORS) -> str:
|
|
356
394
|
""" force/ensure the encoding of text (str or bytes) without any UnicodeDecodeError/UnicodeEncodeError.
|
|
357
395
|
|
|
@@ -400,8 +438,8 @@ def import_module(import_name: str, path: Optional[Union[str, UnsetType]] = UNSE
|
|
|
400
438
|
:return: a reference to the loaded module or ``None`` if module could not be imported.
|
|
401
439
|
"""
|
|
402
440
|
if path is UNSET:
|
|
403
|
-
path = import_name.replace('.',
|
|
404
|
-
path += PY_EXT if
|
|
441
|
+
path = import_name.replace('.', os_path_sep)
|
|
442
|
+
path += PY_EXT if os_path_isfile(path + PY_EXT) else os_path_sep + PY_INIT
|
|
405
443
|
mod_ref = None
|
|
406
444
|
|
|
407
445
|
spec = importlib.util.spec_from_file_location(import_name, path) # type: ignore # silly mypy
|
|
@@ -452,7 +490,7 @@ def load_dotenvs():
|
|
|
452
490
|
"""
|
|
453
491
|
load_env_var_defaults(os.getcwd())
|
|
454
492
|
if file_name := stack_var('__file__'):
|
|
455
|
-
load_env_var_defaults(
|
|
493
|
+
load_env_var_defaults(os_path_dirname(os_path_abspath(file_name)))
|
|
456
494
|
|
|
457
495
|
|
|
458
496
|
def load_env_var_defaults(start_dir: str):
|
|
@@ -468,18 +506,18 @@ def load_env_var_defaults(start_dir: str):
|
|
|
468
506
|
only variables that are not declared in :data:`os.environ` will be added (with the
|
|
469
507
|
value specified in the ``.env`` file to be loaded).
|
|
470
508
|
"""
|
|
471
|
-
file_path =
|
|
472
|
-
if not
|
|
473
|
-
file_path =
|
|
509
|
+
file_path = os_path_abspath(os_path_join(start_dir, DOTENV_FILE_NAME))
|
|
510
|
+
if not os_path_isfile(file_path):
|
|
511
|
+
file_path = os_path_join(os_path_dirname(start_dir), DOTENV_FILE_NAME)
|
|
474
512
|
|
|
475
|
-
while
|
|
513
|
+
while os_path_isfile(file_path):
|
|
476
514
|
for var_nam, var_val in parse_dotenv(file_path).items():
|
|
477
515
|
if var_nam not in os.environ:
|
|
478
516
|
os.environ[var_nam] = var_val
|
|
479
517
|
|
|
480
518
|
if os.sep not in file_path:
|
|
481
519
|
break # pragma: no cover # prevent endless-loop for ``.env`` file in root dir (os.sep == '/')
|
|
482
|
-
file_path =
|
|
520
|
+
file_path = os_path_join(os_path_dirname(os_path_dirname(file_path)), DOTENV_FILE_NAME)
|
|
483
521
|
|
|
484
522
|
|
|
485
523
|
def main_file_paths_parts(portion_name: str) -> Tuple[Tuple[str, ...], ...]:
|
|
@@ -603,20 +641,20 @@ def norm_path(path: str, make_absolute: bool = True, remove_base_path: str = "",
|
|
|
603
641
|
"""
|
|
604
642
|
path = path or "."
|
|
605
643
|
if path[0] == "~":
|
|
606
|
-
path =
|
|
644
|
+
path = os_path_expanduser(path)
|
|
607
645
|
|
|
608
646
|
if remove_dots:
|
|
609
|
-
path =
|
|
647
|
+
path = os_path_normpath(path)
|
|
610
648
|
|
|
611
649
|
if resolve_sym_links:
|
|
612
|
-
path =
|
|
650
|
+
path = os_path_realpath(path)
|
|
613
651
|
elif make_absolute:
|
|
614
|
-
path =
|
|
652
|
+
path = os_path_abspath(path)
|
|
615
653
|
|
|
616
654
|
if remove_base_path:
|
|
617
655
|
if remove_base_path[0] == "~":
|
|
618
|
-
remove_base_path =
|
|
619
|
-
path =
|
|
656
|
+
remove_base_path = os_path_expanduser(remove_base_path)
|
|
657
|
+
path = os_path_relpath(path, remove_base_path)
|
|
620
658
|
|
|
621
659
|
return path
|
|
622
660
|
|
|
@@ -628,10 +666,7 @@ def now_str(sep: str = "") -> str:
|
|
|
628
666
|
the seconds from the microseconds).
|
|
629
667
|
:return: naive UTC timestamp (without timezone info) as string (length=20 + 3 * len(sep)).
|
|
630
668
|
"""
|
|
631
|
-
|
|
632
|
-
return datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None).strftime(NOW_STR_FORMAT.format(sep=sep))
|
|
633
|
-
# else:
|
|
634
|
-
# return datetime.datetime.utcnow().strftime(NOW_STR_FORMAT.format(sep=sep))
|
|
669
|
+
return utc_datetime().strftime(NOW_STR_FORMAT.format(sep=sep))
|
|
635
670
|
|
|
636
671
|
|
|
637
672
|
def os_host_name() -> str:
|
|
@@ -754,23 +789,22 @@ def project_main_file(import_name: str, project_path: str = "") -> str:
|
|
|
754
789
|
sister project (under the same project parent folder).
|
|
755
790
|
:return: absolute file path/name of main module or empty string if no main/version file found.
|
|
756
791
|
"""
|
|
757
|
-
join = os.path.join
|
|
758
792
|
*namespace_dirs, portion_name = import_name.split('.')
|
|
759
793
|
project_name = ('_'.join(namespace_dirs) + '_' if namespace_dirs else "") + portion_name
|
|
760
794
|
paths_parts = main_file_paths_parts(portion_name)
|
|
761
795
|
|
|
762
796
|
project_path = norm_path(project_path)
|
|
763
797
|
module_paths = []
|
|
764
|
-
if
|
|
765
|
-
module_paths.append(
|
|
798
|
+
if os_path_basename(project_path) != project_name:
|
|
799
|
+
module_paths.append(os_path_join(os_path_dirname(project_path), project_name, *namespace_dirs))
|
|
766
800
|
if namespace_dirs:
|
|
767
|
-
module_paths.append(
|
|
801
|
+
module_paths.append(os_path_join(project_path, *namespace_dirs))
|
|
768
802
|
module_paths.append(project_path)
|
|
769
803
|
|
|
770
804
|
for module_path in module_paths:
|
|
771
805
|
for path_parts in paths_parts:
|
|
772
|
-
main_file =
|
|
773
|
-
if
|
|
806
|
+
main_file = os_path_join(module_path, *path_parts)
|
|
807
|
+
if os_path_isfile(main_file):
|
|
774
808
|
return main_file
|
|
775
809
|
return ""
|
|
776
810
|
|
|
@@ -885,7 +919,7 @@ def stack_vars(*skip_modules: str,
|
|
|
885
919
|
:param max_depth: the maximum depth in the call stack from which to return the variables. if the specified
|
|
886
920
|
argument is not zero and no :paramref:`~stack_vars.skip_modules` are specified then the
|
|
887
921
|
first deeper stack frame that is not within the default :data:`SKIPPED_MODULES` will be
|
|
888
|
-
returned. if this argument and :paramref:`~
|
|
922
|
+
returned. if this argument and :paramref:`~stack_vars.find_name` get not passed then the
|
|
889
923
|
variables of the top stack frame will be returned.
|
|
890
924
|
:return: tuple of the global and local variable dicts and the depth in the call stack.
|
|
891
925
|
"""
|
|
@@ -986,33 +1020,38 @@ def uri2filename(uri: str) -> str:
|
|
|
986
1020
|
|
|
987
1021
|
more on allowed characters in file names in the answers of RedGrittyBrick on https://superuser.com/questions/358855
|
|
988
1022
|
and of Christopher Oezbek on https://stackoverflow.com/questions/1976007.
|
|
1023
|
+
|
|
1024
|
+
.. hint:: use :func:`filename2uri` to convert the resulting file name back to the corresponding URO
|
|
989
1025
|
"""
|
|
990
1026
|
# using urllib.parse.quote(uri, safe="") instead would convert also any non-ascii (e.g. umlaut) characters into hex
|
|
991
1027
|
# added [] to str.join() argument because List comprehensions are faster than generator expressions
|
|
992
1028
|
return "".join([f"%{hex(ord(_))[2:].upper()}" if _ in '/|\\:*?"<>%' else _ for _ in uri])
|
|
993
1029
|
|
|
994
1030
|
|
|
995
|
-
def
|
|
996
|
-
"""
|
|
1031
|
+
def utc_datetime() -> datetime.datetime:
|
|
1032
|
+
""" return the current UTC timestamp as string (to use as suffix for file and variable/attribute names).
|
|
997
1033
|
|
|
998
|
-
:
|
|
999
|
-
:return: URI string.
|
|
1034
|
+
:return: timestamp string of the actual UTC date and time.
|
|
1000
1035
|
"""
|
|
1001
|
-
return
|
|
1036
|
+
return datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
|
|
1002
1037
|
|
|
1003
1038
|
|
|
1004
1039
|
def write_file(file_path: str, content: Union[str, bytes], extra_mode: str = "", encoding: Optional[str] = None):
|
|
1005
1040
|
""" (over)write the file specified by :paramref:`~write_file.file_path` with text or binary/bytes content.
|
|
1006
1041
|
|
|
1007
1042
|
:param file_path: file path/name to write the passed content into (overwriting any previous content!).
|
|
1008
|
-
:param content: new file content either
|
|
1009
|
-
|
|
1010
|
-
:param extra_mode: open mode flag characters. passed
|
|
1011
|
-
this argument starts with 'a', else this argument value will be appended to 'w'
|
|
1043
|
+
:param content: new file content passed either as string or bytes array. if a bytes array get passed
|
|
1044
|
+
then this method will automatically write the content as binary.
|
|
1045
|
+
:param extra_mode: additional open mode flag characters. passed to the `mode` argument of :func:`open` if
|
|
1046
|
+
this argument starts with 'a' or 'w', else this argument value will be appended to 'w'
|
|
1047
|
+
before it get passed to the `mode` argument of :func:`open`.
|
|
1048
|
+
if the :paramref:`~write_file.content` is a bytes array, then a 'b' character will
|
|
1049
|
+
be automatically added to the `mode` argument of :func:`open` (if not already specified
|
|
1050
|
+
in this argument).
|
|
1012
1051
|
:param encoding: encoding used to write/convert/interpret the file content to write.
|
|
1013
1052
|
:raises FileExistsError: if file exists already and is write-protected.
|
|
1014
1053
|
:raises FileNotFoundError: if parts of the file path do not exist.
|
|
1015
|
-
:raises OSError: if :paramref:`~
|
|
1054
|
+
:raises OSError: if :paramref:`~write_file.file_path` is misspelled or contains invalid characters.
|
|
1016
1055
|
:raises PermissionError: if current OS user account lacks permissions to read the file content.
|
|
1017
1056
|
:raises ValueError: on decoding errors.
|
|
1018
1057
|
|
|
@@ -1025,7 +1064,13 @@ def write_file(file_path: str, content: Union[str, bytes], extra_mode: str = "",
|
|
|
1025
1064
|
for an intent result.
|
|
1026
1065
|
Related german docs: https://developer.android.com/training/data-storage/shared/media?hl=de
|
|
1027
1066
|
"""
|
|
1028
|
-
|
|
1067
|
+
if isinstance(content, bytes) and 'b' not in extra_mode:
|
|
1068
|
+
extra_mode += 'b'
|
|
1069
|
+
|
|
1070
|
+
if extra_mode == '' or extra_mode[0] not in ('a', 'w'):
|
|
1071
|
+
extra_mode = 'w' + extra_mode
|
|
1072
|
+
|
|
1073
|
+
with open(file_path, mode=extra_mode, encoding=encoding) as file_handle:
|
|
1029
1074
|
file_handle.write(content)
|
|
1030
1075
|
|
|
1031
1076
|
|
|
@@ -1054,16 +1099,16 @@ class ErrorMsgMixin:
|
|
|
1054
1099
|
PACKAGE_NAME = stack_var('__name__') or 'unspecified_package'
|
|
1055
1100
|
PACKAGE_DOMAIN = 'org.test'
|
|
1056
1101
|
PERMISSIONS = "INTERNET, VIBRATE, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE"
|
|
1057
|
-
if
|
|
1102
|
+
if os_path_isfile(BUILD_CONFIG_FILE): # pragma: no cover
|
|
1058
1103
|
PACKAGE_NAME, PACKAGE_DOMAIN, PERMISSIONS = build_config_variable_values(
|
|
1059
1104
|
('package.name', PACKAGE_NAME),
|
|
1060
1105
|
('package.domain', PACKAGE_DOMAIN),
|
|
1061
1106
|
('android.permissions', PERMISSIONS))
|
|
1062
1107
|
elif os_platform == 'android': # pragma: no cover
|
|
1063
1108
|
_importing_package = norm_path(stack_var('__file__') or 'empty_package' + PY_EXT)
|
|
1064
|
-
if
|
|
1065
|
-
_importing_package =
|
|
1066
|
-
_importing_package =
|
|
1109
|
+
if os_path_basename(_importing_package) in (PY_INIT, PY_MAIN):
|
|
1110
|
+
_importing_package = os_path_dirname(_importing_package)
|
|
1111
|
+
_importing_package = os_path_splitext(os_path_basename(_importing_package))[0]
|
|
1067
1112
|
write_file(f'{_importing_package}_debug.log', f"{BUILD_CONFIG_FILE} not bundled - using defaults\n", extra_mode='a')
|
|
1068
1113
|
|
|
1069
1114
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ae_base
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.42
|
|
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.42
|
|
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.41)
|
|
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.42.
|
|
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")
|
|
@@ -739,6 +746,11 @@ class TestBaseHelpers:
|
|
|
739
746
|
assert os.path.exists(test_file)
|
|
740
747
|
assert os.path.isfile(test_file)
|
|
741
748
|
assert read_file(test_file, extra_mode="b") == content
|
|
749
|
+
|
|
750
|
+
write_file(test_file, content) # 'b' in extra_mode arg is optional because content is bytes array
|
|
751
|
+
assert os.path.exists(test_file)
|
|
752
|
+
assert os.path.isfile(test_file)
|
|
753
|
+
assert read_file(test_file, extra_mode="b") == content
|
|
742
754
|
finally:
|
|
743
755
|
if os.path.exists(test_file):
|
|
744
756
|
os.remove(test_file)
|
|
@@ -749,7 +761,7 @@ class TestModuleHelpers:
|
|
|
749
761
|
namespace = TESTS_FOLDER
|
|
750
762
|
mod_name = 'test_module_name'
|
|
751
763
|
att_name = 'test_module_func'
|
|
752
|
-
module_file = os.path.join(namespace, mod_name + PY_EXT)
|
|
764
|
+
module_file = cast(str, os.path.join(namespace, mod_name + PY_EXT))
|
|
753
765
|
try:
|
|
754
766
|
write_file(module_file, f"def {att_name}(*args, **kwargs):\n return args, kwargs\n")
|
|
755
767
|
args = (1, '2')
|
|
@@ -777,7 +789,7 @@ class TestModuleHelpers:
|
|
|
777
789
|
namespace = TESTS_FOLDER
|
|
778
790
|
mod_name = 'test_module_name'
|
|
779
791
|
att_name = 'test_module_func'
|
|
780
|
-
module_file = os.path.join(namespace, mod_name + PY_EXT)
|
|
792
|
+
module_file = cast(str, os.path.join(namespace, mod_name + PY_EXT))
|
|
781
793
|
try:
|
|
782
794
|
write_file(module_file, f"def {att_name}(arg1, args2, kwarg1='default'):\n return arg1, arg2, kwarg1\n")
|
|
783
795
|
|
|
@@ -802,7 +814,7 @@ class TestModuleHelpers:
|
|
|
802
814
|
def test_module_attr_module_ref(self):
|
|
803
815
|
namespace = TESTS_FOLDER
|
|
804
816
|
mod_name = 'test_module_name'
|
|
805
|
-
module_file = os.path.join(namespace, mod_name + PY_EXT)
|
|
817
|
+
module_file = cast(str, os.path.join(namespace, mod_name + PY_EXT))
|
|
806
818
|
cur_dir = os.getcwd()
|
|
807
819
|
try:
|
|
808
820
|
write_file(module_file, "# empty module")
|
|
@@ -825,7 +837,7 @@ class TestModuleHelpers:
|
|
|
825
837
|
namespace = TESTS_FOLDER
|
|
826
838
|
mod_name = 'test_module_name'
|
|
827
839
|
att_name = 'test_module_func'
|
|
828
|
-
module_file = os.path.join(namespace, mod_name + PY_EXT)
|
|
840
|
+
module_file = cast(str, os.path.join(namespace, mod_name + PY_EXT))
|
|
829
841
|
cur_dir = os.getcwd()
|
|
830
842
|
try:
|
|
831
843
|
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
|