utilo 2.112.0__tar.gz → 2.112.2__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 (99) hide show
  1. {utilo-2.112.0/utilo.egg-info → utilo-2.112.2}/PKG-INFO +3 -3
  2. {utilo-2.112.0 → utilo-2.112.2}/pyproject.toml +6 -7
  3. {utilo-2.112.0 → utilo-2.112.2}/tests/test_file.py +26 -0
  4. {utilo-2.112.0 → utilo-2.112.2}/utilo/cacher.py +6 -2
  5. {utilo-2.112.0 → utilo-2.112.2}/utilo/file/__init__.py +29 -11
  6. {utilo-2.112.0 → utilo-2.112.2}/utilo/string/__init__.py +2 -0
  7. {utilo-2.112.0 → utilo-2.112.2/utilo.egg-info}/PKG-INFO +3 -3
  8. {utilo-2.112.0 → utilo-2.112.2}/utilo.egg-info/requires.txt +2 -2
  9. {utilo-2.112.0 → utilo-2.112.2}/LICENSE +0 -0
  10. {utilo-2.112.0 → utilo-2.112.2}/README +0 -0
  11. {utilo-2.112.0 → utilo-2.112.2}/setup.cfg +0 -0
  12. {utilo-2.112.0 → utilo-2.112.2}/tests/test_collection.py +0 -0
  13. {utilo-2.112.0 → utilo-2.112.2}/tests/test_convert.py +0 -0
  14. {utilo-2.112.0 → utilo-2.112.2}/tests/test_datetime.py +0 -0
  15. {utilo-2.112.0 → utilo-2.112.2}/tests/test_decorators.py +0 -0
  16. {utilo-2.112.0 → utilo-2.112.2}/tests/test_error.py +0 -0
  17. {utilo-2.112.0 → utilo-2.112.2}/tests/test_likelihood.py +0 -0
  18. {utilo-2.112.0 → utilo-2.112.2}/tests/test_logger.py +0 -0
  19. {utilo-2.112.0 → utilo-2.112.2}/tests/test_math.py +0 -0
  20. {utilo-2.112.0 → utilo-2.112.2}/tests/test_pages.py +0 -0
  21. {utilo-2.112.0 → utilo-2.112.2}/tests/test_process.py +0 -0
  22. {utilo-2.112.0 → utilo-2.112.2}/tests/test_quick.py +0 -0
  23. {utilo-2.112.0 → utilo-2.112.2}/tests/test_regex.py +0 -0
  24. {utilo-2.112.0 → utilo-2.112.2}/tests/test_string.py +0 -0
  25. {utilo-2.112.0 → utilo-2.112.2}/tests/test_todo.py +0 -0
  26. {utilo-2.112.0 → utilo-2.112.2}/tests/test_typechecker.py +0 -0
  27. {utilo-2.112.0 → utilo-2.112.2}/tests/test_utils.py +0 -0
  28. {utilo-2.112.0 → utilo-2.112.2}/tests/test_yaml.py +0 -0
  29. {utilo-2.112.0 → utilo-2.112.2}/utilo/__init__.py +0 -0
  30. {utilo-2.112.0 → utilo-2.112.2}/utilo/__todo__.py +0 -0
  31. {utilo-2.112.0 → utilo-2.112.2}/utilo/alpha.py +0 -0
  32. {utilo-2.112.0 → utilo-2.112.2}/utilo/baw.py +0 -0
  33. {utilo-2.112.0 → utilo-2.112.2}/utilo/classes.py +0 -0
  34. {utilo-2.112.0 → utilo-2.112.2}/utilo/classifier/__init__.py +0 -0
  35. {utilo-2.112.0 → utilo-2.112.2}/utilo/classifier/base.py +0 -0
  36. {utilo-2.112.0 → utilo-2.112.2}/utilo/classifier/public.py +0 -0
  37. {utilo-2.112.0 → utilo-2.112.2}/utilo/classifier/strategy.py +0 -0
  38. {utilo-2.112.0 → utilo-2.112.2}/utilo/cli/__init__.py +0 -0
  39. {utilo-2.112.0 → utilo-2.112.2}/utilo/collection.py +0 -0
  40. {utilo-2.112.0 → utilo-2.112.2}/utilo/color.py +0 -0
  41. {utilo-2.112.0 → utilo-2.112.2}/utilo/config.py +0 -0
  42. {utilo-2.112.0 → utilo-2.112.2}/utilo/convert.py +0 -0
  43. {utilo-2.112.0 → utilo-2.112.2}/utilo/datetime.py +0 -0
  44. {utilo-2.112.0 → utilo-2.112.2}/utilo/decorator.py +0 -0
  45. {utilo-2.112.0 → utilo-2.112.2}/utilo/error.py +0 -0
  46. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/__init__.py +0 -0
  47. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/cache.py +0 -0
  48. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/collector.py +0 -0
  49. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/config.py +0 -0
  50. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/cprofile.py +0 -0
  51. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/description.py +0 -0
  52. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/outpath.py +0 -0
  53. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/pack.py +0 -0
  54. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/path.py +0 -0
  55. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/processor.py +0 -0
  56. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/userinput.py +0 -0
  57. {utilo-2.112.0 → utilo-2.112.2}/utilo/feature/workplan.py +0 -0
  58. {utilo-2.112.0 → utilo-2.112.2}/utilo/file/action.py +0 -0
  59. {utilo-2.112.0 → utilo-2.112.2}/utilo/file/directory.py +0 -0
  60. {utilo-2.112.0 → utilo-2.112.2}/utilo/file/hashed.py +0 -0
  61. {utilo-2.112.0 → utilo-2.112.2}/utilo/file/info.py +0 -0
  62. {utilo-2.112.0 → utilo-2.112.2}/utilo/file/loader.py +0 -0
  63. {utilo-2.112.0 → utilo-2.112.2}/utilo/file/securewrapper.py +0 -0
  64. {utilo-2.112.0 → utilo-2.112.2}/utilo/file/utils.py +0 -0
  65. {utilo-2.112.0 → utilo-2.112.2}/utilo/group.py +0 -0
  66. {utilo-2.112.0 → utilo-2.112.2}/utilo/hash.py +0 -0
  67. {utilo-2.112.0 → utilo-2.112.2}/utilo/likelihood.py +0 -0
  68. {utilo-2.112.0 → utilo-2.112.2}/utilo/logger.py +0 -0
  69. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/__init__.py +0 -0
  70. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/const.py +0 -0
  71. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/distance.py +0 -0
  72. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/func.py +0 -0
  73. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/line.py +0 -0
  74. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/near.py +0 -0
  75. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/number.py +0 -0
  76. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/rectangle.py +0 -0
  77. {utilo-2.112.0 → utilo-2.112.2}/utilo/math/roman.py +0 -0
  78. {utilo-2.112.0 → utilo-2.112.2}/utilo/optimizer.py +0 -0
  79. {utilo-2.112.0 → utilo-2.112.2}/utilo/pages.py +0 -0
  80. {utilo-2.112.0 → utilo-2.112.2}/utilo/process.py +0 -0
  81. {utilo-2.112.0 → utilo-2.112.2}/utilo/quick.py +0 -0
  82. {utilo-2.112.0 → utilo-2.112.2}/utilo/regex.py +0 -0
  83. {utilo-2.112.0 → utilo-2.112.2}/utilo/secret.py +0 -0
  84. {utilo-2.112.0 → utilo-2.112.2}/utilo/space.py +0 -0
  85. {utilo-2.112.0 → utilo-2.112.2}/utilo/string/compare.py +0 -0
  86. {utilo-2.112.0 → utilo-2.112.2}/utilo/string/display.py +0 -0
  87. {utilo-2.112.0 → utilo-2.112.2}/utilo/string/modify.py +0 -0
  88. {utilo-2.112.0 → utilo-2.112.2}/utilo/string/table.py +0 -0
  89. {utilo-2.112.0 → utilo-2.112.2}/utilo/tuples.py +0 -0
  90. {utilo-2.112.0 → utilo-2.112.2}/utilo/typechecker.py +0 -0
  91. {utilo-2.112.0 → utilo-2.112.2}/utilo/utils.py +0 -0
  92. {utilo-2.112.0 → utilo-2.112.2}/utilo/xyz/__init__.py +0 -0
  93. {utilo-2.112.0 → utilo-2.112.2}/utilo/xyz/lock.py +0 -0
  94. {utilo-2.112.0 → utilo-2.112.2}/utilo/xyz/table.py +0 -0
  95. {utilo-2.112.0 → utilo-2.112.2}/utilo/xyz/tidy.py +0 -0
  96. {utilo-2.112.0 → utilo-2.112.2}/utilo.egg-info/SOURCES.txt +0 -0
  97. {utilo-2.112.0 → utilo-2.112.2}/utilo.egg-info/dependency_links.txt +0 -0
  98. {utilo-2.112.0 → utilo-2.112.2}/utilo.egg-info/entry_points.txt +0 -0
  99. {utilo-2.112.0 → utilo-2.112.2}/utilo.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: utilo
