diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..422cfca --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.22 + hooks: + - id: ruff + name: ruff lint + args: ["--fix"] + files: ^src/python_workflow_definition/ + - id: ruff-format + name: ruff format diff --git a/pyproject.toml b/pyproject.toml index c06709a..04e61b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,39 @@ plot = [ "ipython>=7.33.0,<=9.8.0", ] +[tool.ruff] +exclude = ["documentation", "example_workflows", "tests", "binder", "_version.py"] + +[tool.ruff.lint] +select = [ + # pycodestyle + "E", + # Pyflakes + "F", + # pyupgrade + "UP", + # flake8-bugbear + "B", + # flake8-simplify + "SIM", + # isort + "I", + # flake8-comprehensions + "C4", + # eradicate + "ERA", + # pylint + "PL", +] +ignore = [ + # ignore line-length violations + "E501", + # Too many statements + "PLR0915", + # Too many branches + "PLR0912", +] + [tool.hatch.build] include = [ "src/python_workflow_definition" diff --git a/src/python_workflow_definition/aiida.py b/src/python_workflow_definition/aiida.py index 30212b3..9d17dc0 100644 --- a/src/python_workflow_definition/aiida.py +++ b/src/python_workflow_definition/aiida.py @@ -99,7 +99,6 @@ def write_workflow_json(wg: WorkGraph, file_name: str) -> None: GRAPH_LEVEL_NAMES = ["graph_inputs", "graph_outputs", "graph_ctx"] for node in wg.tasks: - if node.name in GRAPH_LEVEL_NAMES: continue diff --git a/src/python_workflow_definition/cwl/__init__.py b/src/python_workflow_definition/cwl/__init__.py index 05885fc..e8bf3ea 100644 --- a/src/python_workflow_definition/cwl/__init__.py +++ b/src/python_workflow_definition/cwl/__init__.py @@ -20,17 +20,15 @@ def _get_function_argument(argument: str, position: int = 3) -> dict: - return { - argument - + "_file": { - "type": "File", - "inputBinding": { - "prefix": "--arg_" + argument + "=", - "separate": False, - "position": position, - }, + argument_dict = { + "type": "File", + "inputBinding": { + "prefix": "--arg_" + argument + "=", + "separate": False, + "position": position, }, } + return {argument + "_file": argument_dict} def _get_function_template(function_name: str) -> dict: @@ -44,10 +42,11 @@ def _get_function_template(function_name: str) -> dict: def _get_output_name(output_name: str) -> dict: - return { - output_name - + "_file": {"type": "File", "outputBinding": {"glob": output_name + ".pickle"}} + output_dict = { + "type": "File", + "outputBinding": {"glob": output_name + ".pickle"}, } + return {output_name + "_file": output_dict} def _get_function(workflow): @@ -55,24 +54,20 @@ def _get_function(workflow): n["id"]: n["value"] for n in workflow[NODES_LABEL] if n["type"] == "function" } funct_dict = {} - for funct_id in function_nodes_dict.keys(): + for funct_id in function_nodes_dict: target_ports = list( - set( - [ - e[TARGET_PORT_LABEL] - for e in workflow[EDGES_LABEL] - if e["target"] == funct_id - ] - ) + { + e[TARGET_PORT_LABEL] + for e in workflow[EDGES_LABEL] + if e["target"] == funct_id + } ) source_ports = list( - set( - [ - e[SOURCE_PORT_LABEL] - for e in workflow[EDGES_LABEL] - if e["source"] == funct_id - ] - ) + { + e[SOURCE_PORT_LABEL] + for e in workflow[EDGES_LABEL] + if e["source"] == funct_id + } ) funct_dict[funct_id] = { "targetPorts": target_ports, @@ -86,7 +81,7 @@ def _write_function_cwl(workflow, directory_path: str = "."): export_path = Path(directory_path) export_path.mkdir(parents=True, exist_ok=True) - for i in function_nodes_dict.keys(): + for i in function_nodes_dict: template: dict[str, Any] = { "cwlVersion": "v1.2", "class": "CommandLineTool", @@ -142,10 +137,7 @@ def _write_workflow_config(workflow, directory_path: str = "."): export_path.mkdir(parents=True, exist_ok=True) with open(export_path / "workflow.yml", "w") as f: dump( - { - k + "_file": {"class": "File", "path": k + ".pickle"} - for k in input_dict.keys() - }, + {k + "_file": {"class": "File", "path": k + ".pickle"} for k in input_dict}, f, Dumper=Dumper, ) @@ -170,7 +162,7 @@ def _write_workflow(workflow, directory_path: str = "."): last_compute_id = [ e[SOURCE_LABEL] for e in workflow[EDGES_LABEL] if e[TARGET_LABEL] == result_id ][0] - workflow_template["inputs"].update({k + "_file": "File" for k in input_dict.keys()}) + workflow_template["inputs"].update({k + "_file": "File" for k in input_dict}) if funct_dict[last_compute_id]["sourcePorts"] == [None]: workflow_template["outputs"] = { "result_file": { @@ -209,29 +201,29 @@ def _write_workflow(workflow, directory_path: str = "."): for k, v in t[1].items(): if v[SOURCE_LABEL] in input_id_dict: in_dict[k + "_file"] = input_id_dict[v[SOURCE_LABEL]] + "_file" + elif v["sourcePort"] is None: + in_dict[k + "_file"] = ( + step_name_lst[v[SOURCE_LABEL]] + + "_" + + str(v[SOURCE_LABEL]) + + "/result_file" + ) else: - if v["sourcePort"] is None: - in_dict[k + "_file"] = ( - step_name_lst[v[SOURCE_LABEL]] - + "_" - + str(v[SOURCE_LABEL]) - + "/result_file" - ) - else: - in_dict[k + "_file"] = ( - step_name_lst[v[SOURCE_LABEL]] - + "_" - + str(v[SOURCE_LABEL]) - + "/" - + v[SOURCE_PORT_LABEL] - + "_file" - ) + in_dict[k + "_file"] = ( + step_name_lst[v[SOURCE_LABEL]] + + "_" + + str(v[SOURCE_LABEL]) + + "/" + + v[SOURCE_PORT_LABEL] + + "_file" + ) + step_dict = { + "run": node_script, + "in": in_dict, + "out": output, + } workflow_template["steps"].update( - { - step_name_lst[ind] - + "_" - + str(ind): {"run": node_script, "in": in_dict, "out": output} - } + {step_name_lst[ind] + "_" + str(ind): step_dict} ) export_path = Path(directory_path) export_path.mkdir(parents=True, exist_ok=True) @@ -240,7 +232,7 @@ def _write_workflow(workflow, directory_path: str = "."): def write_workflow(file_name: str, directory_path: str = "."): - with open(file_name, "r") as f: + with open(file_name) as f: workflow = json.load(f) _write_function_cwl(workflow=workflow, directory_path=directory_path) diff --git a/src/python_workflow_definition/executorlib.py b/src/python_workflow_definition/executorlib.py index 552d44b..8cb76cb 100644 --- a/src/python_workflow_definition/executorlib.py +++ b/src/python_workflow_definition/executorlib.py @@ -11,10 +11,6 @@ SOURCE_LABEL, SOURCE_PORT_LABEL, convert_nodes_list_to_dict, - get_dict, - get_kwargs, - get_list, - get_source_handles, remove_result, ) @@ -25,9 +21,9 @@ def get_item(obj, key): def _get_value(result_dict: dict, nodes_new_dict: dict, link_dict: dict, exe: Executor): source, source_handle = link_dict[SOURCE_LABEL], link_dict[SOURCE_PORT_LABEL] - if source in result_dict.keys(): + if source in result_dict: result = result_dict[source] - elif source in nodes_new_dict.keys(): + elif source in nodes_new_dict: result = nodes_new_dict[source] else: raise KeyError() diff --git a/src/python_workflow_definition/jobflow.py b/src/python_workflow_definition/jobflow.py index b385177..a4dfb05 100644 --- a/src/python_workflow_definition/jobflow.py +++ b/src/python_workflow_definition/jobflow.py @@ -27,7 +27,7 @@ def _get_function_dict(flow: Flow): - return {job.uuid: job.function for job in flow.jobs} + return {j.uuid: j.function for j in flow.jobs} def _get_nodes_dict(function_dict: dict): @@ -62,8 +62,8 @@ def _get_edges_and_extend_nodes( flow_dict: dict, nodes_mapping_dict: dict, nodes_dict: dict ): edges_lst = [] - for job in flow_dict["jobs"]: - for k, v in job["function_kwargs"].items(): + for j in flow_dict["jobs"]: + for k, v in j["function_kwargs"].items(): if ( isinstance(v, dict) and "@module" in v @@ -72,20 +72,18 @@ def _get_edges_and_extend_nodes( ): edges_lst.append( _get_edge_from_dict( - target=nodes_mapping_dict[job["uuid"]], + target=nodes_mapping_dict[j["uuid"]], key=k, value_dict=v, nodes_mapping_dict=nodes_mapping_dict, ) ) elif isinstance(v, dict) and any( - [ - isinstance(el, dict) - and "@module" in el - and "@class" in el - and "@version" in el - for el in v.values() - ] + isinstance(el, dict) + and "@module" in el + and "@class" in el + and "@version" in el + for el in v.values() ): node_dict_index = len(nodes_dict) nodes_dict[node_dict_index] = get_dict @@ -122,20 +120,18 @@ def _get_edges_and_extend_nodes( ) edges_lst.append( { - TARGET_LABEL: nodes_mapping_dict[job["uuid"]], + TARGET_LABEL: nodes_mapping_dict[j["uuid"]], TARGET_PORT_LABEL: k, SOURCE_LABEL: node_dict_index, SOURCE_PORT_LABEL: None, } ) elif isinstance(v, list) and any( - [ - isinstance(el, dict) - and "@module" in el - and "@class" in el - and "@version" in el - for el in v - ] + isinstance(el, dict) + and "@module" in el + and "@class" in el + and "@version" in el + for el in v ): node_list_index = len(nodes_dict) nodes_dict[node_list_index] = get_list @@ -172,7 +168,7 @@ def _get_edges_and_extend_nodes( ) edges_lst.append( { - TARGET_LABEL: nodes_mapping_dict[job["uuid"]], + TARGET_LABEL: nodes_mapping_dict[j["uuid"]], TARGET_PORT_LABEL: k, SOURCE_LABEL: node_list_index, SOURCE_PORT_LABEL: None, @@ -186,7 +182,7 @@ def _get_edges_and_extend_nodes( node_index = {tv: tk for tk, tv in nodes_dict.items()}[v] edges_lst.append( { - TARGET_LABEL: nodes_mapping_dict[job["uuid"]], + TARGET_LABEL: nodes_mapping_dict[j["uuid"]], TARGET_PORT_LABEL: k, SOURCE_LABEL: node_index, SOURCE_PORT_LABEL: None, @@ -196,10 +192,8 @@ def _get_edges_and_extend_nodes( def _resort_total_lst(total_dict: dict, nodes_dict: dict) -> dict: - nodes_with_dep_lst = list(sorted(total_dict.keys())) - nodes_without_dep_lst = [ - k for k in nodes_dict.keys() if k not in nodes_with_dep_lst - ] + nodes_with_dep_lst = sorted(total_dict.keys()) + nodes_without_dep_lst = [k for k in nodes_dict if k not in nodes_with_dep_lst] ordered_lst: list = [] total_new_dict: dict[Any, dict] = {} while len(total_new_dict) < len(total_dict): @@ -208,7 +202,7 @@ def _resort_total_lst(total_dict: dict, nodes_dict: dict) -> dict: if ind not in ordered_lst: source_lst = [sd[SOURCE_LABEL] for sd in connect.values()] if all( - [s in ordered_lst or s in nodes_without_dep_lst for s in source_lst] + s in ordered_lst or s in nodes_without_dep_lst for s in source_lst ): ordered_lst.append(ind) total_new_dict[ind] = connect @@ -220,7 +214,7 @@ def _group_edges(edges_lst: list) -> dict: for ed_major in edges_lst: target_id = ed_major[TARGET_LABEL] tmp_lst = [] - if target_id not in total_dict.keys(): + if target_id not in total_dict: for ed in edges_lst: if target_id == ed[TARGET_LABEL]: tmp_lst.append(ed) @@ -237,15 +231,15 @@ def _get_workflow( ) -> list: def get_attr_helper(obj, source_handle): if source_handle is None: - return getattr(obj, "output") + return obj.output else: - return getattr(getattr(obj, "output"), source_handle) + return getattr(obj.output, source_handle) memory_dict: dict[Any, Any] = {} - for k in total_dict.keys(): + for k, subdict in total_dict.items(): v = nodes_dict[k] if isfunction(v): - if k in source_handles_dict.keys(): + if k in source_handles_dict: fn = job( method=v, data=[el for el in source_handles_dict[k] if el is not None], @@ -261,7 +255,7 @@ def get_attr_helper(obj, source_handle): source_handle=vw[SOURCE_PORT_LABEL], ) ) - for kw, vw in total_dict[k].items() + for kw, vw in subdict.items() } memory_dict[k] = fn(**kwargs) return list(memory_dict.values()) diff --git a/src/python_workflow_definition/models.py b/src/python_workflow_definition/models.py index 6ddb583..167fa47 100644 --- a/src/python_workflow_definition/models.py +++ b/src/python_workflow_definition/models.py @@ -1,7 +1,7 @@ import json import logging from pathlib import Path -from typing import Annotated, Any, List, Literal, Optional, Type, TypeVar, Union +from typing import Annotated, Any, Literal, TypeVar from pydantic import ( BaseModel, @@ -26,10 +26,10 @@ ) -JsonPrimitive = Union[str, int, float, bool, None] +JsonPrimitive = str | int | float | bool | None AllowableDefaults = TypeAliasType( "AllowableDefaults", - "Union[JsonPrimitive, dict[str, AllowableDefaults], list[AllowableDefaults]]", + "JsonPrimitive | dict[str, AllowableDefaults] | list[AllowableDefaults]", ) @@ -47,7 +47,7 @@ class PythonWorkflowDefinitionInputNode(PythonWorkflowDefinitionBaseNode): type: Literal["input"] name: str - value: Optional[AllowableDefaults] = None + value: AllowableDefaults | None = None class PythonWorkflowDefinitionOutputNode(PythonWorkflowDefinitionBaseNode): @@ -80,11 +80,9 @@ def check_value_format(cls, v: str): # Discriminated Union for Nodes PythonWorkflowDefinitionNode = Annotated[ - Union[ - PythonWorkflowDefinitionInputNode, - PythonWorkflowDefinitionOutputNode, - PythonWorkflowDefinitionFunctionNode, - ], + PythonWorkflowDefinitionInputNode + | PythonWorkflowDefinitionOutputNode + | PythonWorkflowDefinitionFunctionNode, Field(discriminator="type"), ] @@ -93,13 +91,13 @@ class PythonWorkflowDefinitionEdge(BaseModel): """Model for edges connecting nodes.""" target: int - targetPort: Optional[str] = None + targetPort: str | None = None source: int - sourcePort: Optional[str] = None + sourcePort: str | None = None @field_validator("sourcePort", mode="before") @classmethod - def handle_default_source(cls, v: Any) -> Optional[str]: + def handle_default_source(cls, v: Any) -> str | None: """ Transforms incoming None/null for sourcePort to INTERNAL_DEFAULT_HANDLE. Runs before standard validation. @@ -117,7 +115,7 @@ def handle_default_source(cls, v: Any) -> Optional[str]: return v @field_serializer("sourcePort") - def serialize_source_handle(self, v: Optional[str]) -> Optional[str]: + def serialize_source_handle(self, v: str | None) -> str | None: """ SERIALIZATION (Output): Converts internal INTERNAL_DEFAULT_HANDLE ("__result__") back to None. @@ -131,13 +129,13 @@ class PythonWorkflowDefinitionWorkflow(BaseModel): """The main workflow model.""" version: str - nodes: List[PythonWorkflowDefinitionNode] - edges: List[PythonWorkflowDefinitionEdge] + nodes: list[PythonWorkflowDefinitionNode] + edges: list[PythonWorkflowDefinitionEdge] def dump_json( self, *, - indent: Optional[int] = 2, + indent: int | None = 2, **kwargs, ) -> str: """ @@ -171,9 +169,9 @@ def dump_json( def dump_json_file( self, - file_name: Union[str, Path], + file_name: str | Path, *, - indent: Optional[int] = 2, + indent: int | None = 2, **kwargs, ) -> None: """ @@ -196,14 +194,14 @@ def dump_json_file( with open(file_name, "w", encoding="utf-8") as f: f.write(json_string) logger.info(f"Successfully wrote workflow model to {file_name}.") - except IOError as e: + except OSError as e: logger.error( f"Error writing workflow model to file {file_name}: {e}", exc_info=True ) raise @classmethod - def load_json_str(cls: Type[T], json_data: Union[str, bytes]) -> dict: + def load_json_str(cls: type[T], json_data: str | bytes) -> dict: """ Loads and validates workflow data from a JSON string or bytes. @@ -239,7 +237,7 @@ def load_json_str(cls: Type[T], json_data: Union[str, bytes]) -> dict: raise @classmethod - def load_json_file(cls: Type[T], file_name: Union[str, Path]) -> dict: + def load_json_file(cls: type[T], file_name: str | Path) -> dict: """ Loads and validates workflow data from a JSON file. @@ -263,6 +261,6 @@ def load_json_file(cls: Type[T], file_name: Union[str, Path]) -> dict: except FileNotFoundError: logger.error(f"JSON file not found: {file_name}", exc_info=True) raise - except IOError as e: + except OSError as e: logger.error(f"Error reading JSON file {file_name}: {e}", exc_info=True) raise diff --git a/src/python_workflow_definition/plot.py b/src/python_workflow_definition/plot.py index d0ca1b7..04d4413 100644 --- a/src/python_workflow_definition/plot.py +++ b/src/python_workflow_definition/plot.py @@ -11,7 +11,6 @@ SOURCE_LABEL, SOURCE_PORT_LABEL, convert_nodes_list_to_dict, - get_kwargs, ) diff --git a/src/python_workflow_definition/purepython.py b/src/python_workflow_definition/purepython.py index 0e77bd3..f541edc 100644 --- a/src/python_workflow_definition/purepython.py +++ b/src/python_workflow_definition/purepython.py @@ -9,21 +9,15 @@ SOURCE_LABEL, SOURCE_PORT_LABEL, TARGET_LABEL, - TARGET_PORT_LABEL, convert_nodes_list_to_dict, - get_dict, get_kwargs, - get_list, - get_source_handles, remove_result, ) def resort_total_lst(total_lst: list, nodes_dict: dict) -> list: - nodes_with_dep_lst = list(sorted([v[0] for v in total_lst])) - nodes_without_dep_lst = [ - k for k in nodes_dict.keys() if k not in nodes_with_dep_lst - ] + nodes_with_dep_lst = sorted([v[0] for v in total_lst]) + nodes_without_dep_lst = [k for k in nodes_dict if k not in nodes_with_dep_lst] ordered_lst: list = [] total_new_lst: list[list] = [] while len(total_new_lst) < len(total_lst): @@ -31,7 +25,7 @@ def resort_total_lst(total_lst: list, nodes_dict: dict) -> list: if ind not in ordered_lst: source_lst = [sd[SOURCE_LABEL] for sd in connect.values()] if all( - [s in ordered_lst or s in nodes_without_dep_lst for s in source_lst] + s in ordered_lst or s in nodes_without_dep_lst for s in source_lst ): ordered_lst.append(ind) total_new_lst.append([ind, connect]) @@ -55,9 +49,9 @@ def group_edges(edges_lst: list) -> list: def _get_value(result_dict: dict, nodes_new_dict: dict, link_dict: dict): source, source_handle = link_dict[SOURCE_LABEL], link_dict[SOURCE_PORT_LABEL] - if source in result_dict.keys(): + if source in result_dict: result = result_dict[source] - elif source in nodes_new_dict.keys(): + elif source in nodes_new_dict: result = nodes_new_dict[source] else: raise KeyError() diff --git a/src/python_workflow_definition/pyiron_base.py b/src/python_workflow_definition/pyiron_base.py index 17e4aaa..53ee1a7 100644 --- a/src/python_workflow_definition/pyiron_base.py +++ b/src/python_workflow_definition/pyiron_base.py @@ -1,6 +1,6 @@ from importlib import import_module from inspect import isfunction -from typing import Any, Optional +from typing import Any import numpy as np from pyiron_base import Project, job @@ -26,10 +26,8 @@ def _resort_total_lst(total_lst: list, nodes_dict: dict) -> list: - nodes_with_dep_lst = list(sorted([v[0] for v in total_lst])) - nodes_without_dep_lst = [ - k for k in nodes_dict.keys() if k not in nodes_with_dep_lst - ] + nodes_with_dep_lst = sorted([v[0] for v in total_lst]) + nodes_without_dep_lst = [k for k in nodes_dict if k not in nodes_with_dep_lst] ordered_lst: list = [] total_new_lst: list[list] = [] while len(total_new_lst) < len(total_lst): @@ -37,7 +35,7 @@ def _resort_total_lst(total_lst: list, nodes_dict: dict) -> list: if ind not in ordered_lst: source_lst = [sd[SOURCE_LABEL] for sd in connect.values()] if all( - [s in ordered_lst or s in nodes_without_dep_lst for s in source_lst] + s in ordered_lst or s in nodes_without_dep_lst for s in source_lst ): ordered_lst.append(ind) total_new_lst.append([ind, connect]) @@ -62,11 +60,11 @@ def _group_edges(edges_lst: list) -> list: def _get_source( nodes_dict: dict, delayed_object_dict: dict, source: str, source_handle: str ): - if source in delayed_object_dict.keys() and source_handle is not None: + if source in delayed_object_dict and source_handle is not None: return ( delayed_object_dict[source].__getattr__("output").__getattr__(source_handle) ) - elif source in delayed_object_dict.keys(): + elif source in delayed_object_dict: return delayed_object_dict[source] else: return nodes_dict[source] @@ -95,7 +93,7 @@ def _get_delayed_object_dict( def get_dict(**kwargs) -> dict: - return {k: v for k, v in kwargs["kwargs"].items()} + return dict(kwargs["kwargs"].items()) def get_list(**kwargs) -> list: @@ -103,7 +101,7 @@ def get_list(**kwargs) -> list: def _remove_server_obj(nodes_dict: dict, edges_lst: list): - server_lst = [k for k in nodes_dict.keys() if k.startswith("_server_obj_")] + server_lst = [k for k in nodes_dict if k.startswith("_server_obj_")] for s in server_lst: del nodes_dict[s] edges_lst = [ep for ep in edges_lst if s not in ep] @@ -122,12 +120,12 @@ def _get_unique_objects(nodes_dict: dict): for k, v in nodes_dict.items(): if isinstance(v, DelayedObject): delayed_object_dict[k] = v - elif isinstance(v, list) and any([isinstance(el, DelayedObject) for el in v]): + elif isinstance(v, list) and any(isinstance(el, DelayedObject) for el in v): delayed_object_dict[k] = DelayedObject(function=get_list) - delayed_object_dict[k]._input = {i: el for i, el in enumerate(v)} + delayed_object_dict[k]._input = dict(enumerate(v)) delayed_object_dict[k]._python_function = get_list elif isinstance(v, dict) and any( - [isinstance(el, DelayedObject) for el in v.values()] + isinstance(el, DelayedObject) for el in v.values() ): delayed_object_dict[k] = DelayedObject( function=get_dict, @@ -138,13 +136,12 @@ def _get_unique_objects(nodes_dict: dict): unique_lst: list = [] delayed_object_updated_dict: dict[Any, DelayedObject] = {} match_dict: dict[Any, Any] = {} - for dobj in delayed_object_dict.keys(): + for dobj, v in delayed_object_dict.items(): match = False for obj in unique_lst: if ( - delayed_object_updated_dict[obj]._python_function - == delayed_object_dict[dobj]._python_function - and delayed_object_dict[dobj]._input == delayed_object_dict[obj]._input + delayed_object_updated_dict[obj]._python_function == v._python_function + and v._input == delayed_object_dict[obj]._input ): delayed_object_updated_dict[obj] = delayed_object_dict[obj] match_dict[dobj] = obj @@ -152,17 +149,15 @@ def _get_unique_objects(nodes_dict: dict): break if not match: unique_lst.append(dobj) - delayed_object_updated_dict[dobj] = delayed_object_dict[dobj] + delayed_object_updated_dict[dobj] = v update_dict = {} for k, v in nodes_dict.items(): if not ( isinstance(v, DelayedObject) - or ( - isinstance(v, list) and any([isinstance(el, DelayedObject) for el in v]) - ) + or (isinstance(v, list) and any(isinstance(el, DelayedObject) for el in v)) or ( isinstance(v, dict) - and any([isinstance(el, DelayedObject) for el in v.values()]) + and any(isinstance(el, DelayedObject) for el in v.values()) ) ): update_dict[k] = v @@ -180,7 +175,7 @@ def _get_connection_dict(delayed_object_updated_dict: dict, match_dict: dict): lookup_dict[i] = k for k, v in match_dict.items(): - if v in connection_dict.keys(): + if v in connection_dict: connection_dict[k] = connection_dict[v] return connection_dict, lookup_dict @@ -230,7 +225,7 @@ def _get_edges_dict( return edges_dict_lst -def load_workflow_json(file_name: str, project: Optional[Project] = None): +def load_workflow_json(file_name: str, project: Project | None = None): if project is None: project = Project(".") diff --git a/src/python_workflow_definition/pyiron_workflow.py b/src/python_workflow_definition/pyiron_workflow.py index 73d994b..ffdcd5b 100644 --- a/src/python_workflow_definition/pyiron_workflow.py +++ b/src/python_workflow_definition/pyiron_workflow.py @@ -4,7 +4,7 @@ from typing import Any import numpy as np -from pyiron_workflow import Workflow, as_function_node, function_node +from pyiron_workflow import Workflow, function_node from pyiron_workflow.api import Function from python_workflow_definition.models import PythonWorkflowDefinitionWorkflow @@ -111,8 +111,7 @@ def write_workflow_json(graph_as_dict: dict, file_name: str = "workflow.json"): item_node_lst = [ e[SOURCE_LABEL] for e in edges_lst - if e[TARGET_LABEL] in pyiron_workflow_modules.keys() - and e[TARGET_PORT_LABEL] == "item" + if e[TARGET_LABEL] in pyiron_workflow_modules and e[TARGET_PORT_LABEL] == "item" ] values_from_dict_lst = [ @@ -129,8 +128,8 @@ def write_workflow_json(graph_as_dict: dict, file_name: str = "workflow.json"): nodes_remaining_dict = { k: v for k, v in nodes_dict.items() - if k not in pyiron_workflow_modules.keys() - and k not in remap_dict.keys() + if k not in pyiron_workflow_modules + and k not in remap_dict and k not in item_node_lst and k not in remap_get_list_dict.values() } @@ -178,7 +177,7 @@ def write_workflow_json(graph_as_dict: dict, file_name: str = "workflow.json"): SOURCE_PORT_LABEL: connected_edge[SOURCE_PORT_LABEL], } edge_get_list_updated_lst.append(edge_updated) - elif edge[SOURCE_LABEL] in remap_dict.keys(): + elif edge[SOURCE_LABEL] in remap_dict: edge_updated = { TARGET_LABEL: edge[TARGET_LABEL], TARGET_PORT_LABEL: edge[TARGET_PORT_LABEL], @@ -191,7 +190,7 @@ def write_workflow_json(graph_as_dict: dict, file_name: str = "workflow.json"): target_dict: dict[Any, list] = {} for edge in edge_get_list_updated_lst: - for k in pyiron_workflow_modules.keys(): + for k in pyiron_workflow_modules: if k == edge[TARGET_LABEL]: if k not in target_dict: target_dict[k] = [] @@ -199,22 +198,22 @@ def write_workflow_json(graph_as_dict: dict, file_name: str = "workflow.json"): source_dict: dict[Any, list] = {} for edge in edge_get_list_updated_lst: - for k in pyiron_workflow_modules.keys(): + for k in pyiron_workflow_modules: if k == edge[SOURCE_LABEL]: if k not in source_dict: source_dict[k] = [] source_dict[k].append(edge) edge_new_lst, nodes_to_delete = [], [] - for k in target_dict.keys(): + for k, v in target_dict.items(): source, sourcehandle = None, None - for edge in target_dict[k]: + for edge in v: if edge[SOURCE_PORT_LABEL] is None: sourcehandle = nodes_dict[edge[SOURCE_LABEL]] nodes_to_delete.append(edge[SOURCE_LABEL]) else: source = edge[SOURCE_LABEL] - if "s_" == source_dict[k][-1][TARGET_PORT_LABEL][:2]: + if source_dict[k][-1][TARGET_PORT_LABEL][:2] == "s_": edge_new_lst.append( { SOURCE_LABEL: nodes_final_order_dict[source], @@ -247,16 +246,7 @@ def write_workflow_json(graph_as_dict: dict, file_name: str = "workflow.json"): if ( isfunction(source_node) and source_node.__name__ == edge[SOURCE_PORT_LABEL] - ): - edge_new_lst.append( - { - TARGET_LABEL: nodes_final_order_dict[edge[TARGET_LABEL]], - TARGET_PORT_LABEL: edge[TARGET_PORT_LABEL], - SOURCE_LABEL: nodes_final_order_dict[edge[SOURCE_LABEL]], - SOURCE_PORT_LABEL: None, - } - ) - elif ( + ) or ( isfunction(source_node) and source_node.__name__ == "get_dict" and edge[SOURCE_PORT_LABEL] == "dict" @@ -328,15 +318,13 @@ def load_workflow_json(file_name: str) -> Workflow: PythonWorkflowDefinitionWorkflow.load_json_file(file_name=file_name) ) - input_values: dict[int, object] = ( - {} - ) # Type is actually more restrictive, must be jsonifyable object + input_values: dict[int, object] = {} nodes: dict[int, Function] = {} total_counter_dict = Counter( [n["value"] for n in content[NODES_LABEL] if n["type"] == "function"] ) - counter_dict = {k: -1 for k in total_counter_dict.keys()} - wf = Workflow(file_name.split(".")[0]) + counter_dict = dict.fromkeys(total_counter_dict.keys(), -1) + wf = Workflow(file_name.split(".", maxsplit=1)[0]) nodes_look_up_dict = {node["id"]: node["value"] for node in content[NODES_LABEL]} for node_dict in content[NODES_LABEL]: if node_dict["type"] == "function": @@ -384,7 +372,7 @@ def load_workflow_json(file_name: str) -> Workflow: source_port = edge_dict[SOURCE_PORT_LABEL] if source_port is None: - if source_id in input_values.keys(): # Parent input value + if source_id in input_values: # Parent input value upstream = input_values[source_id] else: # Single-output sibling upstream = nodes[source_id] diff --git a/src/python_workflow_definition/shared.py b/src/python_workflow_definition/shared.py index 9cc6680..fe42cfc 100644 --- a/src/python_workflow_definition/shared.py +++ b/src/python_workflow_definition/shared.py @@ -12,9 +12,7 @@ def get_dict(**kwargs) -> dict: - # NOTE: In WG, this will automatically be wrapped in a dict with the `result` key - return {k: v for k, v in kwargs.items()} - # return {'dict': {k: v for k, v in kwargs.items()}} + return dict(kwargs.items()) def get_list(**kwargs) -> list: @@ -34,11 +32,11 @@ def get_kwargs(lst: list) -> dict: def get_source_handles(edges_lst: list) -> dict: source_handle_dict: dict[Any, list] = {} for ed in edges_lst: - if ed[SOURCE_LABEL] not in source_handle_dict.keys(): + if ed[SOURCE_LABEL] not in source_handle_dict: source_handle_dict[ed[SOURCE_LABEL]] = [] source_handle_dict[ed[SOURCE_LABEL]].append(ed[SOURCE_PORT_LABEL]) return { - k: list(range(len(v))) if len(v) > 1 and all([el is None for el in v]) else v + k: list(range(len(v))) if len(v) > 1 and all(el is None for el in v) else v for k, v in source_handle_dict.items() } @@ -55,19 +53,17 @@ def update_node_names(workflow_dict: dict) -> dict: input_nodes = [n for n in workflow_dict[NODES_LABEL] if n["type"] == "input"] node_names_dict = { n["id"]: list( - set( - [ - e[TARGET_PORT_LABEL] - for e in workflow_dict[EDGES_LABEL] - if e[SOURCE_LABEL] == n["id"] - ] - ) + { + e[TARGET_PORT_LABEL] + for e in workflow_dict[EDGES_LABEL] + if e[SOURCE_LABEL] == n["id"] + } )[0] for n in input_nodes } counter_dict = Counter(node_names_dict.values()) - node_names_useage_dict = {k: -1 for k in counter_dict.keys()} + node_names_useage_dict = dict.fromkeys(counter_dict.keys(), -1) for k, v in node_names_dict.items(): node_names_useage_dict[v] += 1 if counter_dict[v] > 1: @@ -83,7 +79,7 @@ def update_node_names(workflow_dict: dict) -> dict: def set_result_node(workflow_dict): node_id_lst = [n["id"] for n in workflow_dict[NODES_LABEL]] - source_lst = list(set([e[SOURCE_LABEL] for e in workflow_dict[EDGES_LABEL]])) + source_lst = list({e[SOURCE_LABEL] for e in workflow_dict[EDGES_LABEL]}) end_node_lst = [] for ni in node_id_lst: