Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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
9 changes: 4 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ 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:
fail-fast: false
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
Expand All @@ -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

Expand All @@ -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
3 changes: 1 addition & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down
5 changes: 3 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ them in reverse chronological order. Releases follow `semantic versioning
<https://anaconda.org/conda-forge/pytask-julia>`_.


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`)
4 changes: 0 additions & 4 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

MIT License

Copyright (c) 2022, Tobias Raabe et al.
Expand All @@ -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.



89 changes: 58 additions & 31 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -36,7 +36,8 @@ pytask-julia
Installation
------------

pytask-julia is available on `PyPI <https://pypi.org/project/pytask-julia>`_ and `Anaconda.org <https://anaconda.org/conda-forge/pytask-julia>`_. Install it with
pytask-julia is available on `PyPI <https://pypi.org/project/pytask-julia>`_ and
`Anaconda.org <https://anaconda.org/conda-forge/pytask-julia>`_. Install it with

.. code-block:: console

Expand All @@ -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 <https://julialang.org/downloads/>`_.


Usage
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -132,12 +139,18 @@ 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.

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():
Expand All @@ -147,16 +160,28 @@ 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

@python.mark.julia(("--verbose", "--")) # for options for the executable.
def task_func():
...


@python.mark.julia(("--", "value")) # for arguments for the script.
def task_func():
...


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.

Expand All @@ -173,23 +198,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
Expand Down Expand Up @@ -220,9 +247,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
Expand Down
5 changes: 4 additions & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ channels:
- nodefaults

dependencies:
- julia
- python
- pip
- setuptools_scm
- toml

# Package dependencies
- julia
- pytask >=0.1
- pytask-parallel >=0.1

Expand All @@ -24,3 +24,6 @@ dependencies:
- pytest-cov
- pytest-xdist
- tox-conda

- pip:
- -e .
3 changes: 1 addition & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
48 changes: 42 additions & 6 deletions src/pytask_julia/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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


Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 - '--' -, 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.

Expand All @@ -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):
Expand Down
3 changes: 2 additions & 1 deletion src/pytask_julia/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."
)
Loading