Skip to content

Commit ef5f8fd

Browse files
committed
Fix.
1 parent 08d3056 commit ef5f8fd

14 files changed

+65
-82
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,19 @@ repos:
1717
- id: python-no-log-warn
1818
- id: python-use-type-annotations
1919
- id: text-unicode-replacement-char
20-
- repo: https://github.com/asottile/reorder-python-imports
21-
rev: v3.12.0
22-
hooks:
23-
- id: reorder-python-imports
24-
args: [--py37-plus, --add-import, 'from __future__ import annotations']
2520
- repo: https://github.com/asottile/setup-cfg-fmt
2621
rev: v2.5.0
2722
hooks:
2823
- id: setup-cfg-fmt
29-
- repo: https://github.com/psf/black
30-
rev: 24.4.0
31-
hooks:
32-
- id: black
3324
- repo: https://github.com/astral-sh/ruff-pre-commit
3425
rev: v0.3.7
3526
hooks:
3627
- id: ruff
28+
- id: ruff-format
3729
- repo: https://github.com/dosisod/refurb
3830
rev: v2.0.0
3931
hooks:
4032
- id: refurb
41-
args: [--ignore, FURB126]
42-
- repo: https://github.com/econchick/interrogate
43-
rev: 1.7.0
44-
hooks:
45-
- id: interrogate
46-
args: [-v, --fail-under=40, src, tests]
4733
- repo: https://github.com/executablebooks/mdformat
4834
rev: 0.7.17
4935
hooks:
@@ -74,7 +60,7 @@ repos:
7460
hooks:
7561
- id: check-manifest
7662
args: [--no-build-isolation]
77-
additional_dependencies: [setuptools-scm, toml]
63+
additional_dependencies: [setuptools-scm, wheel, toml]
7864
- repo: meta
7965
hooks:
8066
- id: check-hooks-apply

pyproject.toml

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.0"]
33
build-backend = "setuptools.build_meta"
44

5-
65
[tool.setuptools_scm]
76
write_to = "src/pytask_r/_version.py"
87

9-
108
[tool.mypy]
119
files = ["src", "tests"]
1210
check_untyped_defs = true
@@ -17,56 +15,41 @@ no_implicit_optional = true
1715
warn_redundant_casts = true
1816
warn_unused_ignores = true
1917

20-
2118
[[tool.mypy.overrides]]
2219
module = "tests.*"
2320
disallow_untyped_defs = false
2421
ignore_errors = true
2522

26-
2723
[tool.ruff]
2824
target-version = "py38"
29-
select = ["ALL"]
3025
fix = true
26+
unsafe-fixes = true
27+
28+
[tool.ruff.lint]
3129
extend-ignore = [
32-
"TCH",
33-
"TRY",
34-
# Numpy docstyle
35-
"D107",
36-
"D203",
37-
"D212",
38-
"D213",
39-
"D402",
40-
"D413",
41-
"D415",
42-
"D416",
43-
"D417",
44-
# Others.
45-
"D404", # Do not start module docstring with "This".
46-
"RET504", # unnecessary variable assignment before return.
4730
"S101", # raise errors for asserts.
4831
"B905", # strict parameter for zip that was implemented in py310.
49-
"I", # ignore isort
5032
"ANN101", # type annotating self
5133
"ANN102", # type annotating cls
5234
"FBT", # flake8-boolean-trap
5335
"EM", # flake8-errmsg
5436
"ANN401", # flake8-annotate typing.Any
5537
"PD", # pandas-vet
56-
"COM812", # trailing comma missing, but black takes care of that
38+
"COM812", # Comply with ruff-format
39+
"ISC001", # Comply with ruff-format
5740
]
41+
select = ["ALL"]
5842

59-
60-
[tool.ruff.per-file-ignores]
43+
[tool.ruff.lint.per-file-ignores]
6144
"tests/*" = ["D", "ANN"]
6245

63-
64-
[tool.ruff.pydocstyle]
46+
[tool.ruff.lint.pydocstyle]
6547
convention = "numpy"
6648

49+
[tool.ruff.lint.isort]
50+
force-single-line = true
6751

