@rosepetal/node-red-contrib-python-executor
v1.1.0
Published
A Node-RED node for executing Python code, similar to the function node but for Python
Readme
node-red-contrib-python-executor
Python into your Node-RED flows with a node that feels familiar, stays friendly, and keeps things fast when you need it.

What You Get
- Run small Python snippets whenever a message arrives.
- Reuse your favorite Python tools without leaving Node-RED.
- Choose between quick start-up or high-speed processing.
- See clear status updates so you always know what is happening.
Before You Start
- Install Python 3 on the machine running Node-RED.
- Know where your Python interpreter lives (for most systems it is
python3).
Node Options
- Name – Optional label that appears on the canvas.
- Python Path – The command Node-RED should run (for example
python3or a full path if you use a virtual environment). - Timeout – How long to wait for Python to finish before giving up. Useful to stop code that gets stuck.
- Python Code – Your script. It always receives a message named
msg, and whatever you return is passed along. - Hot Mode – Keeps Python processes running between messages for faster responses.
- Workers – How many hot workers to run in parallel (only appears when Hot Mode is on). Use more workers when you expect bursts of messages.
- Preload Imports – Optional lines that run once when each hot worker starts. Handy for heavier libraries you do not want to import on every message.
Cold vs Hot Mode
- Cold Mode (default) – Starts a fresh Python process for every message. Great for occasional runs or quick experiments.
- Hot Mode – Keeps a pool of Python workers ready. Messages are handled much faster after the first one. Best for frequent or time-sensitive flows.
Typical Flow
- Drop the python executor node into your flow.
- Connect an Inject node (input) and a Debug node (output).
- Add a short script, for example:
msg['payload'] = f"Hello, {msg.get('payload', 'world')}!" return msg - Deploy and trigger the flow. Adjust options as needed.
Context & Warnings
You can read/write Node-RED context from Python and emit warnings:
count = flow_ctx.get("count", 0)
flow_ctx.set("count", count + 1)
node.warn(f"count is now {count + 1}")
return msg- Use
flow_ctxfor flow context andglobal_ctxfor global context. - Context values should be JSON-serializable.
Working with Images
Rosepetal images travel as a dict {data, width, height, channels, colorSpace, dtype}. Two helpers are injected into every node automatically — no import or boilerplate — so you can round-trip through OpenCV/NumPy:
import cv2
img = rp_to_cv(msg["payload"]) # -> BGR uint8 ndarray, ready for cv2
img = cv2.GaussianBlur(img, (7, 7), 0)
msg["payload"] = rp_from_cv(img, like=msg["payload"])
return msgrp_to_cv(obj)returns a NumPy array. RGB/RGBA inputs are reordered to BGR/BGRA (OpenCV's convention); grayscale stays 2-D. It honorscolorSpaceanddtype, and acceptsdataas raw bytes, a Node Buffer, a list, or base64.rp_from_cv(arr, like=obj)returns a Rosepetal image dict. The array is assumed BGR and reordered back to RGB. Pass the source dict aslike=to preserve itscolorSpacelabel.datacomes back as raw bytes, so it rides the shared-memory fast path in hot mode (no base64 bloat).- Both helpers work in hot and cold mode. Library imports do not: cold mode starts a fresh interpreter per
message, so
import cv2belongs at the top of your code and Preload Imports applies to hot mode only. Re-importing numpy + OpenCV costs roughly 100 ms per message, so use hot mode for image work.
Benefits At A Glance
- Familiar: Works just like the standard function node, only in Python.
- Flexible: Supports both simple scripts and larger libraries.
- Fast: Hot mode cuts response time dramatically for repeat work.
- Clear: Status messages and notifications help you see what is working and what needs attention.
- Binary Friendly: Hot workers stream large Buffers through shared memory instead of JSON, so images and other blobs stay fast.
Handling Large Binary Payloads
- Hot workers stream Buffers to Python as raw bytes inside the worker's binary frame protocol, so image data never touches JSON or base64 and never hits the filesystem.
- Python can return
bytes,bytearray, ormemoryview; they come back the same way and Node converts them to Buffers automatically. This also applies in cold mode. - The optimization is automatic—just enable hot mode when you expect heavy binary traffic.
- Set
ROSEPETAL_PY_INLINE_MAX_BYTES=<bytes>in Node-RED's environment to route buffers at or above that size through a/dev/shmfile instead of the pipe (measured slower on Linux, but available if your platform prefers it).
Performance Notes (hot mode)
The hot path is built to stay off Node-RED's event loop and to cost as little as possible per message:
- One binary frame per direction (
u32 length | JSON header | raw blobs). Nothing is base64-encoded, no temporary files are written, and the user code is shipped to each worker once and cached by id. - The message is walked once, copy-on-write, so a message without Buffers costs zero allocations before
JSON.stringify. - Nothing awaits: for a normal message the whole Node side of a round trip is synchronous work of a few microseconds; there are no promises created per key or per value.
- Node status updates are coalesced (at most one every 50 ms per node) so the editor websocket and Status nodes are not flooded at high message rates. The texts are unchanged:
hot: running,hot: 3ms,hot: ready. - Workers start with glibc malloc thresholds pinned (
MALLOC_MMAP_THRESHOLD_,MALLOC_TRIM_THRESHOLD_,MALLOC_TOP_PAD_) so multi-megabyte image buffers are reused from the heap instead of being page-faulted in on every message. SetROSEPETAL_PY_MALLOC_TUNING=0to disable, or set the variables yourself to override. rp_to_cv/rp_from_cvusecv2.cvtColorfor the RGB↔BGR swap when OpenCV is installed (about 100x faster than the NumPy reversed view on a 720p frame; identical bytes).print()inside hot-mode code goes to the worker's stderr (visible in the Node-RED log) and can no longer corrupt the worker protocol.
Typical round trips measured on a laptop (Node-RED → Python → Node-RED, one worker): a small dict in ~0.1 ms, a 200-item JSON payload in ~0.5 ms, a 16 KB buffer in ~0.13 ms, a 1280×720 RGB image through rp_to_cv/rp_from_cv in ~3 ms. The remaining floor for tiny messages is the two process wake-ups of a pipe round trip; on a machine with the powersave CPU governor those wake-ups are noticeably slower than with performance.
Enjoy mixing Python logic into your Node-RED projects without extra fuss. When you are ready for more performance, flip on Hot Mode and keep building.
