encommon 0.10.0__py3-none-any.whl → 0.11.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 (45) hide show
  1. encommon/config/__init__.py +3 -3
  2. encommon/config/config.py +19 -7
  3. encommon/config/files.py +3 -3
  4. encommon/config/logger.py +39 -7
  5. encommon/config/params.py +21 -1
  6. encommon/config/paths.py +2 -2
  7. encommon/config/test/test_logger.py +4 -1
  8. encommon/config/test/{test_common.py → test_utils.py} +3 -3
  9. encommon/config/{common.py → utils.py} +1 -10
  10. encommon/conftest.py +2 -1
  11. encommon/crypts/crypts.py +68 -18
  12. encommon/crypts/params.py +14 -1
  13. encommon/crypts/test/test_crypts.py +56 -13
  14. encommon/times/__init__.py +14 -2
  15. encommon/times/common.py +0 -127
  16. encommon/times/params.py +155 -0
  17. encommon/times/parse.py +5 -5
  18. encommon/times/test/test_params.py +64 -0
  19. encommon/times/test/test_parse.py +1 -1
  20. encommon/times/test/test_timer.py +86 -0
  21. encommon/times/test/test_timers.py +81 -41
  22. encommon/times/test/{test_common.py → test_utils.py} +3 -3
  23. encommon/times/test/test_window.py +101 -51
  24. encommon/times/test/test_windows.py +250 -0
  25. encommon/times/timer.py +147 -0
  26. encommon/times/timers.py +217 -130
  27. encommon/times/times.py +6 -6
  28. encommon/times/utils.py +148 -0
  29. encommon/times/window.py +124 -85
  30. encommon/times/windows.py +472 -0
  31. encommon/types/notate.py +0 -2
  32. encommon/utils/__init__.py +2 -2
  33. encommon/utils/common.py +0 -39
  34. encommon/utils/files.py +71 -0
  35. encommon/utils/paths.py +1 -1
  36. encommon/utils/sample.py +2 -2
  37. encommon/utils/test/{test_common.py → test_files.py} +2 -2
  38. encommon/utils/test/test_paths.py +2 -1
  39. encommon/version.txt +1 -1
  40. {encommon-0.10.0.dist-info → encommon-0.11.1.dist-info}/METADATA +1 -1
  41. encommon-0.11.1.dist-info/RECORD +73 -0
  42. encommon-0.10.0.dist-info/RECORD +0 -65
  43. {encommon-0.10.0.dist-info → encommon-0.11.1.dist-info}/LICENSE +0 -0
  44. {encommon-0.10.0.dist-info → encommon-0.11.1.dist-info}/WHEEL +0 -0
  45. {encommon-0.10.0.dist-info → encommon-0.11.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,472 @@
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 copy import deepcopy
11
+ from sqlite3 import Connection
12
+ from sqlite3 import connect as SQLite
13
+ from typing import Optional
14
+ from typing import TYPE_CHECKING
15
+
16
+ from .common import PARSABLE
17
+ from .times import Times
18
+ from .window import Window
19
+
20
+ if TYPE_CHECKING:
21
+ from .params import WindowParams
22
+ from .params import WindowsParams
23
+
24
+
25
+
26
+ CACHE_TABLE = (
27
+ """
28
+ create table if not exists
29
+ {0} (
30
+ "group" text not null,
31
+ "unique" text not null,
32
+ "last" text not null,
33
+ "next" text not null,
34
+ "update" text not null,
35
+ primary key (
36
+ "group", "unique"));
37
+ """) # noqa: LIT003
38
+
39
+
40
+
41
+ WINDOWS = dict[str, Window]
42
+
43
+
44
+
45
+ class Windows:
46
+ """
47
+ Track windows on unique key determining when to proceed.
48
+
49
+ .. warning::
50
+ This class will use an in-memory database for cache,
51
+ unless a cache file is explicity defined.
52
+
53
+ .. testsetup::
54
+ >>> from .params import WindowParams
55
+ >>> from .params import WindowsParams
56
+
57
+ Example
58
+ -------
59
+ >>> source = {'one': WindowParams(window=1)}
60
+ >>> params = WindowsParams(windows=source)
61
+ >>> windows = Windows(params, '-2s', 'now')
62
+ >>> [windows.ready('one') for x in range(3)]
63
+ [True, True, False]
64
+
65
+ :param params: Parameters for instantiating the instance.
66
+ :param start: Determine the start for scheduling window.
67
+ :param stop: Determine the ending for scheduling window.
68
+ :param file: Optional path to file for SQLite database,
69
+ allowing for state retention between the executions.
70
+ :param table: Optional override for default table name.
71
+ :param group: Optional override for default group name.
72
+ """
73
+
74
+ __params: 'WindowsParams'
75
+
76
+ __start: Times
77
+ __stop: Times
78
+
79
+ __sqlite: Connection
80
+ __file: str
81
+ __table: str
82
+ __group: str
83
+
84
+ __windows: WINDOWS
85
+
86
+
87
+ def __init__( # noqa: CFQ002
88
+ self,
89
+ params: Optional['WindowsParams'] = None,
90
+ start: PARSABLE = 'now',
91
+ stop: PARSABLE = '3000-01-01',
92
+ *,
93
+ file: str = ':memory:',
94
+ table: str = 'windows',
95
+ group: str = 'default',
96
+ ) -> None:
97
+ """
98
+ Initialize instance for class using provided parameters.
99
+ """
100
+
101
+ from .params import WindowsParams
102
+
103
+ if params is None:
104
+ params = WindowsParams()
105
+
106
+ self.__params = deepcopy(params)
107
+
108
+
109
+ start = Times(start)
110
+ stop = Times(stop)
111
+
112
+ assert stop > start
113
+
114
+ self.__start = start
115
+ self.__stop = stop
116
+
117
+
118
+ sqlite = SQLite(file)
119
+
120
+ sqlite.execute(
121
+ CACHE_TABLE
122
+ .format(table))
123
+
124
+ sqlite.commit()
125
+
126
+ self.__sqlite = sqlite
127
+ self.__file = file
128
+ self.__table = table
129
+ self.__group = group
130
+
131
+
132
+ self.__windows = {}
133
+
134
+ self.load_children()
135
+
136
+
137
+ @property
138
+ def params(
139
+ self,
140
+ ) -> 'WindowsParams':
141
+ """
142
+ Return the Pydantic model containing the configuration.
143
+
144
+ :returns: Pydantic model containing the configuration.
145
+ """
146
+
147
+ return self.__params
148
+
149
+
150
+ @property
151
+ def start(
152
+ self,
153
+ ) -> Times:
154
+ """
155
+ Return the value for the attribute from class instance.
156
+
157
+ :returns: Value for the attribute from class instance.
158
+ """
159
+
160
+ return Times(self.__start)
161
+
162
+
163
+ @property
164
+ def stop(
165
+ self,
166
+ ) -> Times:
167
+ """
168
+ Return the value for the attribute from class instance.
169
+
170
+ :returns: Value for the attribute from class instance.
171
+ """
172
+
173
+ return Times(self.__stop)
174
+
175
+
176
+ @property
177
+ def sqlite(
178
+ self,
179
+ ) -> Connection:
180
+ """
181
+ Return the value for the attribute from class instance.
182
+
183
+ :returns: Value for the attribute from class instance.
184
+ """
185
+
186
+ return self.__sqlite
187
+
188
+
189
+ @property
190
+ def file(
191
+ self,
192
+ ) -> str:
193
+ """
194
+ Return the value for the attribute from class instance.
195
+
196
+ :returns: Value for the attribute from class instance.
197
+ """
198
+
199
+ return self.__file
200
+
201
+
202
+ @property
203
+ def table(
204
+ self,
205
+ ) -> str:
206
+ """
207
+ Return the value for the attribute from class instance.
208
+
209
+ :returns: Value for the attribute from class instance.
210
+ """
211
+
212
+ return self.__table
213
+
214
+
215
+ @property
216
+ def group(
217
+ self,
218
+ ) -> str:
219
+ """
220
+ Return the value for the attribute from class instance.
221
+
222
+ :returns: Value for the attribute from class instance.
223
+ """
224
+
225
+ return self.__group
226
+
227
+
228
+ @property
229
+ def children(
230
+ self,
231
+ ) -> dict[str, Window]:
232
+ """
233
+ Return the value for the attribute from class instance.
234
+
235
+ :returns: Value for the attribute from class instance.
236
+ """
237
+
238
+ return dict(self.__windows)
239
+
240
+
241
+ def load_children(
242
+ self,
243
+ ) -> None:
244
+ """
245
+ Construct the children instances for the primary class.
246
+ """
247
+
248
+ params = self.__params
249
+ windows = self.__windows
250
+
251
+ start = self.__start
252
+ stop = self.__stop
253
+
254
+ sqlite = self.__sqlite
255
+ table = self.__table
256
+ group = self.__group
257
+
258
+
259
+ config = params.windows
260
+
261
+
262
+ cursor = sqlite.execute(
263
+ f"""
264
+ select * from {table}
265
+ where "group"="{group}"
266
+ order by "unique" asc
267
+ """) # noqa: LIT003
268
+
269
+ records = cursor.fetchall()
270
+
271
+ for record in records:
272
+
273
+ unique = record[1]
274
+ next = record[3]
275
+
276
+ if unique not in config:
277
+ continue
278
+
279
+ _config = config[unique]
280
+
281
+ _config.start = next
282
+ _config.anchor = next
283
+
284
+
285
+ items = config.items()
286
+
287
+ for key, value in items:
288
+
289
+ if key in windows:
290
+
291
+ window = windows[key]
292
+
293
+ window.update(
294
+ value.start)
295
+
296
+ continue
297
+
298
+ _start = (
299
+ value.start or start)
300
+
301
+ _stop = (
302
+ value.stop or stop)
303
+
304
+ if _start < start:
305
+ _start = start
306
+
307
+ if _stop > stop:
308
+ _stop = stop
309
+
310
+ _anchor = (
311
+ value.anchor or _start)
312
+
313
+ window = Window(
314
+ value.window,
315
+ start=_start,
316
+ stop=_stop,
317
+ anchor=_anchor,
318
+ delay=value.delay)
319
+
320
+ windows[key] = window
321
+
322
+
323
+ self.__windows = windows
324
+
325
+
326
+ def save_children(
327
+ self,
328
+ ) -> None:
329
+ """
330
+ Save the child caches from the attribute into database.
331
+ """
332
+
333
+ windows = self.__windows
334
+
335
+ sqlite = self.__sqlite
336
+ table = self.__table
337
+ group = self.__group
338
+
339
+
340
+ insert = tuple[
341
+ str, # group
342
+ str, # unique
343
+ str, # last
344
+ str, # next
345
+ str] # update
346
+
347
+ inserts: list[insert] = []
348
+
349
+ items = windows.items()
350
+
351
+ for unique, window in items:
352
+
353
+ append = (
354
+ group, unique,
355
+ window.last.subsec,
356
+ window.next.subsec,
357
+ Times('now').subsec)
358
+
359
+ inserts.append(append)
360
+
361
+
362
+ statement = (
363
+ f"""
364
+ replace into {table}
365
+ ("group", "unique",
366
+ "next", "last",
367
+ "update")
368
+ values (?, ?, ?, ?, ?)
369
+ """) # noqa: LIT003
370
+
371
+ sqlite.executemany(
372
+ statement,
373
+ tuple(sorted(inserts)))
374
+
375
+ sqlite.commit()
376
+
377
+
378
+ def ready(
379
+ self,
380
+ unique: str,
381
+ update: bool = True,
382
+ ) -> bool:
383
+ """
384
+ Determine whether or not the appropriate time has passed.
385
+
386
+ :param unique: Unique identifier for the related child.
387
+ :param update: Determines whether or not time is updated.
388
+ :returns: Boolean indicating whether enough time passed.
389
+ """
390
+
391
+ windows = self.__windows
392
+
393
+ if unique not in windows:
394
+ raise ValueError('unique')
395
+
396
+ window = windows[unique]
397
+
398
+ ready = window.ready(update)
399
+
400
+ if ready is True:
401
+ self.save_children()
402
+
403
+ return ready
404
+
405
+
406
+ def create(
407
+ self,
408
+ unique: str,
409
+ params: 'WindowParams',
410
+ ) -> Window:
411
+ """
412
+ Create a new window using the provided input parameters.
413
+
414
+ :param unique: Unique identifier for the related child.
415
+ :param params: Parameters for instantiating the instance.
416
+ :returns: Newly constructed instance of related class.
417
+ """
418
+
419
+ windows = self.params.windows
420
+
421
+ if unique in windows:
422
+ raise ValueError('unique')
423
+
424
+ windows[unique] = params
425
+
426
+ self.load_children()
427
+
428
+ self.save_children()
429
+
430
+ return self.children[unique]
431
+
432
+
433
+ def update(
434
+ self,
435
+ unique: str,
436
+ value: Optional[PARSABLE] = None,
437
+ ) -> None:
438
+ """
439
+ Update the window from the provided parasable time value.
440
+
441
+ :param unique: Unique identifier for the related child.
442
+ :param value: Override the time updated for window value.
443
+ """
444
+
445
+ windows = self.__windows
446
+
447
+ if unique not in windows:
448
+ raise ValueError('unique')
449
+
450
+ window = windows[unique]
451
+
452
+ self.save_children()
453
+
454
+ return window.update(value)
455
+
456
+
457
+ def delete(
458
+ self,
459
+ unique: str,
460
+ ) -> None:
461
+ """
462
+ Delete the window from the internal dictionary reference.
463
+
464
+ :param unique: Unique identifier for the related child.
465
+ """
466
+
467
+ windows = self.__windows
468
+
469
+ if unique not in windows:
470
+ raise ValueError('unique')
471
+
472
+ del windows[unique]
encommon/types/notate.py CHANGED
@@ -208,7 +208,6 @@ def _setpath(
208
208
  :param path: Path to the value within the source object.
209
209
  :param value: Value which will be defined at noted point.
210
210
  :param delim: Override default delimiter between parts.
211
- :returns: Original provided source containing the value.
212
211
  """
213
212
 
214
213
 
@@ -285,7 +284,6 @@ def _setvalue(
285
284
  :param path: Path to the value within the source object.
286
285
  :param value: Value which will be defined at noted point.
287
286
  :param delim: Override default delimiter between parts.
288
- :returns: Original provided source containing the value.
289
287
  """
290
288
 
291
289
 
@@ -7,8 +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
+ from .files import read_text
11
+ from .files import save_text
12
12
  from .match import fuzz_match
13
13
  from .match import rgxp_match
14
14
  from .paths import resolve_path
encommon/utils/common.py CHANGED
@@ -31,42 +31,3 @@ REPLACE = Union[
31
31
  dict[str, str],
32
32
  dict[str | Path, str],
33
33
  dict[Path, str]]
34
-
35
-
36
-
37
- def read_text(
38
- path: str | Path,
39
- ) -> str:
40
- """
41
- Read the text content from within the provided file path.
42
-
43
- :param path: Complete or relative path to the text file.
44
- :returns: Text content that was read from the file path.
45
- """
46
-
47
- path = Path(path).resolve()
48
-
49
- return path.read_text(
50
- encoding='utf-8')
51
-
52
-
53
-
54
- def save_text(
55
- path: str | Path,
56
- content: str,
57
- ) -> str:
58
- """
59
- Save the provided text content to the provided file path.
60
-
61
- :param path: Complete or relative path to the text file.
62
- :param content: Content that will be written to the file.
63
- :returns: Text content that was read from the file path.
64
- """
65
-
66
- path = Path(path).resolve()
67
-
68
- path.write_text(
69
- data=content,
70
- encoding='utf-8')
71
-
72
- return read_text(path)
@@ -0,0 +1,71 @@
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 pathlib import Path
11
+
12
+
13
+
14
+ def read_text(
15
+ path: str | Path,
16
+ ) -> str:
17
+ """
18
+ Read the text content from within the provided file path.
19
+
20
+ .. testsetup::
21
+ >>> tmpdir = getfixture('tmpdir')
22
+ >>> path = Path(f'{tmpdir}/text.txt')
23
+
24
+ Example
25
+ -------
26
+ >>> save_text(path, 'foo')
27
+ 'foo'
28
+ >>> read_text(path)
29
+ 'foo'
30
+
31
+ :param path: Complete or relative path to the text file.
32
+ :returns: Text content that was read from the file path.
33
+ """
34
+
35
+ path = Path(path).resolve()
36
+
37
+ return path.read_text(
38
+ encoding='utf-8')
39
+
40
+
41
+
42
+ def save_text(
43
+ path: str | Path,
44
+ content: str,
45
+ ) -> str:
46
+ """
47
+ Save the provided text content to the provided file path.
48
+
49
+ .. testsetup::
50
+ >>> tmpdir = getfixture('tmpdir')
51
+ >>> path = Path(f'{tmpdir}/text.txt')
52
+
53
+ Example
54
+ -------
55
+ >>> save_text(path, 'foo')
56
+ 'foo'
57
+ >>> read_text(path)
58
+ 'foo'
59
+
60
+ :param path: Complete or relative path to the text file.
61
+ :param content: Content that will be written to the file.
62
+ :returns: Text content that was read from the file path.
63
+ """
64
+
65
+ path = Path(path).resolve()
66
+
67
+ path.write_text(
68
+ data=content,
69
+ encoding='utf-8')
70
+
71
+ return read_text(path)
encommon/utils/paths.py CHANGED
@@ -110,7 +110,7 @@ def stats_path(
110
110
  Collect stats object for the complete or relative path.
111
111
 
112
112
  .. testsetup::
113
- >>> from . import save_text
113
+ >>> from .files import save_text
114
114
  >>> path = Path(getfixture('tmpdir'))
115
115
  >>> file = path.joinpath('hello.txt')
116
116
  >>> save_text(file, 'Hello world!')
encommon/utils/sample.py CHANGED
@@ -15,8 +15,8 @@ from typing import Callable
15
15
  from typing import Optional
16
16
  from typing import TYPE_CHECKING
17
17
 
18
- from . import read_text
19
- from . import save_text
18
+ from .files import read_text
19
+ from .files import save_text
20
20
 
21
21
  if TYPE_CHECKING:
22
22
  from .common import REPLACE
@@ -9,8 +9,8 @@ is permitted, for more information consult the project license file.
9
9
 
10
10
  from pathlib import Path
11
11
 
12
- from ..common import read_text
13
- from ..common import save_text
12
+ from ..files import read_text
13
+ from ..files import save_text
14
14
 
15
15
 
16
16
 
@@ -76,12 +76,13 @@ def test_stats_path() -> None:
76
76
  assert list(stats) == [
77
77
  '/utils/__init__.py',
78
78
  '/utils/common.py',
79
+ '/utils/files.py',
79
80
  '/utils/match.py',
80
81
  '/utils/paths.py',
81
82
  '/utils/sample.py',
82
83
  '/utils/stdout.py',
83
84
  '/utils/test/__init__.py',
84
- '/utils/test/test_common.py',
85
+ '/utils/test/test_files.py',
85
86
  '/utils/test/test_match.py',
86
87
  '/utils/test/test_paths.py',
87
88
  '/utils/test/test_sample.py',
encommon/version.txt CHANGED
@@ -1 +1 @@
1
- 0.10.0
1
+ 0.11.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: encommon
3
- Version: 0.10.0
3
+ Version: 0.11.1
4
4
  Summary: Enasis Network Common Library
5
5
  License: MIT
6
6
  Classifier: Programming Language :: Python :: 3