From 70384ff6689fc911b08764f77108d2fa4b6533ce Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sun, 13 Nov 2022 10:26:16 +0100 Subject: [PATCH 1/7] Remove code and tests for ini configs. --- environment.yml | 2 +- setup.cfg | 2 +- tests/test_config.py | 26 +++++++------------------- tox.ini | 10 ++-------- 4 files changed, 11 insertions(+), 29 deletions(-) diff --git a/environment.yml b/environment.yml index e6ab11d..934817c 100644 --- a/environment.yml +++ b/environment.yml @@ -16,7 +16,7 @@ dependencies: - conda-verify # Package dependencies - - pytask >=0.2.0 + - pytask >=0.3.0 - cloudpickle - loky - pybaum >=0.1.1 diff --git a/setup.cfg b/setup.cfg index a6484da..1152a9b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ install_requires = cloudpickle loky pybaum>=0.1.1 - pytask>=0.2 + pytask>=0.3 python_requires = >=3.7 include_package_data = True package_dir = =src diff --git a/tests/test_config.py b/tests/test_config.py index b399030..2779617 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -26,9 +26,6 @@ def test_interplay_between_debugging_and_parallel(tmp_path, pdb, n_workers, expe @pytest.mark.end_to_end -@pytest.mark.parametrize( - "config_file", ["pytask.ini", "tox.ini", "setup.cfg", "pyproject.toml"] -) @pytest.mark.parametrize( "configuration_option, value, exit_code", [ @@ -43,26 +40,17 @@ def test_interplay_between_debugging_and_parallel(tmp_path, pdb, n_workers, expe ], ) def test_reading_values_from_config_file( - tmp_path, capsys, config_file, configuration_option, value, exit_code + tmp_path, capsys, configuration_option, value, exit_code ): - if config_file == "pyproject.toml": - config = f""" - [tool.pytask.ini_options] - {configuration_option} = {value!r} - """ - else: - config = f""" - [pytask] - {configuration_option} = {value} - """ - tmp_path.joinpath(config_file).write_text(textwrap.dedent(config)) + config = f""" + [tool.pytask.ini_options] + {configuration_option} = {value!r} + """ + tmp_path.joinpath("pyproject.toml").write_text(textwrap.dedent(config)) session = main({"paths": tmp_path}) captured = capsys.readouterr() - if config_file == "pyproject.toml": - assert "WARNING" not in captured.out - else: - assert "WARNING" in captured.out + assert "WARNING" not in captured.out assert session.exit_code == exit_code if value == "auto": diff --git a/tox.ini b/tox.ini index d0e8914..8ac41cc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,16 +1,14 @@ [tox] envlist = pytest -skipsdist = True -skip_missing_interpreters = True [testenv] -basepython = python +usedevelop = true [testenv:pytest] conda_deps = cloudpickle loky - pytask >=0.1.0 + pytask >=0.3.0 pytest pytest-cov pytest-xdist @@ -21,10 +19,6 @@ commands = pip install --no-deps -e . pytest {posargs} -[doc8] -ignore = D002, D004 -max-line-length = 89 - [flake8] docstring-convention = numpy ignore = From 75c160d215e10dec3a5c4d73024460ae8005c1cb Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sat, 17 Dec 2022 23:41:21 +0100 Subject: [PATCH 2/7] Refactor more stuff. --- .pre-commit-config.yaml | 1 - README.md | 2 +- environment.yml | 2 +- src/pytask_parallel/backends.py | 43 +++++++++++++++++++++++++-------- src/pytask_parallel/build.py | 15 +++++------- src/pytask_parallel/config.py | 42 +------------------------------- src/pytask_parallel/execute.py | 21 +++++++--------- tests/test_config.py | 4 +-- 8 files changed, 52 insertions(+), 78 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8401336..6595cbe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -93,7 +93,6 @@ repos: types-setuptools ] pass_filenames: false - language_version: "3.9" - repo: https://github.com/mgedmin/check-manifest rev: "0.49" hooks: diff --git a/README.md b/README.md index 78159d5..298052b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![image](https://img.shields.io/conda/vn/conda-forge/pytask-parallel.svg)](https://anaconda.org/conda-forge/pytask-parallel) [![image](https://img.shields.io/conda/pn/conda-forge/pytask-parallel.svg)](https://anaconda.org/conda-forge/pytask-parallel) [![PyPI - License](https://img.shields.io/pypi/l/pytask-parallel)](https://pypi.org/project/pytask-parallel) -[![image](https://img.shields.io/github/workflow/status/pytask-dev/pytask-parallel/Continuous%20Integration%20Workflow/main)](https://github.com/pytask-dev/pytask-parallel/actions?query=branch%3Amain) +[![image](https://img.shields.io/github/actions/workflow/status/pytask-dev/pytask-parallel/main.yml?branch=main)](https://github.com/pytask-dev/pytask-parallel/actions?query=branch%3Amain) [![image](https://codecov.io/gh/pytask-dev/pytask-parallel/branch/main/graph/badge.svg)](https://codecov.io/gh/pytask-dev/pytask-parallel) [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pytask-dev/pytask-parallel/main.svg)](https://results.pre-commit.ci/latest/github/pytask-dev/pytask-parallel/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 934817c..dd1290c 100644 --- a/environment.yml +++ b/environment.yml @@ -16,7 +16,7 @@ dependencies: - conda-verify # Package dependencies - - pytask >=0.3.0 + - pytask >=0.3 - cloudpickle - loky - pybaum >=0.1.1 diff --git a/src/pytask_parallel/backends.py b/src/pytask_parallel/backends.py index 8828bef..4ebb9f4 100644 --- a/src/pytask_parallel/backends.py +++ b/src/pytask_parallel/backends.py @@ -3,19 +3,42 @@ from concurrent.futures import ProcessPoolExecutor from concurrent.futures import ThreadPoolExecutor +from enum import Enum -PARALLEL_BACKENDS = { - "processes": ProcessPoolExecutor, - "threads": ThreadPoolExecutor, -} - -PARALLEL_BACKENDS_DEFAULT = "processes" - try: from loky import get_reusable_executor + except ImportError: - pass + + class ParallelBackendsChoices(str, Enum): + PROCESSES = "processes" + THREADS = "threads" + + PARALLEL_BACKENDS_DEFAULT = ParallelBackendsChoices.PROCESSES + + PARALLEL_BACKENDS = { + ParallelBackendsChoices.PROCESSES: ProcessPoolExecutor, + ParallelBackendsChoices.THREADS: ThreadPoolExecutor, + } + else: - PARALLEL_BACKENDS["loky"] = get_reusable_executor - PARALLEL_BACKENDS_DEFAULT = "loky" + + class ParallelBackendsChoices(str, Enum): # type: ignore[no-redef] + PROCESSES = "processes" + THREADS = "threads" + LOKY = "loky" + + PARALLEL_BACKENDS_DEFAULT = ParallelBackendsChoices.PROCESSES + + PARALLEL_BACKENDS = { + ParallelBackendsChoices.PROCESSES: ProcessPoolExecutor, + ParallelBackendsChoices.THREADS: ThreadPoolExecutor, + ParallelBackendsChoices.LOKY: ( # type: ignore[attr-defined] + get_reusable_executor + ), + } + + PARALLEL_BACKENDS_DEFAULT = ( + ParallelBackendsChoices.LOKY # type: ignore[attr-defined] + ) diff --git a/src/pytask_parallel/build.py b/src/pytask_parallel/build.py index d16a674..76e80e0 100644 --- a/src/pytask_parallel/build.py +++ b/src/pytask_parallel/build.py @@ -3,8 +3,8 @@ import click from pytask import hookimpl -from pytask_parallel.backends import PARALLEL_BACKENDS from pytask_parallel.backends import PARALLEL_BACKENDS_DEFAULT +from pytask_parallel.backends import ParallelBackendsChoices @hookimpl @@ -15,19 +15,16 @@ def pytask_extend_command_line_interface(cli: click.Group) -> None: ["-n", "--n-workers"], help=( "Max. number of pytask_parallel tasks. Integer >= 1 or 'auto' which is " - "os.cpu_count() - 1. [default: 1 (no parallelization)]" + "os.cpu_count() - 1." ), metavar="[INTEGER|auto]", - default=None, + default=1, ), click.Option( ["--parallel-backend"], - type=click.Choice(PARALLEL_BACKENDS), - help=( - "Backend for the parallelization. " - f"[default: {PARALLEL_BACKENDS_DEFAULT}]" - ), - default=None, + type=click.Choice(ParallelBackendsChoices), + help="Backend for the parallelization.", + default=PARALLEL_BACKENDS_DEFAULT, ), ] cli.commands["build"].params.extend(additional_parameters) diff --git a/src/pytask_parallel/config.py b/src/pytask_parallel/config.py index a329900..c8fb282 100644 --- a/src/pytask_parallel/config.py +++ b/src/pytask_parallel/config.py @@ -3,61 +3,21 @@ import os from typing import Any -from typing import Callable from pytask import hookimpl -from pytask_parallel.backends import PARALLEL_BACKENDS_DEFAULT -from pytask_parallel.callbacks import n_workers_callback -from pytask_parallel.callbacks import parallel_backend_callback @hookimpl -def pytask_parse_config( - config: dict[str, Any], - config_from_cli: dict[str, Any], - config_from_file: dict[str, Any], -) -> None: +def pytask_parse_config(config: dict[str, Any]) -> None: """Parse the configuration.""" - config["n_workers"] = _get_first_non_none_value( - config_from_cli, - config_from_file, - key="n_workers", - default=1, - callback=n_workers_callback, - ) if config["n_workers"] == "auto": config["n_workers"] = max(os.cpu_count() - 1, 1) config["delay"] = 0.1 - config["parallel_backend"] = _get_first_non_none_value( - config_from_cli, - config_from_file, - key="parallel_backend", - default=PARALLEL_BACKENDS_DEFAULT, - callback=parallel_backend_callback, - ) - @hookimpl def pytask_post_parse(config: dict[str, Any]) -> None: """Disable parallelization if debugging is enabled.""" if config["pdb"] or config["trace"]: config["n_workers"] = 1 - - -def _get_first_non_none_value( - *configs: dict[str, Any], - key: str, - default: Any | None = None, - callback: Callable[..., Any] | None = None, -) -> Any: - """Get the first non-None value for a key from a list of dictionaries. - - This function allows to prioritize information from many configurations by changing - the order of the inputs while also providing a default. - - """ - callback = (lambda x: x) if callback is None else callback # noqa: E731 - processed_values = (callback(config.get(key)) for config in configs) - return next((value for value in processed_values if value is not None), default) diff --git a/src/pytask_parallel/execute.py b/src/pytask_parallel/execute.py index a614cc8..18196f5 100644 --- a/src/pytask_parallel/execute.py +++ b/src/pytask_parallel/execute.py @@ -19,30 +19,27 @@ from pytask import get_marks from pytask import hookimpl from pytask import Mark +from pytask import parse_warning_filter from pytask import remove_internal_traceback_frames_from_exc_info from pytask import Session from pytask import Task +from pytask import warning_record_to_str +from pytask import WarningReport from pytask_parallel.backends import PARALLEL_BACKENDS +from pytask_parallel.backends import ParallelBackendsChoices from rich.console import ConsoleOptions from rich.traceback import Traceback -# Can be removed if pinned to pytask >= 0.2.6. -try: - from pytask import parse_warning_filter - from pytask import warning_record_to_str - from pytask import WarningReport -except ImportError: - from _pytask.warnings import parse_warning_filter - from _pytask.warnings import warning_record_to_str - from _pytask.warnings_utils import WarningReport - @hookimpl def pytask_post_parse(config: dict[str, Any]) -> None: """Register the parallel backend.""" - if config["parallel_backend"] in ("loky", "processes"): + if config["parallel_backend"] in ( + ParallelBackendsChoices.LOKY, # type: ignore[attr-defined] + ParallelBackendsChoices.PROCESSES, + ): config["pm"].register(ProcessesNameSpace) - elif config["parallel_backend"] in ("threads",): + elif config["parallel_backend"] in (ParallelBackendsChoices.THREADS,): config["pm"].register(DefaultBackendNameSpace) diff --git a/tests/test_config.py b/tests/test_config.py index 2779617..dccf168 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -40,7 +40,7 @@ def test_interplay_between_debugging_and_parallel(tmp_path, pdb, n_workers, expe ], ) def test_reading_values_from_config_file( - tmp_path, capsys, configuration_option, value, exit_code + tmp_path, configuration_option, value, exit_code ): config = f""" [tool.pytask.ini_options] @@ -49,8 +49,6 @@ def test_reading_values_from_config_file( tmp_path.joinpath("pyproject.toml").write_text(textwrap.dedent(config)) session = main({"paths": tmp_path}) - captured = capsys.readouterr() - assert "WARNING" not in captured.out assert session.exit_code == exit_code if value == "auto": From f09f23fb1dababbf59dfeb311e66f4feaaefed77 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sun, 18 Dec 2022 14:45:33 +0100 Subject: [PATCH 3/7] Make it work with the config for pytask. --- src/pytask_parallel/backends.py | 20 ++++++++++---------- src/pytask_parallel/build.py | 5 +++-- src/pytask_parallel/config.py | 15 +++++++++++++++ src/pytask_parallel/execute.py | 11 ++++------- tests/test_config.py | 6 +++--- 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/pytask_parallel/backends.py b/src/pytask_parallel/backends.py index 4ebb9f4..4397224 100644 --- a/src/pytask_parallel/backends.py +++ b/src/pytask_parallel/backends.py @@ -11,34 +11,34 @@ except ImportError: - class ParallelBackendsChoices(str, Enum): + class ParallelBackendChoices(str, Enum): PROCESSES = "processes" THREADS = "threads" - PARALLEL_BACKENDS_DEFAULT = ParallelBackendsChoices.PROCESSES + PARALLEL_BACKENDS_DEFAULT = ParallelBackendChoices.PROCESSES PARALLEL_BACKENDS = { - ParallelBackendsChoices.PROCESSES: ProcessPoolExecutor, - ParallelBackendsChoices.THREADS: ThreadPoolExecutor, + ParallelBackendChoices.PROCESSES: ProcessPoolExecutor, + ParallelBackendChoices.THREADS: ThreadPoolExecutor, } else: - class ParallelBackendsChoices(str, Enum): # type: ignore[no-redef] + class ParallelBackendChoices(str, Enum): # type: ignore[no-redef] PROCESSES = "processes" THREADS = "threads" LOKY = "loky" - PARALLEL_BACKENDS_DEFAULT = ParallelBackendsChoices.PROCESSES + PARALLEL_BACKENDS_DEFAULT = ParallelBackendChoices.PROCESSES PARALLEL_BACKENDS = { - ParallelBackendsChoices.PROCESSES: ProcessPoolExecutor, - ParallelBackendsChoices.THREADS: ThreadPoolExecutor, - ParallelBackendsChoices.LOKY: ( # type: ignore[attr-defined] + ParallelBackendChoices.PROCESSES: ProcessPoolExecutor, + ParallelBackendChoices.THREADS: ThreadPoolExecutor, + ParallelBackendChoices.LOKY: ( # type: ignore[attr-defined] get_reusable_executor ), } PARALLEL_BACKENDS_DEFAULT = ( - ParallelBackendsChoices.LOKY # type: ignore[attr-defined] + ParallelBackendChoices.LOKY # type: ignore[attr-defined] ) diff --git a/src/pytask_parallel/build.py b/src/pytask_parallel/build.py index 76e80e0..9bf403a 100644 --- a/src/pytask_parallel/build.py +++ b/src/pytask_parallel/build.py @@ -2,9 +2,10 @@ from __future__ import annotations import click +from pytask import EnumChoice from pytask import hookimpl from pytask_parallel.backends import PARALLEL_BACKENDS_DEFAULT -from pytask_parallel.backends import ParallelBackendsChoices +from pytask_parallel.backends import ParallelBackendChoices @hookimpl @@ -22,7 +23,7 @@ def pytask_extend_command_line_interface(cli: click.Group) -> None: ), click.Option( ["--parallel-backend"], - type=click.Choice(ParallelBackendsChoices), + type=EnumChoice(ParallelBackendChoices), help="Backend for the parallelization.", default=PARALLEL_BACKENDS_DEFAULT, ), diff --git a/src/pytask_parallel/config.py b/src/pytask_parallel/config.py index c8fb282..6c4af23 100644 --- a/src/pytask_parallel/config.py +++ b/src/pytask_parallel/config.py @@ -1,10 +1,12 @@ """Configure pytask.""" from __future__ import annotations +import enum import os from typing import Any from pytask import hookimpl +from pytask_parallel.backends import ParallelBackendChoices @hookimpl @@ -13,6 +15,19 @@ def pytask_parse_config(config: dict[str, Any]) -> None: if config["n_workers"] == "auto": config["n_workers"] = max(os.cpu_count() - 1, 1) + if ( + isinstance(config["parallel_backend"], str) + and config["parallel_backend"] in ParallelBackendChoices._value2member_map_ + ): + config["parallel_backend"] = ParallelBackendChoices(config["parallel_backend"]) + elif ( + isinstance(config["parallel_backend"], enum.Enum) + and config["parallel_backend"] in ParallelBackendChoices + ): + pass + else: + raise ValueError("Invalid value for 'parallel_backend'.") + config["delay"] = 0.1 diff --git a/src/pytask_parallel/execute.py b/src/pytask_parallel/execute.py index 18196f5..d2579a0 100644 --- a/src/pytask_parallel/execute.py +++ b/src/pytask_parallel/execute.py @@ -26,7 +26,7 @@ from pytask import warning_record_to_str from pytask import WarningReport from pytask_parallel.backends import PARALLEL_BACKENDS -from pytask_parallel.backends import ParallelBackendsChoices +from pytask_parallel.backends import ParallelBackendChoices from rich.console import ConsoleOptions from rich.traceback import Traceback @@ -34,13 +34,10 @@ @hookimpl def pytask_post_parse(config: dict[str, Any]) -> None: """Register the parallel backend.""" - if config["parallel_backend"] in ( - ParallelBackendsChoices.LOKY, # type: ignore[attr-defined] - ParallelBackendsChoices.PROCESSES, - ): - config["pm"].register(ProcessesNameSpace) - elif config["parallel_backend"] in (ParallelBackendsChoices.THREADS,): + if config["parallel_backend"] == ParallelBackendChoices.THREADS: config["pm"].register(DefaultBackendNameSpace) + else: + config["pm"].register(ProcessesNameSpace) @hookimpl(tryfirst=True) diff --git a/tests/test_config.py b/tests/test_config.py index dccf168..95929a8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -6,7 +6,7 @@ import pytest from pytask import ExitCode from pytask import main -from pytask_parallel.backends import PARALLEL_BACKENDS +from pytask_parallel.backends import ParallelBackendChoices @pytest.mark.end_to_end @@ -35,8 +35,8 @@ def test_interplay_between_debugging_and_parallel(tmp_path, pdb, n_workers, expe ("parallel_backend", "unknown_backend", ExitCode.CONFIGURATION_FAILED), ] + [ - ("parallel_backend", parallel_backend, ExitCode.OK) - for parallel_backend in PARALLEL_BACKENDS + ("parallel_backend", parallel_backend.value, ExitCode.OK) + for parallel_backend in ParallelBackendChoices ], ) def test_reading_values_from_config_file( From 63bef3413d34a2b41aa98c2c819adae90a5fad45 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sun, 18 Dec 2022 15:33:03 +0100 Subject: [PATCH 4/7] Fix license. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 4c96cf3..f4c44ec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2020-2021 Tobias Raabe +Copyright 2020 Tobias Raabe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software From 0777141f939c668abe2cc15b41bb2954fb764694 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Fri, 6 Jan 2023 13:56:16 +0100 Subject: [PATCH 5/7] Make executor part of the configuration instead of an attribute of the session. --- src/pytask_parallel/execute.py | 6 +++--- tests/test_execute.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pytask_parallel/execute.py b/src/pytask_parallel/execute.py index d2579a0..65d422f 100644 --- a/src/pytask_parallel/execute.py +++ b/src/pytask_parallel/execute.py @@ -60,7 +60,7 @@ def pytask_execute_build(session: Session) -> bool | None: with parallel_backend(max_workers=session.config["n_workers"]) as executor: - session.executor = executor + session.config["_parallel_executor"] = executor sleeper = _Sleeper() while session.scheduler.is_active(): @@ -183,7 +183,7 @@ def pytask_execute_task(session: Session, task: Task) -> Future[Any] | None: bytes_function = cloudpickle.dumps(task) bytes_kwargs = cloudpickle.dumps(kwargs) - return session.executor.submit( + return session.config["_parallel_executor"].submit( _unserialize_and_execute_task, bytes_function=bytes_function, bytes_kwargs=bytes_kwargs, @@ -279,7 +279,7 @@ def pytask_execute_task(session: Session, task: Task) -> Future[Any] | None: """ if session.config["n_workers"] > 1: kwargs = _create_kwargs_for_task(task) - return session.executor.submit( + return session.config["_parallel_executor"].submit( _mock_processes_for_threads, func=task.execute, **kwargs ) else: diff --git a/tests/test_execute.py b/tests/test_execute.py index 958e6e4..1af4048 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -126,7 +126,7 @@ def myfunc(): with PARALLEL_BACKENDS[parallel_backend]( max_workers=session.config["n_workers"] ) as executor: - session.executor = executor + session.config["_parallel_executor"] = executor backend_name_space = { "processes": ProcessesNameSpace, From ffb17af98afbded9feadca3de08ced3cb062c586 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Mon, 9 Jan 2023 19:39:03 +0100 Subject: [PATCH 6/7] Publish types. --- MANIFEST.in | 2 ++ src/pytask_parallel/py.typed | 0 2 files changed, 2 insertions(+) create mode 100644 src/pytask_parallel/py.typed diff --git a/MANIFEST.in b/MANIFEST.in index de254c6..1eb48e3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,3 +7,5 @@ exclude tox.ini include README.md include LICENSE + +recursive-include src py.typed diff --git a/src/pytask_parallel/py.typed b/src/pytask_parallel/py.typed new file mode 100644 index 0000000..e69de29 From f47f7d183d684a3298a244b50fa47f3cfbbd35b2 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Mon, 9 Jan 2023 19:40:12 +0100 Subject: [PATCH 7/7] to changes. --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 1591ce8..4c4d790 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,10 @@ chronological order. Releases follow [semantic versioning](https://semver.org/) releases are available on [PyPI](https://pypi.org/project/pytask-parallel) and [Anaconda.org](https://anaconda.org/conda-forge/pytask-parallel). +## 0.3.0 - 2023-xx-xx + +- {pull}`50` deprecates INI configurations and aligns the package with pytask v0.3. + ## 0.2.1 - 2022-08-19 - {pull}`43` adds docformatter.