Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 24 additions & 26 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,14 @@ repos:
- id: debug-statements
- id: end-of-file-fixer
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0 # Use the ref you want to point at
rev: v1.9.0
hooks:
- id: python-check-blanket-noqa
- id: python-check-mock-methods
- id: python-no-eval
- id: python-no-log-warn
- id: python-use-type-annotations
- id: text-unicode-replacement-char
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
hooks:
- id: pyupgrade
args: [--py37-plus]
- repo: https://github.com/asottile/reorder_python_imports
rev: v3.9.0
hooks:
Expand All @@ -40,27 +35,15 @@ repos:
rev: 22.12.0
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.215
hooks:
- id: flake8
types: [python]
additional_dependencies: [
flake8-alfred,
flake8-bugbear,
flake8-builtins,
flake8-comprehensions,
flake8-docstrings,
flake8-eradicate,
flake8-print,
flake8-pytest-style,
flake8-todo,
flake8-typing-imports,
flake8-unused-arguments,
pep8-naming,
pydocstyle,
Pygments,
]
- id: ruff
- repo: https://github.com/dosisod/refurb
rev: v1.9.1
hooks:
- id: refurb
args: [--ignore, FURB126]
- repo: https://github.com/econchick/interrogate
rev: 1.5.0
hooks:
Expand All @@ -79,6 +62,21 @@ repos:
rev: v2.2.2
hooks:
- id: codespell
- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v0.991'
hooks:
- id: mypy
args: [
--no-strict-optional,
--ignore-missing-imports,
]
additional_dependencies: [
attrs>=21.3.0,
click,
types-PyYAML,
types-setuptools
]
pass_filenames: false
- repo: https://github.com/mgedmin/check-manifest
rev: "0.49"
hooks:
Expand Down
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask-r) and
## 0.3.0 - 2023-xx-xx

- {pull}`33` deprecates INI configurations and aligns the plugin with pytask v0.3.
- {pull}`34` adds mypy, ruff and refurb.

## 0.2.0 - 2022-04-16

Expand Down
55 changes: 55 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,58 @@ build-backend = "setuptools.build_meta"

[tool.setuptools_scm]
write_to = "src/pytask_r/_version.py"


[tool.mypy]
files = ["src", "tests"]
check_untyped_defs = true
disallow_any_generics = true
disallow_incomplete_defs = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true


