iker-python-common 1.0.71__tar.gz → 1.0.73__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/PKG-INFO +1 -1
  2. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/dbutils.py +91 -50
  3. iker_python_common-1.0.71/src/iker/common/utils/shutils.py → iker_python_common-1.0.73/src/iker/common/utils/pathutils.py +36 -88
  4. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker_python_common.egg-info/PKG-INFO +1 -1
  5. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker_python_common.egg-info/SOURCES.txt +12 -12
  6. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/dbutils_test.py +44 -5
  7. iker_python_common-1.0.71/test/iker_tests/common/utils/shutils_test.py → iker_python_common-1.0.73/test/iker_tests/common/utils/pathutils_test.py +127 -119
  8. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/.editorconfig +0 -0
  9. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/.github/workflows/pr.yml +0 -0
  10. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/.github/workflows/push.yml +0 -0
  11. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/.gitignore +0 -0
  12. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/MANIFEST.in +0 -0
  13. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/README.md +0 -0
  14. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/VERSION +0 -0
  15. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/pyproject.toml +0 -0
  16. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/resources/unittest/config/config.cfg +0 -0
  17. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/resources/unittest/csvutils/data.csv +0 -0
  18. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/resources/unittest/csvutils/data.tsv +0 -0
  19. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.baz/file.bar.baz +0 -0
  20. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.baz/file.foo.bar +0 -0
  21. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.baz/file.foo.baz +0 -0
  22. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
  23. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.foo/dir.foo.bar/file.bar.baz +0 -0
  24. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.foo/dir.foo.bar/file.foo.bar +0 -0
  25. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.foo/dir.foo.bar/file.foo.baz +0 -0
  26. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.foo/file.bar +0 -0
  27. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.foo/file.baz +0 -0
  28. {iker_python_common-1.0.71/resources/unittest/shutils → iker_python_common-1.0.73/resources/unittest/pathutils}/dir.foo/file.foo +0 -0
  29. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/setup.cfg +0 -0
  30. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/setup.py +0 -0
  31. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/__init__.py +0 -0
  32. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/__init__.py +0 -0
  33. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/argutils.py +0 -0
  34. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/config.py +0 -0
  35. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/csvutils.py +0 -0
  36. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/dtutils.py +0 -0
  37. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/funcutils.py +0 -0
  38. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/iterutils.py +0 -0
  39. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/jsonutils.py +0 -0
  40. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/logger.py +0 -0
  41. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/numutils.py +0 -0
  42. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/randutils.py +0 -0
  43. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/retry.py +0 -0
  44. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/span.py +0 -0
  45. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/strutils.py +0 -0
  46. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/testutils.py +0 -0
  47. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker/common/utils/typeutils.py +0 -0
  48. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker_python_common.egg-info/dependency_links.txt +0 -0
  49. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker_python_common.egg-info/not-zip-safe +0 -0
  50. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker_python_common.egg-info/requires.txt +0 -0
  51. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/src/iker_python_common.egg-info/top_level.txt +0 -0
  52. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/__init__.py +0 -0
  53. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/__init__.py +0 -0
  54. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/__init__.py +0 -0
  55. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/argutils_test.py +0 -0
  56. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/config_test.py +0 -0
  57. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/csvutils_test.py +0 -0
  58. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/dtutils_test.py +0 -0
  59. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/funcutils_test.py +0 -0
  60. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/iterutils_test.py +0 -0
  61. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/jsonutils_test.py +0 -0
  62. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/logger_test.py +0 -0
  63. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/numutils_test.py +0 -0
  64. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/randutils_test.py +0 -0
  65. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/retry_test.py +0 -0
  66. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/span_test.py +0 -0
  67. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/strutils_test.py +0 -0
  68. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/testutils_test.py +0 -0
  69. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/iker_tests/common/utils/typeutils_test.py +0 -0
  70. {iker_python_common-1.0.71 → iker_python_common-1.0.73}/test/testenv.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.71
3
+ Version: 1.0.73
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -1,6 +1,7 @@
1
1
  import contextlib
2
2
  import dataclasses
3
- from typing import Any, Self, Sequence
3
+ from collections.abc import Callable, Sequence
4
+ from typing import Any, Self
4
5
 
5
6
  import asyncpg
6
7
  import packaging.version
@@ -23,6 +24,8 @@ __all__ = [
23
24
  "AsyncConnectionMaker",
24
25
  "orm_to_dict",
25
26
  "orm_clone",
27
+ "sql_compiler_extension",
28
+ "async_sql_compiler_extension",
26
29
  "mysql_insert_ignore",
27
30
  "postgresql_insert_on_conflict_do_nothing",
28
31
  ]
@@ -75,6 +78,7 @@ class ConnectionMaker(object):
75
78
  self.url = url
76
79
  self.engine_opts = engine_opts or {}
77
80
  self.session_opts = session_opts or {}
81
+ self.sa_engine = None
78
82
 
79
83
  @classmethod
