Skip to navigation

Migrating Python from Holoscan SDK 4.x

View as Markdown

Holoscan SDK 5.x Python is an application-authoring API over the same graph, compiler, plan, and runtime used by C++. It is not a compatibility layer over the 4.x Application, Fragment, condition, scheduler, or Python operator classes.

Use this page as an initial porting checklist. Migrate the application shape first, verify the compiled plan, and then port operator implementations to 5.x Python operators or to the C++ operator and registration model.

You will learn
  • How the major 4.x Python concepts map to 5.x
  • Which changes are mechanical and which require design work
  • A practical order for porting and verifying an application

Concept map

Holoscan SDK 4.x PythonHoloscan SDK 5.x PythonMigration action
Subclass Application and implement compose()Use hs.Graph; subclass only to define a reusable graphMove top-level composition into a graph builder or root Graph; reserve subclasses for typed reusable components
Use Fragment for distributed compositionCompile one graph with external topic and deployment declarationsReplace hand-authored fragment boundaries with graph structure and compiler-visible policy
Construct and attach Condition objectsTemporal contracts belong to operatorsDeclare the required rate, selection, and missing-data behavior in a Python operator’s contract(), or select registered operators whose contracts express it; there are no Python condition objects
Choose GreedyScheduler, MultiThreadScheduler, or EventBasedSchedulerCompile to a Plan; use partitions, regions, and deployment optionsRemove scheduler construction and express placement or grouping constraints on the graph or CompileOptions
Connect operators and name ports with stringsConnect reflected port objectsReplace port-name maps with source.out or sink.in_; use operator["non-identifier-name"] only when no attribute spelling exists
Define Python Operator subclasses with setup() and compute()Subclass hs.Operator, or compose registered C++ operatorsRewrite callbacks against hs.Operator with typed hs.Input and hs.Output ports, hs.Param parameters, and a contract(); or port the operator to C++, register the type, and import its Python proxy
Pass a TensorMap messageUse one schema payload with explicit Tensor fields, or a single Tensor payloadDefine the payload boundary explicitly; do not depend on an untyped name-to-Tensor container
Emit dictionaries or other Python objects between Python operatorshs.pyobject carries the object itself between Python operators in one processDeclare those ports with hs.pyobject; consumers share the object, so copy it before mutating it. Use a generated schema payload when a C++ operator, another process, or an external topic consumes the data
Rely on implicit metadata propagationDeclare metadata in the payload schema or explicit contractIdentify required metadata and make it part of the typed data or boundary contract
Load YAML with app.config() and from_config()Use config.load(), params.bind(), and graph-owned params.set_params()Bind JSON Pointer paths to operator handles and inspect returned provenance before compiling
Let Ctrl-C stop app.run()Use caller-managed Python signals and RunSession.request_stop()Prefer run_async() in a context manager and translate application-owned shutdown events into cooperative stop
Install a Python 3.12–3.13 wheelUse Python 3.12 or newerRebuild the environment on a supported interpreter and select the package for the installed CUDA major version

Port in this order

  1. Inventory operators and payloads. Identify every custom Python operator, 4.x domain operator, TensorMap, and metadata dependency. Confirm that a registered 5.x provider exists, or plan a port to a 5.x Python or C++ operator.
  2. Rebuild the topology as an hs.Graph. Import proxies from the module package that ships them, create instances with schema-backed keyword parameters, and wire reflected ports.
  3. Move runtime choices into authoring policy. Replace conditions, scheduler selection, and resource objects with operator contracts, graph clocks, queue policy, partitions, regions, and deployment options.
  4. Move file configuration onto the graph. Load the document, bind sparse paths to handles, call params.set_params(), and retain provenance if the application audits configuration.
  5. Compile before running. Treat a rejected Plan as structured evidence; resolve every error diagnostic before creating a session.
  6. Make shutdown explicit. Choose blocking run() only for finite plans. For services and recurring graphs, own run_async() with a context manager and route control events to request_stop().
  7. Compare plan artifacts. Save plan.json() for representative graphs and review the topology, policies, endpoints, and diagnostics before testing live devices.

Minimal before-and-after shape

class MyApplication(Application):
def compose(self):
source = SourceOp(self, CountCondition(self, 10), name="source")
sink = SinkOp(self, name="sink")
self.add_flow(source, sink, {("out", "in")})
app = MyApplication()
app.run()

The snippets show application shape, not equivalent operator semantics. In 5.x, the source’s C++ temporal contract determines activation and the caller owns run termination; limit is operator configuration, not a graph-stopping condition.

Current boundaries to plan for

  • 4.x Python operators do not have a 5.x compatibility shim. Rewrite each one as an hs.Operator subclass with typed ports and an explicit contract(), or port it to the registered C++ model. Do not wrap the old setup() or compute() methods.
  • Runtime parameter-control transactions are separate from cold graph configuration. Use params.set_params() before compile; do not expect a live replacement for every 4.x dynamic parameter pattern.
  • Python external topics cover scalar, inline-schema, raw Tensor, and descriptor-backed Tensor authoring. Tensor topics require the matching payload token and complete boundary contracts.
  • Provider modules are explicit imports. Import the package that ships an operator before looking up its proxy; the core package does not scan the environment automatically.

See Python for current application-authoring guidance and the broader 4.x migration map for the C++ runtime and operator model.

Recap

  • Migrate to Graph → Plan → RunSession; do not search for 4.x application, condition, or scheduler compatibility classes.
  • Rewrite custom Python operators as hs.Operator subclasses, or port them to registered C++ operators.
  • Make configuration, shutdown, payload metadata, and deployment constraints explicit, then verify the canonical plan before running.