winipedia-utils 0.4.43__py3-none-any.whl → 0.7.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of winipedia-utils might be problematic. Click here for more details.
- winipedia_utils/artifacts/build.py +27 -0
- winipedia_utils/dev/artifacts/build.py +62 -0
- winipedia_utils/{text → dev/configs/base}/config.py +16 -11
- winipedia_utils/{git/gitignore/config.py → dev/configs/gitignore.py} +2 -2
- winipedia_utils/{git/pre_commit/config.py → dev/configs/pre_commit.py} +6 -6
- winipedia_utils/{projects/poetry/config.py → dev/configs/pyproject.py} +120 -32
- winipedia_utils/{testing/config.py → dev/configs/testing.py} +7 -4
- winipedia_utils/dev/configs/workflows/base/base.py +907 -0
- winipedia_utils/dev/configs/workflows/health_check.py +69 -0
- winipedia_utils/dev/configs/workflows/publish.py +51 -0
- winipedia_utils/dev/configs/workflows/release.py +91 -0
- winipedia_utils/dev/git/github/repo/__init__.py +1 -0
- winipedia_utils/{git → dev/git}/github/repo/protect.py +5 -5
- winipedia_utils/dev/git/pre_commit/hooks.py +85 -0
- winipedia_utils/{git → dev/git}/pre_commit/run_hooks.py +23 -13
- winipedia_utils/dev/projects/poetry/dev_deps.py +21 -0
- winipedia_utils/dev/projects/poetry/poetry.py +248 -0
- winipedia_utils/{projects → dev/projects}/project.py +6 -7
- winipedia_utils/dev/testing/__init__.py +1 -0
- winipedia_utils/{testing → dev/testing}/convention.py +1 -1
- winipedia_utils/{testing → dev/testing}/create_tests.py +14 -14
- winipedia_utils/dev/testing/tests/__init__.py +1 -0
- winipedia_utils/dev/testing/tests/base/__init__.py +1 -0
- winipedia_utils/dev/testing/tests/base/fixtures/__init__.py +1 -0
- winipedia_utils/{testing → dev/testing}/tests/base/fixtures/fixture.py +1 -1
- winipedia_utils/dev/testing/tests/base/fixtures/scopes/__init__.py +1 -0
- winipedia_utils/{testing → dev/testing}/tests/base/fixtures/scopes/class_.py +2 -2
- winipedia_utils/{testing → dev/testing}/tests/base/fixtures/scopes/module.py +2 -2
- winipedia_utils/{testing → dev/testing}/tests/base/fixtures/scopes/session.py +10 -10
- winipedia_utils/dev/testing/tests/base/utils/__init__.py +1 -0
- winipedia_utils/dev/testing/tests/base/utils/utils.py +1 -0
- winipedia_utils/{testing → dev/testing}/tests/conftest.py +2 -2
- winipedia_utils/{testing/tests/base/utils → dev/testing}/utils.py +7 -24
- winipedia_utils/setup.py +9 -5
- winipedia_utils/utils/__init__.py +1 -0
- winipedia_utils/utils/data/dataframe/__init__.py +1 -0
- winipedia_utils/{data → utils/data}/dataframe/cleaning.py +1 -1
- winipedia_utils/utils/data/structures/__init__.py +1 -0
- winipedia_utils/{text → utils/data/structures/text}/string.py +36 -3
- winipedia_utils/utils/git/__init__.py +1 -0
- winipedia_utils/utils/git/github/__init__.py +1 -0
- winipedia_utils/{git → utils/git}/github/github.py +1 -1
- winipedia_utils/utils/git/github/repo/__init__.py +1 -0
- winipedia_utils/{git → utils/git}/github/repo/repo.py +1 -1
- winipedia_utils/{git → utils/git}/gitignore/gitignore.py +2 -2
- winipedia_utils/{concurrent → utils/iterating/concurrent}/concurrent.py +4 -4
- winipedia_utils/{concurrent → utils/iterating/concurrent}/multiprocessing.py +2 -2
- winipedia_utils/{concurrent → utils/iterating/concurrent}/multithreading.py +1 -1
- winipedia_utils/{logging → utils/logging}/logger.py +1 -1
- winipedia_utils/{modules → utils/modules}/class_.py +15 -8
- winipedia_utils/{modules → utils/modules}/function.py +10 -4
- winipedia_utils/utils/modules/inspection.py +56 -0
- winipedia_utils/{modules → utils/modules}/module.py +27 -32
- winipedia_utils/{modules → utils/modules}/package.py +100 -34
- winipedia_utils/{oop → utils/oop}/mixins/meta.py +4 -4
- winipedia_utils/{oop → utils/oop}/mixins/mixin.py +2 -2
- winipedia_utils/{os → utils/os}/os.py +2 -2
- winipedia_utils/utils/resources/__init__.py +1 -0
- winipedia_utils/utils/resources/svgs/__init__.py +1 -0
- winipedia_utils/{resources → utils/resources}/svgs/svg.py +1 -1
- winipedia_utils/utils/testing/__init__.py +1 -0
- winipedia_utils/{testing → utils/testing}/assertions.py +18 -0
- winipedia_utils/{testing → utils/testing}/skip.py +1 -1
- {winipedia_utils-0.4.43.dist-info → winipedia_utils-0.7.1.dist-info}/METADATA +71 -36
- winipedia_utils-0.7.1.dist-info/RECORD +109 -0
- winipedia_utils/git/github/workflows/base/base.py +0 -294
- winipedia_utils/git/github/workflows/health_check.py +0 -57
- winipedia_utils/git/github/workflows/publish.py +0 -49
- winipedia_utils/git/github/workflows/release.py +0 -45
- winipedia_utils/git/pre_commit/hooks.py +0 -147
- winipedia_utils/projects/poetry/poetry.py +0 -129
- winipedia_utils/testing/__init__.py +0 -1
- winipedia_utils/testing/tests/__init__.py +0 -1
- winipedia_utils/testing/tests/base/__init__.py +0 -1
- winipedia_utils/testing/tests/base/fixtures/__init__.py +0 -1
- winipedia_utils/testing/tests/base/fixtures/scopes/__init__.py +0 -1
- winipedia_utils/testing/tests/base/utils/__init__.py +0 -1
- winipedia_utils-0.4.43.dist-info/RECORD +0 -94
- /winipedia_utils/{data/dataframe → artifacts}/__init__.py +0 -0
- /winipedia_utils/{data/structures → dev}/__init__.py +0 -0
- /winipedia_utils/{git/github → dev/artifacts}/__init__.py +0 -0
- /winipedia_utils/{git/github/repo → dev/configs}/__init__.py +0 -0
- /winipedia_utils/{git/github/workflows → dev/configs}/base/__init__.py +0 -0
- /winipedia_utils/{git/github → dev/configs}/workflows/__init__.py +0 -0
- /winipedia_utils/{resources → dev/configs/workflows/base}/__init__.py +0 -0
- /winipedia_utils/{git → dev/git}/__init__.py +0 -0
- /winipedia_utils/{resources/svgs → dev/git/github}/__init__.py +0 -0
- /winipedia_utils/{git → dev/git}/pre_commit/__init__.py +0 -0
- /winipedia_utils/{projects → dev/projects}/__init__.py +0 -0
- /winipedia_utils/{projects → dev/projects}/poetry/__init__.py +0 -0
- /winipedia_utils/{testing → dev/testing}/tests/base/fixtures/scopes/function.py +0 -0
- /winipedia_utils/{testing → dev/testing}/tests/base/fixtures/scopes/package.py +0 -0
- /winipedia_utils/{data → utils/data}/__init__.py +0 -0
- /winipedia_utils/{data → utils/data}/structures/dicts.py +0 -0
- /winipedia_utils/{text → utils/data/structures/text}/__init__.py +0 -0
- /winipedia_utils/{git → utils/git}/gitignore/__init__.py +0 -0
- /winipedia_utils/{iterating → utils/iterating}/__init__.py +0 -0
- /winipedia_utils/{concurrent → utils/iterating/concurrent}/__init__.py +0 -0
- /winipedia_utils/{iterating → utils/iterating}/iterate.py +0 -0
- /winipedia_utils/{logging → utils/logging}/__init__.py +0 -0
- /winipedia_utils/{logging → utils/logging}/ansi.py +0 -0
- /winipedia_utils/{logging → utils/logging}/config.py +0 -0
- /winipedia_utils/{modules → utils/modules}/__init__.py +0 -0
- /winipedia_utils/{oop → utils/oop}/__init__.py +0 -0
- /winipedia_utils/{oop → utils/oop}/mixins/__init__.py +0 -0
- /winipedia_utils/{os → utils/os}/__init__.py +0 -0
- /winipedia_utils/{resources → utils/resources}/svgs/delete_garbage_can.svg +0 -0
- /winipedia_utils/{resources → utils/resources}/svgs/download_arrow.svg +0 -0
- /winipedia_utils/{resources → utils/resources}/svgs/exit_fullscreen_icon.svg +0 -0
- /winipedia_utils/{resources → utils/resources}/svgs/fullscreen_icon.svg +0 -0
- /winipedia_utils/{resources → utils/resources}/svgs/menu_icon.svg +0 -0
- /winipedia_utils/{resources → utils/resources}/svgs/pause_icon.svg +0 -0
- /winipedia_utils/{resources → utils/resources}/svgs/play_icon.svg +0 -0
- /winipedia_utils/{resources → utils/resources}/svgs/plus_icon.svg +0 -0
- /winipedia_utils/{security → utils/security}/__init__.py +0 -0
- /winipedia_utils/{security → utils/security}/cryptography.py +0 -0
- /winipedia_utils/{security → utils/security}/keyring.py +0 -0
- /winipedia_utils/{testing → utils/testing}/fixtures.py +0 -0
- {winipedia_utils-0.4.43.dist-info → winipedia_utils-0.7.1.dist-info}/WHEEL +0 -0
- {winipedia_utils-0.4.43.dist-info → winipedia_utils-0.7.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -17,15 +17,19 @@ from collections.abc import Callable, Sequence
|
|
|
17
17
|
from importlib import import_module
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from types import ModuleType
|
|
20
|
-
from typing import Any
|
|
20
|
+
from typing import Any
|
|
21
21
|
|
|
22
|
-
from winipedia_utils.logging.logger import get_logger
|
|
23
|
-
from winipedia_utils.modules.class_ import (
|
|
22
|
+
from winipedia_utils.utils.logging.logger import get_logger
|
|
23
|
+
from winipedia_utils.utils.modules.class_ import (
|
|
24
24
|
get_all_cls_from_module,
|
|
25
25
|
get_all_methods_from_cls,
|
|
26
26
|
)
|
|
27
|
-
from winipedia_utils.modules.function import get_all_functions_from_module
|
|
28
|
-
from winipedia_utils.modules.
|
|
27
|
+
from winipedia_utils.utils.modules.function import get_all_functions_from_module
|
|
28
|
+
from winipedia_utils.utils.modules.inspection import (
|
|
29
|
+
get_qualname_of_obj,
|
|
30
|
+
get_unwrapped_obj,
|
|
31
|
+
)
|
|
32
|
+
from winipedia_utils.utils.modules.package import (
|
|
29
33
|
get_modules_and_packages_from_package,
|
|
30
34
|
make_dir_with_init_file,
|
|
31
35
|
module_is_package,
|
|
@@ -329,24 +333,6 @@ def get_default_module_content() -> str:
|
|
|
329
333
|
return '''"""module."""'''
|
|
330
334
|
|
|
331
335
|
|
|
332
|
-
def inside_frozen_bundle() -> bool:
|
|
333
|
-
"""Return True if the code is running inside a frozen bundle."""
|
|
334
|
-
return getattr(sys, "frozen", False)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
def get_def_line(obj: Any) -> int:
|
|
338
|
-
"""Return the line number where a method-like object is defined."""
|
|
339
|
-
if isinstance(obj, property):
|
|
340
|
-
obj = obj.fget
|
|
341
|
-
unwrapped = inspect.unwrap(obj)
|
|
342
|
-
if hasattr(unwrapped, "__code__"):
|
|
343
|
-
return int(unwrapped.__code__.co_firstlineno)
|
|
344
|
-
# getsourcelines does not work if in a pyinstaller bundle or something
|
|
345
|
-
if inside_frozen_bundle():
|
|
346
|
-
return 0
|
|
347
|
-
return inspect.getsourcelines(unwrapped)[1]
|
|
348
|
-
|
|
349
|
-
|
|
350
336
|
def get_module_of_obj(obj: Any, default: ModuleType | None = None) -> ModuleType:
|
|
351
337
|
"""Return the module name where a method-like object is defined.
|
|
352
338
|
|
|
@@ -368,14 +354,23 @@ def get_module_of_obj(obj: Any, default: ModuleType | None = None) -> ModuleType
|
|
|
368
354
|
return module
|
|
369
355
|
|
|
370
356
|
|
|
371
|
-
def
|
|
372
|
-
"""
|
|
373
|
-
unwrapped = get_unwrapped_obj(obj)
|
|
374
|
-
return cast("str", unwrapped.__qualname__)
|
|
357
|
+
def get_executing_module() -> ModuleType:
|
|
358
|
+
"""Get the module where execution has started.
|
|
375
359
|
|
|
360
|
+
The executing module is the module that contains the __main__ attribute as __name__
|
|
361
|
+
E.g. if you run `python -m winipedia_utils.setup` from the command line,
|
|
362
|
+
then the executing module is winipedia_utils.modules.setup
|
|
376
363
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
364
|
+
Returns:
|
|
365
|
+
The module where execution has started
|
|
366
|
+
|
|
367
|
+
Raises:
|
|
368
|
+
ValueError: If no __main__ module is found or if the executing module
|
|
369
|
+
cannot be determined
|
|
370
|
+
|
|
371
|
+
"""
|
|
372
|
+
main = sys.modules.get("__main__")
|
|
373
|
+
if main is None:
|
|
374
|
+
msg = "No __main__ module found"
|
|
375
|
+
raise ValueError(msg)
|
|
376
|
+
return main
|
|
@@ -9,18 +9,24 @@ The utilities support both static package analysis and dynamic package manipulat
|
|
|
9
9
|
making them suitable for code generation, testing frameworks, and package management.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
import importlib.metadata
|
|
13
|
+
import importlib.util
|
|
12
14
|
import os
|
|
13
15
|
import pkgutil
|
|
16
|
+
import re
|
|
14
17
|
import sys
|
|
15
18
|
from collections.abc import Generator, Iterable
|
|
16
19
|
from importlib import import_module
|
|
17
20
|
from pathlib import Path
|
|
18
21
|
from types import ModuleType
|
|
22
|
+
from typing import Any
|
|
19
23
|
|
|
24
|
+
import networkx as nx
|
|
20
25
|
from setuptools import find_namespace_packages as _find_namespace_packages
|
|
21
26
|
from setuptools import find_packages as _find_packages
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
import winipedia_utils
|
|
29
|
+
from winipedia_utils.utils.logging.logger import get_logger
|
|
24
30
|
|
|
25
31
|
logger = get_logger(__name__)
|
|
26
32
|
|
|
@@ -40,11 +46,11 @@ def get_src_package() -> ModuleType:
|
|
|
40
46
|
if only the test package exists
|
|
41
47
|
|
|
42
48
|
"""
|
|
43
|
-
from winipedia_utils.testing.convention import ( # noqa: PLC0415 # avoid circular import
|
|
49
|
+
from winipedia_utils.dev.testing.convention import ( # noqa: PLC0415 # avoid circular import
|
|
44
50
|
TESTS_PACKAGE_NAME,
|
|
45
51
|
)
|
|
46
52
|
|
|
47
|
-
packages = find_packages_as_modules(depth=0)
|
|
53
|
+
packages = find_packages_as_modules(depth=0, include_namespace_packages=True)
|
|
48
54
|
return next(p for p in packages if p.__name__ != TESTS_PACKAGE_NAME)
|
|
49
55
|
|
|
50
56
|
|
|
@@ -178,7 +184,7 @@ def find_packages(
|
|
|
178
184
|
find_packages(depth=1) might return ["package1", "package2"]
|
|
179
185
|
|
|
180
186
|
"""
|
|
181
|
-
from winipedia_utils.
|
|
187
|
+
from winipedia_utils.dev.configs.gitignore import ( # noqa: PLC0415
|
|
182
188
|
GitIgnoreConfigFile, # avoid circular import
|
|
183
189
|
)
|
|
184
190
|
|
|
@@ -283,10 +289,10 @@ def make_init_modules_for_package(path: str | Path | ModuleType) -> None:
|
|
|
283
289
|
from get_default_init_module_content.
|
|
284
290
|
|
|
285
291
|
"""
|
|
286
|
-
from winipedia_utils.git.gitignore.gitignore import ( # noqa: PLC0415
|
|
292
|
+
from winipedia_utils.utils.git.gitignore.gitignore import ( # noqa: PLC0415
|
|
287
293
|
walk_os_skipping_gitignore_patterns, # avoid circular import
|
|
288
294
|
)
|
|
289
|
-
from winipedia_utils.modules.module import ( # noqa: PLC0415
|
|
295
|
+
from winipedia_utils.utils.modules.module import ( # noqa: PLC0415
|
|
290
296
|
to_path, # avoid circular import
|
|
291
297
|
)
|
|
292
298
|
|
|
@@ -313,7 +319,7 @@ def make_init_module(path: str | Path) -> None:
|
|
|
313
319
|
Creates parent directories if they don't exist.
|
|
314
320
|
|
|
315
321
|
"""
|
|
316
|
-
from winipedia_utils.modules.module import ( # noqa: PLC0415 # avoid circular import
|
|
322
|
+
from winipedia_utils.utils.modules.module import ( # noqa: PLC0415 # avoid circular import
|
|
317
323
|
get_default_init_module_content,
|
|
318
324
|
to_path,
|
|
319
325
|
)
|
|
@@ -349,7 +355,7 @@ def copy_package(
|
|
|
349
355
|
with_file_content (bool, optional): copies the content of the files.
|
|
350
356
|
|
|
351
357
|
"""
|
|
352
|
-
from winipedia_utils.modules.module import ( # noqa: PLC0415 # avoid circular import
|
|
358
|
+
from winipedia_utils.utils.modules.module import ( # noqa: PLC0415 # avoid circular import
|
|
353
359
|
create_module,
|
|
354
360
|
get_isolated_obj_name,
|
|
355
361
|
get_module_content_as_str,
|
|
@@ -379,7 +385,7 @@ def get_main_package() -> ModuleType:
|
|
|
379
385
|
|
|
380
386
|
Even when this package is installed as a module.
|
|
381
387
|
"""
|
|
382
|
-
from winipedia_utils.modules.module import ( # noqa: PLC0415 # avoid circular import
|
|
388
|
+
from winipedia_utils.utils.modules.module import ( # noqa: PLC0415 # avoid circular import
|
|
383
389
|
to_module_name,
|
|
384
390
|
)
|
|
385
391
|
|
|
@@ -403,28 +409,88 @@ def get_main_package() -> ModuleType:
|
|
|
403
409
|
raise ValueError(msg)
|
|
404
410
|
|
|
405
411
|
|
|
406
|
-
|
|
407
|
-
package
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
412
|
+
class DependencyGraph(nx.DiGraph): # type: ignore [type-arg]
|
|
413
|
+
"""A directed graph representing Python package dependencies."""
|
|
414
|
+
|
|
415
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
416
|
+
"""Initialize the dependency graph and build it immediately."""
|
|
417
|
+
super().__init__(*args, **kwargs)
|
|
418
|
+
self.build()
|
|
419
|
+
|
|
420
|
+
def build(self) -> None:
|
|
421
|
+
"""Build the graph from installed Python distributions."""
|
|
422
|
+
for dist in importlib.metadata.distributions():
|
|
423
|
+
name = self.parse_distname_from_metadata(dist)
|
|
424
|
+
self.add_node(name)
|
|
425
|
+
|
|
426
|
+
requires = dist.requires or []
|
|
427
|
+
for req in requires:
|
|
428
|
+
dep = self.parse_pkg_name_from_req(req)
|
|
429
|
+
if dep:
|
|
430
|
+
self.add_edge(name, dep) # package → dependency
|
|
431
|
+
|
|
432
|
+
@staticmethod
|
|
433
|
+
def parse_distname_from_metadata(dist: importlib.metadata.Distribution) -> str:
|
|
434
|
+
"""Extract the distribution name from its metadata."""
|
|
435
|
+
# replace - with _ to handle packages like winipedia-utils
|
|
436
|
+
name: str = dist.metadata["Name"]
|
|
437
|
+
return DependencyGraph.normalize_package_name(name)
|
|
438
|
+
|
|
439
|
+
@staticmethod
|
|
440
|
+
def normalize_package_name(name: str) -> str:
|
|
441
|
+
"""Normalize a package name."""
|
|
442
|
+
return name.lower().replace("-", "_").strip()
|
|
443
|
+
|
|
444
|
+
@staticmethod
|
|
445
|
+
def parse_pkg_name_from_req(req: str) -> str | None:
|
|
446
|
+
"""Extract the bare dependency name from a requirement string."""
|
|
447
|
+
# split on the first non alphanumeric character like >, <, =, etc.
|
|
448
|
+
# keep - and _ for names like winipedia-utils or winipedia_utils
|
|
449
|
+
dep = re.split(r"[^a-zA-Z0-9_-]", req.strip())[0].strip()
|
|
450
|
+
return DependencyGraph.normalize_package_name(dep) if dep else None
|
|
451
|
+
|
|
452
|
+
def get_all_depending_on(
|
|
453
|
+
self, package: ModuleType, *, include_self: bool = False
|
|
454
|
+
) -> set[ModuleType]:
|
|
455
|
+
"""Return all packages that directly or indirectly depend on the given package.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
package: The module whose dependents should be found.
|
|
459
|
+
include_self: Whether to include the package itself in the result.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
A set of imported module objects representing dependents.
|
|
463
|
+
"""
|
|
464
|
+
# replace - with _ to handle packages like winipedia-utils
|
|
465
|
+
target = package.__name__.lower()
|
|
466
|
+
if target not in self:
|
|
467
|
+
msg = f"Package '{target}' not found in dependency graph"
|
|
468
|
+
raise ValueError(msg)
|
|
469
|
+
|
|
470
|
+
dependents = nx.ancestors(self, target)
|
|
471
|
+
if include_self:
|
|
472
|
+
dependents.add(target)
|
|
473
|
+
|
|
474
|
+
return self.import_packages(dependents)
|
|
475
|
+
|
|
476
|
+
@staticmethod
|
|
477
|
+
def import_packages(names: set[str]) -> set[ModuleType]:
|
|
478
|
+
"""Attempt to import all module names that can be resolved."""
|
|
479
|
+
modules: set[ModuleType] = set()
|
|
480
|
+
for name in names:
|
|
481
|
+
spec = importlib.util.find_spec(name)
|
|
482
|
+
if spec is not None:
|
|
483
|
+
modules.add(importlib.import_module(name))
|
|
484
|
+
return modules
|
|
485
|
+
|
|
486
|
+
def get_all_depending_on_winipedia_utils(
|
|
487
|
+
self, *, include_winipedia_utils: bool = False
|
|
488
|
+
) -> set[ModuleType]:
|
|
489
|
+
"""Return all packages that directly or indirectly depend on winipedia_utils."""
|
|
490
|
+
if get_src_package() == winipedia_utils:
|
|
491
|
+
deps: set[ModuleType] = set()
|
|
492
|
+
else:
|
|
493
|
+
deps = self.get_all_depending_on(winipedia_utils, include_self=False)
|
|
494
|
+
if include_winipedia_utils:
|
|
495
|
+
deps.add(winipedia_utils)
|
|
496
|
+
return deps
|
|
@@ -13,10 +13,10 @@ from collections.abc import Callable
|
|
|
13
13
|
from functools import wraps
|
|
14
14
|
from typing import Any, final
|
|
15
15
|
|
|
16
|
-
from winipedia_utils.
|
|
17
|
-
from winipedia_utils.
|
|
18
|
-
from winipedia_utils.modules.
|
|
19
|
-
from winipedia_utils.
|
|
16
|
+
from winipedia_utils.utils.data.structures.text.string import value_to_truncated_string
|
|
17
|
+
from winipedia_utils.utils.logging.logger import get_logger
|
|
18
|
+
from winipedia_utils.utils.modules.class_ import get_all_methods_from_cls
|
|
19
|
+
from winipedia_utils.utils.modules.function import is_func, unwrap_method
|
|
20
20
|
|
|
21
21
|
logger = get_logger(__name__)
|
|
22
22
|
|
|
@@ -10,8 +10,8 @@ These utilities help create robust class hierarchies with proper implementation
|
|
|
10
10
|
enforcement and built-in logging capabilities.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
from winipedia_utils.logging.logger import get_logger
|
|
14
|
-
from winipedia_utils.oop.mixins.meta import ABCLoggingMeta, StrictABCLoggingMeta
|
|
13
|
+
from winipedia_utils.utils.logging.logger import get_logger
|
|
14
|
+
from winipedia_utils.utils.oop.mixins.meta import ABCLoggingMeta, StrictABCLoggingMeta
|
|
15
15
|
|
|
16
16
|
logger = get_logger(__name__)
|
|
17
17
|
|
|
@@ -7,7 +7,7 @@ These utilities help with system-level operations and configuration.
|
|
|
7
7
|
|
|
8
8
|
import shutil
|
|
9
9
|
import subprocess # nosec: B404
|
|
10
|
-
from
|
|
10
|
+
from collections.abc import Sequence
|
|
11
11
|
from typing import Any
|
|
12
12
|
|
|
13
13
|
|
|
@@ -34,7 +34,7 @@ def which_with_raise(cmd: str, *, raise_error: bool = True) -> str | None:
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def run_subprocess(
|
|
37
|
-
args:
|
|
37
|
+
args: Sequence[str],
|
|
38
38
|
*,
|
|
39
39
|
input_: str | bytes | None = None,
|
|
40
40
|
capture_output: bool = True,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -4,7 +4,7 @@ from importlib.resources import as_file, files
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from types import ModuleType
|
|
6
6
|
|
|
7
|
-
from winipedia_utils.resources import svgs
|
|
7
|
+
from winipedia_utils.utils.resources import svgs
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def get_svg_path(svg_name: str, package: ModuleType | None = None) -> Path:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -7,6 +7,8 @@ specialized validation logic for common testing scenarios.
|
|
|
7
7
|
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
+
from winipedia_utils.utils.modules.function import is_abstractmethod
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
def assert_with_msg(expr: bool, msg: str) -> None: # noqa: FBT001
|
|
12
14
|
"""Assert that an expression is true with a custom error message.
|
|
@@ -46,3 +48,19 @@ Actual: {actual}
|
|
|
46
48
|
{msg}
|
|
47
49
|
"""
|
|
48
50
|
assert_with_msg(expr, msg)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def assert_isabstrct_method(method: Any) -> None:
|
|
54
|
+
"""Assert that a method is an abstract method.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
method: The method to check
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
AssertionError: If the method is not an abstract method
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
assert_with_msg(
|
|
64
|
+
is_abstractmethod(method),
|
|
65
|
+
f"Expected {method} to be abstract method",
|
|
66
|
+
)
|
|
@@ -4,7 +4,7 @@ import functools
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from winipedia_utils.git.github.github import running_in_github_actions
|
|
7
|
+
from winipedia_utils.utils.git.github.github import running_in_github_actions
|
|
8
8
|
|
|
9
9
|
skip_fixture_test: pytest.MarkDecorator = functools.partial(
|
|
10
10
|
pytest.mark.skip,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: winipedia-utils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: A package with many utility functions
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -15,6 +15,7 @@ Requires-Dist: cryptography
|
|
|
15
15
|
Requires-Dist: defusedxml
|
|
16
16
|
Requires-Dist: dotenv
|
|
17
17
|
Requires-Dist: keyring
|
|
18
|
+
Requires-Dist: networkx
|
|
18
19
|
Requires-Dist: pathspec
|
|
19
20
|
Requires-Dist: polars
|
|
20
21
|
Requires-Dist: pygithub
|
|
@@ -78,7 +79,8 @@ git clone https://github.com/owner/repo.git
|
|
|
78
79
|
poetry init # or poetry new
|
|
79
80
|
# 4: Poetry will ask you some stuff when you run poetry init.
|
|
80
81
|
# First author name must be equal to the GitHub repository owner (username).
|
|
81
|
-
# The repository name must be equal to the package/project name.
|
|
82
|
+
# The repository name must be equal to the package/project name.
|
|
83
|
+
# (- instead of _ is fine, but only as the project name in pyproject.toml, folder names should all be _)
|
|
82
84
|
|
|
83
85
|
# 5: Add winipedia-utils to your project
|
|
84
86
|
poetry add winipedia-utils
|
|
@@ -103,7 +105,7 @@ The setup creates the following configuration files:
|
|
|
103
105
|
- `.pre-commit-config.yaml` - Pre-commit hook configuration
|
|
104
106
|
- `.gitignore` - Git ignore rules (assumes you added one on GitHub before.)
|
|
105
107
|
- `pyproject.toml` - Project configuration with Poetry settings
|
|
106
|
-
- `.github/workflows/health_check.yaml` - Health check workflow (Runs on every push and pull request
|
|
108
|
+
- `.github/workflows/health_check.yaml` - Health check workflow (Runs on every push and pull request using a matrix strategy to test across multiple operating systems and Python versions)
|
|
107
109
|
- `.github/workflows/release.yaml` - Release workflow (Creates a release on GitHub when the same actions as in health check pass and commits are pushed to main)
|
|
108
110
|
- `.github/workflows/publish.yaml` - Publishing workflow (Publishes to PyPI when a release is created by the release workflow, if you use this workflow, you need to add a PYPI_TOKEN (named PYPI_TOKEN) to your GitHub secrets that has write access to the package on PyPI.)
|
|
109
111
|
- `py.typed` - PEP 561 marker for type hints
|
|
@@ -112,27 +114,60 @@ The setup creates the following configuration files:
|
|
|
112
114
|
- `conftest.py` - Pytest configuration file
|
|
113
115
|
- `.python-version` - Python version file for pyenv (if you use pyenv, puts in the lowest supported python version in pyproject.toml opposed to the latest possible python version in workflows)
|
|
114
116
|
|
|
117
|
+
### GitHub Workflows and Matrix Strategy
|
|
118
|
+
|
|
119
|
+
The project uses GitHub Actions workflows with a **matrix strategy** to ensure cross-platform compatibility:
|
|
120
|
+
|
|
121
|
+
#### Matrix Configuration
|
|
122
|
+
|
|
123
|
+
The health check and release workflows test your code across:
|
|
124
|
+
- **Operating Systems**: Ubuntu (latest), Windows (latest), macOS (latest)
|
|
125
|
+
- **Python Versions**: All versions specified in your `pyproject.toml` (e.g., 3.12, 3.13, 3.14)
|
|
126
|
+
|
|
127
|
+
This matrix strategy ensures your code works reliably across different environments before merging or releasing.
|
|
128
|
+
|
|
129
|
+
#### Workflow Structure
|
|
130
|
+
|
|
131
|
+
The health check workflow consists of two jobs:
|
|
132
|
+
|
|
133
|
+
1. **Matrix Job** (`health_check_matrix`) - Runs all checks in parallel across the matrix of OS and Python versions:
|
|
134
|
+
- Checkout repository
|
|
135
|
+
- Setup Git, Python, and Poetry
|
|
136
|
+
- Add Poetry to PATH (Windows-specific step)
|
|
137
|
+
- Install dependencies
|
|
138
|
+
- Setup CI keyring
|
|
139
|
+
- Protect repository (applies branch protection rules and repository settings)
|
|
140
|
+
- Run pre-commit hooks (linting, formatting, type checking, security, tests)
|
|
141
|
+
|
|
142
|
+
2. **Aggregation Job** (`health_check`) - Aggregates matrix results into a single status check:
|
|
143
|
+
- Required for branch protection compatibility
|
|
144
|
+
- Only runs after all matrix jobs complete successfully
|
|
145
|
+
- Provides a single status check that can be marked as required in branch protection rules
|
|
146
|
+
|
|
147
|
+
The release workflow extends the health check workflow and adds a release job that runs after all health checks pass.
|
|
148
|
+
A build job is added before the release job if a script src/artifacts/build.py exists. This script is created by the setup command and can be modified to create build artifacts for your project. This script then just needs to create artifacts in a folder called artifacts and those will be uploaded as artifacts to the release.
|
|
149
|
+
|
|
115
150
|
### Pre-commit Hook Workflow
|
|
116
151
|
|
|
117
152
|
When you commit code using `git commit`, the following checks run automatically:
|
|
118
153
|
|
|
119
154
|
Info: If git commit fails bc of ModuleNotFoundError or smth similar, you need to run `poetry run git commit` instead.
|
|
120
155
|
winipedia_utils hook is a python script that depends on winipedia_utils being installed. Poetry is needed to install winipedia_utils.
|
|
121
|
-
Usually VSCode or other IDEs activates the venv automatically when opening the terminal but if not you need to activate it manually or run `poetry run git commit` instead.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
156
|
+
Usually VSCode or other IDEs activates the venv automatically when opening the terminal but if not you need to activate it manually or run `poetry run git commit` instead. It fails fast, so if one hook in winipedia_utils hook fails, the others don't run bc sys.exit(1) is called.
|
|
157
|
+
|
|
158
|
+
Hooks run in the following order:
|
|
159
|
+
|
|
160
|
+
- Update package manager (poetry self update)
|
|
161
|
+
- Install packages (poetry install --with dev)
|
|
162
|
+
- Update packages (poetry update --with dev (winipedia_utils forces all dependencies with * to be updated to latest compatible version))
|
|
163
|
+
- Lock dependencies (poetry lock)
|
|
164
|
+
- Check package manager configs (poetry check --strict)
|
|
165
|
+
- Create tests (python -m winipedia_utils.dev.testing.create_tests)
|
|
166
|
+
- Lint code (ruff check --fix)
|
|
167
|
+
- Format code (ruff format)
|
|
168
|
+
- Check static types (mypy)
|
|
169
|
+
- Check security (bandit -c pyproject.toml -r .)
|
|
170
|
+
- Run tests (pytest (uses pyproject.toml as config))
|
|
136
171
|
|
|
137
172
|
### Auto-generated Test Structure
|
|
138
173
|
|
|
@@ -168,7 +203,7 @@ Configuration files are managed automatically by the setup system:
|
|
|
168
203
|
|
|
169
204
|
## Branch Protection
|
|
170
205
|
|
|
171
|
-
As soon as you push to `main` on GitHub (provided the `REPO_TOKEN` secret is set up correctly), the `health_check.yaml` workflow will run and execute `winipedia_utils.git.github.repo.protect`, which uses PyGithub to protect the repository.
|
|
206
|
+
As soon as you push to `main` on GitHub (provided the `REPO_TOKEN` secret is set up correctly), the `health_check.yaml` workflow will run and execute `winipedia_utils.dev.git.github.repo.protect`, which uses PyGithub to protect the repository.
|
|
172
207
|
|
|
173
208
|
### Repository Settings
|
|
174
209
|
|
|
@@ -201,8 +236,8 @@ A ruleset named `main protection` is created for the `main` branch with the foll
|
|
|
201
236
|
- Requires review thread resolution (all comments in reviews must be resolved before merge)
|
|
202
237
|
- Allowed merge methods: `squash` and `rebase` (no merge commits, keeps history clean)
|
|
203
238
|
- **Required Status Checks:**
|
|
204
|
-
- Strict mode enabled (all status checks must pass on the latest commit, not older ones
|
|
205
|
-
- Health check workflow must pass (the
|
|
239
|
+
- Strict mode enabled (all status checks must pass on the latest commit, not older ones)
|
|
240
|
+
- Health check workflow must pass (the aggregated `health_check` job ensures all matrix combinations passed successfully)
|
|
206
241
|
- **Bypass Actors** - Repository admins can bypass all rules (for emergency situations)
|
|
207
242
|
|
|
208
243
|
## Utilities
|
|
@@ -214,8 +249,8 @@ Winipedia Utils provides comprehensive utility modules for common development ta
|
|
|
214
249
|
Unified interface for multiprocessing and multithreading:
|
|
215
250
|
|
|
216
251
|
```python
|
|
217
|
-
from winipedia_utils.concurrent.multiprocessing import multiprocess_loop
|
|
218
|
-
from winipedia_utils.concurrent.multithreading import multithread_loop
|
|
252
|
+
from winipedia_utils.utils.iterating.concurrent.multiprocessing import multiprocess_loop
|
|
253
|
+
from winipedia_utils.utils.iterating.concurrent.multithreading import multithread_loop
|
|
219
254
|
```
|
|
220
255
|
|
|
221
256
|
### Data Cleaning & Handling
|
|
@@ -223,7 +258,7 @@ from winipedia_utils.concurrent.multithreading import multithread_loop
|
|
|
223
258
|
Build data cleaning pipelines using Polars:
|
|
224
259
|
|
|
225
260
|
```python
|
|
226
|
-
from winipedia_utils.data.dataframe.cleaning import CleaningDF
|
|
261
|
+
from winipedia_utils.utils.data.dataframe.cleaning import CleaningDF
|
|
227
262
|
import polars as pl
|
|
228
263
|
```
|
|
229
264
|
|
|
@@ -232,7 +267,7 @@ import polars as pl
|
|
|
232
267
|
Simple, standardized logging setup with automatic method instrumentation:
|
|
233
268
|
|
|
234
269
|
```python
|
|
235
|
-
from winipedia_utils.logging.logger import get_logger
|
|
270
|
+
from winipedia_utils.utils.logging.logger import get_logger
|
|
236
271
|
|
|
237
272
|
logger = get_logger(__name__)
|
|
238
273
|
logger.info("Application started")
|
|
@@ -250,7 +285,7 @@ logger.error("An error occurred")
|
|
|
250
285
|
Advanced metaclasses and mixins for class composition and behavior extension:
|
|
251
286
|
|
|
252
287
|
```python
|
|
253
|
-
from winipedia_utils.oop.mixins.mixin import ABCLoggingMixin, StrictABCLoggingMixin
|
|
288
|
+
from winipedia_utils.utils.oop.mixins.mixin import ABCLoggingMixin, StrictABCLoggingMixin
|
|
254
289
|
```
|
|
255
290
|
|
|
256
291
|
### Security Utilities
|
|
@@ -258,7 +293,7 @@ from winipedia_utils.oop.mixins.mixin import ABCLoggingMixin, StrictABCLoggingMi
|
|
|
258
293
|
Encryption and secure credential storage using keyring:
|
|
259
294
|
|
|
260
295
|
```python
|
|
261
|
-
from winipedia_utils.security.keyring import (
|
|
296
|
+
from winipedia_utils.utils.security.keyring import (
|
|
262
297
|
get_or_create_fernet,
|
|
263
298
|
get_or_create_aes_gcm
|
|
264
299
|
)
|
|
@@ -269,8 +304,8 @@ from winipedia_utils.security.keyring import (
|
|
|
269
304
|
Comprehensive testing framework with automatic test generation:
|
|
270
305
|
|
|
271
306
|
```python
|
|
272
|
-
from winipedia_utils.testing.assertions import assert_with_msg
|
|
273
|
-
from winipedia_utils.testing.convention import (
|
|
307
|
+
from winipedia_utils.utils.testing.assertions import assert_with_msg
|
|
308
|
+
from winipedia_utils.dev.testing.convention import (
|
|
274
309
|
make_test_obj_name,
|
|
275
310
|
get_test_obj_from_obj,
|
|
276
311
|
make_test_obj_importpath_from_obj
|
|
@@ -300,10 +335,10 @@ test_path = make_test_obj_importpath_from_obj(my_function)
|
|
|
300
335
|
Tools for working with Python modules, packages, classes, and functions:
|
|
301
336
|
|
|
302
337
|
```python
|
|
303
|
-
from winipedia_utils.modules.package import find_packages, walk_package
|
|
304
|
-
from winipedia_utils.modules.module import create_module, import_obj_from_importpath
|
|
305
|
-
from winipedia_utils.modules.class_ import get_all_cls_from_module, get_all_methods_from_cls
|
|
306
|
-
from winipedia_utils.modules.function import get_all_functions_from_module
|
|
338
|
+
from winipedia_utils.utils.modules.package import find_packages, walk_package
|
|
339
|
+
from winipedia_utils.utils.modules.module import create_module, import_obj_from_importpath
|
|
340
|
+
from winipedia_utils.utils.modules.class_ import get_all_cls_from_module, get_all_methods_from_cls
|
|
341
|
+
from winipedia_utils.utils.modules.function import get_all_functions_from_module
|
|
307
342
|
```
|
|
308
343
|
|
|
309
344
|
### Text and String Utilities
|
|
@@ -311,7 +346,7 @@ from winipedia_utils.modules.function import get_all_functions_from_module
|
|
|
311
346
|
String manipulation and configuration file handling:
|
|
312
347
|
|
|
313
348
|
```python
|
|
314
|
-
from winipedia_utils.text.string import value_to_truncated_string
|
|
349
|
+
from winipedia_utils.utils.data.structures.text.string import value_to_truncated_string
|
|
315
350
|
```
|
|
316
351
|
|
|
317
352
|
### OS and System Utilities
|
|
@@ -319,7 +354,7 @@ from winipedia_utils.text.string import value_to_truncated_string
|
|
|
319
354
|
Operating system and subprocess utilities:
|
|
320
355
|
|
|
321
356
|
```python
|
|
322
|
-
from winipedia_utils.os.os import run_subprocess
|
|
357
|
+
from winipedia_utils.utils.os.os import run_subprocess
|
|
323
358
|
```
|
|
324
359
|
|
|
325
360
|
### Iteration Utilities
|
|
@@ -327,7 +362,7 @@ from winipedia_utils.os.os import run_subprocess
|
|
|
327
362
|
Utilities for working with iterables and nested structures:
|
|
328
363
|
|
|
329
364
|
```python
|
|
330
|
-
from winipedia_utils.iterating.iterate import get_len_with_default, nested_structure_is_subset
|
|
365
|
+
from winipedia_utils.utils.iterating.iterate import get_len_with_default, nested_structure_is_subset
|
|
331
366
|
```
|
|
332
367
|
|
|
333
368
|
### Philosophy
|