ae-shell 0.3.15__tar.gz → 0.3.17__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.project_tpls v0.3.79 -->
1
+ <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.project_tpls v0.3.102 -->
2
2
  ### GNU GENERAL PUBLIC LICENSE
3
3
 
4
4
  Version 3, 29 June 2007
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ae_shell
3
- Version: 0.3.15
3
+ Version: 0.3.17
4
4
  Summary: ae namespace module portion shell: shell execution and environment helpers
5
5
  Home-page: https://gitlab.com/ae-group/ae_shell
6
6
  Author: AndiEcker
@@ -62,15 +62,15 @@ Dynamic: requires-dist
62
62
  Dynamic: requires-python
63
63
  Dynamic: summary
64
64
 
65
- <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.104 -->
66
- <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.31 -->
67
- # shell 0.3.15
65
+ <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.110 -->
66
+ <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.33 -->
67
+ # shell 0.3.17
68
68
 
69
69
  [![GitLab develop](https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/develop?logo=python)](
70
70
  https://gitlab.com/ae-group/ae_shell)
71
71
  [![LatestPyPIrelease](
72
- https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/release0.3.15?logo=python)](
73
- https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.15)
72
+ https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/release0.3.17?logo=python)](
73
+ https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.17)
74
74
  [![PyPIVersions](https://img.shields.io/pypi/v/ae_shell)](
75
75
  https://pypi.org/project/ae-shell/#history)
76
76
 
@@ -1,12 +1,12 @@
1
- <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.104 -->
2
- <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.31 -->
3
- # shell 0.3.15
1
+ <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.110 -->
2
+ <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.33 -->
3
+ # shell 0.3.17
4
4
 
5
5
  [![GitLab develop](https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/develop?logo=python)](
6
6
  https://gitlab.com/ae-group/ae_shell)
7
7
  [![LatestPyPIrelease](
8
- https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/release0.3.15?logo=python)](
9
- https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.15)
8
+ https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/release0.3.17?logo=python)](
9
+ https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.17)
10
10
  [![PyPIVersions](https://img.shields.io/pypi/v/ae_shell)](
11
11
  https://pypi.org/project/ae-shell/#history)
12
12
 
@@ -0,0 +1,323 @@
1
+ """
2
+ shell execution and environment helpers
3
+ =======================================
4
+
5
+ this module is designed to provide a comprehensive set of constants and helper functions
6
+ to manage shell printouts, OS environment variables and to execute shell commands.
7
+
8
+ * :func:`debug_or_verbose`: checks if the application is running in debug or verbose mode.
9
+ * :func:`get_domain_user_var`: retrieves an OS environment variable value for a specific domain and/or user.
10
+ * :func:`hint`: provides a hint message for console printouts based on the provided arguments.
11
+ * :func:`in_os_env`: context manager to temporarily add environment variables from the ``.env`` files onto `os.environ`.
12
+ * :func:`mask_token`: hide/mask tokens in a text block, to prevent to show them in logs and printouts.
13
+ * :func:`output_line_split`: decode and split the specified shell/console output streams into line chunks.
14
+ * :func:`output_zero_split`: decode and split the specified shell/console output streams separated by `NUL` (\\0) chars.
15
+ * :func:`sh_exec`: execute command in the current working directory of the OS console/shell.
16
+ * :func:`sh_exit_if_exec_err`: extended version of :func:`sh_exec` with automatically checks for errors
17
+ after a command is executed and handles application shutdown/termination gracefully.
18
+
19
+ * :data:`STDERR_BEG_MARKER`: marker used in the console/shell printouts for the beginning of merged-in stderr output.
20
+ * :data:`STDERR_END_MARKER`: marker used in the console/shell printouts for the end of merged-in stderr output.
21
+ """
22
+ import os
23
+ import shlex
24
+ import subprocess
25
+
26
+ from collections.abc import Callable, Iterable, Iterator, MutableMapping
27
+ from contextlib import contextmanager
28
+ from typing import Any, cast, overload
29
+
30
+ from ae.base import UNSET, UnsetType, dummy_function, env_str, norm_name # type: ignore
31
+ from ae.system import load_env_var_defaults, os_env_venv # type: ignore
32
+ from ae.core import main_app_instance, AppBase # type: ignore
33
+ from ae.console import MAIN_SECTION_NAME, ConsoleApp # type: ignore
34
+
35
+
36
+ __version__ = '0.3.17'
37
+
38
+
39
+ STDERR_BEG_MARKER = 'vvv STDERR vvv' #: :paramref:`ae.shell.sh_exec.output_lines` begin stderr lines marker
40
+ STDERR_END_MARKER = '^^^ STDERR ^^^' #: end stderr lines marker in :paramref:`ae.shell.sh_exec.output_lines`
41
+
42
+
43
+ def debug_or_verbose(app_obj: ConsoleApp | UnsetType | None = None) -> bool:
44
+ """ determine if the current app runs in debug|verbose mode, while preventing early .get_option() call an app init.
45
+
46
+ :param app_obj: optional ConsoleApp instance (def=main_app_instance()).
47
+ :return: a boolean False when the main app debug level is :data:`~ae.core.DEBUG_LEVEL_DISABLED`
48
+ and the app option --more_verbose is not specified (in cfg-file or at the command line),
49
+ else True.
50
+
51
+ .. note:: the return value on app startup/initialization, before the command line parsing, is always True.
52
+
53
+ .. hint::
54
+ the debug mode can be activated via the :class:`~ae.console.ConsoleApp` option `debug_level`, specified either
55
+ in a config file or via the command line options. the verbose mode get activated via the `more_verbose` option.
56
+ """
57
+ if app_obj is None:
58
+ app_obj = main_app_instance()
59
+
60
+ return bool(
61
+ not isinstance(app_obj, AppBase) # prevent exception in early app startup and unit test runs
62
+ or app_obj.debug # main_app.debug_level > DEBUG_LEVEL_DISABLED
63
+ or not isinstance(app_obj, ConsoleApp) # return True for pure ae.core.AppBase instances
64
+ or not getattr(app_obj, '_parsed_arguments', None) # ConsoleApp._parsed_arguments args Namespace not created
65
+ or app_obj.get_option('more_verbose')) # optional ConsoleApp instance verbose option
66
+
67
+
68
+ def get_domain_user_var(variable_name: str, domain: str = "", user: str = "") -> Any:
69
+ """ determine the value of an OS environment variable for a specific domain and/or username.
70
+
71
+ :param variable_name: name of the config variable.
72
+ :param domain: name of the domain.
73
+ :param user: name/id of the user to get a user-specific value of.
74
+ :return: domain/user-specific value of the specified config variable.
75
+ """
76
+ parts = (MAIN_SECTION_NAME, variable_name.lower(), f'AT_{norm_name(domain)}'.lower(), norm_name(user).lower())
77
+ value = None
78
+ if domain:
79
+ if user:
80
+ value = env_str('_'.join(parts), convert_name=True)
81
+ if value is None:
82
+ value = env_str('_'.join(parts[:-1]), convert_name=True)
83
+ elif user:
84
+ value = env_str('_'.join(parts[:2] + parts[-1:]), convert_name=True)
85
+
86
+ if value is None:
87
+ value = env_str('_'.join(parts[:2]), convert_name=True)
88
+
89
+ return value
90
+
91
+
92
+ def hint(command: str, action: Callable | str, message_suffix: str = "") -> str:
93
+ """ return hint string in debug/verbose mode, to be appended onto a shell/console output.
94
+
95
+ :param command: shell command.
96
+ :param action: shell command action function/method.
97
+ :param message_suffix: extra message text, added to the end of the returned console output string.
98
+ :return: in debug/verbose mode return a string with leading line feed to be sent
99
+ to console output, else return an empty string.
100
+ """
101
+ if not isinstance(action, str):
102
+ action = action.__name__
103
+ return f"{os.linesep} (run: {command} {action}{message_suffix})" if debug_or_verbose() else ""
104
+
105
+
106
+ @contextmanager
107
+ def in_os_env(start_dir: str = "") -> Iterator[MutableMapping[str, str]]:
108
+ """ temporarily add environment variables from the ``.env`` files that not exist in :attr:`os.environ` to it.
109
+
110
+ :param start_dir: path to the folder where the first dotenv file (with the highest priority) is stored.
111
+ :return: yielding the OS env variables that got added to os.environ in this temporary context.
112
+ """
113
+ loaded_env_vars = load_env_var_defaults(start_dir, os.environ)
114
+ try:
115
+ yield loaded_env_vars
116
+ finally:
117
+ for var_name in loaded_env_vars:
118
+ os.environ.pop(var_name)
119
+
120
+
121
+ @overload
122
+ def mask_token(text: str) -> str: ...
123
+
124
+
125
+ @overload
126
+ def mask_token(text: list[str]) -> list[str]: ...
127
+
128
+
129
+ @overload
130
+ def mask_token(text: str | list[str]) -> str | list[str]: ...
131
+
132
+
133
+ def mask_token(text: str | list[str]) -> str | list[str]:
134
+ """ hide most parts of any Codeberg/GitHub/GitHub URL tokens found in the specified text/-lines.
135
+
136
+ :param text: text, specified either as str object or as a list of str objects (lines),
137
+ each str/line get searched for URL tokens, to hide/mask the most part of them.
138
+ :return: text with masked URL tokens (only leaving the first/last 3 token characters unmasked).
139
+
140
+ .. note:: see also :func:`ae.base.mask_url` of a more generic way to hide passwords and tokens in URLs.
141
+ """
142
+ if is_str_arg := isinstance(text, str):
143
+ lines = [text]
144
+ else:
145
+ lines = list(text) # copy to not change text list content
146
+
147
+ url_beg = 'https://' # PDV_REPO_HOST_PROTOCOL
148
+ url_beg_len = len(url_beg)
149
+ for tok_beg, tok_end in ((':', '@codeberg.org'), ('glpat-', '@gitlab.com'), ('ghp_', '@github.com')):
150
+ for idx, line in enumerate(lines):
151
+ beg = -1
152
+ while ((beg := line.find(url_beg, beg + 1)) != -1 and
153
+ (beg := line.find(tok_beg, beg + url_beg_len)) != -1 and
154
+ (end := line.find(tok_end, beg)) != -1):
155
+ line = line[:beg + 3] + "***-masked-token-***" + line[end - 3:]
156
+ lines[idx] = line
157
+
158
+ return lines[0] if is_str_arg else lines
159
+
160
+
161
+ def output_line_split(output: bytes) -> list[str]:
162
+ """ decode and split the specified shell/console output streams into line chunks.
163
+
164
+ :param output: captured stdout/stderr output from an executed OS shell command.
165
+ :return: list of non-empty shell/console output lines, decoded into string.
166
+ """
167
+ return [line for line in output.decode().splitlines() if line.strip()]
168
+
169
+
170
+ def output_zero_split(output: bytes) -> list[str]:
171
+ """ decode and split the specified shell/console output streams separated by `NUL` (\\0) characters.
172
+
173
+ :param output: captured stdout/stderr output from an executed OS shell command (e.g. `env -0`).
174
+ :return: list of non-empty shell/console output chunks, decoded into string.
175
+ """
176
+ return [line for line in output.decode().split('\0') if line.strip()]
177
+
178
+
179
+ # pylint: disable-next=too-many-arguments,too-many-positional-arguments
180
+ def sh_exec(command_line: str, extra_args: Iterable[str] = (), console_input: str = "",
181
+ output_lines: list[str] | None = None, app_obj: AppBase | UnsetType | None = None, shell: bool = False,
182
+ env_vars: dict[str, str] | UnsetType | None = UNSET, err_redirect: int | None = None,
183
+ decoder_splitter: Callable[[bytes], list[str]] = output_line_split) -> int:
184
+ """ execute command in the current working directory of the OS console/shell.
185
+
186
+ :param command_line: command line string to execute on the console/shell. could contain command line args
187
+ separated by whitespace characters (alternatively use :paramref:`.extra_args`).
188
+ :param extra_args: optional iterable with extra command line arguments.
189
+ :param console_input: optional string to be sent to the stdin stream of the console/shell.
190
+ :param output_lines: specify a list to be extended with the lines printed on the console/shell stdout stream,
191
+ and to also hide this output on the console. if and how the stderr stream get also
192
+ hidden/redirected to this list can be controlled via the value passed in the argument
193
+ :paramref:`.err_redirect`.
194
+ :param app_obj: optional :class:`~ae.core.AppBase`/:class:`~ae.console.ConsoleApp` instance, used for
195
+ logging. if not specified or None and if :func:`~ae.core.main_app_instance()` returns
196
+ None then the Python :func:`print` function is used.
197
+ specify :data:`~ae.base.UNSET` to suppress any printing/logging output.
198
+ :param shell: pass True to execute command in the default OS shell (for more info check the
199
+ documentation of the parameter :paramref:`~subprocess.run.shell` of the
200
+ :func:`subprocess.run` function).
201
+ :param env_vars: OS shell environment variables to be used instead of the console/bash defaults.
202
+ if not specified or `UNSET` then an isolated dict copy of :attr:`os.environ` will get
203
+ passed onto :func:`subprocess.run` in order to avoid potential runtime errors, if the
204
+ parent process (or a concurrent thread) modifies :attr:`os.environ` during the creation
205
+ of the subprocess. Additionally, with the convertion of the special :class:`_Environ`
206
+ object into a standard dict, the execution will result slightly faster and avoids
207
+ any special method overrides interfering with the child process creation. it also is
208
+ preventing rare errors with multithread-processes that try to change OS env variable
209
+ (e.g. Conda in relation with pip could lead to raise a `RuntimeError: dictionary
210
+ changed size during iteration` exception).
211
+ specify `None` in order to use the original/unisolated :attr:`os.environ` object.
212
+ :param err_redirect: this argument controls if and how the output of the executed command onto the console
213
+ stderr stream gets captured/redirected. it gets passed directly onto the
214
+ :paramref:`~subprocess.run.stderr` argument of :func:`subprocess.run` function.
215
+ if the argument of :paramref:`.output_lines` is a list, and you specified the
216
+ argument value :data:`subprocess.PIPE`, then the stderr output will get added at the
217
+ end of this list (enclosed between the list items :data:`STDERR_BEG_MARKER` and
218
+ :data:`STDERR_END_MARKER`). if the argument of :paramref:`.output_lines` is a
219
+ list, and you specified the argument value :data:`subprocess.STDOUT` then the stderr
220
+ output will get merged without any markers into this list in the order they get printed.
221
+ specify :data:`subprocess.DEVNULL` to hide any stderr output onto the console/shell
222
+ as well as in the :paramref:`.output_lines` list. if you specify `None`
223
+ (the default argument) then the stderr output will be printed only on the console.
224
+ :param decoder_splitter: callable to decode and split the output from the stdout/stderr streams into a list
225
+ of string chunks/lines, to be added to and returned by :paramref:`.output_lines`.
226
+ :return: return code of the executed command or 126 if execution raised any other exception.
227
+ """
228
+ if shell:
229
+ all_args: str | list[str] = command_line + (" " + " ".join(extra_args) if extra_args else "")
230
+ else:
231
+ all_args = shlex.split(command_line) + list(extra_args)
232
+ masked_args = mask_token(all_args)
233
+ if app_obj is None:
234
+ app_obj = main_app_instance()
235
+ print_out = dummy_function if app_obj is UNSET else app_obj.print_out if isinstance(app_obj, AppBase) else print
236
+ debug_out = dummy_function if app_obj is UNSET else app_obj.debug_out if isinstance(app_obj, AppBase) else print
237
+ if env_vars is UNSET:
238
+ env_vars = os.environ.copy()
239
+
240
+ debug_out(f" . executing {masked_args} at {os.getcwd()=} in {os_env_venv()=}")
241
+ result: subprocess.CompletedProcess | subprocess.CalledProcessError # having: stdout/stderr/returncode
242
+ try:
243
+ result = subprocess.run(all_args,
244
+ stdout=subprocess.PIPE if isinstance(output_lines, list) else None,
245
+ stderr=err_redirect,
246
+ input=console_input.encode(),
247
+ check=True,
248
+ shell=shell,
249
+ env=env_vars)
250
+ except subprocess.CalledProcessError as exc:
251
+ debug_out(f"**** subprocess.run({masked_args}) returned non-zero exit code {exc.returncode}; {exc=}")
252
+ result = exc
253
+ except Exception as exc: # pylint: disable=broad-except
254
+ print_out(f"**** subprocess.run({masked_args}) raised exception {exc}")
255
+ return (126, )[0] # put return/exit code into tuple for global code search
256
+
257
+ if isinstance(output_lines, list):
258
+ if result.stdout:
259
+ output_lines.extend(decoder_splitter(result.stdout))
260
+ if err_redirect == subprocess.PIPE and result.stderr:
261
+ output_lines.append(STDERR_BEG_MARKER)
262
+ output_lines.extend(decoder_splitter(result.stderr))
263
+ output_lines.append(STDERR_END_MARKER)
264
+
265
+ return result.returncode
266
+
267
+
268
+ # pylint: disable-next=too-many-arguments,too-many-positional-arguments
269
+ def sh_exit_if_exec_err(err_code: int, command_line: str,
270
+ extra_args: Iterable[str] = (), output_lines: list[str] | None = None, exit_on_err: bool = True,
271
+ exit_msg: str = "", app_obj: ConsoleApp | UnsetType | None = None, shell: bool = False,
272
+ env_vars: dict[str, str] | UnsetType | None = UNSET,
273
+ err_redirect: int | None = subprocess.DEVNULL,
274
+ decoder_splitter: Callable[[bytes], list[str]] = output_line_split) -> int:
275
+ """ execute command in the current working directory, optionally capturing console output and exit app on error.
276
+
277
+ :param err_code: error code to pass to the console as exit code if the command set an error code and
278
+ value of the :paramref:`.exit_on_err` argument is `True`.
279
+ :param command_line: command line string to execute. this argument could contain additional command line
280
+ arguments, separated by whitespace characters. alternatively use the argument
281
+ :paramref:`.extra_args` which allows to pass command line argument values,
282
+ with containing space characters.
283
+ :param extra_args: optional iterable of extra command line arguments.
284
+ :param output_lines: optional list extended with the lines printed to stdout/stderr on execution.
285
+ :param exit_on_err: pass False to not exit the app on error.
286
+ :param exit_msg: additional text to print on stdout/console if the app debug level is greater or equal
287
+ to 1 (:data:`~ae.core.DEBUG_LEVEL_ENABLED`) or if an error occurred.
288
+ :param app_obj: :class:`~ae.console.ConsoleApp` instance, used for logging/force-ignorable error.
289
+ :param shell: pass True to execute command in the default OS shell (see :func:`sh_exec`).
290
+ :param env_vars: OS shell environment variables to be used instead of the console/bash defaults.
291
+ :param err_redirect: control how the output on stderr gets captured, redirected and returned. see also
292
+ :paramref:`sh_exec.err_redirect` for more details to the supported argument values.
293
+ if this argument is not specified or has the value :data:`subprocess.DEVNULL` then the
294
+ stderr output will get suppressed on the console and will also not get added to the
295
+ list argument in :paramref:`.output_lines`.
296
+ :param decoder_splitter: callable to decode and split the output from the stdout/stderr streams into a list
297
+ of string chunks/lines, to be added to and returned by :paramref:`.output_lines`.
298
+ :return: 0 on success, or if an error occurred the error number set by the executed command.
299
+ """
300
+ if output_lines is None:
301
+ output_lines = []
302
+ output_len = 0
303
+ else:
304
+ output_len = len(output_lines)
305
+ if app_obj is None:
306
+ app_obj = cast(ConsoleApp, main_app_instance()) # calls app_obj./ConsoleApp.chk() method
307
+
308
+ sh_err = sh_exec(command_line, extra_args=extra_args, output_lines=output_lines, app_obj=app_obj, shell=shell,
309
+ env_vars=env_vars, err_redirect=err_redirect, decoder_splitter=decoder_splitter)
310
+
311
+ if isinstance(app_obj, ConsoleApp) and (app_obj.debug or sh_err and exit_on_err):
312
+ for line in output_lines[output_len:]:
313
+ if app_obj.verbose or not line.startswith("LOG: "): # if verbose show mypy's endless (stderr) log entries
314
+ app_obj.po(" " * 6 + line)
315
+ command = mask_token(f"{command_line} " + " ".join('"' + _a + '"' if " " in _a else _a for _a in extra_args))
316
+ if sh_err == 0:
317
+ app_obj.dpo(f" = successfully executed {command=}")
318
+ else:
319
+ if exit_msg:
320
+ app_obj.po(f" {exit_msg}")
321
+ app_obj.chk(err_code, not exit_on_err, f"sh_exit_if_exec_err({err_code}, {command!r}) error {sh_err}")
322
+
323
+ return sh_err
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ae_shell
3
- Version: 0.3.15
3
+ Version: 0.3.17
4
4
  Summary: ae namespace module portion shell: shell execution and environment helpers
5
5
  Home-page: https://gitlab.com/ae-group/ae_shell
6
6
  Author: AndiEcker
@@ -62,15 +62,15 @@ Dynamic: requires-dist
62
62
  Dynamic: requires-python
63
63
  Dynamic: summary
64
64
 
65
- <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.104 -->
66
- <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.31 -->
67
- # shell 0.3.15
65
+ <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project ae.ae v0.3.110 -->
66
+ <!-- THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.namespace_root_tpls v0.3.33 -->
67
+ # shell 0.3.17
68
68
 
69
69
  [![GitLab develop](https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/develop?logo=python)](
70
70
  https://gitlab.com/ae-group/ae_shell)
71
71
  [![LatestPyPIrelease](
72
- https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/release0.3.15?logo=python)](
73
- https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.15)
72
+ https://img.shields.io/gitlab/pipeline/ae-group/ae_shell/release0.3.17?logo=python)](
73
+ https://gitlab.com/ae-group/ae_shell/-/tree/release0.3.17)
74
74
  [![PyPIVersions](https://img.shields.io/pypi/v/ae_shell)](
75
75
  https://pypi.org/project/ae-shell/#history)
76
76
 
@@ -1,4 +1,7 @@
1
- # THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.project_tpls v0.3.79
1
+ # THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.project_tpls v0.3.102
2
2
  [build-system]
3
3
  requires = ["setuptools>=42", "wheel"]
4
4
  build-backend = "setuptools.build_meta"
5
+
6
+ [tool.coverage.run]
7
+ omit = ["**/*.kv"] # ignore Kivy's .kv files in pytest unit tests with coverage
@@ -1,15 +1,14 @@
1
- # THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.project_tpls v0.3.79
1
+ # THIS FILE IS EXCLUSIVELY MAINTAINED by the project aedev.project_tpls v0.3.102
2
2
  """ setup of ae namespace module portion shell: shell execution and environment helpers. """
3
- import sys
4
- # noinspection PyUnresolvedReferences
5
3
  import pathlib
6
- # noinspection PyUnresolvedReferences
4
+ import sys
5
+ from typing import Any
7
6
  import setuptools
8
7
 
9
8
 
10
9
  print("SetUp " + __name__ + ": " + sys.executable + str(sys.argv) + f" {sys.path=}")
11
10
 
12
- setup_kwargs = {
11
+ setup_kwargs: dict[str, Any] = {
13
12
  'author': 'AndiEcker',
14
13
  'author_email': 'aecker2@gmail.com',
15
14
  'classifiers': [
@@ -78,10 +77,10 @@ setup_kwargs = {
78
77
  },
79
78
  'python_requires': '>=3.12',
80
79
  'url': 'https://gitlab.com/ae-group/ae_shell',
81
- 'version': '0.3.15',
80
+ 'version': '0.3.17',
82
81
  'zip_safe': True,
83
82
  }
84
83
 
85
84
  if __name__ == "__main__":
86
85
  setuptools.setup(**setup_kwargs)
87
- pass
86
+ ...