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
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Contains the pull request workflow.
|
|
2
|
+
|
|
3
|
+
This workflow is used to run tests on pull requests.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from winipedia_utils.dev.configs.workflows.base.base import Workflow
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HealthCheckWorkflow(Workflow):
|
|
12
|
+
"""Pull request workflow.
|
|
13
|
+
|
|
14
|
+
This workflow is triggered by a pull request.
|
|
15
|
+
It runs tests on the pull request.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def get_workflow_triggers(cls) -> dict[str, Any]:
|
|
20
|
+
"""Get the workflow triggers."""
|
|
21
|
+
triggers = super().get_workflow_triggers()
|
|
22
|
+
triggers.update(cls.on_pull_request())
|
|
23
|
+
triggers.update(cls.on_schedule(cron="0 6 * * *"))
|
|
24
|
+
return triggers
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def get_jobs(cls) -> dict[str, Any]:
|
|
28
|
+
"""Get the workflow jobs."""
|
|
29
|
+
jobs: dict[str, Any] = {}
|
|
30
|
+
jobs.update(cls.job_health_check_matrix())
|
|
31
|
+
jobs.update(cls.job_health_check())
|
|
32
|
+
return jobs
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def job_health_check_matrix(cls) -> dict[str, Any]:
|
|
36
|
+
"""Get the health check matrix job."""
|
|
37
|
+
return cls.get_job(
|
|
38
|
+
job_func=cls.job_health_check_matrix,
|
|
39
|
+
strategy=cls.strategy_matrix_os_and_python_version(),
|
|
40
|
+
runs_on=cls.insert_matrix_os(),
|
|
41
|
+
steps=cls.steps_health_check_matrix(),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def job_health_check(cls) -> dict[str, Any]:
|
|
46
|
+
"""Get the health check job."""
|
|
47
|
+
return cls.get_job(
|
|
48
|
+
job_func=cls.job_health_check,
|
|
49
|
+
needs=[cls.make_id_from_func(cls.job_health_check_matrix)],
|
|
50
|
+
steps=cls.steps_aggregate_matrix_results(),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def steps_health_check_matrix(cls) -> list[dict[str, Any]]:
|
|
55
|
+
"""Get the health check matrix steps."""
|
|
56
|
+
return [
|
|
57
|
+
*cls.steps_core_matrix_setup(
|
|
58
|
+
python_version=cls.insert_matrix_python_version()
|
|
59
|
+
),
|
|
60
|
+
cls.step_protect_repository(),
|
|
61
|
+
cls.step_run_pre_commit_hooks(),
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def steps_aggregate_matrix_results(cls) -> list[dict[str, Any]]:
|
|
66
|
+
"""Get the aggregate matrix results step."""
|
|
67
|
+
return [
|
|
68
|
+
cls.step_aggregate_matrix_results(),
|
|
69
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Contains the publish workflow.
|
|
2
|
+
|
|
3
|
+
This workflow is used to publish the package to PyPI with poetry.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from winipedia_utils.dev.configs.workflows.base.base import Workflow
|
|
9
|
+
from winipedia_utils.dev.configs.workflows.release import ReleaseWorkflow
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PublishWorkflow(Workflow):
|
|
13
|
+
"""Publish workflow.
|
|
14
|
+
|
|
15
|
+
This workflow is triggered by the release workflow.
|
|
16
|
+
It publishes the package to PyPI with poetry.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def get_workflow_triggers(cls) -> dict[str, Any]:
|
|
21
|
+
"""Get the workflow triggers."""
|
|
22
|
+
triggers = super().get_workflow_triggers()
|
|
23
|
+
triggers.update(
|
|
24
|
+
cls.on_workflow_run(workflows=[ReleaseWorkflow.get_workflow_name()])
|
|
25
|
+
)
|
|
26
|
+
return triggers
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def get_jobs(cls) -> dict[str, Any]:
|
|
30
|
+
"""Get the workflow jobs."""
|
|
31
|
+
jobs: dict[str, Any] = {}
|
|
32
|
+
jobs.update(cls.job_publish())
|
|
33
|
+
return jobs
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def job_publish(cls) -> dict[str, Any]:
|
|
37
|
+
"""Get the publish job."""
|
|
38
|
+
return cls.get_job(
|
|
39
|
+
job_func=cls.job_publish,
|
|
40
|
+
steps=cls.steps_publish(),
|
|
41
|
+
if_condition=cls.if_workflow_run_is_success(),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def steps_publish(cls) -> list[dict[str, Any]]:
|
|
46
|
+
"""Get the publish steps."""
|
|
47
|
+
return [
|
|
48
|
+
*cls.steps_core_setup(),
|
|
49
|
+
cls.step_add_pypi_token_to_poetry(),
|
|
50
|
+
cls.step_publish_to_pypi(),
|
|
51
|
+
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Contains the release workflow.
|
|
2
|
+
|
|
3
|
+
This workflow is used to create a release on GitHub.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from winipedia_utils.dev.configs.workflows.health_check import HealthCheckWorkflow
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ReleaseWorkflow(HealthCheckWorkflow):
|
|
12
|
+
"""Release workflow.
|
|
13
|
+
|
|
14
|
+
This workflow is triggered by a push to the main branch.
|
|
15
|
+
It creates a tag for the release and builds a changelog.
|
|
16
|
+
With tag and changelog it creates a release on GitHub
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def get_workflow_triggers(cls) -> dict[str, Any]:
|
|
21
|
+
"""Get the workflow triggers."""
|
|
22
|
+
triggers = super().get_workflow_triggers()
|
|
23
|
+
triggers.update(cls.on_push())
|
|
24
|
+
triggers.update(cls.on_schedule(cron="0 6 * * 2"))
|
|
25
|
+
return triggers
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def get_permissions(cls) -> dict[str, Any]:
|
|
29
|
+
"""Get the workflow permissions."""
|
|
30
|
+
permissions = super().get_permissions()
|
|
31
|
+
permissions["contents"] = "write"
|
|
32
|
+
return permissions
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def get_jobs(cls) -> dict[str, Any]:
|
|
36
|
+
"""Get the workflow jobs."""
|
|
37
|
+
jobs = super().get_jobs()
|
|
38
|
+
last_job_name = list(jobs.keys())[-1]
|
|
39
|
+
jobs.update(cls.job_build(needs=[last_job_name]))
|
|
40
|
+
jobs.update(cls.job_release())
|
|
41
|
+
return jobs
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def job_build(cls, needs: list[str] | None = None) -> dict[str, Any]:
|
|
45
|
+
"""Get the build job."""
|
|
46
|
+
return cls.get_job(
|
|
47
|
+
job_func=cls.job_build,
|
|
48
|
+
needs=needs,
|
|
49
|
+
strategy=cls.strategy_matrix_os(),
|
|
50
|
+
runs_on=cls.insert_matrix_os(),
|
|
51
|
+
steps=cls.steps_build(),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def job_release(cls) -> dict[str, Any]:
|
|
56
|
+
"""Get the release job."""
|
|
57
|
+
return cls.get_job(
|
|
58
|
+
job_func=cls.job_release,
|
|
59
|
+
needs=[cls.make_id_from_func(cls.job_build)],
|
|
60
|
+
steps=cls.steps_release(),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def steps_build(cls) -> list[dict[str, Any]]:
|
|
65
|
+
"""Get the build steps."""
|
|
66
|
+
if not cls.BUILD_SCRIPT_PATH.exists():
|
|
67
|
+
return [cls.step_no_build_script()]
|
|
68
|
+
return [
|
|
69
|
+
*cls.steps_core_matrix_setup(),
|
|
70
|
+
cls.step_build_artifacts(),
|
|
71
|
+
cls.step_upload_artifacts(),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def steps_release(cls) -> list[dict[str, Any]]:
|
|
76
|
+
"""Get the release steps."""
|
|
77
|
+
return [
|
|
78
|
+
*cls.steps_core_setup(repo_token=True),
|
|
79
|
+
cls.step_install_python_dependencies(),
|
|
80
|
+
cls.step_setup_keyring(),
|
|
81
|
+
cls.step_setup_git(),
|
|
82
|
+
cls.step_add_version_patch(),
|
|
83
|
+
cls.step_run_pre_commit_hooks(),
|
|
84
|
+
cls.step_commit_added_changes(),
|
|
85
|
+
cls.step_push_commits(),
|
|
86
|
+
cls.step_create_and_push_tag(),
|
|
87
|
+
cls.step_extract_version(),
|
|
88
|
+
cls.step_download_artifacts(),
|
|
89
|
+
cls.step_build_changelog(),
|
|
90
|
+
cls.step_create_release(),
|
|
91
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
from winipedia_utils.
|
|
6
|
-
from winipedia_utils.
|
|
5
|
+
from winipedia_utils.dev.configs.pyproject import PyprojectConfigFile
|
|
6
|
+
from winipedia_utils.dev.configs.workflows.health_check import HealthCheckWorkflow
|
|
7
|
+
from winipedia_utils.utils.git.github.github import get_github_repo_token
|
|
8
|
+
from winipedia_utils.utils.git.github.repo.repo import (
|
|
7
9
|
DEFAULT_BRANCH,
|
|
8
10
|
DEFAULT_RULESET_NAME,
|
|
9
11
|
create_or_update_ruleset,
|
|
10
12
|
get_repo,
|
|
11
13
|
get_rules_payload,
|
|
12
14
|
)
|
|
13
|
-
from winipedia_utils.
|
|
14
|
-
from winipedia_utils.modules.package import get_src_package
|
|
15
|
-
from winipedia_utils.projects.poetry.config import PyprojectConfigFile
|
|
15
|
+
from winipedia_utils.utils.modules.package import get_src_package
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def protect_repository() -> None:
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""module contains functions that return the input for subprocess.run().
|
|
2
|
+
|
|
3
|
+
Each function is named after the hook it represents. The docstring of each function
|
|
4
|
+
describes the hook it represents. The function returns a list of strings that
|
|
5
|
+
represent the command to run. The first string is the command, and the following
|
|
6
|
+
strings are the arguments to the command. These funcs will be called by
|
|
7
|
+
run_hooks.py, which will pass the returned list to subprocess.run().
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from winipedia_utils.dev.projects.poetry.poetry import (
|
|
11
|
+
POETRY_ARG,
|
|
12
|
+
get_poetry_run_module_args,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def add_updates_to_git() -> list[str]:
|
|
17
|
+
"""Add the updated dependencies to git.
|
|
18
|
+
|
|
19
|
+
This function returns the input for subprocess.run() to add the updated
|
|
20
|
+
dependencies to git, so that the hook does not fail bc the file was changed.
|
|
21
|
+
"""
|
|
22
|
+
return ["git", "add", "pyproject.toml", "poetry.lock"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def check_package_manager_configs() -> list[str]:
|
|
26
|
+
"""Check that poetry.lock and pyproject.toml is up to date.
|
|
27
|
+
|
|
28
|
+
This function returns the input for subprocess.run() to check that poetry.lock
|
|
29
|
+
is up to date.
|
|
30
|
+
"""
|
|
31
|
+
return [POETRY_ARG, "check", "--strict"]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_missing_tests() -> list[str]:
|
|
35
|
+
"""Create all tests for the project.
|
|
36
|
+
|
|
37
|
+
This function returns the input for subprocess.run() to create all tests.
|
|
38
|
+
"""
|
|
39
|
+
from winipedia_utils.dev.testing import ( # noqa: PLC0415 # avoid circular import
|
|
40
|
+
create_tests,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return get_poetry_run_module_args(create_tests)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def lint_code() -> list[str]:
|
|
47
|
+
"""Check the code.
|
|
48
|
+
|
|
49
|
+
This function returns the input for subprocess.run() to lint the code.
|
|
50
|
+
It autofixes all errors that can be autofixed with --fix.
|
|
51
|
+
"""
|
|
52
|
+
return ["ruff", "check", "--fix"]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def format_code() -> list[str]:
|
|
56
|
+
"""Format the code.
|
|
57
|
+
|
|
58
|
+
This function calls ruff format to format the code.
|
|
59
|
+
"""
|
|
60
|
+
return ["ruff", "format"]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def check_static_types() -> list[str]:
|
|
64
|
+
"""Check the types.
|
|
65
|
+
|
|
66
|
+
This function returns the input for subprocess.run() to check the static types.
|
|
67
|
+
"""
|
|
68
|
+
return ["mypy", "--exclude-gitignore"]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def check_security() -> list[str]:
|
|
72
|
+
"""Check the security of the code.
|
|
73
|
+
|
|
74
|
+
This function returns the input for subprocess.run() to check the security of
|
|
75
|
+
the code.
|
|
76
|
+
"""
|
|
77
|
+
return ["bandit", "-c", "pyproject.toml", "-r", "."]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def run_tests() -> list[str]:
|
|
81
|
+
"""Run the tests.
|
|
82
|
+
|
|
83
|
+
This function returns the input for subprocess.run() to run all tests.
|
|
84
|
+
"""
|
|
85
|
+
return ["pytest"]
|
|
@@ -6,11 +6,11 @@ and should not be modified manually.
|
|
|
6
6
|
|
|
7
7
|
import sys
|
|
8
8
|
|
|
9
|
-
from winipedia_utils.git.pre_commit import hooks
|
|
10
|
-
from winipedia_utils.logging.ansi import GREEN, RED, RESET
|
|
11
|
-
from winipedia_utils.logging.logger import get_logger
|
|
12
|
-
from winipedia_utils.modules.function import get_all_functions_from_module
|
|
13
|
-
from winipedia_utils.os.os import run_subprocess
|
|
9
|
+
from winipedia_utils.dev.git.pre_commit import hooks
|
|
10
|
+
from winipedia_utils.utils.logging.ansi import GREEN, RED, RESET
|
|
11
|
+
from winipedia_utils.utils.logging.logger import get_logger
|
|
12
|
+
from winipedia_utils.utils.modules.function import get_all_functions_from_module
|
|
13
|
+
from winipedia_utils.utils.os.os import run_subprocess
|
|
14
14
|
|
|
15
15
|
logger = get_logger(__name__)
|
|
16
16
|
|
|
@@ -19,7 +19,6 @@ def run_hooks() -> None:
|
|
|
19
19
|
"""Import all funcs defined in hooks.py and runs them."""
|
|
20
20
|
hook_funcs = get_all_functions_from_module(hooks)
|
|
21
21
|
|
|
22
|
-
exit_code = 0
|
|
23
22
|
for hook_func in hook_funcs:
|
|
24
23
|
subprocess_args = hook_func()
|
|
25
24
|
result = run_subprocess(
|
|
@@ -28,19 +27,30 @@ def run_hooks() -> None:
|
|
|
28
27
|
passed = result.returncode == 0
|
|
29
28
|
|
|
30
29
|
log_method = logger.info
|
|
31
|
-
|
|
30
|
+
status_str = (f"{GREEN}PASSED" if passed else f"{RED}FAILED") + RESET
|
|
32
31
|
if not passed:
|
|
33
32
|
log_method = logger.error
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
status_str += f"""
|
|
34
|
+
---------------------------------------------------------------------------------------------
|
|
35
|
+
Stdout:
|
|
36
|
+
|
|
37
|
+
{result.stdout}
|
|
38
|
+
|
|
39
|
+
---------------------------------------------------------------------------------------------
|
|
40
|
+
Stderr:
|
|
41
|
+
|
|
42
|
+
{result.stderr}
|
|
43
|
+
|
|
44
|
+
---------------------------------------------------------------------------------------------
|
|
45
|
+
"""
|
|
46
|
+
|
|
36
47
|
# make the dashes always the same lentgth by adjusting to len of hook name
|
|
37
48
|
num_dashes = 50 - len(hook_func.__name__)
|
|
38
49
|
log_method(
|
|
39
50
|
"Hook %s -%s> %s",
|
|
40
51
|
hook_func.__name__,
|
|
41
52
|
"-" * num_dashes,
|
|
42
|
-
|
|
53
|
+
status_str,
|
|
43
54
|
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
sys.exit(exit_code)
|
|
55
|
+
if not passed:
|
|
56
|
+
sys.exit(1)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Contains a dict with the dev dependencies.
|
|
2
|
+
|
|
3
|
+
For poetry when winipedia_utils is a dependency.
|
|
4
|
+
winipedia_utils will add these automatically to the pyproject.toml file.
|
|
5
|
+
winipedia utils PyprojectConfigFile will auto dump the config here so it can access it
|
|
6
|
+
when being a dependency in another project.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
DEV_DEPENDENCIES: set[str] = {
|
|
10
|
+
"ruff",
|
|
11
|
+
"types-networkx",
|
|
12
|
+
"types-defusedxml",
|
|
13
|
+
"types-pyyaml",
|
|
14
|
+
"pytest",
|
|
15
|
+
"types-setuptools",
|
|
16
|
+
"pytest-mock",
|
|
17
|
+
"bandit",
|
|
18
|
+
"pre-commit",
|
|
19
|
+
"mypy",
|
|
20
|
+
"types-tqdm",
|
|
21
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Project utilities for introspection and manipulation.
|
|
2
|
+
|
|
3
|
+
This module provides utility functions for working with Python projects
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from collections.abc import Iterable
|
|
7
|
+
from types import ModuleType
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
from packaging.specifiers import SpecifierSet
|
|
11
|
+
from packaging.version import Version
|
|
12
|
+
|
|
13
|
+
from winipedia_utils.utils.logging.logger import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
POETRY_ARG = "poetry"
|
|
19
|
+
|
|
20
|
+
POETRY_RUN_ARGS = [POETRY_ARG, "run"]
|
|
21
|
+
|
|
22
|
+
RUN_PYTHON_MODULE_ARGS = ["python", "-m"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_script_from_args(args: Iterable[str]) -> str:
|
|
26
|
+
"""Get the script from args."""
|
|
27
|
+
return " ".join(args)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_run_python_module_args(module: ModuleType) -> list[str]:
|
|
31
|
+
"""Get the args to run a module."""
|
|
32
|
+
from winipedia_utils.utils.modules.module import ( # noqa: PLC0415 # avoid circular import
|
|
33
|
+
make_obj_importpath,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return [*RUN_PYTHON_MODULE_ARGS, make_obj_importpath(module)]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_poetry_run_module_args(module: ModuleType) -> list[str]:
|
|
40
|
+
"""Get the args to run a module."""
|
|
41
|
+
return [*POETRY_RUN_ARGS, *get_run_python_module_args(module)]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_python_module_script(module: ModuleType) -> str:
|
|
45
|
+
"""Get the script to run a module."""
|
|
46
|
+
return get_script_from_args(get_run_python_module_args(module))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def get_poetry_run_module_script(module: ModuleType) -> str:
|
|
50
|
+
"""Get the script to run a module."""
|
|
51
|
+
return get_script_from_args(get_poetry_run_module_args(module))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class VersionConstraint:
|
|
55
|
+
"""Version class."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, constraint: str) -> None:
|
|
58
|
+
"""Initialize the version."""
|
|
59
|
+
self.constraint = constraint
|
|
60
|
+
self.spec = self.constraint.strip().strip('"').strip("'")
|
|
61
|
+
self.sset = SpecifierSet(self.spec)
|
|
62
|
+
|
|
63
|
+
self.lowers_inclusive = [
|
|
64
|
+
Version(s.version) for s in self.sset if s.operator == ">="
|
|
65
|
+
]
|
|
66
|
+
self.lowers_exclusive = [
|
|
67
|
+
Version(s.version) for s in self.sset if s.operator == ">"
|
|
68
|
+
]
|
|
69
|
+
# increment the last number of exclusive, so
|
|
70
|
+
# >3.4.1 to >=3.4.2; <3.4.0 to <=3.4.1; 3.0.0 to <=3.0.1
|
|
71
|
+
self.lowers_exclusive_to_inclusive = [
|
|
72
|
+
Version(f"{v.major}.{v.minor}.{v.micro + 1}") for v in self.lowers_exclusive
|
|
73
|
+
]
|
|
74
|
+
self.lowers_inclusive = (
|
|
75
|
+
self.lowers_inclusive + self.lowers_exclusive_to_inclusive
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
self.uppers_inclusive = [
|
|
79
|
+
Version(s.version) for s in self.sset if s.operator == "<="
|
|
80
|
+
]
|
|
81
|
+
self.uppers_exclusive = [
|
|
82
|
+
Version(s.version) for s in self.sset if s.operator == "<"
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
# increment the last number of inclusive, so
|
|
86
|
+
# <=3.4.1 to <3.4.2; >=3.4.0 to >3.4.1; 3.0.0 to >3.0.1
|
|
87
|
+
self.uppers_inclusive_to_exclusive = [
|
|
88
|
+
Version(f"{v.major}.{v.minor}.{v.micro + 1}") for v in self.uppers_inclusive
|
|
89
|
+
]
|
|
90
|
+
self.uppers_exclusive = (
|
|
91
|
+
self.uppers_inclusive_to_exclusive + self.uppers_exclusive
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
self.upper_exclusive = (
|
|
95
|
+
min(self.uppers_exclusive) if self.uppers_exclusive else None
|
|
96
|
+
)
|
|
97
|
+
self.lower_inclusive = (
|
|
98
|
+
max(self.lowers_inclusive) if self.lowers_inclusive else None
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def get_lower_inclusive(
|
|
102
|
+
self, default: str | Version | None = None
|
|
103
|
+
) -> Version | None:
|
|
104
|
+
"""Get the minimum version.
|
|
105
|
+
|
|
106
|
+
Is given inclusive. E.g. >=3.8, <3.12 -> 3.8
|
|
107
|
+
if >3.7, <3.12 -> 3.7.1
|
|
108
|
+
|
|
109
|
+
E.g. >=3.8, <3.12 -> 3.8
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
default: The default value to return if there is no minimum version
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
The minimum version
|
|
116
|
+
"""
|
|
117
|
+
default = str(default) if default else None
|
|
118
|
+
if self.lower_inclusive is None:
|
|
119
|
+
return Version(default) if default else None
|
|
120
|
+
|
|
121
|
+
return self.lower_inclusive
|
|
122
|
+
|
|
123
|
+
def get_upper_exclusive(
|
|
124
|
+
self, default: str | Version | None = None
|
|
125
|
+
) -> Version | None:
|
|
126
|
+
"""Get the maximum version.
|
|
127
|
+
|
|
128
|
+
Is given exclusive. E.g. >=3.8, <3.12 -> 3.12
|
|
129
|
+
if >=3.8, <=3.12 -> 3.12.1
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
default: The default value to return if there is no maximum version
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
The maximum version
|
|
136
|
+
"""
|
|
137
|
+
default = str(default) if default else None
|
|
138
|
+
if self.upper_exclusive is None:
|
|
139
|
+
return Version(default) if default else None
|
|
140
|
+
|
|
141
|
+
return self.upper_exclusive
|
|
142
|
+
|
|
143
|
+
def get_upper_inclusive(
|
|
144
|
+
self, default: str | Version | None = None
|
|
145
|
+
) -> Version | None:
|
|
146
|
+
"""Get the maximum version.
|
|
147
|
+
|
|
148
|
+
Is given inclusive. E.g. >=3.8, <3.12 -> 3.11
|
|
149
|
+
if >=3.8, <=3.12 -> 3.12
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
default: The default value to return if there is no maximum version
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
The maximum version
|
|
156
|
+
"""
|
|
157
|
+
# increment the default by 1 micro to make it exclusive
|
|
158
|
+
if default:
|
|
159
|
+
default = Version(str(default))
|
|
160
|
+
default = Version(f"{default.major}.{default.minor}.{default.micro + 1}")
|
|
161
|
+
upper_exclusive = self.get_upper_exclusive(default)
|
|
162
|
+
if upper_exclusive is None:
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
if upper_exclusive.micro != 0:
|
|
166
|
+
return Version(
|
|
167
|
+
f"{upper_exclusive.major}.{upper_exclusive.minor}.{upper_exclusive.micro - 1}" # noqa: E501
|
|
168
|
+
)
|
|
169
|
+
if upper_exclusive.minor != 0:
|
|
170
|
+
return Version(f"{upper_exclusive.major}.{upper_exclusive.minor - 1}")
|
|
171
|
+
return Version(f"{upper_exclusive.major - 1}")
|
|
172
|
+
|
|
173
|
+
def get_version_range(
|
|
174
|
+
self,
|
|
175
|
+
level: Literal["major", "minor", "micro"] = "major",
|
|
176
|
+
lower_default: str | Version | None = None,
|
|
177
|
+
upper_default: str | Version | None = None,
|
|
178
|
+
) -> list[Version]:
|
|
179
|
+
"""Get the version range.
|
|
180
|
+
|
|
181
|
+
returns a range of versions according to the level
|
|
182
|
+
|
|
183
|
+
E.g. >=3.8, <3.12; level=major -> 3
|
|
184
|
+
>=3.8, <4.12; level=major -> 3, 4
|
|
185
|
+
E.g. >=3.8, <=3.12; level=minor -> 3.8, 3.9, 3.10, 3.11, 3.12
|
|
186
|
+
E.g. >=3.8.1, <=4.12.1; level=micro -> 3.8.1, 3.8.2, ... 4.12.1
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
level: The level of the version to return
|
|
190
|
+
lower_default: The default lower bound if none is specified
|
|
191
|
+
upper_default: The default upper bound if none is specified
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
A list of versions
|
|
195
|
+
"""
|
|
196
|
+
lower = self.get_lower_inclusive(lower_default)
|
|
197
|
+
upper = self.get_upper_inclusive(upper_default)
|
|
198
|
+
|
|
199
|
+
if lower is None or upper is None:
|
|
200
|
+
msg = "No lower or upper bound. Please specify default values."
|
|
201
|
+
raise ValueError(msg)
|
|
202
|
+
|
|
203
|
+
major_level, minor_level, micro_level = range(3)
|
|
204
|
+
level_int = {"major": major_level, "minor": minor_level, "micro": micro_level}[
|
|
205
|
+
level
|
|
206
|
+
]
|
|
207
|
+
lower_as_list = [lower.major, lower.minor, lower.micro]
|
|
208
|
+
upper_as_list = [upper.major, upper.minor, upper.micro]
|
|
209
|
+
|
|
210
|
+
versions: list[list[int]] = []
|
|
211
|
+
for major in range(lower_as_list[major_level], upper_as_list[major_level] + 1):
|
|
212
|
+
version = [major]
|
|
213
|
+
|
|
214
|
+
minor_lower_og, minor_upper_og = (
|
|
215
|
+
lower_as_list[minor_level],
|
|
216
|
+
upper_as_list[minor_level],
|
|
217
|
+
)
|
|
218
|
+
diff = minor_upper_og - minor_lower_og
|
|
219
|
+
minor_lower = minor_lower_og if diff >= 0 else 0
|
|
220
|
+
minor_upper = minor_upper_og if diff >= 0 else minor_lower_og + abs(diff)
|
|
221
|
+
for minor in range(
|
|
222
|
+
minor_lower,
|
|
223
|
+
minor_upper + 1,
|
|
224
|
+
):
|
|
225
|
+
# pop the minor if one already exists
|
|
226
|
+
if len(version) > minor_level:
|
|
227
|
+
version.pop()
|
|
228
|
+
|
|
229
|
+
version.append(minor)
|
|
230
|
+
|
|
231
|
+
micro_lower_og, micro_upper_og = (
|
|
232
|
+
lower_as_list[micro_level],
|
|
233
|
+
upper_as_list[micro_level],
|
|
234
|
+
)
|
|
235
|
+
diff = micro_upper_og - micro_lower_og
|
|
236
|
+
micro_lower = micro_lower_og if diff >= 0 else 0
|
|
237
|
+
micro_upper = (
|
|
238
|
+
micro_upper_og if diff >= 0 else micro_lower_og + abs(diff)
|
|
239
|
+
)
|
|
240
|
+
for micro in range(
|
|
241
|
+
micro_lower,
|
|
242
|
+
micro_upper + 1,
|
|
243
|
+
):
|
|
244
|
+
version.append(micro)
|
|
245
|
+
versions.append(version[: level_int + 1])
|
|
246
|
+
version.pop()
|
|
247
|
+
version_versions = sorted({Version(".".join(map(str, v))) for v in versions})
|
|
248
|
+
return [v for v in version_versions if self.sset.contains(v)]
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
"""Utilities for working with Python projects."""
|
|
2
2
|
|
|
3
|
-
from winipedia_utils.
|
|
4
|
-
from winipedia_utils.projects.poetry.config import (
|
|
5
|
-
PyprojectConfigFile, # avoid circular import
|
|
6
|
-
)
|
|
7
|
-
from winipedia_utils.text.config import (
|
|
3
|
+
from winipedia_utils.dev.configs.base.config import (
|
|
8
4
|
ConfigFile, # avoid circular import
|
|
9
5
|
)
|
|
6
|
+
from winipedia_utils.dev.configs.pyproject import (
|
|
7
|
+
PyprojectConfigFile, # avoid circular import
|
|
8
|
+
)
|
|
9
|
+
from winipedia_utils.utils.modules.module import create_module
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def create_project_root() -> None:
|
|
13
13
|
"""Create the project root."""
|
|
14
|
-
ConfigFile.init_config_files()
|
|
15
|
-
|
|
16
14
|
src_package_name = PyprojectConfigFile.get_package_name()
|
|
17
15
|
create_module(src_package_name, is_package=True)
|
|
16
|
+
ConfigFile.init_config_files()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""__init__ module."""
|