Skip to content

ENH: Convert pipelines to python scripts#1572

Open
imikejackson wants to merge 6 commits intoBlueQuartzSoftware:developfrom
imikejackson:topic/gen_python
Open

ENH: Convert pipelines to python scripts#1572
imikejackson wants to merge 6 commits intoBlueQuartzSoftware:developfrom
imikejackson:topic/gen_python

Conversation

@imikejackson
Copy link
Copy Markdown
Contributor

This lays down infrastructure that allows users to convert a .d3dpipeline file into a fully functioning python script.

@imikejackson imikejackson requested a review from JDuffeyBQ March 31, 2026 22:48
@imikejackson imikejackson self-assigned this Mar 31, 2026
@imikejackson imikejackson added the enhancement New feature or request label Mar 31, 2026

for arg_name in sorted(args.keys()):
value = args[arg_name]
codec = self._registry.find(value)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We know the exact parameter since we have the filter so we should just use a mapping of filter parameter to python conversion.

Comment on lines +27 to +31
MODULE_IMPORTS: dict[str, str] = {
"simplnx": "import simplnx as nx",
"orientationanalysis": "import orientationanalysis as nxor",
"itkimageprocessing": "import itkimageprocessing as nxitk",
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems redundant given that an import statement can be constructed from the above MODULE_ALIASES.


def _encode_simple_value(value: Any) -> str:
"""Encode a single simple value to a Python expression string."""
import pathlib
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be with the other imports.

print(generator.generate(pipeline))
"""

from __future__ import annotations
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is only for deferred evaluation of annotations but those don't seem to be used? Also we should be importing the simplnx modules. And some type annotations can be updated from Any.

return registry


def create_default_generator() -> PipelineCodeGenerator:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PipelineCodeGenerator doesn't change so I think it would be easier if you could call this from a function something like generate_python_pipeline() using a constant version of the generator.

"import shutil",
"from pathlib import Path",
"",
"def check_filter_result(filter: nx.IFilter, result: nx.IFilter.ExecuteResult) -> None:",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about moving this function into the bindings module directly. Not difficult just wasn't a priority before.

filters = [pipeline[i] for i in range(len(pipeline))]
return self._generate_full(filters)

def generate_filters(self, filters: list[Any]) -> str:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions specifically want a PipelineFilter object but I think it should support instances of IFilter which the pipeline filter can just hand to that function. I think it would be to have a function to generate a single filter as well.

Comment on lines +216 to +218
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/utils/simplnx_utilities.py
DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/."
)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't copy to the correct place on the Visual Studio generator.

Comment on lines +202 to +204
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/utils/simplnx_utilities.py
DESTINATION ${SIMPLNX_PY_INSTALL_DIR}
)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should make sure this is good for conda builds.


pipeline = nx.Pipeline.from_file("path/to/pipeline.d3dpipeline")
generator = create_default_generator()
print(generator.generate(pipeline))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should a test to ensure that we generate valid code ideally. Minimum is that the script runs.

Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
imikejackson and others added 5 commits April 2, 2026 23:21
Exposes a Python method that returns a dict mapping argument names
to their parameter type UUID strings. This enables UUID-based codec
lookup in simplnx_utilities.py instead of duck-typing values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Flatten PipelineCodeGenerator/CodecRegistry/FilterModuleResolver classes
  into module-level functions (generate_python_pipeline, generate_python_filters)
- Use UUID-based parameter-to-codec mapping instead of duck-typing values
- Import simplnx at module top level; use concrete types where possible
- Remove redundant MODULE_IMPORTS dict (construct from MODULE_ALIASES)
- Remove unused os/shutil/Path imports from generated code
- Move importlib import to module top level
- Remove from __future__ import annotations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use $<TARGET_FILE_DIR:simplnx> instead of CMAKE_RUNTIME_OUTPUT_DIRECTORY
so the copy works correctly with Visual Studio and other multi-config
generators that append a config subdirectory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests that generate_python_pipeline() and generate_python_filters()
produce syntactically valid Python code. Covers basic filters and
complex parameter types like ArrayThresholdSet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename 'result' to 'uuidDict' to avoid shadowing the outer 'result'
variable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants