encommon 0.4.0__tar.gz → 0.5.0__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.
Files changed (70) hide show
  1. {encommon-0.4.0 → encommon-0.5.0}/LICENSE +1 -1
  2. {encommon-0.4.0/encommon.egg-info → encommon-0.5.0}/PKG-INFO +20 -15
  3. {encommon-0.4.0 → encommon-0.5.0}/README.md +19 -14
  4. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/params.py +10 -0
  5. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/test/test_config.py +21 -1
  6. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/common.py +1 -1
  7. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/duration.py +99 -43
  8. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/test/test_duration.py +70 -17
  9. {encommon-0.4.0 → encommon-0.5.0}/encommon/types/__init__.py +5 -1
  10. {encommon-0.4.0 → encommon-0.5.0}/encommon/types/dicts.py +26 -0
  11. encommon-0.5.0/encommon/types/strings.py +25 -0
  12. {encommon-0.4.0 → encommon-0.5.0}/encommon/types/test/test_dicts.py +33 -13
  13. encommon-0.5.0/encommon/types/test/test_strings.py +19 -0
  14. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/sample.py +1 -1
  15. encommon-0.5.0/encommon/version.txt +1 -0
  16. {encommon-0.4.0 → encommon-0.5.0/encommon.egg-info}/PKG-INFO +20 -15
  17. {encommon-0.4.0 → encommon-0.5.0}/encommon.egg-info/SOURCES.txt +3 -1
  18. encommon-0.4.0/encommon/version.txt +0 -1
  19. {encommon-0.4.0 → encommon-0.5.0}/MANIFEST.in +0 -0
  20. {encommon-0.4.0 → encommon-0.5.0}/encommon/__init__.py +0 -0
  21. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/__init__.py +0 -0
  22. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/common.py +0 -0
  23. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/config.py +0 -0
  24. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/files.py +0 -0
  25. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/logger.py +0 -0
  26. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/paths.py +0 -0
  27. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/test/__init__.py +0 -0
  28. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/test/test_common.py +0 -0
  29. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/test/test_files.py +0 -0
  30. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/test/test_logger.py +0 -0
  31. {encommon-0.4.0 → encommon-0.5.0}/encommon/config/test/test_paths.py +0 -0
  32. {encommon-0.4.0/encommon/config/test → encommon-0.5.0/encommon}/conftest.py +0 -0
  33. {encommon-0.4.0 → encommon-0.5.0}/encommon/crypts/__init__.py +0 -0
  34. {encommon-0.4.0 → encommon-0.5.0}/encommon/crypts/crypts.py +0 -0
  35. {encommon-0.4.0 → encommon-0.5.0}/encommon/crypts/hashes.py +0 -0
  36. {encommon-0.4.0 → encommon-0.5.0}/encommon/crypts/params.py +0 -0
  37. {encommon-0.4.0 → encommon-0.5.0}/encommon/crypts/test/__init__.py +0 -0
  38. {encommon-0.4.0 → encommon-0.5.0}/encommon/crypts/test/test_crypts.py +0 -0
  39. {encommon-0.4.0 → encommon-0.5.0}/encommon/crypts/test/test_hashes.py +0 -0
  40. {encommon-0.4.0 → encommon-0.5.0}/encommon/py.typed +0 -0
  41. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/__init__.py +0 -0
  42. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/parse.py +0 -0
  43. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/test/__init__.py +0 -0
  44. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/test/test_common.py +0 -0
  45. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/test/test_parse.py +0 -0
  46. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/test/test_timers.py +0 -0
  47. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/test/test_times.py +0 -0
  48. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/test/test_window.py +0 -0
  49. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/timers.py +1 -1
  50. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/times.py +0 -0
  51. {encommon-0.4.0 → encommon-0.5.0}/encommon/times/window.py +0 -0
  52. {encommon-0.4.0 → encommon-0.5.0}/encommon/types/empty.py +0 -0
  53. {encommon-0.4.0 → encommon-0.5.0}/encommon/types/test/__init__.py +0 -0
  54. {encommon-0.4.0 → encommon-0.5.0}/encommon/types/test/test_empty.py +0 -0
  55. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/__init__.py +0 -0
  56. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/common.py +0 -0
  57. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/match.py +0 -0
  58. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/paths.py +0 -0
  59. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/stdout.py +0 -0
  60. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/test/__init__.py +0 -0
  61. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/test/test_match.py +0 -0
  62. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/test/test_paths.py +0 -0
  63. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/test/test_sample.py +0 -0
  64. {encommon-0.4.0 → encommon-0.5.0}/encommon/utils/test/test_stdout.py +0 -0
  65. {encommon-0.4.0 → encommon-0.5.0}/encommon.egg-info/dependency_links.txt +0 -0
  66. {encommon-0.4.0 → encommon-0.5.0}/encommon.egg-info/requires.txt +0 -0
  67. {encommon-0.4.0 → encommon-0.5.0}/encommon.egg-info/top_level.txt +0 -0
  68. {encommon-0.4.0 → encommon-0.5.0}/pyproject.toml +0 -0
  69. {encommon-0.4.0 → encommon-0.5.0}/reqs-install.txt +0 -0
  70. {encommon-0.4.0 → encommon-0.5.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Enasis Network
3
+ Copyright (c) 2024 Enasis Network
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: encommon
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Enasis Network Common Library
5
5
  License: MIT
6
6
  Classifier: Programming Language :: Python :: 3
@@ -18,14 +18,15 @@ Requires-Dist: snaptime
18
18
 
19
19
  # Enasis Network Common Library
20
20
 
21
- [![](https://img.shields.io/github/actions/workflow/status/enasisnetwork/encommon/build.yml?style=flat-square&label=GitHub%20actions)](https://github.com/enasisnetwork/encommon/actions)
22
- [![](https://img.shields.io/readthedocs/encommon?style=flat-square&label=Read%20the%20Docs)](https://encommon.readthedocs.io/en/stable)
23
- [![](https://img.shields.io/pypi/v/encommon.svg?style=flat-square&label=PyPi%20version)](https://pypi.org/project/encommon)
24
- [![](https://img.shields.io/pypi/dm/encommon?style=flat-square&label=PyPi%20downloads)](https://pypi.org/project/encommon)
25
-
26
21
  Common classes and functions used in various public and private projects for
27
22
  [Enasis Network](https://github.com/enasisnetwork).
28
23
 
24
+ [![](https://img.shields.io/github/actions/workflow/status/enasisnetwork/encommon/build.yml?style=flat-square&label=GitHub%20actions)](https://github.com/enasisnetwork/encommon/actions)<br>
25
+ [![codecov](https://codecov.io/gh/enasisnetwork/encommon/graph/badge.svg?token=7PGOXKJU0E)](https://codecov.io/gh/enasisnetwork/encommon)<br>
26
+ [![](https://img.shields.io/readthedocs/encommon?style=flat-square&label=Read%20the%20Docs)](https://encommon.readthedocs.io/en/stable)<br>
27
+ [![](https://img.shields.io/pypi/v/encommon.svg?style=flat-square&label=PyPi%20version)](https://pypi.org/project/encommon)<br>
28
+ [![](https://img.shields.io/pypi/dm/encommon?style=flat-square&label=PyPi%20downloads)](https://pypi.org/project/encommon)
29
+
29
30
  ## Installing the package
30
31
  Installing stable from the PyPi repository
31
32
  ```
@@ -67,12 +68,16 @@ information found in the `htmlcov` folder in the root of the project.
67
68
  make -s pytest
68
69
  ```
69
70
 
70
- ## Future features and subpackages
71
-
72
- ### `netdom` subpackage
73
- This package will include class objects for handling network addresses and
74
- other functions to assist with validating and normalizing domain names.
75
-
76
- ### `formats` subpackage
77
- This package will include class objects to help with dealing with verious
78
- formats of data. These formats will include Jinja2, CSV, and HTML.
71
+ ## Build and upload to PyPi
72
+ Build the package.
73
+ ```
74
+ make -s pypackage
75
+ ```
76
+ Upload to the test PyPi.
77
+ ```
78
+ make -s pypi-upload-test
79
+ ```
80
+ Upload to the prod PyPi.
81
+ ```
82
+ make -s pypi-upload-prod
83
+ ```
@@ -1,13 +1,14 @@
1
1
  # Enasis Network Common Library
2
2
 
3
- [![](https://img.shields.io/github/actions/workflow/status/enasisnetwork/encommon/build.yml?style=flat-square&label=GitHub%20actions)](https://github.com/enasisnetwork/encommon/actions)
4
- [![](https://img.shields.io/readthedocs/encommon?style=flat-square&label=Read%20the%20Docs)](https://encommon.readthedocs.io/en/stable)
5
- [![](https://img.shields.io/pypi/v/encommon.svg?style=flat-square&label=PyPi%20version)](https://pypi.org/project/encommon)
6
- [![](https://img.shields.io/pypi/dm/encommon?style=flat-square&label=PyPi%20downloads)](https://pypi.org/project/encommon)
7
-
8
3
  Common classes and functions used in various public and private projects for
9
4
  [Enasis Network](https://github.com/enasisnetwork).
10
5
 
6
+ [![](https://img.shields.io/github/actions/workflow/status/enasisnetwork/encommon/build.yml?style=flat-square&label=GitHub%20actions)](https://github.com/enasisnetwork/encommon/actions)<br>
7
+ [![codecov](https://codecov.io/gh/enasisnetwork/encommon/graph/badge.svg?token=7PGOXKJU0E)](https://codecov.io/gh/enasisnetwork/encommon)<br>
8
+ [![](https://img.shields.io/readthedocs/encommon?style=flat-square&label=Read%20the%20Docs)](https://encommon.readthedocs.io/en/stable)<br>
9
+ [![](https://img.shields.io/pypi/v/encommon.svg?style=flat-square&label=PyPi%20version)](https://pypi.org/project/encommon)<br>
10
+ [![](https://img.shields.io/pypi/dm/encommon?style=flat-square&label=PyPi%20downloads)](https://pypi.org/project/encommon)
11
+
11
12
  ## Installing the package
12
13
  Installing stable from the PyPi repository
13
14
  ```
@@ -49,12 +50,16 @@ information found in the `htmlcov` folder in the root of the project.
49
50
  make -s pytest
50
51
  ```
51
52
 
52
- ## Future features and subpackages
53
-
54
- ### `netdom` subpackage
55
- This package will include class objects for handling network addresses and
56
- other functions to assist with validating and normalizing domain names.
57
-
58
- ### `formats` subpackage
59
- This package will include class objects to help with dealing with verious
60
- formats of data. These formats will include Jinja2, CSV, and HTML.
53
+ ## Build and upload to PyPi
54
+ Build the package.
55
+ ```
56
+ make -s pypackage
57
+ ```
58
+ Upload to the test PyPi.
59
+ ```
60
+ make -s pypi-upload-test
61
+ ```
62
+ Upload to the prod PyPi.
63
+ ```
64
+ make -s pypi-upload-prod
65
+ ```
@@ -20,7 +20,9 @@ class ConfigParams(BaseModel, extra='forbid'):
20
20
  """
21
21
  Process and validate the common configuration parameters.
22
22
 
23
+ :param paths: Complete or relative path to config paths.
23
24
  :param data: Keyword arguments passed to Pydantic model.
25
+ This parameter is picked up by autodoc, please ignore.
24
26
  """
25
27
 
26
28
  paths: Optional[list[str]] = None
@@ -31,7 +33,11 @@ class LoggerParams(BaseModel, extra='forbid'):
31
33
  """
32
34
  Process and validate the common configuration parameters.
33
35
 
36
+ :param stdo_level: Minimum log message severity level.
37
+ :param file_level: Minimum log message severity level.
38
+ :param file_path: Enables writing to the filesystem path.
34
39
  :param data: Keyword arguments passed to Pydantic model.
40
+ This parameter is picked up by autodoc, please ignore.
35
41
  """
36
42
 
37
43
  stdo_level: Optional[LOGLEVELS] = None
@@ -44,7 +50,11 @@ class Params(BaseModel, extra='forbid'):
44
50
  """
45
51
  Process and validate the common configuration parameters.
46
52
 
53
+ :param enconfig: Configuration for the `.Config` object.
54
+ :param enlogger: Configuration for the `.Logger` object.
55
+ :param encrypts: Configuration for the `.Crypts` object.
47
56
  :param data: Keyword arguments passed to Pydantic model.
57
+ This parameter is picked up by autodoc, please ignore.
48
58
  """
49
59
 
50
60
  enconfig: Optional[ConfigParams] = None
@@ -25,7 +25,7 @@ SAMPLES = (
25
25
 
26
26
 
27
27
 
28
- def test_Config(
28
+ def test_Config( # noqa: CFQ001
29
29
  config_path: Path,
30
30
  ) -> None:
31
31
  """
@@ -108,6 +108,26 @@ def test_Config(
108
108
  assert sample == expect
109
109
 
110
110
 
111
+ _params1 = config.params
112
+ _params2 = config.params
113
+
114
+ assert _params1 is _params2
115
+
116
+ sample = load_sample(
117
+ path=SAMPLES.joinpath('params.json'),
118
+ update=ENPYRWS,
119
+ content=_params1.model_dump(),
120
+ replace={
121
+ 'config_path': str(config_path)})
122
+
123
+ expect = prep_sample(
124
+ content=_params2.model_dump(),
125
+ replace={
126
+ 'config_path': str(config_path)})
127
+
128
+ assert sample == expect
129
+
130
+
111
131
  logger = config.logger
112
132
 
113
133
  assert isinstance(logger, Logger)
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
20
20
 
21
21
 
22
22
  NUMERISH = compile(r'^\-?\d+(\.\d+)?$')
23
- SNAPABLE = compile(r'^\-|\+[\d\@a-z]+$')
23
+ SNAPABLE = compile(r'^(\-|\+)[\d\@a-z\-\+]+$')
24
24
  STRINGNOW = {'None', 'null', 'now'}
25
25
 
26
26
  NUMERIC = Union[int, float]
@@ -9,6 +9,7 @@ is permitted, for more information consult the project license file.
9
9
 
10
10
  from typing import Literal
11
11
  from typing import Union
12
+ from typing import get_args
12
13
 
13
14
 
14
15
 
@@ -17,14 +18,16 @@ DURAGROUP = Literal[
17
18
  'day', 'hour', 'minute',
18
19
  'second']
19
20
 
20
- DURASHORT = {
21
- 'year': 'y',
22
- 'month': 'mon',
23
- 'week': 'w',
24
- 'day': 'd',
25
- 'hour': 'h',
26
- 'minute': 'm',
27
- 'second': 's'}
21
+ DURASHORT = Literal[
22
+ 'y', 'mon', 'w',
23
+ 'd', 'h', 'm', 's']
24
+
25
+ DURAMAPS = dict(zip(
26
+ get_args(DURAGROUP),
27
+ get_args(DURASHORT)))
28
+
29
+ DURAUNITS = Union[
30
+ DURAGROUP, DURASHORT]
28
31
 
29
32
 
30
33
 
@@ -49,16 +52,20 @@ class Duration:
49
52
 
50
53
  :param seconds: Period in seconds that will be iterated.
51
54
  :param smart: Determines if we hide seconds after minute.
55
+ :param groups: Determine the amount of groups to show,
56
+ ensuring the larger units are returned before smaller.
52
57
  """
53
58
 
54
59
  __source: float
55
60
  __smart: bool
61
+ __groups: int
56
62
 
57
63
 
58
64
  def __init__(
59
65
  self,
60
66
  seconds: int | float,
61
67
  smart: bool = True,
68
+ groups: int = len(DURAMAPS),
62
69
  ) -> None:
63
70
  """
64
71
  Initialize instance for class using provided parameters.
@@ -66,6 +73,7 @@ class Duration:
66
73
 
67
74
  self.__source = float(seconds)
68
75
  self.__smart = bool(smart)
76
+ self.__groups = int(groups)
69
77
 
70
78
 
71
79
  def __repr__(
@@ -77,7 +85,11 @@ class Duration:
77
85
  :returns: String representation for values from instance.
78
86
  """
79
87
 
80
- return f'Duration({self.source}, {self.smart})'
88
+ return (
89
+ f'Duration('
90
+ f'seconds={self.source}, '
91
+ f'smart={self.smart}, '
92
+ f'groups={self.groups})')
81
93
 
82
94
 
83
95
  def __hash__(
@@ -290,16 +302,31 @@ class Duration:
290
302
  @property
291
303
  def groups(
292
304
  self,
293
- ) -> dict[DURAGROUP, int]:
305
+ ) -> int:
306
+ """
307
+ Return the property for attribute from the class instance.
308
+
309
+ :returns: Property for attribute from the class instance.
310
+ """
311
+
312
+ return self.__groups
313
+
314
+
315
+ def units(
316
+ self,
317
+ short: bool = False,
318
+ ) -> dict[DURAUNITS, int]:
294
319
  """
295
320
  Return the groups of time units with each relevant value.
296
321
 
322
+ :param short: Determine if we should use the short hand.
297
323
  :returns: Groups of time units with each relevant value.
298
324
  """
299
325
 
300
- seconds = int(self.__source)
326
+ source = self.__source
327
+ seconds = int(source)
301
328
 
302
- returned: dict[DURAGROUP, int] = {}
329
+ returned: dict[DURAUNITS, int] = {}
303
330
 
304
331
  groups: dict[DURAGROUP, int] = {
305
332
  'year': 31536000,
@@ -309,6 +336,7 @@ class Duration:
309
336
  'hour': 3600,
310
337
  'minute': 60}
311
338
 
339
+
312
340
  for key, value in groups.items():
313
341
 
314
342
  if seconds < value:
@@ -320,71 +348,99 @@ class Duration:
320
348
 
321
349
  seconds %= value
322
350
 
323
- if seconds >= 1:
351
+
352
+ if (source < 60
353
+ or (seconds >= 1
354
+ and source > 60)):
324
355
  returned['second'] = seconds
325
356
 
326
- return returned
357
+ if (self.__smart
358
+ and 'second' in returned
359
+ and len(returned) >= 2):
360
+ del returned['second']
327
361
 
328
362
 
329
- @property
330
- def compact(
363
+ items = (
364
+ list(returned.items())
365
+ [:self.__groups])
366
+
367
+ if short is False:
368
+ return dict(items)
369
+
370
+ return {
371
+ DURAMAPS[k]: v
372
+ for k, v in items}
373
+
374
+
375
+ def __duration(
331
376
  self,
377
+ delim: str = ' ',
378
+ short: bool = True,
332
379
  ) -> str:
333
380
  """
334
381
  Return the compact format calculated from source duration.
335
382
 
383
+ :param delim: Optional delimiter for between the groups.
384
+ :param short: Determine if we should use the short hand.
336
385
  :returns: Compact format calculated from source duration.
337
386
  """
338
387
 
339
388
  parts: list[str] = []
340
389
 
341
- source = self.__source
342
- groups = self.groups
390
+ groups = self.units(short)
391
+ spaced = '' if short else ' '
343
392
 
344
393
  for part, value in groups.items():
345
394
 
346
- if (part == 'second'
347
- and self.__smart
348
- and source >= 60):
349
- continue
395
+ unit: str = part
350
396
 
351
- unit = DURASHORT[part]
397
+ if (short is False
398
+ and value != 1):
399
+ unit += 's'
352
400
 
353
- parts.append(f'{value}{unit}')
401
+ parts.append(
402
+ f'{value}{spaced}{unit}')
354
403
 
355
- return ''.join(parts)
404
+ return delim.join(parts)
356
405
 
357
406
 
358
407
  @property
359
- def verbose(
408
+ def short(
360
409
  self,
361
410
  ) -> str:
362
411
  """
363
- Return the verbose format calculated from source duration.
412
+ Return the compact format calculated from source duration.
364
413
 
365
414
  :returns: Compact format calculated from source duration.
366
415
  """
367
416
 
368
- parts: list[str] = []
417
+ return self.__duration()
369
418
 
370
- source = self.__source
371
- groups = self.groups
372
419
 
373
- if source < 60:
374
- return 'just now'
420
+ @property
421
+ def compact(
422
+ self,
423
+ ) -> str:
424
+ """
425
+ Return the compact format calculated from source duration.
375
426
 
376
- for part, value in groups.items():
427
+ :returns: Compact format calculated from source duration.
428
+ """
377
429
 
378
- if (part == 'second'
379
- and self.__smart
380
- and source >= 60):
381
- continue
430
+ return self.short.replace(' ', '')
431
+
432
+
433
+ @property
434
+ def verbose(
435
+ self,
436
+ ) -> str:
437
+ """
438
+ Return the verbose format calculated from source duration.
382
439
 
383
- unit = (
384
- f'{part}s'
385
- if value != 1
386
- else part)
440
+ :returns: Compact format calculated from source duration.
441
+ """
387
442
 
388
- parts.append(f'{value} {unit}')
443
+ if self.__source < 60:
444
+ return 'just now'
389
445
 
390
- return ', '.join(parts)
446
+ return self.__duration(', ', False)
@@ -16,44 +16,97 @@ def test_Duration() -> None:
16
16
  Perform various tests associated with relevant routines.
17
17
  """
18
18
 
19
- duration = Duration(60)
19
+ duration = Duration(95401)
20
20
 
21
21
  attrs = list(duration.__dict__)
22
22
 
23
23
  assert attrs == [
24
24
  '_Duration__source',
25
- '_Duration__smart']
25
+ '_Duration__smart',
26
+ '_Duration__groups']
26
27
 
27
28
 
28
29
  assert repr(duration) == (
29
- 'Duration(60.0, True)')
30
+ 'Duration('
31
+ 'seconds=95401.0, '
32
+ 'smart=True, '
33
+ 'groups=7)')
34
+
30
35
  assert isinstance(hash(duration), int)
31
- assert str(duration) == '1m'
36
+ assert str(duration) == '1d2h30m'
32
37
 
33
38
 
34
- assert int(duration) == 60
35
- assert float(duration) == 60
39
+ assert int(duration) == 95401
40
+ assert float(duration) == 95401
36
41
 
37
- assert duration + 1 == 61
38
- assert duration + duration == 120
39
- assert duration - 1 == 59
42
+ assert duration + 1 == 95402
43
+ assert duration + duration == 190802
44
+ assert duration - 1 == 95400
40
45
  assert duration - duration == 0
41
46
 
42
47
  assert duration == duration
43
- assert duration != Duration(61)
48
+ assert duration != Duration(60)
44
49
  assert duration != 'invalid'
45
50
 
46
- assert duration > Duration(59)
47
- assert duration >= Duration(60)
48
- assert duration < Duration(61)
49
- assert duration <= Duration(60)
51
+ assert duration > Duration(95400)
52
+ assert duration >= Duration(95401)
53
+ assert duration < Duration(95402)
54
+ assert duration <= Duration(95401)
50
55
 
51
56
 
52
- assert duration.source == 60
57
+ assert duration.source == 95401
53
58
  assert duration.smart is True
59
+ assert duration.groups == 7
60
+
61
+ assert duration.short == '1d 2h 30m'
62
+ assert duration.compact == '1d2h30m'
63
+ assert duration.verbose == (
64
+ '1 day, 2 hours, 30 minutes')
65
+
66
+ assert duration.units() == {
67
+ 'day': 1,
68
+ 'hour': 2,
69
+ 'minute': 30}
70
+
71
+
72
+ duration = Duration(
73
+ seconds=7501,
74
+ smart=False)
75
+
76
+ assert duration.short == '2h 5m 1s'
77
+ assert duration.compact == '2h5m1s'
78
+ assert duration.verbose == (
79
+ '2 hours, 5 minutes, 1 second')
80
+
81
+
82
+ duration = Duration(
83
+ seconds=694800,
84
+ smart=False,
85
+ groups=3)
86
+
87
+ assert duration.short == '1w 1d 1h'
88
+ assert duration.compact == '1w1d1h'
89
+ assert duration.verbose == (
90
+ '1 week, 1 day, 1 hour')
91
+
92
+
93
+ duration = Duration(36295261)
94
+
95
+ assert duration.units() == {
96
+ 'year': 1,
97
+ 'week': 3,
98
+ 'month': 1,
99
+ 'day': 4,
100
+ 'hour': 2,
101
+ 'minute': 1}
54
102
 
55
- assert duration.compact == '1m'
56
- assert duration.verbose == '1 minute'
103
+ assert duration.units(True) == {
104
+ 'y': 1,
105
+ 'w': 3,
106
+ 'mon': 1,
107
+ 'd': 4,
108
+ 'h': 2,
109
+ 'm': 1}
57
110
 
58
111
 
59
112
 
@@ -8,10 +8,14 @@ is permitted, for more information consult the project license file.
8
8
 
9
9
 
10
10
  from .dicts import merge_dicts
11
+ from .dicts import sort_dict
11
12
  from .empty import Empty
13
+ from .strings import striplower
12
14
 
13
15
 
14
16
 
15
17
  __all__ = [
16
18
  'Empty',
17
- 'merge_dicts']
19
+ 'merge_dicts',
20
+ 'sort_dict',
21
+ 'striplower']
@@ -58,3 +58,29 @@ def merge_dicts(
58
58
 
59
59
  elif force is True:
60
60
  dict1[key] = value
61
+
62
+
63
+
64
+ def sort_dict(
65
+ value: dict[Any, Any],
66
+ reverse: bool = False,
67
+ ) -> dict[Any, Any]:
68
+ """
69
+ Sort the keys within the dictionary and return new one.
70
+
71
+ Example
72
+ -------
73
+ >>> foo = {'b': 'be', 'a': 'ey'}
74
+ >>> sort_dict(foo)
75
+ {'a': 'ey', 'b': 'be'}
76
+ >>> sort_dict(foo, True)
77
+ {'b': 'be', 'a': 'ey'}
78
+
79
+ :param value: Dictionary whose keys are sorted into new.
80
+ :param reverse: Optionally reverse the sort direction.
81
+ :returns: New dictionary with keys sorted alphabetical.
82
+ """
83
+
84
+ return dict(sorted(
85
+ value.items(),
86
+ reverse=reverse))
@@ -0,0 +1,25 @@
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
+ def striplower(
11
+ value: str,
12
+ ) -> str:
13
+ """
14
+ Return the provided string but stripped and lower cased.
15
+
16
+ Example
17
+ -------
18
+ >>> striplower(' Foo ')
19
+ 'foo'
20
+
21
+ :param value: String which will be stripped and lowered.
22
+ :returns: Provided string but stripped and lower cased.
23
+ """
24
+
25
+ return value.strip().lower()
@@ -10,6 +10,23 @@ is permitted, for more information consult the project license file.
10
10
  from copy import deepcopy
11
11
 
12
12
  from ..dicts import merge_dicts
13
+ from ..dicts import sort_dict
14
+
15
+
16
+
17
+ DICT1 = {
18
+ 'dict1': 'dict1',
19
+ 'str': 'd1string',
20
+ 'list': ['d1list'],
21
+ 'dict': {'key': 'd1value'},
22
+ 'bool': False}
23
+
24
+ DICT2 = {
25
+ 'dict2': 'dict2',
26
+ 'str': 'd2string',
27
+ 'list': ['d2list'],
28
+ 'dict': {'key': 'd2value'},
29
+ 'bool': True}
13
30
 
14
31
 
15
32
 
@@ -18,19 +35,8 @@ def test_merge_dicts() -> None:
18
35
  Perform various tests associated with relevant routines.
19
36
  """
20
37
 
21
- dict1 = {
22
- 'dict1': 'dict1',
23
- 'str': 'd1string',
24
- 'list': ['d1list'],
25
- 'dict': {'key': 'd1value'},
26
- 'bool': False}
27
-
28
- dict2 = {
29
- 'dict2': 'dict2',
30
- 'str': 'd2string',
31
- 'list': ['d2list'],
32
- 'dict': {'key': 'd2value'},
33
- 'bool': True}
38
+ dict1 = deepcopy(DICT1)
39
+ dict2 = deepcopy(DICT2)
34
40
 
35
41
  dict1['recurse'] = deepcopy(dict1)
36
42
  dict2['recurse'] = deepcopy(dict2)
@@ -100,3 +106,17 @@ def test_merge_dicts() -> None:
100
106
  'list': ['d1list'],
101
107
  'dict': {'key': 'd1value'},
102
108
  'bool': False}}
109
+
110
+
111
+
112
+ def test_sort_dict() -> None:
113
+ """
114
+ Perform various tests associated with relevant routines.
115
+ """
116
+
117
+ assert sort_dict(DICT1) == {
118
+ 'bool': False,
119
+ 'dict': {'key': 'd1value'},
120
+ 'dict1': 'dict1',
121
+ 'list': ['d1list'],
122
+ 'str': 'd1string'}
@@ -0,0 +1,19 @@
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 ..strings import striplower
11
+
12
+
13
+
14
+ def test_striplower() -> None:
15
+ """
16
+ Perform various tests associated with relevant routines.
17
+ """
18
+
19
+ assert striplower(' Foo ') == 'foo'
@@ -98,7 +98,7 @@ def load_sample(
98
98
  :param update: Determine whether the sample is updated.
99
99
  :param content: Content which will be processed for JSON.
100
100
  :param default: Callable used when stringifying values.
101
- :param replace: Optional string values to replace in path.
101
+ :param replace: Optional string values to replace in file.
102
102
  :returns: Content after processing using JSON functions.
103
103
  """
104
104
 
@@ -0,0 +1 @@
1
+ 0.5.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: encommon
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Enasis Network Common Library
5
5
  License: MIT
6
6
  Classifier: Programming Language :: Python :: 3
@@ -18,14 +18,15 @@ Requires-Dist: snaptime
18
18
 
19
19
  # Enasis Network Common Library
20
20
 
21
- [![](https://img.shields.io/github/actions/workflow/status/enasisnetwork/encommon/build.yml?style=flat-square&label=GitHub%20actions)](https://github.com/enasisnetwork/encommon/actions)
22
- [![](https://img.shields.io/readthedocs/encommon?style=flat-square&label=Read%20the%20Docs)](https://encommon.readthedocs.io/en/stable)
23
- [![](https://img.shields.io/pypi/v/encommon.svg?style=flat-square&label=PyPi%20version)](https://pypi.org/project/encommon)
24
- [![](https://img.shields.io/pypi/dm/encommon?style=flat-square&label=PyPi%20downloads)](https://pypi.org/project/encommon)
25
-
26
21
  Common classes and functions used in various public and private projects for
27
22
  [Enasis Network](https://github.com/enasisnetwork).
28
23
 
24
+ [![](https://img.shields.io/github/actions/workflow/status/enasisnetwork/encommon/build.yml?style=flat-square&label=GitHub%20actions)](https://github.com/enasisnetwork/encommon/actions)<br>
25
+ [![codecov](https://codecov.io/gh/enasisnetwork/encommon/graph/badge.svg?token=7PGOXKJU0E)](https://codecov.io/gh/enasisnetwork/encommon)<br>
26
+ [![](https://img.shields.io/readthedocs/encommon?style=flat-square&label=Read%20the%20Docs)](https://encommon.readthedocs.io/en/stable)<br>
27
+ [![](https://img.shields.io/pypi/v/encommon.svg?style=flat-square&label=PyPi%20version)](https://pypi.org/project/encommon)<br>
28
+ [![](https://img.shields.io/pypi/dm/encommon?style=flat-square&label=PyPi%20downloads)](https://pypi.org/project/encommon)
29
+
29
30
  ## Installing the package
30
31
  Installing stable from the PyPi repository
31
32
  ```
@@ -67,12 +68,16 @@ information found in the `htmlcov` folder in the root of the project.
67
68
  make -s pytest
68
69
  ```
69
70
 
70
- ## Future features and subpackages
71
-
72
- ### `netdom` subpackage
73
- This package will include class objects for handling network addresses and
74
- other functions to assist with validating and normalizing domain names.
75
-
76
- ### `formats` subpackage
77
- This package will include class objects to help with dealing with verious
78
- formats of data. These formats will include Jinja2, CSV, and HTML.
71
+ ## Build and upload to PyPi
72
+ Build the package.
73
+ ```
74
+ make -s pypackage
75
+ ```
76
+ Upload to the test PyPi.
77
+ ```
78
+ make -s pypi-upload-test
79
+ ```
80
+ Upload to the prod PyPi.
81
+ ```
82
+ make -s pypi-upload-prod
83
+ ```
@@ -5,6 +5,7 @@ pyproject.toml
5
5
  reqs-install.txt
6
6
  setup.cfg
7
7
  encommon/__init__.py
8
+ encommon/conftest.py
8
9
  encommon/py.typed
9
10
  encommon/version.txt
10
11
  encommon.egg-info/PKG-INFO
@@ -20,7 +21,6 @@ encommon/config/logger.py
20
21
  encommon/config/params.py
21
22
  encommon/config/paths.py
22
23
  encommon/config/test/__init__.py
23
- encommon/config/test/conftest.py
24
24
  encommon/config/test/test_common.py
25
25
  encommon/config/test/test_config.py
26
26
  encommon/config/test/test_files.py
@@ -50,9 +50,11 @@ encommon/times/test/test_window.py
50
50
  encommon/types/__init__.py
51
51
  encommon/types/dicts.py
52
52
  encommon/types/empty.py
53
+ encommon/types/strings.py
53
54
  encommon/types/test/__init__.py
54
55
  encommon/types/test/test_dicts.py
55
56
  encommon/types/test/test_empty.py
57
+ encommon/types/test/test_strings.py
56
58
  encommon/utils/__init__.py
57
59
  encommon/utils/common.py
58
60
  encommon/utils/match.py
@@ -1 +0,0 @@
1
- 0.4.0
File without changes
File without changes
File without changes
@@ -130,8 +130,8 @@ class Timers:
130
130
  Update the existing timer from mapping within the cache.
131
131
 
132
132
  :param unique: Unique identifier for the timer in mapping.
133
- :param started: Determines when the time starts for timer.
134
133
  :param minimum: Determines minimum seconds that must pass.
134
+ :param started: Determines when the time starts for timer.
135
135
  """
136
136
 
137
137
  timer = self.__timing
File without changes
File without changes
File without changes