Skip to content

Commit 9f7aa2b

Browse files
committed
connecting workflow logic to new api syntax
1 parent efa668a commit 9f7aa2b

30 files changed

+1382
-1466
lines changed

benchmark.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import asyncio
2+
import time
3+
4+
5+
def sync_function(x):
6+
return x * 2
7+
8+
9+
async def async_function(x):
10+
return x * 2
11+
12+
13+
def benchmark_sync():
14+
start_time = time.time()
15+
for _ in range(1000000):
16+
sync_function(10)
17+
end_time = time.time()
18+
return end_time - start_time
19+
20+
21+
async def benchmark_async():
22+
start_time = time.time()
23+
for _ in range(1000000):
24+
await async_function(10)
25+
end_time = time.time()
26+
return end_time - start_time
27+
28+
29+
def main():
30+
sync_time = benchmark_sync()
31+
print(f"Sync function time: {sync_time:.6f} seconds")
32+
33+
async_time = asyncio.run(benchmark_async())
34+
print(f"Async function time: {async_time:.6f} seconds")
35+
36+
37+
if __name__ == "__main__":
38+
main()

example.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import asyncio
2+
3+
4+
def is_coroutine_function(func):
5+
return asyncio.iscoroutinefunction(func)
6+
7+
8+
async def call_function(func, *args, **kwargs):
9+
if is_coroutine_function(func):
10+
return await func(*args, **kwargs)
11+
else:
12+
return func(*args, **kwargs)
13+
14+
15+
# Example usage
16+
async def async_function(x):
17+
await asyncio.sleep(1)
18+
return x * 2
19+
20+
21+
def sync_function(x):
22+
return x * 2
23+
24+
25+
async def main():
26+
result1 = await call_function(async_function, 10)
27+
result2 = await call_function(sync_function, 10)
28+
print(result1) # Output: 20
29+
print(result2) # Output: 20
30+
31+
32+
# To run the example
33+
if __name__ == "__main__":
34+
asyncio.run(main())

new-docs/source/index.rst

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,41 @@
33
Pydra
44
=====
55

6-
Pydra is a lightweight Python dataflow engine for scientific analysis.
7-
Although designed as a successor to Nipype_, Pydra is supports analytics in any domain.
8-
Pydra helps build reproducible, scalable, reusable workflows that link processing tasks
9-
implemented in Python or shell commands to be executed on distributed compute platforms.
10-
11-
The power of Pydra lies in ease of constructing workflows containing complex
12-
multiparameter map-reduce operations in Python code (see :ref:`Design philosophy` for
13-
the rationale behind its design).
6+
Pydra is a lightweight dataflow engine written in Python. Although designed to succeed
7+
Nipype_ in order to address the needs of the neuroimaging community, Pydra can be used
8+
for analytics in any scientific domain. Pydra facilitates the design of reproducible,
9+
scalable and robust workflows that can link diverse processing tasks implemented as
10+
shell commands or Python functions.
1411

1512
**Key features:**
1613

17-
* Combine diverse tasks (`Python functions <./tutorial/3-python.html>`__ or `shell commands <./tutorial/4-shell.html>`__) into coherent `workflows <./tutorial/5-workflow.html>`__
18-
* Concurrent execution on `choice of computing platform (e.g. workstation, SLURM, SGE, Dask, etc...) <./tutorial/2-advanced-execution.html#Workers>`__
14+
* Combine diverse tasks (`Python functions <./tutorial/3-python.html>`__ or `shell commands <./tutorial/4-shell.html>`__) into coherent, robust `workflows <./tutorial/5-workflow.html>`__
1915
* Dynamic workflow construction using Python code (see :ref:`Dynamic construction`)
16+
* Concurrent execution on `choice of computing platform (e.g. workstation, SLURM, SGE, Dask, etc...) <./tutorial/2-advanced-execution.html#Workers>`__
2017
* Map-reduce-like semantics (see :ref:`Splitting and combining`)
2118
* Global caching to reduce recomputation (see :ref:`Caches and hashes`)
2219
* Tasks can be executed in separate software environments, e.g. containers (see :ref:`Software environments`)
2320
* Strong type-checking, including file types, before execution (see :ref:`Typing and file-formats`)
2421