6852
[tool.pytest.ini_options]
69-
# Do not add src since it messes with the loading of pytask-parallel as a plugin.
7053
testpaths = ["tests"]
7154
markers = [
7255
"wip: Tests that are work-in-progress.",

src/pytask_r/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""This module contains the main namespace of pytask-r."""
1+
"""Contains the main namespace of pytask-r."""
22

33
from __future__ import annotations
44

src/pytask_r/collect.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,31 @@
77
from pathlib import Path
88
from typing import Any
99

10-
from pytask import has_mark
11-
from pytask import hookimpl
12-
from pytask import is_task_function
1310
from pytask import Mark
1411
from pytask import NodeInfo
15-
from pytask import parse_dependencies_from_task_function
16-
from pytask import parse_products_from_task_function
1712
from pytask import PathNode
1813
from pytask import PTask
1914
from pytask import PythonNode
20-
from pytask import remove_marks
2115
from pytask import Session
2216
from pytask import Task
2317
from pytask import TaskWithoutPath
24-
from pytask_r.serialization import create_path_to_serialized
18+
from pytask import has_mark
19+
from pytask import hookimpl
20+
from pytask import is_task_function
21+
from pytask import parse_dependencies_from_task_function
22+
from pytask import parse_products_from_task_function
23+
from pytask import remove_marks
24+
2525
from pytask_r.serialization import SERIALIZERS
26+
from pytask_r.serialization import create_path_to_serialized
2627
from pytask_r.shared import r
2728

2829

2930
def run_r_script(
30-
_script: Path, _options: list[str], _serialized: Path, **kwargs: Any # noqa: ARG001
31+
_script: Path,
32+
_options: list[str],
33+
_serialized: Path,
34+
**kwargs: Any, # noqa: ARG001
3135
) -> None:
3236
"""Run an R script."""
3337
cmd = ["Rscript", _script.as_posix(), *_options, str(_serialized)]
@@ -50,10 +54,11 @@ def pytask_collect_task(
5054
# Parse @pytask.mark.r decorator.
5155
obj, marks = remove_marks(obj, "r")
5256
if len(marks) > 1:
53-
raise ValueError(
57+
msg = (
5458
f"Task {name!r} has multiple @pytask.mark.r marks, but only one is "
5559
"allowed."
5660
)
61+
raise ValueError(msg)
5762

5863
mark = _parse_r_mark(
5964
mark=marks[0],
@@ -89,10 +94,11 @@ def pytask_collect_task(
8994
)
9095

9196
if not (isinstance(script_node, PathNode) and script_node.path.suffix == ".r"):
92-
raise ValueError(
97+
msg = (
9398
"The 'script' keyword of the @pytask.mark.r decorator must point "
9499
f"to Julia file with the .r suffix, but it is {script_node}."
95100
)
101+
raise ValueError(msg)
96102

97103
options_node = session.hook.pytask_collect_node(
98104
session=session,
@@ -182,5 +188,4 @@ def _parse_r_mark(
182188
)
183189
parsed_kwargs["suffix"] = suffix or proposed_suffix # type: ignore[assignment]
184190

185-
mark = Mark("r", (), parsed_kwargs)
186-
return mark
191+
return Mark("r", (), parsed_kwargs)

src/pytask_r/config.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Any
66

77
from pytask import hookimpl
8+
89
from pytask_r.serialization import SERIALIZERS
910

1011

@@ -14,10 +15,11 @@ def pytask_parse_config(config: dict[str, Any]) -> None:
1415
config["markers"]["r"] = "Tasks which are executed with Rscript."
1516
config["r_serializer"] = config.get("r_serializer", "json")
1617
if config["r_serializer"] not in SERIALIZERS:
17-
raise ValueError(
18+
msg = (
1819
f"'r_serializer' is {config['r_serializer']} and not one of "
1920
f"{list(SERIALIZERS)}."
2021
)
22+
raise ValueError(msg)
2123
config["r_suffix"] = config.get("r_suffix", "")
2224
config["r_options"] = _parse_value_or_whitespace_option(config.get("r_options"))
2325

@@ -28,4 +30,5 @@ def _parse_value_or_whitespace_option(value: Any) -> list[str] | None:
2830
return None
2931
if isinstance(value, list):
3032
return list(map(str, value))
31-
raise ValueError(f"'r_options' is {value} and not a list.")
33+
msg = f"'r_options' is {value} and not a list."
34+
raise ValueError(msg)

src/pytask_r/execute.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
import shutil
66
from typing import Any
77

8-
from pytask import get_marks
9-
from pytask import hookimpl
108
from pytask import PPathNode
119
from pytask import PTask
1210
from pytask import PythonNode
11+
from pytask import get_marks
12+
from pytask import hookimpl
1313
from pytask.tree_util import tree_map
14+
1415
from pytask_r.serialization import serialize_keyword_arguments
1516
from pytask_r.shared import r
1617

@@ -21,9 +22,10 @@ def pytask_execute_task_setup(task: PTask) -> None:
2122
marks = get_marks(task, "r")
2223
if marks:
2324
if shutil.which("Rscript") is None:
24-
raise RuntimeError(
25+
msg = (
2526
"Rscript is needed to run R scripts, but it is not found on your PATH."
2627
)
28+
raise RuntimeError(msg)
2729

2830
assert len(marks) == 1
2931

@@ -32,9 +34,9 @@ def pytask_execute_task_setup(task: PTask) -> None:
3234
assert suffix
3335

3436
serialized_node: PythonNode = task.depends_on["_serialized"] # type: ignore[assignment]
35-
serialized_node.value.parent.mkdir(parents=True, exist_ok=True)
37+
serialized_node.value.parent.mkdir(parents=True, exist_ok=True) # type: ignore[union-attr]
3638
kwargs = collect_keyword_arguments(task)
37-
serialize_keyword_arguments(serializer, serialized_node.value, kwargs)
39+
serialize_keyword_arguments(serializer, serialized_node.value, kwargs) # type: ignore[arg-type]
3840

3941

4042
def collect_keyword_arguments(task: PTask) -> dict[str, Any]:

src/pytask_r/plugin.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22

33
from __future__ import annotations
44

5-
from pluggy import PluginManager
5+
from typing import TYPE_CHECKING
6+
67
from pytask import hookimpl
8+
79
from pytask_r import collect
810
from pytask_r import config
911
from pytask_r import execute
1012

13+
if TYPE_CHECKING:
14+
from pluggy import PluginManager
15+
1116

1217
@hookimpl
1318
def pytask_add_hooks(pm: PluginManager) -> None:

src/pytask_r/serialization.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""This module contains the code to serialize keyword arguments to the task."""
1+
"""Contains the code to serialize keyword arguments to the task."""
22

33
from __future__ import annotations
44

@@ -10,7 +10,6 @@
1010
from pytask import PTask
1111
from pytask import PTaskWithPath
1212

13-
1413
_HIDDEN_FOLDER = ".pytask/pytask-r"
1514

1615

@@ -30,8 +29,7 @@ def create_path_to_serialized(task: PTask, suffix: str) -> Path:
3029
"""Create path to serialized."""
3130
parent = task.path.parent if isinstance(task, PTaskWithPath) else Path.cwd()
3231
file_name = create_file_name(task, suffix)
33-
path = parent.joinpath(_HIDDEN_FOLDER, file_name).with_suffix(suffix)
34-
return path
32+
return parent.joinpath(_HIDDEN_FOLDER, file_name).with_suffix(suffix)
3533

3634

3735
def create_file_name(task: PTask, suffix: str) -> str:
@@ -60,11 +58,10 @@ def serialize_keyword_arguments(
6058
if callable(serializer):
6159
serializer_func = serializer
6260
elif isinstance(serializer, str) and serializer in SERIALIZERS:
63-
serializer_func = SERIALIZERS[serializer][
64-
"serializer"
65-
] # type: ignore[assignment]
61+
serializer_func = SERIALIZERS[serializer]["serializer"] # type: ignore[assignment]
6662
else:
67-
raise ValueError(f"Serializer {serializer!r} is not known.")
63+
msg = f"Serializer {serializer!r} is not known."
64+
raise ValueError(msg)
6865

6966
serialized = serializer_func(kwargs)
7067
path_to_serialized.write_text(serialized)

src/pytask_r/shared.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
"""This module contains shared functions."""
1+
"""Contains shared functions."""
22

33
from __future__ import annotations
44

5-
from pathlib import Path
5+
from typing import TYPE_CHECKING
66
from typing import Any
77
from typing import Callable
88
from typing import Iterable
99
from typing import Sequence
1010

11+
if TYPE_CHECKING:
12+
from pathlib import Path
13+
1114

1215
def r(
1316
*,

tests/conftest.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import pytest
99
from click.testing import CliRunner
1010

11-
1211
needs_rscript = pytest.mark.skipif(
1312
shutil.which("Rscript") is None, reason="R with Rscript needs to be installed."
1413
)
@@ -37,7 +36,7 @@ class SysPathsSnapshot:
3736
"""A snapshot for sys.path."""
3837

3938
def __init__(self) -> None:
40-
self.__saved = list(sys.path), list(sys.meta_path)
39+
self.__saved = sys.path.copy(), sys.meta_path.copy()
4140

4241
def restore(self) -> None:
4342
sys.path[:], sys.meta_path[:] = self.__saved
@@ -48,7 +47,7 @@ class SysModulesSnapshot:
4847

4948
def __init__(self, preserve: Callable[[str], bool] | None = None) -> None:
5049
self.__preserve = preserve
51-
self.__saved = dict(sys.modules)
50+
self.__saved = sys.modules.copy()
5251

5352
def restore(self) -> None:
5453
if self.__preserve:

0 commit comments

Comments
 (0)