encommon 0.7.6__py3-none-any.whl → 0.8.1__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 (53) hide show
  1. encommon/config/__init__.py +6 -0
  2. encommon/config/common.py +18 -14
  3. encommon/config/config.py +21 -14
  4. encommon/config/files.py +13 -7
  5. encommon/config/logger.py +94 -88
  6. encommon/config/params.py +4 -4
  7. encommon/config/paths.py +16 -8
  8. encommon/config/test/test_common.py +27 -4
  9. encommon/config/test/test_config.py +48 -82
  10. encommon/config/test/test_files.py +58 -43
  11. encommon/config/test/test_logger.py +129 -82
  12. encommon/config/test/test_paths.py +70 -30
  13. encommon/conftest.py +52 -12
  14. encommon/crypts/__init__.py +2 -0
  15. encommon/crypts/crypts.py +3 -1
  16. encommon/crypts/hashes.py +2 -2
  17. encommon/crypts/test/test_crypts.py +50 -28
  18. encommon/crypts/test/test_hashes.py +20 -18
  19. encommon/times/__init__.py +2 -0
  20. encommon/times/common.py +99 -15
  21. encommon/times/duration.py +50 -36
  22. encommon/times/parse.py +13 -25
  23. encommon/times/test/test_common.py +47 -16
  24. encommon/times/test/test_duration.py +104 -79
  25. encommon/times/test/test_parse.py +53 -63
  26. encommon/times/test/test_timers.py +90 -36
  27. encommon/times/test/test_times.py +21 -30
  28. encommon/times/test/test_window.py +73 -21
  29. encommon/times/timers.py +91 -58
  30. encommon/times/times.py +36 -34
  31. encommon/times/window.py +4 -4
  32. encommon/types/dicts.py +10 -4
  33. encommon/types/empty.py +7 -2
  34. encommon/types/strings.py +10 -0
  35. encommon/types/test/test_dicts.py +5 -5
  36. encommon/types/test/test_empty.py +4 -1
  37. encommon/types/test/test_strings.py +1 -1
  38. encommon/utils/__init__.py +4 -0
  39. encommon/utils/common.py +51 -6
  40. encommon/utils/match.py +5 -4
  41. encommon/utils/paths.py +42 -23
  42. encommon/utils/sample.py +31 -27
  43. encommon/utils/stdout.py +28 -17
  44. encommon/utils/test/test_common.py +35 -0
  45. encommon/utils/test/test_paths.py +3 -2
  46. encommon/utils/test/test_sample.py +28 -12
  47. encommon/version.txt +1 -1
  48. {encommon-0.7.6.dist-info → encommon-0.8.1.dist-info}/METADATA +1 -1
  49. encommon-0.8.1.dist-info/RECORD +63 -0
  50. encommon-0.7.6.dist-info/RECORD +0 -62
  51. {encommon-0.7.6.dist-info → encommon-0.8.1.dist-info}/LICENSE +0 -0
  52. {encommon-0.7.6.dist-info → encommon-0.8.1.dist-info}/WHEEL +0 -0
  53. {encommon-0.7.6.dist-info → encommon-0.8.1.dist-info}/top_level.txt +0 -0
encommon/times/timers.py CHANGED
@@ -20,10 +20,9 @@ if TYPE_CHECKING:
20
20
 
21
21
 
22
22
 