22+
See :ref:`Design philosophy` for more details on the rationale behind Pydra's design.
23+
2524

2625
Installation
2726
------------
2827

29-
Pydra itself is a pure-Python package, which has only a handful of dependencies,
30-
therefore, it is straightforward to install via pip for Python >= 3.11
28+
Pydra is implemented purely in Python and has a small number of dependencies
29+
It is easy to install via pip for Python >= 3.11 (preferably within a
30+
`virtual environment`_):
3131

3232
.. code-block:: bash
3333
3434
$ pip install pydra
3535
36-
Pre-designed tasks are available under the `pydra.tasks.*` package namespace. These tasks
37-
are implemented within separate packages that are typically specific to a given
38-
shell-command toolkit such as FSL_, AFNI_ or ANTs_, or a collection of related
39-
tasks/workflows (e.g. `niworkflows`_). Pip can be used to install these packages as well:
36+
Pre-designed tasks are available under the `pydra.tasks.*` namespace. These tasks
37+
are typically implemented within separate packages that are specific to a given
38+
shell-command toolkit, such as FSL_ (*pydra-fsl*), AFNI_ (*pydra-afni*) or
39+
ANTs_ (*pydra-ants*), or a collection of related tasks/workflows, such as Niworkflows
40+
(*pydra-niworkflows*). Pip can be used to install these extension packages as well:
4041

4142
.. code-block:: bash
4243
@@ -50,13 +51,15 @@ to run them (see :ref:`Software environments`).
5051
Tutorials and notebooks
5152
-----------------------
5253

53-
The following tutorials provide a step-by-step guide to using Pydra.
54-
They can be read in any order, but it is recommended to start with :ref:`Getting started`.
55-
The tutorials are implemented as Jupyter notebooks, which can be downloaded and run locally
54+
The following tutorials provide a step-by-step guide to using Pydra. They can be
55+
studied in any order, but it is recommended to start with :ref:`Getting started` and
56+
step through the list from there.
57+
58+
The tutorials are written in Jupyter notebooks, which can be downloaded and run locally
5659
or run online using the |Binder| button within each tutorial.
5760

5861
If you decide to download the notebooks and run locally, be sure to install the necessary
59-
dependencies with
62+
dependencies (ideally within a `virtual environment`_):
6063

6164
.. code-block:: bash
6265
@@ -74,7 +77,8 @@ Learn how to execute existing tasks (including workflows) on different systems
7477
Design
7578
~~~~~~
7679

77-
Learn how to design your own tasks
80+
Learn how to design your own tasks, wrapped shell commands or Python functions, or
81+
workflows,
7882

7983
* :ref:`Python-tasks`
8084
* :ref:`Shell-tasks`
@@ -171,5 +175,6 @@ See the full reference documentation for Pydra
171175
.. _AFNI: https://afni.nimh.nih.gov/
172176
.. _niworkflows: https://niworkflows.readthedocs.io/en/latest/
173177
.. _Nipype: https://nipype.readthedocs.io/en/latest/
178+
.. _virtual environment: https://docs.python.org/3/library/venv.html
174179
.. |Binder| image:: https://mybinder.org/badge_logo.svg
175180
:target: https://mybinder.org/v2/gh/nipype/pydra/develop

pydra/design/base.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
if ty.TYPE_CHECKING:
2929
from pydra.engine.specs import TaskDef, TaskOutputs
30-
from pydra.engine.core import Task
30+
3131

