ae-base 0.3.37__py3-none-any.whl → 0.3.39__py3-none-any.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.
ae/base.py CHANGED
@@ -42,7 +42,7 @@ absolute, call the function :func:`norm_path`.
42
42
 
43
43
  :func:`camel_to_snake` and :func:`snake_to_camel` providing name conversions of class and method names.
44
44
 
45
- to encode unicode strings to other codecs the functions :func:`force_encoding` and :func:`to_ascii` can be used.
45
+ to encode Unicode strings to other codecs the functions :func:`force_encoding` and :func:`to_ascii` can be used.
46
46
 
47
47
  the :func:`round_traditional` function get provided by this module for traditional rounding of float values. the
48
48
  function signature is fully compatible to Python's :func:`round` function.
@@ -137,6 +137,7 @@ import shutil
137
137
  import socket
138
138
  import sys
139
139
  import unicodedata
140
+ import urllib.parse
140
141
  import warnings
141
142
 
142
143
  from configparser import ConfigParser, ExtendedInterpolation
@@ -146,7 +147,8 @@ from inspect import getinnerframes, getouterframes, getsourcefile
146
147
  from types import ModuleType
147
148
  from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union, cast
148
149
 
149
- __version__ = '0.3.37'
150
+
151
+ __version__ = '0.3.39'
150
152
 
151
153
 
152
154
  DOCS_FOLDER = 'docs' #: project documentation root folder name
@@ -435,7 +437,8 @@ def load_dotenvs():
435
437
  .. hint:: call from main module of project/app in order to also load ``.env`` files in/above the project folder.
436
438
  """
437
439
  load_env_var_defaults(os.getcwd())
438
- load_env_var_defaults(os.path.dirname(stack_var('__file__', depth=2)))
440
+ if file_name := stack_var('__file__'):
441
+ load_env_var_defaults(os.path.dirname(os.path.abspath(file_name)))
439
442
 
440
443
 
441
444
  def load_env_var_defaults(start_dir: str):
@@ -920,7 +923,7 @@ def sys_env_text(ind_ch: str = " ", ind_len: int = 12, key_ch: str = "=", key_le
920
923
  extra_sys_env_dict: Optional[Dict[str, str]] = None) -> str:
921
924
  """ compile formatted text block with system environment info.
922
925
 
923
- :param ind_ch: indent character (default=" ").
926
+ :param ind_ch: indent character (defaults to " ").
924
927
  :param ind_len: indent depths (default=12 characters).
925
928
  :param key_ch: key-value separator character (default="=").
926
929
  :param key_len: key-name minimum length (default=15 characters).
