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
7 changes: 6 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ chronological order. Releases follow [semantic versioning](https://semver.org/)
releases are available on [PyPI](https://pypi.org/project/pytask-r) and
[Anaconda.org](https://anaconda.org/conda-forge/pytask-r).

## 0.2.0 - 2022-xx-xx
## 0.3.0 - 2023-xx-xx

- {pull}`33` deprecates INI configurations and aligns the plugin with pytask v0.3.

## 0.2.0 - 2022-04-16

- {pull}`24` removes an unnecessary hook implementation.
- {pull}`26` implements the new interface for v0.2.

## 0.1.1 - 2022-02-08

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![image](https://img.shields.io/conda/vn/conda-forge/pytask-r.svg)](https://anaconda.org/conda-forge/pytask-r)
[![image](https://img.shields.io/conda/pn/conda-forge/pytask-r.svg)](https://anaconda.org/conda-forge/pytask-r)
[![PyPI - License](https://img.shields.io/pypi/l/pytask-r)](https://pypi.org/project/pytask-r)
[![image](https://img.shields.io/github/workflow/status/pytask-dev/pytask-r/main/main)](https://github.com/pytask-dev/pytask-r/actions?query=branch%3Amain)
[![image](https://img.shields.io/github/actions/workflow/status/pytask-dev/pytask-r/main.yml?branch=main)](https://github.com/pytask-dev/pytask-r/actions?query=branch%3Amain)
[![image](https://codecov.io/gh/pytask-dev/pytask-r/branch/main/graph/badge.svg)](https://codecov.io/gh/pytask-dev/pytask-r)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pytask-dev/pytask-r/main.svg)](https://results.pre-commit.ci/latest/github/pytask-dev/pytask-r/main)
[![image](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
Expand Down
4 changes: 2 additions & 2 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ dependencies:
- conda-verify

# Package dependencies
- pytask >=0.2
- pytask-parallel >=0.2
- pytask >=0.3
- pytask-parallel >=0.3

- r-base >4
- r-jsonlite
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ packages = find:
install_requires =
click
pybaum>=0.1.1
pytask>=0.2
pytask>=0.3
python_requires = >=3.7
include_package_data = True
package_dir = =src
Expand Down
6 changes: 3 additions & 3 deletions src/pytask_r/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pytask import produces
from pytask import remove_marks
from pytask import Task
from pytask_r.serialization import SERIALIZER
from pytask_r.serialization import SERIALIZERS
from pytask_r.shared import r


Expand Down Expand Up @@ -101,9 +101,9 @@ def _parse_r_mark(mark, default_options, default_serializer, default_suffix):

if (
isinstance(parsed_kwargs["serializer"], str)
and parsed_kwargs["serializer"] in SERIALIZER
and parsed_kwargs["serializer"] in SERIALIZERS
):
proposed_suffix = SERIALIZER[parsed_kwargs["serializer"]]["suffix"]
proposed_suffix = SERIALIZERS[parsed_kwargs["serializer"]]["suffix"]
else:
proposed_suffix = default_suffix
parsed_kwargs["suffix"] = suffix if suffix else proposed_suffix
Expand Down
22 changes: 12 additions & 10 deletions src/pytask_r/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,28 @@
from __future__ import annotations

from pytask import hookimpl
from pytask_r.serialization import SERIALIZERS


@hookimpl
def pytask_parse_config(config, config_from_file):
def pytask_parse_config(config):
"""Register the r marker."""
config["markers"]["r"] = "Tasks which are executed with Rscript."
config["r_serializer"] = config_from_file.get("r_serializer", "json")
config["r_suffix"] = config_from_file.get("r_suffix", "")
config["r_options"] = _parse_value_or_whitespace_option(
config_from_file.get("r_options")
)
config["r_serializer"] = config.get("r_serializer", "json")
if config["r_serializer"] not in SERIALIZERS:
raise ValueError(
f"'r_serializer' is {config['r_serializer']} and not one of "
f"{list(SERIALIZERS)}."
)
config["r_suffix"] = config.get("r_suffix", "")
config["r_options"] = _parse_value_or_whitespace_option(config.get("r_options"))


def _parse_value_or_whitespace_option(value: str | None) -> None | str | list[str]:
"""Parse option which can hold a single value or values separated by new lines."""
if value in ["none", "None", None, ""]:
if value is None:
return None
elif isinstance(value, list):
return list(map(str, value))
elif isinstance(value, str):
return [v.strip() for v in value.split(" ") if v.strip()]
else:
raise ValueError(f"Input {value!r} is neither a 'str' nor 'None'.")
raise ValueError(f"'r_options' is {value} and not a list.")
10 changes: 5 additions & 5 deletions src/pytask_r/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
_HIDDEN_FOLDER = ".pytask"


SERIALIZER = {"json": {"serializer": json.dumps, "suffix": ".json"}}
SERIALIZERS = {"json": {"serializer": json.dumps, "suffix": ".json"}}


try:
import yaml
except ImportError: # pragma: no cover
pass
else:
SERIALIZER["yaml"] = {"serializer": yaml.dump, "suffix": ".yaml"}
SERIALIZER["yml"] = {"serializer": yaml.dump, "suffix": ".yml"}
SERIALIZERS["yaml"] = {"serializer": yaml.dump, "suffix": ".yaml"}
SERIALIZERS["yml"] = {"serializer": yaml.dump, "suffix": ".yml"}


def create_path_to_serialized(task: Task, suffix: str) -> Path:
Expand Down Expand Up @@ -56,8 +56,8 @@ def serialize_keyword_arguments(
) -> None:
if callable(serializer):
serializer_func = serializer
elif isinstance(serializer, str) and serializer in SERIALIZER:
serializer_func = SERIALIZER[serializer]["serializer"]
elif isinstance(serializer, str) and serializer in SERIALIZERS:
serializer_func = SERIALIZERS[serializer]["serializer"]
else:
raise ValueError(f"Serializer {serializer!r} is not known.")

Expand Down
38 changes: 0 additions & 38 deletions src/pytask_r/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,7 @@
from typing import Sequence


_ERROR_MSG = """The old syntax for @pytask.mark.r was suddenly deprecated starting \
with pytask-r v0.2 to provide a better user experience. Thank you for your \
understanding!

It is recommended to upgrade to the new syntax, so you enjoy all the benefits of v0.2 \
of pytask and pytask-r which is among others access to 'depends_on' and 'produces', \
and other kwargs inside the R script.

You can find a manual here: \
https://github.com/pytask-dev/pytask-r/blob/v0.2.0/README.md

Upgrading can be as easy as rewriting your current task from

@pytask.mark.r(["--option", "path_to_dependency.txt"])
@pytask.mark.depends_on("script.R")
@pytask.mark.produces("out.csv")
def task_r():
...

to

@pytask.mark.r(script="script.r", options="--option")
@pytask.mark.depends_on("path_to_dependency.txt")
@pytask.mark.produces("out.csv")
def task_r():
...

You can also fix the version of pytask and pytask-r to <0.2, so you do not have to \
to upgrade. At the same time, you will not enjoy the improvements released with \
version v0.2 of pytask and pytask-r.

"""


def r(
*args,
script: str | Path = None,
options: str | Iterable[str] | None = None,
serializer: str | Callable[..., str] | str | None = None,
Expand Down Expand Up @@ -70,9 +35,6 @@ def r(
``".json"``.

"""
if args or script is None:
raise RuntimeError(_ERROR_MSG)

options = [] if options is None else list(map(str, _to_list(options)))
return script, options, serializer, suffix

Expand Down
42 changes: 2 additions & 40 deletions tests/test_collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,13 @@
from pytask import Mark
from pytask_r.collect import _parse_r_mark
from pytask_r.collect import r
from pytask_r.serialization import SERIALIZER
from pytask_r.serialization import SERIALIZERS


@pytest.mark.unit
@pytest.mark.parametrize(
"args, kwargs, expectation, expected",
[
((), {}, pytest.raises(RuntimeError, match="The old syntax"), None),
(
("-o"),
{"script": "script.r"},
pytest.raises(RuntimeError, match="The old syntax"),
None,
),
(
(),
{"options": ("-o")},
pytest.raises(RuntimeError, match="The old syntax"),
None,
),
(
(),
{
Expand Down Expand Up @@ -60,31 +47,6 @@ def test_r(args, kwargs, expectation, expected):
@pytest.mark.parametrize(
"mark, default_options, default_serializer, default_suffix, expectation, expected",
[
(
Mark("r", (), {}),
[],
None,
".json",
pytest.raises(RuntimeError, match="The old syntax for @pytask.mark.r"),
Mark(
"r",
(),
{
"script": None,
"options": [],
"serializer": None,
"suffix": ".json",
},
),
),
(
Mark("r", ("-o"), {}),
[],
None,
".json",
pytest.raises(RuntimeError, match="The old syntax for @pytask.mark.r"),
None,
),
(
Mark("r", (), {"script": "script.r"}),
[],
Expand Down Expand Up @@ -122,7 +84,7 @@ def test_r(args, kwargs, expectation, expected):
"script": "script.r",
"options": [],
"serializer": "json",
"suffix": SERIALIZER["json"]["suffix"],
"suffix": SERIALIZERS["json"]["suffix"],
},
),
),
Expand Down
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ basepython = python

[testenv:pytest]
conda_deps =
pytask >=0.1.0
pytask-parallel >=0.1.0
pytask >=0.3
pytask-parallel >=0.3
pybaum >=0.1.1

# Optional dependencies.
Expand Down