23
- TABLE = (
23
+ CACHE_TABLE = (
24
24
  """
25
- create table
26
- if not exists
25
+ create table if not exists
27
26
  {0} (
28
27
  "unique" text not null,
29
28
  "update" text not null,
@@ -32,6 +31,11 @@ TABLE = (
32
31
 
33
32
 
34
33
 
34
+ _TIMERS = dict[str, float]
35
+ _CACHED = dict[str, Times]
36
+
37
+
38
+
35
39
  class Timers:
36
40
  """
37
41
  Track timers on unique key and determine when to proceed.
@@ -53,51 +57,57 @@ class Timers:
53
57
  True
54
58
 
55
59
  :param timers: Seconds that are used for each of timers.
56
- :param cache_file: Optional path to SQLite database for
60
+ :param file: Optional path to SQLite database for
57
61
  cache. This will allow for use between executions.
58
- :param cache_name: Optional override default table name.
62
+ :param table: Optional override default table name.
59
63
  """
60
64
 
61
- __timers: dict[str, float]
62
- __cache_file: 'Connection'
63
- __cache_name: str
64
- __cache_dict: dict[str, Times]
65
+ __config: _TIMERS
66
+ __sqlite: 'Connection'
67
+ __file: str
68
+ __table: str
69
+ __cache: _CACHED
65
70
 
66
71
 
67
72
  def __init__(
68
73
  self,
69
74
  timers: Optional[dict[str, NUMERIC]] = None,
70
- cache_file: str = ':memory:',
71
- cache_name: str = 'encommon_timers',
75
+ file: str = ':memory:',
76
+ table: str = 'timers',
72
77
  ) -> None:
73
78
  """
74
79
  Initialize instance for class using provided parameters.
75
80
  """
76
81
 
77
- timers = timers or {}
78
82
 
83
+ timers = dict(timers or {})
79
84
 
80
- self.__timers = {
81
- k: float(v)
82
- for k, v in
83
- timers.items()}
85
+ items = timers.items()
84
86
 
87
+ for key, value in items:
88
+ timers[key] = float(value)
85
89
 
86
- cached = SQLite(cache_file)
87
90
 
88
- cached.execute(
89
- TABLE.format(cache_name))
91
+ sqlite = SQLite(file)
90
92
 
91
- cached.commit()
93
+ sqlite.execute(
94
+ CACHE_TABLE
95
+ .format(table))
96
+
97
+ sqlite.commit()
98
+
99
+
100
+ cached: _CACHED = {}
92
101
 
93
- self.__cache_file = cached
94
- self.__cache_name = cache_name
102
+ for timer in timers:
103
+ cached[timer] = Times()
95
104
 
96
105
 
97
- self.__cache_dict = {
98
- x: Times()
99
- for x in
100
- self.__timers}
106
+ self.__config = timers
107
+ self.__sqlite = sqlite
108
+ self.__file = file
109
+ self.__table = table
110
+ self.__cache = cached
101
111
 
102
112
 
103
113
  self.load_cache()
@@ -111,9 +121,9 @@ class Timers:
111
121
  Load the timers cache from the database into attribute.
112
122
  """
113
123
 
114
- cached = self.__cache_file
115
- table = self.__cache_name
116
- cachem = self.__cache_dict
124
+ cached = self.__sqlite
125
+ table = self.__table
126
+ cachem = self.__cache
117
127
 
118
128
  cursor = cached.execute(
119
129
  f'select * from {table}'
@@ -138,19 +148,29 @@ class Timers:
138
148
  Save the timers cache from the attribute into database.
139
149
  """
140
150
 
141
- cached = self.__cache_file
142
- table = self.__cache_name
143
- cachem = self.__cache_dict
151
+ insert = tuple[str, str]
152
+ inserts: list[insert] = []
153
+
154
+
155
+ cached = self.__sqlite
156
+ table = self.__table
157
+ cachem = self.__cache
158
+
159
+
160
+ items = cachem.items()
161
+
162
+ for key, value in items:
163
+
164
+ append = (key, str(value))
165
+
166
+ inserts.append(append)
144
167
 
145
- insert = [
146
- (k, str(v)) for k, v
147
- in cachem.items()]
148
168
 
149
169
  cached.executemany(
150
170
  (f'replace into {table}'
151
171
  ' ("unique", "update")'
152
172
  ' values (?, ?)'),
153
- tuple(insert))
173
+ tuple(sorted(inserts)))
154
174
 
155
175
  cached.commit()
156
176
 
@@ -158,18 +178,18 @@ class Timers:
158
178
  @property
159
179
  def timers(
160
180
  self,
161
- ) -> dict[str, float]:
181
+ ) -> _TIMERS:
162
182
  """
163
183
  Return the value for the attribute from class instance.
164
184
 
165
185
  :returns: Value for the attribute from class instance.
166
186
  """
167
187
 
168
- return dict(self.__timers)
188
+ return dict(self.__config)
169
189
 
170
190
 
171
191
  @property
172
- def cache_file(
192
+ def sqlite(
173
193
  self,
174
194
  ) -> 'Connection':
175
195
  """
@@ -178,24 +198,24 @@ class Timers:
178
198
  :returns: Value for the attribute from class instance.
179
199
  """
180
200
 
181
- return self.__cache_file
201
+ return self.__sqlite
182
202
 
183
203
 
184
204
  @property
185
- def cache_dict(
205
+ def file(
186
206
  self,
187
- ) -> dict[str, Times]:
207
+ ) -> str:
188
208
  """
189
209
  Return the value for the attribute from class instance.
190
210
 
191
211
  :returns: Value for the attribute from class instance.
192
212
  """
193
213
 
194
- return dict(self.__cache_dict)
214
+ return self.__file
195
215
 
196
216
 
197
217
  @property
198
- def cache_name(
218
+ def table(
199
219
  self,
200
220
  ) -> str:
201
221
  """
@@ -204,7 +224,20 @@ class Timers:
204
224
  :returns: Value for the attribute from class instance.
205
225
  """
206
226
 
207
- return self.__cache_name
227
+ return self.__table
228
+
229
+
230
+ @property
231
+ def cache(
232
+ self,
233
+ ) -> _CACHED:
234
+ """
235
+ Return the value for the attribute from class instance.
236
+
237
+ :returns: Value for the attribute from class instance.
238
+ """
239
+
240
+ return dict(self.__cache)
208
241
 
209
242
 
210
243
  def ready(
@@ -219,18 +252,18 @@ class Timers:
219
252
  For performance reasons, this method will not notice
220
253
  changes within the database unless refreshed first.
221
254
 
222
- :param unique: Unique identifier for the timer in mapping.
255
+ :param unique: Which timer configuration from reference.
223
256
  :param update: Determines whether or not time is updated.
224
257
  """
225
258
 
226
- timers = self.__timers
227
- caches = self.__cache_dict
259
+ config = self.__config
260
+ caches = self.__cache
228
261
 
229
262
  if unique not in caches:
230
263
  raise ValueError('unique')
231
264
 
232
265
  cache = caches[unique]
233
- timer = timers[unique]
266
+ timer = config[unique]
234
267
 
235
268
  ready = cache.since >= timer
236
269
 
@@ -248,11 +281,11 @@ class Timers:
248
281
  """
249
282
  Update the existing timer from mapping within the cache.
250
283
 
251
- :param unique: Unique identifier for the timer in mapping.
284
+ :param unique: Which timer configuration from reference.
252
285
  :param started: Override the start time for timer value.
253
286
  """
254
287
 
255
- caches = self.__cache_dict
288
+ caches = self.__cache
256
289
 
257
290
  if unique not in caches:
258
291
  raise ValueError('unique')
@@ -271,18 +304,18 @@ class Timers:
271
304
  """
272
305
  Update the existing timer from mapping within the cache.
273
306
 
274
- :param unique: Unique identifier for the timer in mapping.
275
- :param minimum: Determines minimum seconds that must pass.
276
- :param started: Determines when the time starts for timer.
307
+ :param unique: Which timer configuration from reference.
308
+ :param minimum: Determine minimum seconds that must pass.
309
+ :param started: Determine when time starts for the timer.
277
310
  """
278
311
 
279
- timers = self.__timers
280
- caches = self.__cache_dict
312
+ config = self.__config
313
+ caches = self.__cache
281
314
 
282
- if unique in timers:
315
+ if unique in config:
283
316
  raise ValueError('unique')
284
317
 
285
- timers[unique] = float(minimum)
318
+ config[unique] = float(minimum)
286
319
  caches[unique] = Times(started)
287
320
 
288
321
  self.save_cache()
encommon/times/times.py CHANGED
@@ -7,17 +7,17 @@ is permitted, for more information consult the project license file.
7
7
 
8
8
 
9
9
 
10
+ from contextlib import suppress
10
11
  from datetime import datetime
11
12
  from datetime import timedelta
12
- from datetime import timezone
13
13
  from typing import Optional
14
14
 
15
- from dateutil.tz import gettz
16
-
17
15
  from .common import PARSABLE
18
16
  from .common import STAMP_HUMAN
19
17
  from .common import STAMP_SIMPLE
20
18
  from .common import STAMP_SUBSEC
19
+ from .common import findtz
20
+ from .common import strftime
21
21
  from .parse import parse_time
22
22
  from .parse import since_time
23
23
 
@@ -40,6 +40,7 @@ class Times:
40
40
  """
41
41
 
42
42
  __source: datetime
43
+ __hashed: int
43
44
 
44
45
 
45
46
  def __init__(
@@ -54,12 +55,16 @@ class Times:
54
55
  Initialize instance for class using provided parameters.
55
56
  """
56
57
 
57
- self.__source = parse_time(
58
+ parsed = parse_time(
58
59
  source=source,
59
60
  anchor=anchor,
60
61
  format=format,
61
62
  tzname=tzname)
62
63
 
64
+ self.__source = parsed
65
+ self.__hashed = int(
66
+ self.mpoch * 1000)
67
+
63
68
 
64
69
  def __repr__(
65
70
  self,
@@ -82,7 +87,7 @@ class Times:
82
87
  :returns: Boolean indicating outcome from the operation.
83
88
  """
84
89
 
85
- return int(self.mpoch * 100000)
90
+ return int(1e9 + self.__hashed)
86
91
 
87
92
 
88
93
  def __str__(
@@ -103,7 +108,7 @@ class Times:
103
108
  """
104
109
  Built-in method representing numeric value for instance.
105
110
 
106
- :returns: Numeric representation for values from instance.
111
+ :returns: Numeric representation for value in instance.
107
112
  """
108
113
 
109
114
  return int(self.epoch)
@@ -115,7 +120,7 @@ class Times:
115
120
  """
116
121
  Built-in method representing numeric value for instance.
117
122
 
118
- :returns: Numeric representation for values from instance.
123
+ :returns: Numeric representation for value in instance.
119
124
  """
120
125
 
121
126
  return float(self.epoch)
@@ -168,13 +173,16 @@ class Times:
168
173
  :returns: Boolean indicating outcome from the operation.
169
174
  """
170
175
 
171
- try:
172
- parsed = parse_time(other) # type: ignore
176
+ source = self.__source
177
+
178
+ with suppress(Exception):
173
179
 
174
- except Exception:
175
- return False
180
+ parsed = parse_time(
181
+ other) # type: ignore
176
182
 
177
- return self.__source == parsed
183
+ return source == parsed
184
+
185
+ return False
178
186
 
179
187
 
180
188
  def __ne__(
@@ -278,7 +286,9 @@ class Times:
278
286
  :returns: Seconds since the Unix epoch for the instance.
279
287
  """
280
288
 
281
- return self.__source.timestamp()
289
+ source = self.__source
290
+
291
+ return source.timestamp()
282
292
 
283
293
 
284
294
  @property
@@ -369,10 +379,10 @@ class Times:
369
379
  :returns: Object containing time just before the time.
370
380
  """
371
381
 
372
- delta = timedelta(
373
- microseconds=1)
382
+ source = self.__source
374
383
 
375
- source = self.__source - delta
384
+ source -= timedelta(
385
+ microseconds=1)
376
386
 
377
387
  return Times(source)
378
388
 
@@ -387,10 +397,10 @@ class Times:
387
397
  :returns: Object containing time just after the time.
388
398
  """
389
399
 
390
- delta = timedelta(
391
- microseconds=1)
400
+ source = self.__source
392
401
 
393
- source = self.__source + delta
402
+ source += timedelta(
403
+ microseconds=1)
394
404
 
395
405
  return Times(source)
396
406
 
@@ -409,21 +419,13 @@ class Times:
409
419
  :returns: Timestamp using provided format for instance.
410
420
  """
411
421
 
412
- parsed = self.__source
422
+ source = self.__source
413
423
 
424
+ tzinfo = findtz(tzname)
414
425
 
415
- if tzname is not None:
416
- tzinfo = gettz(tzname)
417
- else:
418
- tzinfo = timezone.utc
426
+ parsed = source.astimezone(tzinfo)
419
427
 
420
- if tzinfo is None:
421
- raise ValueError('tzname')
422
-
423
-
424
- return datetime.strftime(
425
- parsed.astimezone(tzinfo),
426
- format)
428
+ return strftime(parsed, format)
427
429
 
428
430
 
429
431
  def shift(
@@ -437,6 +439,6 @@ class Times:
437
439
  :returns: New instance of the class using shifted time.
438
440
  """
439
441
 
440
- return Times(
441
- source=notate,
442
- anchor=self.__source)
442
+ source = self.__source
443
+
444
+ return Times(notate, anchor=source)
encommon/times/window.py CHANGED
@@ -170,7 +170,7 @@ class Window:
170
170
 
171
171
 
172
172
  @property
173
- def next(
173
+ def last(
174
174
  self,
175
175
  ) -> Times:
176
176
  """
@@ -179,11 +179,11 @@ class Window:
179
179
  :returns: Value for the attribute from class instance.
180
180
  """
181
181
 
182
- return Times(self.__winext)
182
+ return Times(self.__wilast)
183
183
 
184
184
 
185
185
  @property
186
- def last(
186
+ def next(
187
187
  self,
188
188
  ) -> Times:
189
189
  """
@@ -192,7 +192,7 @@ class Window:
192
192
  :returns: Value for the attribute from class instance.
193
193
  """
194
194
 
195
- return Times(self.__wilast)
195
+ return Times(self.__winext)
196
196
 
197
197
 
198
198
  @property
encommon/types/dicts.py CHANGED
@@ -49,12 +49,16 @@ def merge_dicts(
49
49
  elif (isinstance(dict1[key], list)
50
50
  and isinstance(value, list)
51
51
  and merge_list is True):
52
- dict1[key] = [] + dict1[key] + value
52
+
53
+ dict1[key] = (
54
+ [] + dict1[key] + value)
53
55
 
54
56
  elif (isinstance(dict1[key], dict)
55
57
  and isinstance(value, dict)
56
58
  and merge_dict is True):
57
- merge_dicts(dict1[key], value, force)
59
+
60
+ merge_dicts(
61
+ dict1[key], value, force)
58
62
 
59
63
  elif force is True:
60
64
  dict1[key] = value
@@ -81,6 +85,8 @@ def sort_dict(
81
85
  :returns: New dictionary with keys sorted alphabetical.
82
86
  """
83
87
 
84
- return dict(sorted(
88
+ items = sorted(
85
89
  value.items(),
86
- reverse=reverse))
90
+ reverse=reverse)
91
+
92
+ return dict(items)
encommon/types/empty.py CHANGED
@@ -51,8 +51,13 @@ class EmptyType:
51
51
  :returns: Same instance of class that first instantiated.
52
52
  """
53
53
 
54
- if cls.__empty is None:
55
- cls.__empty = object.__new__(cls)
54
+ empty = cls.__empty
55
+
56
+ if empty is not None:
57
+ return empty
58
+
59
+ cls.__empty = (
60
+ object.__new__(cls))
56
61
 
57
62
  return cls.__empty
58
63
 
encommon/types/strings.py CHANGED
@@ -7,6 +7,16 @@ is permitted, for more information consult the project license file.
7
7
 
8
8
 
9
9
 
10
+ COMMAS = ', '
11
+ COMMAD = ','
12
+
13
+ NEWLINE = '\n'
14
+
15
+ SEMPTY = ''
16
+ SPACED = ' '
17
+
18
+
19
+
10
20
  def striplower(
11
21
  value: str,
12
22
  ) -> str:
@@ -14,14 +14,14 @@ from ..dicts import sort_dict
14
14
 
15
15
 
16
16
 
17
- DICT1 = {
17
+ _DICT1 = {
18
18
  'dict1': 'dict1',
19
19
  'str': 'd1string',
20
20
  'list': ['d1list'],
21
21
  'dict': {'key': 'd1value'},
22
22
  'bool': False}
23
23
 
24
- DICT2 = {
24
+ _DICT2 = {
25
25
  'dict2': 'dict2',
26
26
  'str': 'd2string',
27
27
  'list': ['d2list'],
@@ -35,8 +35,8 @@ def test_merge_dicts() -> None:
35
35
  Perform various tests associated with relevant routines.
36
36
  """
37
37
 
38
- dict1 = deepcopy(DICT1)
39
- dict2 = deepcopy(DICT2)
38
+ dict1 = deepcopy(_DICT1)
39
+ dict2 = deepcopy(_DICT2)
40
40
 
41
41
  dict1['recurse'] = deepcopy(dict1)
42
42
  dict2['recurse'] = deepcopy(dict2)
@@ -114,7 +114,7 @@ def test_sort_dict() -> None:
114
114
  Perform various tests associated with relevant routines.
115
115
  """
116
116
 
117
- assert sort_dict(DICT1) == {
117
+ assert sort_dict(_DICT1) == {
118
118
  'bool': False,
119
119
  'dict': {'key': 'd1value'},
120
120
  'dict1': 'dict1',
@@ -22,15 +22,18 @@ def test_EmptyType() -> None:
22
22
 
23
23
  empty = EmptyType()
24
24
 
25
+
25
26
  attrs = list(empty.__dict__)
26
27
 
27
28
  assert attrs == [
28
29
  '_EmptyType__empty']
29
30
 
31
+
30
32
  assert repr(empty) == 'Empty'
31
- assert isinstance(hash(empty), int)
33
+ assert hash(empty) > 0
32
34
  assert str(empty) == 'Empty'
33
35
 
36
+
34
37
  assert not (Empty or None)
35
38
  assert Empty is empty
36
39
  assert Empty is Empty
@@ -16,4 +16,4 @@ def test_striplower() -> None:
16
16
  Perform various tests associated with relevant routines.
17
17
  """
18
18
 
19
- assert striplower(' Foo ') == 'foo'
19
+ assert striplower(' Foo ') == 'foo'
@@ -7,6 +7,8 @@ is permitted, for more information consult the project license file.
7
7
 
8
8
 
9
9
 
10
+ from .common import read_text
11
+ from .common import save_text
10
12
  from .match import fuzz_match
11
13
  from .match import rgxp_match
12
14
  from .paths import resolve_path
@@ -30,8 +32,10 @@ __all__ = [
30
32
  'make_ansi',
31
33
  'prep_sample',
32
34
  'print_ansi',
35
+ 'read_text',
33
36
  'resolve_path',
34
37
  'resolve_paths',
35
38
  'rgxp_match',
39
+ 'save_text',
36
40
  'stats_path',
37
41
  'strip_ansi']