Skip to content

Commit f168e8e

Browse files
authored
Fix #45. (#47)
1 parent 91845ce commit f168e8e

File tree

10 files changed

+125
-77
lines changed

10 files changed

+125
-77
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848

4949
- name: Run unit tests and doctests.
5050
shell: bash -l {0}
51-
run: tox -e pytest -- -m "unit or (not integration and not end_to_end)" --cov=./ --cov-report=xml -n auto
51+
run: tox -e test -- -m "unit or (not integration and not end_to_end)" --cov=./ --cov-report=xml -n auto
5252

5353
- name: Upload coverage report for unit tests and doctests.
5454
if: runner.os == 'Linux' && matrix.python-version == '3.10'
@@ -57,7 +57,7 @@ jobs:
5757

5858
- name: Run end-to-end tests.
5959
shell: bash -l {0}
60-
run: tox -e pytest -- -m end_to_end --cov=./ --cov-report=xml -n auto
60+
run: tox -e test -- -m end_to_end --cov=./ --cov-report=xml -n auto
6161

6262
- name: Upload coverage reports of end-to-end tests.
6363
if: runner.os == 'Linux' && matrix.python-version == '3.10'

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ repos:
4646
additional_dependencies: [
4747
attrs>=21.3.0,
4848
click,
49-
pytask>=0.4.0,
49+
pytask>=0.4.5,
5050
types-PyYAML,
5151
types-setuptools
5252
]

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ chronological order. Releases follow [semantic versioning](https://semver.org/)
55
releases are available on [PyPI](https://pypi.org/project/pytask-r) and
66
[Anaconda.org](https://anaconda.org/conda-forge/pytask-r).
77

8-
## 0.4.1 - 2024-xx-xx
8+
## 0.4.1 - 2024-04-20
99

1010
- {pull}`46` modernizes the package.
11+
- {pull}`47` fixes couple of issues raised in {issue}`45`.
1112

1213
## 0.4.0 - 2023-10-08
1314

README.md

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,30 @@ You also need to have R installed and `Rscript` on your command line. Test it by
3131
the following on the command line
3232

3333
```console
34-
$ Rscript --help
34+
Rscript --help
3535
```
3636

37-
If an error is shown instead of a help page, you can install R with `conda` by choosing
38-
either R or Microsoft R Open (MRO). Choose one of the two following commands. (See
39-
[here](https://docs.anaconda.com/anaconda/user-guide/tasks/%20using-r-language) for
40-
further explanation on Anaconda, R, and MRO.)
37+
If an error is shown instead of a help page, you can install R with `conda`.
4138

4239
```console
43-
$ conda install -c r r-base # For normal R.
44-
$ conda install -c r mro-base # For MRO.
40+
conda install -c conda-forge r-base
4541
```
4642

4743
Or install install R from the official [R Project](https://www.r-project.org/).
4844

4945
## Usage
5046

51-
To create a task which runs a R script, define a task function with the `@pytask.mark.r`
52-
decorator. The `script` keyword provides an absolute path or path relative to the task
53-
module to the R script.
47+
To create a task that runs an R script, define a task function with the `@mark.r`
48+
decorator. The `script` keyword provides an absolute path or a path relative to the task
49+
module.
5450

5551
```python
56-
import pytask
52+
from pathlib import Path
53+
from pytask import mark
5754

5855

59-
@pytask.mark.r(script="script.r")
60-
@pytask.mark.produces("out.rds")
61-
def task_run_r_script():
56+
@mark.r(script=Path("script.r"))
57+
def task_run_r_script(produces: Path = Path("out.rds")):
6258
pass
6359
```
6460

@@ -68,10 +64,9 @@ more information.
6864

6965
### Dependencies and Products
7066

71-
Dependencies and products can be added as with a normal pytask task using the
72-
`@pytask.mark.depends_on` and `@pytask.mark.produces` decorators. which is explained in
73-
this
74-
[tutorial](https://pytask-dev.readthedocs.io/en/stable/tutorials/defining_dependencies_products.html).
67+
Dependencies and products can be added as usual. See this
68+
[tutorial](https://pytask-dev.readthedocs.io/en/stable/tutorials/defining_dependencies_products.html)
69+
for some help.
7570

7671
### Accessing dependencies and products in the script
7772

@@ -99,10 +94,13 @@ To parse the JSON file, you need to install
9994
You can also pass any other information to your script by using the `@task` decorator.
10095

10196
```python
97+
from pathlib import Path
98+
from pytask import mark, task
99+
100+
102101
@task(kwargs={"number": 1})
103-
@pytask.mark.r(script="script.r")
104-
@pytask.mark.produces("out.rds")
105-
def task_run_r_script():
102+
@mark.r(script=Path("script.r"))
103+
def task_run_r_script(produces: Path = Path("out.rds")):
106104
pass
107105
```
108106

@@ -115,11 +113,11 @@ config$number # Is 1.
115113
### Debugging
116114

117115
In case a task throws an error, you might want to execute the script independently from
118-
pytask. After a failed execution, you see the command which executed the R script in the
116+
pytask. After a failed execution, you see the command that executed the R script in the
119117
report of the task. It looks roughly like this
120118

121119
```console
122-
$ Rscript <options> script.r <path-to>/.pytask/task_py_task_example.json
120+
Rscript <options> script.r <path-to>/.pytask/task_py_task_example.json
123121
```
124122

125123
### Command Line Arguments
@@ -128,9 +126,8 @@ The decorator can be used to pass command line arguments to `Rscript`. See the f
128126
example.
129127

130128
```python
131-
@pytask.mark.r(script="script.r", options="--vanilla")
132-
@pytask.mark.produces("out.rds")
133-
def task_run_r_script():
129+
@mark.r(script=Path("script.r"), options="--vanilla")
130+
def task_run_r_script(produces: Path = Path("out.rds")):
134131
pass
135132
```
136133

@@ -146,9 +143,8 @@ different outputs.
146143
for i in range(2):
147144

148145
@task
149-
@pytask.mark.r(script=f"script_{i}.r")
150-
@pytask.mark.produces(f"out_{i}.csv")
151-
def task_execute_r_script():
146+
@mark.r(script=Path(f"script_{i}.r"))
147+
def task_execute_r_script(produces: Path = Path(f"out_{i}.csv")):
152148
pass
153149
```
154150

@@ -159,9 +155,8 @@ If you want to pass different inputs to the same R script, pass these arguments
159155
for i in range(2):
160156

161157
@task(kwargs={"i": i})
162-
@pytask.mark.r(script="script.r")
163-
@pytask.mark.produces(f"output_{i}.csv")
164-
def task_execute_r_script():
158+
@mark.r(script=Path("script.r"))
159+
def task_execute_r_script(produces: Path = Path(f"output_{i}.csv")):
165160
pass
166161
```
167162

@@ -189,11 +184,11 @@ supports YAML (if PyYaml is installed).
189184
Use the `serializer` keyword arguments of the `@pytask.mark.r` decorator with
190185

191186
```python
192-
@pytask.mark.r(script="script.r", serializer="yaml")
187+
@mark.r(script=Path("script.r"), serializer="yaml")
193188
def task_example(): ...
194189
```
195190

196-
And in your R script use
191+
And, in your R script use
197192

198193
```r
199194
library(yaml)
@@ -203,22 +198,22 @@ config <- read_yaml(args[length(args)])
203198

204199
Note that the `YAML` package needs to be installed.
205200

206-
If you need a custom serializer, you can also provide any callable to `serializer` which
207-
transforms data to a string. Use `suffix` to set the correct file ending.
201+
If you need a custom serializer, you can also provide any callable `serializer` which
202+
transforms data into a string. Use `suffix` to set the correct file ending.
208203

209204
Here is a replication of the JSON example.
210205

211206
```python
212207
import json
213208

214209

215-
@pytask.mark.r(script="script.r", serializer=json.dumps, suffix=".json")
210+
@mark.r(script=Path("script.r"), serializer=json.dumps, suffix=".json")
216211
def task_example(): ...
217212
```
218213

219214
### Configuration
220215

221-
You can influence the default behavior of pytask-r with some configuration values.
216+
You can influence the default behavior of pytask-r with configuration values.
222217

223218
**`r_serializer`**
224219

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ classifiers = [
1010
"Programming Language :: R",
1111
]
1212
requires-python = ">=3.8"
13-
dependencies = ["click", "pluggy>=1.0.0", "pytask>=0.4.0"]
13+
dependencies = ["click", "pluggy>=1.0.0", "pytask>=0.4.5"]
1414
dynamic = ["version"]
1515

1616
[[project.authors]]

src/pytask_r/collect.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ def pytask_collect_task(
6666
default_serializer=session.config["r_serializer"],
6767
default_suffix=session.config["r_suffix"],
6868
)
69-
script, options, _, suffix = r(**marks[0].kwargs)
69+
script, options, _, suffix = r(**mark.kwargs)
7070

7171
obj.pytask_meta.markers.append(mark)
7272

73-
# Collect the nodes in @pytask.mark.julia and validate them.
73+
# Collect the nodes in @pytask.mark.r and validate them.
7474
path_nodes = Path.cwd() if path is None else path.parent
7575

7676
if isinstance(script, str):
@@ -93,10 +93,13 @@ def pytask_collect_task(
9393
),
9494
)
9595

96-
if not (isinstance(script_node, PathNode) and script_node.path.suffix == ".r"):
96+
if not (
97+
isinstance(script_node, PathNode)
98+
and script_node.path.suffix in (".r", ".R")
99+
):
97100
msg = (
98101
"The 'script' keyword of the @pytask.mark.r decorator must point "
99-
f"to Julia file with the .r suffix, but it is {script_node}."
102+
f"to an R file with the .r or .R extension, but it is {script_node}."
100103
)
101104
raise ValueError(msg)
102105

@@ -169,7 +172,7 @@ def _parse_r_mark(
169172
default_serializer: str,
170173
default_suffix: str,
171174
) -> Mark:
172-
"""Parse a Julia mark."""
175+
"""Parse an R mark."""
173176
script, options, serializer, suffix = r(**mark.kwargs)
174177

175178
parsed_kwargs = {}

src/pytask_r/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def pytask_parse_config(config: dict[str, Any]) -> None:
2020
f"{list(SERIALIZERS)}."
2121
)
2222
raise ValueError(msg)
23-
config["r_suffix"] = config.get("r_suffix", "")
23+
config["r_suffix"] = config.get("r_suffix", ".json")
2424
config["r_options"] = _parse_value_or_whitespace_option(config.get("r_options"))
2525

2626

src/pytask_r/execute.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ def collect_keyword_arguments(task: PTask) -> dict[str, Any]:
4444
# Remove all kwargs from the task so that they are not passed to the function.
4545
kwargs: dict[str, Any] = {
4646
**tree_map( # type: ignore[dict-item]
47-
lambda x: str(x.path) if isinstance(x, PPathNode) else str(x.value),
47+
lambda x: str(x.path) if isinstance(x, PPathNode) else x.value,
4848
task.depends_on,
4949
),
5050
**tree_map( # type: ignore[dict-item]
51-
lambda x: str(x.path) if isinstance(x, PPathNode) else str(x.value),
51+
lambda x: str(x.path) if isinstance(x, PPathNode) else x.value,
5252
task.produces,
5353
),
5454
}

0 commit comments

Comments
 (0)