encommon 0.13.0__py3-none-any.whl → 0.14.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. encommon/__init__.py +2 -7
  2. encommon/colors/__init__.py +14 -0
  3. encommon/colors/colors.py +518 -0
  4. encommon/colors/test/__init__.py +6 -0
  5. encommon/colors/test/test_colors.py +189 -0
  6. encommon/config/config.py +73 -20
  7. encommon/config/files.py +3 -0
  8. encommon/config/logger.py +5 -0
  9. encommon/config/params.py +1 -2
  10. encommon/config/paths.py +3 -0
  11. encommon/config/test/test_config.py +5 -1
  12. encommon/config/test/test_logger.py +13 -8
  13. encommon/config/test/test_paths.py +1 -1
  14. encommon/config/utils.py +7 -1
  15. encommon/conftest.py +33 -22
  16. encommon/crypts/params.py +23 -4
  17. encommon/times/__init__.py +3 -1
  18. encommon/times/common.py +2 -1
  19. encommon/times/params.py +38 -8
  20. encommon/times/parse.py +37 -10
  21. encommon/times/test/test_parse.py +16 -9
  22. encommon/times/test/test_times.py +35 -2
  23. encommon/times/test/test_unitime.py +23 -0
  24. encommon/times/timers.py +6 -0
  25. encommon/times/times.py +71 -13
  26. encommon/times/unitime.py +48 -0
  27. encommon/times/windows.py +6 -0
  28. encommon/types/__init__.py +20 -2
  29. encommon/types/classes.py +97 -0
  30. encommon/types/lists.py +27 -0
  31. encommon/types/strings.py +29 -4
  32. encommon/types/test/test_classes.py +74 -0
  33. encommon/types/test/test_lists.py +23 -0
  34. encommon/types/test/test_strings.py +15 -3
  35. encommon/types/types.py +20 -0
  36. encommon/utils/__init__.py +4 -0
  37. encommon/utils/paths.py +5 -6
  38. encommon/utils/sample.py +117 -41
  39. encommon/utils/stdout.py +51 -5
  40. encommon/utils/test/test_sample.py +127 -28
  41. encommon/utils/test/test_stdout.py +91 -27
  42. encommon/version.txt +1 -1
  43. {encommon-0.13.0.dist-info → encommon-0.14.0.dist-info}/METADATA +1 -1
  44. encommon-0.14.0.dist-info/RECORD +84 -0
  45. {encommon-0.13.0.dist-info → encommon-0.14.0.dist-info}/WHEEL +1 -1
  46. encommon-0.13.0.dist-info/RECORD +0 -73
  47. {encommon-0.13.0.dist-info → encommon-0.14.0.dist-info}/LICENSE +0 -0
  48. {encommon-0.13.0.dist-info → encommon-0.14.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,189 @@
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 import mark
11
+
12
+ from ..colors import Colors
13
+ from ...types import lattrs
14
+
15
+
16
+
17
+ def test_Colors() -> None:
18
+ """
19
+ Perform various tests associated with relevant routines.
20
+ """
21
+
22
+ color = Colors('000001')
23
+
24
+
25
+ attrs = lattrs(color)
26
+
27
+ assert attrs == [
28
+ '_Colors__source']
29
+
30
+
31
+ assert repr(color) == (
32
+ "Colors('#000001')")
33
+
34
+ assert hash(color) > 0
35
+
36
+ assert str(color) == '#000001'
37
+
38
+
39
+ assert int(color) == 1
40
+ assert float(color) == 1.0
41
+
42
+ assert color + 1 == '000002'
43
+ assert color - 1 == '000000'
44
+
45
+ assert color == '000001'
46
+ assert color != '000000'
47
+ assert color != int
48
+
49
+ assert color > 0
50
+ assert color >= 1
51
+ assert color <= 1
52
+ assert color < 2
53
+
54
+ assert color > '000000'
55
+ assert color >= '000001'
56
+ assert color <= '000001'
57
+ assert color < '000002'
58
+
59
+
60
+
61
+ @mark.parametrize(
62
+ 'source,expect',
63
+ [('ff00cc', (255, 0, 204)),
64
+ ('ffffff', (255, 255, 255)),
65
+ ('000000', (0, 0, 0)),
66
+ ('ff0000', (255, 0, 0)),
67
+ ('00ff00', (0, 255, 0)),
68
+ ('0000ff', (0, 0, 255)),
69
+ ('808080', (128, 128, 128)),
70
+ ('ffff00', (255, 255, 0)),
71
+ ('00ffff', (0, 255, 255)),
72
+ ('ff00ff', (255, 0, 255)),
73
+ ('800080', (128, 0, 128))])
74
+ def test_Colors_rgb(
75
+ source: str,
76
+ expect: tuple[int, ...],
77
+ ) -> None:
78
+ """
79
+ Perform various tests associated with relevant routines.
80
+
81
+ :param source: Source color used when converting values.
82
+ :param expect: Expected output from the testing routine.
83
+ """
84
+
85
+ assert Colors(source).rgb == expect
86
+
87
+
88
+
89
+ @mark.parametrize(
90
+ 'source,expect',
91
+ [('ff00cc', (52.1391, 25.6196, 59.3238)),
92
+ ('ffffff', (95.0500, 100.0000, 108.9000)),
93
+ ('000000', (0.0000, 0.0000, 0.0000)),
94
+ ('ff0000', (41.2400, 21.2600, 1.9300)),
95
+ ('00ff00', (35.7600, 71.5200, 11.9200)),
96
+ ('0000ff', (18.0500, 7.2200, 95.0500)),
97
+ ('808080', (20.5175, 21.5861, 23.5072)),
98
+ ('ffff00', (77.0000, 92.7800, 13.8500)),
99
+ ('00ffff', (53.8100, 78.7400, 106.9700)),
100
+ ('ff00ff', (59.2900, 28.4800, 96.9800)),
101
+ ('800080', (12.7984, 6.1477, 20.9342))])
102
+ def test_Colors_xyz(
103
+ source: str,
104
+ expect: tuple[int, ...],
105
+ ) -> None:
106
+ """
107
+ Perform various tests associated with relevant routines.
108
+
109
+ :param source: Source color used when converting values.
110
+ :param expect: Expected output from the testing routine.
111
+ """
112
+
113
+ assert Colors(source).xyz == expect
114
+
115
+
116
+
117
+ @mark.parametrize(
118
+ 'source,expect',
119
+ [('ff00cc', (0.3803, 0.1869)),
120
+ ('ffffff', (0.3127, 0.3290)),
121
+ ('000000', (0.0000, 0.0000)),
122
+ ('ff0000', (0.6401, 0.3300)),
123
+ ('00ff00', (0.3000, 0.6000)),
124
+ ('0000ff', (0.1500, 0.0600)),
125
+ ('808080', (0.3127, 0.3290)),
126
+ ('ffff00', (0.4193, 0.5053)),
127
+ ('00ffff', (0.2247, 0.3287)),
128
+ ('ff00ff', (0.3209, 0.1542)),
129
+ ('800080', (0.3209, 0.1542))])
130
+ def test_Colors_xy(
131
+ source: str,
132
+ expect: tuple[int, ...],
133
+ ) -> None:
134
+ """
135
+ Perform various tests associated with relevant routines.
136
+
137
+ :param source: Source color used when converting values.
138
+ :param expect: Expected output from the testing routine.
139
+ """
140
+
141
+ assert Colors(source).xy == expect
142
+
143
+
144
+
145
+ @mark.parametrize(
146
+ 'source,expect',
147
+ [('ff00cc', (312, 100, 50)),
148
+ ('ffffff', (0, 0, 100)),
149
+ ('000000', (0, 0, 0)),
150
+ ('ff0000', (0, 100, 50)),
151
+ ('00ff00', (120, 100, 50)),
152
+ ('0000ff', (240, 100, 50)),
153
+ ('808080', (0, 0, 50)),
154
+ ('ffff00', (60, 100, 50)),
155
+ ('00ffff', (180, 100, 50)),
156
+ ('ff00ff', (300, 100, 50)),
157
+ ('800080', (300, 100, 25))])
158
+ def test_Colors_hsl(
159
+ source: str,
160
+ expect: tuple[int, ...],
161
+ ) -> None:
162
+ """
163
+ Perform various tests associated with relevant routines.
164
+
165
+ :param source: Source color used when converting values.
166
+ :param expect: Expected output from the testing routine.
167
+ """
168
+
169
+ assert Colors(source).hsl == expect
170
+
171
+
172
+
173
+ def test_Colors_cover() -> None:
174
+ """
175
+ Perform various tests associated with relevant routines.
176
+ """
177
+
178
+ color1 = Colors('000001')
179
+ color2 = Colors('#000001')
180
+
181
+ assert not (color1 > None) # type: ignore
182
+ assert not (color1 >= None) # type: ignore
183
+ assert not (color1 <= None) # type: ignore
184
+ assert not (color1 < None) # type: ignore
185
+
186
+ assert color1 - color2 == 0
187
+ assert color1 - '000001' == 0
188
+ assert color1 + color2 == 2
189
+ assert color1 + '000001' == 2
encommon/config/config.py CHANGED
@@ -67,8 +67,8 @@ class Config:
67
67
 
68
68
  def __init__(
69
69
  self,
70
- *,
71
70
  files: Optional['PATHABLE'] = None,
71
+ *,
72
72
  paths: Optional['PATHABLE'] = None,
73
73
  cargs: Optional[dict[str, Any]] = None,
74
74
  sargs: Optional[dict[str, Any]] = None,
@@ -179,14 +179,69 @@ class Config:
179
179
  self,
180
180
  ) -> dict[str, Any]:
181
181
  """
182
- Return the configuration in dictionary format for files.
182
+ Return the configuration dumped from the Pydantic model.
183
183
 
184
- :returns: Configuration in dictionary format for files.
184
+ :returns: Configuration dumped from the Pydantic model.
185
185
  """
186
186
 
187
187
  return self.params.model_dump()
188
188
 
189
189
 
190
+ @property
191
+ def basic(
192
+ self,
193
+ ) -> dict[str, Any]:
194
+ """
195
+ Return the configuration source loaded from the objects.
196
+
197
+ :returns: Configuration source loaded from the objects.
198
+ """
199
+
200
+ files = self.files
201
+
202
+ ferged = files.merged
203
+
204
+ merge_dicts(
205
+ dict1=ferged,
206
+ dict2=self.cargs,
207
+ force=True)
208
+
209
+ return deepcopy(ferged)
210
+
211
+
212
+ @property
213
+ def merge(
214
+ self,
215
+ ) -> dict[str, Any]:
216
+ """
217
+ Return the configuration source loaded from the objects.
218
+
219
+ :returns: Configuration source loaded from the objects.
220
+ """
221
+
222
+ files = self.files
223
+ paths = self.paths
224
+
225
+ ferged = files.merged
226
+ perged = paths.merged
227
+
228
+ merge_dicts(
229
+ dict1=ferged,
230
+ dict2=self.cargs,
231
+ force=True)
232
+
233
+ values = perged.values()
234
+
235
+ for merge in values:
236
+
237
+ merge_dicts(
238
+ dict1=ferged,
239
+ dict2=merge,
240
+ force=False)
241
+
242
+ return deepcopy(ferged)
243
+
244
+
190
245
  @property
191
246
  def model(
192
247
  self,
@@ -215,20 +270,12 @@ class Config:
215
270
  if params is not None:
216
271
  return params
217
272
 
218
-
219
- merged = self.files.merged
220
-
221
- merge_dicts(
222
- dict1=merged,
223
- dict2=self.cargs,
224
- force=True)
225
-
226
- params = self.model(**merged)
227
-
273
+ params = self.model(
274
+ **self.basic)
228
275
 
229
276
  self.__params = params
230
277
 
231
- return params
278
+ return self.__params
232
279
 
233
280
 
234
281
  @property
@@ -241,11 +288,14 @@ class Config:
241
288
  :returns: Instance of Python logging library created.
242
289
  """
243
290
 
244
- if self.__logger is not None:
245
- return self.__logger
291
+ logger = self.__logger
292
+ params = self.params
293
+
294
+ if logger is not None:
295
+ return logger
246
296
 
247
297
  logger = Logger(
248
- self.params.enlogger)
298
+ params.enlogger)
249
299
 
250
300
  self.__logger = logger
251
301
 
@@ -262,11 +312,14 @@ class Config:
262
312
  :returns: Instance of the encryption instance created.
263
313
  """
264
314
 
265
- if self.__crypts is not None:
266
- return self.__crypts
315
+ crypts = self.__crypts
316
+ params = self.params
317
+
318
+ if crypts is not None:
319
+ return crypts
267
320
 
268
321
  crypts = Crypts(
269
- self.params.encrypts)
322
+ params.encrypts)
270
323
 
271
324
  self.__crypts = crypts
272
325
 
encommon/config/files.py CHANGED
@@ -17,6 +17,7 @@ from .utils import config_load
17
17
  from .utils import config_path
18
18
  from .utils import config_paths
19
19
  from ..types import merge_dicts
20
+ from ..types import sort_dict
20
21
 
21
22
  if TYPE_CHECKING:
22
23
  from ..utils.common import PATHABLE
@@ -111,6 +112,8 @@ class ConfigFiles:
111
112
  force=False)