@@ -939,7 +942,7 @@ def sys_env_text(ind_ch: str = " ", ind_len: int = 12, key_ch: str = "=", key_le
939
942
 
940
943
 
941
944
  def to_ascii(unicode_str: str) -> str:
942
- """ converts unicode string into ascii representation.
945
+ """ converts Unicode string into ascii representation.
943
946
 
944
947
  useful for fuzzy string compare; inspired by MiniQuark's answer
945
948
  in: https://stackoverflow.com/questions/517923/what-is-the-best-way-to-remove-accents-in-a-python-unicode-string
@@ -951,6 +954,36 @@ def to_ascii(unicode_str: str) -> str:
951
954
  return "".join([c for c in nfkd_form if not unicodedata.combining(c)]).replace('ß', "ss").replace('€', "Euro")
952
955
 
953
956
 
957
+ def uri2filename(uri: str) -> str:
958
+ """ convert a URI to be usable as name of a file or folder
959
+
960
+ :param uri: URI to convert to a corresponding file name, that will be revertible back to this URI.
961
+ :return: name of a file/folder representing the specified URI.
962
+
963
+ in *nix only / and \0 are not allowed characters in file names.
964
+ in MS Windows are not allowed: ASCII 0...31): / \\ : * ? ” < > | (). some blogs recommend to also not allow
965
+ (convert) the characters # and '.
966
+ only old POSIX seems to be even more restricted (only allowing alphanumeric characters plus . - and _).
967
+
968
+ file name length is not restricted/shortened by this function, although the maximum is 255 characters on most OSs.
969
+
970
+ more on allowed characters in file names in the answers of RedGrittyBrick on https://superuser.com/questions/358855
971
+ and of Christopher Oezbek on https://stackoverflow.com/questions/1976007.
972
+ """
973
+ # using urllib.parse.quote(uri, safe="") instead would convert also any non-ascii (e.g. umlaut) characters into hex
974
+ # added [] to str.join() argument because List comprehensions are faster than generator expressions
975
+ return "".join([f"%{hex(ord(_))[2:].upper()}" if _ in '/|\\:*?"<>%' else _ for _ in uri])
976
+
977
+
978
+ def filename2uri(file_name: str) -> str:
979
+ """ convert a file name converted by :func:`uri2filename` back to its representation as a URI
980
+
981
+ :param file_name: name of the file/folder to convert back to its URI representation.
982
+ :return: URI string.
983
+ """
984
+ return urllib.parse.unquote(file_name)
985
+
986
+
954
987
  def write_file(file_path: str, content: Union[str, bytes], extra_mode: str = "", encoding: Optional[str] = None):
955
988
  """ (over)write the file specified by :paramref:`~write_file.file_path` with text or binary/bytes content.
956
989
 
@@ -965,11 +998,42 @@ def write_file(file_path: str, content: Union[str, bytes], extra_mode: str = "",
965
998
  :raises OSError: if :paramref:`~read_file.file_path` is misspelled or contains invalid characters.
966
999
  :raises PermissionError: if current OS user account lacks permissions to read the file content.
967
1000
  :raises ValueError: on decoding errors.
1001
+
1002
+ to extend this function for Android 14+ see https://github.com/beeware/toga/pull/1158#issuecomment-2254564657
1003
+ and https://gist.github.com/neonankiti/05922cf0a44108a2e2732671ed9ef386
1004
+ Yes, to use ACTION_CREATE_DOCUMENT, you don't supply a URI in the intent. You wait for the intent result, and that
1005
+ will contain a URI which you can write to.
1006
+ See #1158 (comment - https://github.com/beeware/toga/pull/1158#issuecomment-2254564657) for a link to a Java
1007
+ example, and #1158 (comment - https://github.com/beeware/toga/pull/1158#issuecomment-1446196973) for how to wait
1008
+ for an intent result.
1009
+ Related german docs: https://developer.android.com/training/data-storage/shared/media?hl=de
968
1010
  """
969
1011
  with open(file_path, ('' if extra_mode.startswith('a') else 'w') + extra_mode, encoding=encoding) as file_handle:
970
1012
  file_handle.write(content)
971
1013
 
972
1014
 
1015
+ class ErrorMsgMixin:
1016
+ """ mixin class providing error message """
1017
+ _err_msg: str = ""
1018
+
1019
+ @property
1020
+ def error_message(self) -> str:
1021
+ """ error message string if an error occurred or an empty string if not.
1022
+
1023
+ :getter: return the accumulated error message of the recently occurred error(s).
1024
+ :setter: any assigned error message will be accumulated to recent error messages.
1025
+ pass an empty string to reset the error message.
1026
+ """
1027
+ return self._err_msg
1028
+
1029
+ @error_message.setter
1030
+ def error_message(self, next_err_msg: str):
1031
+ if next_err_msg:
1032
+ self._err_msg += ("\n\n" if self._err_msg else "") + next_err_msg
1033
+ else:
1034
+ self._err_msg = ""
1035
+
1036
+
973
1037
  PACKAGE_NAME = stack_var('__name__') or 'unspecified_package'
974
1038
  PACKAGE_DOMAIN = 'org.test'
975
1039
  PERMISSIONS = "INTERNET, VIBRATE, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ae_base
3
- Version: 0.3.37
3
+ Version: 0.3.39
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
@@ -22,48 +22,48 @@ Requires-Python: >=3.9
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.md
24
24
  Provides-Extra: dev
25
- Requires-Dist: aedev-tpl-project ; extra == 'dev'
26
- Requires-Dist: ae-ae ; extra == 'dev'
27
- Requires-Dist: anybadge ; extra == 'dev'
28
- Requires-Dist: coverage-badge ; extra == 'dev'
29
- Requires-Dist: aedev-git-repo-manager ; extra == 'dev'
30
- Requires-Dist: flake8 ; extra == 'dev'
31
- Requires-Dist: mypy ; extra == 'dev'
32
- Requires-Dist: pylint ; extra == 'dev'
33
- Requires-Dist: pytest ; extra == 'dev'
34
- Requires-Dist: pytest-cov ; extra == 'dev'
35
- Requires-Dist: typing ; extra == 'dev'
36
- Requires-Dist: types-setuptools ; extra == 'dev'
37
- Requires-Dist: wheel ; extra == 'dev'
38
- Requires-Dist: twine ; extra == 'dev'
25
+ Requires-Dist: aedev-tpl-project; extra == "dev"
26
+ Requires-Dist: ae-ae; extra == "dev"
27
+ Requires-Dist: anybadge; extra == "dev"
28
+ Requires-Dist: coverage-badge; extra == "dev"
29
+ Requires-Dist: aedev-git-repo-manager; extra == "dev"
30
+ Requires-Dist: flake8; extra == "dev"
31
+ Requires-Dist: mypy; extra == "dev"
32
+ Requires-Dist: pylint; extra == "dev"
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: pytest-cov; extra == "dev"
35
+ Requires-Dist: typing; extra == "dev"
36
+ Requires-Dist: types-setuptools; extra == "dev"
37
+ Requires-Dist: wheel; extra == "dev"
38
+ Requires-Dist: twine; extra == "dev"
39
39
  Provides-Extra: docs
40
40
  Provides-Extra: tests
41
- Requires-Dist: anybadge ; extra == 'tests'
42
- Requires-Dist: coverage-badge ; extra == 'tests'
43
- Requires-Dist: aedev-git-repo-manager ; extra == 'tests'
44
- Requires-Dist: flake8 ; extra == 'tests'
45
- Requires-Dist: mypy ; extra == 'tests'
46
- Requires-Dist: pylint ; extra == 'tests'
47
- Requires-Dist: pytest ; extra == 'tests'
48
- Requires-Dist: pytest-cov ; extra == 'tests'
49
- Requires-Dist: typing ; extra == 'tests'
50
- Requires-Dist: types-setuptools ; extra == 'tests'
51
- Requires-Dist: wheel ; extra == 'tests'
52
- Requires-Dist: twine ; extra == 'tests'
41
+ Requires-Dist: anybadge; extra == "tests"
42
+ Requires-Dist: coverage-badge; extra == "tests"
43
+ Requires-Dist: aedev-git-repo-manager; extra == "tests"
44
+ Requires-Dist: flake8; extra == "tests"
45
+ Requires-Dist: mypy; extra == "tests"
46
+ Requires-Dist: pylint; extra == "tests"
47
+ Requires-Dist: pytest; extra == "tests"
48
+ Requires-Dist: pytest-cov; extra == "tests"
49
+ Requires-Dist: typing; extra == "tests"
50
+ Requires-Dist: types-setuptools; extra == "tests"
51
+ Requires-Dist: wheel; extra == "tests"
52
+ Requires-Dist: twine; extra == "tests"
53
53
 
54
54
  <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.94 -->
55
55
  <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_namespace_root V0.3.14 -->
56
- # base 0.3.37
56
+ # base 0.3.39
57
57
 
58
58
  [![GitLab develop](https://img.shields.io/gitlab/pipeline/ae-group/ae_base/develop?logo=python)](
59
59
  https://gitlab.com/ae-group/ae_base)
60
60
  [![LatestPyPIrelease](
61
- https://img.shields.io/gitlab/pipeline/ae-group/ae_base/release0.3.36?logo=python)](
62
- https://gitlab.com/ae-group/ae_base/-/tree/release0.3.36)
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
63
  [![PyPIVersions](https://img.shields.io/pypi/v/ae_base)](
64
64
  https://pypi.org/project/ae-base/#history)
65
65
 
66
- >ae_base module 0.3.37.
66
+ >ae_base module 0.3.39.
67
67
 
68
68
  [![Coverage](https://ae-group.gitlab.io/ae_base/coverage.svg)](
69
69
  https://ae-group.gitlab.io/ae_base/coverage/index.html)
@@ -0,0 +1,7 @@
1
+ ae/base.py,sha256=aA_qa4s-17EzXKjN9wwfya688eGnFNJP0EglSLeYJjw,55182
2
+ ae_base-0.3.39.dist-info/LICENSE.md,sha256=3X7IwvwQFt4PqRHb7mV8qoJjQ1E-HmcGioyT4Y6-6c8,35002
3
+ ae_base-0.3.39.dist-info/METADATA,sha256=YPZbM4qKqfaO-3WvDLvV77O9WNWOfxg6uxhkO7nu0vA,5219
4
+ ae_base-0.3.39.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
5
+ ae_base-0.3.39.dist-info/top_level.txt,sha256=vUdgAslSmhZLXWU48fm8AG2BjVnkOWLco8rzuW-5zY0,3
6
+ ae_base-0.3.39.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
+ ae_base-0.3.39.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,7 +0,0 @@
1
- ae/base.py,sha256=Jb4C0-uyu40_vK0dQZnHHR2hTwDILzaGpS7ecp-0BQs,52008
2
- ae_base-0.3.37.dist-info/LICENSE.md,sha256=3X7IwvwQFt4PqRHb7mV8qoJjQ1E-HmcGioyT4Y6-6c8,35002
3
- ae_base-0.3.37.dist-info/METADATA,sha256=2wTlS7GMWnV1vuGdsuMb1z_KWr-y2KwUsLcesuWYVMw,5245
4
- ae_base-0.3.37.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
5
- ae_base-0.3.37.dist-info/top_level.txt,sha256=vUdgAslSmhZLXWU48fm8AG2BjVnkOWLco8rzuW-5zY0,3
6
- ae_base-0.3.37.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
- ae_base-0.3.37.dist-info/RECORD,,