80
84
  def create(
@@ -144,13 +148,23 @@ class ConnectionMaker(object):
144
148
 
145
149
  :return: The SQLAlchemy ``Engine``.
146
150
  """
147
- return sqlalchemy.create_engine(self.connection_string, **self.engine_opts)
151
+ if self.sa_engine is None:
152
+ self.sa_engine = sqlalchemy.create_engine(self.connection_string, **self.engine_opts)
153
+ return self.sa_engine
148
154
 
149
- def make_connection(self) -> sqlalchemy.Connection:
155
+ def reset_engine(self):
156
+ """
157
+ Disposes of the current SQLAlchemy engine, if it exists, and resets the cached engine instance.
158
+ """
159
+ if self.sa_engine is not None:
160
+ self.sa_engine.dispose()
161
+ self.sa_engine = None
162
+
163
+ def make_connection(self) -> contextlib.AbstractContextManager[sqlalchemy.Connection]:
150
164
  """
151
165
  Establishes and returns a new database connection using the SQLAlchemy engine.
152
166
 
153
- :return: A database connection object.
167
+ :return: A context manager yielding a database connection object.
154
168
  """
155
169
  return self.engine.connect()
156
170
 
@@ -159,9 +173,9 @@ class ConnectionMaker(object):
159
173
  Creates a context-managed SQLAlchemy session with the configured engine and session options.
160
174
 
161
175
  :param kwargs: Additional keyword arguments for session creation.
162
- :return: A context manager yielding a SQLAlchemy ``Session``.
176
+ :return: A context manager yielding a database session object.
163
177
  """
164
- return contextlib.closing(sqlalchemy.orm.sessionmaker(self.engine, **{**self.session_opts, **kwargs})())
178
+ return sqlalchemy.orm.sessionmaker(self.engine, **{**self.session_opts, **kwargs})()
165
179
 
166
180
  def create_model(self, orm_base):
167
181
  """
@@ -237,27 +251,34 @@ class AsyncConnectionMaker(ConnectionMaker):
237
251
 
238
252
  :return: The SQLAlchemy ``AsyncEngine``.
239
253
  """
240
- return sqlalchemy.ext.asyncio.create_async_engine(self.connection_string, **self.engine_opts)
254
+ if self.sa_engine is None:
255
+ self.sa_engine = sqlalchemy.ext.asyncio.create_async_engine(self.connection_string, **self.engine_opts)
256
+ return self.sa_engine
257
+
258
+ async def reset_engine(self):
259
+ """
260
+ Disposes of the current SQLAlchemy async engine, if it exists, and resets the cached engine instance.
261
+ """
262
+ if self.sa_engine is not None:
263
+ await self.sa_engine.dispose()
264
+ self.sa_engine = None
241
265
 
242
- async def make_connection(self) -> sqlalchemy.ext.asyncio.AsyncConnection:
266
+ def make_connection(self) -> contextlib.AbstractAsyncContextManager[sqlalchemy.ext.asyncio.AsyncConnection]:
243
267
  """
244
- Asynchronously establishes and returns a new database connection using the SQLAlchemy async engine.
268
+ Establishes and returns a new asynchronous database connection using the SQLAlchemy async engine.
245
269
 
246
- :return: A database connection object.
270
+ :return: An asynchronous context manager yielding a database connection object.
247
271
  """
248
- return await self.engine.connect()
272
+ return self.engine.begin()
249
273
 
250
- def make_session(self, **kwargs) -> contextlib.AbstractAsyncContextManager[
251
- sqlalchemy.ext.asyncio.AsyncSession
252
- ]:
274
+ def make_session(self, **kwargs) -> contextlib.AbstractAsyncContextManager[sqlalchemy.ext.asyncio.AsyncSession]:
253
275
  """
254
276
  Creates a context-managed asynchronous SQLAlchemy session with the configured async engine and session options.
255
277
 
256
278
  :param kwargs: Additional keyword arguments for session creation.
257
- :return: A context manager yielding a SQLAlchemy ``AsyncSession``.
279
+ :return: An asynchronous context manager yielding a database session object.
258
280
  """
259
- return contextlib.aclosing(
260
- sqlalchemy.ext.asyncio.async_sessionmaker(self.engine, **{**self.session_opts, **kwargs})())
281
+ return sqlalchemy.ext.asyncio.async_sessionmaker(self.engine, **{**self.session_opts, **kwargs})()
261
282
 
262
283
  async def create_model(self, orm_base):
263
284
  """
@@ -269,7 +290,7 @@ class AsyncConnectionMaker(ConnectionMaker):
269
290
  if not isinstance(orm_base, type) or not issubclass(orm_base, sqlalchemy.orm.DeclarativeBase):
270
291
  raise TypeError("not a subclass of 'sqlalchemy.orm.DeclarativeBase'")
271
292
 
272
- async with self.engine.begin() as conn:
293
+ async with self.make_connection() as conn:
273
294
  await conn.run_sync(orm_base.metadata.create_all)
274
295
 
275
296
  async def drop_model(self, orm_base):
@@ -282,7 +303,7 @@ class AsyncConnectionMaker(ConnectionMaker):
282
303
  if not isinstance(orm_base, type) or not issubclass(orm_base, sqlalchemy.orm.DeclarativeBase):
283
304
  raise TypeError("not a subclass of 'sqlalchemy.orm.DeclarativeBase'")
284
305
 
285
- async with self.engine.begin() as conn:
306
+ async with self.make_connection() as conn:
286
307
  await conn.run_sync(orm_base.metadata.drop_all)
287
308
 
288
309
  async def execute(self, sql: str, **params):
@@ -368,48 +389,68 @@ def orm_clone(orm, exclude: set[str] = None, no_autoincrement: bool = False):
368
389
 
369
390
 
370
391
  @contextlib.contextmanager
371
- def mysql_insert_ignore():
392
+ def sql_compiler_extension(connection_maker: ConnectionMaker, extension: Callable[[bool], ...]):
372
393
  """
373
- Registers a SQLAlchemy compiler extension to add ``IGNORE`` to MySQL ``INSERT`` statements.
394
+ A context manager to temporarily enable a SQL compiler extension for the duration of the context block.
395
+
396
+ The provided ``extension`` callable should accept a boolean argument to enable or disable the extension, and the
397
+ context manager will reset the SQLAlchemy engine before and after the context block to ensure the extension is
398
+ applied correctly.
374
399
  """
400
+ connection_maker.reset_engine()
401
+ extension(True)
402
+ try:
403
+ yield
404
+ finally:
405
+ extension(False)
406
+ connection_maker.reset_engine()
375
407
 
376
- def context(enabled: bool):
377
- @sqlalchemy.ext.compiler.compiles(sqlalchemy.sql.Insert, Dialects.mysql)
378
- def dispatch(insert: sqlalchemy.sql.Insert, compiler: sqlalchemy.sql.compiler.SQLCompiler, **kwargs) -> str:
379
- if not enabled:
380
- return compiler.visit_insert(insert, **kwargs)
381
408
 
382
- return compiler.visit_insert(insert.prefix_with("IGNORE"), **kwargs)
409
+ @contextlib.asynccontextmanager
410
+ async def async_sql_compiler_extension(connection_maker: AsyncConnectionMaker, extension: Callable[[bool], ...]):
411
+ """
412
+ A context manager to temporarily enable a SQL compiler extension for the duration of the context block.
383
413
 
384
- context(True)
414
+ The provided ``extension`` callable should accept a boolean argument to enable or disable the extension, and the
415
+ context manager will reset the SQLAlchemy engine before and after the context block to ensure the extension is
416
+ applied correctly.
417
+ """
418
+ await connection_maker.reset_engine()
419
+ extension(True)
385
420
  try:
386
421
  yield
387
422
  finally:
388
- context(False)
423
+ extension(False)
424
+ await connection_maker.reset_engine()
389
425
 
390
426
 
391
- @contextlib.contextmanager
392
- def postgresql_insert_on_conflict_do_nothing():
427
+ def mysql_insert_ignore(enabled: bool):
428
+ """
429
+ Registers a SQLAlchemy compiler extension to add ``IGNORE`` to MySQL ``INSERT`` statements.
430
+ """
431
+
432
+ @sqlalchemy.ext.compiler.compiles(sqlalchemy.sql.Insert, Dialects.mysql)
433
+ def dispatch(insert: sqlalchemy.sql.Insert, compiler: sqlalchemy.sql.compiler.SQLCompiler, **kwargs) -> str:
434
+ if not enabled:
435
+ return compiler.visit_insert(insert, **kwargs)
436
+
437
+ return compiler.visit_insert(insert.prefix_with("IGNORE"), **kwargs)
438
+
439
+
440
+ def postgresql_insert_on_conflict_do_nothing(enabled: bool):
393
441
  """
394
442
  Registers a SQLAlchemy compiler extension to add ``ON CONFLICT DO NOTHING`` to Postgresql ``INSERT`` statements.
395
443
  """
396
444
 
397
- def context(enabled: bool):
398
- @sqlalchemy.ext.compiler.compiles(sqlalchemy.sql.Insert, Dialects.postgresql)
399
- def dispatch(insert: sqlalchemy.sql.Insert, compiler: sqlalchemy.sql.compiler.SQLCompiler, **kwargs) -> str:
400
- if not enabled:
401
- return compiler.visit_insert(insert, **kwargs)
402
-
403
- statement = compiler.visit_insert(insert, **kwargs)
404
- # If we have a ``RETURNING`` clause, we must insert before it
405
- returning_position = statement.find("RETURNING")
406
- if returning_position >= 0:
407
- return statement[:returning_position] + " ON CONFLICT DO NOTHING " + statement[returning_position:]
408
- else:
409
- return statement + " ON CONFLICT DO NOTHING"
410
-
411
- context(True)
412
- try:
413
- yield
414
- finally:
415
- context(False)
445
+ @sqlalchemy.ext.compiler.compiles(sqlalchemy.sql.Insert, Dialects.postgresql)
446
+ def dispatch(insert: sqlalchemy.sql.Insert, compiler: sqlalchemy.sql.compiler.SQLCompiler, **kwargs) -> str:
447
+ if not enabled:
448
+ return compiler.visit_insert(insert, **kwargs)
449
+
450
+ statement = compiler.visit_insert(insert, **kwargs)
451
+ # If we have a ``RETURNING`` clause, we must insert before it
452
+ returning_position = statement.find("RETURNING")
453
+ if returning_position >= 0:
454
+ return statement[:returning_position] + " ON CONFLICT DO NOTHING " + statement[returning_position:]
455
+ else:
456
+ return statement + " ON CONFLICT DO NOTHING"
@@ -17,8 +17,6 @@ __all__ = [
17
17
  "glob_match",
18
18
  "scan_files",
19
19
  "copy_files",
20
- "run",
21
- "execute",
22
20
  ]
23
21
 
24
22
 
@@ -74,41 +72,32 @@ def make_path(
74
72
  return path
75
73
 
76
74
 
77
- if sys.version_info < (3, 14):
78
- def fn_suffix(filename: str | os.PathLike[str] | None) -> str | None:
79
- """
80
- Extracts the filename suffix from the given filename or path.
75
+ def fn_suffix(filename: str | os.PathLike[str] | None) -> str | None:
76
+ """
77
+ Extracts the filename suffix from the given filename or path.
81
78
 
82
- :param filename: The specific filename or path.
83
- :return: The filename suffix, including the leading dot (e.g., ".txt" for "file.txt"),
84
- or ``None`` if the input filename is ``None``.
85
- """
86
- if (path := make_path(filename)) is not None:
79
+ :param filename: The specific filename or path.
80
+ :return: The filename suffix, including the leading dot (e.g., ".txt" for "file.txt"),
81
+ or ``None`` if the input filename is ``None``.
82
+ """
83
+ if (path := make_path(filename)) is not None:
84
+ if sys.version_info < (3, 14):
87
85
  _, suffix_name = os.path.splitext(path.name)
88
86
  return suffix_name
89
- return None
90
- else:
91
- def fn_suffix(filename: str | os.PathLike[str] | None) -> str | None:
92
- """
93
- Extracts the filename suffix from the given filename or path.
94
-
95
- :param filename: The specific filename or path.
96
- :return: The filename suffix, including the leading dot (e.g., ".txt" for "file.txt"),
97
- or ``None`` if the input filename is ``None``.
98
- """
99
- if (path := make_path(filename)) is not None:
87
+ else:
100
88
  return path.suffix
101
- return None
89
+ return None
90
+
102
91
 
103
- if sys.version_info < (3, 14):
104
- def fn_suffixes(filename: str | os.PathLike[str] | None) -> list[str]:
105
- """
106
- Extracts all filename suffixes from the given filename or path.
92
+ def fn_suffixes(filename: str | os.PathLike[str] | None) -> list[str]:
93
+ """
94
+ Extracts all filename suffixes from the given filename or path.
107
95
 
108
- :param filename: The specific filename or path.
109
- :return: A list of filename suffixes, each including the leading dot (e.g., [".tar", ".gz"] for "archive.tar.gz").
110
- """
111
- if (path := make_path(filename)) is not None:
96
+ :param filename: The specific filename or path.
97
+ :return: A list of filename suffixes, each including the leading dot (e.g., [".tar", ".gz"] for "archive.tar.gz").
98
+ """
99
+ if (path := make_path(filename)) is not None:
100
+ if sys.version_info < (3, 14):
112
101
  results = []
113
102
  stem_name = path.name
114
103
  while True:
@@ -116,29 +105,21 @@ if sys.version_info < (3, 14):
116
105
  if is_empty(suffix_name):
117
106
  return list(reversed(results))
118
107
  results.append(suffix_name)
119
- return []
120
- else:
121
- def fn_suffixes(filename: str | os.PathLike[str] | None) -> list[str]:
122
- """
123
- Extracts all filename suffixes from the given filename or path.
124
-
125
- :param filename: The specific filename or path.
126
- :return: A list of filename suffixes, each including the leading dot (e.g., [".tar", ".gz"] for "archive.tar.gz").
127
- """
128
- if (path := make_path(filename)) is not None:
108
+ else:
129
109
  return path.suffixes
130
- return []
131
-
132
- if sys.version_info < (3, 14):
133
- def fn_stem(filename: str | os.PathLike[str] | None, minimal: bool = False) -> str | None:
134
- """
135
- Extracts the filename stem from the given filename or path.
136
-
137
- :param filename: The specific filename or path.
138
- :param minimal: If ``True``, extracts the minimal (shortest) stem, otherwise the default stem.
139
- :return: The filename stem.
140
- """
141
- if (path := make_path(filename)) is not None:
110
+ return []
111
+
112
+
113
+ def fn_stem(filename: str | os.PathLike[str] | None, minimal: bool = False) -> str | None:
114
+ """
115
+ Extracts the filename stem from the given filename or path.
116
+
117
+ :param filename: The specific filename or path.
118
+ :param minimal: If ``True``, extracts the minimal (shortest) stem, otherwise the default stem.
119
+ :return: The filename stem.
120
+ """
121
+ if (path := make_path(filename)) is not None:
122
+ if sys.version_info < (3, 14):
142
123
  if not minimal:
143
124
  stem_name, _ = os.path.splitext(path.name)
144
125
  return stem_name
@@ -148,17 +129,7 @@ if sys.version_info < (3, 14):
148
129
  stem_name, suffix_name = os.path.splitext(stem_name)
149
130
  if is_empty(suffix_name):
150
131
  return stem_name
151
- return None
152
- else:
153
- def fn_stem(filename: str | os.PathLike[str] | None, minimal: bool = False) -> str | None:
154
- """
155
- Extracts the filename stem from the given filename or path.
156
-
157
- :param filename: The specific filename or path.
158
- :param minimal: If ``True``, extracts the minimal (shortest) stem, otherwise the default stem.
159
- :return: The filename stem.
160
- """
161
- if (path := make_path(filename)) is not None:
132
+ else:
162
133
  if not minimal:
163
134
  return path.stem
164
135
  else:
@@ -166,7 +137,7 @@ else:
166
137
  if is_empty(path.suffix):
167
138
  return path.stem
168
139
  path = path.with_suffix("")
169
- return None
140
+ return None
170
141
 
171
142
 
172
143
  def path_depth(root: str | os.PathLike[str], child: str | os.PathLike[str]) -> int:
@@ -295,26 +266,3 @@ def copy_files(
295
266
  ignore_dangling_symlinks=ignore_dangling_symlinks,
296
267
  dirs_exist_ok=dirs_exist_ok,
297
268
  copy_function=copy_func)
298
-
299
-
300
- def run(cmd: str) -> bool:
301
- """
302
- Runs the given command and returns the success status.
303
-
304
- :param cmd: Command to run.
305
- :return: ``True`` if the command has been successfully run, ``False`` otherwise.
306
- """
307
- return os.system(cmd) == 0
308
-
309
-
310
- def execute(cmd: str, strip: bool = True) -> str:
311
- """
312
- Executes the given command and returns contents collected from standard output.
313
-
314
- :param cmd: Command to execute.
315
- :param strip: If ``True``, the contents will be stripped.
316
- :return: The content from standard output.
317
- """
318
- if strip:
319
- return os.popen(cmd).read().strip()
320
- return os.popen(cmd).read()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iker-python-common
3
- Version: 1.0.71
3
+ Version: 1.0.73
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.12
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -10,16 +10,16 @@ setup.py
10
10
  resources/unittest/config/config.cfg
11
11
  resources/unittest/csvutils/data.csv
12
12
  resources/unittest/csvutils/data.tsv
13
- resources/unittest/shutils/dir.baz/file.bar.baz
14
- resources/unittest/shutils/dir.baz/file.foo.bar
15
- resources/unittest/shutils/dir.baz/file.foo.baz
16
- resources/unittest/shutils/dir.foo/file.bar
17
- resources/unittest/shutils/dir.foo/file.baz
18
- resources/unittest/shutils/dir.foo/file.foo
19
- resources/unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz
20
- resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar
21
- resources/unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz
22
- resources/unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz
13
+ resources/unittest/pathutils/dir.baz/file.bar.baz
14
+ resources/unittest/pathutils/dir.baz/file.foo.bar
15
+ resources/unittest/pathutils/dir.baz/file.foo.baz
16
+ resources/unittest/pathutils/dir.foo/file.bar
17
+ resources/unittest/pathutils/dir.foo/file.baz
18
+ resources/unittest/pathutils/dir.foo/file.foo
19
+ resources/unittest/pathutils/dir.foo/dir.foo.bar/file.bar.baz
20
+ resources/unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar
21
+ resources/unittest/pathutils/dir.foo/dir.foo.bar/file.foo.baz
22
+ resources/unittest/pathutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz
23
23
  src/iker/common/__init__.py
24
24
  src/iker/common/utils/__init__.py
25
25
  src/iker/common/utils/argutils.py
@@ -32,9 +32,9 @@ src/iker/common/utils/iterutils.py
32
32
  src/iker/common/utils/jsonutils.py
33
33
  src/iker/common/utils/logger.py
34
34
  src/iker/common/utils/numutils.py
35
+ src/iker/common/utils/pathutils.py
35
36
  src/iker/common/utils/randutils.py
36
37
  src/iker/common/utils/retry.py
37
- src/iker/common/utils/shutils.py
38
38
  src/iker/common/utils/span.py
39
39
  src/iker/common/utils/strutils.py
40
40
  src/iker/common/utils/testutils.py
@@ -59,9 +59,9 @@ test/iker_tests/common/utils/iterutils_test.py
59
59
  test/iker_tests/common/utils/jsonutils_test.py
60
60
  test/iker_tests/common/utils/logger_test.py
61
61
  test/iker_tests/common/utils/numutils_test.py
62
+ test/iker_tests/common/utils/pathutils_test.py
62
63
  test/iker_tests/common/utils/randutils_test.py
63
64
  test/iker_tests/common/utils/retry_test.py
64
- test/iker_tests/common/utils/shutils_test.py
65
65
  test/iker_tests/common/utils/span_test.py
66
66
  test/iker_tests/common/utils/strutils_test.py
67
67
  test/iker_tests/common/utils/testutils_test.py
@@ -13,6 +13,7 @@ import sqlalchemy.exc as sa_exc
13
13
  import sqlalchemy.orm as sa_orm
14
14
 
15
15
  from iker.common.utils.dbutils import AsyncConnectionMaker, ConnectionMaker, Dialects, Drivers
16
+ from iker.common.utils.dbutils import async_sql_compiler_extension, sql_compiler_extension
16
17
  from iker.common.utils.dbutils import make_scheme
17
18
  from iker.common.utils.dbutils import mysql_insert_ignore, postgresql_insert_on_conflict_do_nothing
18
19
  from iker.common.utils.dbutils import orm_clone
@@ -190,7 +191,19 @@ def test_mysql_connection_maker(fixture_mysql_test_proc, fixture_mysql_test):
190
191
 
191
192
  result = records[0]
192
193
 
193
- with mysql_insert_ignore():
194
+ with maker.make_session() as session:
195
+ # PK violation, which causes ``IntegrityError``
196
+ with pytest.raises(sa_exc.IntegrityError):
197
+ session.add(orm_clone(result))
198
+ session.commit()
199
+
200
+ with maker.make_session() as session:
201
+ # Unique index violation, which causes ``IntegrityError``
202
+ with pytest.raises(sa_exc.IntegrityError):
203
+ session.add(orm_clone(result, no_autoincrement=True))
204
+ session.commit()
205
+
206
+ with sql_compiler_extension(maker, mysql_insert_ignore):
194
207
  with maker.make_session() as session:
195
208
  # PK violation, but suppressed by the compiler plugin
196
209
  session.add(orm_clone(result))
@@ -331,7 +344,8 @@ def test_mysql_connection_maker__sql_text(fixture_mysql_test_proc, fixture_mysql
331
344
  maker.execute(
332
345
  # language=mysql
333
346
  """
334
- INSERT IGNORE INTO `mysql_dummy_record` (`dummy_id`,
347
+ INSERT
348
+ IGNORE INTO `mysql_dummy_record` (`dummy_id`,
335
349
  `dummy_char`,
336
350
  `dummy_varchar`,
337
351
  `dummy_text`,
@@ -383,7 +397,8 @@ def test_mysql_connection_maker__sql_text(fixture_mysql_test_proc, fixture_mysql
383
397
  maker.execute(
384
398
  # language=mysql
385
399
  """
386
- INSERT IGNORE INTO `mysql_dummy_record` (`dummy_char`,
400
+ INSERT
401
+ IGNORE INTO `mysql_dummy_record` (`dummy_char`,
387
402
  `dummy_varchar`,
388
403
  `dummy_text`,
389
404
  `dummy_boolean`,
@@ -624,7 +639,19 @@ def test_postgresql_connection_maker(fixture_postgresql_test_proc, fixture_postg
624
639
 
625
640
  result = records[0]
626
641
 
627
- with postgresql_insert_on_conflict_do_nothing():
642
+ with maker.make_session() as session:
643
+ # PK violation, which causes ``IntegrityError``
644
+ with pytest.raises(sa_exc.IntegrityError):
645
+ session.add(orm_clone(result))
646
+ session.commit()
647
+
648
+ with maker.make_session() as session:
649
+ # Unique index violation, which causes ``IntegrityError``
650
+ with pytest.raises(sa_exc.IntegrityError):
651
+ session.add(orm_clone(result, no_autoincrement=True))
652
+ session.commit()
653
+
654
+ with sql_compiler_extension(maker, postgresql_insert_on_conflict_do_nothing):
628
655
  with maker.make_session() as session:
629
656
  # PK violation, but suppressed by the compiler plugin
630
657
  session.add(orm_clone(result))
@@ -1122,7 +1149,19 @@ async def test_async_postgresql_connection_maker(fixture_postgresql_test_proc, f
1122
1149
 
1123
1150
  result = records[0]
1124
1151
 
1125
- with postgresql_insert_on_conflict_do_nothing():
1152
+ async with maker.make_session() as session:
1153
+ # PK violation, which causes ``IntegrityError``
1154
+ with pytest.raises(sa_exc.IntegrityError):
1155
+ session.add(orm_clone(result))
1156
+ await session.commit()
1157
+
1158
+ async with maker.make_session() as session:
1159
+ # Unique index violation, which causes ``IntegrityError``
1160
+ with pytest.raises(sa_exc.IntegrityError):
1161
+ session.add(orm_clone(result, no_autoincrement=True))
1162
+ await session.commit()
1163
+
1164
+ async with async_sql_compiler_extension(maker, postgresql_insert_on_conflict_do_nothing):
1126
1165
  async with maker.make_session() as session:
1127
1166
  # PK violation, but suppressed by the compiler plugin
1128
1167
  session.add(orm_clone(result))
@@ -5,10 +5,10 @@ import unittest
5
5
 
6
6
  import ddt
7
7
 
8
- from iker.common.utils.shutils import copy_files, scan_files
9
- from iker.common.utils.shutils import fn_stem, fn_suffix, fn_suffixes
10
- from iker.common.utils.shutils import glob_match
11
- from iker.common.utils.shutils import make_path, path_depth
8
+ from iker.common.utils.pathutils import copy_files, scan_files
9
+ from iker.common.utils.pathutils import fn_stem, fn_suffix, fn_suffixes
10
+ from iker.common.utils.pathutils import glob_match
11
+ from iker.common.utils.pathutils import make_path, path_depth
12
12
  from testenv import resources_directory
13
13
 
14
14
  work_dir = pathlib.Path.cwd
@@ -16,8 +16,10 @@ home_dir = pathlib.Path.home
16
16
 
17
17
 
18
18
  @ddt.ddt
19
- class ShUtilsTest(unittest.TestCase):
19
+ class PathUtilsTest(unittest.TestCase):
20
20
  data_fn_suffix = [
21
+ (None, None),
22
+ ("", ""),
21
23
  (".", ""),
22
24
  ("..", ""),
23
25
  ("...", ""),
@@ -49,6 +51,8 @@ class ShUtilsTest(unittest.TestCase):
49
51
  self.assertEqual(expect, fn_suffix(data))
50
52
 
51
53
  data_fn_suffixes = [
54
+ (None, []),
55
+ ("", []),
52
56
  (".", []),
53
57
  ("..", []),
54
58
  ("...", []),
@@ -80,6 +84,8 @@ class ShUtilsTest(unittest.TestCase):
80
84
  self.assertEqual(expect, fn_suffixes(data))
81
85
 
82
86
  data_fn_stem = [
87
+ (None, None),
88
+ ("", ""),
83
89
  (".", ""),
84
90
  ("..", ".."),
85
91
  ("...", "..."),
@@ -111,6 +117,8 @@ class ShUtilsTest(unittest.TestCase):
111
117
  self.assertEqual(expect, fn_stem(data))
112
118
 
113
119
  data_fn_stem__minimal = [
120
+ (None, None),
121
+ ("", ""),
114
122
  (".", ""),
115
123
  ("..", ".."),
116
124
  ("...", "..."),
@@ -289,133 +297,133 @@ class ShUtilsTest(unittest.TestCase):
289
297
 
290
298
  data_scan_files = [
291
299
  (
292
- "unittest/shutils",
300
+ "unittest/pathutils",
293
301
  [],
294
302
  [],
295
303
  0,
296
304
  [
297
- "unittest/shutils/dir.baz/file.foo.bar",
298
- "unittest/shutils/dir.baz/file.foo.baz",
299
- "unittest/shutils/dir.baz/file.bar.baz",
300
- "unittest/shutils/dir.foo/file.bar",
301
- "unittest/shutils/dir.foo/file.baz",
302
- "unittest/shutils/dir.foo/file.foo",
303
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar",
304
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz",
305
- "unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz",
306
- "unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz",
305
+ "unittest/pathutils/dir.baz/file.foo.bar",
306
+ "unittest/pathutils/dir.baz/file.foo.baz",
307
+ "unittest/pathutils/dir.baz/file.bar.baz",
308
+ "unittest/pathutils/dir.foo/file.bar",
309
+ "unittest/pathutils/dir.foo/file.baz",
310
+ "unittest/pathutils/dir.foo/file.foo",
311
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar",
312
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.baz",
313
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.bar.baz",
314
+ "unittest/pathutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz",
307
315
  ],
308
316
  ),
309
317
  (
310
- "unittest/shutils/dir.foo",
318
+ "unittest/pathutils/dir.foo",
311
319
  [],
312
320
  [],
313
321
  0,
314
322
  [
315
- "unittest/shutils/dir.foo/file.bar",
316
- "unittest/shutils/dir.foo/file.baz",
317
- "unittest/shutils/dir.foo/file.foo",
318
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar",
319
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz",
320
- "unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz",
321
- "unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz",
323
+ "unittest/pathutils/dir.foo/file.bar",
324
+ "unittest/pathutils/dir.foo/file.baz",
325
+ "unittest/pathutils/dir.foo/file.foo",
326
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar",
327
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.baz",
328
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.bar.baz",
329
+ "unittest/pathutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz",
322
330
  ],
323
331
  ),
324
332
  (
325
- "unittest/shutils/dir.baz",
333
+ "unittest/pathutils/dir.baz",
326
334
  [],
327
335
  [],
328
336
  0,
329
337
  [
330
- "unittest/shutils/dir.baz/file.foo.bar",
331
- "unittest/shutils/dir.baz/file.foo.baz",
332
- "unittest/shutils/dir.baz/file.bar.baz",
338
+ "unittest/pathutils/dir.baz/file.foo.bar",
339
+ "unittest/pathutils/dir.baz/file.foo.baz",
340
+ "unittest/pathutils/dir.baz/file.bar.baz",
333
341
  ],
334
342
  ),
335
343
  (
336
- "unittest/shutils",
344
+ "unittest/pathutils",
337
345
  ["*.foo", "*.bar"],
338
346
  ["*.foo.bar"],
339
347
  0,
340
348
  [
341
- "unittest/shutils/dir.foo/file.bar",
342
- "unittest/shutils/dir.foo/file.foo",
349
+ "unittest/pathutils/dir.foo/file.bar",
350
+ "unittest/pathutils/dir.foo/file.foo",
343
351
  ],
344
352
  ),
345
353
  (
346
- "unittest/shutils",
354
+ "unittest/pathutils",
347
355
  ["*.foo", "*.bar"],
348
356
  [],
349
357
  0,
350
358
  [
351
- "unittest/shutils/dir.baz/file.foo.bar",
352
- "unittest/shutils/dir.foo/file.bar",
353
- "unittest/shutils/dir.foo/file.foo",
354
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar",
359
+ "unittest/pathutils/dir.baz/file.foo.bar",
360
+ "unittest/pathutils/dir.foo/file.bar",
361
+ "unittest/pathutils/dir.foo/file.foo",
362
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar",
355
363
  ],
356
364
  ),
357
365
  (
358
- "unittest/shutils",
366
+ "unittest/pathutils",
359
367
  [],
360
368
  ["*.baz"],
361
369
  0,
362
370
  [
363
- "unittest/shutils/dir.baz/file.foo.bar",
364
- "unittest/shutils/dir.foo/file.bar",
365
- "unittest/shutils/dir.foo/file.foo",
366
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar",
371
+ "unittest/pathutils/dir.baz/file.foo.bar",
372
+ "unittest/pathutils/dir.foo/file.bar",
373
+ "unittest/pathutils/dir.foo/file.foo",
374
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar",
367
375
  ],
368
376
  ),
369
377
  (
370
- "unittest/shutils",
378
+ "unittest/pathutils",
371
379
  [],
372
380
  [],
373
381
  2,
374
382
  [
375
- "unittest/shutils/dir.baz/file.foo.bar",
376
- "unittest/shutils/dir.baz/file.foo.baz",
377
- "unittest/shutils/dir.baz/file.bar.baz",
378
- "unittest/shutils/dir.foo/file.bar",
379
- "unittest/shutils/dir.foo/file.baz",
380
- "unittest/shutils/dir.foo/file.foo",
383
+ "unittest/pathutils/dir.baz/file.foo.bar",
384
+ "unittest/pathutils/dir.baz/file.foo.baz",
385
+ "unittest/pathutils/dir.baz/file.bar.baz",
386
+ "unittest/pathutils/dir.foo/file.bar",
387
+ "unittest/pathutils/dir.foo/file.baz",
388
+ "unittest/pathutils/dir.foo/file.foo",
381
389
  ],
382
390
  ),
383
391
  (
384
- "unittest/shutils/dir.foo/file.bar",
392
+ "unittest/pathutils/dir.foo/file.bar",
385
393
  [],
386
394
  [],
387
395
  0,
388
396
  [
389
- "unittest/shutils/dir.foo/file.bar",
397
+ "unittest/pathutils/dir.foo/file.bar",
390
398
  ],
391
399
  ),
392
400
  (
393
- "unittest/shutils/dir.foo/file.bar",
401
+ "unittest/pathutils/dir.foo/file.bar",
394
402
  [],
395
403
  [],
396
404
  2,
397
405
  [
398
- "unittest/shutils/dir.foo/file.bar",
406
+ "unittest/pathutils/dir.foo/file.bar",
399
407
  ],
400
408
  ),
401
409
  (
402
- "unittest/shutils/dir.foo/file.bar",
410
+ "unittest/pathutils/dir.foo/file.bar",
403
411
  ["*.foo", "*.bar"],
404
412
  ["*.baz"],
405
413
  0,
406
414
  [
407
- "unittest/shutils/dir.foo/file.bar",
415
+ "unittest/pathutils/dir.foo/file.bar",
408
416
  ],
409
417
  ),
410
418
  (
411
- "unittest/shutils/dir.foo/file.bar",
419
+ "unittest/pathutils/dir.foo/file.bar",
412
420
  ["*.foo"],
413
421
  [],
414
422
  0,
415
423
  [],
416
424
  ),
417
425
  (
418
- "unittest/shutils/dir.foo/file.bar",
426
+ "unittest/pathutils/dir.foo/file.bar",
419
427
  [],
420
428
  ["*.bar"],
421
429
  0,
@@ -436,145 +444,145 @@ class ShUtilsTest(unittest.TestCase):
436
444
 
437
445
  data_copy_files = [
438
446
  (
439
- "unittest/shutils",
440
- "unittest/shutils",
447
+ "unittest/pathutils",
448
+ "unittest/pathutils",
441
449
  [],
442
450
  [],
443
451
  0,
444
452
  [
445
- "unittest/shutils/dir.baz/file.foo.bar",
446
- "unittest/shutils/dir.baz/file.foo.baz",
447
- "unittest/shutils/dir.baz/file.bar.baz",
448
- "unittest/shutils/dir.foo/file.bar",
449
- "unittest/shutils/dir.foo/file.baz",
450
- "unittest/shutils/dir.foo/file.foo",
451
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar",
452
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz",
453
- "unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz",
454
- "unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz",
453
+ "unittest/pathutils/dir.baz/file.foo.bar",
454
+ "unittest/pathutils/dir.baz/file.foo.baz",
455
+ "unittest/pathutils/dir.baz/file.bar.baz",
456
+ "unittest/pathutils/dir.foo/file.bar",
457
+ "unittest/pathutils/dir.foo/file.baz",
458
+ "unittest/pathutils/dir.foo/file.foo",
459
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar",
460
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.baz",
461
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.bar.baz",
462
+ "unittest/pathutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz",
455
463
  ],
456
464
  ),
457
465
  (
458
- "unittest/shutils/dir.foo",
459
- "unittest/shutils/dir.foo",
466
+ "unittest/pathutils/dir.foo",
467
+ "unittest/pathutils/dir.foo",
460
468
  [],
461
469
  [],
462
470
  0,
463
471
  [
464
- "unittest/shutils/dir.foo/file.bar",
465
- "unittest/shutils/dir.foo/file.baz",
466
- "unittest/shutils/dir.foo/file.foo",
467
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar",
468
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.baz",
469
- "unittest/shutils/dir.foo/dir.foo.bar/file.bar.baz",
470
- "unittest/shutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz",
472
+ "unittest/pathutils/dir.foo/file.bar",
473
+ "unittest/pathutils/dir.foo/file.baz",
474
+ "unittest/pathutils/dir.foo/file.foo",
475
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar",
476
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.baz",
477
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.bar.baz",
478
+ "unittest/pathutils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz",
471
479
  ],
472
480
  ),
473
481
  (
474
- "unittest/shutils/dir.baz",
475
- "unittest/shutils/dir.baz",
482
+ "unittest/pathutils/dir.baz",
483
+ "unittest/pathutils/dir.baz",
476
484
  [],
477
485
  [],
478
486
  0,
479
487
  [
480
- "unittest/shutils/dir.baz/file.foo.bar",
481
- "unittest/shutils/dir.baz/file.foo.baz",
482
- "unittest/shutils/dir.baz/file.bar.baz",
488
+ "unittest/pathutils/dir.baz/file.foo.bar",
489
+ "unittest/pathutils/dir.baz/file.foo.baz",
490
+ "unittest/pathutils/dir.baz/file.bar.baz",
483
491
  ],
484
492
  ),
485
493
  (
486
- "unittest/shutils",
487
- "unittest/shutils",
494
+ "unittest/pathutils",
495
+ "unittest/pathutils",
488
496
  ["*.foo", "*.bar"],
489
497
  ["*.foo.bar"],
490
498
  0,
491
499
  [
492
- "unittest/shutils/dir.foo/file.bar",
493
- "unittest/shutils/dir.foo/file.foo",
500
+ "unittest/pathutils/dir.foo/file.bar",
501
+ "unittest/pathutils/dir.foo/file.foo",
494
502
  ],
495
503
  ),
496
504
  (
497
- "unittest/shutils",
498
- "unittest/shutils",
505
+ "unittest/pathutils",
506
+ "unittest/pathutils",
499
507
  ["*.foo", "*.bar"],
500
508
  [],
501
509
  0,
502
510
  [
503
- "unittest/shutils/dir.baz/file.foo.bar",
504
- "unittest/shutils/dir.foo/file.bar",
505
- "unittest/shutils/dir.foo/file.foo",
506
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar",
511
+ "unittest/pathutils/dir.baz/file.foo.bar",
512
+ "unittest/pathutils/dir.foo/file.bar",
513
+ "unittest/pathutils/dir.foo/file.foo",
514
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar",
507
515
  ],
508
516
  ),
509
517
  (
510
- "unittest/shutils",
511
- "unittest/shutils",
518
+ "unittest/pathutils",
519
+ "unittest/pathutils",
512
520
  [],
513
521
  ["*.baz"],
514
522
  0,
515
523
  [
516
- "unittest/shutils/dir.baz/file.foo.bar",
517
- "unittest/shutils/dir.foo/file.bar",
518
- "unittest/shutils/dir.foo/file.foo",
519
- "unittest/shutils/dir.foo/dir.foo.bar/file.foo.bar",
524
+ "unittest/pathutils/dir.baz/file.foo.bar",
525
+ "unittest/pathutils/dir.foo/file.bar",
526
+ "unittest/pathutils/dir.foo/file.foo",
527
+ "unittest/pathutils/dir.foo/dir.foo.bar/file.foo.bar",
520
528
  ],
521
529
  ),
522
530
  (
523
- "unittest/shutils",
524
- "unittest/shutils",
531
+ "unittest/pathutils",
532
+ "unittest/pathutils",
525
533
  [],
526
534
  [],
527
535
  2,
528
536
  [
529
- "unittest/shutils/dir.baz/file.foo.bar",
530
- "unittest/shutils/dir.baz/file.foo.baz",
531
- "unittest/shutils/dir.baz/file.bar.baz",
532
- "unittest/shutils/dir.foo/file.bar",
533
- "unittest/shutils/dir.foo/file.baz",
534
- "unittest/shutils/dir.foo/file.foo",
537
+ "unittest/pathutils/dir.baz/file.foo.bar",
538
+ "unittest/pathutils/dir.baz/file.foo.baz",
539
+ "unittest/pathutils/dir.baz/file.bar.baz",
540
+ "unittest/pathutils/dir.foo/file.bar",
541
+ "unittest/pathutils/dir.foo/file.baz",
542
+ "unittest/pathutils/dir.foo/file.foo",
535
543
  ],
536
544
  ),
537
545
  (
538
- "unittest/shutils/dir.foo/file.bar",
539
- "unittest/shutils/dir.foo/file.bar",
546
+ "unittest/pathutils/dir.foo/file.bar",
547
+ "unittest/pathutils/dir.foo/file.bar",
540
548
  [],
541
549
  [],
542
550
  0,
543
551
  [
544
- "unittest/shutils/dir.foo/file.bar",
552
+ "unittest/pathutils/dir.foo/file.bar",
545
553
  ],
546
554
  ),
547
555
  (
548
- "unittest/shutils/dir.foo/file.bar",
549
- "unittest/shutils/dir.foo/file.bar",
556
+ "unittest/pathutils/dir.foo/file.bar",
557
+ "unittest/pathutils/dir.foo/file.bar",
550
558
  [],
551
559
  [],
552
560
  2,
553
561
  [
554
- "unittest/shutils/dir.foo/file.bar",
562
+ "unittest/pathutils/dir.foo/file.bar",
555
563
  ],
556
564
  ),
557
565
  (
558
- "unittest/shutils/dir.foo/file.bar",
559
- "unittest/shutils/dir.foo/file.bar",
566
+ "unittest/pathutils/dir.foo/file.bar",
567
+ "unittest/pathutils/dir.foo/file.bar",
560
568
  ["*.foo", "*.bar"],
561
569
  ["*.baz"],
562
570
  0,
563
571
  [
564
- "unittest/shutils/dir.foo/file.bar",
572
+ "unittest/pathutils/dir.foo/file.bar",
565
573
  ],
566
574
  ),
567
575
  (
568
- "unittest/shutils/dir.foo/file.bar",
569
- "unittest/shutils/dir.foo/file.bar",
576
+ "unittest/pathutils/dir.foo/file.bar",
577
+ "unittest/pathutils/dir.foo/file.bar",
570
578
  ["*.foo"],
571
579
  [],
572
580
  0,
573
581
  [],
574
582
  ),
575
583
  (
576
- "unittest/shutils/dir.foo/file.bar",
577
- "unittest/shutils/dir.foo/file.bar",
584
+ "unittest/pathutils/dir.foo/file.bar",
585
+ "unittest/pathutils/dir.foo/file.bar",
578
586
  [],
579
587
  ["*.bar"],
580
588
  0,