ae-core 0.3.76__tar.gz → 0.3.78__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_core-0.3.76 → ae_core-0.3.78}/LICENSE.md +1 -1
- {ae_core-0.3.76/ae_core.egg-info → ae_core-0.3.78}/PKG-INFO +6 -6
- {ae_core-0.3.76 → ae_core-0.3.78}/README.md +5 -5
- {ae_core-0.3.76 → ae_core-0.3.78}/ae/core.py +75 -29
- {ae_core-0.3.76 → ae_core-0.3.78/ae_core.egg-info}/PKG-INFO +6 -6
- {ae_core-0.3.76 → ae_core-0.3.78}/pyproject.toml +1 -1
- {ae_core-0.3.76 → ae_core-0.3.78}/setup.py +7 -7
- {ae_core-0.3.76 → ae_core-0.3.78}/tests/test_core.py +182 -79
- {ae_core-0.3.76 → ae_core-0.3.78}/ae_core.egg-info/SOURCES.txt +0 -0
- {ae_core-0.3.76 → ae_core-0.3.78}/ae_core.egg-info/dependency_links.txt +0 -0
- {ae_core-0.3.76 → ae_core-0.3.78}/ae_core.egg-info/requires.txt +0 -0
- {ae_core-0.3.76 → ae_core-0.3.78}/ae_core.egg-info/top_level.txt +0 -0
- {ae_core-0.3.76 → ae_core-0.3.78}/ae_core.egg-info/zip-safe +0 -0
- {ae_core-0.3.76 → ae_core-0.3.78}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ae_core
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.78
|
|
4
4
|
Summary: ae namespace module portion core: application core constants, helper functions and base classes
|
|
5
5
|
Home-page: https://gitlab.com/ae-group/ae_core
|
|
6
6
|
Author: AndiEcker
|
|
@@ -67,15 +67,15 @@ Dynamic: requires-dist
|
|
|
67
67
|
Dynamic: requires-python
|
|
68
68
|
Dynamic: summary
|
|
69
69
|
|
|
70
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.
|
|
71
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.
|
|
72
|
-
# core 0.3.
|
|
70
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.101 -->
|
|
71
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.22 -->
|
|
72
|
+
# core 0.3.78
|
|
73
73
|
|
|
74
74
|
[](
|
|
75
75
|
https://gitlab.com/ae-group/ae_core)
|
|
76
76
|
[](
|
|
78
|
+
https://gitlab.com/ae-group/ae_core/-/tree/release0.3.78)
|
|
79
79
|
[](
|
|
80
80
|
https://pypi.org/project/ae-core/#history)
|
|
81
81
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.
|
|
2
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.
|
|
3
|
-
# core 0.3.
|
|
1
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.101 -->
|
|
2
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.22 -->
|
|
3
|
+
# core 0.3.78
|
|
4
4
|
|
|
5
5
|
[](
|
|
6
6
|
https://gitlab.com/ae-group/ae_core)
|
|
7
7
|
[](
|
|
9
|
+
https://gitlab.com/ae-group/ae_core/-/tree/release0.3.78)
|
|
10
10
|
[](
|
|
11
11
|
https://pypi.org/project/ae-core/#history)
|
|
12
12
|
|
|
@@ -218,6 +218,7 @@ application code.
|
|
|
218
218
|
.. _debug-level-constants:
|
|
219
219
|
|
|
220
220
|
"""
|
|
221
|
+
# pylint: disable=too-many-lines
|
|
221
222
|
import datetime
|
|
222
223
|
import faulthandler
|
|
223
224
|
import logging
|
|
@@ -230,7 +231,7 @@ import traceback
|
|
|
230
231
|
import weakref
|
|
231
232
|
|
|
232
233
|
from io import StringIO
|
|
233
|
-
from typing import Any, Callable,
|
|
234
|
+
from typing import Any, Callable, Optional, TextIO, Union, cast
|
|
234
235
|
|
|
235
236
|
from ae.base import ( # type: ignore
|
|
236
237
|
BUILD_CONFIG_FILE, DATE_TIME_ISO, DEF_ENCODE_ERRORS, PY_EXT, PY_INIT, PY_MAIN,
|
|
@@ -242,7 +243,7 @@ from ae.paths import (
|
|
|
242
243
|
from ae.updater import check_all # type: ignore
|
|
243
244
|
|
|
244
245
|
|
|
245
|
-
__version__ = '0.3.
|
|
246
|
+
__version__ = '0.3.78'
|
|
246
247
|
|
|
247
248
|
|
|
248
249
|
# package and permissions handling defaults for all platforms and frameworks
|
|
@@ -268,7 +269,7 @@ if os_platform == 'android': # pragma: no cover
|
|
|
268
269
|
from android.permissions import request_permissions, Permission # type: ignore # pylint: disable=import-error
|
|
269
270
|
from jnius import autoclass # type: ignore
|
|
270
271
|
|
|
271
|
-
def request_app_permissions(callback: Optional[Callable[[
|
|
272
|
+
def request_app_permissions(callback: Optional[Callable[[list[Permission], list[bool]], None]] = None):
|
|
272
273
|
""" request app/service permissions on Android OS.
|
|
273
274
|
|
|
274
275
|
:param callback: optional callback receiving two list arguments with identical length,
|
|
@@ -321,11 +322,13 @@ DEBUG_LEVEL_DISABLED: int = 0 #: lowest debug level - only display logging
|
|
|
321
322
|
DEBUG_LEVEL_ENABLED: int = 1 #: minimum debugging info - display logging levels WARNING or higher.
|
|
322
323
|
DEBUG_LEVEL_VERBOSE: int = 2 #: verbose debug info - display logging levels INFO/DEBUG or higher.
|
|
323
324
|
|
|
324
|
-
DEBUG_LEVELS:
|
|
325
|
+
DEBUG_LEVELS: dict[int, str] = {DEBUG_LEVEL_DISABLED: 'disabled',
|
|
326
|
+
DEBUG_LEVEL_ENABLED: 'enabled',
|
|
325
327
|
DEBUG_LEVEL_VERBOSE: 'verbose'}
|
|
326
328
|
""" numeric ids and names of all supported debug levels. """
|
|
327
329
|
|
|
328
|
-
LOGGING_LEVELS:
|
|
330
|
+
LOGGING_LEVELS: dict[int, int] = {DEBUG_LEVEL_DISABLED: logging.WARNING,
|
|
331
|
+
DEBUG_LEVEL_ENABLED: logging.INFO,
|
|
329
332
|
DEBUG_LEVEL_VERBOSE: logging.DEBUG}
|
|
330
333
|
""" association between ae debug levels and python logging levels. """
|
|
331
334
|
|
|
@@ -367,6 +370,13 @@ def logger_late_init():
|
|
|
367
370
|
_LOGGER = logging.getLogger(__name__) # reset (_LOGGER = None) done in _unregister_app_instance()
|
|
368
371
|
|
|
369
372
|
|
|
373
|
+
def logger_shutdown():
|
|
374
|
+
""" reset logger and logging module. """
|
|
375
|
+
global _LOGGER # pylint: disable=global-statement
|
|
376
|
+
_LOGGER = None
|
|
377
|
+
logging.shutdown()
|
|
378
|
+
|
|
379
|
+
|
|
370
380
|
_MULTI_THREADING_ACTIVATED: bool = False #: flag if threading is used in your application
|
|
371
381
|
|
|
372
382
|
|
|
@@ -382,6 +392,7 @@ def _deactivate_multi_threading():
|
|
|
382
392
|
_MULTI_THREADING_ACTIVATED = False
|
|
383
393
|
|
|
384
394
|
|
|
395
|
+
# pylint: disable=too-many-arguments,too-many-branches,too-many-locals,too-many-statements
|
|
385
396
|
def print_out(*objects, sep: str = " ", end: str = "\n", file: Optional[TextIO] = None, flush: bool = False,
|
|
386
397
|
encode_errors_def: str = DEF_ENCODE_ERRORS, logger: Optional['logging.Logger'] = None,
|
|
387
398
|
app: Optional['AppBase'] = None, **kwargs):
|
|
@@ -427,6 +438,7 @@ def print_out(*objects, sep: str = " ", end: str = "\n", file: Optional[TextIO]
|
|
|
427
438
|
else:
|
|
428
439
|
app = main_app
|
|
429
440
|
|
|
441
|
+
# pylint: disable=too-many-boolean-expressions
|
|
430
442
|
if processing:
|
|
431
443
|
file = ori_std_out
|
|
432
444
|
elif logger is not None and file is None and (
|
|
@@ -477,6 +489,42 @@ def print_out(*objects, sep: str = " ", end: str = "\n", file: Optional[TextIO]
|
|
|
477
489
|
break
|
|
478
490
|
|
|
479
491
|
|
|
492
|
+
def debug_out(*objects, **kwargs):
|
|
493
|
+
""" print out if debug mode is enabled. if app instance is available, then use :meth:`AppBase.debug_out` instead.
|
|
494
|
+
|
|
495
|
+
:param objects: see argument description of :meth:`AppBase.debug_out`.
|
|
496
|
+
:param kwargs: see argument description of :meth:`AppBase.debug_out`.
|
|
497
|
+
"""
|
|
498
|
+
getattr(main_app_instance(), 'debug_out', print_out)(*objects, **kwargs)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def verbose_out(*objects, **kwargs):
|
|
502
|
+
""" print out if verbose debug mode is enabled. if app instance is available, then use :meth:`AppBase.verbose_out`.
|
|
503
|
+
|
|
504
|
+
:param objects: see argument description of :meth:`AppBase.verbose_out`.
|
|
505
|
+
:param kwargs: see argument description of :meth:`AppBase.verbose_out`.
|
|
506
|
+
"""
|
|
507
|
+
getattr(main_app_instance(), 'verbose_out', print_out)(*objects, **kwargs)
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
def is_debug() -> bool:
|
|
511
|
+
""" determine if the debug level of the main app instance is set/enabled.
|
|
512
|
+
|
|
513
|
+
:return: True if the debugging of the main app instance is enabled or if the app main instance
|
|
514
|
+
did not get registered, else False (main app instance exists, but no debugging enabled).
|
|
515
|
+
"""
|
|
516
|
+
return getattr(main_app_instance(), 'debug', True)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def is_verbose() -> bool:
|
|
520
|
+
""" determine if the verbose debug level of the main app instance is set/enabled.
|
|
521
|
+
|
|
522
|
+
:return: True if the debugging of the main app instance is enabled or if the app main instance
|
|
523
|
+
did not get registered, else False (main app exists, but no verbose debugging enabled).
|
|
524
|
+
"""
|
|
525
|
+
return getattr(main_app_instance(), 'verbose', True)
|
|
526
|
+
|
|
527
|
+
|
|
480
528
|
APP_KEY_SEP: str = '@' #: separator character used in :attr:`~AppBase.app_key` of :class:`AppBase` instance
|
|
481
529
|
|
|
482
530
|
# had to use type comment because the following line is throwing an error in the Sphinx docs make:
|
|
@@ -505,7 +553,7 @@ def main_app_instance() -> Optional['AppBase']:
|
|
|
505
553
|
return _APP_INSTANCES.get(_MAIN_APP_INST_KEY)
|
|
506
554
|
|
|
507
555
|
|
|
508
|
-
def registered_app_names() ->
|
|
556
|
+
def registered_app_names() -> list[str]:
|
|
509
557
|
""" determine the app names of all registered/running applications. """
|
|
510
558
|
with app_inst_lock:
|
|
511
559
|
return [app.app_name for app in _APP_INSTANCES.values()]
|
|
@@ -541,8 +589,7 @@ def _unregister_app_instance(app_key: str) -> Optional['AppBase']:
|
|
|
541
589
|
:return: removed :class:`AppBase` instance.
|
|
542
590
|
"""
|
|
543
591
|
with app_inst_lock:
|
|
544
|
-
|
|
545
|
-
_LOGGER = None
|
|
592
|
+
logger_shutdown()
|
|
546
593
|
|
|
547
594
|
global _MAIN_APP_INST_KEY # pylint: disable=global-statement
|
|
548
595
|
app = _APP_INSTANCES.pop(app_key, None)
|
|
@@ -563,7 +610,7 @@ def _shut_down_sub_app_instances(timeout: Optional[float] = None):
|
|
|
563
610
|
acquisition of the threading locks of :data:`the ae log file <log_file_lock>` and the
|
|
564
611
|
:data:`app instances <app_inst_lock>`.
|
|
565
612
|
"""
|
|
566
|
-
aqc_kwargs:
|
|
613
|
+
aqc_kwargs: dict[str, Any] = ({'blocking': False} if timeout is None else {'timeout': timeout})
|
|
567
614
|
blocked = app_inst_lock.acquire(**aqc_kwargs) # pylint: disable=consider-using-with
|
|
568
615
|
for app in reversed(list(_APP_INSTANCES.values())): # list() because the weak ref dict gets changed in the loop
|
|
569
616
|
if not app.is_main:
|
|
@@ -591,7 +638,7 @@ class _PrintingReplicator:
|
|
|
591
638
|
:param any_str: string or bytes to output.
|
|
592
639
|
"""
|
|
593
640
|
message = any_str.decode() if isinstance(any_str, bytes) else any_str
|
|
594
|
-
app_streams:
|
|
641
|
+
app_streams: list[tuple[Optional[AppBase], TextIO]] = []
|
|
595
642
|
with log_file_lock, app_inst_lock:
|
|
596
643
|
for app in list(_APP_INSTANCES.values()):
|
|
597
644
|
stream = app.log_file_check(app.active_log_stream) # check if log rotation or buf-to-file-switch needed
|
|
@@ -649,7 +696,7 @@ def _join_app_threads(timeout: Optional[float] = None):
|
|
|
649
696
|
_deactivate_multi_threading()
|
|
650
697
|
|
|
651
698
|
|
|
652
|
-
class AppBase:
|
|
699
|
+
class AppBase: # pylint: disable=too-many-instance-attributes
|
|
653
700
|
""" provides easy logging and debugging for your application.
|
|
654
701
|
|
|
655
702
|
most applications only need a single instance of this class; apps with threads could create separate instances
|
|
@@ -693,9 +740,10 @@ class AppBase:
|
|
|
693
740
|
_log_file_name: str = "" #: log file name
|
|
694
741
|
_log_with_timestamp: Union[bool, str] = False #: True of strftime format string to enable timestamp
|
|
695
742
|
_nul_std_out: Optional[TextIO] = None #: logging null stream
|
|
696
|
-
py_log_params:
|
|
743
|
+
py_log_params: dict[str, Any] = {} #: dict of config parameters for py logging
|
|
697
744
|
_shut_down: bool = False #: True if this app instance got shut down already
|
|
698
745
|
|
|
746
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
699
747
|
def __init__(self, app_title: str = '', app_name: str = '', app_version: str = '', sys_env_id: str = '',
|
|
700
748
|
debug_level: int = DEBUG_LEVEL_VERBOSE, multi_threading: bool = False, suppress_stdout: bool = False):
|
|
701
749
|
""" initialize a new :class:`AppBase` instance.
|
|
@@ -749,7 +797,7 @@ class AppBase:
|
|
|
749
797
|
else: # pragma: no cover
|
|
750
798
|
self.vpo(f"AppBase.__init__() upgrade check skipped because {app_path=} != {cwd_path=}")
|
|
751
799
|
|
|
752
|
-
def _init_path_placeholders(self):
|
|
800
|
+
def _init_path_placeholders(self): # pylint: disable=too-many-locals
|
|
753
801
|
""" correct app_name/main_app_name, the related path placeholders and ensure write access for some of them. """
|
|
754
802
|
# correct app name guess, init by :mod:`ae.paths` (main app from ("", 'pyTstConsAppKey', '_jb_pytest_runner'))
|
|
755
803
|
PATH_PLACEHOLDERS['main_app_name'] = PATH_PLACEHOLDERS['app_name'] = app_name = self.app_name
|
|
@@ -881,7 +929,8 @@ class AppBase:
|
|
|
881
929
|
|
|
882
930
|
return None
|
|
883
931
|
|
|
884
|
-
|
|
932
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
933
|
+
def init_logging(self, py_logging_params: Optional[dict[str, Any]] = None, log_file_name: str = "",
|
|
885
934
|
log_file_size_max: float = LOG_FILE_MAX_SIZE, log_with_timestamp: Union[bool, str] = False,
|
|
886
935
|
disable_buffering: bool = False):
|
|
887
936
|
""" initialize the logging system.
|
|
@@ -1000,7 +1049,7 @@ class AppBase:
|
|
|
1000
1049
|
:param kwargs: all the other supported kwargs of this method are documented
|
|
1001
1050
|
:func:`at the print_out() function of this module <print_out>`.
|
|
1002
1051
|
|
|
1003
|
-
this method has an alias named :meth:`.po`
|
|
1052
|
+
.. hint:: this method has an alias named :meth:`.po`
|
|
1004
1053
|
"""
|
|
1005
1054
|
if file is None and main_app_instance() is not self: # self.is_main==True when main_app_instance() is None
|
|
1006
1055
|
with log_file_lock:
|
|
@@ -1013,23 +1062,16 @@ class AppBase:
|
|
|
1013
1062
|
|
|
1014
1063
|
po = print_out #: alias of method :meth:`.print_out`
|
|
1015
1064
|
|
|
1016
|
-
def debug_out(self, *objects,
|
|
1017
|
-
"""
|
|
1018
|
-
|
|
1019
|
-
this method will print out the passed objects only if the :attr:`current debug level
|
|
1020
|
-
<.core.AppBase.debug_level>` of this app instance is higher than the value passed into the
|
|
1021
|
-
:paramref:`~debug_out.minimum_debug_level` argument. in this case the print-out will be
|
|
1022
|
-
delegated onto the :meth:`~.print_out`.
|
|
1065
|
+
def debug_out(self, *objects, **kwargs):
|
|
1066
|
+
""" print objects if :attr:`the current debug level <.core.AppBase.debug_level>`of this app instance is enabled.
|
|
1023
1067
|
|
|
1024
1068
|
:param objects: objects to be printed out.
|
|
1025
|
-
:param minimum_debug_level: minimum debug level to print the passed objects.
|
|
1026
1069
|
:param kwargs: all the supported kwargs of this method are documented at the
|
|
1027
|
-
:func:`print_out() function <core.print_out
|
|
1028
|
-
(including the :paramref:`~.print_out.file` argument).
|
|
1070
|
+
:func:`print_out() function <core.print_out>`.
|
|
1029
1071
|
|
|
1030
|
-
this method has an alias named :meth:`.dpo`.
|
|
1072
|
+
.. hint:: this method has an alias named :meth:`.dpo`.
|
|
1031
1073
|
"""
|
|
1032
|
-
if self.debug_level >=
|
|
1074
|
+
if self.debug_level >= DEBUG_LEVEL_ENABLED:
|
|
1033
1075
|
self.po(*objects, **kwargs)
|
|
1034
1076
|
|
|
1035
1077
|
dpo = debug_out #: alias of method :meth:`.debug_out`
|
|
@@ -1043,7 +1085,7 @@ class AppBase:
|
|
|
1043
1085
|
supported kwargs of this method are documented at the
|
|
1044
1086
|
:func:`print_out() function <~ae.core.print_out>` of the :mod:`~.core` module.
|
|
1045
1087
|
|
|
1046
|
-
this method has an alias named :meth:`.vpo`.
|
|
1088
|
+
.. hint:: this method has an alias named :meth:`.vpo`.
|
|
1047
1089
|
"""
|
|
1048
1090
|
if self.debug_level >= DEBUG_LEVEL_VERBOSE:
|
|
1049
1091
|
self.po(*objects, **kwargs)
|
|
@@ -1061,11 +1103,13 @@ class AppBase:
|
|
|
1061
1103
|
"""
|
|
1062
1104
|
if self._shut_down:
|
|
1063
1105
|
return
|
|
1064
|
-
aqc_kwargs:
|
|
1106
|
+
aqc_kwargs: dict[str, Any] = {'blocking': False} if timeout is None else {'timeout': timeout}
|
|
1065
1107
|
is_main_app_instance = main_app_instance() is self # self.is_main==True when main_app_instance() is None
|
|
1066
1108
|
force = is_main_app_instance and exit_code # prevent deadlock on app error exit/shutdown
|
|
1067
1109
|
|
|
1068
1110
|
if exit_code is not None:
|
|
1111
|
+
if not 0 <= exit_code <= 255:
|
|
1112
|
+
self.po(f" ### extended exit code {exit_code}! most shells only get 8 bits(0..255)=={exit_code % 256}")
|
|
1069
1113
|
self.po(f"##### {'forced ' if force else ''}shutdown of {self.app_name} with {exit_code=}", logger=_LOGGER)
|
|
1070
1114
|
|
|
1071
1115
|
# pylint: disable-next=consider-using-with
|
|
@@ -1119,7 +1163,9 @@ class AppBase:
|
|
|
1119
1163
|
else:
|
|
1120
1164
|
# pylint: disable-next=unspecified-encoding, consider-using-with
|
|
1121
1165
|
std_out = self._nul_std_out = open(os.devnull, 'w')
|
|
1166
|
+
# noinspection PyInvalidCast
|
|
1122
1167
|
sys.stdout = cast(TextIO, _PrintingReplicator(sys_out_obj=std_out))
|
|
1168
|
+
# noinspection PyInvalidCast
|
|
1123
1169
|
sys.stderr = cast(TextIO, _PrintingReplicator(sys_out_obj=ori_std_err))
|
|
1124
1170
|
else:
|
|
1125
1171
|
if is_main_app_instance:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ae_core
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.78
|
|
4
4
|
Summary: ae namespace module portion core: application core constants, helper functions and base classes
|
|
5
5
|
Home-page: https://gitlab.com/ae-group/ae_core
|
|
6
6
|
Author: AndiEcker
|
|
@@ -67,15 +67,15 @@ Dynamic: requires-dist
|
|
|
67
67
|
Dynamic: requires-python
|
|
68
68
|
Dynamic: summary
|
|
69
69
|
|
|
70
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.
|
|
71
|
-
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.
|
|
72
|
-
# core 0.3.
|
|
70
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.101 -->
|
|
71
|
+
<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.22 -->
|
|
72
|
+
# core 0.3.78
|
|
73
73
|
|
|
74
74
|
[](
|
|
75
75
|
https://gitlab.com/ae-group/ae_core)
|
|
76
76
|
[](
|
|
78
|
+
https://gitlab.com/ae-group/ae_core/-/tree/release0.3.78)
|
|
79
79
|
[](
|
|
80
80
|
https://pypi.org/project/ae-core/#history)
|
|
81
81
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.project_tpls v0.3.
|
|
1
|
+
# THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.project_tpls v0.3.59
|
|
2
2
|
""" setup of ae namespace module portion core: application core constants, helper functions and base classes. """
|
|
3
3
|
# noinspection PyUnresolvedReferences
|
|
4
4
|
import sys
|
|
@@ -23,15 +23,15 @@ setup_kwargs = {
|
|
|
23
23
|
'install_requires': ['ae_base', 'ae_paths', 'ae_updater'],
|
|
24
24
|
'keywords': ['configuration', 'development', 'environment', 'productivity'],
|
|
25
25
|
'license': 'GPL-3.0-or-later',
|
|
26
|
-
'long_description': ('<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.
|
|
27
|
-
'<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.
|
|
28
|
-
'# core 0.3.
|
|
26
|
+
'long_description': ('<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.101 -->\n'
|
|
27
|
+
'<!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.22 -->\n'
|
|
28
|
+
'# core 0.3.78\n'
|
|
29
29
|
'\n'
|
|
30
30
|
'[](\n'
|
|
31
31
|
' https://gitlab.com/ae-group/ae_core)\n'
|
|
32
32
|
'[](\n'
|
|
34
|
+
' https://gitlab.com/ae-group/ae_core/-/tree/release0.3.78)\n'
|
|
35
35
|
'[](\n'
|
|
36
36
|
' https://pypi.org/project/ae-core/#history)\n'
|
|
37
37
|
'\n'
|
|
@@ -108,7 +108,7 @@ setup_kwargs = {
|
|
|
108
108
|
'Source': 'https://ae.readthedocs.io/en/latest/_modules/ae/core.html'},
|
|
109
109
|
'python_requires': '>=3.9',
|
|
110
110
|
'url': 'https://gitlab.com/ae-group/ae_core',
|
|
111
|
-
'version': '0.3.
|
|
111
|
+
'version': '0.3.78',
|
|
112
112
|
'zip_safe': True,
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -12,23 +12,53 @@ from typing import cast, Any, TextIO
|
|
|
12
12
|
from unittest.mock import patch
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
import ae.paths # for patch tests of ae.paths.PATH_PLACEHOLDERS
|
|
16
|
+
import ae.core # for patch tests of ae.core.PATH_PLACEHOLDERS
|
|
17
|
+
|
|
18
|
+
from ae.base import (
|
|
19
|
+
DATE_TIME_ISO, force_encoding, norm_path, os_path_dirname, os_path_isfile, os_path_splitext, read_file, write_file)
|
|
16
20
|
from ae.paths import PATH_PLACEHOLDERS, placeholder_path, coll_folders, Collector
|
|
17
21
|
# noinspection PyProtectedMember
|
|
18
22
|
from ae.core import (
|
|
19
23
|
APP_KEY_SEP, DEBUG_LEVELS, DEBUG_LEVEL_DISABLED, DEBUG_LEVEL_ENABLED, DEBUG_LEVEL_VERBOSE,
|
|
20
24
|
LOG_FILE_IDX_WIDTH, MAX_NUM_LOG_FILES,
|
|
21
25
|
activate_multi_threading, _deactivate_multi_threading, hide_dup_line_prefix, main_app_instance, print_out,
|
|
26
|
+
debug_out, verbose_out, is_debug, is_verbose,
|
|
22
27
|
registered_app_names,
|
|
23
28
|
AppBase, _PrintingReplicator)
|
|
24
|
-
import ae.paths # for patch tests of ae.paths.PATH_PLACEHOLDERS
|
|
25
|
-
import ae.core # for patch tests of ae.core.PATH_PLACEHOLDERS
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
__version__ = '3.6.9dev-test' # used for automatic app version find tests
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
class TestCoreHelpers:
|
|
35
|
+
def test_debug_out(self, capsys, restore_app_env):
|
|
36
|
+
app = AppBase()
|
|
37
|
+
app.debug_level = DEBUG_LEVEL_VERBOSE
|
|
38
|
+
|
|
39
|
+
debug_out('tst console output')
|
|
40
|
+
|
|
41
|
+
out, err = capsys.readouterr()
|
|
42
|
+
assert 'tst console output' in out
|
|
43
|
+
assert err == ""
|
|
44
|
+
|
|
45
|
+
app.debug_level = DEBUG_LEVEL_ENABLED
|
|
46
|
+
|
|
47
|
+
debug_out('tst console output')
|
|
48
|
+
|
|
49
|
+
out, err = capsys.readouterr()
|
|
50
|
+
assert 'tst console output' in out
|
|
51
|
+
assert err == ""
|
|
52
|
+
|
|
53
|
+
app.debug_level = DEBUG_LEVEL_DISABLED
|
|
54
|
+
|
|
55
|
+
debug_out('tst console output')
|
|
56
|
+
|
|
57
|
+
out, err = capsys.readouterr()
|
|
58
|
+
assert 'tst console output' not in out
|
|
59
|
+
assert out == ""
|
|
60
|
+
assert err == ""
|
|
61
|
+
|
|
32
62
|
def test_hide_dup_line_prefix(self):
|
|
33
63
|
l1 = "<t_s_t>"
|
|
34
64
|
l2 = l1
|
|
@@ -39,6 +69,34 @@ class TestCoreHelpers:
|
|
|
39
69
|
l2 = l1[:3] + l1
|
|
40
70
|
assert hide_dup_line_prefix(l1, l2) == " " * 3 + l1
|
|
41
71
|
|
|
72
|
+
def test_is_debug(self, restore_app_env):
|
|
73
|
+
app = AppBase()
|
|
74
|
+
app.debug_level = DEBUG_LEVEL_DISABLED
|
|
75
|
+
|
|
76
|
+
assert not is_debug()
|
|
77
|
+
|
|
78
|
+
app.debug_level = DEBUG_LEVEL_ENABLED
|
|
79
|
+
|
|
80
|
+
assert is_debug()
|
|
81
|
+
|
|
82
|
+
app.debug_level = DEBUG_LEVEL_VERBOSE
|
|
83
|
+
|
|
84
|
+
assert is_debug()
|
|
85
|
+
|
|
86
|
+
def test_is_verbose(self, restore_app_env):
|
|
87
|
+
app = AppBase()
|
|
88
|
+
app.debug_level = DEBUG_LEVEL_DISABLED
|
|
89
|
+
|
|
90
|
+
assert not is_verbose()
|
|
91
|
+
|
|
92
|
+
app.debug_level = DEBUG_LEVEL_ENABLED
|
|
93
|
+
|
|
94
|
+
assert not is_verbose()
|
|
95
|
+
|
|
96
|
+
app.debug_level = DEBUG_LEVEL_VERBOSE
|
|
97
|
+
|
|
98
|
+
assert is_verbose()
|
|
99
|
+
|
|
42
100
|
def test_print_out_basics(self, capsys):
|
|
43
101
|
print_out()
|
|
44
102
|
out, err = capsys.readouterr()
|
|
@@ -85,6 +143,34 @@ class TestCoreHelpers:
|
|
|
85
143
|
assert len(registered_app_names()) == 1
|
|
86
144
|
assert app.app_name == registered_app_names()[0]
|
|
87
145
|
|
|
146
|
+
def test_verbose_out(self, capsys, restore_app_env):
|
|
147
|
+
app = AppBase()
|
|
148
|
+
app.debug_level = DEBUG_LEVEL_VERBOSE
|
|
149
|
+
|
|
150
|
+
verbose_out('tst console output')
|
|
151
|
+
|
|
152
|
+
out, err = capsys.readouterr()
|
|
153
|
+
assert 'tst console output' in out
|
|
154
|
+
assert err == ""
|
|
155
|
+
|
|
156
|
+
app.debug_level = DEBUG_LEVEL_ENABLED
|
|
157
|
+
|
|
158
|
+
verbose_out('tst console output')
|
|
159
|
+
|
|
160
|
+
out, err = capsys.readouterr()
|
|
161
|
+
assert 'tst console output' not in out
|
|
162
|
+
assert out == ""
|
|
163
|
+
assert err == ""
|
|
164
|
+
|
|
165
|
+
app.debug_level = DEBUG_LEVEL_DISABLED
|
|
166
|
+
|
|
167
|
+
verbose_out('tst console output')
|
|
168
|
+
|
|
169
|
+
out, err = capsys.readouterr()
|
|
170
|
+
assert 'tst console output' not in out
|
|
171
|
+
assert out == ""
|
|
172
|
+
assert err == ""
|
|
173
|
+
|
|
88
174
|
|
|
89
175
|
class TestPrintingReplicator:
|
|
90
176
|
def test_init(self):
|
|
@@ -149,7 +235,7 @@ class TestAeLogging:
|
|
|
149
235
|
for idx in range(MAX_NUM_LOG_FILES + 9):
|
|
150
236
|
for line_no in range(16): # full loop is creating 1 kB of log entries (16 * 64 bytes)
|
|
151
237
|
app.po("TestBaseLogEntry{: >26}{: >26}".format(idx, line_no))
|
|
152
|
-
assert
|
|
238
|
+
assert os_path_isfile(log_file)
|
|
153
239
|
finally:
|
|
154
240
|
assert delete_files(log_file, keep_ext=True) == MAX_NUM_LOG_FILES + 1
|
|
155
241
|
|
|
@@ -157,7 +243,7 @@ class TestAeLogging:
|
|
|
157
243
|
log_file = 'test_ae_cov_log.log'
|
|
158
244
|
valid_log_content = "TestBaseLogEntry"
|
|
159
245
|
invalid_log_content = "NeverAppearInLogFile"
|
|
160
|
-
fb, ext =
|
|
246
|
+
fb, ext = os_path_splitext(log_file) # simulate a left-over log file from the last app run - coverage
|
|
161
247
|
idx = 1
|
|
162
248
|
write_file(f"{fb}-{idx:0>{LOG_FILE_IDX_WIDTH}}{ext}",
|
|
163
249
|
f"log file content to test left-over from last app run{invalid_log_content}")
|
|
@@ -167,7 +253,7 @@ class TestAeLogging:
|
|
|
167
253
|
for idx in range(MAX_NUM_LOG_FILES + 9):
|
|
168
254
|
for line_no in range(16): # full loop is creating 1 kB of log entries (16 * 64 bytes)
|
|
169
255
|
app.po(f"{valid_log_content}{idx: >26}{line_no: >26}")
|
|
170
|
-
assert
|
|
256
|
+
assert os_path_isfile(log_file)
|
|
171
257
|
finally:
|
|
172
258
|
contents = delete_files(log_file, keep_ext=True, ret_type='contents')
|
|
173
259
|
assert len(contents) == MAX_NUM_LOG_FILES + 1
|
|
@@ -187,7 +273,7 @@ class TestAeLogging:
|
|
|
187
273
|
for idx in range(MAX_NUM_LOG_FILES + 9):
|
|
188
274
|
for line_no in range(16):
|
|
189
275
|
app.po("TestBaseLogEntry{: >26}{: >26}".format(idx, line_no))
|
|
190
|
-
assert
|
|
276
|
+
assert os_path_isfile(log_file)
|
|
191
277
|
finally:
|
|
192
278
|
assert delete_files(log_file, keep_ext=True) == MAX_NUM_LOG_FILES + 1
|
|
193
279
|
|
|
@@ -201,7 +287,7 @@ class TestAeLogging:
|
|
|
201
287
|
for idx in range(MAX_NUM_LOG_FILES + 9):
|
|
202
288
|
for line_no in range(16):
|
|
203
289
|
app.po("TestBaseLogEntry{: >26}{: >26}".format(idx, line_no))
|
|
204
|
-
assert
|
|
290
|
+
assert os_path_isfile(log_file)
|
|
205
291
|
finally:
|
|
206
292
|
assert delete_files(log_file, keep_ext=True) == MAX_NUM_LOG_FILES + 1
|
|
207
293
|
|
|
@@ -216,7 +302,7 @@ class TestAeLogging:
|
|
|
216
302
|
out, err = capsys.readouterr()
|
|
217
303
|
assert out == "" and err == ""
|
|
218
304
|
app.init_logging() # close the log file
|
|
219
|
-
assert
|
|
305
|
+
assert os_path_isfile(log_file)
|
|
220
306
|
out, err = capsys.readouterr()
|
|
221
307
|
assert out == "" and err == ""
|
|
222
308
|
finally:
|
|
@@ -237,7 +323,7 @@ class TestAeLogging:
|
|
|
237
323
|
out, err = capsys.readouterr()
|
|
238
324
|
assert out == "" and err == ""
|
|
239
325
|
app.init_logging() # close the log file
|
|
240
|
-
assert
|
|
326
|
+
assert os_path_isfile(log_file)
|
|
241
327
|
out, err = capsys.readouterr()
|
|
242
328
|
assert out == "" and err == ""
|
|
243
329
|
finally:
|
|
@@ -251,7 +337,7 @@ class TestAeLogging:
|
|
|
251
337
|
app.init_logging(log_file_name=log_file)
|
|
252
338
|
with pytest.raises(FileNotFoundError):
|
|
253
339
|
app.log_file_check() # coverage of callee exception
|
|
254
|
-
assert not
|
|
340
|
+
assert not os_path_isfile(log_file)
|
|
255
341
|
|
|
256
342
|
def test_log_file_flush(self, restore_app_env):
|
|
257
343
|
log_file = 'test_ae_base_log_flush.log'
|
|
@@ -259,7 +345,7 @@ class TestAeLogging:
|
|
|
259
345
|
app = AppBase('test_base_log_file_flush')
|
|
260
346
|
app.init_logging(log_file_name=log_file)
|
|
261
347
|
app.log_file_check()
|
|
262
|
-
assert
|
|
348
|
+
assert os_path_isfile(log_file)
|
|
263
349
|
finally:
|
|
264
350
|
assert delete_files(log_file) == 1
|
|
265
351
|
|
|
@@ -281,8 +367,8 @@ class TestAeLogging:
|
|
|
281
367
|
# NOT WORKING: capsys.readouterr() returning empty strings
|
|
282
368
|
# out, err = capsys.readouterr()
|
|
283
369
|
# assert out.count(tst_out) == 3 and err == ""
|
|
284
|
-
assert
|
|
285
|
-
assert
|
|
370
|
+
assert os_path_isfile(mp + log_file)
|
|
371
|
+
assert os_path_isfile(sp + log_file)
|
|
286
372
|
finally:
|
|
287
373
|
contents = delete_files(sp + log_file, ret_type='contents')
|
|
288
374
|
assert len(contents)
|
|
@@ -323,8 +409,8 @@ class TestAeLogging:
|
|
|
323
409
|
sub.init_logging() # close the sub-app log file created by sub_thread
|
|
324
410
|
sub_thread.join()
|
|
325
411
|
app.init_logging() # close the main-app log file
|
|
326
|
-
assert
|
|
327
|
-
assert
|
|
412
|
+
assert os_path_isfile(sp + log_file)
|
|
413
|
+
assert os_path_isfile(mp + log_file)
|
|
328
414
|
finally:
|
|
329
415
|
contents = delete_files(sp + log_file, ret_type='contents')
|
|
330
416
|
assert len(contents)
|
|
@@ -340,6 +426,7 @@ class TestAeLogging:
|
|
|
340
426
|
def test_exception_log_file_flush(self, restore_app_env):
|
|
341
427
|
app = AppBase('test_exception_base_log_file_flush')
|
|
342
428
|
# cause/provoke _append_eof_and_flush_file() exceptions for coverage by passing any other non-stream object
|
|
429
|
+
# noinspection PyInvalidCast
|
|
343
430
|
app._append_eof_and_flush_file(cast(TextIO, None), 'invalid stream')
|
|
344
431
|
|
|
345
432
|
def test_app_instances_reset2(self):
|
|
@@ -350,46 +437,40 @@ class TestPythonLogging:
|
|
|
350
437
|
""" test python logging module support
|
|
351
438
|
"""
|
|
352
439
|
def test_log_init(self, restore_app_env):
|
|
353
|
-
var_val =
|
|
354
|
-
|
|
440
|
+
var_val = {'version': 1,
|
|
441
|
+
'disable_existing_loggers': False}
|
|
355
442
|
app = AppBase('log_init')
|
|
356
443
|
app.init_logging(py_logging_params=var_val)
|
|
357
444
|
|
|
358
445
|
assert app.py_log_params == var_val
|
|
359
446
|
|
|
360
|
-
logging.shutdown()
|
|
361
|
-
|
|
362
447
|
def test_app_instances_reset1(self):
|
|
363
448
|
assert main_app_instance() is None
|
|
364
449
|
|
|
365
450
|
def test_logging_params_dict_console_from_init(self, restore_app_env):
|
|
366
|
-
var_val =
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
451
|
+
var_val = {'version': 1,
|
|
452
|
+
'disable_existing_loggers': False,
|
|
453
|
+
'handlers': {'console': {'class': 'logging.StreamHandler',
|
|
454
|
+
'level': logging.INFO}}}
|
|
370
455
|
print(str(var_val))
|
|
371
456
|
|
|
372
457
|
main_app = AppBase('test_python_logging_params_dict_console')
|
|
373
458
|
main_app.init_logging(py_logging_params=var_val)
|
|
374
459
|
|
|
375
460
|
assert main_app.py_log_params == var_val
|
|
376
|
-
logging.shutdown()
|
|
377
461
|
|
|
378
462
|
def test_logging_params_dict_console_from_init_with_log_file(self, restore_app_env):
|
|
379
463
|
log_file = 'tst_py_log_complex.log'
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
'ae': dict(handlers=['console']),
|
|
391
|
-
'ae.console': dict(handlers=['console'])}
|
|
392
|
-
)
|
|
464
|
+
var_val = {'version': 1,
|
|
465
|
+
'disable_existing_loggers': False,
|
|
466
|
+
'handlers': {'console': {'class': 'logging.handlers.RotatingFileHandler',
|
|
467
|
+
'level': logging.INFO,
|
|
468
|
+
'filename': log_file,
|
|
469
|
+
'maxBytes': 33,
|
|
470
|
+
'backupCount': 63}},
|
|
471
|
+
'loggers': {'root': {'handlers': ['console']},
|
|
472
|
+
'ae': {'handlers': ['console']},
|
|
473
|
+
'ae.console': {'handlers': ['console']}}}
|
|
393
474
|
print(str(var_val))
|
|
394
475
|
|
|
395
476
|
cae = AppBase('test_python_logging_params_dict_file', debug_level=DEBUG_LEVEL_DISABLED)
|
|
@@ -397,42 +478,24 @@ class TestPythonLogging:
|
|
|
397
478
|
|
|
398
479
|
assert cae.py_log_params == var_val
|
|
399
480
|
|
|
400
|
-
# logging.shutdown() TODO: encapsulate with _LOGGER = None into new method reset_logging() and add it to restore_app_env fixture ?!?!?
|
|
401
|
-
|
|
402
481
|
# empty log file created by logging.config.dictConfig(py_logging_params) in :func:`ae.core.AppBase.init_logging`
|
|
403
482
|
assert delete_files(log_file, ret_type='contents')[0] == ""
|
|
404
483
|
|
|
405
484
|
def test_logging_params_dict_complex(self, restore_app_env):
|
|
406
|
-
""" test logging with rotating file handler
|
|
407
|
-
|
|
408
|
-
TODO: investigate and fix the 4 commented out asserts in this test method
|
|
409
|
-
.. which found log_text via `pjm check` pytest in console as well as in pycharm pytest run
|
|
410
|
-
.. but in this test module (test_core.py) only the pjm/pytest run does not find log_text/log_files at all!
|
|
411
|
-
.. or shows them accumulated in a later test
|
|
412
|
-
Strange: a very similar test method did run fine with pjm&pycharm in ae_console/tests/test_console.py (v0.3.63)
|
|
413
|
-
|
|
414
|
-
Looks like the problem lies in pytest (previous version done with caplog had also empty .text)
|
|
415
|
-
.. but also in PyCharm, see:
|
|
416
|
-
|
|
417
|
-
* https://stackoverflow.com/questions/59875983
|
|
418
|
-
* https://github.com/pytest-dev/pytest/issues/3697
|
|
419
|
-
* https://youtrack.jetbrains.com/issue/PY-48743/Running-Pytest-is-not-showing-logging-output
|
|
420
|
-
|
|
421
|
-
"""
|
|
485
|
+
""" test logging with rotating file handler. """
|
|
422
486
|
log_file = 'test_py_log_complex.log'
|
|
423
487
|
entry_prefix = "TEST LOG ENTRY "
|
|
424
488
|
|
|
425
|
-
var_val =
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
)
|
|
489
|
+
var_val = {'version': 1,
|
|
490
|
+
'disable_existing_loggers': False,
|
|
491
|
+
'handlers': {'console': {'class': 'logging.handlers.RotatingFileHandler',
|
|
492
|
+
'level': logging.INFO,
|
|
493
|
+
'filename': log_file,
|
|
494
|
+
'maxBytes': 33,
|
|
495
|
+
'backupCount': 63}},
|
|
496
|
+
'loggers': {'root': {'handlers': ['console']},
|
|
497
|
+
'ae': {'handlers': ['console']},
|
|
498
|
+
'ae.core': {'handlers': ['console']}}}
|
|
436
499
|
print(str(var_val))
|
|
437
500
|
|
|
438
501
|
cae = AppBase('test_python_logging_params_dict_file', debug_level=DEBUG_LEVEL_DISABLED)
|
|
@@ -503,6 +566,7 @@ class TestPythonLogging:
|
|
|
503
566
|
logging.shutdown()
|
|
504
567
|
# pjm: assert delete_files(log_file) == 0
|
|
505
568
|
# pycharm: assert delete_files(log_file, ret_type='contents')[0].endswith(log_text + os.linesep)
|
|
569
|
+
assert delete_files(log_file, ret_type='contents')[0].endswith(log_text + os.linesep)
|
|
506
570
|
|
|
507
571
|
try:
|
|
508
572
|
log_text = entry_prefix + "4 error logging"
|
|
@@ -511,6 +575,7 @@ class TestPythonLogging:
|
|
|
511
575
|
logging.shutdown()
|
|
512
576
|
# pjm: assert delete_files(log_file) == 0
|
|
513
577
|
# pycharm: assert delete_files(log_file, ret_type='contents')[0].endswith(log_text + os.linesep)
|
|
578
|
+
assert delete_files(log_file, ret_type='contents')[0].endswith(log_text + os.linesep)
|
|
514
579
|
|
|
515
580
|
# loggers
|
|
516
581
|
try:
|
|
@@ -520,6 +585,7 @@ class TestPythonLogging:
|
|
|
520
585
|
logging.shutdown()
|
|
521
586
|
# pjm: assert delete_files(log_file) == 0
|
|
522
587
|
# pycharm: assert delete_files(log_file, ret_type='contents')[0].endswith(log_text + os.linesep)
|
|
588
|
+
assert delete_files(log_file, ret_type='contents')[0].endswith(log_text + os.linesep)
|
|
523
589
|
|
|
524
590
|
try:
|
|
525
591
|
log_text = entry_prefix + "4 error ae"
|
|
@@ -540,15 +606,16 @@ class TestPythonLogging:
|
|
|
540
606
|
# AppBase.debug_out/.dpo
|
|
541
607
|
sys.argv = ['tl_cdc'] # sys.argv has to be set to allow get_option('debug_level') calls done by debug_out()
|
|
542
608
|
try:
|
|
543
|
-
log_text = entry_prefix + "5 not logged dpo"
|
|
544
|
-
cae.dpo(log_text
|
|
609
|
+
log_text = entry_prefix + "5 not logged debug_out/dpo"
|
|
610
|
+
cae.dpo(log_text)
|
|
545
611
|
finally:
|
|
546
612
|
logging.shutdown()
|
|
547
613
|
assert delete_files(log_file) == 0
|
|
548
614
|
|
|
615
|
+
# AppBase.print_out/.po
|
|
549
616
|
try:
|
|
550
|
-
log_text = entry_prefix + "5
|
|
551
|
-
cae.
|
|
617
|
+
log_text = entry_prefix + "5 print_out/po"
|
|
618
|
+
cae.po(log_text, logger=ae_cae_logger)
|
|
552
619
|
finally:
|
|
553
620
|
logging.shutdown()
|
|
554
621
|
assert delete_files(log_file, ret_type='contents')[0].endswith(log_text + os.linesep)
|
|
@@ -579,7 +646,7 @@ class TestAppBase: # only some basic tests - test coverage is done by :clas
|
|
|
579
646
|
app = AppBase(title, app_version=ver)
|
|
580
647
|
assert app.app_title == title
|
|
581
648
|
assert app.app_version == ver
|
|
582
|
-
assert app.app_path == norm_path(
|
|
649
|
+
assert app.app_path == norm_path(os_path_dirname(sys.argv[0]))
|
|
583
650
|
|
|
584
651
|
def test_app_find_version(self, restore_app_env):
|
|
585
652
|
app = AppBase()
|
|
@@ -604,7 +671,7 @@ class TestAppBase: # only some basic tests - test coverage is done by :clas
|
|
|
604
671
|
|
|
605
672
|
_instance = _OtherClass()
|
|
606
673
|
t_args = (1, 2, '3')
|
|
607
|
-
t_kwargs =
|
|
674
|
+
t_kwargs = {'a': 1, 'b': 2, 'c': '3'}
|
|
608
675
|
assert app.call_method(_instance._method, *t_args, **t_kwargs) == (_instance, t_args, t_kwargs)
|
|
609
676
|
|
|
610
677
|
def test_call_method_no_exception_if_not_exists(self, restore_app_env):
|
|
@@ -648,6 +715,12 @@ class TestAppBase: # only some basic tests - test coverage is done by :clas
|
|
|
648
715
|
setattr(app, 'test_raiser', _raising_ex)
|
|
649
716
|
app.call_method('test_raiser')
|
|
650
717
|
|
|
718
|
+
def test_debug_out(self, capsys, restore_app_env):
|
|
719
|
+
app = AppBase(debug_level=DEBUG_LEVEL_ENABLED)
|
|
720
|
+
tst = "tsT-debug-out-string"
|
|
721
|
+
app.debug_out(tst)
|
|
722
|
+
assert tst in capsys.readouterr()[0]
|
|
723
|
+
|
|
651
724
|
def test_init_path_placeholders(self, restore_app_env):
|
|
652
725
|
cae = AppBase("test_init_path_placeholders")
|
|
653
726
|
|
|
@@ -781,16 +854,46 @@ class TestAppBase: # only some basic tests - test coverage is done by :clas
|
|
|
781
854
|
def test_app_instances_reset2(self):
|
|
782
855
|
assert main_app_instance() is None
|
|
783
856
|
|
|
857
|
+
def test_shutdown_none(self, capsys, restore_app_env):
|
|
858
|
+
app = AppBase(debug_level=DEBUG_LEVEL_DISABLED)
|
|
859
|
+
|
|
860
|
+
with patch('ae.core.sys.exit', lambda *args, **kwargs: None):
|
|
861
|
+
app.shutdown(None)
|
|
862
|
+
|
|
863
|
+
out, err = capsys.readouterr()
|
|
864
|
+
assert out == ""
|
|
865
|
+
assert err == ""
|
|
866
|
+
|
|
867
|
+
def test_shutdown_0(self, capsys, restore_app_env):
|
|
868
|
+
app = AppBase(debug_level=DEBUG_LEVEL_ENABLED)
|
|
869
|
+
|
|
870
|
+
with patch('ae.core.sys.exit', lambda *args, **kwargs: None):
|
|
871
|
+
app.shutdown() # exit_code == 0
|
|
872
|
+
|
|
873
|
+
out, err = capsys.readouterr()
|
|
874
|
+
assert 'shutdown of ' in out
|
|
875
|
+
assert err == ""
|
|
876
|
+
|
|
877
|
+
def test_shutdown_1(self, capsys, restore_app_env):
|
|
878
|
+
app = AppBase(debug_level=DEBUG_LEVEL_VERBOSE)
|
|
879
|
+
|
|
880
|
+
with patch('ae.core.sys.exit', lambda *args, **kwargs: None):
|
|
881
|
+
app.shutdown(1)
|
|
882
|
+
|
|
883
|
+
assert 'shutdown of ' in capsys.readouterr()[0]
|
|
884
|
+
|
|
885
|
+
def test_shutdown_123456(self, capsys, restore_app_env):
|
|
886
|
+
app = AppBase()
|
|
887
|
+
|
|
888
|
+
with patch('ae.core.sys.exit', lambda *args, **kwargs: None):
|
|
889
|
+
app.shutdown(123456) # test warning if exit code is not in 0..255
|
|
890
|
+
|
|
891
|
+
assert 'extended exit code' in capsys.readouterr()[0]
|
|
892
|
+
|
|
784
893
|
def test_verbose(self, capsys, restore_app_env):
|
|
785
894
|
app = AppBase(debug_level=DEBUG_LEVEL_VERBOSE)
|
|
786
895
|
assert app.verbose
|
|
787
896
|
|
|
788
|
-
def test_debug_out(self, capsys, restore_app_env):
|
|
789
|
-
app = AppBase(debug_level=DEBUG_LEVEL_ENABLED)
|
|
790
|
-
tst = "tsT-debug-out-string"
|
|
791
|
-
app.debug_out(tst)
|
|
792
|
-
assert tst in capsys.readouterr()[0]
|
|
793
|
-
|
|
794
897
|
def test_verbose_out(self, capsys, restore_app_env):
|
|
795
898
|
app = AppBase(debug_level=DEBUG_LEVEL_ENABLED)
|
|
796
899
|
tst = "tsT-debug-miss-string"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|