encommon 0.6.0__tar.gz → 0.7.1__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.
- {encommon-0.6.0/encommon.egg-info → encommon-0.7.1}/PKG-INFO +1 -1
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/logger.py +10 -1
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/test/test_logger.py +11 -7
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/__init__.py +2 -0
- encommon-0.7.1/encommon/utils/stdout.py +338 -0
- encommon-0.7.1/encommon/utils/test/test_stdout.py +231 -0
- encommon-0.7.1/encommon/version.txt +1 -0
- {encommon-0.6.0 → encommon-0.7.1/encommon.egg-info}/PKG-INFO +1 -1
- {encommon-0.6.0 → encommon-0.7.1}/setup.cfg +2 -0
- encommon-0.6.0/encommon/utils/stdout.py +0 -123
- encommon-0.6.0/encommon/utils/test/test_stdout.py +0 -80
- encommon-0.6.0/encommon/version.txt +0 -1
- {encommon-0.6.0 → encommon-0.7.1}/LICENSE +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/MANIFEST.in +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/README.md +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/common.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/config.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/files.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/params.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/paths.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/test/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/test/test_common.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/test/test_config.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/test/test_files.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/config/test/test_paths.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/conftest.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/crypts/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/crypts/crypts.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/crypts/hashes.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/crypts/params.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/crypts/test/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/crypts/test/test_crypts.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/crypts/test/test_hashes.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/py.typed +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/common.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/duration.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/parse.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/test/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/test/test_common.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/test/test_duration.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/test/test_parse.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/test/test_timers.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/test/test_times.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/test/test_window.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/timers.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/times.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/times/window.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/types/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/types/dicts.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/types/empty.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/types/strings.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/types/test/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/types/test/test_dicts.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/types/test/test_empty.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/types/test/test_strings.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/common.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/match.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/paths.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/sample.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/test/__init__.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/test/test_match.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/test/test_paths.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon/utils/test/test_sample.py +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon.egg-info/SOURCES.txt +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon.egg-info/dependency_links.txt +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon.egg-info/requires.txt +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/encommon.egg-info/top_level.txt +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/pyproject.toml +0 -0
- {encommon-0.6.0 → encommon-0.7.1}/reqs-install.txt +0 -0
@@ -60,7 +60,7 @@ class Message:
|
|
60
60
|
-------
|
61
61
|
>>> message = Message('info', '1970-01-01', foo='bar')
|
62
62
|
>>> strip_ansi(message.stdo_output)
|
63
|
-
'level="info" time="1970-01-01T00:00:
|
63
|
+
'level="info" time="1970-01-01T00:00:00Z" foo="bar"'
|
64
64
|
|
65
65
|
:param level: Severity which log message is classified.
|
66
66
|
:param time: What time the log message actually occurred.
|
@@ -95,6 +95,10 @@ class Message:
|
|
95
95
|
if value in [None, Empty]:
|
96
96
|
continue
|
97
97
|
|
98
|
+
if (key == 'elapsed'
|
99
|
+
and isinstance(value, float)):
|
100
|
+
value = round(value, 2)
|
101
|
+
|
98
102
|
value = str(value)
|
99
103
|
|
100
104
|
self.__fields[key] = value
|
@@ -190,6 +194,11 @@ class Message:
|
|
190
194
|
fields |= dict(self.__fields)
|
191
195
|
|
192
196
|
|
197
|
+
fields['time'] = (
|
198
|
+
fields['time']
|
199
|
+
.replace('+0000', 'Z'))
|
200
|
+
|
201
|
+
|
193
202
|
output: list[str] = []
|
194
203
|
|
195
204
|
for key, value in fields.items():
|
@@ -14,7 +14,6 @@ from _pytest.logging import LogCaptureFixture
|
|
14
14
|
|
15
15
|
from ..logger import Logger
|
16
16
|
from ..logger import Message
|
17
|
-
from ...times.common import UNIXEPOCH
|
18
17
|
from ...times.common import UNIXMPOCH
|
19
18
|
from ...utils.stdout import strip_ansi
|
20
19
|
|
@@ -35,7 +34,8 @@ def test_Message() -> None:
|
|
35
34
|
int=1,
|
36
35
|
list=[1, '2', 3],
|
37
36
|
none=None,
|
38
|
-
string='foo'
|
37
|
+
string='foo',
|
38
|
+
elapsed=0.69420)
|
39
39
|
|
40
40
|
attrs = list(message.__dict__)
|
41
41
|
|
@@ -60,7 +60,8 @@ def test_Message() -> None:
|
|
60
60
|
'float="1.0", '
|
61
61
|
'int="1", '
|
62
62
|
'list="[1, \'2\', 3]", '
|
63
|
-
'string="foo"
|
63
|
+
'string="foo", '
|
64
|
+
'elapsed="0.69")')
|
64
65
|
|
65
66
|
assert str(message) == repr(message)
|
66
67
|
|
@@ -73,19 +74,21 @@ def test_Message() -> None:
|
|
73
74
|
'float': '1.0',
|
74
75
|
'int': '1',
|
75
76
|
'list': "[1, '2', 3]",
|
76
|
-
'string': 'foo'
|
77
|
+
'string': 'foo',
|
78
|
+
'elapsed': '0.69'}
|
77
79
|
|
78
80
|
|
79
81
|
output = strip_ansi(message.stdo_output)
|
80
82
|
|
81
83
|
assert output == (
|
82
84
|
'level="info"'
|
83
|
-
|
85
|
+
' time="1970-01-01T00:00:00Z"'
|
84
86
|
' dict="{\'foo\': \'bar\'}"'
|
85
87
|
' float="1.0"'
|
86
88
|
' int="1"'
|
87
89
|
' list="[1, \'2\', 3]"'
|
88
|
-
' string="foo"'
|
90
|
+
' string="foo"'
|
91
|
+
' elapsed="0.69"')
|
89
92
|
|
90
93
|
|
91
94
|
assert message.file_output == (
|
@@ -95,7 +98,8 @@ def test_Message() -> None:
|
|
95
98
|
' "float": "1.0",'
|
96
99
|
' "int": "1",'
|
97
100
|
' "list": "[1, \'2\', 3]",'
|
98
|
-
' "string": "foo"
|
101
|
+
' "string": "foo",'
|
102
|
+
' "elapsed": "0.69"}')
|
99
103
|
|
100
104
|
|
101
105
|
|
@@ -14,6 +14,7 @@ from .paths import resolve_paths
|
|
14
14
|
from .paths import stats_path
|
15
15
|
from .sample import load_sample
|
16
16
|
from .sample import prep_sample
|
17
|
+
from .stdout import array_ansi
|
17
18
|
from .stdout import kvpair_ansi
|
18
19
|
from .stdout import make_ansi
|
19
20
|
from .stdout import print_ansi
|
@@ -22,6 +23,7 @@ from .stdout import strip_ansi
|
|
22
23
|
|
23
24
|
|
24
25
|
__all__ = [
|
26
|
+
'array_ansi',
|
25
27
|
'fuzz_match',
|
26
28
|
'kvpair_ansi',
|
27
29
|
'load_sample',
|
@@ -0,0 +1,338 @@
|
|
1
|
+
"""
|
2
|
+
Functions and routines associated with Enasis Network Common Library.
|
3
|
+
|
4
|
+
This file is part of Enasis Network software eco-system. Distribution
|
5
|
+
is permitted, for more information consult the project license file.
|
6
|
+
"""
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
from dataclasses import dataclass
|
11
|
+
from re import compile
|
12
|
+
from re import sub as re_sub
|
13
|
+
from sys import stdout
|
14
|
+
from typing import Any
|
15
|
+
from typing import Literal
|
16
|
+
from typing import Optional
|
17
|
+
from typing import Union
|
18
|
+
|
19
|
+
from encommon.times import Duration
|
20
|
+
from encommon.times import Times
|
21
|
+
from encommon.types import Empty
|
22
|
+
|
23
|
+
from .common import JOINABLE
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
ANSICODE = compile(
|
28
|
+
r'\x1b\[[^A-Za-z]*[A-Za-z]')
|
29
|
+
|
30
|
+
ANSIARRAL = Union[
|
31
|
+
list[Any],
|
32
|
+
tuple[Any, ...],
|
33
|
+
set[Any]]
|
34
|
+
|
35
|
+
ANSIARRAD = dict[Any, Any]
|
36
|
+
|
37
|
+
ANSIARRAY = Union[
|
38
|
+
ANSIARRAL,
|
39
|
+
ANSIARRAD]
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
@dataclass(frozen=True)
|
44
|
+
class ArrayColors:
|
45
|
+
"""
|
46
|
+
Colors used to colorize the provided array like object.
|
47
|
+
"""
|
48
|
+
|
49
|
+
label: int = 37
|
50
|
+
key: int = 97
|
51
|
+
|
52
|
+
bool: int = 93
|
53
|
+
none: int = 33
|
54
|
+
str: int = 92
|
55
|
+
num: int = 93
|
56
|
+
|
57
|
+
times: int = 96
|
58
|
+
empty: int = 36
|
59
|
+
other: int = 91
|
60
|
+
|
61
|
+
colon: int = 37
|
62
|
+
hyphen: int = 37
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
def print_ansi(
|
67
|
+
string: str = '',
|
68
|
+
method: Literal['stdout', 'print'] = 'stdout',
|
69
|
+
output: bool = True,
|
70
|
+
) -> str:
|
71
|
+
"""
|
72
|
+
Print the ANSI colorized string to the standard output.
|
73
|
+
|
74
|
+
Example
|
75
|
+
-------
|
76
|
+
>>> print_ansi('<c91>ERROR<c0>')
|
77
|
+
'\\x1b[0;91mERROR\\x1b[0;0m'
|
78
|
+
|
79
|
+
:param string: String processed using inline directives.
|
80
|
+
:param method: Which method for standard output is used.
|
81
|
+
:param output: Whether or not hte output should be print.
|
82
|
+
:returns: ANSI colorized string using inline directives.
|
83
|
+
""" # noqa: D301 LIT102
|
84
|
+
|
85
|
+
string = make_ansi(string)
|
86
|
+
|
87
|
+
if output is True:
|
88
|
+
if method == 'stdout':
|
89
|
+
stdout.write(f'{string}\n')
|
90
|
+
else:
|
91
|
+
print(string) # noqa: T201
|
92
|
+
|
93
|
+
return string
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
def make_ansi(
|
98
|
+
string: str,
|
99
|
+
) -> str:
|
100
|
+
"""
|
101
|
+
Parse the string and replace directives with ANSI codes.
|
102
|
+
|
103
|
+
Example
|
104
|
+
-------
|
105
|
+
>>> make_ansi('<c91>ERROR<c0>')
|
106
|
+
'\\x1b[0;91mERROR\\x1b[0;0m'
|
107
|
+
|
108
|
+
:param string: String containing directives to replace.
|
109
|
+
:returns: Provided string with the directives replaced.
|
110
|
+
""" # noqa: D301 LIT102
|
111
|
+
|
112
|
+
pattern = r'\<c([\d\;]+)\>'
|
113
|
+
replace = r'\033[0;\1m'
|
114
|
+
|
115
|
+
return re_sub(pattern, replace, string)
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
def kvpair_ansi(
|
120
|
+
key: str,
|
121
|
+
value: Any, # noqa: ANN401
|
122
|
+
) -> str:
|
123
|
+
"""
|
124
|
+
Process and colorize keys and values for standard output.
|
125
|
+
|
126
|
+
Example
|
127
|
+
-------
|
128
|
+
>>> kvpair_ansi('k', 'v')
|
129
|
+
'\\x1b[0;90mk\\x1b[0;37m="\\x1b[0;0m...
|
130
|
+
|
131
|
+
:param key: String value to use for the key name portion.
|
132
|
+
:param value: String value to use for the value portion.
|
133
|
+
:returns: ANSI colorized string using inline directives.
|
134
|
+
""" # noqa: D301 LIT102
|
135
|
+
|
136
|
+
if isinstance(value, JOINABLE): # type: ignore
|
137
|
+
value = ','.join([
|
138
|
+
str(x) for x in value])
|
139
|
+
|
140
|
+
elif not isinstance(value, str):
|
141
|
+
value = str(value)
|
142
|
+
|
143
|
+
return make_ansi(
|
144
|
+
f'<c90>{key}<c37>="<c0>'
|
145
|
+
f'{value}<c37>"<c0>')
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
def strip_ansi(
|
150
|
+
string: str,
|
151
|
+
) -> str:
|
152
|
+
"""
|
153
|
+
Return the provided string with the ANSI codes removed.
|
154
|
+
|
155
|
+
Example
|
156
|
+
-------
|
157
|
+
>>> strip_ansi('\\x1b[0;91mERROR\\x1b[0;0m')
|
158
|
+
'ERROR'
|
159
|
+
|
160
|
+
:param string: String which contains ANSI codes to strip.
|
161
|
+
:returns: Provided string with the ANSI codes removed.
|
162
|
+
""" # noqa: D301 LIT102
|
163
|
+
|
164
|
+
return re_sub(ANSICODE, '', string)
|
165
|
+
|
166
|
+
|
167
|
+
|
168
|
+
def array_ansi( # noqa: CFQ001, CFQ004
|
169
|
+
source: ANSIARRAY,
|
170
|
+
*,
|
171
|
+
indent: int = 0,
|
172
|
+
colors: ArrayColors = ArrayColors(),
|
173
|
+
) -> str:
|
174
|
+
"""
|
175
|
+
Print the ANSI colorized iterable to the standard output.
|
176
|
+
|
177
|
+
.. note::
|
178
|
+
This massive function should be refactored, possibly
|
179
|
+
into a class with methods where there are functions.
|
180
|
+
|
181
|
+
:param source: Value in supported and iterable formats.
|
182
|
+
:param indent: How many levels for initial indentation.
|
183
|
+
:param colors: Determine colors used with different types.
|
184
|
+
:returns: ANSI colorized string using inline directives.
|
185
|
+
"""
|
186
|
+
|
187
|
+
output: list[str] = []
|
188
|
+
|
189
|
+
repeat = f'<c{colors.other}>REPEAT<c0>'
|
190
|
+
|
191
|
+
|
192
|
+
def _append(
|
193
|
+
prefix: str,
|
194
|
+
value: Any, # noqa: ANN401
|
195
|
+
indent: int,
|
196
|
+
refers: set[int],
|
197
|
+
) -> None:
|
198
|
+
|
199
|
+
if id(value) in refers:
|
200
|
+
return output.append(
|
201
|
+
f'{prefix} {repeat}')
|
202
|
+
|
203
|
+
|
204
|
+
if isinstance(value, list | tuple | dict):
|
205
|
+
refers.add(id(value))
|
206
|
+
|
207
|
+
|
208
|
+
types = {
|
209
|
+
'list': list,
|
210
|
+
'tuple': tuple,
|
211
|
+
'dict': dict,
|
212
|
+
'frozenset': frozenset,
|
213
|
+
'set': set}
|
214
|
+
|
215
|
+
for name, _type in types.items():
|
216
|
+
|
217
|
+
if not isinstance(value, _type):
|
218
|
+
continue
|
219
|
+
|
220
|
+
output.append(
|
221
|
+
f'{prefix} '
|
222
|
+
f'<c{colors.label}>{name}<c0>')
|
223
|
+
|
224
|
+
return _process(
|
225
|
+
source=value,
|
226
|
+
indent=indent + 2,
|
227
|
+
refers=refers)
|
228
|
+
|
229
|
+
|
230
|
+
value = _concrete(value)
|
231
|
+
|
232
|
+
output.append(
|
233
|
+
f'{prefix} {value}')
|
234
|
+
|
235
|
+
|
236
|
+
def _concrete(
|
237
|
+
source: Any, # noqa: ANN401
|
238
|
+
) -> str:
|
239
|
+
|
240
|
+
color = colors.other
|
241
|
+
|
242
|
+
if isinstance(source, bool):
|
243
|
+
color = colors.bool
|
244
|
+
|
245
|
+
if isinstance(source, int | float):
|
246
|
+
color = colors.num
|
247
|
+
|
248
|
+
elif isinstance(source, str):
|
249
|
+
color = colors.str
|
250
|
+
|
251
|
+
elif source is None:
|
252
|
+
color = colors.none
|
253
|
+
|
254
|
+
elif isinstance(source, Duration):
|
255
|
+
color = colors.times
|
256
|
+
|
257
|
+
elif isinstance(source, Times):
|
258
|
+
color = colors.times
|
259
|
+
|
260
|
+
elif source is Empty:
|
261
|
+
color = colors.empty
|
262
|
+
|
263
|
+
string = f'<c{color}>{source}<c0>'
|
264
|
+
|
265
|
+
if isinstance(source, str):
|
266
|
+
string = f"'{string}'"
|
267
|
+
|
268
|
+
return string
|
269
|
+
|
270
|
+
|
271
|
+
def _dict(
|
272
|
+
source: ANSIARRAD,
|
273
|
+
indent: int,
|
274
|
+
refers: Optional[set[int]] = None,
|
275
|
+
) -> None:
|
276
|
+
|
277
|
+
assert isinstance(source, dict)
|
278
|
+
|
279
|
+
for key, value in source.items():
|
280
|
+
|
281
|
+
prefix = (
|
282
|
+
f'{" " * indent}'
|
283
|
+
f'<c{colors.key}>{key}'
|
284
|
+
f'<c{colors.colon}>:<c0>')
|
285
|
+
|
286
|
+
_append(
|
287
|
+
prefix, value, indent,
|
288
|
+
refers=refers or set())
|
289
|
+
|
290
|
+
|
291
|
+
def _list(
|
292
|
+
source: ANSIARRAL,
|
293
|
+
indent: int,
|
294
|
+
refers: Optional[set[int]] = None,
|
295
|
+
) -> None:
|
296
|
+
|
297
|
+
assert isinstance(
|
298
|
+
source, (list, tuple, set))
|
299
|
+
|
300
|
+
for value in source:
|
301
|
+
|
302
|
+
prefix = (
|
303
|
+
f'{" " * indent}'
|
304
|
+
f'<c{colors.hyphen}>-<c0>')
|
305
|
+
|
306
|
+
_append(
|
307
|
+
prefix, value, indent,
|
308
|
+
refers=refers or set())
|
309
|
+
|
310
|
+
|
311
|
+
def _process(
|
312
|
+
source: ANSIARRAD | ANSIARRAL,
|
313
|
+
**kwargs: Any,
|
314
|
+
) -> None:
|
315
|
+
|
316
|
+
if isinstance(source, dict):
|
317
|
+
return _dict(source, **kwargs)
|
318
|
+
|
319
|
+
return _list(source, **kwargs)
|
320
|
+
|
321
|
+
|
322
|
+
if isinstance(source, dict):
|
323
|
+
_dict(source, indent)
|
324
|
+
|
325
|
+
elif isinstance(source, list):
|
326
|
+
_list(source, indent)
|
327
|
+
|
328
|
+
elif isinstance(source, tuple):
|
329
|
+
_list(source, indent)
|
330
|
+
|
331
|
+
elif isinstance(source, set):
|
332
|
+
_list(source, indent)
|
333
|
+
|
334
|
+
|
335
|
+
_output = [
|
336
|
+
make_ansi(x) for x in output]
|
337
|
+
|
338
|
+
return '\n'.join(_output)
|
@@ -0,0 +1,231 @@
|
|
1
|
+
"""
|
2
|
+
Functions and routines associated with Enasis Network Common Library.
|
3
|
+
|
4
|
+
This file is part of Enasis Network software eco-system. Distribution
|
5
|
+
is permitted, for more information consult the project license file.
|
6
|
+
"""
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
from _pytest.capture import CaptureFixture
|
11
|
+
|
12
|
+
from ..stdout import array_ansi
|
13
|
+
from ..stdout import kvpair_ansi
|
14
|
+
from ..stdout import make_ansi
|
15
|
+
from ..stdout import print_ansi
|
16
|
+
from ..stdout import strip_ansi
|
17
|
+
from ...times import Duration
|
18
|
+
from ...times import Times
|
19
|
+
from ...times.common import UNIXMPOCH
|
20
|
+
from ...types import Empty
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
def test_print_ansi(
|
25
|
+
capsys: CaptureFixture[str],
|
26
|
+
) -> None:
|
27
|
+
"""
|
28
|
+
Perform various tests associated with relevant routines.
|
29
|
+
|
30
|
+
:param capsys: pytest object for capturing print message.
|
31
|
+
"""
|
32
|
+
|
33
|
+
print_ansi('<c91>test<c0>', 'print')
|
34
|
+
|
35
|
+
output = capsys.readouterr().out
|
36
|
+
|
37
|
+
assert strip_ansi(output) == 'test\n'
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
def test_make_ansi() -> None:
|
42
|
+
"""
|
43
|
+
Perform various tests associated with relevant routines.
|
44
|
+
"""
|
45
|
+
|
46
|
+
output = make_ansi('<c31>test<c0>')
|
47
|
+
|
48
|
+
assert output == (
|
49
|
+
'\x1b[0;31mtest\x1b[0;0m')
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
def test_kvpair_ansi() -> None:
|
54
|
+
"""
|
55
|
+
Perform various tests associated with relevant routines.
|
56
|
+
"""
|
57
|
+
|
58
|
+
output = kvpair_ansi('key', 'value')
|
59
|
+
|
60
|
+
assert output == (
|
61
|
+
'\x1b[0;90mkey\x1b[0;37m="\x1b'
|
62
|
+
'[0;0mvalue\x1b[0;37m"\x1b[0;0m')
|
63
|
+
|
64
|
+
output = kvpair_ansi('key', [1, 2])
|
65
|
+
|
66
|
+
assert output == (
|
67
|
+
'\x1b[0;90mkey\x1b[0;37m="\x1b'
|
68
|
+
'[0;0m1,2\x1b[0;37m"\x1b[0;0m')
|
69
|
+
|
70
|
+
output = kvpair_ansi('key', None)
|
71
|
+
|
72
|
+
assert output == (
|
73
|
+
'\x1b[0;90mkey\x1b[0;37m="\x1b'
|
74
|
+
'[0;0mNone\x1b[0;37m"\x1b[0;0m')
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
def test_strip_ansi() -> None:
|
79
|
+
"""
|
80
|
+
Perform various tests associated with relevant routines.
|
81
|
+
"""
|
82
|
+
|
83
|
+
output = '\x1b[0;31mtest\x1b[0;0m'
|
84
|
+
|
85
|
+
assert strip_ansi(output) == 'test'
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
def test_array_ansi() -> None: # noqa: CFQ001
|
90
|
+
"""
|
91
|
+
Perform various tests associated with relevant routines.
|
92
|
+
"""
|
93
|
+
|
94
|
+
simple = {
|
95
|
+
'str': 'value',
|
96
|
+
'list': [1, 2],
|
97
|
+
'bool': False}
|
98
|
+
|
99
|
+
repeat = {
|
100
|
+
'dict': simple | {
|
101
|
+
'dict': simple}}
|
102
|
+
|
103
|
+
source = {
|
104
|
+
'str': 'value',
|
105
|
+
'int': 1,
|
106
|
+
'float': 1.0,
|
107
|
+
'complex': complex(3, 1),
|
108
|
+
'list': [simple],
|
109
|
+
'tuple': (simple,),
|
110
|
+
'range': range(1, 3),
|
111
|
+
'dict1': simple,
|
112
|
+
'dict2': simple,
|
113
|
+
'dict3': simple,
|
114
|
+
'set': {1, 2, 3},
|
115
|
+
'frozenset': {1, 2, 3},
|
116
|
+
'bool': True,
|
117
|
+
'none': None,
|
118
|
+
'_private': None,
|
119
|
+
'repeat': repeat,
|
120
|
+
'Empty': Empty,
|
121
|
+
'Duration': Duration(190802),
|
122
|
+
'Times': Times(0)}
|
123
|
+
|
124
|
+
|
125
|
+
output = strip_ansi(
|
126
|
+
array_ansi(simple))
|
127
|
+
|
128
|
+
assert output == (
|
129
|
+
"str: 'value'\n"
|
130
|
+
'list: list\n'
|
131
|
+
' - 1\n'
|
132
|
+
' - 2\n'
|
133
|
+
'bool: False')
|
134
|
+
|
135
|
+
|
136
|
+
output = strip_ansi(
|
137
|
+
array_ansi([simple]))
|
138
|
+
|
139
|
+
assert output == (
|
140
|
+
'- dict\n'
|
141
|
+
" str: 'value'\n"
|
142
|
+
' list: list\n'
|
143
|
+
' - 1\n'
|
144
|
+
' - 2\n'
|
145
|
+
' bool: False')
|
146
|
+
|
147
|
+
|
148
|
+
output = strip_ansi(
|
149
|
+
array_ansi((1, 2, 3)))
|
150
|
+
|
151
|
+
assert output == (
|
152
|
+
'- 1\n'
|
153
|
+
'- 2\n'
|
154
|
+
'- 3')
|
155
|
+
|
156
|
+
|
157
|
+
output = strip_ansi(
|
158
|
+
array_ansi({1, 2, 3}))
|
159
|
+
|
160
|
+
assert output == (
|
161
|
+
'- 1\n'
|
162
|
+
'- 2\n'
|
163
|
+
'- 3')
|
164
|
+
|
165
|
+
|
166
|
+
output = strip_ansi(
|
167
|
+
array_ansi(source))
|
168
|
+
|
169
|
+
assert output == (
|
170
|
+
"str: 'value'\n"
|
171
|
+
'int: 1\n'
|
172
|
+
'float: 1.0\n'
|
173
|
+
'complex: (3+1j)\n'
|
174
|
+
'list: list\n'
|
175
|
+
' - dict\n'
|
176
|
+
" str: 'value'\n"
|
177
|
+
' list: list\n'
|
178
|
+
' - 1\n'
|
179
|
+
' - 2\n'
|
180
|
+
' bool: False\n'
|
181
|
+
'tuple: tuple\n'
|
182
|
+
' - dict\n'
|
183
|
+
" str: 'value'\n"
|
184
|
+
' list: list\n'
|
185
|
+
' - 1\n'
|
186
|
+
' - 2\n'
|
187
|
+
' bool: False\n'
|
188
|
+
'range: range(1, 3)\n'
|
189
|
+
'dict1: dict\n'
|
190
|
+
" str: 'value'\n"
|
191
|
+
' list: list\n'
|
192
|
+
' - 1\n'
|
193
|
+
' - 2\n'
|
194
|
+
' bool: False\n'
|
195
|
+
'dict2: dict\n'
|
196
|
+
" str: 'value'\n"
|
197
|
+
' list: list\n'
|
198
|
+
' - 1\n'
|
199
|
+
' - 2\n'
|
200
|
+
' bool: False\n'
|
201
|
+
'dict3: dict\n'
|
202
|
+
" str: 'value'\n"
|
203
|
+
' list: list\n'
|
204
|
+
' - 1\n'
|
205
|
+
' - 2\n'
|
206
|
+
' bool: False\n'
|
207
|
+
'set: set\n'
|
208
|
+
' - 1\n'
|
209
|
+
' - 2\n'
|
210
|
+
' - 3\n'
|
211
|
+
'frozenset: set\n'
|
212
|
+
' - 1\n'
|
213
|
+
' - 2\n'
|
214
|
+
' - 3\n'
|
215
|
+
'bool: True\n'
|
216
|
+
'none: None\n'
|
217
|
+
'_private: None\n'
|
218
|
+
'repeat: dict\n'
|
219
|
+
' dict: dict\n'
|
220
|
+
" str: 'value'\n"
|
221
|
+
' list: list\n'
|
222
|
+
' - 1\n'
|
223
|
+
' - 2\n'
|
224
|
+
' bool: False\n'
|
225
|
+
' dict: dict\n'
|
226
|
+
" str: 'value'\n"
|
227
|
+
' list: REPEAT\n'
|
228
|
+
' bool: False\n'
|
229
|
+
'Empty: Empty\n'
|
230
|
+
'Duration: 2d5h\n'
|
231
|
+
f'Times: {UNIXMPOCH}')
|
@@ -0,0 +1 @@
|
|
1
|
+
0.7.1
|
@@ -1,123 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Functions and routines associated with Enasis Network Common Library.
|
3
|
-
|
4
|
-
This file is part of Enasis Network software eco-system. Distribution
|
5
|
-
is permitted, for more information consult the project license file.
|
6
|
-
"""
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
from re import compile
|
11
|
-
from re import sub as re_sub
|
12
|
-
from sys import stdout
|
13
|
-
from typing import Any
|
14
|
-
from typing import Literal
|
15
|
-
|
16
|
-
from .common import JOINABLE
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
ANSICODE = compile(
|
21
|
-
r'\x1b\[[^A-Za-z]*[A-Za-z]')
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def print_ansi(
|
26
|
-
string: str = '',
|
27
|
-
method: Literal['stdout', 'print'] = 'stdout',
|
28
|
-
output: bool = True,
|
29
|
-
) -> str:
|
30
|
-
"""
|
31
|
-
Print the ANSI colorized string to the standard output.
|
32
|
-
|
33
|
-
Example
|
34
|
-
-------
|
35
|
-
>>> print_ansi('<c91>ERROR<c0>')
|
36
|
-
'\\x1b[0;91mERROR\\x1b[0;0m'
|
37
|
-
|
38
|
-
:param string: String processed using inline directives.
|
39
|
-
:param method: Which method for standard output is used.
|
40
|
-
:param output: Whether or not hte output should be print.
|
41
|
-
:returns: ANSI colorized string using inline directives.
|
42
|
-
""" # noqa: D301 LIT102
|
43
|
-
|
44
|
-
string = make_ansi(string)
|
45
|
-
|
46
|
-
if output is True:
|
47
|
-
if method == 'stdout':
|
48
|
-
stdout.write(f'{string}\n')
|
49
|
-
else:
|
50
|
-
print(string) # noqa: T201
|
51
|
-
|
52
|
-
return string
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
def make_ansi(
|
57
|
-
string: str,
|
58
|
-
) -> str:
|
59
|
-
"""
|
60
|
-
Parse the string and replace directives with ANSI codes.
|
61
|
-
|
62
|
-
Example
|
63
|
-
-------
|
64
|
-
>>> make_ansi('<c91>ERROR<c0>')
|
65
|
-
'\\x1b[0;91mERROR\\x1b[0;0m'
|
66
|
-
|
67
|
-
:param string: String containing directives to replace.
|
68
|
-
:returns: Provided string with the directives replaced.
|
69
|
-
""" # noqa: D301 LIT102
|
70
|
-
|
71
|
-
pattern = r'\<c([\d\;]+)\>'
|
72
|
-
replace = r'\033[0;\1m'
|
73
|
-
|
74
|
-
return re_sub(pattern, replace, string)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
def kvpair_ansi(
|
79
|
-
key: str,
|
80
|
-
value: Any, # noqa: ANN401
|
81
|
-
) -> str:
|
82
|
-
"""
|
83
|
-
Process and colorize keys and values for standard output.
|
84
|
-
|
85
|
-
Example
|
86
|
-
-------
|
87
|
-
>>> kvpair_ansi('k', 'v')
|
88
|
-
'\\x1b[0;90mk\\x1b[0;37m="\\x1b[0;0m...
|
89
|
-
|
90
|
-
:param key: String value to use for the key name portion.
|
91
|
-
:param value: String value to use for the value portion.
|
92
|
-
:returns: ANSI colorized string using inline directives.
|
93
|
-
""" # noqa: D301 LIT102
|
94
|
-
|
95
|
-
if isinstance(value, JOINABLE): # type: ignore
|
96
|
-
value = ','.join([
|
97
|
-
str(x) for x in value])
|
98
|
-
|
99
|
-
elif not isinstance(value, str):
|
100
|
-
value = str(value)
|
101
|
-
|
102
|
-
return make_ansi(
|
103
|
-
f'<c90>{key}<c37>="<c0>'
|
104
|
-
f'{value}<c37>"<c0>')
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
def strip_ansi(
|
109
|
-
string: str,
|
110
|
-
) -> str:
|
111
|
-
"""
|
112
|
-
Return the provided string with the ANSI codes removed.
|
113
|
-
|
114
|
-
Example
|
115
|
-
-------
|
116
|
-
>>> strip_ansi('\\x1b[0;91mERROR\\x1b[0;0m')
|
117
|
-
'ERROR'
|
118
|
-
|
119
|
-
:param string: String which contains ANSI codes to strip.
|
120
|
-
:returns: Provided string with the ANSI codes removed.
|
121
|
-
""" # noqa: D301 LIT102
|
122
|
-
|
123
|
-
return re_sub(ANSICODE, '', string)
|
@@ -1,80 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Functions and routines associated with Enasis Network Common Library.
|
3
|
-
|
4
|
-
This file is part of Enasis Network software eco-system. Distribution
|
5
|
-
is permitted, for more information consult the project license file.
|
6
|
-
"""
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
from _pytest.capture import CaptureFixture
|
11
|
-
|
12
|
-
from ..stdout import kvpair_ansi
|
13
|
-
from ..stdout import make_ansi
|
14
|
-
from ..stdout import print_ansi
|
15
|
-
from ..stdout import strip_ansi
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def test_print_ansi(
|
20
|
-
capsys: CaptureFixture[str],
|
21
|
-
) -> None:
|
22
|
-
"""
|
23
|
-
Perform various tests associated with relevant routines.
|
24
|
-
|
25
|
-
:param capsys: pytest object for capturing print message.
|
26
|
-
"""
|
27
|
-
|
28
|
-
print_ansi('<c91>test<c0>', 'print')
|
29
|
-
|
30
|
-
output = capsys.readouterr().out
|
31
|
-
|
32
|
-
assert strip_ansi(output) == 'test\n'
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def test_make_ansi() -> None:
|
37
|
-
"""
|
38
|
-
Perform various tests associated with relevant routines.
|
39
|
-
"""
|
40
|
-
|
41
|
-
output = make_ansi('<c31>test<c0>')
|
42
|
-
|
43
|
-
assert output == (
|
44
|
-
'\x1b[0;31mtest\x1b[0;0m')
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
def test_kvpair_ansi() -> None:
|
49
|
-
"""
|
50
|
-
Perform various tests associated with relevant routines.
|
51
|
-
"""
|
52
|
-
|
53
|
-
output = kvpair_ansi('key', 'value')
|
54
|
-
|
55
|
-
assert output == (
|
56
|
-
'\x1b[0;90mkey\x1b[0;37m="\x1b'
|
57
|
-
'[0;0mvalue\x1b[0;37m"\x1b[0;0m')
|
58
|
-
|
59
|
-
output = kvpair_ansi('key', [1, 2])
|
60
|
-
|
61
|
-
assert output == (
|
62
|
-
'\x1b[0;90mkey\x1b[0;37m="\x1b'
|
63
|
-
'[0;0m1,2\x1b[0;37m"\x1b[0;0m')
|
64
|
-
|
65
|
-
output = kvpair_ansi('key', None)
|
66
|
-
|
67
|
-
assert output == (
|
68
|
-
'\x1b[0;90mkey\x1b[0;37m="\x1b'
|
69
|
-
'[0;0mNone\x1b[0;37m"\x1b[0;0m')
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def test_strip_ansi() -> None:
|
74
|
-
"""
|
75
|
-
Perform various tests associated with relevant routines.
|
76
|
-
"""
|
77
|
-
|
78
|
-
output = '\x1b[0;31mtest\x1b[0;0m'
|
79
|
-
|
80
|
-
assert strip_ansi(output) == 'test'
|
@@ -1 +0,0 @@
|
|
1
|
-
0.6.0
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|