ae-base 0.3.45__tar.gz → 0.3.46__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.45 → ae_base-0.3.46}/LICENSE.md +1 -1
- {ae_base-0.3.45/ae_base.egg-info → ae_base-0.3.46}/PKG-INFO +7 -6
- {ae_base-0.3.45 → ae_base-0.3.46}/README.md +5 -5
- {ae_base-0.3.45 → ae_base-0.3.46}/ae/base.py +65 -36
- {ae_base-0.3.45 → ae_base-0.3.46/ae_base.egg-info}/PKG-INFO +7 -6
- {ae_base-0.3.45 → ae_base-0.3.46}/setup.py +1 -1
- {ae_base-0.3.45 → ae_base-0.3.46}/tests/test_base.py +61 -6
- {ae_base-0.3.45 → ae_base-0.3.46}/ae_base.egg-info/SOURCES.txt +0 -0
- {ae_base-0.3.45 → ae_base-0.3.46}/ae_base.egg-info/dependency_links.txt +0 -0
- {ae_base-0.3.45 → ae_base-0.3.46}/ae_base.egg-info/requires.txt +0 -0
- {ae_base-0.3.45 → ae_base-0.3.46}/ae_base.egg-info/top_level.txt +0 -0
- {ae_base-0.3.45 → ae_base-0.3.46}/ae_base.egg-info/zip-safe +0 -0
- {ae_base-0.3.45 → ae_base-0.3.46}/setup.cfg +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ae_base
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.46
|
|
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
|
|
7
7
|
Author-email: aecker2@gmail.com
|
|
8
8
|
License: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
9
|
+
Project-URL: Bug Tracker, https://gitlab.com/ae-group/ae_base/-/issues
|
|
9
10
|
Project-URL: Documentation, https://ae.readthedocs.io/en/latest/_autosummary/ae.base.html
|
|
10
11
|
Project-URL: Repository, https://gitlab.com/ae-group/ae_base
|
|
11
12
|
Project-URL: Source, https://ae.readthedocs.io/en/latest/_modules/ae/base.html
|
|
@@ -53,19 +54,19 @@ Requires-Dist: types-setuptools; extra == "tests"
|
|
|
53
54
|
Requires-Dist: wheel; extra == "tests"
|
|
54
55
|
Requires-Dist: twine; extra == "tests"
|
|
55
56
|
|
|
56
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.
|
|
57
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.95 -->
|
|
57
58
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_namespace_root V0.3.14 -->
|
|
58
|
-
# base 0.3.
|
|
59
|
+
# base 0.3.46
|
|
59
60
|
|
|
60
61
|
[](
|
|
61
62
|
https://gitlab.com/ae-group/ae_base)
|
|
62
63
|
[](
|
|
65
|
+
https://gitlab.com/ae-group/ae_base/-/tree/release0.3.45)
|
|
65
66
|
[](
|
|
66
67
|
https://pypi.org/project/ae-base/#history)
|
|
67
68
|
|
|
68
|
-
>ae_base module 0.3.
|
|
69
|
+
>ae_base module 0.3.46.
|
|
69
70
|
|
|
70
71
|
[](
|
|
71
72
|
https://ae-group.gitlab.io/ae_base/coverage/index.html)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.
|
|
1
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.95 -->
|
|
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.46
|
|
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.45)
|
|
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.46.
|
|
14
14
|
|
|
15
15
|
[](
|
|
16
16
|
https://ae-group.gitlab.io/ae_base/coverage/index.html)
|
|
@@ -9,10 +9,10 @@ functions, useful classes and context managers.
|
|
|
9
9
|
base constants
|
|
10
10
|
--------------
|
|
11
11
|
|
|
12
|
-
ISO format strings for
|
|
12
|
+
ISO format strings for ``date`` and ``datetime`` values are provided by the constants :data:`DATE_ISO` and
|
|
13
13
|
:data:`DATE_TIME_ISO`.
|
|
14
14
|
|
|
15
|
-
the :data:`UNSET` constant is useful in cases where
|
|
15
|
+
the :data:`UNSET` constant is useful in cases where ``None`` is a valid data value and another special value is needed
|
|
16
16
|
to specify that e.g. an argument or attribute has no (valid) value or did not get specified/passed.
|
|
17
17
|
|
|
18
18
|
default values to compile file and folder names for a package or an app project are provided by the constants:
|
|
@@ -20,8 +20,8 @@ default values to compile file and folder names for a package or an app project
|
|
|
20
20
|
:data:`PACKAGE_INCLUDE_FILES_PREFIX`, :data:`PY_EXT`, :data:`PY_INIT`, :data:`PY_MAIN`, :data:`CFG_EXT`
|
|
21
21
|
and :data:`INI_EXT`.
|
|
22
22
|
|
|
23
|
-
the constants :data:`PACKAGE_NAME`, :data:`PACKAGE_DOMAIN` and :data:`PERMISSIONS` are mainly
|
|
24
|
-
on mobile devices. to avoid redundancies, these values get loaded from the
|
|
23
|
+
the constants :data:`PACKAGE_NAME`, :data:`PACKAGE_DOMAIN` and :data:`PERMISSIONS` are mainly
|
|
24
|
+
used for apps running 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
27
|
with the help of the format string constant :data:`NOW_STR_FORMAT` and the function :func:`now_str` you can create a
|
|
@@ -104,8 +104,8 @@ links to other android code and service examples and documentation:
|
|
|
104
104
|
* `https://github.com/Android-for-Python/Android-for-Python-Users`__
|
|
105
105
|
* `https://github.com/Android-for-Python/INDEX-of-Examples`__
|
|
106
106
|
|
|
107
|
-
big thanks to `Robert Flatt <https://github.com/RobertFlatt>`
|
|
108
|
-
code and build Kivy apps for the Android OS, and to `Gabriel Pettier <https://github.com/tshirtman>`
|
|
107
|
+
big thanks to `Robert Flatt <https://github.com/RobertFlatt>`__ for his investigations, findings and documentations to
|
|
108
|
+
code and build Kivy apps for the Android OS, and to `Gabriel Pettier <https://github.com/tshirtman>`__ for his service
|
|
109
109
|
osc example.
|
|
110
110
|
|
|
111
111
|
|
|
@@ -113,9 +113,9 @@ types, classes and mixins
|
|
|
113
113
|
-------------------------
|
|
114
114
|
|
|
115
115
|
the :class:`UnsetType` class can be used e.g. for the declaration of optional function and method parameters,
|
|
116
|
-
allowing also
|
|
116
|
+
allowing also ``None`` is an accepted argument value.
|
|
117
117
|
|
|
118
|
-
to extend any class with an intelligent error message
|
|
118
|
+
to extend any class with an intelligent error message handling, add the mixin :class:`ErrorMsgMixin` to it.
|
|
119
119
|
|
|
120
120
|
|
|
121
121
|
generic context manager
|
|
@@ -173,7 +173,7 @@ from types import ModuleType
|
|
|
173
173
|
from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union, cast
|
|
174
174
|
|
|
175
175
|
|
|
176
|
-
__version__ = '0.3.
|
|
176
|
+
__version__ = '0.3.46'
|
|
177
177
|
|
|
178
178
|
|
|
179
179
|
os_path_abspath = os.path.abspath
|
|
@@ -265,7 +265,7 @@ class UnsetType:
|
|
|
265
265
|
return 0
|
|
266
266
|
|
|
267
267
|
|
|
268
|
-
UNSET = UnsetType() #: pseudo value used for attributes/arguments if
|
|
268
|
+
UNSET = UnsetType() #: pseudo value used for attributes/arguments if ``None`` is needed as a valid value
|
|
269
269
|
|
|
270
270
|
|
|
271
271
|
def app_name_guess() -> str:
|
|
@@ -405,10 +405,11 @@ def defuse(value: str) -> str:
|
|
|
405
405
|
|
|
406
406
|
the ASCII character range 0..31 gets converted to the Unicode range U+2400 + ord(char): 0==U+2400 ... 31==U+241F.
|
|
407
407
|
|
|
408
|
-
in
|
|
408
|
+
in most unix variants only the slash and the ASCII 0 characters are not allowed in file names.
|
|
409
409
|
|
|
410
|
-
in MS Windows are not allowed: ASCII 0
|
|
410
|
+
in MS Windows are not allowed: ASCII 0..31 / | \\ : * ? ” % < > ( ). some blogs recommend to also not allow
|
|
411
411
|
(convert) the characters # and '.
|
|
412
|
+
|
|
412
413
|
only old POSIX seems to be even more restricted (only allowing alphanumeric characters plus . - and _).
|
|
413
414
|
|
|
414
415
|
more on allowed characters in file names in the answers of RedGrittyBrick on https://superuser.com/questions/358855
|
|
@@ -416,7 +417,7 @@ def defuse(value: str) -> str:
|
|
|
416
417
|
|
|
417
418
|
file name length is not restricted/shortened by this function, although the maximum is 255 characters on most OSs.
|
|
418
419
|
|
|
419
|
-
.. hint:: use :func:`dedefuse` to convert the defused string back to the corresponding URI/file-path.
|
|
420
|
+
.. hint:: use the :func:`dedefuse` function to convert the defused string back to the corresponding URI/file-path.
|
|
420
421
|
|
|
421
422
|
"""
|
|
422
423
|
defused = ""
|
|
@@ -663,7 +664,7 @@ def module_file_path(local_object: Optional[Callable] = None) -> str:
|
|
|
663
664
|
def module_name(*skip_modules: str, depth: int = 0) -> Optional[str]:
|
|
664
665
|
""" find the first module in the call stack that is *not* in :paramref:`~module_name.skip_modules`.
|
|
665
666
|
|
|
666
|
-
:param skip_modules: module names to skip (def=this
|
|
667
|
+
:param skip_modules: module names to skip (def=this and other core modules, see :data:`SKIPPED_MODULES`).
|
|
667
668
|
:param depth: the calling level from which on to search. the default value 0 refers the frame and
|
|
668
669
|
the module of the caller of this function.
|
|
669
670
|
pass 1 or an even higher value if you want to get the module name of a function/method
|
|
@@ -851,12 +852,11 @@ def parse_dotenv(file_path: str) -> Dict[str, str]:
|
|
|
851
852
|
delimiter = None
|
|
852
853
|
if delimiter != "'":
|
|
853
854
|
for parts in _env_variable.findall(var_val):
|
|
854
|
-
if
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
var_val = var_val.replace("".join(parts[0:-1]), replace)
|
|
855
|
+
# substitute env variables in a value with its value, if declared and not escaped
|
|
856
|
+
if parts[0] == '\\' or (replace := env_vars.get(parts[-1], os.environ.get(parts[-1], UNSET))) is UNSET:
|
|
857
|
+
replace = "".join(parts[1:-1]) # don't replace escaped/undeclared vars to prevent value cut at '$'
|
|
858
|
+
|
|
859
|
+
var_val = var_val.replace("".join(parts[0:-1]), cast(str, replace))
|
|
860
860
|
|
|
861
861
|
env_vars[var_nam] = var_val
|
|
862
862
|
|
|
@@ -901,8 +901,8 @@ def read_file(file_path: str, extra_mode: str = "", encoding: Optional[str] = No
|
|
|
901
901
|
read the content of a binary file returned as bytes array. in binary mode the argument
|
|
902
902
|
passed in :paramref:`~read_file.error_handling` will be ignored.
|
|
903
903
|
:param encoding: encoding used to load and convert/interpret the file content.
|
|
904
|
-
:param error_handling: for files opened in text mode pass `'strict'` or
|
|
905
|
-
empty string) for the cases where either a decoding `ValueError` exception or any
|
|
904
|
+
:param error_handling: for files opened in text mode pass `'strict'` or ``None`` to return ``None`` (instead of
|
|
905
|
+
an empty string) for the cases where either a decoding `ValueError` exception or any
|
|
906
906
|
`OSError`, `FileNotFoundError` or `PermissionError` exception got raised.
|
|
907
907
|
the default value `'ignore'` will ignore any decoding errors (missing some characters)
|
|
908
908
|
and will return an empty string on any file/os exception. this parameter will be ignored
|
|
@@ -1118,14 +1118,14 @@ def write_file(file_path: str, content: Union[str, bytes],
|
|
|
1118
1118
|
:raises PermissionError: if current OS user account lacks permissions to read the file content.
|
|
1119
1119
|
:raises ValueError: on decoding errors.
|
|
1120
1120
|
|
|
1121
|
-
to extend this function for Android 14+ see https://github.com/beeware/toga/pull/1158#issuecomment-2254564657
|
|
1122
|
-
and https://gist.github.com/neonankiti/05922cf0a44108a2e2732671ed9ef386
|
|
1121
|
+
to extend this function for Android 14+ see `<https://github.com/beeware/toga/pull/1158#issuecomment-2254564657>`__
|
|
1122
|
+
and `<https://gist.github.com/neonankiti/05922cf0a44108a2e2732671ed9ef386>`__
|
|
1123
1123
|
Yes, to use ACTION_CREATE_DOCUMENT, you don't supply a URI in the intent. You wait for the intent result, and that
|
|
1124
1124
|
will contain a URI which you can write to.
|
|
1125
|
-
See #1158 (comment - https://github.com/beeware/toga/pull/1158#issuecomment-2254564657) for a link to a Java
|
|
1126
|
-
example, and #1158 (comment - https://github.com/beeware/toga/pull/1158#issuecomment-1446196973) for how to
|
|
1127
|
-
for an intent result.
|
|
1128
|
-
Related german docs: https://developer.android.com/training/data-storage/shared/media?hl=de
|
|
1125
|
+
See #1158 (comment - `<https://github.com/beeware/toga/pull/1158#issuecomment-2254564657>`__) for a link to a Java
|
|
1126
|
+
example, and #1158 (comment - `<https://github.com/beeware/toga/pull/1158#issuecomment-1446196973>`__) for how to
|
|
1127
|
+
wait for an intent result.
|
|
1128
|
+
Related german docs: `<https://developer.android.com/training/data-storage/shared/media?hl=de>`__
|
|
1129
1129
|
"""
|
|
1130
1130
|
if make_dirs and (dir_path := os_path_dirname(file_path)):
|
|
1131
1131
|
os.makedirs(dir_path, exist_ok=True)
|
|
@@ -1141,9 +1141,33 @@ def write_file(file_path: str, content: Union[str, bytes],
|
|
|
1141
1141
|
|
|
1142
1142
|
|
|
1143
1143
|
class ErrorMsgMixin:
|
|
1144
|
-
""" mixin class providing error message """
|
|
1144
|
+
""" mixin class providing sophisticated error message handling. """
|
|
1145
1145
|
_err_msg: str = ""
|
|
1146
1146
|
|
|
1147
|
+
cae = None
|
|
1148
|
+
po = print
|
|
1149
|
+
dpo = print
|
|
1150
|
+
vpo = print
|
|
1151
|
+
|
|
1152
|
+
def __init__(self):
|
|
1153
|
+
try:
|
|
1154
|
+
from ae.core import main_app_instance # type: ignore
|
|
1155
|
+
|
|
1156
|
+
self.cae = cae = main_app_instance()
|
|
1157
|
+
assert cae is not None, f"{self.__class__.__name__}.__init__() called too early; main app instance not"
|
|
1158
|
+
|
|
1159
|
+
self.po = cae.po
|
|
1160
|
+
self.dpo = cae.dpo
|
|
1161
|
+
self.vpo = cae.vpo
|
|
1162
|
+
|
|
1163
|
+
except (ImportError, AssertionError, Exception) as exc:
|
|
1164
|
+
print(f"{self.__class__.__name__}.__init__() raised {exc}; using print() instead of main app error loggers")
|
|
1165
|
+
|
|
1166
|
+
# self.cae = None
|
|
1167
|
+
# self.po = print
|
|
1168
|
+
# self.dpo = print
|
|
1169
|
+
# self.vpo = print
|
|
1170
|
+
|
|
1147
1171
|
@property
|
|
1148
1172
|
def error_message(self) -> str:
|
|
1149
1173
|
""" error message string if an error occurred or an empty string if not.
|
|
@@ -1157,14 +1181,19 @@ class ErrorMsgMixin:
|
|
|
1157
1181
|
@error_message.setter
|
|
1158
1182
|
def error_message(self, next_err_msg: str):
|
|
1159
1183
|
if next_err_msg:
|
|
1184
|
+
if "WARNING" in next_err_msg.upper():
|
|
1185
|
+
self.vpo(f" .::. {next_err_msg}")
|
|
1186
|
+
else:
|
|
1187
|
+
self.dpo(f" .::. {next_err_msg}")
|
|
1160
1188
|
self._err_msg += ("\n\n" if self._err_msg else "") + next_err_msg
|
|
1161
1189
|
else:
|
|
1162
1190
|
self._err_msg = ""
|
|
1163
1191
|
|
|
1164
1192
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1193
|
+
# package and permissions handling defaults for all other platforms and frameworks
|
|
1194
|
+
PACKAGE_NAME = stack_var('__name__') or 'unspecified_package' #: package name default
|
|
1195
|
+
PACKAGE_DOMAIN = 'org.test' #: package domain default
|
|
1196
|
+
PERMISSIONS = "INTERNET, VIBRATE, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE" #: permissions default
|
|
1168
1197
|
if os_path_isfile(BUILD_CONFIG_FILE): # pragma: no cover
|
|
1169
1198
|
PACKAGE_NAME, PACKAGE_DOMAIN, PERMISSIONS = build_config_variable_values(
|
|
1170
1199
|
('package.name', PACKAGE_NAME),
|
|
@@ -1180,8 +1209,8 @@ elif os_platform == 'android': # pragma: no cov
|
|
|
1180
1209
|
|
|
1181
1210
|
if os_platform == 'android': # pragma: no cover
|
|
1182
1211
|
# monkey patch the :func:`shutil.copystat` and :func:`shutil.copymode` helper functions, which are crashing on
|
|
1183
|
-
# 'android' (see # https://bugs.python.org/issue28141 and https://bugs.python.org/issue32073). these
|
|
1184
|
-
# used by shutil.copy2/copy/copytree/move to copy OS-specific file attributes.
|
|
1212
|
+
# 'android' (see # `<https://bugs.python.org/issue28141>`__ and `<https://bugs.python.org/issue32073>`__). these
|
|
1213
|
+
# functions are used by shutil.copy2/copy/copytree/move to copy OS-specific file attributes.
|
|
1185
1214
|
# although shutil.copytree() and shutil.move() are copying/moving the files correctly when the copy_function
|
|
1186
1215
|
# arg is set to :func:`shutil.copyfile`, they will finally also crash afterward when they try to set the attributes
|
|
1187
1216
|
# on the destination root directory.
|
|
@@ -1213,8 +1242,8 @@ if os_platform == 'android': # pragma: no cov
|
|
|
1213
1242
|
:param service_arg: string value to be assigned to environment variable PYTHON_SERVICE_ARGUMENT on start.
|
|
1214
1243
|
:return: service instance.
|
|
1215
1244
|
|
|
1216
|
-
see https://github.com/tshirtman/kivy_service_osc/blob/master/src/main.py
|
|
1217
|
-
and https://python-for-android.readthedocs.io/en/latest/services/#arbitrary-scripts-services
|
|
1245
|
+
see `<https://github.com/tshirtman/kivy_service_osc/blob/master/src/main.py>`__
|
|
1246
|
+
and `<https://python-for-android.readthedocs.io/en/latest/services/#arbitrary-scripts-services>`__
|
|
1218
1247
|
"""
|
|
1219
1248
|
service_instance = autoclass(f"{PACKAGE_DOMAIN}.{PACKAGE_NAME}.Service{PACKAGE_NAME.capitalize()}")
|
|
1220
1249
|
activity = autoclass('org.kivy.android.PythonActivity').mActivity
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ae_base
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.46
|
|
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
|
|
7
7
|
Author-email: aecker2@gmail.com
|
|
8
8
|
License: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
9
|
+
Project-URL: Bug Tracker, https://gitlab.com/ae-group/ae_base/-/issues
|
|
9
10
|
Project-URL: Documentation, https://ae.readthedocs.io/en/latest/_autosummary/ae.base.html
|
|
10
11
|
Project-URL: Repository, https://gitlab.com/ae-group/ae_base
|
|
11
12
|
Project-URL: Source, https://ae.readthedocs.io/en/latest/_modules/ae/base.html
|
|
@@ -53,19 +54,19 @@ Requires-Dist: types-setuptools; extra == "tests"
|
|
|
53
54
|
Requires-Dist: wheel; extra == "tests"
|
|
54
55
|
Requires-Dist: twine; extra == "tests"
|
|
55
56
|
|
|
56
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.
|
|
57
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae V0.3.95 -->
|
|
57
58
|
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.tpl_namespace_root V0.3.14 -->
|
|
58
|
-
# base 0.3.
|
|
59
|
+
# base 0.3.46
|
|
59
60
|
|
|
60
61
|
[](
|
|
61
62
|
https://gitlab.com/ae-group/ae_base)
|
|
62
63
|
[](
|
|
65
|
+
https://gitlab.com/ae-group/ae_base/-/tree/release0.3.45)
|
|
65
66
|
[](
|
|
66
67
|
https://pypi.org/project/ae-base/#history)
|
|
67
68
|
|
|
68
|
-
>ae_base module 0.3.
|
|
69
|
+
>ae_base module 0.3.46.
|
|
69
70
|
|
|
70
71
|
[](
|
|
71
72
|
https://ae-group.gitlab.io/ae_base/coverage/index.html)
|
|
@@ -3,6 +3,7 @@ import datetime
|
|
|
3
3
|
import os
|
|
4
4
|
import string
|
|
5
5
|
import tempfile
|
|
6
|
+
from unittest.mock import patch
|
|
6
7
|
|
|
7
8
|
import pytest
|
|
8
9
|
import shutil
|
|
@@ -71,7 +72,43 @@ def test_unset_null_length():
|
|
|
71
72
|
|
|
72
73
|
class TestErrorMsgMixin:
|
|
73
74
|
def test_instantiation(self):
|
|
74
|
-
|
|
75
|
+
ins = ErrorMsgMixin()
|
|
76
|
+
assert ins
|
|
77
|
+
assert ins.cae is None # in test env is no console/gui app available
|
|
78
|
+
assert ins.po is ins.dpo is ins.vpo is print
|
|
79
|
+
|
|
80
|
+
with patch('ae.core.main_app_instance', lambda : None):
|
|
81
|
+
ins = ErrorMsgMixin()
|
|
82
|
+
assert ins
|
|
83
|
+
assert ins.cae is None
|
|
84
|
+
assert ins.po is ins.dpo is ins.vpo is print
|
|
85
|
+
|
|
86
|
+
class _AppMock(ErrorMsgMixin):
|
|
87
|
+
cae = None
|
|
88
|
+
|
|
89
|
+
def po(self):
|
|
90
|
+
""" po() mock """
|
|
91
|
+
return "po"
|
|
92
|
+
|
|
93
|
+
def dpo(self):
|
|
94
|
+
""" dpo() mock """
|
|
95
|
+
return "dpo"
|
|
96
|
+
|
|
97
|
+
def vpo(self):
|
|
98
|
+
""" vpo() mock """
|
|
99
|
+
return "vpo"
|
|
100
|
+
|
|
101
|
+
app_ins = _AppMock()
|
|
102
|
+
|
|
103
|
+
with patch('ae.core.main_app_instance', lambda : app_ins):
|
|
104
|
+
ins = ErrorMsgMixin()
|
|
105
|
+
assert ins.cae is app_ins
|
|
106
|
+
assert ins.po is not print
|
|
107
|
+
assert ins.po() == "po"
|
|
108
|
+
assert ins.dpo is not print
|
|
109
|
+
assert ins.dpo() == "dpo"
|
|
110
|
+
assert ins.vpo is not print
|
|
111
|
+
assert ins.vpo() == "vpo"
|
|
75
112
|
|
|
76
113
|
def test_error_message_property(self):
|
|
77
114
|
ins = ErrorMsgMixin()
|
|
@@ -89,6 +126,14 @@ class TestErrorMsgMixin:
|
|
|
89
126
|
ins.error_message = ""
|
|
90
127
|
assert ins.error_message == ""
|
|
91
128
|
|
|
129
|
+
def test_error_message_property_for_warnings(self):
|
|
130
|
+
ins = ErrorMsgMixin()
|
|
131
|
+
|
|
132
|
+
err_msg = "error message with the word warning"
|
|
133
|
+
ins.error_message = err_msg
|
|
134
|
+
ins.error_message = "another message"
|
|
135
|
+
assert err_msg in ins.error_message
|
|
136
|
+
|
|
92
137
|
|
|
93
138
|
class TestBaseHelpers:
|
|
94
139
|
def test_app_name_guess(self):
|
|
@@ -542,12 +587,15 @@ class TestBaseHelpers:
|
|
|
542
587
|
print(os_user_name())
|
|
543
588
|
assert os_user_name()
|
|
544
589
|
|
|
545
|
-
def
|
|
590
|
+
def test_parse_dotenv_dollar_char_does_not_cutoff_value(self):
|
|
546
591
|
with tempfile.NamedTemporaryFile(mode="w") as fp:
|
|
547
|
-
fp.write('
|
|
592
|
+
fp.write('declaredVar = DeclaredValue\n')
|
|
593
|
+
fp.write('replacedVar = beforeTheDollar$declaredVar\n')
|
|
594
|
+
fp.write('uncutVar = beforeTheDollar$afterTheDollar\n')
|
|
548
595
|
fp.seek(0)
|
|
549
596
|
loaded = parse_dotenv(fp.name)
|
|
550
|
-
assert '
|
|
597
|
+
assert loaded['replacedVar'] == "beforeTheDollarDeclaredValue"
|
|
598
|
+
assert loaded['uncutVar'] == "beforeTheDollar$afterTheDollar"
|
|
551
599
|
|
|
552
600
|
def test_parse_dotenv_double_quoted_value(self):
|
|
553
601
|
with tempfile.NamedTemporaryFile(mode="w") as fp:
|
|
@@ -557,6 +605,13 @@ class TestBaseHelpers:
|
|
|
557
605
|
assert 'var_nam' in loaded
|
|
558
606
|
assert loaded['var_nam'] == "var val"
|
|
559
607
|
|
|
608
|
+
def test_parse_dotenv_error_space_prefixed_var_name(self):
|
|
609
|
+
with tempfile.NamedTemporaryFile(mode="w") as fp:
|
|
610
|
+
fp.write(' var_nam="var val"')
|
|
611
|
+
fp.seek(0)
|
|
612
|
+
loaded = parse_dotenv(fp.name)
|
|
613
|
+
assert 'var_nam' not in loaded # added warning
|
|
614
|
+
|
|
560
615
|
def test_parse_dotenv_single_value(self):
|
|
561
616
|
with tempfile.NamedTemporaryFile(mode="w") as fp:
|
|
562
617
|
fp.write("var_nam='var val'")
|
|
@@ -624,14 +679,14 @@ class TestBaseHelpers:
|
|
|
624
679
|
assert 'env_var' in loaded
|
|
625
680
|
assert loaded['env_var'] == "var val"
|
|
626
681
|
|
|
627
|
-
def
|
|
682
|
+
def test_parse_dotenv_var_expands_not_an_undefined_variable_to_empty_string(self):
|
|
628
683
|
with tempfile.NamedTemporaryFile(mode="w") as fp:
|
|
629
684
|
fp.write("var_nam=$env_var")
|
|
630
685
|
fp.seek(0)
|
|
631
686
|
loaded = parse_dotenv(fp.name)
|
|
632
687
|
assert 'env_var' not in loaded
|
|
633
688
|
assert 'var_nam' in loaded
|
|
634
|
-
assert loaded['var_nam'] == ""
|
|
689
|
+
assert loaded['var_nam'] == "$env_var"
|
|
635
690
|
|
|
636
691
|
def test_parse_dotenv_var_expands_in_double_quoted_values(self):
|
|
637
692
|
with tempfile.NamedTemporaryFile(mode="w") as fp:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|