Creating a new NiFi Python processor
Every processor is a single .py file dropped into nifi_extensions/. NiFi picks it up on the next restart (or after a short poll).
The skeleton
Copy this and fill in the blanks:
from nifiapi.flowfiletransform import FlowFileTransform, FlowFileTransformResult
from nifiapi.properties import PropertyDescriptor, StandardValidators, ExpressionLanguageScope
from nifiapi.__jvm__ import JvmHolder
class MyProcessor(FlowFileTransform):
# 1. Tells NiFi this is a FlowFile transformer. Do not change this.
class Java:
implements = ['org.apache.nifi.python.processor.FlowFileTransform']
# 2. Metadata and pip packages NiFi will install automatically.
class ProcessorDetails:
version = "0.1.0"
description = "One sentence describing what this does."
tags = ["quantum", "qiskit"]
dependencies = ["qiskit>=2.0.0", "qiskit-aer>=0.13.0"]
# 3. Constructor — always this exact pattern.
def __init__(self, **kwargs):
JvmHolder.jvm = kwargs.get('jvm') # must come first
super().__init__() # no kwargs here
# Define your properties here (not outside __init__)
self.my_property = PropertyDescriptor(
name="My Property",
description="What the user should type here.",
required=True,
default_value="some default",
validators=[StandardValidators.NON_EMPTY_VALIDATOR],
expression_language_scope=ExpressionLanguageScope.FLOWFILE_ATTRIBUTES,
)
self.descriptors = [self.my_property]
def getPropertyDescriptors(self):
return self.descriptors
# 4. The actual logic. Receives a FlowFile, returns a result.
def transform(self, context, flowFile):
# Read your property value
value = context.getProperty(self.my_property) \
.evaluateAttributeExpressions(flowFile) \
.getValue()
# Import heavy libraries here, not at the top of the file
from qiskit import QuantumCircuit
# ... do work ...
output = "result"
return FlowFileTransformResult(
relationship="success",
contents=output.encode("utf-8"),
attributes={"my.output": output},
)
What each piece does
Java inner class — required marker so NiFi can find the processor when it scans the file. Never remove it.
ProcessorDetails — dependencies is the list of pip packages NiFi installs into its own isolated environment when the processor first loads. Add anything your code needs here.
__init__ — NiFi passes the JVM gateway as jvm=. You must assign it to JvmHolder.jvm before calling super().__init__(), otherwise validators like StandardValidators.POSITIVE_INTEGER_VALIDATOR will not work. Properties must also be created here, not as class-level variables — they need the JVM to be ready.
transform — called once per FlowFile. context.getProperty(descriptor).getValue() reads the configured value. Return FlowFileTransformResult with relationship="success" (or "failure") and optionally new contents (bytes) and attributes (dict).
Lazy imports — import Qiskit (and other heavy packages) inside transform, not at the top of the file. This avoids a race condition where the module loads before the dependency venv is fully ready.
Available validators
| Validator | Use for |
|---|---|
StandardValidators.NON_EMPTY_VALIDATOR |
any required string |
StandardValidators.POSITIVE_INTEGER_VALIDATOR |
counts, shot numbers |
StandardValidators.INTEGER_VALIDATOR |
any integer |
StandardValidators.NUMBER_VALIDATOR |
floats |
StandardValidators.BOOLEAN_VALIDATOR |
true/false flags |
Workflow
- Create
nifi_extensions/MyProcessor.pyusing the skeleton above. - Restart NiFi (
bin/nifi.sh restartfrom the NiFi home directory). - Search for
MyProcessorin the NiFi UI processor list — it will appear. - The first time you place it on the canvas, NiFi installs the pip dependencies. This takes ~30 seconds and the processor shows "Initializing" during that time.
- After that, subsequent restarts reuse the cached packages (fast).
Qiskit package split to be aware of
Some Qiskit features require extra packages beyond the core:
| Feature | Extra package needed |
|---|---|
qasm3.loads() (reading QASM3) |
qiskit-qasm3-import |
qasm3.dumps() (writing QASM3) |
none — built into qiskit |
| QPY read/write | none — built into qiskit |
| Simulation | qiskit-aer |
Add the extra package to ProcessorDetails.dependencies and delete the processor's cached venv (work/python/extensions/<Name>/) before restarting, otherwise NiFi reuses the old install.
Things that will break it silently
- Defining
PropertyDescriptoroutside__init__(class-level) — properties won't show up in the UI. - Calling
super().__init__(**kwargs)instead ofsuper().__init__()— NiFi crashes with aTypeError. - Missing the
Javainner class — NiFi won't discover the processor at all. - Importing Qiskit at the top of the file — may fail on first load before the venv is ready.
- Missing an optional Qiskit sub-package — error only appears at runtime when the feature is called, not at load time.