Skip to content

Commit c91d008

Browse files
authored
Deprecate INI configuration. (#50)
1 parent c22187b commit c91d008

15 files changed

+88
-113
lines changed

.pre-commit-config.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ repos:
9393
types-setuptools
9494
]
9595
pass_filenames: false
96-
language_version: "3.9"
9796
- repo: https://github.com/mgedmin/check-manifest
9897
rev: "0.49"
9998
hooks:

CHANGES.md

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

8+
## 0.3.0 - 2023-xx-xx
9+
10+
- {pull}`50` deprecates INI configurations and aligns the package with pytask v0.3.
11+
812
## 0.2.1 - 2022-08-19
913

1014
- {pull}`43` adds docformatter.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright 2020-2021 Tobias Raabe
1+
Copyright 2020 Tobias Raabe
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy of this
44
software and associated documentation files (the "Software"), to deal in the Software

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ exclude tox.ini
77

88
include README.md
99
include LICENSE
10+
11+
recursive-include src py.typed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![image](https://img.shields.io/conda/vn/conda-forge/pytask-parallel.svg)](https://anaconda.org/conda-forge/pytask-parallel)
66
[![image](https://img.shields.io/conda/pn/conda-forge/pytask-parallel.svg)](https://anaconda.org/conda-forge/pytask-parallel)
77
[![PyPI - License](https://img.shields.io/pypi/l/pytask-parallel)](https://pypi.org/project/pytask-parallel)
8-
[![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)
8+
[![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)
99
[![image](https://codecov.io/gh/pytask-dev/pytask-parallel/branch/main/graph/badge.svg)](https://codecov.io/gh/pytask-dev/pytask-parallel)
1010
[![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)
1111
[![image](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ dependencies:
1616
- conda-verify
1717

1818
# Package dependencies
19-
- pytask >=0.2.0
19+
- pytask >=0.3
2020
- cloudpickle
2121
- loky
2222
- pybaum >=0.1.1

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ install_requires =
2828
cloudpickle
2929
loky
3030
pybaum>=0.1.1
31-
pytask>=0.2
31+
pytask>=0.3
3232
python_requires = >=3.7
3333
include_package_data = True
3434
package_dir = =src

src/pytask_parallel/backends.py

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,42 @@
33

44
from concurrent.futures import ProcessPoolExecutor
55
from concurrent.futures import ThreadPoolExecutor
6+
from enum import Enum
67

78

8-
PARALLEL_BACKENDS = {
9-
"processes": ProcessPoolExecutor,
10-
"threads": ThreadPoolExecutor,
11-
}
12-
13-
PARALLEL_BACKENDS_DEFAULT = "processes"
14-
159
try:
1610
from loky import get_reusable_executor
11+
1712
except ImportError:
18-
pass
13+
14+
class ParallelBackendChoices(str, Enum):
15+
PROCESSES = "processes"
16+
THREADS = "threads"
17+
18+
PARALLEL_BACKENDS_DEFAULT = ParallelBackendChoices.PROCESSES
19+
20+
PARALLEL_BACKENDS = {
21+
ParallelBackendChoices.PROCESSES: ProcessPoolExecutor,
22+
ParallelBackendChoices.THREADS: ThreadPoolExecutor,
23+
}
24+
1925
else:
20-
PARALLEL_BACKENDS["loky"] = get_reusable_executor
21-
PARALLEL_BACKENDS_DEFAULT = "loky"
26+
27+
class ParallelBackendChoices(str, Enum): # type: ignore[no-redef]
28+
PROCESSES = "processes"
29+
THREADS = "threads"
30+
LOKY = "loky"
31+
32+
PARALLEL_BACKENDS_DEFAULT = ParallelBackendChoices.PROCESSES
33+
34+
PARALLEL_BACKENDS = {
35+
ParallelBackendChoices.PROCESSES: ProcessPoolExecutor,
36+
ParallelBackendChoices.THREADS: ThreadPoolExecutor,
37+
ParallelBackendChoices.LOKY: ( # type: ignore[attr-defined]
38+
get_reusable_executor
39+
),
40+
}
41+
42+
PARALLEL_BACKENDS_DEFAULT = (
43+
ParallelBackendChoices.LOKY # type: ignore[attr-defined]
44+
)

src/pytask_parallel/build.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
from __future__ import annotations
33

44
import click
5+
from pytask import EnumChoice
56
from pytask import hookimpl
6-
from pytask_parallel.backends import PARALLEL_BACKENDS
77
from pytask_parallel.backends import PARALLEL_BACKENDS_DEFAULT
8+
from pytask_parallel.backends import ParallelBackendChoices
89

910

1011
@hookimpl
@@ -15,19 +16,16 @@ def pytask_extend_command_line_interface(cli: click.Group) -> None:
1516
["-n", "--n-workers"],
1617
help=(
1718
"Max. number of pytask_parallel tasks. Integer >= 1 or 'auto' which is "
18-
"os.cpu_count() - 1. [default: 1 (no parallelization)]"
19+
"os.cpu_count() - 1."
1920
),
2021
metavar="[INTEGER|auto]",
21-
default=None,
22+
default=1,
2223
),
2324
click.Option(
2425
["--parallel-backend"],
25-
type=click.Choice(PARALLEL_BACKENDS),
26-
help=(
27-
"Backend for the parallelization. "
28-
f"[default: {PARALLEL_BACKENDS_DEFAULT}]"
29-
),
30-
default=None,
26+
type=EnumChoice(ParallelBackendChoices),
27+
help="Backend for the parallelization.",
28+
default=PARALLEL_BACKENDS_DEFAULT,
3129
),
3230
]
3331
cli.commands["build"].params.extend(additional_parameters)

src/pytask_parallel/config.py

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,38 @@
11
"""Configure pytask."""
22
from __future__ import annotations
33

4+
import enum
45
import os
56
from typing import Any
6-
from typing import Callable
77

88
from pytask import hookimpl
9-
from pytask_parallel.backends import PARALLEL_BACKENDS_DEFAULT
10-
from pytask_parallel.callbacks import n_workers_callback
11-
from pytask_parallel.callbacks import parallel_backend_callback
9+
from pytask_parallel.backends import ParallelBackendChoices
1210

1311

1412
@hookimpl
15-
def pytask_parse_config(
16-
config: dict[str, Any],
17-
config_from_cli: dict[str, Any],
18-
config_from_file: dict[str, Any],
19-
) -> None:
13+
def pytask_parse_config(config: dict[str, Any]) -> None:
2014
"""Parse the configuration."""
21-
config["n_workers"] = _get_first_non_none_value(
22-
config_from_cli,
23-
config_from_file,
24-
key="n_workers",
25-
default=1,
26-
callback=n_workers_callback,
27-
)
2815
if config["n_workers"] == "auto":
2916
config["n_workers"] = max(os.cpu_count() - 1, 1)
3017

31-
config["delay"] = 0.1
18+
if (
19+
isinstance(config["parallel_backend"], str)
20+
and config["parallel_backend"] in ParallelBackendChoices._value2member_map_
21+
):
22+
config["parallel_backend"] = ParallelBackendChoices(config["parallel_backend"])
23+
elif (
24+
isinstance(config["parallel_backend"], enum.Enum)
25+
and config["parallel_backend"] in ParallelBackendChoices
26+
):
27+
pass
28+
else:
29+
raise ValueError("Invalid value for 'parallel_backend'.")
3230

33-
config["parallel_backend"] = _get_first_non_none_value(
34-
config_from_cli,
35-
config_from_file,
36-
key="parallel_backend",
37-
default=PARALLEL_BACKENDS_DEFAULT,
38-
callback=parallel_backend_callback,
39-
)
31+
config["delay"] = 0.1
4032

4133

4234
@hookimpl
4335
def pytask_post_parse(config: dict[str, Any]) -> None:
4436
"""Disable parallelization if debugging is enabled."""
4537
if config["pdb"] or config["trace"]:
4638
config["n_workers"] = 1
47-
48-
49-
def _get_first_non_none_value(
50-
*configs: dict[str, Any],
51-
key: str,
52-
default: Any | None = None,
53-
callback: Callable[..., Any] | None = None,
54-
) -> Any:
55-
"""Get the first non-None value for a key from a list of dictionaries.
56-
57-
This function allows to prioritize information from many configurations by changing
58-
the order of the inputs while also providing a default.
59-
60-
"""
61-
callback = (lambda x: x) if callback is None else callback # noqa: E731
62-
processed_values = (callback(config.get(key)) for config in configs)
63-
return next((value for value in processed_values if value is not None), default)

src/pytask_parallel/execute.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,31 +19,25 @@
1919
from pytask import get_marks
2020
from pytask import hookimpl
2121
from pytask import Mark
22+
from pytask import parse_warning_filter
2223
from pytask import remove_internal_traceback_frames_from_exc_info
2324
from pytask import Session
2425
from pytask import Task
26+
from pytask import warning_record_to_str
27+
from pytask import WarningReport
2528
from pytask_parallel.backends import PARALLEL_BACKENDS
29+
from pytask_parallel.backends import ParallelBackendChoices
2630
from rich.console import ConsoleOptions
2731
from rich.traceback import Traceback
2832

29-
# Can be removed if pinned to pytask >= 0.2.6.
30-
try:
31-
from pytask import parse_warning_filter
32-
from pytask import warning_record_to_str
33-
from pytask import WarningReport
34-
except ImportError:
35-
from _pytask.warnings import parse_warning_filter
36-
from _pytask.warnings import warning_record_to_str
37-
from _pytask.warnings_utils import WarningReport
38-
3933

4034
@hookimpl
4135
def pytask_post_parse(config: dict[str, Any]) -> None:
4236
"""Register the parallel backend."""
43-
if config["parallel_backend"] in ("loky", "processes"):
44-
config["pm"].register(ProcessesNameSpace)
45-
elif config["parallel_backend"] in ("threads",):
37+
if config["parallel_backend"] == ParallelBackendChoices.THREADS:
4638
config["pm"].register(DefaultBackendNameSpace)
39+
else:
40+
config["pm"].register(ProcessesNameSpace)
4741

4842

4943
@hookimpl(tryfirst=True)
@@ -66,7 +60,7 @@ def pytask_execute_build(session: Session) -> bool | None:
6660

6761
with parallel_backend(max_workers=session.config["n_workers"]) as executor:
6862

69-
session.executor = executor
63+
session.config["_parallel_executor"] = executor
7064
sleeper = _Sleeper()
7165

7266
while session.scheduler.is_active():
@@ -189,7 +183,7 @@ def pytask_execute_task(session: Session, task: Task) -> Future[Any] | None:
189183
bytes_function = cloudpickle.dumps(task)
190184
bytes_kwargs = cloudpickle.dumps(kwargs)
191185

192-
return session.executor.submit(
186+
return session.config["_parallel_executor"].submit(
193187
_unserialize_and_execute_task,
194188
bytes_function=bytes_function,
195189
bytes_kwargs=bytes_kwargs,
@@ -285,7 +279,7 @@ def pytask_execute_task(session: Session, task: Task) -> Future[Any] | None:
285279
"""
286280
if session.config["n_workers"] > 1:
287281
kwargs = _create_kwargs_for_task(task)
288-
return session.executor.submit(
282+
return session.config["_parallel_executor"].submit(
289283
_mock_processes_for_threads, func=task.execute, **kwargs
290284
)
291285
else:

src/pytask_parallel/py.typed

Whitespace-only changes.

tests/test_config.py

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pytest
77
from pytask import ExitCode
88
from pytask import main
9-
from pytask_parallel.backends import PARALLEL_BACKENDS
9+
from pytask_parallel.backends import ParallelBackendChoices
1010

1111

1212
@pytest.mark.end_to_end
@@ -26,9 +26,6 @@ def test_interplay_between_debugging_and_parallel(tmp_path, pdb, n_workers, expe
2626

2727

2828
@pytest.mark.end_to_end
29-
@pytest.mark.parametrize(
30-
"config_file", ["pytask.ini", "tox.ini", "setup.cfg", "pyproject.toml"]
31-
)
3229
@pytest.mark.parametrize(
3330
"configuration_option, value, exit_code",
3431
[
@@ -38,31 +35,20 @@ def test_interplay_between_debugging_and_parallel(tmp_path, pdb, n_workers, expe
3835
("parallel_backend", "unknown_backend", ExitCode.CONFIGURATION_FAILED),
3936
]
4037
+ [
41-
("parallel_backend", parallel_backend, ExitCode.OK)
42-
for parallel_backend in PARALLEL_BACKENDS
38+
("parallel_backend", parallel_backend.value, ExitCode.OK)
39+
for parallel_backend in ParallelBackendChoices
4340
],
4441
)
4542
def test_reading_values_from_config_file(
46-
tmp_path, capsys, config_file, configuration_option, value, exit_code
43+
tmp_path, configuration_option, value, exit_code
4744
):
48-
if config_file == "pyproject.toml":
49-
config = f"""
50-
[tool.pytask.ini_options]
51-
{configuration_option} = {value!r}
52-
"""
53-
else:
54-
config = f"""
55-
[pytask]
56-
{configuration_option} = {value}
57-
"""
58-
tmp_path.joinpath(config_file).write_text(textwrap.dedent(config))
45+
config = f"""
46+
[tool.pytask.ini_options]
47+
{configuration_option} = {value!r}
48+
"""
49+
tmp_path.joinpath("pyproject.toml").write_text(textwrap.dedent(config))
5950

6051
session = main({"paths": tmp_path})
61-
captured = capsys.readouterr()
62-
if config_file == "pyproject.toml":
63-
assert "WARNING" not in captured.out
64-
else:
65-
assert "WARNING" in captured.out
6652

6753
assert session.exit_code == exit_code
6854
if value == "auto":

tests/test_execute.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def myfunc():
126126
with PARALLEL_BACKENDS[parallel_backend](
127127
max_workers=session.config["n_workers"]
128128
) as executor:
129-
session.executor = executor
129+
session.config["_parallel_executor"] = executor
130130

131131
backend_name_space = {
132132
"processes": ProcessesNameSpace,

tox.ini

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
[tox]
22
envlist = pytest
3-
skipsdist = True
4-
skip_missing_interpreters = True
53

64
[testenv]
7-
basepython = python
5+
usedevelop = true
86

97
[testenv:pytest]
108
conda_deps =
119
cloudpickle
1210
loky
13-
pytask >=0.1.0
11+
pytask >=0.3.0
1412
pytest
1513
pytest-cov
1614
pytest-xdist
@@ -21,10 +19,6 @@ commands =
2119
pip install --no-deps -e .
2220
pytest {posargs}
2321

24-
[doc8]
25-
ignore = D002, D004
26-
max-line-length = 89
27-
2822
[flake8]
2923
docstring-convention = numpy
3024
ignore =

0 commit comments

Comments
 (0)