diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c27b58a..1d95a9d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,7 +11,7 @@ jobs: run-tests: - name: Run tests for ${{ matrix.os }} on ${{ matrix.python-version }} and ${{ matrix.julia-version }} + name: Run tests for ${{ matrix.os }} on ${{ matrix.python-version }} runs-on: ${{ matrix.os }} strategy: @@ -19,7 +19,6 @@ jobs: matrix: os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] python-version: ['3.7', '3.8', '3.9'] - julia-version: ['main_version'] steps: - uses: actions/checkout@v2 @@ -39,7 +38,7 @@ jobs: run: tox -e pytest -- -m "unit or (not integration and not end_to_end)" --cov=./ --cov-report=xml -n auto - name: Upload coverage report for unit tests and doctests. - if: runner.os == 'Linux' && matrix.python-version == '3.8' && matrix.julia-version == 'main_version' + if: runner.os == 'Linux' && matrix.python-version == '3.8' shell: bash -l {0} run: bash <(curl -s https://codecov.io/bash) -F unit -c @@ -48,11 +47,11 @@ jobs: run: tox -e pytest -- -m end_to_end --cov=./ --cov-report=xml -n auto - name: Upload coverage reports of end-to-end tests. - if: runner.os == 'Linux' && matrix.python-version == '3.8' && matrix.julia-version == 'main_version' + if: runner.os == 'Linux' && matrix.python-version == '3.8' shell: bash -l {0} run: bash <(curl -s https://codecov.io/bash) -F end_to_end -c - name: Validate codecov.yml - if: runner.os == 'Linux' && matrix.python-version == '3.8' && matrix.julia-version == 'main_version' + if: runner.os == 'Linux' && matrix.python-version == '3.8' shell: bash -l {0} run: cat codecov.yml | curl --data-binary @- https://codecov.io/validate diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 865c7e0..d32ef30 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,6 @@ repos: - id: check-merge-conflict - id: check-vcs-permalinks - id: check-yaml - exclude: meta.yaml - id: debug-statements - id: end-of-file-fixer - id: fix-byte-order-marker @@ -33,7 +32,7 @@ repos: rev: v2.21.2 hooks: - id: pyupgrade - args: [--py36-plus] + args: [--py37-plus] - repo: https://github.com/asottile/reorder_python_imports rev: v2.5.0 hooks: diff --git a/CHANGES.rst b/CHANGES.rst index e094b1e..a767c76 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,7 +8,8 @@ them in reverse chronological order. Releases follow `semantic versioning `_. -0.0.1 - 202X-XX-XX +0.1.0 - 2022-xx-xx ------------------ -- :gh:`01` fixes ... +- :gh:`2` polishes the first release of pytask-julia. (Thanks to :ghuser:`hmgaudecker`, + :ghuser:`hildebrandecon`) diff --git a/LICENSE b/LICENSE index a745662..3fdd05c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,3 @@ - MIT License Copyright (c) 2022, Tobias Raabe et al. @@ -19,6 +18,3 @@ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - diff --git a/README.rst b/README.rst index 6c4dbda..2849e16 100644 --- a/README.rst +++ b/README.rst @@ -15,18 +15,18 @@ pytask-julia :alt: PyPI - License :target: https://pypi.org/project/pytask-julia -.. image:: https://img.shields.io/github/workflow/status/hmgaudecker/pytask-julia/main/main - :target: https://github.com/hmgaudecker/pytask-julia/actions?query=branch%3Amain +.. image:: https://img.shields.io/github/workflow/status/pytask-dev/pytask-julia/main/main + :target: https://github.com/pytask-dev/pytask-julia/actions?query=branch%3Amain .. image:: https://readthedocs.org/projects/pytask-julia/badge/?version=latest :target: https://pytask-julia.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status -.. image:: https://codecov.io/gh/hmgaudecker/pytask-julia/branch/main/graph/badge.svg - :target: https://codecov.io/gh/hmgaudecker/pytask-julia +.. image:: https://codecov.io/gh/pytask-dev/pytask-julia/branch/main/graph/badge.svg + :target: https://codecov.io/gh/pytask-dev/pytask-julia -.. image:: https://results.pre-commit.ci/badge/github/hmgaudecker/pytask-julia/main.svg - :target: https://results.pre-commit.ci/latest/github/hmgaudecker/pytask-julia/main +.. image:: https://results.pre-commit.ci/badge/github/pytask-dev/pytask-julia/main.svg + :target: https://results.pre-commit.ci/latest/github/pytask-dev/pytask-julia/main :alt: pre-commit.ci status .. image:: https://img.shields.io/badge/code%20style-black-000000.svg @@ -36,7 +36,8 @@ pytask-julia Installation ------------ -pytask-julia is available on `PyPI `_ and `Anaconda.org `_. Install it with +pytask-julia is available on `PyPI `_ and +`Anaconda.org `_. Install it with .. code-block:: console @@ -53,7 +54,13 @@ typing the following on the command line $ julia -h -If an error is shown instead of a help page, you can install Julia .... +If an error is shown instead of a help page, you can install Julia on Unix systems with + +.. code-block:: console + + $ conda install -c conda-forge julia + +or choose one of the installers on this `page `_. Usage @@ -77,8 +84,8 @@ Here is an example where you want to run ``script.julia``. def task_run_jl_script(): pass -Note that, you need to apply the ``@pytask.mark.julia`` marker so that pytask-julia handles the -task. +Note that, you need to apply the ``@pytask.mark.julia`` marker so that pytask-julia +handles the task. If you are wondering why the function body is empty, know that pytask-julia replaces the body with a predefined internal function. See the section on implementation details for @@ -88,8 +95,8 @@ more information. Multiple dependencies and products ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -What happens if a task has more dependencies? Using a list, the Julia script which should be -executed must be found in the first position of the list. +What happens if a task has more dependencies? Using a list, the Julia script which +should be executed must be found in the first position of the list. .. code-block:: python @@ -132,31 +139,65 @@ for a ``"source"`` key in the dictionary and, secondly, under the key ``0``. Command Line Arguments ~~~~~~~~~~~~~~~~~~~~~~ -The decorator can be used to pass command line arguments to ``julia``. See the -following example. +The decorator can be used to pass command line arguments to ``julia``. An important +detail is that you need to differentiate between options passed to the Julia executable +and arguments passed to the script. + +First, pass options to the executable, then, use ``"--"`` as a separator, and after that +arguments to the script. Provide all arguments in a tuple or a list as below. + +The following shows how to pass both with the decorator. .. code-block:: python - @pytask.mark.julia("value") + @pytask.mark.julia(("--threads", "2", "--", "value")) @pytask.mark.depends_on("script.jl") @pytask.mark.produces("out.csv") def task_run_jl_script(): pass +which executes the something similar to the following on the command line. + +.. code-block:: console + + $ julia --threads 2 -- value + And in your ``script.jl``, you can intercept the value with .. code-block:: Julia - FIXME FOR YOUR LANGUAGE - args <- commandArgs(trailingOnly=TRUE) - arg <- args[1] # holds ``"value"`` + arg = ARGS[1] # holds ``"value"`` + +If you pass only of of them, either options for the executable or arguments to the +script, you still need to include the separator. + +.. code-block:: python + + @pytask.mark.julia(("--verbose", "--")) # for options for the executable. + @pytask.mark.depends_on("script.jl") + def task_func(): + ... + + + @pytask.mark.julia(("--", "value")) # for arguments for the script. + @pytask.mark.depends_on("script.jl") + def task_func(): + ... + +The corresponding commands on the command line are + +.. code-block:: console + + $ julia --verbose -- script.jl + + $ julia -- script.jl value Parametrization ~~~~~~~~~~~~~~~ -You can also parametrize the execution of scripts, meaning executing multiple Julia scripts -as well as passing different command line arguments to the same Julia script. +You can also parametrize the execution of scripts, meaning executing multiple Julia +scripts as well as passing different command line arguments to the same Julia script. The following task executes two Julia scripts which produce different outputs. @@ -173,23 +214,25 @@ The following task executes two Julia scripts which produce different outputs. def task_execute_julia_script(): pass -And the R script includes something like +And the Julia script includes something like -.. code-block:: r +.. code-block:: julia - args <- commandArgs(trailingOnly=TRUE) - produces <- args[1] # holds the path + produces = ARGS[1] # holds the path -If you want to pass different command line arguments to the same Julia script, you have to -include the ``@pytask.mark.julia`` decorator in the parametrization just like with -``@pytask.mark.depends_on`` and ``@pytask.mark.produces``. +If you want to pass different command line arguments to the same Julia script, you +have to include the ``@pytask.mark.julia`` decorator in the parametrization just like +with ``@pytask.mark.depends_on`` and ``@pytask.mark.produces``. .. code-block:: python @pytask.mark.depends_on("script.jl") @pytask.mark.parametrize( "produces, julia", - [(BLD / "output_1.csv", "1"), (BLD / "output_2.csv", "2")], + [ + (BLD / "output_1.csv", ("--", "1")), + (BLD / "output_2.csv", ("--", "2")), + ], ) def task_execute_julia_script(): pass @@ -220,9 +263,9 @@ The plugin is a convenient wrapper around to which you can always resort to when the plugin does not deliver functionality you need. -It is not possible to enter a post-mortem debugger when an error happens in the Julia script -or enter the debugger when starting the script. If there exists a solution for that, -hints as well as contributions are highly appreciated. +It is not possible to enter a post-mortem debugger when an error happens in the Julia +script or enter the debugger when starting the script. If there exists a solution for +that, hints as well as contributions are highly appreciated. Changes diff --git a/environment.yml b/environment.yml index 88d11bd..a2b7531 100644 --- a/environment.yml +++ b/environment.yml @@ -5,13 +5,13 @@ channels: - nodefaults dependencies: - - julia - python - pip - setuptools_scm - toml # Package dependencies + - julia - pytask >=0.1 - pytask-parallel >=0.1 @@ -24,3 +24,6 @@ dependencies: - pytest-cov - pytest-xdist - tox-conda + + - pip: + - -e . diff --git a/setup.cfg b/setup.cfg index 453692b..f367652 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -name = pytask-julia +name = pytask_julia description = A Pytask plugin for Julia long_description = file: README.rst long_description_content_type = text/x-rst @@ -18,7 +18,6 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 project_urls = Changelog = https://github.com/pytask-dev/pytask-julia/blob/main/CHANGES.rst Documentation = https://github.com/pytask-dev/pytask-julia diff --git a/src/pytask_julia/collect.py b/src/pytask_julia/collect.py index 747b4c3..eb7468e 100644 --- a/src/pytask_julia/collect.py +++ b/src/pytask_julia/collect.py @@ -15,6 +15,10 @@ from _pytask.parametrize import _copy_func +_SEPARATOR = "--" +"""str: Separates options for the Julia executable and arguments to the file.""" + + def julia(options: Optional[Union[str, Iterable[str]]] = None): """Specify command line options for Julia. @@ -24,8 +28,9 @@ def julia(options: Optional[Union[str, Iterable[str]]] = None): One or multiple command line options passed to Julia. """ - options = [] if options is None else _to_list(options) + options = [_SEPARATOR] if options is None else _to_list(options) options = [str(i) for i in options] + return options @@ -57,11 +62,10 @@ def pytask_collect_task_teardown(session, task): """Perform some checks.""" if get_specific_markers_from_task(task, "julia"): source = _get_node_from_dictionary(task.depends_on, "source") - if isinstance(source, FilePathNode) and source.value.suffix not in [ - ".jl" - ]: + if isinstance(source, FilePathNode) and source.value.suffix not in [".jl"]: raise ValueError( - "The first dependency of a Julia task must be the script to be executed." + "The first dependency of a Julia task must be the script to be " + "executed." ) julia_function = _copy_func(run_jl_script) @@ -91,6 +95,24 @@ def _merge_all_markers(task): return mark +_ERROR_MSG_MISSING_SEPARATOR = f"""The inputs of the @pytask.mark.julia decorator do not +contain the separator to differentiate between options for the julia executable and +arguments to the script. This was passed to the decorator: + +{{}} + +Construct the inputs to the decorator should contain, first, options to the executable, +secondly, the separator - "{_SEPARATOR}" -, thirdly, arguments to the script. + +Here is an example: + +@pytask.mark.julia(("--threads", "1", "{_SEPARATOR}", "input.file")) + +Even if you do not need the left or the right side of the decorator, you must put the +separator at the correct position. +""" + + def _prepare_cmd_options(session, task, args): """Prepare the command line arguments to execute the do-file. @@ -101,7 +123,21 @@ def _prepare_cmd_options(session, task, args): source = _get_node_from_dictionary( task.depends_on, session.config["julia_source_key"] ) - return ["julia", source.path.as_posix(), *args] + + if _SEPARATOR not in args: + raise ValueError(_ERROR_MSG_MISSING_SEPARATOR.format(args)) + else: + idx_sep = args.index(_SEPARATOR) + executable_options = args[:idx_sep] + script_arguments = args[idx_sep + 1 :] + + return [ + "julia", + *executable_options, + _SEPARATOR, + source.path.as_posix(), + *script_arguments, + ] def _to_list(scalar_or_iter): diff --git a/src/pytask_julia/execute.py b/src/pytask_julia/execute.py index 9449a25..8a3bb38 100644 --- a/src/pytask_julia/execute.py +++ b/src/pytask_julia/execute.py @@ -11,5 +11,6 @@ def pytask_execute_task_setup(task): if get_specific_markers_from_task(task, "julia"): if shutil.which("julia") is None: raise RuntimeError( - "julia is needed to run Julia scripts, but it is not found on your PATH." + "julia is needed to run Julia scripts, but it is not found on your " + "PATH." ) diff --git a/tests/test_collect.py b/tests/test_collect.py index 38004f0..35b1e78 100644 --- a/tests/test_collect.py +++ b/tests/test_collect.py @@ -7,9 +7,9 @@ from pytask_julia.collect import _get_node_from_dictionary from pytask_julia.collect import _merge_all_markers from pytask_julia.collect import _prepare_cmd_options +from pytask_julia.collect import julia from pytask_julia.collect import pytask_collect_task from pytask_julia.collect import pytask_collect_task_teardown -from pytask_julia.collect import julia class DummyClass: @@ -24,7 +24,7 @@ def task_dummy(): @pytest.mark.parametrize( "julia_args, expected", [ - (None, []), + (None, ["--"]), ("--some-option", ["--some-option"]), (["--a", "--b"], ["--a", "--b"]), ], @@ -123,15 +123,20 @@ def test_get_node_from_dictionary(obj, key, expected): @pytest.mark.unit @pytest.mark.parametrize( - "args", + "args, expectation, expected", [ - [], - ["a"], - ["a", "b"], + (("--",), does_not_raise(), ["julia", "--", "script.jl"]), + ( + ("--verbose", "--"), + does_not_raise(), + ["julia", "--verbose", "--", "script.jl"], + ), + (("--", "seed"), does_not_raise(), ["julia", "--", "script.jl", "seed"]), + (("--verbose",), pytest.raises(ValueError, match="The inputs"), None), ], ) @pytest.mark.parametrize("julia_source_key", ["source", "script"]) -def test_prepare_cmd_options(args, julia_source_key): +def test_prepare_cmd_options(args, expectation, expected, julia_source_key): session = DummyClass() session.config = {"julia_source_key": julia_source_key} @@ -141,6 +146,6 @@ def test_prepare_cmd_options(args, julia_source_key): task.depends_on = {julia_source_key: node} task.name = "task" - result = _prepare_cmd_options(session, task, args) - - assert result == ["julia", "script.jl", *args] + with expectation: + result = _prepare_cmd_options(session, task, args) + assert result == expected diff --git a/tests/test_execute.py b/tests/test_execute.py index 81e60da..7841499 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -1,10 +1,10 @@ -import os import textwrap from contextlib import ExitStack as does_not_raise # noqa: N813 import pytest from _pytask.mark import Mark from conftest import needs_julia +from pytask import cli from pytask import main from pytask_julia.execute import pytask_execute_task_setup @@ -24,7 +24,9 @@ class DummyTask: def test_pytask_execute_task_setup(monkeypatch, found_julia, expectation): """Make sure that the task setup raises errors.""" # Act like julia is installed since we do not test this. - monkeypatch.setattr("pytask_julia.execute.shutil.which", lambda x: found_julia) + monkeypatch.setattr( + "pytask_julia.execute.shutil.which", lambda x: found_julia # noqa: U100 + ) task = DummyTask() task.markers = [Mark("julia", (), {})] @@ -38,18 +40,23 @@ def test_pytask_execute_task_setup(monkeypatch, found_julia, expectation): @pytest.mark.parametrize( "depends_on", [ - "'script.jl'", + "script.jl", {"source": "script.jl"}, {0: "script.jl"}, {"script": "script.jl"}, ], ) -def test_run_jl_script(tmp_path, depends_on): +def test_run_jl_script(runner, tmp_path, depends_on): + if isinstance(depends_on, str): + full_depends_on = "'" + (tmp_path / depends_on).as_posix() + "'" + else: + full_depends_on = {k: (tmp_path / v).as_posix() for k, v in depends_on.items()} + task_source = f""" import pytask @pytask.mark.julia - @pytask.mark.depends_on({depends_on}) + @pytask.mark.depends_on({full_depends_on}) @pytask.mark.produces("out.txt") def task_run_jl_script(): pass @@ -57,9 +64,8 @@ def task_run_jl_script(): """ tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(task_source)) - julia_script = """ - write("out.txt", "So, so you think you can tell heaven from hell?") - """ + out = tmp_path.joinpath("out.txt").as_posix() + julia_script = f'write("{out}", "So, so you think you can tell heaven from hell?")' tmp_path.joinpath("script.jl").write_text(textwrap.dedent(julia_script)) if ( @@ -67,12 +73,13 @@ def task_run_jl_script(): and "source" not in depends_on and 0 not in depends_on ): - tmp_path.joinpath("pytask.ini").write_text("[pytask]\njulia_source_key = script") + tmp_path.joinpath("pytask.ini").write_text( + "[pytask]\njulia_source_key = script" + ) - os.chdir(tmp_path) - session = main({"paths": tmp_path}) + result = runner.invoke(cli, [tmp_path.as_posix()]) - assert session.exit_code == 0 + assert result.exit_code == 0 assert tmp_path.joinpath("out.txt").exists() @@ -90,13 +97,14 @@ def task_run_jl_script(): """ tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(task_source)) - julia_script = """ - write("out.txt", "So, so you think you can tell heaven from hell?") - """ + out = tmp_path.joinpath("out.txt").as_posix() + julia_script = f'write("{out}", "So, so you think you can tell heaven from hell?")' tmp_path.joinpath("script.jl").write_text(textwrap.dedent(julia_script)) # Hide julia if available. - monkeypatch.setattr("pytask_julia.execute.shutil.which", lambda x: None) + monkeypatch.setattr( + "pytask_julia.execute.shutil.which", lambda x: None # noqa: U100 + ) session = main({"paths": tmp_path}) @@ -106,12 +114,11 @@ def task_run_jl_script(): @needs_julia @pytest.mark.end_to_end -def test_run_jl_script_w_wrong_cmd_option(tmp_path): - """Apparently, RScript simply discards wrong cmd options -- hopefully julia does better.""" +def test_run_jl_script_w_wrong_cmd_option(runner, tmp_path): task_source = """ import pytask - @pytask.mark.julia("--wrong-flag") + @pytask.mark.julia(("--wrong-flag", "--")) @pytask.mark.depends_on("script.jl") @pytask.mark.produces("out.txt") def task_run_jl_script(): @@ -120,12 +127,38 @@ def task_run_jl_script(): """ tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(task_source)) - julia_script = """ - write("out.txt", "So, so you think you can tell heaven from hell?") + out = tmp_path.joinpath("out.txt").as_posix() + julia_script = f'write("{out}", "So, so you think you can tell heaven from hell?")' + tmp_path.joinpath("script.jl").write_text(textwrap.dedent(julia_script)) + + result = runner.invoke(cli, [tmp_path.as_posix()]) + + assert result.exit_code == 1 + + +@needs_julia +@pytest.mark.end_to_end +@pytest.mark.parametrize("n_threads", [2, 3]) +def test_check_passing_cmd_line_options(runner, tmp_path, n_threads): + task_source = f""" + import pytask + + @pytask.mark.julia(("--threads", "{n_threads}", "--")) + @pytask.mark.depends_on("script.jl") + @pytask.mark.produces("out.txt") + def task_run_jl_script(): + pass + + """ + tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(task_source)) + + out = tmp_path.joinpath("out.txt").as_posix() + julia_script = f""" + write("{out}", "So, so you think you can tell heaven from hell?") + @assert Threads.nthreads() == {n_threads} """ tmp_path.joinpath("script.jl").write_text(textwrap.dedent(julia_script)) - os.chdir(tmp_path) - session = main({"paths": tmp_path}) + result = runner.invoke(cli, [tmp_path.as_posix()]) - assert session.exit_code == 0 + assert result.exit_code == 0 diff --git a/tests/test_parallel.py b/tests/test_parallel.py index b904b8b..df8c91d 100644 --- a/tests/test_parallel.py +++ b/tests/test_parallel.py @@ -42,15 +42,15 @@ def task_execute_julia(): """ tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(source)) - julia_script = """FIXME FOR YOUR LANGUAGE - Sys.sleep(2) - saveRDS(1, file=paste0(1, ".csv")) + julia_script = """ + sleep(4) + write("1.csv", "1") """ tmp_path.joinpath("script_1.jl").write_text(textwrap.dedent(julia_script)) - r_script = """ - Sys.sleep(2) - saveRDS(2, file=paste0(2, ".csv")) + julia_script = """ + sleep(4) + write("2.csv", "2") """ tmp_path.joinpath("script_2.jl").write_text(textwrap.dedent(julia_script)) @@ -88,19 +88,19 @@ def test_parallel_parametrization_over_source_file(runner, tmp_path): @pytask.mark.depends_on("script.jl") @pytask.mark.parametrize("produces, julia", [ - (SRC / "0.csv", (1, SRC / "0.csv")), (SRC / "1.csv", (1, SRC / "1.csv")) + (SRC / "0.csv", ("--", 1, SRC / "0.csv")), + (SRC / "1.csv", ("--", 1, SRC / "1.csv")), ]) def task_execute_julia_script(): pass """ tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(source)) - julia_script = """FIXME FOR YOUR LANGUAGE - Sys.sleep(2) - args <- commandArgs(trailingOnly=TRUE) - number <- args[1] - produces <- args[2] - saveRDS(number, file=produces) + julia_script = """ + number = ARGS[1] + produces = ARGS[2] + sleep(4) + write(produces, number) """ tmp_path.joinpath("script.jl").write_text(textwrap.dedent(julia_script)) diff --git a/tests/test_parametrize.py b/tests/test_parametrize.py index 3032882..1f11dc8 100644 --- a/tests/test_parametrize.py +++ b/tests/test_parametrize.py @@ -8,30 +8,28 @@ @needs_julia @pytest.mark.end_to_end -def test_parametrized_execution_of_r_script(tmp_path): +def test_parametrized_execution_of_jl_script(tmp_path): task_source = """ import pytask @pytask.mark.julia @pytask.mark.parametrize("depends_on, produces", [ - ("script_1.r", "0.txt"), - ("script_2.r", "1.txt"), + ("script_1.jl", "0.txt"), + ("script_2.jl", "1.txt"), ]) - def task_run_r_script(): + def task_run_jl_script(): pass """ tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(task_source)) for name, content, out in [ - ("script_1.r", "Cities breaking down on a camel's back", "0.txt"), - ("script_2.r", "They just have to go 'cause they don't know whack", "1.txt"), + ("script_1.jl", "Cities breaking down on a camel's back", "0.txt"), + ("script_2.jl", "They just have to go 'cause they don't know whack", "1.txt"), ]: - r_script = f""" - file_descr <- file("{out}") - writeLines(c("{content}"), file_descr) - close(file_descr) + julia_script = f""" + write("{out}", "{content}") """ - tmp_path.joinpath(name).write_text(textwrap.dedent(r_script)) + tmp_path.joinpath(name).write_text(textwrap.dedent(julia_script)) os.chdir(tmp_path) session = main({"paths": tmp_path}) @@ -43,33 +41,34 @@ def task_run_r_script(): @needs_julia @pytest.mark.end_to_end -def test_parametrize_r_options_and_product_paths(tmp_path): +def test_parametrize_jl_options_and_product_paths(tmp_path): task_source = """ import pytask from pathlib import Path SRC = Path(__file__).parent - @pytask.mark.depends_on("script.r") - @pytask.mark.parametrize("produces, r", [ - (SRC / "0.rds", (0, SRC / "0.rds")), (SRC / "1.rds", (1, SRC / "1.rds")) + @pytask.mark.depends_on("script.jl") + @pytask.mark.parametrize("produces, julia", [ + (SRC / "0.csv", ("--", 0, SRC / "0.csv")), + (SRC / "1.csv", ("--", 1, SRC / "1.csv")), ]) - def task_execute_r_script(): + def task_run_jl_script(): pass """ tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(task_source)) - r_script = """ - args <- commandArgs(trailingOnly=TRUE) - number <- args[1] - produces <- args[2] - saveRDS(number, file=produces) + julia_script = """ + number = ARGS[1] + produces = ARGS[2] + write(produces, number) """ - tmp_path.joinpath("script.r").write_text(textwrap.dedent(r_script)) + + tmp_path.joinpath("script.jl").write_text(textwrap.dedent(julia_script)) os.chdir(tmp_path) session = main({"paths": tmp_path}) assert session.exit_code == 0 - assert tmp_path.joinpath("0.rds").exists() - assert tmp_path.joinpath("1.rds").exists() + assert tmp_path.joinpath("0.csv").exists() + assert tmp_path.joinpath("1.csv").exists() diff --git a/tox.ini b/tox.ini index 04e5a67..970800d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = pytest, pre-commit +envlist = pytest skipsdist = True skip_missing_interpreters = True passenv = PY_IGNORE_IMPORTMISMATCH