112
113
 
113
114
 
115
+ merged = sort_dict(merged)
116
+
114
117
  self.__merged = merged
115
118
 
116
119
  return deepcopy(merged)
encommon/config/logger.py CHANGED
@@ -122,6 +122,11 @@ class Message:
122
122
  value = COMMAD.join(values)
123
123
 
124
124
 
125
+ if (isinstance(value, Times)
126
+ and key == 'elapsed'):
127
+ value = value.since
128
+
129
+
125
130
  if (isinstance(value, float)
126
131
  and key == 'elapsed'):
127
132
 
encommon/config/params.py CHANGED
@@ -10,10 +10,9 @@ is permitted, for more information consult the project license file.
10
10
  from pathlib import Path
11
11
  from typing import Optional
12
12
 
13
- from pydantic import BaseModel
14
-
15
13
  from .logger import LOGLEVELS
16
14
  from ..crypts import CryptsParams
15
+ from ..types import BaseModel
17
16
 
18
17
 
19
18
 
encommon/config/paths.py CHANGED
@@ -17,6 +17,7 @@ from typing import TYPE_CHECKING
17
17
  from .files import ConfigFile
18
18
  from .utils import config_path
19
19
  from .utils import config_paths
20
+ from ..types import sort_dict
20
21
 
21
22
  if TYPE_CHECKING:
22
23
  from ..utils.common import PATHABLE
@@ -118,6 +119,8 @@ class ConfigPaths:
118
119
  merged[key] = file.config
119
120
 
120
121
 
122
+ merged = sort_dict(merged)
123
+
121
124
  self.__merged = merged
122
125
 
123
126
  return deepcopy(merged)
@@ -13,13 +13,13 @@ from typing import TYPE_CHECKING
13
13
  from . import SAMPLES
14
14
  from ..logger import Logger
15
15
  from ..params import Params
16
- from ... import ENPYRWS
17
16
  from ... import PROJECT
18
17
  from ...crypts import Crypts
19
18
  from ...types import inrepr
20
19
  from ...types import instr
21
20
  from ...utils import load_sample
22
21
  from ...utils import prep_sample
22
+ from ...utils.sample import ENPYRWS
23
23
 
24
24
  if TYPE_CHECKING:
25
25
  from ..config import Config
@@ -76,6 +76,10 @@ def test_Config(
76
76
 
77
77
  assert len(config.config) == 3
78
78
 
79
+ assert len(config.basic) == 3
80
+
81
+ assert len(config.merge) == 5
82
+
79
83
  assert config.model is Params
80
84
 
81
85
  assert isinstance(config.params, Params)
@@ -16,6 +16,7 @@ from pytest import fixture
16
16
  from ..logger import Logger
17
17
  from ..logger import Message
18
18
  from ..params import LoggerParams
19
+ from ...times import Times
19
20
  from ...times.common import UNIXMPOCH
20
21
  from ...times.common import UNIXSPOCH
21
22
  from ...types import inrepr
@@ -196,6 +197,8 @@ def test_Logger_cover(
196
197
  :param caplog: pytest object for capturing log message.
197
198
  """
198
199
 
200
+ times = Times('now')
201
+
199
202
 
200
203
  def _logger_logs() -> None:
201
204
  logger.log_d(msg='pytest')
@@ -203,6 +206,7 @@ def test_Logger_cover(
203
206
  logger.log_e(msg='pytest')
204
207
  logger.log_i(msg='pytest')
205
208
  logger.log_w(msg='pytest')
209
+ logger.log_i(elapsed=times)
206
210
 
207
211
 
208
212
  def _logger_stdo() -> _CAPLOG:
@@ -229,16 +233,16 @@ def test_Logger_cover(
229
233
 
230
234
  _logger_logs()
231
235
 
232
- assert len(_logger_stdo()) == 5
233
- assert len(_logger_file()) == 5
236
+ assert len(_logger_stdo()) == 6
237
+ assert len(_logger_file()) == 6
234
238
 
235
239
  logger.stop()
236
240
 
237
241
 
238
242
  _logger_logs()
239
243
 
240
- assert len(_logger_stdo()) == 5
241
- assert len(_logger_file()) == 5
244
+ assert len(_logger_stdo()) == 6
245
+ assert len(_logger_file()) == 6
242
246
 
243
247
 
244
248
  logger.start()
@@ -256,13 +260,14 @@ def test_Logger_cover(
256
260
  assert message in str(stdo)
257
261
  assert message in str(file)
258
262
 
259
- assert len(_logger_stdo()) == 6
260
- assert len(_logger_file()) == 6
263
+ assert len(_logger_stdo()) == 7
264
+ assert len(_logger_file()) == 7
265
+
261
266
 
262
267
  logger.stop()
263
268
 
264
269
 
265
270
  _logger_logs()
266
271
 
267
- assert len(_logger_stdo()) == 6
268
- assert len(_logger_file()) == 6
272
+ assert len(_logger_stdo()) == 7
273
+ assert len(_logger_file()) == 7
@@ -14,12 +14,12 @@ from pytest import fixture
14
14
  from . import SAMPLES
15
15
  from ..paths import ConfigPath
16
16
  from ..paths import ConfigPaths
17
- from ... import ENPYRWS
18
17
  from ... import PROJECT
19
18
  from ...types import inrepr
20
19
  from ...types import instr
21
20
  from ...utils import load_sample
22
21
  from ...utils import prep_sample
22
+ from ...utils.sample import ENPYRWS
23
23
 
24
24
 
25
25
 
encommon/config/utils.py CHANGED
@@ -84,8 +84,14 @@ def config_paths(
84
84
  :returns: New resolved filesystem path object instances.
85
85
  """
86
86
 
87
+ if replace is None:
88
+ replace = {}
89
+
90
+ replace = dict(replace)
91
+
87
92
  replace = {
88
93
  'PROJECT': PROJECT,
89
94
  'WORKSPACE': WORKSPACE}
90
95
 
91
- return resolve_paths(paths, replace)
96
+ return resolve_paths(
97
+ paths, replace)
encommon/conftest.py CHANGED
@@ -27,31 +27,42 @@ def config_factory(
27
27
  :returns: Newly constructed instance of related class.
28
28
  """
29
29
 
30
+ content = (
31
+ f"""
32
+
33
+ enconfig:
34
+ paths:
35
+ - '{SAMPLES}/stark'
36
+ - '{SAMPLES}/wayne'
37
+
38
+ enlogger:
39
+ stdo_level: info
40
+
41
+ encrypts:
42
+ phrases:
43
+ default:
44
+ phrase: phrase
45
+
46
+ """) # noqa: LIT003
47
+
48
+ config_path = (
49
+ tmp_path / 'config.yml')
50
+
51
+ config_log = (
52
+ tmp_path / 'config.log')
53
+
30
54
  save_text(
31
- f'{tmp_path}/config.yml',
32
- content=(
33
- 'enconfig:\n'
34
- ' paths:\n'
35
- f" - '{SAMPLES}/stark'\n"
36
- f" - '{SAMPLES}/wayne'\n"
37
- 'enlogger:\n'
38
- ' stdo_level: info\n'
39
- 'encrypts:\n'
40
- ' phrases:\n'
41
- ' default:\n'
42
- ' phrase: phrase\n'))
43
-
44
- config_log = f'{tmp_path}/config.log'
45
-
46
- cargs = {
47
- 'enlogger/file_path': config_log,
48
- 'enlogger/file_level': 'info'}
49
-
50
- sargs = {
51
- 'custom/parameter': 'fart'}
55
+ config_path, content)
56
+
57
+ logger = {
58
+ 'file_path': config_log,
59
+ 'file_level': 'info'}
60
+
61
+ cargs = {'enlogger': logger}
62
+ sargs = {'cus/tom': 'fart'}
52
63
 
53
64
  return Config(
54
- files=f'{tmp_path}/config.yml',
65
+ config_path,
55
66
  cargs=cargs,
56
67
  sargs=sargs)
57
68
 
encommon/crypts/params.py CHANGED
@@ -7,7 +7,13 @@ is permitted, for more information consult the project license file.
7
7
 
8
8
 
9
9
 
10
- from pydantic import BaseModel
10
+ from typing import Optional
11
+
12
+ from ..types import BaseModel
13
+
14
+
15
+
16
+ _CRYPTS = dict[str, 'CryptParams']
11
17
 
12
18
 
13
19
 
@@ -29,8 +35,21 @@ class CryptsParams(BaseModel, extra='forbid'):
29
35
  Process and validate the core configuration parameters.
30
36
 
31
37
  :param phrases: Passphrases that are used in operations.
32
- :param data: Keyword arguments passed to Pydantic model.
33
- Parameter is picked up by autodoc, please ignore.
34
38
  """
35
39
 
36
- phrases: dict[str, CryptParams] = {}
40
+ phrases: _CRYPTS
41
+
42
+
43
+ def __init__(
44
+ self,
45
+ phrases: Optional[_CRYPTS] = None,
46
+ ) -> None:
47
+ """
48
+ Initialize instance for class using provided parameters.
49
+ """
50
+
51
+ if phrases is None:
52
+ phrases = {}
53
+
54
+ super().__init__(
55
+ phrases=phrases)
@@ -19,6 +19,7 @@ from .parse import string_time
19
19
  from .timer import Timer
20
20
  from .timers import Timers
21
21
  from .times import Times
22
+ from .unitime import unitime
22
23
  from .utils import findtz
23
24
  from .window import Window
24
25
  from .windows import Windows
@@ -40,4 +41,5 @@ __all__ = [
40
41
  'parse_time',
41
42
  'shift_time',
42
43
  'since_time',
43
- 'string_time']
44
+ 'string_time',
45
+ 'unitime']
encommon/times/common.py CHANGED
@@ -24,7 +24,8 @@ SNAPABLE = compile(
24
24
  r'^(\-|\+)[\d\@a-z\-\+]+$')
25
25
 
26
26
  STRINGNOW = {
27
- 'None', 'null', 'now'}
27
+ 'None', None,
28
+ 'null', 'now'}
28
29
 
29
30
 
30
31