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.
@@ -1,4 +1,4 @@
1
- <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_project V0.3.30 -->
1
+ <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_project V0.3.31 -->
2
2
  ### GNU GENERAL PUBLIC LICENSE
3
3
 
4
4
  Version 3, 29 June 2007
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ae_base
3
- Version: 0.3.39
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.39
58
+ # base 0.3.41
57
59
 
58
60
  [![GitLab develop](https://img.shields.io/gitlab/pipeline/ae-group/ae_base/develop?logo=python)](
59
61
  https://gitlab.com/ae-group/ae_base)
60
62
  [![LatestPyPIrelease](
61
- https://img.shields.io/gitlab/pipeline/ae-group/ae_base/release0.3.38?logo=python)](
62
- https://gitlab.com/ae-group/ae_base/-/tree/release0.3.38)
63
+ https://img.shields.io/gitlab/pipeline/ae-group/ae_base/release0.3.40?logo=python)](
64
+ https://gitlab.com/ae-group/ae_base/-/tree/release0.3.40)
63
65
  [![PyPIVersions](https://img.shields.io/pypi/v/ae_base)](
64
66
  https://pypi.org/project/ae-base/#history)
65
67
 
66
- >ae_base module 0.3.39.
68
+ >ae_base module 0.3.41.
67
69
 
68
70
  [![Coverage](https://ae-group.gitlab.io/ae_base/coverage.svg)](
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.39
3
+ # base 0.3.41
4
4
 
5
5
  [![GitLab develop](https://img.shields.io/gitlab/pipeline/ae-group/ae_base/develop?logo=python)](
6
6
  https://gitlab.com/ae-group/ae_base)
7
7
  [![LatestPyPIrelease](
8
- https://img.shields.io/gitlab/pipeline/ae-group/ae_base/release0.3.38?logo=python)](
9
- https://gitlab.com/ae-group/ae_base/-/tree/release0.3.38)
8
+ https://img.shields.io/gitlab/pipeline/ae-group/ae_base/release0.3.40?logo=python)](
9
+ https://gitlab.com/ae-group/ae_base/-/tree/release0.3.40)
10
10
  [![PyPIVersions](https://img.shields.io/pypi/v/ae_base)](
11
11
  https://pypi.org/project/ae-base/#history)
12
12
 
13
- >ae_base module 0.3.39.
13
+ >ae_base module 0.3.41.
14
14
 
15
15
  [![Coverage](https://ae-group.gitlab.io/ae_base/coverage.svg)](
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.39'
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 = '_' #: name parts separator character, e.g. for :func:`norm_name`
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 datetime.datetime.utcnow().strftime("{sep}%Y%m%d{sep}%H%M%S{sep}%f".format(sep=sep))
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 filename2uri(file_name: str) -> str:
979
- """ convert a file name converted by :func:`uri2filename` back to its representation as a URI
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
- :param file_name: name of the file/folder to convert back to its URI representation.
982
- :return: URI string.
1014
+ :return: timestamp string of the actual UTC date and time.
983
1015
  """
984
- return urllib.parse.unquote(file_name)
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.39
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.39
58
+ # base 0.3.41
57
59
 
58
60
  [![GitLab develop](https://img.shields.io/gitlab/pipeline/ae-group/ae_base/develop?logo=python)](
59
61
  https://gitlab.com/ae-group/ae_base)
60
62
  [![LatestPyPIrelease](
61
- https://img.shields.io/gitlab/pipeline/ae-group/ae_base/release0.3.38?logo=python)](
62
- https://gitlab.com/ae-group/ae_base/-/tree/release0.3.38)
63
+ https://img.shields.io/gitlab/pipeline/ae-group/ae_base/release0.3.40?logo=python)](
64
+ https://gitlab.com/ae-group/ae_base/-/tree/release0.3.40)
63
65
  [![PyPIVersions](https://img.shields.io/pypi/v/ae_base)](
64
66
  https://pypi.org/project/ae-base/#history)
65
67
 
66
- >ae_base module 0.3.39.
68
+ >ae_base module 0.3.41.
67
69
 
68
70
  [![Coverage](https://ae-group.gitlab.io/ae_base/coverage.svg)](
69
71
  https://ae-group.gitlab.io/ae_base/coverage/index.html)
@@ -10,6 +10,7 @@ mypy
10
10
  pylint
11
11
  pytest
12
12
  pytest-cov
13
+ pytest-django
13
14
  typing
14
15
  types-setuptools
15
16
  wheel
@@ -26,6 +27,7 @@ mypy
26
27
  pylint
27
28
  pytest
28
29
  pytest-cov
30
+ pytest-django
29
31
  typing
30
32
  types-setuptools
31
33
  wheel
@@ -1,4 +1,4 @@
1
- # THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_project V0.3.30
1
+ # THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_project V0.3.31
2
2
  """ setup this project with setuptools and aedev.setup_project. """
3
3
  import pprint
4
4
  import sys
@@ -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, filename2uri, uri2filename, write_file, ErrorMsgMixin)
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
- del sys.__dict__['frozen']
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