3
- Version: 2.112.0
3
+ Version: 2.112.2
4
4
  Author-email: Helmut Konrad Schewe <helmutus@outlook.com>
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/anaticulae/utilo
@@ -11,13 +11,13 @@ Classifier: Programming Language :: Python :: 3.14
11
11
  Requires-Python: >=3.12
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: filelock<5.0.0,>=4.0.0
14
+ Requires-Dist: filelock<5.0.0,>=4.2.10
15
15
  Requires-Dist: utilosafe<2.0.0,>=1.0.0
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: PyYAML<7.0.0,>=6.0.3; extra == "dev"
18
18
  Requires-Dist: pytest-timeout<3.0.0,>=2.4.0; extra == "dev"
19
19
  Requires-Dist: utilotest<2.0.0,>=1.1.1; extra == "dev"
20
- Requires-Dist: iamraw<5.0.0,>=4.95.0; extra == "dev"
20
+ Requires-Dist: iamraw<5.0.0,>=4.95.1; extra == "dev"
21
21
  Dynamic: license-file
22
22
 
23
23
  UTILA
@@ -7,14 +7,14 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "utilo"
10
- version = "2.112.0"
10
+ version = "2.112.2"
11
11
  description = ""
12
12
  requires-python = ">=3.12"
13
13
  authors = [
14
14
  { name = "Helmut Konrad Schewe", email = "helmutus@outlook.com" },
15
15
  ]