[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
ignore_errors = true


[tool.ruff]
target-version = "py37"
select = ["ALL"]
fix = true
extend-ignore = [
# Numpy docstyle
"D107",
"D203",
"D212",
"D213",
"D402",
"D413",
"D415",
"D416",
"D417",
# Others.
"D404", # Do not start module docstring with "This".
"RET504", # unnecessary variable assignment before return.
"S101", # raise errors for asserts.
"B905", # strict parameter for zip that was implemented in py310.
"I", # ignore isort
"ANN101", # type annotating self
"ANN102", # type annotating cls
"FBT", # flake8-boolean-trap
"EM", # flake8-errmsg
"ANN401", # flake8-annotate typing.Any
"PD", # pandas-vet
]


[tool.ruff.per-file-ignores]
"tests/*" = ["D", "ANN"]


[tool.ruff.pydocstyle]
convention = "numpy"
1 change: 1 addition & 0 deletions src/pytask_r/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""This module contains the main namespace of pytask-r."""
from __future__ import annotations

try:
Expand Down
35 changes: 22 additions & 13 deletions src/pytask_r/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import subprocess
from pathlib import Path
from types import FunctionType
from typing import Any

from pytask import depends_on
from pytask import has_mark
Expand All @@ -13,6 +14,7 @@
from pytask import parse_nodes
from pytask import produces
from pytask import remove_marks
from pytask import Session
from pytask import Task
from pytask_r.serialization import SERIALIZERS
from pytask_r.shared import r
Expand All @@ -26,7 +28,9 @@ def run_r_script(script: Path, options: list[str], serialized: Path) -> None:


@hookimpl
def pytask_collect_task(session, path, name, obj):
def pytask_collect_task(
session: Session, path: Path, name: str, obj: Any
) -> Task | None:
"""Perform some checks."""
__tracebackhide__ = True

Expand Down Expand Up @@ -62,7 +66,7 @@ def pytask_collect_task(session, path, name, obj):
task = Task(
base_name=name,
path=path,
function=_copy_func(run_r_script),
function=_copy_func(run_r_script), # type: ignore[arg-type]
depends_on=dependencies,
produces=products,
markers=markers,
Expand All @@ -85,28 +89,33 @@ def pytask_collect_task(session, path, name, obj):
)

return task
return None


def _parse_r_mark(mark, default_options, default_serializer, default_suffix):
def _parse_r_mark(
mark: Mark,
default_options: list[str] | None,
default_serializer: str,
default_suffix: str,
) -> Mark:
"""Parse a Julia mark."""
script, options, serializer, suffix = r(**mark.kwargs)

parsed_kwargs = {}
for arg_name, value, default in [
for arg_name, value, default in (
("script", script, None),
("options", options, default_options),
("serializer", serializer, default_serializer),
]:
parsed_kwargs[arg_name] = value if value else default
):
parsed_kwargs[arg_name] = value or default

if (
isinstance(parsed_kwargs["serializer"], str)
proposed_suffix = (
SERIALIZERS[parsed_kwargs["serializer"]]["suffix"]
if isinstance(parsed_kwargs["serializer"], str)
and parsed_kwargs["serializer"] in SERIALIZERS
):
proposed_suffix = SERIALIZERS[parsed_kwargs["serializer"]]["suffix"]
else:
proposed_suffix = default_suffix
parsed_kwargs["suffix"] = suffix if suffix else proposed_suffix
else default_suffix
)
parsed_kwargs["suffix"] = suffix or proposed_suffix # type: ignore[assignment]

mark = Mark("r", (), parsed_kwargs)
return mark
Expand Down
9 changes: 5 additions & 4 deletions src/pytask_r/config.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""Configure pytask."""
from __future__ import annotations

from typing import Any

from pytask import hookimpl
from pytask_r.serialization import SERIALIZERS


@hookimpl
def pytask_parse_config(config):
def pytask_parse_config(config: dict[str, Any]) -> None:
"""Register the r marker."""
config["markers"]["r"] = "Tasks which are executed with Rscript."
config["r_serializer"] = config.get("r_serializer", "json")
Expand All @@ -23,7 +25,6 @@ def _parse_value_or_whitespace_option(value: str | None) -> None | str | list[st
"""Parse option which can hold a single value or values separated by new lines."""
if value is None:
return None
elif isinstance(value, list):
if isinstance(value, list):
return list(map(str, value))
else:
raise ValueError(f"'r_options' is {value} and not a list.")
raise ValueError(f"'r_options' is {value} and not a list.")
2 changes: 1 addition & 1 deletion src/pytask_r/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


@hookimpl
def pytask_execute_task_setup(task):
def pytask_execute_task_setup(task: Task) -> None:
"""Perform some checks when a task marked with the r marker is executed."""
marks = get_marks(task, "r")
if marks:
Expand Down
9 changes: 5 additions & 4 deletions src/pytask_r/parametrize.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"""Parametrize tasks."""
from __future__ import annotations

from typing import Any

import pytask
from pytask import hookimpl


@hookimpl
def pytask_parametrize_kwarg_to_marker(obj, kwargs):
def pytask_parametrize_kwarg_to_marker(obj: Any, kwargs: dict[Any, Any]) -> None:
"""Attach parametrized r arguments to the function with a marker."""
if callable(obj):
if "r" in kwargs:
pytask.mark.r(**kwargs.pop("r"))(obj)
if callable(obj) and "r" in kwargs:
pytask.mark.r(**kwargs.pop("r"))(obj)
3 changes: 2 additions & 1 deletion src/pytask_r/plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Register hook specifications and implementations."""
from __future__ import annotations

from pluggy import PluginManager
from pytask import hookimpl
from pytask_r import collect
from pytask_r import config
Expand All @@ -9,7 +10,7 @@


@hookimpl
def pytask_add_hooks(pm):
def pytask_add_hooks(pm: PluginManager) -> None:
"""Register hook implementations."""
pm.register(collect)
pm.register(config)
Expand Down
7 changes: 5 additions & 2 deletions src/pytask_r/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,17 @@ def create_file_name(task: Task, suffix: str) -> str:


def serialize_keyword_arguments(
serializer: str | Callable[dict[str, Any], str],
serializer: str | Callable[..., str],
path_to_serialized: Path,
kwargs: dict[str, Any],
) -> None:
"""Serialize keyword arguments."""
if callable(serializer):
serializer_func = serializer
elif isinstance(serializer, str) and serializer in SERIALIZERS:
serializer_func = SERIALIZERS[serializer]["serializer"]
serializer_func = SERIALIZERS[serializer][
"serializer"
] # type: ignore[assignment]
else:
raise ValueError(f"Serializer {serializer!r} is not known.")

Expand Down
12 changes: 3 additions & 9 deletions src/pytask_r/shared.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""This module contains shared functions."""
from __future__ import annotations

from pathlib import Path
from typing import Any
from typing import Callable
from typing import Iterable
from typing import Sequence
Expand Down Expand Up @@ -39,17 +41,9 @@ def r(
return script, options, serializer, suffix


def _to_list(scalar_or_iter):
def _to_list(scalar_or_iter: Any) -> list[Any]:
"""Convert scalars and iterables to list.

Parameters
----------
scalar_or_iter : str or list

Returns
-------
list

Examples
--------
>>> _to_list("a")
Expand Down
15 changes: 11 additions & 4 deletions tests/test_collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from pytask_r.serialization import SERIALIZERS


@pytest.mark.unit
@pytest.mark.unit()
@pytest.mark.parametrize(
"args, kwargs, expectation, expected",
("args", "kwargs", "expectation", "expected"),
[
(
(),
Expand Down Expand Up @@ -43,9 +43,16 @@ def test_r(args, kwargs, expectation, expected):
assert result == expected


@pytest.mark.unit
@pytest.mark.unit()
@pytest.mark.parametrize(
"mark, default_options, default_serializer, default_suffix, expectation, expected",
(
"mark",
"default_options",
"default_serializer",
"default_suffix",
"expectation",
"expected",
),
[
(
Mark("r", (), {"script": "script.r"}),
Expand Down
2 changes: 1 addition & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from pytask import main


@pytest.mark.end_to_end
@pytest.mark.end_to_end()
def test_marker_is_configured(tmp_path):
session = main({"paths": tmp_path})

Expand Down
Loading