Skip to content

Commit 1112203

Browse files
authored
Deprecate the use of Builder in run_in_subprocess (#1094)
With the introduction of sub-pipelines, the implementation of `Pipeline` is now centered around the pipeline definitions (`PipelineConfig` classes and such), and `PipelineBuilder` only covers the straight chain pipeline. We updated `run_pipeline_in_subprocess` to support `PipeineConfig`, but with the introduction of `run_pipeilne_in_subinterpreter`, the implementation becomes simpler if we only support `PipelineConfig` in these helper function. This commit changes the type annotation of `run_pipeline_in_subprocess` so that `PipelineBuilder` is removed though it's still supported at runtime. We encourage users to explicitly convert to the config object.
1 parent 18cb88b commit 1112203

3 files changed

Lines changed: 132 additions & 116 deletions

File tree

src/spdl/pipeline/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
# pyre-strict
1010

11-
from ._build import build_pipeline
12-
from ._builder import PipelineBuilder, run_pipeline_in_subprocess
11+
from ._build import build_pipeline, run_pipeline_in_subprocess
12+
from ._builder import PipelineBuilder
1313
from ._common._misc import create_task
1414
from ._components import (
1515
AsyncQueue,

src/spdl/pipeline/_build.py

Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,28 @@
44
# This source code is licensed under the BSD-style license found in the
55
# LICENSE file in the root directory of this source tree.
66

7+
78
__all__ = [
89
"_build_pipeline",
910
"build_pipeline",
11+
"run_pipeline_in_subprocess",
1012
]
1113

1214
import logging
13-
from collections.abc import Callable
15+
import warnings
16+
from collections.abc import Callable, Iterable, Iterator, Sequence
1417
from concurrent.futures import ThreadPoolExecutor
15-
from typing import TypeVar
16-
17-
from spdl.pipeline._components import _build_pipeline_coro, AsyncQueue, TaskHook
18+
from functools import partial
19+
from typing import Any, Generic, TypeVar
20+
21+
from spdl.pipeline._components import (
22+
_build_pipeline_coro,
23+
_get_global_id,
24+
_set_global_id,
25+
AsyncQueue,
26+
TaskHook,
27+
)
28+
from spdl.pipeline._iter_utils import iterate_in_subprocess
1829
from spdl.pipeline.defs import PipelineConfig
1930

2031
from ._pipeline import Pipeline
@@ -184,3 +195,115 @@ def build_pipeline(
184195
task_hook_factory=task_hook_factory,
185196
stage_id=stage_id,
186197
)
198+
199+
200+
################################################################################
201+
# run in subprocess
202+
################################################################################
203+
204+
205+
class _Wrapper(Generic[U]):
206+
def __init__(
207+
self,
208+
config: PipelineConfig[U],
209+
num_threads: int,
210+
max_failures: int,
211+
report_stats_interval: float,
212+
queue_class: type[AsyncQueue] | None,
213+
task_hook_factory: Callable[[str], list[TaskHook]] | None = None,
214+
) -> None:
215+
self.config = config
216+
self.num_threads = num_threads
217+
self.max_failures = max_failures
218+
self.report_stats_interval = report_stats_interval
219+
self.queue_class = queue_class
220+
self.task_hook_factory = task_hook_factory
221+
222+
def __iter__(self) -> Iterator[U]:
223+
pipeline = build_pipeline(
224+
self.config,
225+
num_threads=self.num_threads,
226+
max_failures=self.max_failures,
227+
report_stats_interval=self.report_stats_interval,
228+
queue_class=self.queue_class,
229+
task_hook_factory=self.task_hook_factory,
230+
)
231+
with pipeline.auto_stop():
232+
yield from pipeline
233+
234+
235+
def _get_initializer(kwargs: Any) -> Sequence[Callable[[], None]]:
236+
initializer = [partial(_set_global_id, _get_global_id())]
237+
if "initializer" not in kwargs:
238+
return initializer
239+
240+
init_ = kwargs.pop("initializer")
241+
if not isinstance(init_, Sequence):
242+
initializer.append(init_)
243+
else:
244+
initializer.extend(init_)
245+
return initializer
246+
247+
248+
def run_pipeline_in_subprocess(
249+
config_or_builder: PipelineConfig[T],
250+
/,
251+
*,
252+
num_threads: int,
253+
max_failures: int = -1,
254+
report_stats_interval: float = -1,
255+
queue_class: type[AsyncQueue] | None = None,
256+
task_hook_factory: Callable[[str], list[TaskHook]] | None = None,
257+
**kwargs: Any,
258+
) -> Iterable[T]:
259+
"""Run the given Pipeline in a subprocess, and iterate on the result.
260+
261+
Args:
262+
config_or_builder: The definition of :py:class:`Pipeline`. Can be either a
263+
:py:class:`PipelineConfig` or :py:class:`PipelineBuilder`.
264+
265+
.. warning::
266+
267+
The support for :py:class:`PipelineBuilder` is deprecated, and will be removed in
268+
the future. Please call `get_config()` method and pass the config object.
269+
270+
num_threads,max_failures,report_stats_interval,queue_class,task_hook_factory:
271+
Passed to :py:func:`build_pipeline`.
272+
kwargs: Passed to :py:func:`iterate_in_subprocess`.
273+
274+
Yields:
275+
The results yielded from the pipeline.
276+
277+
.. seealso::
278+
279+
- :py:func:`iterate_in_subprocess` implements the logic for manipulating an iterable
280+
in a subprocess.
281+
- :ref:`parallelism-performance` for the context in which this function was created.
282+
"""
283+
if not isinstance(config_or_builder, PipelineConfig):
284+
warnings.warn(
285+
"Passing a `PipelineBuilder` object directly to `run_pipeline_in_subprocess` is "
286+
"now deprecated. Please call `get_config()` method and pass the config object.",
287+
stacklevel=2,
288+
)
289+
290+
config = (
291+
config_or_builder
292+
if isinstance(config_or_builder, PipelineConfig)
293+
else config_or_builder.get_config() # pyre-ignore[16]
294+
)
295+
296+
initializer = _get_initializer(kwargs)
297+
return iterate_in_subprocess(
298+
fn=partial(
299+
_Wrapper,
300+
config=config,
301+
num_threads=num_threads,
302+
max_failures=max_failures,
303+
report_stats_interval=report_stats_interval,
304+
queue_class=queue_class,
305+
task_hook_factory=task_hook_factory,
306+
),
307+
initializer=initializer,
308+
**kwargs,
309+
)

src/spdl/pipeline/_builder.py

Lines changed: 3 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,12 @@
77
# pyre-strict
88

99
import logging
10-
from collections.abc import AsyncIterable, Callable, Iterable, Iterator, Sequence
10+
from collections.abc import AsyncIterable, Callable, Iterable
1111
from concurrent.futures import Executor
12-
from functools import partial
13-
from typing import Any, Generic, TypeVar
12+
from typing import Generic, TypeVar
1413

1514
from spdl._internal import log_api_usage_once
16-
from spdl.pipeline._components import (
17-
_get_global_id,
18-
_set_global_id,
19-
AsyncQueue,
20-
TaskHook,
21-
)
22-
from spdl.pipeline._iter_utils import iterate_in_subprocess
15+
from spdl.pipeline._components import AsyncQueue, TaskHook
2316
from spdl.pipeline.defs import (
2417
_TPipeInputs,
2518
Aggregate,
@@ -38,7 +31,6 @@
3831

3932
__all__ = [
4033
"PipelineBuilder",
41-
"run_pipeline_in_subprocess",
4234
]
4335

4436
_LG: logging.Logger = logging.getLogger(__name__)
@@ -291,102 +283,3 @@ def build(
291283
task_hook_factory=task_hook_factory,
292284
stage_id=stage_id,
293285
)
294-
295-
296-
################################################################################
297-
# run in subprocess
298-
################################################################################
299-
300-
301-
class _Wrapper(Generic[U]):
302-
def __init__(
303-
self,
304-
config: PipelineConfig[U],
305-
num_threads: int,
306-
max_failures: int,
307-
report_stats_interval: float,
308-
queue_class: type[AsyncQueue] | None,
309-
task_hook_factory: Callable[[str], list[TaskHook]] | None = None,
310-
) -> None:
311-
self.config = config
312-
self.num_threads = num_threads
313-
self.max_failures = max_failures
314-
self.report_stats_interval = report_stats_interval
315-
self.queue_class = queue_class
316-
self.task_hook_factory = task_hook_factory
317-
318-
def __iter__(self) -> Iterator[U]:
319-
pipeline = build_pipeline(
320-
self.config,
321-
num_threads=self.num_threads,
322-
max_failures=self.max_failures,
323-
report_stats_interval=self.report_stats_interval,
324-
queue_class=self.queue_class,
325-
task_hook_factory=self.task_hook_factory,
326-
)
327-
with pipeline.auto_stop():
328-
yield from pipeline
329-
330-
331-
def _get_initializer(kwargs: Any) -> Sequence[Callable[[], None]]:
332-
initializer = [partial(_set_global_id, _get_global_id())]
333-
if "initializer" not in kwargs:
334-
return initializer
335-
336-
init_ = kwargs.pop("initializer")
337-
if not isinstance(init_, Sequence):
338-
initializer.append(init_)
339-
else:
340-
initializer.extend(init_)
341-
return initializer
342-
343-
344-
def run_pipeline_in_subprocess(
345-
config_or_builder: PipelineConfig[U] | PipelineBuilder[T, U],
346-
/,
347-
*,
348-
num_threads: int,
349-
max_failures: int = -1,
350-
report_stats_interval: float = -1,
351-
queue_class: type[AsyncQueue] | None = None,
352-
task_hook_factory: Callable[[str], list[TaskHook]] | None = None,
353-
**kwargs: Any,
354-
) -> Iterable[T]:
355-
"""Run the given Pipeline in a subprocess, and iterate on the result.
356-
357-
Args:
358-
config_or_builder: The definition of :py:class:`Pipeline`. Can be either a
359-
:py:class:`PipelineConfig` or :py:class:`PipelineBuilder`.
360-
num_threads,max_failures,report_stats_interval,queue_class,task_hook_factory:
361-
Passed to :py:func:`build_pipeline`.
362-
kwargs: Passed to :py:func:`iterate_in_subprocess`.
363-
364-
Yields:
365-
The results yielded from the pipeline.
366-
367-
.. seealso::
368-
369-
- :py:func:`iterate_in_subprocess` implements the logic for manipulating an iterable
370-
in a subprocess.
371-
- :ref:`parallelism-performance` for the context in which this function was created.
372-
"""
373-
config = (
374-
config_or_builder.get_config()
375-
if isinstance(config_or_builder, PipelineBuilder)
376-
else config_or_builder
377-
)
378-
379-
initializer = _get_initializer(kwargs)
380-
return iterate_in_subprocess(
381-
fn=partial(
382-
_Wrapper,
383-
config=config,
384-
num_threads=num_threads,
385-
max_failures=max_failures,
386-
report_stats_interval=report_stats_interval,
387-
queue_class=queue_class,
388-
task_hook_factory=task_hook_factory,
389-
),
390-
initializer=initializer,
391-
**kwargs, # pyre-ignore: [6]
392-
)

0 commit comments

Comments
 (0)