16
16
  dependencies = [
17
- "filelock>=4.0.0,<5.0.0",
17
+ "filelock>=4.2.10,<5.0.0",
18
18
  "utilosafe>=1.0.0,<2.0.0",
19
19
  ]
20
20
  keywords = []
@@ -43,7 +43,7 @@ dev = [
43
43
  "PyYAML>=6.0.3,<7.0.0",
44
44
  "pytest-timeout>=2.4.0,<3.0.0",
45
45
  "utilotest>=1.1.1,<2.0.0",
46
- "iamraw>=4.95.0,<5.0.0",
46
+ "iamraw>=4.95.1,<5.0.0",
47
47
  ]
48
48
 
49
49
  [project.urls]
@@ -63,21 +63,20 @@ output_format = "md"
63
63
  changelog_file = "CHANGELOG"
64
64
 
65
65
  [tool.semantic_release.commit_parser_options]
66
- patch_tags = [
66
+ allowed_tags = [
67
67
  "build",
68
68
  "chore",
69
69
  "ci",
70
70
  "deps",
71
71
  "docs",
72
+ "feat",
72
73
  "fix",
73
74
  "perf",
74
75
  "refactor",
75
76
  "style",
76
77
  "test",
77
78
  ]
78
- minor_tags = [
79
- "feat",
80
- ]
79
+ default_bump_level = 2
81
80
 
82
81
  [tool.setuptools.packages.find]
83
82
  where = [
@@ -7,6 +7,7 @@
7
7
  # be prosecuted under federal law. Its content is company confidential.
8
8
  #==============================================================================
9
9
 
10
+ import multiprocessing
10
11
  import os
11
12
 
12
13
  import pytest
@@ -602,3 +603,28 @@ def test_file_create_owner(tmpdir):
602
603
  # -rw-rw-r-- local
603
604
  # -rw-r--r-- github
604
605
  assert mode in {33204, 33188}
606
+
607
+
608
+ def test_file_replace_binary_overlap(tmpdir):
609
+ """Verify that concurrent processes replace file correctly."""
610
+ expected = b'binary'
611
+ path = utilo.join(tmpdir, 'file.txt')
612
+ concurrent_run(
613
+ target=utilo.file_replace_binary,
614
+ args=(path, expected),
615
+ )
616
+ content = utilo.file_read_binary(path, lock=True)
617
+ assert content == expected
618
+
619
+
620
+ def concurrent_run(target, args, times: int = 100):
621
+ processes = [
622
+ multiprocessing.Process(
623
+ target=target,
624
+ args=args,
625
+ ) for _ in range(times)
626
+ ]
627
+ for p in processes:
628
+ p.start()
629
+ for p in processes:
630
+ p.join()
@@ -78,8 +78,12 @@ def safehash(item):
78
78
  return hash(str(item))
79
79
 
80
80
 
81
- functools._HashedSeq.__init__.__defaults__ = (safehash,)
82
-
81
+ try:
82
+ # python <= 3.12
83
+ functools._HashedSeq.__init__.__defaults__ = (safehash,)
84
+ except AttributeError:
85
+ pass
86
+ # python >= 3.13
83
87
  # pylint:disable=all
84
88
 
85
89
  ################################################################################
@@ -85,12 +85,14 @@ def file_append(path: str, content: str, create: bool = False, private: bool = F
85
85
 
86
86
 
87
87
  @contextlib.contextmanager
88
- def lock_file(path: str, lock: bool = True): # pylint:disable=unused-argument
89
- if not lock:
90
- yield
91
- return
92
- lockpath = utilo.tmpfile(root=tmp('/utilo'))
93
- with filelock.FileLock(lockpath):
88
+ def lock_file(path: str, lock: bool = True):
89
+ if lock:
90
+ lockdir = tmp('/utilo')
91
+ simple = utilo.simple(path, maxlength=255)
92
+ lockpath = utilo.join(lockdir, simple)
93
+ with filelock.FileLock(lockpath):
94
+ yield
95
+ else:
94
96
  yield
95
97
 
96
98
 
@@ -211,8 +213,11 @@ def file_read_binary(
211
213
  """Read binary file content"""
212
214
  with lock_file(path, lock=lock):
213
215
  utilo.exists_assert(path)
214
- with utilo.file.securewrapper.open(path, mode='rb',
215
- private=private) as fp:
216
+ with utilo.file.securewrapper.open(
217
+ path,
218
+ mode='rb',
219
+ private=private,
220
+ ) as fp:
216
221
  content = fp.read(size)
217
222
  return content
218
223
 
@@ -252,7 +257,11 @@ def file_replace(
252
257
  lock=False,
253
258
  )
254
259
  return
255
- current_content = file_read(path, private=private)
260
+ current_content = file_read(
261
+ path,
262
+ private=private,
263
+ lock=False,
264
+ )
256
265
  if current_content == content:
257
266
  return
258
267
  with utilo.file.securewrapper.open(
@@ -279,9 +288,18 @@ def file_replace_binary(path: str, content: bytes, private: bool = False):
279
288
  """
280
289
  with lock_file(path, lock=True):
281
290
  if not os.path.exists(path):
282
- file_create_binary(path, content, private=private)
291
+ file_create_binary(
292
+ path,
293
+ content,
294
+ private=private,
295
+ lock=False,
296
+ )
283
297
  return
284
- current_content = file_read_binary(path, private=private)
298
+ current_content = file_read_binary(
299
+ path,
300
+ private=private,
301
+ lock=False,
302
+ )
285
303
  if current_content == content:
286
304
  return
287
305
  with utilo.file.securewrapper.open(
@@ -386,6 +386,8 @@ def simple(item: str, maxlength: int = 25) -> str:
386
386
 
387
387
  >>> simple('No spaces _+; 133')
388
388
  'Nospaces133'
389
+ >>> simple('/c/windows/tmp/abc/test.txt')
390
+ 'cwindowstmpabctesttxt'
389
391
  """
390
392
  item = utilo.fix_encoding(item)
391
393
  item = item.translate(SIMPLIFY)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: utilo
3
- Version: 2.112.0
3
+ Version: 2.112.2
4
4
  Author-email: Helmut Konrad Schewe <helmutus@outlook.com>
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/anaticulae/utilo
@@ -11,13 +11,13 @@ Classifier: Programming Language :: Python :: 3.14
11
11
  Requires-Python: >=3.12
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: filelock<5.0.0,>=4.0.0
14
+ Requires-Dist: filelock<5.0.0,>=4.2.10
15
15
  Requires-Dist: utilosafe<2.0.0,>=1.0.0
16
16
  Provides-Extra: dev
17
17
  Requires-Dist: PyYAML<7.0.0,>=6.0.3; extra == "dev"
18
18
  Requires-Dist: pytest-timeout<3.0.0,>=2.4.0; extra == "dev"
19
19
  Requires-Dist: utilotest<2.0.0,>=1.1.1; extra == "dev"
20
- Requires-Dist: iamraw<5.0.0,>=4.95.0; extra == "dev"
20
+ Requires-Dist: iamraw<5.0.0,>=4.95.1; extra == "dev"
21
21
  Dynamic: license-file
22
22
 
23
23
  UTILA
@@ -1,8 +1,8 @@
1
- filelock<5.0.0,>=4.0.0
1
+ filelock<5.0.0,>=4.2.10
2
2
  utilosafe<2.0.0,>=1.0.0
3
3
 
4
4
  [dev]
5
5
  PyYAML<7.0.0,>=6.0.3
6
6
  pytest-timeout<3.0.0,>=2.4.0
7
7
  utilotest<2.0.0,>=1.1.1
8
- iamraw<5.0.0,>=4.95.0
8
+ iamraw<5.0.0,>=4.95.1
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
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