Skip to content

Commit 6e73ce4

Browse files
authored
Merge branch 'main' into pre-commit-ci-update-config
2 parents 20c2463 + 715920a commit 6e73ce4

31 files changed

+183
-137
lines changed

docs/source/changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and
2525
- {pull}`536` allows partialed functions to be task functions.
2626
- {pull}`540` changes the CLI entry-point and allow `pytask.build(tasks=task_func)` as
2727
the signatures suggested.
28+
- {pull}`542` refactors the plugin manager.
2829

2930
## 0.4.4 - 2023-12-04
3031

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ ignore = [
146146
convention = "numpy"
147147

148148
[tool.pytest.ini_options]
149-
testpaths = ["tests"]
149+
testpaths = ["src", "tests"]
150150
markers = [
151151
"wip: Tests that are work-in-progress.",
152152
"unit: Flag for unit tests which target mainly a single function.",

src/_pytask/build.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from _pytask.capture_utils import CaptureMethod
1616
from _pytask.capture_utils import ShowCapture
1717
from _pytask.click import ColoredCommand
18-
from _pytask.config import hookimpl
1918
from _pytask.config_utils import find_project_root_and_config
2019
from _pytask.config_utils import read_config
2120
from _pytask.console import console
@@ -26,6 +25,8 @@
2625
from _pytask.outcomes import ExitCode
2726
from _pytask.path import HashPathCache
2827
from _pytask.pluginmanager import get_plugin_manager
28+
from _pytask.pluginmanager import hookimpl
29+
from _pytask.pluginmanager import storage
2930
from _pytask.session import Session
3031
from _pytask.shared import parse_paths
3132
from _pytask.shared import to_list
@@ -62,7 +63,7 @@ def pytask_unconfigure(session: Session) -> None:
6263
path.write_text(json.dumps(HashPathCache._cache))
6364

6465

65-
def build( # noqa: C901, PLR0912, PLR0913
66+
def build( # noqa: C901, PLR0912, PLR0913, PLR0915
6667
*,
6768
capture: Literal["fd", "no", "sys", "tee-sys"] | CaptureMethod = CaptureMethod.FD,
6869
check_casing_of_paths: bool = True,
@@ -177,8 +178,6 @@ def build( # noqa: C901, PLR0912, PLR0913
177178
178179
"""
179180
try:
180-
pm = get_plugin_manager()
181-
182181
raw_config = {
183182
"capture": capture,
184183
"check_casing_of_paths": check_casing_of_paths,
@@ -212,6 +211,12 @@ def build( # noqa: C901, PLR0912, PLR0913
212211
**kwargs,
213212
}
214213

214+
if "command" not in raw_config:
215+
pm = get_plugin_manager()
216+
storage.store(pm)
217+
else:
218+
pm = storage.get()
219+
215220
# If someone called the programmatic interface, we need to do some parsing.
216221
if "command" not in raw_config:
217222
raw_config["command"] = "build"

src/_pytask/capture.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
from _pytask.capture_utils import CaptureMethod
4545
from _pytask.capture_utils import ShowCapture
4646
from _pytask.click import EnumChoice
47-
from _pytask.config import hookimpl
47+
from _pytask.pluginmanager import hookimpl
4848
from _pytask.shared import convert_to_enum
4949

5050
if TYPE_CHECKING:

src/_pytask/clean.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import click
1515
from _pytask.click import ColoredCommand
1616
from _pytask.click import EnumChoice
17-
from _pytask.config import hookimpl
1817
from _pytask.console import console
1918
from _pytask.exceptions import CollectionError
2019
from _pytask.git import get_all_files
@@ -26,7 +25,8 @@
2625
from _pytask.outcomes import ExitCode
2726
from _pytask.path import find_common_ancestor
2827
from _pytask.path import relative_to
29-
from _pytask.pluginmanager import get_plugin_manager
28+
from _pytask.pluginmanager import hookimpl
29+
from _pytask.pluginmanager import storage
3030
from _pytask.session import Session
3131
from _pytask.shared import to_list
3232
from _pytask.traceback import Traceback
@@ -97,12 +97,11 @@ def pytask_parse_config(config: dict[str, Any]) -> None:
9797
)
9898
def clean(**raw_config: Any) -> NoReturn: # noqa: C901, PLR0912
9999
"""Clean the provided paths by removing files unknown to pytask."""
100+
pm = storage.get()
100101
raw_config["command"] = "clean"
101102

102103
try:
103104
# Duplication of the same mechanism in :func:`pytask.build`.
104-
pm = get_plugin_manager()
105-
106105
config = pm.hook.pytask_configure(pm=pm, raw_config=raw_config)
107106
session = Session.from_config(config)
108107

src/_pytask/cli.py

Lines changed: 6 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,29 @@
22
from __future__ import annotations
33

44
from typing import Any
5-
from typing import TYPE_CHECKING
65

76
import click
87
from _pytask.click import ColoredGroup
9-
from _pytask.config import hookimpl
10-
from _pytask.pluginmanager import get_plugin_manager
8+
from _pytask.pluginmanager import storage
119
from packaging.version import parse as parse_version
1210

13-
if TYPE_CHECKING:
14-
import pluggy
15-
1611

1712
_CONTEXT_SETTINGS: dict[str, Any] = {
1813
"help_option_names": ("-h", "--help"),
1914
"show_default": True,
2015
}
2116

2217

23-
if parse_version(click.__version__) < parse_version("8"): # pragma: no cover
24-
_VERSION_OPTION_KWARGS: dict[str, Any] = {}
25-
else: # pragma: no cover
18+
if parse_version(click.__version__) >= parse_version("8"): # pragma: no cover
2619
_VERSION_OPTION_KWARGS = {"package_name": "pytask"}
20+
else: # pragma: no cover
21+
_VERSION_OPTION_KWARGS = {}
2722

2823

2924
def _extend_command_line_interface(cli: click.Group) -> click.Group:
3025
"""Add parameters from plugins to the commandline interface."""
31-
pm = get_plugin_manager()
32-
pm.hook.pytask_extend_command_line_interface(cli=cli)
26+
pm = storage.create()
27+
pm.hook.pytask_extend_command_line_interface.call_historic(kwargs={"cli": cli})
3328
_sort_options_for_each_command_alphabetically(cli)
3429
return cli
3530

@@ -42,54 +37,6 @@ def _sort_options_for_each_command_alphabetically(cli: click.Group) -> None:
4237
)
4338

4439

45-
@hookimpl
46-
def pytask_add_hooks(pm: pluggy.PluginManager) -> None:
47-
"""Add hooks."""
48-
from _pytask import build
49-
from _pytask import capture
50-
from _pytask import clean
51-
from _pytask import collect
52-
from _pytask import collect_command
53-
from _pytask import config
54-
from _pytask import database
55-
from _pytask import debugging
56-
from _pytask import execute
57-
from _pytask import dag_command
58-
from _pytask import live
59-
from _pytask import logging
60-
from _pytask import mark
61-
from _pytask import nodes
62-
from _pytask import parameters
63-
from _pytask import persist
64-
from _pytask import profile
65-
from _pytask import dag
66-
from _pytask import skipping
67-
from _pytask import task
68-
from _pytask import warnings
69-
70-
pm.register(build)
71-
pm.register(capture)
72-
pm.register(clean)
73-
pm.register(collect)
74-
pm.register(collect_command)
75-
pm.register(config)
76-
pm.register(database)
77-
pm.register(debugging)
78-
pm.register(execute)
79-
pm.register(dag_command)
80-
pm.register(live)
81-
pm.register(logging)
82-
pm.register(mark)
83-
pm.register(nodes)
84-
pm.register(parameters)
85-
pm.register(persist)
86-
pm.register(profile)
87-
pm.register(dag)
88-
pm.register(skipping)
89-
pm.register(task)
90-
pm.register(warnings)
91-
92-
9340
@click.group(
9441
cls=ColoredGroup,
9542
context_settings=_CONTEXT_SETTINGS,

src/_pytask/collect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from _pytask.collect_utils import create_name_of_python_node
1616
from _pytask.collect_utils import parse_dependencies_from_task_function
1717
from _pytask.collect_utils import parse_products_from_task_function
18-
from _pytask.config import hookimpl
1918
from _pytask.config import IS_FILE_SYSTEM_CASE_SENSITIVE
2019
from _pytask.console import console
2120
from _pytask.console import create_summary_panel
@@ -36,6 +35,7 @@
3635
from _pytask.path import find_case_sensitive_path
3736
from _pytask.path import import_path
3837
from _pytask.path import shorten_path
38+
from _pytask.pluginmanager import hookimpl
3939
from _pytask.reports import CollectionReport
4040
from _pytask.shared import find_duplicates
4141
from _pytask.shared import to_list

src/_pytask/collect_command.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import click
1010
from _pytask.click import ColoredCommand
11-
from _pytask.config import hookimpl
1211
from _pytask.console import console
1312
from _pytask.console import create_url_style_for_path
1413
from _pytask.console import FILE_ICON
@@ -27,7 +26,8 @@
2726
from _pytask.outcomes import ExitCode
2827
from _pytask.path import find_common_ancestor
2928
from _pytask.path import relative_to
30-
from _pytask.pluginmanager import get_plugin_manager
29+
from _pytask.pluginmanager import hookimpl
30+
from _pytask.pluginmanager import storage
3131
from _pytask.session import Session
3232
from _pytask.tree_util import tree_leaves
3333
from rich.text import Text
@@ -54,10 +54,10 @@ def pytask_extend_command_line_interface(cli: click.Group) -> None:
5454
)
5555
def collect(**raw_config: Any | None) -> NoReturn:
5656
"""Collect tasks and report information about them."""
57+
pm = storage.get()
5758
raw_config["command"] = "collect"
5859

5960
try:
60-
pm = get_plugin_manager()
6161
config = pm.hook.pytask_configure(pm=pm, raw_config=raw_config)
6262
session = Session.from_config(config)
6363

src/_pytask/compat.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"""Contains functions to assess compatibility and optional dependencies."""
22
from __future__ import annotations
33

4-
import importlib
54
import shutil
65
import sys
76
import warnings
7+
from importlib import import_module
88
from typing import TYPE_CHECKING
99

1010
from packaging.version import parse as parse_version
@@ -89,7 +89,9 @@ def import_optional_dependency(
8989
f"Use pip or conda to install {install_name!r}."
9090
)
9191
try:
92-
module = importlib.import_module(name)
92+
# The from import is used to avoid monkeypatching errors in some tests. See
93+
# https://stackoverflow.com/a/31746577 for more information.
94+
module = import_module(name)
9395
except ImportError:
9496
if errors == "raise":
9597
raise ImportError(msg) from None

src/_pytask/config.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
import tempfile
55
from pathlib import Path
66
from typing import Any
7+
from typing import TYPE_CHECKING
78

8-
import pluggy
9+
from _pytask.pluginmanager import hookimpl
910
from _pytask.shared import parse_markers
1011
from _pytask.shared import parse_paths
1112
from _pytask.shared import to_list
1213

13-
14-
hookimpl = pluggy.HookimplMarker("pytask")
14+
if TYPE_CHECKING:
15+
from pluggy import PluginManager
1516

1617

1718
_IGNORED_FOLDERS: list[str] = [".git/*", ".venv/*"]
@@ -59,9 +60,7 @@ def is_file_system_case_sensitive() -> bool:
5960

6061

6162
@hookimpl
62-
def pytask_configure(
63-
pm: pluggy.PluginManager, raw_config: dict[str, Any]
64-
) -> dict[str, Any]:
63+
def pytask_configure(pm: PluginManager, raw_config: dict[str, Any]) -> dict[str, Any]:
6564
"""Configure pytask."""
6665
# Add all values by default so that many plugins do not need to copy over values.
6766
config = {"pm": pm, "markers": {}, **raw_config}

src/_pytask/dag.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from typing import TYPE_CHECKING
77

88
import networkx as nx
9-
from _pytask.config import hookimpl
109
from _pytask.console import ARROW_DOWN_ICON
1110
from _pytask.console import console
1211
from _pytask.console import FILE_ICON
@@ -19,6 +18,7 @@
1918
from _pytask.node_protocols import PNode
2019
from _pytask.node_protocols import PTask
2120
from _pytask.nodes import PythonNode
21+
from _pytask.pluginmanager import hookimpl
2222
from _pytask.reports import DagReport
2323
from _pytask.shared import reduce_names_of_multiple_nodes
2424
from _pytask.tree_util import tree_map

src/_pytask/dag_command.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from _pytask.click import EnumChoice
1313
from _pytask.compat import check_for_optional_program
1414
from _pytask.compat import import_optional_dependency
15-
from _pytask.config import hookimpl
1615
from _pytask.config_utils import find_project_root_and_config
1716
from _pytask.config_utils import read_config
1817
from _pytask.console import console
@@ -21,6 +20,8 @@
2120
from _pytask.exceptions import ResolvingDependenciesError
2221
from _pytask.outcomes import ExitCode
2322
from _pytask.pluginmanager import get_plugin_manager
23+
from _pytask.pluginmanager import hookimpl
24+
from _pytask.pluginmanager import storage
2425
from _pytask.session import Session
2526
from _pytask.shared import parse_paths
2627
from _pytask.shared import reduce_names_of_multiple_nodes
@@ -80,7 +81,7 @@ def pytask_extend_command_line_interface(cli: click.Group) -> None:
8081
def dag(**raw_config: Any) -> int:
8182
"""Create a visualization of the project's directed acyclic graph."""
8283
try:
83-
pm = get_plugin_manager()
84+
pm = storage.get()
8485
config = pm.hook.pytask_configure(pm=pm, raw_config=raw_config)
8586
session = Session.from_config(config)
8687

@@ -143,6 +144,7 @@ def build_dag(raw_config: dict[str, Any]) -> nx.DiGraph:
143144
"""
144145
try:
145146
pm = get_plugin_manager()
147+
storage.store(pm)
146148

147149
# If someone called the programmatic interface, we need to do some parsing.
148150
if "command" not in raw_config:

0 commit comments

Comments
 (0)