Skip to content

Commit 60ba8cf

Browse files
committed
Added serialisation to docs
1 parent b78521c commit 60ba8cf

File tree

2 files changed

+134
-19
lines changed

2 files changed

+134
-19
lines changed

docs/source/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ workflows,
8585
* :ref:`Python-tasks`
8686
* :ref:`Shell-tasks`
8787
* :ref:`Workflows`
88-
* :ref:`Canonical task form`
88+
* :ref:`Canonical form and serialisation`
8989

9090
Examples
9191
~~~~~~~~

docs/source/tutorial/7-canonical-form.ipynb

Lines changed: 133 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"# Canonical task form\n",
7+
"# Canonical form and serialisation\n",
8+
"\n",
9+
"## Canonical task form\n",
810
"\n",
911
"Under the hood, all Python, shell and workflow tasks generated by the\n",
1012
"`pydra.compose.*.define` decorators/functions are translated to\n",
@@ -149,33 +151,32 @@
149151
"from pathlib import Path\n",
150152
"from fileformats import generic\n",
151153
"from pydra.compose import shell\n",
152-
"from pydra.utils.typing import MultiInputObj\n",
154+
"\n",
155+
"\n",
156+
"# the \"copied\" output is magically passed to this function because the name matches\n",
157+
"def get_file_size(copied: Path) -> int:\n",
158+
" \"\"\"Calculate the file size\"\"\"\n",
159+
" result = os.stat(copied)\n",
160+
" return result.st_size\n",
153161
"\n",
154162
"\n",
155163
"@shell.define\n",
156-
"class CpWithSize(shell.Task[\"CpWithSize.Outputs\"]):\n",
164+
"class CpFileWithSize(shell.Task[\"CpFileWithSize.Outputs\"]):\n",
157165
"\n",
158166
" executable = \"cp\"\n",
159167
"\n",
160-
" in_fs_objects: MultiInputObj[generic.FsObject]\n",
161-
" recursive: bool = shell.arg(argstr=\"-R\")\n",
162-
" text_arg: str = shell.arg(argstr=\"--text-arg\")\n",
163-
" int_arg: int | None = shell.arg(argstr=\"--int-arg\")\n",
164-
" tuple_arg: tuple[int, str] | None = shell.arg(argstr=\"--tuple-arg\")\n",
168+
" in_file: generic.File # = shell.arg() is assumed\n",
169+
" archive_mode: bool = shell.arg(argstr=\"-a\", default=False)\n",
165170
"\n",
166171
" class Outputs(shell.Outputs):\n",
167172
"\n",
168-
" @staticmethod\n",
169-
" def get_file_size(out_file: Path) -> int:\n",
170-
" \"\"\"Calculate the file size\"\"\"\n",
171-
" result = os.stat(out_file)\n",
172-
" return result.st_size\n",
173+
" copied: generic.File = shell.outarg(\n",
174+
" position=-1, path_template=\"{in_file}_copied\"\n",
175+
" )\n",
176+
" file_size: int = shell.out(callable=get_file_size)\n",
173177
"\n",
174-
" copied: generic.FsObject = shell.outarg(path_template=\"copied\")\n",
175-
" out_file_size: int = shell.out(callable=get_file_size)\n",
176178
"\n",
177-
"\n",
178-
"print_help(CpWithSize)"
179+
"print_help(CpFileWithSize)"
179180
]
180181
},
181182
{
@@ -197,7 +198,6 @@
197198
"import typing as ty\n",
198199
"import re\n",
199200
"from pydra.compose import python, workflow\n",
200-
"from pydra.compose.base import is_set\n",
201201
"from pydra.utils import print_help, show_workflow\n",
202202
"\n",
203203
"\n",
@@ -237,6 +237,121 @@
237237
"print_help(CanonicalWorkflowTask)\n",
238238
"show_workflow(CanonicalWorkflowTask)"
239239
]
240+
},
241+
{
242+
"cell_type": "markdown",
243+
"metadata": {},
244+
"source": [
245+
"## Serialisation\n",
246+
"\n",
247+
"As well as the dataclass-like canonical form, it is also possible to represent all tasks\n",
248+
"in a nested dictionary form, which could be written to a static file (e.g. in JSON or\n",
249+
"YAML format). The dictionary form of a class can be generated by the `pydra.utils.task_def_as_dict`\n",
250+
"function. For example, the following shell command"
251+
]
252+
},
253+
{
254+
"cell_type": "code",
255+
"execution_count": null,
256+
"metadata": {},
257+
"outputs": [],
258+
"source": [
259+
"MyCmd = shell.define(\n",
260+
" \"my-cmd <in_file> <out|out_file> --an-arg <an_arg?> \"\n",
261+
" \"--a-flag<a_flag> --arg-with-default <arg_with_default:int=3>\"\n",
262+
")\n",
263+
"\n",
264+
"print_help(MyCmd)"
265+
]
266+
},
267+
{
268+
"cell_type": "markdown",
269+
"metadata": {},
270+
"source": [
271+
"Can be converted into a serialised dictionary form"
272+
]
273+
},
274+
{
275+
"cell_type": "code",
276+
"execution_count": null,
277+
"metadata": {},
278+
"outputs": [],
279+
"source": [
280+
"from pprint import pprint\n",
281+
"from pydra.utils import task_def_as_dict\n",
282+
"\n",
283+
"my_cmd_dict = task_def_as_dict(MyCmd)\n",
284+
"\n",
285+
"pprint(my_cmd_dict)"
286+
]
287+
},
288+
{
289+
"cell_type": "markdown",
290+
"metadata": {},
291+
"source": [
292+
"Noting that there is still a little more work has to be done to serialise some Python\n",
293+
"objects, such as the `type` field and `xor` before it could be written to JSON/YAML. \n",
294+
"This is more challenging for task definitions containing Python functions such as Python\n",
295+
"and Workflow tasks or Shell tasks with converters, validators or callables, e.g."
296+
]
297+
},
298+
{
299+
"cell_type": "code",
300+
"execution_count": null,
301+
"metadata": {},
302+
"outputs": [],
303+
"source": [
304+
"cp_with_size_dict = task_def_as_dict(CpFileWithSize)\n",
305+
"\n",
306+
"pprint(cp_with_size_dict)"
307+
]
308+
},
309+
{
310+
"cell_type": "markdown",
311+
"metadata": {},
312+
"source": [
313+
"To unserialize the general dictionary form back into a Task class, you can use the\n",
314+
"`pydra.utils.task_def_from_dict` method"
315+
]
316+
},
317+
{
318+
"cell_type": "code",
319+
"execution_count": null,
320+
"metadata": {},
321+
"outputs": [],
322+
"source": [
323+
"from pydra.utils import task_def_from_dict\n",
324+
"\n",
325+
"ReloadedCpFileWithSize = task_def_from_dict(cp_with_size_dict)"
326+
]
327+
},
328+
{
329+
"cell_type": "markdown",
330+
"metadata": {},
331+
"source": [
332+
"which should run just as before"
333+
]
334+
},
335+
{
336+
"cell_type": "code",
337+
"execution_count": null,
338+
"metadata": {},
339+
"outputs": [],
340+
"source": [
341+
"from pathlib import Path\n",
342+
"import tempfile\n",
343+
"from pydra.utils import task_as_dict\n",
344+
"\n",
345+
"tmp_dir = Path(tempfile.mkdtemp())\n",
346+
"\n",
347+
"a_file = tmp_dir / \"hello-world.txt\"\n",
348+
"a_file.write_text(\"Hello world\")\n",
349+
"\n",
350+
"cp_file_with_size = ReloadedCpFileWithSize(in_file=a_file)\n",
351+
"outputs = cp_file_with_size(cache_root=tmp_dir / \"cache\")\n",
352+
"\n",
353+
"pprint(task_as_dict(outputs))"
354+
]
240355
}
241356
],
242357
"metadata": {

0 commit comments

Comments
 (0)