3232
__all__ = [
3333
"Field",
@@ -352,7 +352,6 @@ def get_fields(klass, field_type, auto_attribs, helps) -> dict[str, Field]:
352352
def make_task_def(
353353
spec_type: type["TaskDef"],
354354
out_type: type["TaskOutputs"],
355-
task_type: type["Task"],
356355
inputs: dict[str, Arg],
357356
outputs: dict[str, Out],
358357
klass: type | None = None,
@@ -418,15 +417,12 @@ def make_task_def(
418417
name=name,
419418
bases=bases,
420419
kwds={},
421-
exec_body=lambda ns: ns.update(
422-
{"Task": task_type, "Outputs": outputs_klass}
423-
),
420+
exec_body=lambda ns: ns.update({"Outputs": outputs_klass}),
424421
)
425422
else:
426423
# Ensure that the class has it's own annotations dict so we can modify it without
427424
# messing up other classes
428425
klass.__annotations__ = copy(klass.__annotations__)
429-
klass.Task = task_type
430426
klass.Outputs = outputs_klass
431427
# Now that we have saved the attributes in lists to be
432428
for arg in inputs.values():

pydra/design/boutiques.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from functools import reduce
77
from fileformats.generic import File
88
from pydra.engine.specs import ShellDef
9-
from pydra.engine.task import BoshTask
109
from .base import make_task_def
1110
from . import shell
1211

@@ -115,7 +114,6 @@ def define(
115114
)
116115
return make_task_def(
117116
spec_type=ShellDef,
118-
task_type=BoshTask,
119117
out_type=out,
120118
arg_type=arg,
121119
inputs=inputs,

pydra/design/python.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ def define(
121121
PythonDef
122122
The task definition class for the Python function
123123
"""
124-
from pydra.engine.task import PythonTask
125124
from pydra.engine.specs import PythonDef, PythonOutputs
126125

127126
def make(wrapped: ty.Callable | type) -> PythonDef:
@@ -162,7 +161,6 @@ def make(wrapped: ty.Callable | type) -> PythonDef:
162161
interface = make_task_def(
163162
PythonDef,
164163
PythonOutputs,
165-
PythonTask,
166164
parsed_inputs,
167165
parsed_outputs,
168166
name=name,

pydra/design/shell.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,6 @@ def define(
287287
ShellDef
288288
The interface for the shell command
289289
"""
290-
from pydra.engine.task import ShellTask
291290
from pydra.engine.specs import ShellDef, ShellOutputs
292291

293292
def make(
@@ -376,7 +375,6 @@ def make(
376375
interface = make_task_def(
377376
ShellDef,
378377
ShellOutputs,
379-
ShellTask,
380378
parsed_inputs,
381379
parsed_outputs,
382380
name=class_name,

pydra/design/workflow.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
)
1515

1616
if ty.TYPE_CHECKING:
17-
from pydra.engine.workflow.base import Workflow
17+
from pydra.engine.core import Workflow
1818
from pydra.engine.specs import TaskDef, TaskOutputs, WorkflowDef
1919

2020

@@ -128,7 +128,6 @@ def define(
128128
TaskDef
129129
The interface for the function or class.
130130
"""
131-
from pydra.engine.core import WorkflowTask
132131
from pydra.engine.specs import TaskDef, WorkflowDef, WorkflowOutputs
133132

134133
if lazy is None:
@@ -174,7 +173,6 @@ def make(wrapped: ty.Callable | type) -> TaskDef:
174173
interface = make_task_def(
175174
WorkflowDef,
176175
WorkflowOutputs,
177-
WorkflowTask,
178176
parsed_inputs,
179177
parsed_outputs,
180178
name=name,
@@ -208,12 +206,12 @@ def this() -> "Workflow":
208206
OutputsType = ty.TypeVar("OutputsType", bound="TaskOutputs")
209207

210208

211-
def add(task_spec: "TaskDef[OutputsType]", name: str = None) -> OutputsType:
209+
def add(task_def: "TaskDef[OutputsType]", name: str = None) -> OutputsType:
212210
"""Add a node to the workflow currently being constructed
213211
214212
Parameters
215213
----------
216-
task_spec : TaskDef
214+
task_def : TaskDef
217215
The definition of the task to add to the workflow as a node
218216
name : str, optional
219217
The name of the node, by default it will be the name of the task definition
@@ -224,4 +222,4 @@ def add(task_spec: "TaskDef[OutputsType]", name: str = None) -> OutputsType:
224222
Outputs
225223
The outputs definition of the node
226224
"""
227-
return this().add(task_spec, name=name)
225+
return this().add(task_def, name=name)

0 commit comments

Comments
 (0)