pyprojkit 0.1.0__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.
- pyprojkit/__init__.py +104 -0
- pyprojkit/_paths.py +43 -0
- pyprojkit/_run.py +36 -0
- pyprojkit/cli.py +58 -0
- pyprojkit/config/__init__.py +79 -0
- pyprojkit/config/base.py +84 -0
- pyprojkit/config/profiles.py +97 -0
- pyprojkit/config/project.py +194 -0
- pyprojkit/config/tools.py +170 -0
- pyprojkit/discovery.py +51 -0
- pyprojkit/sessions.py +71 -0
- pyprojkit/sync.py +243 -0
- pyprojkit/tasks.py +375 -0
- pyprojkit/versions.py +114 -0
- pyprojkit-0.1.0.dist-info/METADATA +310 -0
- pyprojkit-0.1.0.dist-info/RECORD +19 -0
- pyprojkit-0.1.0.dist-info/WHEEL +4 -0
- pyprojkit-0.1.0.dist-info/entry_points.txt +3 -0
- pyprojkit-0.1.0.dist-info/licenses/LICENSE +21 -0
pyprojkit/__init__.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Development workflow toolkit for Python projects.
|
|
3
|
+
|
|
4
|
+
Declare configuration once in `pyprojconf.py`; pyprojkit keeps `pyproject.toml` in sync
|
|
5
|
+
and provides `doit` task and `nox` session factories.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__submodules__ = [
|
|
9
|
+
"config",
|
|
10
|
+
"versions",
|
|
11
|
+
"discovery",
|
|
12
|
+
"sync",
|
|
13
|
+
"tasks",
|
|
14
|
+
"sessions",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
# isort: off
|
|
18
|
+
# <AUTOGEN_INIT>
|
|
19
|
+
from .versions import (
|
|
20
|
+
DEFAULT_PATCH_VERSIONS,
|
|
21
|
+
PythonVersions,
|
|
22
|
+
)
|
|
23
|
+
from .config import (
|
|
24
|
+
ConfigError,
|
|
25
|
+
BaseToolConfig,
|
|
26
|
+
BaseFormatterConfig,
|
|
27
|
+
AutoflakeConfig,
|
|
28
|
+
IsortConfig,
|
|
29
|
+
BlackConfig,
|
|
30
|
+
DocformatterConfig,
|
|
31
|
+
TomlSortConfig,
|
|
32
|
+
PytestConfig,
|
|
33
|
+
CoverageRunConfig,
|
|
34
|
+
CoverageReportConfig,
|
|
35
|
+
DoitConfig,
|
|
36
|
+
NoxConfig,
|
|
37
|
+
MypyConfig,
|
|
38
|
+
ProjectConfig,
|
|
39
|
+
ToolsConfig,
|
|
40
|
+
FormattingConfig,
|
|
41
|
+
TestConfig,
|
|
42
|
+
DocConfig,
|
|
43
|
+
MkinitConfig,
|
|
44
|
+
SphinxConfig,
|
|
45
|
+
AnalysisConfig,
|
|
46
|
+
PublishConfig,
|
|
47
|
+
get_formatting_profile,
|
|
48
|
+
get_tools_profile,
|
|
49
|
+
register_formatting_profile,
|
|
50
|
+
register_tools_profile,
|
|
51
|
+
)
|
|
52
|
+
from .discovery import (
|
|
53
|
+
load_config,
|
|
54
|
+
)
|
|
55
|
+
from .sync import (
|
|
56
|
+
compute_managed_tables,
|
|
57
|
+
render,
|
|
58
|
+
sync,
|
|
59
|
+
)
|
|
60
|
+
from .tasks import (
|
|
61
|
+
TaskFactory,
|
|
62
|
+
)
|
|
63
|
+
from .sessions import (
|
|
64
|
+
NoxFactory,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
__all__ = [
|
|
68
|
+
"sync",
|
|
69
|
+
"DEFAULT_PATCH_VERSIONS",
|
|
70
|
+
"PythonVersions",
|
|
71
|
+
"ConfigError",
|
|
72
|
+
"BaseToolConfig",
|
|
73
|
+
"BaseFormatterConfig",
|
|
74
|
+
"AutoflakeConfig",
|
|
75
|
+
"IsortConfig",
|
|
76
|
+
"BlackConfig",
|
|
77
|
+
"DocformatterConfig",
|
|
78
|
+
"TomlSortConfig",
|
|
79
|
+
"PytestConfig",
|
|
80
|
+
"CoverageRunConfig",
|
|
81
|
+
"CoverageReportConfig",
|
|
82
|
+
"DoitConfig",
|
|
83
|
+
"NoxConfig",
|
|
84
|
+
"MypyConfig",
|
|
85
|
+
"ProjectConfig",
|
|
86
|
+
"ToolsConfig",
|
|
87
|
+
"FormattingConfig",
|
|
88
|
+
"TestConfig",
|
|
89
|
+
"DocConfig",
|
|
90
|
+
"MkinitConfig",
|
|
91
|
+
"SphinxConfig",
|
|
92
|
+
"AnalysisConfig",
|
|
93
|
+
"PublishConfig",
|
|
94
|
+
"get_formatting_profile",
|
|
95
|
+
"get_tools_profile",
|
|
96
|
+
"register_formatting_profile",
|
|
97
|
+
"register_tools_profile",
|
|
98
|
+
"load_config",
|
|
99
|
+
"compute_managed_tables",
|
|
100
|
+
"render",
|
|
101
|
+
"TaskFactory",
|
|
102
|
+
"NoxFactory",
|
|
103
|
+
]
|
|
104
|
+
# </AUTOGEN_INIT>
|
pyprojkit/_paths.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared path conventions for caches, artifacts, and badges.
|
|
3
|
+
|
|
4
|
+
These are used by config defaults, doit task factories, and nox session factories alike,
|
|
5
|
+
so they must not be coupled to any one tool.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
# caches
|
|
11
|
+
CACHE_PATH = Path("__cache__")
|
|
12
|
+
DOIT_DB_PATH = CACHE_PATH / ".doit.db"
|
|
13
|
+
COVERAGE_DATA_PATH = CACHE_PATH / ".coverage"
|
|
14
|
+
PYTEST_CACHE_PATH = CACHE_PATH / "pytest"
|
|
15
|
+
NOX_ENVDIR_PATH = CACHE_PATH / "nox"
|
|
16
|
+
|
|
17
|
+
# artifact output
|
|
18
|
+
OUT_PATH = Path("__out__")
|
|
19
|
+
|
|
20
|
+
# test coverage results
|
|
21
|
+
TESTS_PATH = OUT_PATH / "test"
|
|
22
|
+
JUNIT_PATH = TESTS_PATH / "junit.xml"
|
|
23
|
+
COV_PATH = TESTS_PATH / "cov"
|
|
24
|
+
COV_HTML_PATH = COV_PATH / "html"
|
|
25
|
+
COV_XML_PATH = COV_PATH / "coverage.xml"
|
|
26
|
+
|
|
27
|
+
# documentation
|
|
28
|
+
DOC_PATH = OUT_PATH / "doc"
|
|
29
|
+
DOC_HTML_PATH = DOC_PATH / "html"
|
|
30
|
+
|
|
31
|
+
# static analysis results
|
|
32
|
+
ANALYSIS_PATH = OUT_PATH / "analysis"
|
|
33
|
+
MYPY_PATH = ANALYSIS_PATH / "mypy"
|
|
34
|
+
MYPY_HTML_PATH = MYPY_PATH / "html"
|
|
35
|
+
MYPY_XML_PATH = MYPY_PATH / "xml"
|
|
36
|
+
|
|
37
|
+
# build/publish artifacts
|
|
38
|
+
UV_PATH = OUT_PATH / "uv"
|
|
39
|
+
|
|
40
|
+
# badges output
|
|
41
|
+
BADGES_PATH = Path("badges")
|
|
42
|
+
PYTEST_BADGE_PATH = BADGES_PATH / "tests.svg"
|
|
43
|
+
COV_BADGE_PATH = BADGES_PATH / "cov.svg"
|
pyprojkit/_run.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Subprocess helpers for task actions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def run(cmd: list[str], expect_rc: int | set[int] = 0):
|
|
13
|
+
"""
|
|
14
|
+
Run command, exiting if the return code is not among the expected ones.
|
|
15
|
+
|
|
16
|
+
The current interpreter's bin directory is prepended to `PATH` so tools from the
|
|
17
|
+
active environment are found even if it isn't activated.
|
|
18
|
+
"""
|
|
19
|
+
expect_rcs = expect_rc if isinstance(expect_rc, set) else {expect_rc}
|
|
20
|
+
env = os.environ.copy()
|
|
21
|
+
env["PATH"] = os.pathsep.join(
|
|
22
|
+
[str(Path(sys.executable).parent), env.get("PATH", "")]
|
|
23
|
+
)
|
|
24
|
+
print(f"=== Running: {cmd[0]}")
|
|
25
|
+
rc = subprocess.call(cmd, env=env)
|
|
26
|
+
if rc not in expect_rcs:
|
|
27
|
+
sys.exit(f"{cmd[0]} failed: rc={rc}, cmd={cmd}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def cleanup_dir(output_dir: Path | str):
|
|
31
|
+
"""
|
|
32
|
+
Remove directory if it exists.
|
|
33
|
+
"""
|
|
34
|
+
path = Path(output_dir)
|
|
35
|
+
if path.exists():
|
|
36
|
+
shutil.rmtree(path)
|
pyprojkit/cli.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface: `pyprojkit sync [--check]`.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from .config.base import ConfigError
|
|
12
|
+
from .discovery import load_config
|
|
13
|
+
from .sync import sync
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"main",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main(argv: list[str] | None = None) -> int:
|
|
21
|
+
parser = argparse.ArgumentParser(
|
|
22
|
+
prog="pyprojkit", description="Development workflow toolkit"
|
|
23
|
+
)
|
|
24
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
25
|
+
|
|
26
|
+
sync_parser = subparsers.add_parser(
|
|
27
|
+
"sync", help="Sync pyproject.toml from pyprojconf.py"
|
|
28
|
+
)
|
|
29
|
+
sync_parser.add_argument(
|
|
30
|
+
"--check",
|
|
31
|
+
action="store_true",
|
|
32
|
+
help="Verify pyproject.toml is in sync without writing; exit 1 if not",
|
|
33
|
+
)
|
|
34
|
+
sync_parser.add_argument(
|
|
35
|
+
"--root",
|
|
36
|
+
type=Path,
|
|
37
|
+
default=None,
|
|
38
|
+
help="Project root (default: current directory)",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
args = parser.parse_args(argv)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
config = load_config(args.root)
|
|
45
|
+
in_sync = sync(config, args.root, check=args.check)
|
|
46
|
+
except ConfigError as exc:
|
|
47
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
48
|
+
return 2
|
|
49
|
+
|
|
50
|
+
if args.check and not in_sync:
|
|
51
|
+
print("pyproject.toml is out of sync; run `pyprojkit sync`", file=sys.stderr)
|
|
52
|
+
return 1
|
|
53
|
+
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
sys.exit(main())
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration model for pyprojkit.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__submodules__ = [
|
|
6
|
+
"base",
|
|
7
|
+
"tools",
|
|
8
|
+
"project",
|
|
9
|
+
"profiles",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
# isort: off
|
|
13
|
+
# <AUTOGEN_INIT>
|
|
14
|
+
from .base import (
|
|
15
|
+
ConfigError,
|
|
16
|
+
BaseToolConfig,
|
|
17
|
+
BaseFormatterConfig,
|
|
18
|
+
)
|
|
19
|
+
from .tools import (
|
|
20
|
+
AutoflakeConfig,
|
|
21
|
+
IsortConfig,
|
|
22
|
+
BlackConfig,
|
|
23
|
+
DocformatterConfig,
|
|
24
|
+
TomlSortConfig,
|
|
25
|
+
PytestConfig,
|
|
26
|
+
CoverageRunConfig,
|
|
27
|
+
CoverageReportConfig,
|
|
28
|
+
DoitConfig,
|
|
29
|
+
NoxConfig,
|
|
30
|
+
MypyConfig,
|
|
31
|
+
)
|
|
32
|
+
from .project import (
|
|
33
|
+
ProjectConfig,
|
|
34
|
+
ToolsConfig,
|
|
35
|
+
FormattingConfig,
|
|
36
|
+
TestConfig,
|
|
37
|
+
DocConfig,
|
|
38
|
+
MkinitConfig,
|
|
39
|
+
SphinxConfig,
|
|
40
|
+
AnalysisConfig,
|
|
41
|
+
PublishConfig,
|
|
42
|
+
)
|
|
43
|
+
from .profiles import (
|
|
44
|
+
get_formatting_profile,
|
|
45
|
+
get_tools_profile,
|
|
46
|
+
register_formatting_profile,
|
|
47
|
+
register_tools_profile,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"ConfigError",
|
|
52
|
+
"BaseToolConfig",
|
|
53
|
+
"BaseFormatterConfig",
|
|
54
|
+
"AutoflakeConfig",
|
|
55
|
+
"IsortConfig",
|
|
56
|
+
"BlackConfig",
|
|
57
|
+
"DocformatterConfig",
|
|
58
|
+
"TomlSortConfig",
|
|
59
|
+
"PytestConfig",
|
|
60
|
+
"CoverageRunConfig",
|
|
61
|
+
"CoverageReportConfig",
|
|
62
|
+
"DoitConfig",
|
|
63
|
+
"NoxConfig",
|
|
64
|
+
"MypyConfig",
|
|
65
|
+
"ProjectConfig",
|
|
66
|
+
"ToolsConfig",
|
|
67
|
+
"FormattingConfig",
|
|
68
|
+
"TestConfig",
|
|
69
|
+
"DocConfig",
|
|
70
|
+
"MkinitConfig",
|
|
71
|
+
"SphinxConfig",
|
|
72
|
+
"AnalysisConfig",
|
|
73
|
+
"PublishConfig",
|
|
74
|
+
"get_formatting_profile",
|
|
75
|
+
"get_tools_profile",
|
|
76
|
+
"register_formatting_profile",
|
|
77
|
+
"register_tools_profile",
|
|
78
|
+
]
|
|
79
|
+
# </AUTOGEN_INIT>
|
pyprojkit/config/base.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base classes for tool configurations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass, fields
|
|
9
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .project import ProjectConfig
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ConfigError",
|
|
16
|
+
"BaseToolConfig",
|
|
17
|
+
"BaseFormatterConfig",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ConfigError(Exception):
|
|
22
|
+
"""
|
|
23
|
+
Raised for invalid or missing configuration.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(kw_only=True)
|
|
28
|
+
class BaseToolConfig(ABC):
|
|
29
|
+
"""
|
|
30
|
+
Configuration for a tool which reads its settings from a table in
|
|
31
|
+
`pyproject.toml`, e.g. `[tool.black]`.
|
|
32
|
+
|
|
33
|
+
Dataclass fields map to table entries; a field whose name differs from its
|
|
34
|
+
TOML key declares the key via `field(metadata={"toml": "some-key"})`.
|
|
35
|
+
Fields set to `None` are omitted. Values derived from project-wide config
|
|
36
|
+
(e.g. black's `target-version`) are contributed by `extra_toml()`.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
table_path: ClassVar[str]
|
|
40
|
+
"""
|
|
41
|
+
Dotted path of the tool's table, e.g. `"tool.pytest.ini_options"`.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def to_toml(self, project: ProjectConfig) -> dict[str, Any]:
|
|
45
|
+
"""
|
|
46
|
+
Get this tool's table contents.
|
|
47
|
+
"""
|
|
48
|
+
config: dict[str, Any] = {}
|
|
49
|
+
for field_ in fields(self):
|
|
50
|
+
value = getattr(self, field_.name)
|
|
51
|
+
if value is None:
|
|
52
|
+
continue
|
|
53
|
+
key = field_.metadata.get("toml", field_.name)
|
|
54
|
+
config[key] = list(value) if isinstance(value, tuple) else value
|
|
55
|
+
config.update(self.extra_toml(project))
|
|
56
|
+
return config
|
|
57
|
+
|
|
58
|
+
def extra_toml(self, project: ProjectConfig) -> dict[str, Any]:
|
|
59
|
+
"""
|
|
60
|
+
Get extra table entries derived from project-wide config.
|
|
61
|
+
"""
|
|
62
|
+
return {}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(kw_only=True)
|
|
66
|
+
class BaseFormatterConfig(BaseToolConfig):
|
|
67
|
+
"""
|
|
68
|
+
Configuration for a tool which additionally runs as part of the `format` task.
|
|
69
|
+
|
|
70
|
+
Adds the command line to invoke and the expected return codes; the order of
|
|
71
|
+
formatters in `FormattingConfig` is the run order.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
expect_rc: ClassVar[frozenset[int]] = frozenset({0})
|
|
75
|
+
"""
|
|
76
|
+
Return codes indicating success.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def command(self, py_paths: list[str], toml_paths: list[str]) -> list[str]:
|
|
81
|
+
"""
|
|
82
|
+
Get the command line to run, given python and toml paths to format.
|
|
83
|
+
"""
|
|
84
|
+
...
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Profile registries: named factories for pre-canned configurations.
|
|
3
|
+
|
|
4
|
+
A tools profile encompasses all tool categories; a formatting profile covers
|
|
5
|
+
just the formatter chain. Third parties can register their own profiles and
|
|
6
|
+
reuse them across projects:
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
from pyprojkit import register_tools_profile
|
|
10
|
+
|
|
11
|
+
register_tools_profile("mycompany", lambda: ToolsConfig(...))
|
|
12
|
+
```
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Callable
|
|
18
|
+
|
|
19
|
+
from .base import ConfigError
|
|
20
|
+
from .project import FormattingConfig, ToolsConfig
|
|
21
|
+
from .tools import (
|
|
22
|
+
AutoflakeConfig,
|
|
23
|
+
BlackConfig,
|
|
24
|
+
DocformatterConfig,
|
|
25
|
+
IsortConfig,
|
|
26
|
+
TomlSortConfig,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"get_formatting_profile",
|
|
31
|
+
"get_tools_profile",
|
|
32
|
+
"register_formatting_profile",
|
|
33
|
+
"register_tools_profile",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _default_formatting() -> FormattingConfig:
|
|
38
|
+
return FormattingConfig(
|
|
39
|
+
formatters=(
|
|
40
|
+
AutoflakeConfig(),
|
|
41
|
+
IsortConfig(),
|
|
42
|
+
BlackConfig(),
|
|
43
|
+
DocformatterConfig(),
|
|
44
|
+
TomlSortConfig(),
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_FORMATTING_PROFILES: dict[str, Callable[[], FormattingConfig]] = {
|
|
50
|
+
"default": _default_formatting,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# the zero-arg ToolsConfig constructor is itself the default profile, as its
|
|
54
|
+
# field defaults resolve to the default sub-configs
|
|
55
|
+
_TOOLS_PROFILES: dict[str, Callable[[], ToolsConfig]] = {
|
|
56
|
+
"default": ToolsConfig,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_formatting_profile(name: str) -> FormattingConfig:
|
|
61
|
+
"""
|
|
62
|
+
Get a new instance of the given formatting profile.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
factory = _FORMATTING_PROFILES[name]
|
|
66
|
+
except KeyError:
|
|
67
|
+
raise ConfigError(
|
|
68
|
+
f"Unknown formatting profile '{name}'; have {sorted(_FORMATTING_PROFILES)}"
|
|
69
|
+
) from None
|
|
70
|
+
return factory()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_tools_profile(name: str) -> ToolsConfig:
|
|
74
|
+
"""
|
|
75
|
+
Get a new instance of the given tools profile.
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
factory = _TOOLS_PROFILES[name]
|
|
79
|
+
except KeyError:
|
|
80
|
+
raise ConfigError(
|
|
81
|
+
f"Unknown tools profile '{name}'; have {sorted(_TOOLS_PROFILES)}"
|
|
82
|
+
) from None
|
|
83
|
+
return factory()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def register_formatting_profile(name: str, factory: Callable[[], FormattingConfig]):
|
|
87
|
+
"""
|
|
88
|
+
Register a named formatting profile.
|
|
89
|
+
"""
|
|
90
|
+
_FORMATTING_PROFILES[name] = factory
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def register_tools_profile(name: str, factory: Callable[[], ToolsConfig]):
|
|
94
|
+
"""
|
|
95
|
+
Register a named tools profile.
|
|
96
|
+
"""
|
|
97
|
+
_TOOLS_PROFILES[name] = factory
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project-wide configuration, declared in a project's `pyprojconf.py`.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import Any, Sequence
|
|
9
|
+
|
|
10
|
+
from .. import _paths
|
|
11
|
+
from ..versions import PythonVersions
|
|
12
|
+
from .base import BaseFormatterConfig
|
|
13
|
+
from .tools import (
|
|
14
|
+
CoverageReportConfig,
|
|
15
|
+
CoverageRunConfig,
|
|
16
|
+
DoitConfig,
|
|
17
|
+
MypyConfig,
|
|
18
|
+
NoxConfig,
|
|
19
|
+
PytestConfig,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"ProjectConfig",
|
|
24
|
+
"ToolsConfig",
|
|
25
|
+
"FormattingConfig",
|
|
26
|
+
"TestConfig",
|
|
27
|
+
"DocConfig",
|
|
28
|
+
"MkinitConfig",
|
|
29
|
+
"SphinxConfig",
|
|
30
|
+
"AnalysisConfig",
|
|
31
|
+
"PublishConfig",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(kw_only=True)
|
|
36
|
+
class FormattingConfig:
|
|
37
|
+
"""
|
|
38
|
+
Formatting configuration: which formatters run (in order) and their settings.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
formatters: tuple[BaseFormatterConfig, ...]
|
|
42
|
+
"""
|
|
43
|
+
Formatters in run order.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def default(cls) -> FormattingConfig:
|
|
48
|
+
"""
|
|
49
|
+
Get the default formatting profile.
|
|
50
|
+
"""
|
|
51
|
+
from .profiles import get_formatting_profile
|
|
52
|
+
|
|
53
|
+
return get_formatting_profile("default")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(kw_only=True)
|
|
57
|
+
class TestConfig:
|
|
58
|
+
"""
|
|
59
|
+
Testing configuration (pytest + coverage).
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
pytest: PytestConfig = field(default_factory=PytestConfig)
|
|
63
|
+
coverage_run: CoverageRunConfig = field(default_factory=CoverageRunConfig)
|
|
64
|
+
coverage_report: CoverageReportConfig = field(default_factory=CoverageReportConfig)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(kw_only=True)
|
|
68
|
+
class MkinitConfig:
|
|
69
|
+
"""
|
|
70
|
+
Configuration for generating `__init__.py` files using mkinit.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
args: tuple[str, ...] = (
|
|
74
|
+
"--recursive",
|
|
75
|
+
"--nomods",
|
|
76
|
+
"--relative",
|
|
77
|
+
"-i",
|
|
78
|
+
"--source-order",
|
|
79
|
+
)
|
|
80
|
+
"""
|
|
81
|
+
Arguments passed to mkinit.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
@dataclass(kw_only=True)
|
|
85
|
+
class SphinxConfig:
|
|
86
|
+
"""
|
|
87
|
+
Configuration for building documentation using sphinx.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
source_dir: str = "doc"
|
|
91
|
+
"""
|
|
92
|
+
Directory containing sphinx sources.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
copy_env_var: str | None = None
|
|
96
|
+
"""
|
|
97
|
+
Name of environment variable (or `.env` entry) giving a directory to which to copy
|
|
98
|
+
built documentation when the doc task is run with `--copy`.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
@dataclass(kw_only=True)
|
|
102
|
+
class DocConfig:
|
|
103
|
+
"""
|
|
104
|
+
Documentation tool configurations.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
mkinit: MkinitConfig | None = field(default_factory=MkinitConfig)
|
|
108
|
+
"""
|
|
109
|
+
Enables the `init` task; enabled by default.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
sphinx: SphinxConfig | None = None
|
|
113
|
+
"""
|
|
114
|
+
Enables the `doc` task; opt-in.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
@dataclass(kw_only=True)
|
|
118
|
+
class AnalysisConfig:
|
|
119
|
+
"""
|
|
120
|
+
Static analysis configuration.
|
|
121
|
+
"""
|
|
122
|
+
mypy: MypyConfig | None = field(default_factory=MypyConfig)
|
|
123
|
+
pyright: bool = True
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass(kw_only=True)
|
|
127
|
+
class PublishConfig:
|
|
128
|
+
"""
|
|
129
|
+
Configuration for building and publishing via uv.
|
|
130
|
+
"""
|
|
131
|
+
out_dir: str = str(_paths.UV_PATH)
|
|
132
|
+
"""
|
|
133
|
+
Directory for build artifacts; cleaned before each build so stale artifacts are
|
|
134
|
+
never published.
|
|
135
|
+
"""
|
|
136
|
+
@dataclass(kw_only=True)
|
|
137
|
+
class ToolsConfig:
|
|
138
|
+
"""
|
|
139
|
+
Development tool configurations, grouped by broad tool category.
|
|
140
|
+
|
|
141
|
+
A "tools profile" is simply a pre-canned `ToolsConfig` factory; see
|
|
142
|
+
`pyprojkit.config.profiles`. The default instance corresponds to the default
|
|
143
|
+
profile.
|
|
144
|
+
"""
|
|
145
|
+
formatting: FormattingConfig = field(default_factory=FormattingConfig.default)
|
|
146
|
+
test: TestConfig | None = field(default_factory=TestConfig)
|
|
147
|
+
doit: DoitConfig = field(default_factory=DoitConfig)
|
|
148
|
+
nox: NoxConfig = field(default_factory=NoxConfig)
|
|
149
|
+
doc: DocConfig = field(default_factory=DocConfig)
|
|
150
|
+
analysis: AnalysisConfig | None = None
|
|
151
|
+
publish: PublishConfig = field(default_factory=PublishConfig)
|
|
152
|
+
|
|
153
|
+
tool_overrides: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
154
|
+
"""
|
|
155
|
+
Escape hatch: extra entries merged last into managed tables, keyed by table path,
|
|
156
|
+
e.g. `{"tool.pytest.ini_options": {"addopts": "..."}}`.
|
|
157
|
+
|
|
158
|
+
A key not otherwise managed creates a new managed table.
|
|
159
|
+
"""
|
|
160
|
+
@classmethod
|
|
161
|
+
def default(cls) -> ToolsConfig:
|
|
162
|
+
"""
|
|
163
|
+
Get the default tools profile.
|
|
164
|
+
"""
|
|
165
|
+
from .profiles import get_tools_profile
|
|
166
|
+
|
|
167
|
+
return get_tools_profile("default")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@dataclass(kw_only=True)
|
|
171
|
+
class ProjectConfig:
|
|
172
|
+
"""
|
|
173
|
+
Top-level project configuration.
|
|
174
|
+
|
|
175
|
+
A project's `pyprojconf.py` must define a module-level instance named `config`.
|
|
176
|
+
"""
|
|
177
|
+
package: str
|
|
178
|
+
"""
|
|
179
|
+
Import name of the package, e.g. `"trilium_alchemy"`.
|
|
180
|
+
"""
|
|
181
|
+
python: PythonVersions
|
|
182
|
+
"""
|
|
183
|
+
Supported Python versions.
|
|
184
|
+
"""
|
|
185
|
+
format_paths: Sequence[str] = ("src", "test", "doc", "examples")
|
|
186
|
+
"""
|
|
187
|
+
Directories to format (those which exist), in addition to `*.py` and `*.toml` files
|
|
188
|
+
at the project root.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
tools: ToolsConfig = field(default_factory=ToolsConfig.default)
|
|
192
|
+
"""
|
|
193
|
+
Tool configurations.
|
|
194
|
+
"""
|