poe-tasks-common 0.1__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.
@@ -0,0 +1,31 @@
1
+ Metadata-Version: 2.4
2
+ Name: poe-tasks-common
3
+ Version: 0.1
4
+ Summary: An opinionated collection of common poe tasks
5
+ Keywords: poethepoet,poe,uv,pre-commit,pip-audit,ruff,pytest,coverage,sphinx
6
+ Author: Jonathan King
7
+ License-Expression: MIT
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Software Development :: Build Tools
20
+ Classifier: Topic :: Software Development :: Quality Assurance
21
+ Classifier: Topic :: Software Development :: Testing
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Maintainer: Jonathan King
24
+ Requires-Python: >=3.11
25
+ Project-URL: Repository, https://github.com/JonKing93/poe-tasks-common
26
+ Project-URL: Documentation, https://jonking93.github.io/poe-tasks-common
27
+ Description-Content-Type: text/markdown
28
+
29
+ # poe-tasks-common
30
+
31
+ An opinionated collection of common poe tasks
@@ -0,0 +1,3 @@
1
+ # poe-tasks-common
2
+
3
+ An opinionated collection of common poe tasks
@@ -0,0 +1,94 @@
1
+ [build-system]
2
+ requires = ["uv_build >= 0.11.6, <1"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "poe-tasks-common"
7
+ version = "0.1"
8
+ description = "An opinionated collection of common poe tasks"
9
+ license = "MIT"
10
+ readme = "README.md"
11
+ requires-python = ">=3.11"
12
+ authors = [
13
+ {name = "Jonathan King" },
14
+ ]
15
+ maintainers = [
16
+ {name = "Jonathan King" },
17
+ ]
18
+ keywords = [
19
+ "poethepoet",
20
+ "poe",
21
+ "uv",
22
+ "pre-commit",
23
+ "pip-audit",
24
+ "ruff",
25
+ "pytest",
26
+ "coverage",
27
+ "sphinx",
28
+ ]
29
+ classifiers = [
30
+ "Development Status :: 3 - Alpha",
31
+ "Environment :: Console",
32
+ "Intended Audience :: Developers",
33
+ "Natural Language :: English",
34
+ "Operating System :: OS Independent",
35
+ "Programming Language :: Python :: 3",
36
+ "Programming Language :: Python :: 3 :: Only",
37
+ "Programming Language :: Python :: 3.11",
38
+ "Programming Language :: Python :: 3.12",
39
+ "Programming Language :: Python :: 3.13",
40
+ "Programming Language :: Python :: 3.14",
41
+ "Topic :: Software Development :: Build Tools",
42
+ "Topic :: Software Development :: Quality Assurance",
43
+ "Topic :: Software Development :: Testing",
44
+ "Topic :: Software Development :: Libraries :: Python Modules",
45
+ ]
46
+
47
+ [dependency-groups]
48
+ tooling = ["poethepoet"]
49
+ hooks = ["pre-commit"]
50
+ security = ["pip-audit"]
51
+ lint = ["ruff"]
52
+ test = ["pytest>=9", "coverage>=7.10", "pytest-cov", "pytest-issues"]
53
+ docs = [
54
+ "sphinx>=8.1",
55
+ "furo>=2025.12",
56
+ "sphinx_design>=0.6",
57
+ "sphinx-copybutton>=0.5",
58
+ "sphinx-substitution-extensions>=2025.12",
59
+ ]
60
+
61
+ [project.urls]
62
+ Repository = "https://github.com/JonKing93/poe-tasks-common"
63
+ Documentation = "https://jonking93.github.io/poe-tasks-common"
64
+
65
+
66
+ #####
67
+ # Dev Tool Configurations
68
+ #####
69
+
70
+ [tool.uv.build-backend]
71
+ module-root = "src"
72
+
73
+ [tool.poe]
74
+ include_script = "poe_tasks_common:tasks"
75
+ executor = "uv"
76
+
77
+ [tool.ruff.lint]
78
+ select = [
79
+ "I", # isort
80
+ "E", # pycodestyle
81
+ "F", # PyFlakes
82
+ "B", # flake8-bugbear
83
+ "UP", # pyupgrade
84
+ "SIM", # flake8-simplify
85
+ "RUF", # ruff
86
+ "S", # flake8-bandit
87
+ "T", # flake8-print
88
+ ]
89
+ ignore = ["RUF022", "SIM108", "SIM300", "E501"]
90
+
91
+ [tool.ruff.lint.per-file-ignores]
92
+ "tests/**" = ["S101"]
93
+ "__init__.py" = ["F401"]
94
+
@@ -0,0 +1,3 @@
1
+ __all__ = ["tasks"]
2
+
3
+ from poe_tasks_common._api import tasks
@@ -0,0 +1,102 @@
1
+ """
2
+ Function that collects and returns the complete poe schema
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from copy import deepcopy
8
+ from typing import TYPE_CHECKING
9
+
10
+ from poe_tasks_common._groups import GROUPS
11
+ from poe_tasks_common._utilities import TASKS as UTILITIES
12
+
13
+ if TYPE_CHECKING:
14
+ Strings = list[str] | tuple[str, ...]
15
+
16
+
17
+ def tasks(
18
+ *,
19
+ exclude_tasks: Strings = (),
20
+ include_tasks: Strings = (),
21
+ exclude_groups: Strings = (),
22
+ include_groups: Strings = (),
23
+ ) -> dict:
24
+ """
25
+ Builds, filters, and exports a task schema
26
+
27
+ tasks()
28
+ Builds and returns the complete task schema dict.
29
+
30
+ tasks(*, exclude_tasks)
31
+ tasks(*, include_tasks)
32
+ tasks(*, exclude_groups)
33
+ tasks(*, include_groups)
34
+ Returns a filtered schema dict. Use these options to select which tasks are
35
+ included in the final schema. Use `include_tasks` or `exclude_tasks` to filter
36
+ by specific task names. Use `include_groups` or `exclude_groups` to filter by
37
+ task groups. Each provided filter should be a sequence of strings. When multiple
38
+ filters are provided, they are have the following priority:
39
+
40
+ exclude_tasks > include_tasks > exclude_groups > include_groups
41
+
42
+ In brief, task filters take priority over group filters, and excludes have priority
43
+ over includes. Note that providing an "include" filter activates opt-in
44
+ behavior - only the included tasks are exported, all others are discarded.
45
+
46
+ Args:
47
+ exclude_tasks: Specific tasks that should be excluded from the schema
48
+ include_tasks: Specific tasks that should be included in the schema
49
+ exclude_groups: Task groups that should be excluded from the schema
50
+ include_groups: Tasks groups that should be included in the schema
51
+
52
+ Returns:
53
+ dict: The poe task schema
54
+ """
55
+
56
+ filtered_groups = _filter(
57
+ GROUPS, include_tasks, exclude_tasks, include_groups, exclude_groups
58
+ )
59
+ return {
60
+ "tasks": deepcopy(UTILITIES),
61
+ "groups": deepcopy(filtered_groups),
62
+ }
63
+
64
+
65
+ def _filter(
66
+ all_groups: dict,
67
+ include_tasks: Strings,
68
+ exclude_tasks: Strings,
69
+ include_groups: Strings,
70
+ exclude_groups: Strings,
71
+ ):
72
+ "Additively builds a filtered group dict"
73
+
74
+ # Determine whether tasks are opt-in or opt-out.
75
+ # Default is opt-out. Opt-in is triggered if an inclusion group is provided
76
+ is_opt_out = not (include_tasks or include_groups)
77
+
78
+ # Check baseline group eligibility
79
+ groups = {}
80
+ for group_name, group in all_groups.items():
81
+ group_id = group_name.split("_", maxsplit=1)[1]
82
+ keep_group = is_opt_out or group_id in include_groups or group_name in include_groups
83
+ if group_id in exclude_groups or group_name in exclude_groups:
84
+ keep_group = False
85
+
86
+ # Determine the eligibility of tasks within the group
87
+ tasks = {}
88
+ for task_name, task in group["tasks"].items():
89
+ keep_task = keep_group
90
+ if task_name in include_tasks:
91
+ keep_task = True
92
+ if task_name in exclude_tasks:
93
+ keep_task = False
94
+
95
+ # Save any tasks that are eligible
96
+ if keep_task:
97
+ tasks[task_name] = task
98
+
99
+ # Only save groups that have tasks
100
+ if tasks:
101
+ groups[group_name] = {"heading": group["heading"], "tasks": tasks}
102
+ return groups
@@ -0,0 +1,27 @@
1
+ """
2
+ Helper functions to format parts of a poe schema
3
+ ----------
4
+ Functions:
5
+ task - Formats a task configuration
6
+ utilities - Formats a collection of private utility tasks
7
+ group - Formats a task group
8
+ _replace_underscores - Replaces underscores with hyphens in task names
9
+ """
10
+
11
+
12
+ def task(help: str, **task_config) -> dict:
13
+ "Formats a task configuration"
14
+ return {"help": help} | task_config
15
+
16
+ def utilities(**tasks: dict) -> dict:
17
+ "Formats the schema for a collection of utility tasks"
18
+ tasks = _replace_underscores(tasks)
19
+ return {f"_{key}": task for key, task in tasks.items()}
20
+
21
+ def group(heading: str, **tasks: dict) -> dict:
22
+ "Formats the schema for a task group"
23
+ return {"heading": heading, "tasks": _replace_underscores(tasks)}
24
+
25
+ def _replace_underscores(tasks: dict) -> dict:
26
+ "Replaces underscores with hyphens in task names"
27
+ return {key.replace("_", "-"): task for key, task in tasks.items()}
@@ -0,0 +1,35 @@
1
+ """
2
+ Modules that build task groups
3
+ ----------
4
+ Each of these modules defines a particular task group, which is stored in its "GROUP"
5
+ attribute. Then this subpackage collects all the task groups in its "GROUPS" attribute.
6
+ ----------
7
+ Attributes:
8
+ GROUP - The complete collection of task groups
9
+
10
+ Modules:
11
+ _docs - Documentation task group
12
+ _env - Development environment task group
13
+ _lint - Formatting and linting task group
14
+ _security - Security testing task group
15
+ _setup - First time setup task group
16
+ _test - Testing task group
17
+ """
18
+
19
+ from poe_tasks_common._groups import (
20
+ _docs,
21
+ _env,
22
+ _lint,
23
+ _security,
24
+ _test,
25
+ _setup
26
+ )
27
+
28
+ GROUPS = {
29
+ "100_setup": _setup.GROUP,
30
+ "200_env": _env.GROUP,
31
+ "300_security": _security.GROUP,
32
+ "400_lint": _lint.GROUP,
33
+ "500_test": _test.GROUP,
34
+ "600_docs": _docs.GROUP,
35
+ }
@@ -0,0 +1,32 @@
1
+ "Task group for documentation"
2
+
3
+ from poe_tasks_common._format import group, task
4
+
5
+ output_folder = {
6
+ "help": "Folder containing the built documentation",
7
+ "name": "_output_folder",
8
+ "options": ["-o", "--output"],
9
+ "default": "${DOCS_OUTPUT:-public}",
10
+ }
11
+
12
+
13
+ docs = task(
14
+ "Rebuilds the documentation",
15
+ sequence=[
16
+ {"ref": "_rmtree ${_output_folder}"},
17
+ {"cmd": "sphinx-build -qa docs ${_output_folder}"},
18
+ ],
19
+ args=[output_folder],
20
+ )
21
+
22
+ open_docs = task(
23
+ "Opens the built documentation in a web browser",
24
+ ref="_browse-index ${_output_folder}",
25
+ args=[output_folder],
26
+ )
27
+
28
+ GROUP = group(
29
+ "Documentation:",
30
+ docs=docs,
31
+ open_docs=open_docs,
32
+ )
@@ -0,0 +1,21 @@
1
+ "Task group for managing the developer environment"
2
+
3
+ from poe_tasks_common._format import group, task
4
+
5
+
6
+
7
+ sync = task(
8
+ "Syncs the development environment",
9
+ cmd="uv sync --all-groups --all-extras",
10
+ )
11
+
12
+ upgrade = task(
13
+ "Syncs and upgrades the development environment",
14
+ ref="sync --upgrade",
15
+ )
16
+
17
+ GROUP = group(
18
+ "Environment:",
19
+ sync=sync,
20
+ upgrade=upgrade,
21
+ )
@@ -0,0 +1,23 @@
1
+ "Task group for linting and formatting"
2
+
3
+ from poe_tasks_common._format import group, task
4
+
5
+ format = task(
6
+ "Formats the code and its tests",
7
+ default_item_type="cmd",
8
+ sequence=[
9
+ "ruff format ${POE_ROOT}/src ${POE_ROOT}/tests",
10
+ "ruff check ${POE_ROOT}/src ${POE_ROOT}/tests --select I --fix",
11
+ ],
12
+ )
13
+
14
+ lint = task(
15
+ help="Lints the code and its tests",
16
+ cmd="ruff check ${POE_ROOT}/src ${POE_ROOT}/tests",
17
+ )
18
+
19
+ GROUP = group(
20
+ "Linting:",
21
+ format=format,
22
+ lint=lint,
23
+ )
@@ -0,0 +1,13 @@
1
+ "Task group for security testing"
2
+
3
+ from poe_tasks_common._format import group, task
4
+
5
+ audit = task(
6
+ "Check package dependencies for security issues",
7
+ cmd="pip-audit",
8
+ )
9
+
10
+ GROUP = group(
11
+ "Security:",
12
+ audit=audit,
13
+ )
@@ -0,0 +1,28 @@
1
+ "Task group for first time setup"
2
+
3
+ from poe_tasks_common._format import group, task
4
+
5
+
6
+ install_precommit = task(
7
+ "Installs pre-commit",
8
+ default_item_type="cmd",
9
+ sequence=[
10
+ "uv tool install pre-commit --with pre-commit-uv --force-reinstall",
11
+ "pre-commit install",
12
+ ],
13
+ )
14
+
15
+ setup = task(
16
+ "Syncs all dependencies and installs pre-commit",
17
+ default_item_type="ref",
18
+ sequence = [
19
+ "sync --frozen",
20
+ "install-precommit",
21
+ ]
22
+ )
23
+
24
+ GROUP = group(
25
+ "Setup:",
26
+ setup=setup,
27
+ install_precommit=install_precommit,
28
+ )
@@ -0,0 +1,49 @@
1
+ "Task group for testing"
2
+
3
+ from poe_tasks_common._format import group, task
4
+
5
+ coverage = task(
6
+ "Prints the coverage report for the tests",
7
+ cmd="coverage report",
8
+ )
9
+
10
+ tests = task(
11
+ "Runs the tests and requires a minimum coverage",
12
+ sequence=[
13
+ {
14
+ "cmd": (
15
+ "pytest ${POE_ROOT}/tests "
16
+ "--cov=${POE_ROOT}/src --cov=${POE_ROOT}/tests "
17
+ "--cov-fail-under=${_min-coverage} "
18
+ "--cov-report xml:coverage.xml"
19
+ )
20
+ },
21
+ {"ref": "coverage"},
22
+ ],
23
+ args=[
24
+ {
25
+ "name": "_min-coverage",
26
+ "help": "Minimum required test coverage percent",
27
+ "options": ["--min-coverage"],
28
+ "type": "string",
29
+ "default": "${MIN_COVERAGE:-100}",
30
+ }
31
+ ],
32
+ )
33
+
34
+ htmlcov = task(
35
+ "Builds an HTML coverage report and opens in a web browser",
36
+ sequence=[
37
+ {"ref": "_rmtree htmlcov"},
38
+ {"cmd": "coverage html --skip-covered"},
39
+ {"ref": "_browse-index htmlcov"},
40
+ ],
41
+ )
42
+
43
+
44
+ GROUP = group(
45
+ "Testing:",
46
+ tests=tests,
47
+ htmlcov=htmlcov,
48
+ coverage=coverage,
49
+ )
@@ -0,0 +1,18 @@
1
+ """
2
+ Modules that build private utility tasks
3
+ ----------
4
+ Each of these modules defines utility tasks related to a particular theme. The complete
5
+ task collection for each module is stored in its "TASKS" attribute. Then this subpackage
6
+ collects all these tasks in its "TASKS" attribute.
7
+ ----------
8
+ Attributes:
9
+ TASKS - The complete collection of utility tasks
10
+
11
+ Modules:
12
+ _browse - Utility tasks for opening assets in a web browser
13
+ _delete - Utility tasks for deleting files
14
+ """
15
+
16
+ from poe_tasks_common._utilities import _browse, _delete
17
+
18
+ TASKS = _browse.TASKS | _delete.TASKS
@@ -0,0 +1,50 @@
1
+ "Utility group for opening files in a web browser"
2
+
3
+ from poe_tasks_common._format import task, utilities
4
+
5
+ browse = task(
6
+ "Opens a URI (file or web URL) in a web browser",
7
+ expr="webbrowser.open(_uri)",
8
+ imports=["webbrowser"],
9
+ args=[
10
+ {
11
+ "name": "_uri",
12
+ "help": "The target URI (file or web URL) to open",
13
+ "positional": True,
14
+ "required": True,
15
+ }
16
+ ],
17
+ )
18
+
19
+ browse_file = task(
20
+ "Opens a project file in a web browser",
21
+ expr="webbrowser.open( (pathlib.Path(${POE_ROOT}) / _path).as_uri() )",
22
+ imports=["webbrowser", "pathlib"],
23
+ args=[
24
+ {
25
+ "name": "_path",
26
+ "help": "A file path relative to the project root",
27
+ "positional": True,
28
+ "required": True,
29
+ }
30
+ ],
31
+ )
32
+
33
+ browse_index = task(
34
+ "Opens a project subfolder's index.html file",
35
+ ref="_browse-file ${_folder}/index.html",
36
+ args=[
37
+ {
38
+ "name": "_folder",
39
+ "help": "A folder path relative to the project root",
40
+ "positional": True,
41
+ "required": True,
42
+ }
43
+ ],
44
+ )
45
+
46
+ TASKS = utilities(
47
+ browse=browse,
48
+ browse_file=browse_file,
49
+ browse_index=browse_index,
50
+ )
@@ -0,0 +1,44 @@
1
+ "Utility group for deleting files"
2
+
3
+ from poe_tasks_common._format import task, utilities
4
+
5
+ rmtree = task(
6
+ "Deletes a project subfolder and its contents",
7
+ expr=(
8
+ "shutil.rmtree(_folder) "
9
+ "if (pathlib.Path(${POE_ROOT}) / _folder).exists() "
10
+ "else None"
11
+ ),
12
+ imports=["shutil", "pathlib"],
13
+ args=[
14
+ {
15
+ "name": "_folder",
16
+ "help": "A folder path relative to the project root",
17
+ "positional": True,
18
+ "required": True,
19
+ }
20
+ ],
21
+ )
22
+
23
+ delete = task(
24
+ "Deletes a project file",
25
+ expr=(
26
+ "(pathlib.Path(${POE_ROOT}) / _file).unlink() "
27
+ "if (pathlib.Path(${POE_ROOT}) / _file).exists() "
28
+ "else None"
29
+ ),
30
+ imports=["pathlib"],
31
+ args=[
32
+ {
33
+ "name": "_file",
34
+ "help": "A file path relative to the project root",
35
+ "positional": True,
36
+ "required": True,
37
+ }
38
+ ],
39
+ )
40
+
41
+ TASKS = utilities(
42
+ delete=delete,
43
+ rmtree=rmtree,
44
+ )