plexus-python-common 1.0.33__tar.gz → 1.0.35__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 (82) hide show
  1. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/PKG-INFO +2 -3
  2. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/pyproject.toml +2 -4
  3. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/datautils.py +3 -0
  4. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/s3utils.py +148 -115
  5. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/strutils.py +12 -12
  6. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/PKG-INFO +2 -3
  7. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/requires.txt +1 -2
  8. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/s3utils_test.py +16 -16
  9. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/strutils_test.py +22 -22
  10. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/.editorconfig +0 -0
  11. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/.github/workflows/pr.yml +0 -0
  12. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/.github/workflows/push.yml +0 -0
  13. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/.gitignore +0 -0
  14. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/MANIFEST.in +0 -0
  15. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/README.md +0 -0
  16. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/VERSION +0 -0
  17. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
  18. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
  19. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
  20. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.baz/file.bar.baz +0 -0
  21. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.baz/file.foo.bar +0 -0
  22. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.baz/file.foo.baz +0 -0
  23. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  24. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  25. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  26. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  27. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/file.bar +0 -0
  28. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/file.baz +0 -0
  29. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/file.foo +0 -0
  30. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils_archive/archive.compressed.zip +0 -0
  31. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/s3utils_archive/archive.uncompressed.zip +0 -0
  32. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/0-dummy +0 -0
  33. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/1-dummy +0 -0
  34. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/2-dummy +0 -0
  35. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.0.0.jsonl +0 -0
  36. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.0.0.vol-0.jsonl +0 -0
  37. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.0.jsonl +0 -0
  38. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.1.1.jsonl +0 -0
  39. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.1.1.vol-1.jsonl +0 -0
  40. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.1.jsonl +0 -0
  41. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.2.2.jsonl +0 -0
  42. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.2.2.vol-2.jsonl +0 -0
  43. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.2.jsonl +0 -0
  44. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.csv.part0 +0 -0
  45. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.csv.part1 +0 -0
  46. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.csv.part2 +0 -0
  47. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.txt +0 -0
  48. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/setup.cfg +0 -0
  49. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/setup.py +0 -0
  50. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/__init__.py +0 -0
  51. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMFile.py +0 -0
  52. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMNode.py +0 -0
  53. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMTags.py +0 -0
  54. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMWay.py +0 -0
  55. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/carto/__init__.py +0 -0
  56. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/pose.py +0 -0
  57. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/proj.py +0 -0
  58. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/__init__.py +0 -0
  59. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/apiutils.py +0 -0
  60. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/bagutils.py +0 -0
  61. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/config.py +0 -0
  62. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/jsonutils.py +0 -0
  63. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/ormutils.py +0 -0
  64. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/shutils.py +0 -0
  65. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/sqlutils.py +0 -0
  66. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus/common/utils/testutils.py +0 -0
  67. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/SOURCES.txt +0 -0
  68. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
  69. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
  70. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/top_level.txt +0 -0
  71. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_test.py +0 -0
  72. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/__init__.py +0 -0
  73. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/carto/osm_file_test.py +0 -0
  74. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
  75. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/pose_test.py +0 -0
  76. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/proj_test.py +0 -0
  77. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
  78. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/datautils_test.py +0 -0
  79. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/jsonutils_test.py +0 -0
  80. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/ormutils_test.py +0 -0
  81. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/shutils_test.py +0 -0
  82. {plexus_python_common-1.0.33 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/testutils_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python-common
3
- Version: 1.0.33
3
+ Version: 1.0.35
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -8,8 +8,7 @@ Classifier: Programming Language :: Python :: 3.14
8
8
  Requires-Python: <3.15,>=3.12
9
9
  Requires-Dist: aiobotocore<2.27,>=2.26
10
10
  Requires-Dist: boto3<1.42,>=1.41
11
- Requires-Dist: boto3-stubs<1.42,>=1.41
12
- Requires-Dist: cloudpathlib>=0.21
11
+ Requires-Dist: boto3-stubs[s3]<1.42,>=1.41
13
12
  Requires-Dist: fsspec>=2025.10
14
13
  Requires-Dist: lxml>=6.0
15
14
  Requires-Dist: numpy>=2.3
@@ -10,8 +10,7 @@ build-backend = "setuptools.build_meta"
10
10
  dev = [
11
11
  "aiobotocore>=2.26,<2.27",
12
12
  "boto3>=1.41,<1.42",
13
- "boto3-stubs>=1.41,<1.42",
14
- "cloudpathlib>=0.21",
13
+ "boto3-stubs[s3]>=1.41,<1.42",
15
14
  "fsspec>=2025.10",
16
15
  "lxml>=6.0",
17
16
  "numpy>=2.3",
@@ -53,8 +52,7 @@ classifiers = [
53
52
  dependencies = [
54
53
  "aiobotocore>=2.26,<2.27",
55
54
  "boto3>=1.41,<1.42",
56
- "boto3-stubs>=1.41,<1.42",
57
- "cloudpathlib>=0.21",
55
+ "boto3-stubs[s3]>=1.41,<1.42",
58
56
  "fsspec>=2025.10",
59
57
  "lxml>=6.0",
60
58
  "numpy>=2.3",
@@ -15,6 +15,7 @@ from plexus.common.utils.strutils import hex_string_parser
15
15
  from plexus.common.utils.strutils import parse_bag_name, parse_user_name, parse_vehicle_name
16
16
  from plexus.common.utils.strutils import semver_parser, uuid_parser
17
17
  from plexus.common.utils.strutils import strict_abspath_parser, strict_relpath_parser
18
+ from plexus.common.utils.strutils import strict_fragmented_abspath_parser, strict_fragmented_relpath_parser
18
19
  from plexus.common.utils.strutils import topic_parser, vin_code_chars, vin_code_parser
19
20
 
20
21
 
@@ -55,6 +56,8 @@ validate_uuid = make_validate_string(uuid_parser)
55
56
 
56
57
  validate_strict_relpath = make_validate_string(strict_relpath_parser)
57
58
  validate_strict_abspath = make_validate_string(strict_abspath_parser)
59
+ validate_strict_fragmented_relpath = make_validate_string(strict_fragmented_relpath_parser)
60
+ validate_strict_fragmented_abspath = make_validate_string(strict_fragmented_abspath_parser)
58
61
 
59
62
  validate_semver = make_validate_string(semver_parser)
60
63
 
@@ -13,21 +13,21 @@ import typing
13
13
  import zipfile
14
14
  import zlib
15
15
  from collections.abc import Callable, Generator
16
- from pathlib import Path
17
16
  from typing import Any, Literal
18
17
 
19
18
  import boto3
20
19
  import fsspec
21
20
  import fsspec.utils
22
- from cloudpathlib import CloudPath, S3Client, S3Path
23
21
  from iker.common.utils.sequtils import chunk_between, head, last
24
22
  from iker.common.utils.shutils import glob_match, listfile, path_depth
25
23
  from iker.common.utils.strutils import is_empty, trim_to_none
26
- from rich.progress import BarColumn, DownloadColumn, Progress, TaskID, TextColumn, TransferSpeedColumn
24
+ from mypy_boto3_s3 import S3Client
25
+ from rich.progress import BarColumn, DownloadColumn, Progress, TextColumn, TransferSpeedColumn
27
26
 
28
27
  __all__ = [
29
28
  "S3ObjectMeta",
30
29
  "s3_make_client",
30
+ "s3_head_object",
31
31
  "s3_list_objects",
32
32
  "s3_listfile",
33
33
  "s3_cp_download",
@@ -36,10 +36,8 @@ __all__ = [
36
36
  "s3_sync_upload",
37
37
  "s3_pull_text",
38
38
  "s3_push_text",
39
- "S3TransferCallbackClient",
40
- "ArchiveMemberChunk",
41
- "s3_make_progress_callback",
42
39
  "s3_make_progressed_client",
40
+ "ArchiveMemberChunk",
43
41
  "s3_archive_member_tree",
44
42
  "s3_archive_listfile",
45
43
  "s3_archive_open_member",
@@ -84,12 +82,28 @@ def s3_make_client(
84
82
  session = boto3.Session(aws_access_key_id=trim_to_none(access_key_id),
85
83
  aws_secret_access_key=trim_to_none(secret_access_key),
86
84
  region_name=trim_to_none(region_name))
87
- client = S3Client(boto3_session=session, endpoint_url=trim_to_none(endpoint_url))
85
+ client = session.client("s3", endpoint_url=trim_to_none(endpoint_url))
88
86
  try:
89
87
  yield client
90
88
  finally:
91
- if hasattr(client, "close"):
92
- client.close()
89
+ client.close()
90
+
91
+
92
+ def s3_head_object(
93
+ client: S3Client,
94
+ bucket: str,
95
+ key: str,
96
+ ) -> S3ObjectMeta:
97
+ """
98
+ Retrieves metadata of an object from the given S3 ``bucket`` and ``key``.
99
+
100
+ :param client: An instance of ``S3Client``.
101
+ :param bucket: Bucket name.
102
+ :param key: Object key.
103
+ :return: An ``S3ObjectMeta`` object representing the S3 object.
104
+ """
105
+ response = client.head_object(Bucket=bucket, Key=key)
106
+ return S3ObjectMeta(key=key, last_modified=response["LastModified"], size=response["ContentLength"])
93
107
 
94
108
 
95
109
  def s3_list_objects(
@@ -111,12 +125,12 @@ def s3_list_objects(
111
125
  count = 0
112
126
  while True:
113
127
  if is_empty(continuation_token):
114
- response = client.client.list_objects_v2(MaxKeys=1000, Bucket=bucket, Prefix=prefix)
128
+ response = client.list_objects_v2(MaxKeys=1000, Bucket=bucket, Prefix=prefix)
115
129
  else:
116
- response = client.client.list_objects_v2(MaxKeys=1000,
117
- Bucket=bucket,
118
- Prefix=prefix,
119
- ContinuationToken=continuation_token)
130
+ response = client.list_objects_v2(MaxKeys=1000,
131
+ Bucket=bucket,
132
+ Prefix=prefix,
133
+ ContinuationToken=continuation_token)
120
134
 
121
135
  contents = response.get("Contents", [])
122
136
  count += len(contents)
@@ -175,7 +189,7 @@ def s3_cp_download(client: S3Client, bucket: str, key: str, file_path: str | os.
175
189
  :param key: Object key.
176
190
  :param file_path: Local file path to save the object.
177
191
  """
178
- client.client.download_file(bucket, key, file_path)
192
+ client.download_file(bucket, key, file_path)
179
193
 
180
194
 
181
195
  def s3_cp_upload(client: S3Client, file_path: str | os.PathLike[str], bucket: str, key: str):
@@ -188,10 +202,10 @@ def s3_cp_upload(client: S3Client, file_path: str | os.PathLike[str], bucket: st
188
202
  :param key: Object key for the uploaded file.
189
203
  """
190
204
  t, _ = mimetypes.MimeTypes().guess_type(file_path)
191
- client.client.upload_file(file_path,
192
- bucket,
193
- key,
194
- ExtraArgs={"ContentType": "binary/octet-stream" if t is None else t})
205
+ client.upload_file(file_path,
206
+ bucket,
207
+ key,
208
+ ExtraArgs={"ContentType": "binary/octet-stream" if t is None else t})
195
209
 
196
210
 
197
211
  def s3_sync_download(
@@ -311,7 +325,7 @@ def s3_pull_text(client: S3Client, bucket: str, key: str, encoding: str = None)
311
325
  :return: The decoded text content.
312
326
  """
313
327
  with tempfile.TemporaryFile() as fp:
314
- client.client.download_fileobj(bucket, key, fp)
328
+ client.download_fileobj(bucket, key, fp)
315
329
  fp.seek(0)
316
330
  return fp.read().decode(encoding or "utf-8")
317
331
 
@@ -329,95 +343,111 @@ def s3_push_text(client: S3Client, text: str, bucket: str, key: str, encoding: s
329
343
  with tempfile.TemporaryFile() as fp:
330
344
  fp.write(text.encode(encoding or "utf-8"))
331
345
  fp.seek(0)
332
- client.client.upload_fileobj(fp, bucket, key)
346
+ client.upload_fileobj(fp, bucket, key)
333
347
 
334
348
 
335
- TransferDirection = Literal["download", "upload"]
336
- TransferState = Literal["start", "update", "stop"]
349
+ class S3ClientProgressProxy(object):
350
+ def __init__(self, client: S3Client, progress: Progress):
351
+ self.client = client
352
+ self.progress = progress
337
353
 
354
+ @contextlib.contextmanager
355
+ def make_transfer_callback(self, key: str, bytes_total: int, direction: Literal["download", "upload"]):
356
+ task_id = self.progress.add_task(direction, total=bytes_total, key=key)
357
+ try:
358
+ yield lambda bytes_sent: self.progress.update(task_id, advance=bytes_sent)
359
+ finally:
360
+ self.progress.remove_task(task_id)
338
361
 
339
- @contextlib.contextmanager
340
- def make_transfer_callback(
341
- callback: Callable[[CloudPath, TransferDirection, TransferState, int], None],
342
- path: Path | CloudPath,
343
- direction: TransferDirection,
344
- ):
345
- if callback is None:
346
- yield None
347
- return
348
-
349
- callback(path, direction, "start", 0)
350
- try:
351
- yield functools.partial(callback, path, direction, "update")
352
- finally:
353
- callback(path, direction, "stop", 0)
362
+ def __getattr__(self, name):
363
+ return getattr(self.client, name)
354
364
 
355
-
356
- class S3TransferCallbackClient(S3Client):
357
- def __init__(
365
+ def download_file(
358
366
  self,
359
- *args,
360
- transfer_callback: Callable[[Path | CloudPath, TransferDirection, TransferState, int], None],
361
- **kwargs,
367
+ Bucket,
368
+ Key,
369
+ Filename,
370
+ ExtraArgs=None,
371
+ Callback=None,
372
+ Config=None,
362
373
  ):
363
- super().__init__(*args, **kwargs)
364
- self.transfer_callback = transfer_callback
365
-
366
- def _download_file(self, cloud_path: S3Path, local_path: str | os.PathLike[str]) -> Path:
367
- local_path = Path(local_path)
368
-
369
- obj = self.s3.Object(cloud_path.bucket, cloud_path.key)
370
-
371
- with make_transfer_callback(self.transfer_callback, cloud_path, "download") as callback:
372
- obj.download_file(
373
- str(local_path),
374
- Config=self.boto3_transfer_config,
375
- ExtraArgs=self.boto3_dl_extra_args,
376
- Callback=callback,
377
- )
378
- return local_path
379
-
380
- def _upload_file(self, local_path: str | os.PathLike[str], cloud_path: S3Path) -> S3Path:
381
- local_path = Path(local_path)
382
-
383
- obj = self.s3.Object(cloud_path.bucket, cloud_path.key)
384
-
385
- extra_args = self.boto3_ul_extra_args.copy()
386
-
387
- if self.content_type_method is not None:
388
- content_type, content_encoding = self.content_type_method(str(local_path))
389
- if content_type is not None:
390
- extra_args["ContentType"] = content_type
391
- if content_encoding is not None:
392
- extra_args["ContentEncoding"] = content_encoding
393
-
394
- with make_transfer_callback(self.transfer_callback, local_path, "upload") as callback:
395
- obj.upload_file(
396
- str(local_path),
397
- Config=self.boto3_transfer_config,
398
- ExtraArgs=extra_args,
399
- Callback=callback,
400
- )
401
- return cloud_path
402
-
403
-
404
- def s3_make_progress_callback(
405
- progress: Progress,
406
- ) -> Callable[[Path | CloudPath, TransferDirection, TransferState, int], None]:
407
- task_ids: dict[Path | CloudPath, TaskID] = {}
408
-
409
- def progress_callback(path: Path | CloudPath, direction: TransferDirection, state: TransferState, bytes_sent: int):
410
- if state == "start":
411
- size = path.stat().st_size
412
- task_ids[path] = progress.add_task(direction, total=size, filename=path.name)
413
- elif state == "stop":
414
- if path in task_ids:
415
- progress.remove_task(task_ids[path])
416
- del task_ids[path]
417
- else:
418
- progress.update(task_ids[path], advance=bytes_sent)
419
-
420
- return progress_callback
374
+ object_meta = s3_head_object(self.client, Bucket, Key)
375
+ with (
376
+ contextlib.nullcontext(Callback) if Callback is not None
377
+ else self.make_transfer_callback(Key, object_meta.size, "download")
378
+ ) as callback:
379
+ return self.client.download_file(Bucket,
380
+ Key,
381
+ Filename,
382
+ ExtraArgs=ExtraArgs,
383
+ Callback=callback,
384
+ Config=Config)
385
+
386
+ def download_fileobj(
387
+ self,
388
+ Bucket,
389
+ Key,
390
+ Fileobj,
391
+ ExtraArgs=None,
392
+ Callback=None,
393
+ Config=None,
394
+ ):
395
+ object_meta = s3_head_object(self.client, Bucket, Key)
396
+ with (
397
+ contextlib.nullcontext(Callback) if Callback is not None
398
+ else self.make_transfer_callback(Key, object_meta.size, "download")
399
+ ) as callback:
400
+ return self.client.download_fileobj(Bucket,
401
+ Key,
402
+ Fileobj,
403
+ ExtraArgs=ExtraArgs,
404
+ Callback=callback,
405
+ Config=Config)
406
+
407
+ def upload_file(
408
+ self,
409
+ Filename,
410
+ Bucket,
411
+ Key,
412
+ ExtraArgs=None,
413
+ Callback=None,
414
+ Config=None,
415
+ ):
416
+ bytes_total = os.path.getsize(Filename)
417
+ with (
418
+ contextlib.nullcontext(Callback) if Callback is not None
419
+ else self.make_transfer_callback(Key, bytes_total, "upload")
420
+ ) as callback:
421
+ return self.client.upload_file(Filename,
422
+ Bucket,
423
+ Key,
424
+ ExtraArgs=ExtraArgs,
425
+ Callback=callback,
426
+ Config=Config)
427
+
428
+ def upload_fileobj(
429
+ self,
430
+ Fileobj,
431
+ Bucket,
432
+ Key,
433
+ ExtraArgs=None,
434
+ Callback=None,
435
+ Config=None,
436
+ ):
437
+ current_pos = Fileobj.tell()
438
+ Fileobj.seek(0, os.SEEK_END)
439
+ bytes_total = Fileobj.tell() - current_pos
440
+ Fileobj.seek(current_pos)
441
+ with (
442
+ contextlib.nullcontext(Callback) if Callback is not None
443
+ else self.make_transfer_callback(Key, bytes_total, "upload")
444
+ ) as callback:
445
+ return self.client.upload_fileobj(Fileobj,
446
+ Bucket,
447
+ Key,
448
+ ExtraArgs=ExtraArgs,
449
+ Callback=callback,
450
+ Config=Config)
421
451
 
422
452
 
423
453
  if typing.TYPE_CHECKING:
@@ -435,18 +465,18 @@ def s3_make_progressed_client(
435
465
  secret_access_key: str = None,
436
466
  region_name: str = None,
437
467
  endpoint_url: str = None,
438
- ) -> Generator[S3Client, None, None]:
468
+ ) -> Generator[S3ClientProgressProxy, None, None]:
439
469
  """
440
- Creates an S3 client with progress callback as a context manager for safe resource handling.
470
+ Creates an S3 client with progress reporting as a context manager.
441
471
 
442
472
  :param access_key_id: AWS access key ID.
443
473
  :param secret_access_key: AWS secret access key.
444
474
  :param region_name: AWS service region name.
445
475
  :param endpoint_url: AWS service endpoint URL.
446
- :return: An instance of ``S3TransferCallbackClient``.
476
+ :return: An instance of ``S3Client`` with progress reporting.
447
477
  """
448
478
  with Progress(
449
- TextColumn("[blue]{task.fields[filename]}"),
479
+ TextColumn("[blue]{task.fields[key]}"),
450
480
  BarColumn(),
451
481
  DownloadColumn(),
452
482
  TransferSpeedColumn(),
@@ -454,9 +484,12 @@ def s3_make_progressed_client(
454
484
  session = boto3.Session(aws_access_key_id=trim_to_none(access_key_id),
455
485
  aws_secret_access_key=trim_to_none(secret_access_key),
456
486
  region_name=trim_to_none(region_name))
457
- yield S3TransferCallbackClient(boto3_session=session,
458
- endpoint_url=trim_to_none(endpoint_url),
459
- transfer_callback=s3_make_progress_callback(progress))
487
+ client = session.client("s3", endpoint_url=trim_to_none(endpoint_url))
488
+ proxy = S3ClientProgressProxy(client, progress)
489
+ try:
490
+ yield proxy
491
+ finally:
492
+ proxy.close()
460
493
 
461
494
 
462
495
  def s3_options_from_s3_client(client: S3Client) -> dict[str, Any]:
@@ -466,12 +499,12 @@ def s3_options_from_s3_client(client: S3Client) -> dict[str, Any]:
466
499
  :param client: An instance of ``S3Client``.
467
500
  :return: A dictionary of S3 connection options.
468
501
  """
469
- if client.sess is None:
502
+ if client is None:
470
503
  return {}
471
504
 
472
505
  s3_options: dict[str, Any] = {}
473
506
 
474
- credentials = client.sess.get_credentials()
507
+ credentials = client._request_signer._credentials
475
508
  if credentials is not None:
476
509
  if credentials.access_key:
477
510
  s3_options["key"] = credentials.access_key
@@ -481,10 +514,10 @@ def s3_options_from_s3_client(client: S3Client) -> dict[str, Any]:
481
514
  s3_options["token"] = credentials.token
482
515
 
483
516
  client_kwargs = {}
484
- if client.sess.region_name or client.client.meta.region_name:
485
- client_kwargs["region_name"] = client.sess.region_name or client.client.meta.region_name
486
- if client.client.meta.endpoint_url:
487
- client_kwargs["endpoint_url"] = client.client.meta.endpoint_url
517
+ if client.meta.region_name:
518
+ client_kwargs["region_name"] = client.meta.region_name
519
+ if client.meta.endpoint_url:
520
+ client_kwargs["endpoint_url"] = client.meta.endpoint_url
488
521
 
489
522
  if client_kwargs:
490
523
  s3_options["client_kwargs"] = client_kwargs
@@ -22,10 +22,10 @@ __all__ = [
22
22
  "strict_relpath_parser",
23
23
  "strict_abspath_pattern",
24
24
  "strict_abspath_parser",
25
- "strict_archive_relpath_pattern",
26
- "strict_archive_relpath_parser",
27
- "strict_archive_abspath_pattern",
28
- "strict_archive_abspath_parser",
25
+ "strict_fragmented_relpath_pattern",
26
+ "strict_fragmented_relpath_parser",
27
+ "strict_fragmented_abspath_pattern",
28
+ "strict_fragmented_abspath_parser",
29
29
  "email_address_pattern",
30
30
  "email_address_parser",
31
31
  "semver_pattern",
@@ -160,9 +160,9 @@ strict_relpath_regexp: re.Pattern[str] = re.compile(
160
160
  rf"(?!.*(^|/)\.+($|/))(?:{strict_chars_regexp.pattern}/)*(?:{strict_chars_regexp.pattern})?")
161
161
  strict_abspath_regexp: re.Pattern[str] = re.compile(
162
162
  rf"(?!.*(^|/)\.+($|/))/(?:{strict_chars_regexp.pattern}/)*(?:{strict_chars_regexp.pattern})?")
163
- strict_archive_relpath_regexp: re.Pattern[str] = re.compile(
163
+ strict_fragmented_relpath_regexp: re.Pattern[str] = re.compile(
164
164
  rf"(?!.*(^|/)\.+($|/))(?:{strict_chars_regexp.pattern}/)*({strict_chars_regexp.pattern})(?:#({strict_relpath_regexp.pattern}))?")
165
- strict_archive_abspath_regexp: re.Pattern[str] = re.compile(
165
+ strict_fragmented_abspath_regexp: re.Pattern[str] = re.compile(
166
166
  rf"(?!.*(^|/)\.+($|/))/(?:{strict_chars_regexp.pattern}/)*({strict_chars_regexp.pattern})(?:#({strict_relpath_regexp.pattern}))?")
167
167
 
168
168
  strict_path_chars_element = strict_chars_element.copy()
@@ -173,10 +173,10 @@ strict_relpath_element: pp.ParserElement = pp.Combine(
173
173
  (strict_path_chars_element + slash_token)[...] + strict_path_chars_element[0, 1])
174
174
  strict_abspath_element: pp.ParserElement = pp.Combine(
175
175
  slash_token + (strict_path_chars_element + slash_token)[...] + strict_path_chars_element[0, 1])
176
- strict_archive_relpath_element: pp.ParserElement = pp.Combine(
176
+ strict_fragmented_relpath_element: pp.ParserElement = pp.Combine(
177
177
  (strict_path_chars_element + slash_token)[...] + strict_path_chars_element +
178
178
  (sharp_token + strict_relpath_element)[0, 1])
179
- strict_archive_abspath_element: pp.ParserElement = pp.Combine(
179
+ strict_fragmented_abspath_element: pp.ParserElement = pp.Combine(
180
180
  slash_token + (strict_path_chars_element + slash_token)[...] + strict_path_chars_element +
181
181
  (sharp_token + strict_relpath_element)[0, 1])
182
182
 
@@ -221,10 +221,10 @@ strict_relpath_pattern = make_string_pattern(strict_relpath_regexp)
221
221
  strict_relpath_parser = make_string_parser(strict_relpath_element)
222
222
  strict_abspath_pattern = make_string_pattern(strict_abspath_regexp)
223
223
  strict_abspath_parser = make_string_parser(strict_abspath_element)
224
- strict_archive_relpath_pattern = make_string_pattern(strict_archive_relpath_regexp)
225
- strict_archive_relpath_parser = make_string_parser(strict_archive_relpath_element)
226
- strict_archive_abspath_pattern = make_string_pattern(strict_archive_abspath_regexp)
227
- strict_archive_abspath_parser = make_string_parser(strict_archive_abspath_element)
224
+ strict_fragmented_relpath_pattern = make_string_pattern(strict_fragmented_relpath_regexp)
225
+ strict_fragmented_relpath_parser = make_string_parser(strict_fragmented_relpath_element)
226
+ strict_fragmented_abspath_pattern = make_string_pattern(strict_fragmented_abspath_regexp)
227
+ strict_fragmented_abspath_parser = make_string_parser(strict_fragmented_abspath_element)
228
228
 
229
229
  email_address_pattern = make_string_pattern(email_address_regexp)
230
230
  email_address_parser = make_string_parser(email_address_element)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexus-python-common
3
- Version: 1.0.33
3
+ Version: 1.0.35
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -8,8 +8,7 @@ Classifier: Programming Language :: Python :: 3.14
8
8
  Requires-Python: <3.15,>=3.12
9
9
  Requires-Dist: aiobotocore<2.27,>=2.26
10
10
  Requires-Dist: boto3<1.42,>=1.41
11
- Requires-Dist: boto3-stubs<1.42,>=1.41
12
- Requires-Dist: cloudpathlib>=0.21
11
+ Requires-Dist: boto3-stubs[s3]<1.42,>=1.41
13
12
  Requires-Dist: fsspec>=2025.10
14
13
  Requires-Dist: lxml>=6.0
15
14
  Requires-Dist: numpy>=2.3
@@ -1,7 +1,6 @@
1
1
  aiobotocore<2.27,>=2.26
2
2
  boto3<1.42,>=1.41
3
- boto3-stubs<1.42,>=1.41
4
- cloudpathlib>=0.21
3
+ boto3-stubs[s3]<1.42,>=1.41
5
4
  fsspec>=2025.10
6
5
  lxml>=6.0
7
6
  numpy>=2.3
@@ -31,7 +31,7 @@ class S3UtilsTest(unittest.TestCase):
31
31
 
32
32
  def test_s3_list_object__random_text_files(self):
33
33
  with moto.mock_aws(), s3_make_client() as client:
34
- client.client.create_bucket(Bucket="dummy-bucket")
34
+ client.create_bucket(Bucket="dummy-bucket")
35
35
 
36
36
  data = []
37
37
  for i in range(0, 2000):
@@ -49,7 +49,7 @@ class S3UtilsTest(unittest.TestCase):
49
49
  self.assertEqual(s3_pull_text(client, "dummy-bucket", key), text)
50
50
 
51
51
  with moto.mock_aws(), s3_make_progressed_client() as client:
52
- client.client.create_bucket(Bucket="dummy-bucket")
52
+ client.create_bucket(Bucket="dummy-bucket")
53
53
 
54
54
  data = []
55
55
  for i in range(0, 2000):
@@ -58,17 +58,17 @@ class S3UtilsTest(unittest.TestCase):
58
58
  data.append((key, text))
59
59
 
60
60
  for key, text in data:
61
- client.CloudPath("s3://dummy-bucket/", key).write_text(text)
61
+ s3_push_text(client, text, "dummy-bucket", key)
62
62
 
63
63
  result = list(s3_list_objects(client, "dummy-bucket", "dummy_prefix/"))
64
64
 
65
65
  for key, text in data:
66
66
  self.assertTrue(any(o.key == key for o in result))
67
- self.assertEqual(client.CloudPath("s3://dummy-bucket", key).read_text(), text)
67
+ self.assertEqual(s3_pull_text(client, "dummy-bucket", key), text)
68
68
 
69
69
  def test_s3_listfile__random_text_files(self):
70
70
  with moto.mock_aws(), s3_make_client() as client:
71
- client.client.create_bucket(Bucket="dummy-bucket")
71
+ client.create_bucket(Bucket="dummy-bucket")
72
72
 
73
73
  data = []
74
74
  for i in range(0, 2000):
@@ -86,7 +86,7 @@ class S3UtilsTest(unittest.TestCase):
86
86
  self.assertEqual(s3_pull_text(client, "dummy-bucket", key), text)
87
87
 
88
88
  with moto.mock_aws(), s3_make_progressed_client() as client:
89
- client.client.create_bucket(Bucket="dummy-bucket")
89
+ client.create_bucket(Bucket="dummy-bucket")
90
90
 
91
91
  data = []
92
92
  for i in range(0, 2000):
@@ -95,13 +95,13 @@ class S3UtilsTest(unittest.TestCase):
95
95
  data.append((key, text))
96
96
 
97
97
  for key, text in data:
98
- client.CloudPath("s3://dummy-bucket/", key).write_text(text)
98
+ s3_push_text(client, text, "dummy-bucket", key)
99
99
 
100
100
  result = list(s3_listfile(client, "dummy-bucket", "dummy_prefix/"))
101
101
 
102
102
  for key, text in data:
103
103
  self.assertTrue(any(o.key == key for o in result))
104
- self.assertEqual(client.CloudPath("s3://dummy-bucket", key).read_text(), text)
104
+ self.assertEqual(s3_pull_text(client, "dummy-bucket", key), text)
105
105
 
106
106
  data_s3_listfile = [
107
107
  (
@@ -236,7 +236,7 @@ class S3UtilsTest(unittest.TestCase):
236
236
  @ddt.unpack
237
237
  def test_s3_listfile(self, src, bucket, prefix, include_patterns, exclude_patterns, depth, expect):
238
238
  with moto.mock_aws(), s3_make_client() as client:
239
- client.client.create_bucket(Bucket=bucket)
239
+ client.create_bucket(Bucket=bucket)
240
240
 
241
241
  s3_sync_upload(client,
242
242
  os.path.join(resources_directory, src),
@@ -394,7 +394,7 @@ class S3UtilsTest(unittest.TestCase):
394
394
  @ddt.unpack
395
395
  def test_s3_sync(self, src, dst, bucket, prefix, include_patterns, exclude_patterns, depth, expect):
396
396
  with moto.mock_aws(), s3_make_client() as client:
397
- client.client.create_bucket(Bucket=bucket)
397
+ client.create_bucket(Bucket=bucket)
398
398
 
399
399
  s3_sync_upload(client,
400
400
  os.path.join(resources_directory, src),
@@ -414,7 +414,7 @@ class S3UtilsTest(unittest.TestCase):
414
414
  set(map(lambda x: norm_path(os.path.join(temp_directory, x)), expect)))
415
415
 
416
416
  with moto.mock_aws(), s3_make_client() as client:
417
- client.client.create_bucket(Bucket=bucket)
417
+ client.create_bucket(Bucket=bucket)
418
418
 
419
419
  s3_sync_upload(client,
420
420
  os.path.join(resources_directory, src),
@@ -434,7 +434,7 @@ class S3UtilsTest(unittest.TestCase):
434
434
  set(map(lambda x: norm_path(os.path.join(temp_directory, x)), expect)))
435
435
 
436
436
  with moto.mock_aws(), s3_make_client() as client:
437
- client.client.create_bucket(Bucket=bucket)
437
+ client.create_bucket(Bucket=bucket)
438
438
 
439
439
  s3_sync_upload(client,
440
440
  os.path.join(resources_directory, src),
@@ -467,7 +467,7 @@ class S3UtilsTest(unittest.TestCase):
467
467
  @ddt.unpack
468
468
  def test_s3_text(self, bucket, key, text, encoding):
469
469
  with moto.mock_aws(), s3_make_client() as client:
470
- client.client.create_bucket(Bucket=bucket)
470
+ client.create_bucket(Bucket=bucket)
471
471
 
472
472
  s3_push_text(client, text, bucket, key, encoding=encoding)
473
473
  self.assertEqual(s3_pull_text(client, bucket, key, encoding=encoding), text)
@@ -478,7 +478,7 @@ class S3UtilsTest(unittest.TestCase):
478
478
  moto_server_endpoint_url() as endpoint_url,
479
479
  s3_make_client(endpoint_url=endpoint_url) as client,
480
480
  ):
481
- client.client.create_bucket(Bucket="dummy-bucket")
481
+ client.create_bucket(Bucket="dummy-bucket")
482
482
 
483
483
  s3_sync_upload(client,
484
484
  os.path.join(resources_directory, "unittest", "s3utils_archive"),
@@ -538,7 +538,7 @@ class S3UtilsTest(unittest.TestCase):
538
538
  moto_server_endpoint_url() as endpoint_url,
539
539
  s3_make_client(endpoint_url=endpoint_url) as client,
540
540
  ):
541
- client.client.create_bucket(Bucket="dummy-bucket")
541
+ client.create_bucket(Bucket="dummy-bucket")
542
542
 
543
543
  s3_sync_upload(client,
544
544
  os.path.join(resources_directory, "unittest", "s3utils_archive"),
@@ -565,7 +565,7 @@ class S3UtilsTest(unittest.TestCase):
565
565
  moto_server_endpoint_url() as endpoint_url,
566
566
  s3_make_client(endpoint_url=endpoint_url) as client,
567
567
  ):
568
- client.client.create_bucket(Bucket="dummy-bucket")
568
+ client.create_bucket(Bucket="dummy-bucket")
569
569
 
570
570
  s3_sync_upload(client,
571
571
  os.path.join(resources_directory, "unittest", "s3utils_archive"),
@@ -13,8 +13,8 @@ from plexus.common.utils.strutils import kebab_case_parser, kebab_case_pattern
13
13
  from plexus.common.utils.strutils import parse_bag_name, parse_user_email, parse_user_name, parse_vehicle_name
14
14
  from plexus.common.utils.strutils import snake_case_parser, snake_case_pattern
15
15
  from plexus.common.utils.strutils import strict_abspath_parser, strict_abspath_pattern
16
- from plexus.common.utils.strutils import strict_archive_abspath_parser, strict_archive_abspath_pattern
17
- from plexus.common.utils.strutils import strict_archive_relpath_parser, strict_archive_relpath_pattern
16
+ from plexus.common.utils.strutils import strict_fragmented_abspath_parser, strict_fragmented_abspath_pattern
17
+ from plexus.common.utils.strutils import strict_fragmented_relpath_parser, strict_fragmented_relpath_pattern
18
18
  from plexus.common.utils.strutils import strict_relpath_parser, strict_relpath_pattern
19
19
  from plexus.common.utils.strutils import topic_parser, topic_pattern
20
20
  from plexus.common.utils.strutils import vin_code_parser, vin_code_pattern
@@ -379,7 +379,7 @@ class StrUtilsTest(unittest.TestCase):
379
379
  with self.assertRaises(pp.ParseException):
380
380
  strict_abspath_parser.parse_string(data, parse_all=True)
381
381
 
382
- data_strict_archive_relpath_pattern = [
382
+ data_strict_fragmented_relpath_pattern = [
383
383
  ("archive.zip",),
384
384
  ("directory/archive.zip",),
385
385
  ("directory/dummy/archive.zip",),
@@ -399,13 +399,13 @@ class StrUtilsTest(unittest.TestCase):
399
399
  ("directory/dummy/.path_to-archive.zip#directory/dummy/.path_to-file.txt",),
400
400
  ]
401
401
 
402
- @ddt.idata(data_strict_archive_relpath_pattern)
402
+ @ddt.idata(data_strict_fragmented_relpath_pattern)
403
403
  @ddt.unpack
404
- def test_strict_archive_relpath_pattern(self, data):
405
- self.assertIsNotNone(strict_archive_relpath_pattern.match(data))
406
- self.assertIsNotNone(strict_archive_relpath_parser.parse_string(data, parse_all=True))
404
+ def test_strict_fragmented_relpath_pattern(self, data):
405
+ self.assertIsNotNone(strict_fragmented_relpath_pattern.match(data))
406
+ self.assertIsNotNone(strict_fragmented_relpath_parser.parse_string(data, parse_all=True))
407
407
 
408
- data_strict_archive_relpath_pattern__bad_cases = [
408
+ data_strict_fragmented_relpath_pattern__bad_cases = [
409
409
  ("/archive.zip",),
410
410
  ("/directory/archive.zip",),
411
411
  ("/directory/dummy/archive.zip",),
@@ -446,14 +446,14 @@ class StrUtilsTest(unittest.TestCase):
446
446
  ("directory/archive.zip#directory/\n/dummy",),
447
447
  ]
448
448
 
449
- @ddt.idata(data_strict_archive_relpath_pattern__bad_cases)
449
+ @ddt.idata(data_strict_fragmented_relpath_pattern__bad_cases)
450
450
  @ddt.unpack
451
- def test_strict_archive_relpath_pattern__bad_cases(self, data):
452
- self.assertIsNone(strict_archive_relpath_pattern.match(data))
451
+ def test_strict_fragmented_relpath_pattern__bad_cases(self, data):
452
+ self.assertIsNone(strict_fragmented_relpath_pattern.match(data))
453
453
  with self.assertRaises(pp.ParseException):
454
- strict_archive_relpath_parser.parse_string(data, parse_all=True)
454
+ strict_fragmented_relpath_parser.parse_string(data, parse_all=True)
455
455
 
456
- data_strict_archive_abspath_pattern = [
456
+ data_strict_fragmented_abspath_pattern = [
457
457
  ("/archive.zip",),
458
458
  ("/directory/archive.zip",),
459
459
  ("/directory/dummy/archive.zip",),
@@ -473,13 +473,13 @@ class StrUtilsTest(unittest.TestCase):
473
473
  ("/directory/dummy/.path_to-archive.zip#directory/dummy/.path_to-file.txt",),
474
474
  ]
475
475
 
476
- @ddt.idata(data_strict_archive_abspath_pattern)
476
+ @ddt.idata(data_strict_fragmented_abspath_pattern)
477
477
  @ddt.unpack
478
- def test_strict_archive_abspath_pattern(self, data):
479
- self.assertIsNotNone(strict_archive_abspath_pattern.match(data))
480
- self.assertIsNotNone(strict_archive_abspath_parser.parse_string(data, parse_all=True))
478
+ def test_strict_fragmented_abspath_pattern(self, data):
479
+ self.assertIsNotNone(strict_fragmented_abspath_pattern.match(data))
480
+ self.assertIsNotNone(strict_fragmented_abspath_parser.parse_string(data, parse_all=True))
481
481
 
482
- data_strict_archive_abspath_pattern__bad_cases = [
482
+ data_strict_fragmented_abspath_pattern__bad_cases = [
483
483
  ("archive.zip",),
484
484
  ("directory/archive.zip",),
485
485
  ("directory/dummy/archive.zip",),
@@ -520,12 +520,12 @@ class StrUtilsTest(unittest.TestCase):
520
520
  ("/directory/archive.zip#directory/\n/dummy",),
521
521
  ]
522
522
 
523
- @ddt.idata(data_strict_archive_abspath_pattern__bad_cases)
523
+ @ddt.idata(data_strict_fragmented_abspath_pattern__bad_cases)
524
524
  @ddt.unpack
525
- def test_strict_archive_abspath_pattern__bad_cases(self, data):
526
- self.assertIsNone(strict_archive_abspath_pattern.match(data))
525
+ def test_strict_fragmented_abspath_pattern__bad_cases(self, data):
526
+ self.assertIsNone(strict_fragmented_abspath_pattern.match(data))
527
527
  with self.assertRaises(pp.ParseException):
528
- strict_archive_abspath_parser.parse_string(data, parse_all=True)
528
+ strict_fragmented_abspath_parser.parse_string(data, parse_all=True)
529
529
 
530
530
  data_email_address_pattern = [
531
531
  ("someone@dummy.com",),