diff --git a/CHANGES.md b/CHANGES.md index e44d3e5..759817b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 diff --git a/README.md b/README.md index 7d90f84..0ffeef9 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/environment.yml b/environment.yml index 836ac2c..e08ca6f 100644 --- a/environment.yml +++ b/environment.yml @@ -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 diff --git a/setup.cfg b/setup.cfg index 615dce3..35ed3b7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 diff --git a/src/pytask_r/collect.py b/src/pytask_r/collect.py index 84d668e..97dea84 100644 --- a/src/pytask_r/collect.py +++ b/src/pytask_r/collect.py @@ -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 @@ -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 diff --git a/src/pytask_r/config.py b/src/pytask_r/config.py index faabb81..a135fbc 100644 --- a/src/pytask_r/config.py +++ b/src/pytask_r/config.py @@ -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.") diff --git a/src/pytask_r/serialization.py b/src/pytask_r/serialization.py index 9bb0499..a68b57f 100644 --- a/src/pytask_r/serialization.py +++ b/src/pytask_r/serialization.py @@ -12,7 +12,7 @@ _HIDDEN_FOLDER = ".pytask" -SERIALIZER = {"json": {"serializer": json.dumps, "suffix": ".json"}} +SERIALIZERS = {"json": {"serializer": json.dumps, "suffix": ".json"}} try: @@ -20,8 +20,8 @@ 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: @@ -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.") diff --git a/src/pytask_r/shared.py b/src/pytask_r/shared.py index 36026af..db9ccc8 100644 --- a/src/pytask_r/shared.py +++ b/src/pytask_r/shared.py @@ -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, @@ -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 diff --git a/tests/test_collect.py b/tests/test_collect.py index fa78c14..b9cd250 100644 --- a/tests/test_collect.py +++ b/tests/test_collect.py @@ -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, - ), ( (), { @@ -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"}), [], @@ -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"], }, ), ), diff --git a/tox.ini b/tox.ini index 706acb7..a3c2fa8 100644 --- a/tox.ini +++ b/tox.ini @@ -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.