pynode-flow 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pynode_flow-0.2.0/.dockerignore +23 -0
- pynode_flow-0.2.0/.github/workflows/ci.yml +44 -0
- pynode_flow-0.2.0/.github/workflows/publish.yaml +60 -0
- pynode_flow-0.2.0/.gitignore +193 -0
- pynode_flow-0.2.0/DOCKER.md +113 -0
- pynode_flow-0.2.0/Dockerfile +66 -0
- pynode_flow-0.2.0/Dockerfile.cpu +49 -0
- pynode_flow-0.2.0/INSTALL.md +145 -0
- pynode_flow-0.2.0/LICENSE +21 -0
- pynode_flow-0.2.0/MANIFEST.in +7 -0
- pynode_flow-0.2.0/PKG-INFO +520 -0
- pynode_flow-0.2.0/README.md +453 -0
- pynode_flow-0.2.0/docker-compose.yml +32 -0
- pynode_flow-0.2.0/docs/CREATING_NODES.md +695 -0
- pynode_flow-0.2.0/docs/CUSTOM_NODES.md +611 -0
- pynode_flow-0.2.0/docs/DOCKER.md +398 -0
- pynode_flow-0.2.0/docs/EXTENSIBILITY.md +612 -0
- pynode_flow-0.2.0/docs/UI_COMPONENTS.md +560 -0
- pynode_flow-0.2.0/examples/README.md +117 -0
- pynode_flow-0.2.0/examples/camera_workflow.py +196 -0
- pynode_flow-0.2.0/examples/camera_yolo_workflow.py +228 -0
- pynode_flow-0.2.0/install_nodes.bat +58 -0
- pynode_flow-0.2.0/install_nodes.sh +50 -0
- pynode_flow-0.2.0/plan-pynodeRepoReview.prompt.md +117 -0
- pynode_flow-0.2.0/pynode/__init__.py +37 -0
- pynode_flow-0.2.0/pynode/__main__.py +9 -0
- pynode_flow-0.2.0/pynode/_perf.py +65 -0
- pynode_flow-0.2.0/pynode/_version.py +24 -0
- pynode_flow-0.2.0/pynode/api/__init__.py +15 -0
- pynode_flow-0.2.0/pynode/api/helpers.py +79 -0
- pynode_flow-0.2.0/pynode/api/nodes.py +423 -0
- pynode_flow-0.2.0/pynode/api/services.py +124 -0
- pynode_flow-0.2.0/pynode/api/sse.py +58 -0
- pynode_flow-0.2.0/pynode/api/uploads.py +59 -0
- pynode_flow-0.2.0/pynode/api/workflows.py +466 -0
- pynode_flow-0.2.0/pynode/config.py +93 -0
- pynode_flow-0.2.0/pynode/install_nodes.py +143 -0
- pynode_flow-0.2.0/pynode/main.py +118 -0
- pynode_flow-0.2.0/pynode/node_registry.py +156 -0
- pynode_flow-0.2.0/pynode/nodes/AutoSyncNode/auto_sync_node.py +151 -0
- pynode_flow-0.2.0/pynode/nodes/BBoxMetricsNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/BBoxMetricsNode/bbox_metrics_node.py +250 -0
- pynode_flow-0.2.0/pynode/nodes/BBoxMetricsNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/BatchNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/BatchNode/batch_node.py +86 -0
- pynode_flow-0.2.0/pynode/nodes/BatchNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/CameraNode/__init__.py +4 -0
- pynode_flow-0.2.0/pynode/nodes/CameraNode/camera_node.py +204 -0
- pynode_flow-0.2.0/pynode/nodes/CameraNode/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/ChangeNode/__init__.py +1 -0
- pynode_flow-0.2.0/pynode/nodes/ChangeNode/change_node.py +255 -0
- pynode_flow-0.2.0/pynode/nodes/ChangeNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/ConfidenceFilterNode/__init__.py +4 -0
- pynode_flow-0.2.0/pynode/nodes/ConfidenceFilterNode/confidence_filter_node.py +208 -0
- pynode_flow-0.2.0/pynode/nodes/ConfidenceFilterNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/CounterNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/CounterNode/counter_node.py +191 -0
- pynode_flow-0.2.0/pynode/nodes/CropNode/__init__.py +4 -0
- pynode_flow-0.2.0/pynode/nodes/CropNode/crop_node.py +317 -0
- pynode_flow-0.2.0/pynode/nodes/CropNode/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/DebugNode/__init__.py +4 -0
- pynode_flow-0.2.0/pynode/nodes/DebugNode/debug_node.py +169 -0
- pynode_flow-0.2.0/pynode/nodes/DebugNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/DelayNode/delay_node.py +218 -0
- pynode_flow-0.2.0/pynode/nodes/DelayNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/DrawPredictionsNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/DrawPredictionsNode/draw_predictions_node.py +365 -0
- pynode_flow-0.2.0/pynode/nodes/DrawPredictionsNode/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/ErrorNode/error_node.py +94 -0
- pynode_flow-0.2.0/pynode/nodes/ErrorNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/FilterNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/FilterNode/filter_node.py +102 -0
- pynode_flow-0.2.0/pynode/nodes/FilterNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/FrameSourceNode/__init__.py +4 -0
- pynode_flow-0.2.0/pynode/nodes/FrameSourceNode/frame_source_node.py +319 -0
- pynode_flow-0.2.0/pynode/nodes/FrameSourceNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/FunctionNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/FunctionNode/function_node.py +168 -0
- pynode_flow-0.2.0/pynode/nodes/FunctionNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/GateNode/gate_node.py +60 -0
- pynode_flow-0.2.0/pynode/nodes/GateNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/GetiUploadNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/GetiUploadNode/geti_upload_node.py +278 -0
- pynode_flow-0.2.0/pynode/nodes/GetiUploadNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/ImageFormatNode/__init__.py +4 -0
- pynode_flow-0.2.0/pynode/nodes/ImageFormatNode/image_format_node.py +310 -0
- pynode_flow-0.2.0/pynode/nodes/ImageFormatNode/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/ImageUploadNode/image_upload_node.py +96 -0
- pynode_flow-0.2.0/pynode/nodes/ImageViewerNode/__init__.py +4 -0
- pynode_flow-0.2.0/pynode/nodes/ImageViewerNode/image_viewer_node.py +162 -0
- pynode_flow-0.2.0/pynode/nodes/ImageViewerNode/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/ImageWriterNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/ImageWriterNode/imagewriter_node.py +352 -0
- pynode_flow-0.2.0/pynode/nodes/ImageWriterNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/__init__.py +15 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/device_detection.py +149 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/engines/base_engine.py +155 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/engines/example_engine_template.py +272 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/engines/geti_engine.py +309 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/engines/onnx_engine.py +346 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/engines/pass_engine.py +86 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/engines/ultralytics_engine.py +863 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/inference_engine_factory.py +533 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/readme.md +107 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/InferenceEngine/result_converters.py +121 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/inference_node.py +451 -0
- pynode_flow-0.2.0/pynode/nodes/InferenceNode/requirements.txt +13 -0
- pynode_flow-0.2.0/pynode/nodes/InjectNode/inject_node.py +245 -0
- pynode_flow-0.2.0/pynode/nodes/InjectNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/JoinNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/JoinNode/join_node.py +156 -0
- pynode_flow-0.2.0/pynode/nodes/JoinNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/LabelFilterNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/LabelFilterNode/label_filter_node.py +188 -0
- pynode_flow-0.2.0/pynode/nodes/LabelFilterNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/MQTTNode/__init__.py +8 -0
- pynode_flow-0.2.0/pynode/nodes/MQTTNode/mqtt_in_node.py +148 -0
- pynode_flow-0.2.0/pynode/nodes/MQTTNode/mqtt_out_node.py +157 -0
- pynode_flow-0.2.0/pynode/nodes/MQTTNode/mqtt_service.py +367 -0
- pynode_flow-0.2.0/pynode/nodes/MQTTNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/MessageWriterNode/__init__.py +7 -0
- pynode_flow-0.2.0/pynode/nodes/MessageWriterNode/messagereader_node.py +568 -0
- pynode_flow-0.2.0/pynode/nodes/MessageWriterNode/messagewriter_node.py +505 -0
- pynode_flow-0.2.0/pynode/nodes/OmronCameraNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/OmronCameraNode/omron_camera_node.py +652 -0
- pynode_flow-0.2.0/pynode/nodes/OmronCameraNode/requirements.txt +4 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/__init__.py +1 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/arithmetic_node.py +376 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/bitwise_node.py +127 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/blend_node.py +127 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/blob_detector_node.py +233 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/blur_node.py +141 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/color_space_node.py +139 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/colormap_node.py +262 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/contours_node.py +258 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/denormalize_coords_node.py +365 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/draw_node.py +312 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/edge_detector_node.py +177 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/equirectangular_node.py +447 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/fft_node.py +102 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/focus_estimation_node.py +189 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/frequency_filter_node.py +241 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/histogram_node.py +160 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/hough_circles_node.py +224 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/hough_lines_node.py +243 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/in_range_node.py +186 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/morphology_node.py +136 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/normalize_coords_node.py +344 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/paste_node.py +244 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/perspective_node.py +174 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/realsense_depth_node.py +253 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/resize_node.py +175 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/rotate_node.py +159 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/template_match_node.py +227 -0
- pynode_flow-0.2.0/pynode/nodes/OpenCV/threshold_node.py +167 -0
- pynode_flow-0.2.0/pynode/nodes/PointInShapeNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/PointInShapeNode/point_in_shape_node.py +313 -0
- pynode_flow-0.2.0/pynode/nodes/PointInShapeNode/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/PolygonMetricsNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/PolygonMetricsNode/polygon_metrics_node.py +331 -0
- pynode_flow-0.2.0/pynode/nodes/PolygonMetricsNode/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/QueueLengthProbeNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/QueueLengthProbeNode/queue_length_probe_node.py +116 -0
- pynode_flow-0.2.0/pynode/nodes/Qwen3VLMNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/Qwen3VLMNode/qwen3_vlm_node.py +519 -0
- pynode_flow-0.2.0/pynode/nodes/Qwen3VLMNode/requirements.txt +5 -0
- pynode_flow-0.2.0/pynode/nodes/RESTEndpointNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/RESTEndpointNode/rest_endpoint_node.py +491 -0
- pynode_flow-0.2.0/pynode/nodes/RangeNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/RangeNode/range_node.py +115 -0
- pynode_flow-0.2.0/pynode/nodes/RangeNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/RateProbeNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/RateProbeNode/rate_probe_node.py +183 -0
- pynode_flow-0.2.0/pynode/nodes/RateProbeNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/RoboflowUploadNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/RoboflowUploadNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/RoboflowUploadNode/roboflow_upload_node.py +229 -0
- pynode_flow-0.2.0/pynode/nodes/SliceNode/README.md +149 -0
- pynode_flow-0.2.0/pynode/nodes/SliceNode/__init__.py +5 -0
- pynode_flow-0.2.0/pynode/nodes/SliceNode/merge_predictions_node.py +444 -0
- pynode_flow-0.2.0/pynode/nodes/SliceNode/requirements.txt +3 -0
- pynode_flow-0.2.0/pynode/nodes/SliceNode/slice_collector_node.py +370 -0
- pynode_flow-0.2.0/pynode/nodes/SliceNode/slice_image_node.py +380 -0
- pynode_flow-0.2.0/pynode/nodes/SplitNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/SplitNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/SplitNode/split_node.py +155 -0
- pynode_flow-0.2.0/pynode/nodes/SwitchNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/SwitchNode/switch_node.py +259 -0
- pynode_flow-0.2.0/pynode/nodes/SyncNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/SyncNode/sync_node.py +287 -0
- pynode_flow-0.2.0/pynode/nodes/TemplateNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/TemplateNode/template_node.py +86 -0
- pynode_flow-0.2.0/pynode/nodes/TrackerNode/__init__.py +2 -0
- pynode_flow-0.2.0/pynode/nodes/TrackerNode/supervision_tracker_node.py +248 -0
- pynode_flow-0.2.0/pynode/nodes/TrackerNode/tracker_node.py +745 -0
- pynode_flow-0.2.0/pynode/nodes/UltralyticsNode/__init__.py +4 -0
- pynode_flow-0.2.0/pynode/nodes/UltralyticsNode/requirements.txt +2 -0
- pynode_flow-0.2.0/pynode/nodes/UltralyticsNode/ultralytics_node.py +364 -0
- pynode_flow-0.2.0/pynode/nodes/UnknownNode/__init__.py +93 -0
- pynode_flow-0.2.0/pynode/nodes/VLLMNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/VLLMNode/vllm_node.py +345 -0
- pynode_flow-0.2.0/pynode/nodes/VideoReaderNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/VideoReaderNode/video_reader_node.py +425 -0
- pynode_flow-0.2.0/pynode/nodes/VideoWriterNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/VideoWriterNode/video_writer_node.py +441 -0
- pynode_flow-0.2.0/pynode/nodes/WebhookNode/__init__.py +3 -0
- pynode_flow-0.2.0/pynode/nodes/WebhookNode/webhook_node.py +591 -0
- pynode_flow-0.2.0/pynode/nodes/__init__.py +109 -0
- pynode_flow-0.2.0/pynode/nodes/base_node.py +562 -0
- pynode_flow-0.2.0/pynode/nodes/image_utils.py +291 -0
- pynode_flow-0.2.0/pynode/nodes/info.py +109 -0
- pynode_flow-0.2.0/pynode/nodes/mDNSNode/__init__.py +13 -0
- pynode_flow-0.2.0/pynode/nodes/mDNSNode/mdns_broadcast_node.py +214 -0
- pynode_flow-0.2.0/pynode/nodes/mDNSNode/mdns_discovery_node.py +362 -0
- pynode_flow-0.2.0/pynode/nodes/mDNSNode/mdns_manager.py +399 -0
- pynode_flow-0.2.0/pynode/nodes/mDNSNode/requirements.txt +1 -0
- pynode_flow-0.2.0/pynode/nodes/messages.py +81 -0
- pynode_flow-0.2.0/pynode/server.py +187 -0
- pynode_flow-0.2.0/pynode/static/images/favicon4.png +0 -0
- pynode_flow-0.2.0/pynode/static/index.html +200 -0
- pynode_flow-0.2.0/pynode/static/js/README.md +139 -0
- pynode_flow-0.2.0/pynode/static/js/auth.js +89 -0
- pynode_flow-0.2.0/pynode/static/js/clipboard.js +152 -0
- pynode_flow-0.2.0/pynode/static/js/config.js +14 -0
- pynode_flow-0.2.0/pynode/static/js/connections.js +844 -0
- pynode_flow-0.2.0/pynode/static/js/debug.js +524 -0
- pynode_flow-0.2.0/pynode/static/js/events.js +775 -0
- pynode_flow-0.2.0/pynode/static/js/history.js +199 -0
- pynode_flow-0.2.0/pynode/static/js/main.js +55 -0
- pynode_flow-0.2.0/pynode/static/js/minimap.js +242 -0
- pynode_flow-0.2.0/pynode/static/js/nodes.js +995 -0
- pynode_flow-0.2.0/pynode/static/js/palette.js +255 -0
- pynode_flow-0.2.0/pynode/static/js/properties.js +1270 -0
- pynode_flow-0.2.0/pynode/static/js/selection.js +241 -0
- pynode_flow-0.2.0/pynode/static/js/state.js +211 -0
- pynode_flow-0.2.0/pynode/static/js/ui-utils.js +28 -0
- pynode_flow-0.2.0/pynode/static/js/viewport.js +204 -0
- pynode_flow-0.2.0/pynode/static/js/workflow.js +469 -0
- pynode_flow-0.2.0/pynode/static/js/workflows.js +344 -0
- pynode_flow-0.2.0/pynode/static/style.css +2751 -0
- pynode_flow-0.2.0/pynode/workflow_engine.py +483 -0
- pynode_flow-0.2.0/pynode/workflow_manager.py +440 -0
- pynode_flow-0.2.0/pynode_flow.egg-info/PKG-INFO +520 -0
- pynode_flow-0.2.0/pynode_flow.egg-info/SOURCES.txt +275 -0
- pynode_flow-0.2.0/pynode_flow.egg-info/dependency_links.txt +1 -0
- pynode_flow-0.2.0/pynode_flow.egg-info/entry_points.txt +3 -0
- pynode_flow-0.2.0/pynode_flow.egg-info/requires.txt +52 -0
- pynode_flow-0.2.0/pynode_flow.egg-info/scm_file_list.json +270 -0
- pynode_flow-0.2.0/pynode_flow.egg-info/scm_version.json +8 -0
- pynode_flow-0.2.0/pynode_flow.egg-info/top_level.txt +1 -0
- pynode_flow-0.2.0/pyproject.toml +180 -0
- pynode_flow-0.2.0/requirements.txt +13 -0
- pynode_flow-0.2.0/setup.bat +165 -0
- pynode_flow-0.2.0/setup.cfg +4 -0
- pynode_flow-0.2.0/setup.py +11 -0
- pynode_flow-0.2.0/setup.sh +83 -0
- pynode_flow-0.2.0/tests/__init__.py +0 -0
- pynode_flow-0.2.0/tests/conftest.py +109 -0
- pynode_flow-0.2.0/tests/test_api_auth.py +199 -0
- pynode_flow-0.2.0/tests/test_api_errors.py +93 -0
- pynode_flow-0.2.0/tests/test_api_nodes.py +261 -0
- pynode_flow-0.2.0/tests/test_api_security.py +153 -0
- pynode_flow-0.2.0/tests/test_api_workflows.py +565 -0
- pynode_flow-0.2.0/tests/test_base_node_messaging.py +456 -0
- pynode_flow-0.2.0/tests/test_concurrency.py +104 -0
- pynode_flow-0.2.0/tests/test_config.py +137 -0
- pynode_flow-0.2.0/tests/test_counter_sse.py +60 -0
- pynode_flow-0.2.0/tests/test_device_detection.py +295 -0
- pynode_flow-0.2.0/tests/test_message_reader_pickle.py +77 -0
- pynode_flow-0.2.0/tests/test_node_registration.py +36 -0
- pynode_flow-0.2.0/tests/test_node_types_api.py +125 -0
- pynode_flow-0.2.0/tests/test_persistence.py +125 -0
- pynode_flow-0.2.0/tests/test_serialization.py +118 -0
- pynode_flow-0.2.0/tests/test_video_reader_node.py +304 -0
- pynode_flow-0.2.0/tests/test_workflow_engine.py +98 -0
- pynode_flow-0.2.0/tests/test_workflow_roundtrip.py +230 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Docker ignore file
|
|
2
|
+
.venv/
|
|
3
|
+
appenv/
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.pyc
|
|
6
|
+
*.pyo
|
|
7
|
+
*.pyd
|
|
8
|
+
.Python
|
|
9
|
+
*.so
|
|
10
|
+
*.egg
|
|
11
|
+
*.egg-info
|
|
12
|
+
dist/
|
|
13
|
+
build/
|
|
14
|
+
.git/
|
|
15
|
+
.gitignore
|
|
16
|
+
.vscode/
|
|
17
|
+
.idea/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
*~
|
|
21
|
+
.DS_Store
|
|
22
|
+
logs/
|
|
23
|
+
*.log
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.11"
|
|
16
|
+
cache: pip
|
|
17
|
+
cache-dependency-path: pyproject.toml
|
|
18
|
+
|
|
19
|
+
- name: Install system libraries for OpenCV
|
|
20
|
+
run: sudo apt-get update && sudo apt-get install -y libgl1 libglib2.0-0
|
|
21
|
+
|
|
22
|
+
- name: Install package
|
|
23
|
+
run: pip install -e .[dev]
|
|
24
|
+
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: pytest -q
|
|
27
|
+
|
|
28
|
+
typecheck:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
continue-on-error: true
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- uses: actions/setup-python@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.11"
|
|
37
|
+
cache: pip
|
|
38
|
+
cache-dependency-path: pyproject.toml
|
|
39
|
+
|
|
40
|
+
- name: Install package and pyright
|
|
41
|
+
run: pip install -e .[dev] pyright
|
|
42
|
+
|
|
43
|
+
- name: Run pyright
|
|
44
|
+
run: pyright
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
# Manual runs must be dispatched from a tag ref (e.g. v0.3.0), otherwise
|
|
8
|
+
# setuptools_scm produces a dev version that PyPI rejects.
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
name: Build distribution
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
# setuptools_scm derives the version from git tags, so it needs the
|
|
19
|
+
# full history and the tags (a shallow checkout would break it).
|
|
20
|
+
fetch-depth: 0
|
|
21
|
+
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.11"
|
|
25
|
+
|
|
26
|
+
- name: Build sdist and wheel
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade build
|
|
29
|
+
python -m build
|
|
30
|
+
|
|
31
|
+
- name: Check metadata
|
|
32
|
+
run: |
|
|
33
|
+
python -m pip install --upgrade twine
|
|
34
|
+
twine check dist/*
|
|
35
|
+
|
|
36
|
+
- uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
publish:
|
|
42
|
+
name: Publish to PyPI
|
|
43
|
+
needs: build
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
# Trusted Publishing (OIDC) -- no API token stored in the repo. Configure a
|
|
46
|
+
# trusted publisher for the `pynode-flow` project on PyPI pointing at this
|
|
47
|
+
# workflow, and create a GitHub environment named `pypi`.
|
|
48
|
+
environment:
|
|
49
|
+
name: pypi
|
|
50
|
+
url: https://pypi.org/p/pynode-flow
|
|
51
|
+
permissions:
|
|
52
|
+
id-token: write
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/download-artifact@v4
|
|
55
|
+
with:
|
|
56
|
+
name: dist
|
|
57
|
+
path: dist/
|
|
58
|
+
|
|
59
|
+
- name: Publish to PyPI
|
|
60
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
PIPFILE.lock
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Installer logs
|
|
34
|
+
pip-log.txt
|
|
35
|
+
pip-delete-this-directory.txt
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
*.py,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
cover/
|
|
51
|
+
|
|
52
|
+
# Translations
|
|
53
|
+
*.mo
|
|
54
|
+
*.pot
|
|
55
|
+
|
|
56
|
+
# Django stuff:
|
|
57
|
+
*.log
|
|
58
|
+
local_settings.py
|
|
59
|
+
db.sqlite3
|
|
60
|
+
db.sqlite3-journal
|
|
61
|
+
|
|
62
|
+
# Flask stuff:
|
|
63
|
+
instance/
|
|
64
|
+
.webassets-cache
|
|
65
|
+
|
|
66
|
+
# Scrapy stuff:
|
|
67
|
+
.scrapy
|
|
68
|
+
|
|
69
|
+
# Sphinx documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
|
|
72
|
+
# PyBuilder
|
|
73
|
+
.pybuilder/
|
|
74
|
+
target/
|
|
75
|
+
|
|
76
|
+
# Jupyter Notebook
|
|
77
|
+
.ipynb_checkpoints
|
|
78
|
+
|
|
79
|
+
# IPython
|
|
80
|
+
profile_default/
|
|
81
|
+
ipython_config.py
|
|
82
|
+
|
|
83
|
+
# pyenv
|
|
84
|
+
.python-version
|
|
85
|
+
|
|
86
|
+
# pipenv
|
|
87
|
+
Pipfile.lock
|
|
88
|
+
|
|
89
|
+
# poetry
|
|
90
|
+
poetry.lock
|
|
91
|
+
|
|
92
|
+
# pdm
|
|
93
|
+
.pdm.toml
|
|
94
|
+
|
|
95
|
+
# PEP 582
|
|
96
|
+
__pypackages__/
|
|
97
|
+
|
|
98
|
+
# Celery stuff
|
|
99
|
+
celerybeat-schedule
|
|
100
|
+
celerybeat.pid
|
|
101
|
+
|
|
102
|
+
# SageMath parsed files
|
|
103
|
+
*.sage.py
|
|
104
|
+
|
|
105
|
+
# Environments
|
|
106
|
+
.env
|
|
107
|
+
.venv
|
|
108
|
+
appenv/
|
|
109
|
+
env/
|
|
110
|
+
venv/
|
|
111
|
+
ENV/
|
|
112
|
+
env.bak/
|
|
113
|
+
venv.bak/
|
|
114
|
+
|
|
115
|
+
# Spyder project settings
|
|
116
|
+
.spyderproject
|
|
117
|
+
.spyproject
|
|
118
|
+
|
|
119
|
+
# Rope project settings
|
|
120
|
+
.ropeproject
|
|
121
|
+
|
|
122
|
+
# mkdocs documentation
|
|
123
|
+
/site
|
|
124
|
+
|
|
125
|
+
# mypy
|
|
126
|
+
.mypy_cache/
|
|
127
|
+
.dmypy.json
|
|
128
|
+
dmypy.json
|
|
129
|
+
|
|
130
|
+
# Pyre type checker
|
|
131
|
+
.pyre/
|
|
132
|
+
|
|
133
|
+
# pytype static type analyzer
|
|
134
|
+
.pytype/
|
|
135
|
+
|
|
136
|
+
# Cython debug symbols
|
|
137
|
+
cython_debug/
|
|
138
|
+
|
|
139
|
+
# IDE
|
|
140
|
+
.vscode/
|
|
141
|
+
.idea/
|
|
142
|
+
*.swp
|
|
143
|
+
*.swo
|
|
144
|
+
*~
|
|
145
|
+
|
|
146
|
+
# OS
|
|
147
|
+
.DS_Store
|
|
148
|
+
Thumbs.db
|
|
149
|
+
|
|
150
|
+
# Project specific - ignore all JSON files (workflows and backups)
|
|
151
|
+
*.json
|
|
152
|
+
|
|
153
|
+
# PyTorch model files
|
|
154
|
+
*.pt
|
|
155
|
+
models/
|
|
156
|
+
todo
|
|
157
|
+
*.bin
|
|
158
|
+
*.xml
|
|
159
|
+
*.yaml
|
|
160
|
+
# ...but always keep GitHub Actions workflows (ci.yml / publish.yaml live here)
|
|
161
|
+
!.github/
|
|
162
|
+
!.github/**
|
|
163
|
+
todo
|
|
164
|
+
|
|
165
|
+
# Media and data files
|
|
166
|
+
*.mp4
|
|
167
|
+
*.avi
|
|
168
|
+
*.mov
|
|
169
|
+
*.mkv
|
|
170
|
+
*.log
|
|
171
|
+
*.csv
|
|
172
|
+
*.db
|
|
173
|
+
*.sqlite
|
|
174
|
+
*.sqlite3
|
|
175
|
+
*.h5
|
|
176
|
+
*.ckpt
|
|
177
|
+
*.tflite
|
|
178
|
+
*.pb
|
|
179
|
+
*.npy
|
|
180
|
+
*.npz
|
|
181
|
+
*.weights
|
|
182
|
+
|
|
183
|
+
# Version file generated by setuptools_scm at build time
|
|
184
|
+
pynode/_version.py
|
|
185
|
+
|
|
186
|
+
# folders
|
|
187
|
+
data/
|
|
188
|
+
models/
|
|
189
|
+
logs/
|
|
190
|
+
checkpoints/
|
|
191
|
+
output/
|
|
192
|
+
output_low/
|
|
193
|
+
output_not/
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Docker Setup for PyNode
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- Docker Engine with GPU support (NVIDIA Container Toolkit)
|
|
6
|
+
- Docker Compose v1.28+ (for GPU support)
|
|
7
|
+
- NVIDIA GPU with CUDA support
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
### Build and Run
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
docker-compose up --build
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Run in Background
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
docker-compose up -d
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### View Logs
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
docker-compose logs -f
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Stop the Application
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
docker-compose down
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
### Ports
|
|
38
|
+
|
|
39
|
+
The application runs on port `5000` by default. To change the port, edit `docker-compose.yml`:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
ports:
|
|
43
|
+
- "8080:5000" # Change 8080 to your desired port
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### GPU Support
|
|
47
|
+
|
|
48
|
+
The Docker Compose file is configured to use all available NVIDIA GPUs. To limit GPU access:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
deploy:
|
|
52
|
+
resources:
|
|
53
|
+
reservations:
|
|
54
|
+
devices:
|
|
55
|
+
- driver: nvidia
|
|
56
|
+
device_ids: ['0'] # Use only GPU 0
|
|
57
|
+
capabilities: [gpu]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Persistent Storage
|
|
61
|
+
|
|
62
|
+
The following directories are mounted for persistence:
|
|
63
|
+
- `./workflows` - Saved workflows
|
|
64
|
+
- `./logs` - Application logs
|
|
65
|
+
|
|
66
|
+
## Building
|
|
67
|
+
|
|
68
|
+
### Custom CUDA Version
|
|
69
|
+
|
|
70
|
+
To use a different CUDA version, edit the `Dockerfile`:
|
|
71
|
+
|
|
72
|
+
```dockerfile
|
|
73
|
+
FROM nvidia/cuda:12.8.0-runtime-ubuntu22.04 # Change CUDA version
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Without GPU Support
|
|
77
|
+
|
|
78
|
+
For CPU-only deployment, use the CPU Dockerfile:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
docker build -f Dockerfile.cpu -t pynode-cpu .
|
|
82
|
+
docker run -p 5000:5000 pynode-cpu
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Accessing the Application
|
|
86
|
+
|
|
87
|
+
Once running, access the web interface at:
|
|
88
|
+
- http://localhost:5000
|
|
89
|
+
|
|
90
|
+
## Troubleshooting
|
|
91
|
+
|
|
92
|
+
### GPU Not Detected
|
|
93
|
+
|
|
94
|
+
Verify NVIDIA Container Toolkit is installed:
|
|
95
|
+
```bash
|
|
96
|
+
docker run --rm --gpus all nvidia/cuda:12.6.0-base-ubuntu22.04 nvidia-smi
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Permission Issues
|
|
100
|
+
|
|
101
|
+
Ensure the mounted directories have proper permissions:
|
|
102
|
+
```bash
|
|
103
|
+
mkdir -p workflows logs
|
|
104
|
+
chmod 777 workflows logs
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Rebuild from Scratch
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
docker-compose down
|
|
111
|
+
docker-compose build --no-cache
|
|
112
|
+
docker-compose up
|
|
113
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
FROM docker.io/nvidia/cuda:12.6.0-devel-ubuntu22.04
|
|
2
|
+
|
|
3
|
+
# Set environment variables
|
|
4
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
5
|
+
ENV PYTHONUNBUFFERED=1
|
|
6
|
+
ENV CUDA_VERSION=12.6
|
|
7
|
+
|
|
8
|
+
# Install system dependencies
|
|
9
|
+
RUN apt-get update && apt-get install -y \
|
|
10
|
+
python3.10 \
|
|
11
|
+
python3-pip \
|
|
12
|
+
python3-venv \
|
|
13
|
+
git \
|
|
14
|
+
wget \
|
|
15
|
+
gfortran \
|
|
16
|
+
libgl1-mesa-glx \
|
|
17
|
+
libgtk-3-0 \
|
|
18
|
+
libglib2.0-0 \
|
|
19
|
+
libsm6 \
|
|
20
|
+
libxext6 \
|
|
21
|
+
libxrender-dev \
|
|
22
|
+
libgomp1 \
|
|
23
|
+
libavcodec-dev \
|
|
24
|
+
libavformat-dev \
|
|
25
|
+
libswscale-dev \
|
|
26
|
+
libv4l-dev \
|
|
27
|
+
libxvidcore-dev \
|
|
28
|
+
libx264-dev \
|
|
29
|
+
libjpeg-dev \
|
|
30
|
+
libpng-dev \
|
|
31
|
+
libtiff-dev \
|
|
32
|
+
libopenblas-dev \
|
|
33
|
+
liblapack-dev \
|
|
34
|
+
portaudio19-dev \
|
|
35
|
+
libasound2-dev \
|
|
36
|
+
libusb-1.0-0 \
|
|
37
|
+
libusb-1.0-0-dev \
|
|
38
|
+
libudev-dev \
|
|
39
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
40
|
+
|
|
41
|
+
# Set working directory
|
|
42
|
+
WORKDIR /app
|
|
43
|
+
|
|
44
|
+
# Copy application files
|
|
45
|
+
COPY . /app/
|
|
46
|
+
|
|
47
|
+
# Make scripts executable
|
|
48
|
+
RUN chmod +x /app/setup.sh /app/install_nodes.sh
|
|
49
|
+
|
|
50
|
+
# Run setup script
|
|
51
|
+
RUN bash -c "source /app/setup.sh < /dev/null"
|
|
52
|
+
|
|
53
|
+
# Create a non-root user and give it ownership of the app directory
|
|
54
|
+
RUN useradd --create-home --uid 1000 appuser \
|
|
55
|
+
&& chown -R appuser:appuser /app
|
|
56
|
+
USER appuser
|
|
57
|
+
|
|
58
|
+
# Expose port
|
|
59
|
+
EXPOSE 5000
|
|
60
|
+
|
|
61
|
+
# Health check (uses the bundled Python interpreter; no extra tools required)
|
|
62
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
|
|
63
|
+
CMD ["/app/appenv/bin/python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/')"]
|
|
64
|
+
|
|
65
|
+
# Run the application in production mode
|
|
66
|
+
CMD ["/app/appenv/bin/python", "-m", "pynode", "--production", "--host", "0.0.0.0", "--port", "5000"]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
FROM python:3.10-slim
|
|
2
|
+
|
|
3
|
+
# Set environment variables
|
|
4
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
5
|
+
ENV PYTHONUNBUFFERED=1
|
|
6
|
+
|
|
7
|
+
# Install system dependencies
|
|
8
|
+
RUN apt-get update && apt-get install -y \
|
|
9
|
+
libgl1-mesa-glx \
|
|
10
|
+
libglib2.0-0 \
|
|
11
|
+
libsm6 \
|
|
12
|
+
libxext6 \
|
|
13
|
+
libxrender-dev \
|
|
14
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
15
|
+
|
|
16
|
+
# Set working directory
|
|
17
|
+
WORKDIR /app
|
|
18
|
+
|
|
19
|
+
# Copy application files
|
|
20
|
+
COPY . /app/
|
|
21
|
+
|
|
22
|
+
# Create virtual environment
|
|
23
|
+
RUN python3 -m venv appenv
|
|
24
|
+
|
|
25
|
+
# Activate venv and install dependencies (declared in pyproject.toml;
|
|
26
|
+
# CPU-only torch is installed first so the vision extra reuses it)
|
|
27
|
+
RUN . appenv/bin/activate && \
|
|
28
|
+
pip install --upgrade pip && \
|
|
29
|
+
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu && \
|
|
30
|
+
pip install -e ".[vision,mqtt]"
|
|
31
|
+
|
|
32
|
+
# Install per-node dependencies (best-effort: a node whose vendor SDK is not
|
|
33
|
+
# on PyPI, e.g. OmronCameraNode's stapipy, is skipped without failing the build)
|
|
34
|
+
RUN . appenv/bin/activate && pynode-install-nodes || true
|
|
35
|
+
|
|
36
|
+
# Create a non-root user and give it ownership of the app directory
|
|
37
|
+
RUN useradd --create-home --uid 1000 appuser \
|
|
38
|
+
&& chown -R appuser:appuser /app
|
|
39
|
+
USER appuser
|
|
40
|
+
|
|
41
|
+
# Expose port
|
|
42
|
+
EXPOSE 5000
|
|
43
|
+
|
|
44
|
+
# Health check (uses the bundled Python interpreter; no extra tools required)
|
|
45
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
|
|
46
|
+
CMD ["/app/appenv/bin/python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/')"]
|
|
47
|
+
|
|
48
|
+
# Run the application in production mode
|
|
49
|
+
CMD ["/app/appenv/bin/python", "-m", "pynode", "--production", "--host", "0.0.0.0", "--port", "5000"]
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Installation Guide
|
|
2
|
+
|
|
3
|
+
PyNode is published on PyPI as **`pynode-flow`** (the import package is still
|
|
4
|
+
`pynode`).
|
|
5
|
+
|
|
6
|
+
## Install from PyPI
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# Core install (Flask API + built-in nodes that need no extra packages)
|
|
10
|
+
pip install pynode-flow
|
|
11
|
+
|
|
12
|
+
# Everything PyPI-installable (all optional nodes)
|
|
13
|
+
pip install "pynode-flow[full]"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Nodes whose optional dependencies are missing are simply skipped at startup, so
|
|
17
|
+
a core-only install still runs fine.
|
|
18
|
+
|
|
19
|
+
### Optional extras
|
|
20
|
+
|
|
21
|
+
Install only the node groups you need:
|
|
22
|
+
|
|
23
|
+
| Extra | Installs | Nodes it enables |
|
|
24
|
+
|-------|----------|------------------|
|
|
25
|
+
| `vision` | ultralytics, torch, torchvision, supervision | UltralyticsNode, TrackerNode, DrawPredictionsNode, … |
|
|
26
|
+
| `mqtt` | paho-mqtt | MQTTNode |
|
|
27
|
+
| `camera` | framesource[full] | FrameSourceNode |
|
|
28
|
+
| `inference` | onnxruntime, geti-sdk (+ `vision`) | InferenceNode |
|
|
29
|
+
| `vlm` | transformers, qwen-vl-utils, Pillow (+ `vision`) | Qwen3VLMNode |
|
|
30
|
+
| `upload` | roboflow, geti-sdk | RoboflowUploadNode, GetiUploadNode |
|
|
31
|
+
| `discovery` | zeroconf | mDNSNode |
|
|
32
|
+
| `full` | all of the above | every PyPI-installable node |
|
|
33
|
+
| `dev` | pytest, type stubs | (development only) |
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install "pynode-flow[vision,mqtt]" # pick specific groups
|
|
37
|
+
pip install "pynode-flow[full]" # or grab everything
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
> **Note:** `pip install pynode-flow` (or any extra) installs **only** what is
|
|
41
|
+
> declared in `pyproject.toml`. It does **not** run the per-node
|
|
42
|
+
> `requirements.txt` files — those are covered by the extras above, or by the
|
|
43
|
+
> `pynode-install-nodes` command below.
|
|
44
|
+
|
|
45
|
+
## Development Installation
|
|
46
|
+
|
|
47
|
+
Install PyNode from a source checkout in editable mode (recommended for
|
|
48
|
+
development):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Clone or navigate to the project directory
|
|
52
|
+
cd pynode
|
|
53
|
+
|
|
54
|
+
# Install in editable mode (core dependencies only)
|
|
55
|
+
pip install -e .
|
|
56
|
+
|
|
57
|
+
# Optional extras (same names as above):
|
|
58
|
+
pip install -e ".[vision]" # ML vision nodes
|
|
59
|
+
pip install -e ".[mqtt]" # MQTT node
|
|
60
|
+
pip install -e ".[full]" # everything PyPI-installable
|
|
61
|
+
pip install -e ".[dev]" # development tools (pytest, type stubs)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This installs PyNode as a package while keeping your source files editable.
|
|
65
|
+
|
|
66
|
+
> **Versioning:** the package version is derived from git tags by
|
|
67
|
+
> `setuptools_scm`. A clean checkout at tag `v0.2.0` builds as `0.2.0`; commits
|
|
68
|
+
> after a tag build as a dev version. To cut a release, tag the commit
|
|
69
|
+
> (`git tag v0.2.0 && git push --tags`) — see [Publishing](#publishing-to-pypi).
|
|
70
|
+
|
|
71
|
+
## Running PyNode
|
|
72
|
+
|
|
73
|
+
After installation, you can run PyNode in multiple ways:
|
|
74
|
+
|
|
75
|
+
### Option 1: Using the `pynode` command
|
|
76
|
+
```bash
|
|
77
|
+
pynode
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Option 2: Using Python module syntax
|
|
81
|
+
```bash
|
|
82
|
+
python -m pynode
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Option 3: With custom options
|
|
86
|
+
```bash
|
|
87
|
+
pynode --host 0.0.0.0 --port 5000 --production
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Command Line Options
|
|
91
|
+
|
|
92
|
+
- `--host`: Host address to bind to (default: 0.0.0.0)
|
|
93
|
+
- `--port`: Port to bind to (default: 5000)
|
|
94
|
+
- `--production`: Run in production mode using Waitress server
|
|
95
|
+
|
|
96
|
+
## Installing Node Dependencies
|
|
97
|
+
|
|
98
|
+
A few nodes rely on packages that cannot live in `pyproject.toml` — for example
|
|
99
|
+
the Omron/Sentech `stapipy` SDK (OmronCameraNode), which is a vendor download
|
|
100
|
+
rather than a PyPI package. Each such node ships its own `requirements.txt`.
|
|
101
|
+
|
|
102
|
+
**If you installed from PyPI**, use the bundled command to walk the installed
|
|
103
|
+
node folders and install their requirements:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pynode-install-nodes # install every node's requirements.txt
|
|
107
|
+
pynode-install-nodes --list # list nodes that have a requirements.txt
|
|
108
|
+
pynode-install-nodes --node MQTTNode --node mDNSNode # only specific nodes
|
|
109
|
+
pynode-install-nodes --dry-run # preview without installing
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**If you're working from a source checkout**, you can instead run the helper
|
|
113
|
+
scripts, which iterate the node folders in the repo:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Windows
|
|
117
|
+
install_nodes.bat
|
|
118
|
+
|
|
119
|
+
# Linux/Mac
|
|
120
|
+
./install_nodes.sh
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Publishing to PyPI
|
|
124
|
+
|
|
125
|
+
Releases are published by `.github/workflows/publish.yaml` via PyPI
|
|
126
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (OIDC — no API
|
|
127
|
+
token is stored in the repo). To release:
|
|
128
|
+
|
|
129
|
+
1. Configure a trusted publisher for the `pynode-flow` project on PyPI, pointing
|
|
130
|
+
at this repository / `publish.yaml`, and create a GitHub environment named
|
|
131
|
+
`pypi` (one-time setup).
|
|
132
|
+
2. Tag the release commit and push the tag:
|
|
133
|
+
```bash
|
|
134
|
+
git tag v0.2.0
|
|
135
|
+
git push origin v0.2.0
|
|
136
|
+
```
|
|
137
|
+
The tag drives the version via `setuptools_scm`. (A manual
|
|
138
|
+
`workflow_dispatch` run must also be dispatched from a tag ref, otherwise the
|
|
139
|
+
build produces a dev version that PyPI rejects.)
|
|
140
|
+
|
|
141
|
+
## Uninstalling
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pip uninstall pynode-flow
|
|
145
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Oliver Hamilton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|