pycforge-sdk 0.6.3__py3-none-any.whl
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.
- pycforge_sdk/__init__.py +6 -0
- pycforge_sdk/__main__.py +3 -0
- pycforge_sdk/catalogue.py +287 -0
- pycforge_sdk/cli.py +114 -0
- pycforge_sdk/connection.py +136 -0
- pycforge_sdk/discovery.py +227 -0
- pycforge_sdk/editing.py +84 -0
- pycforge_sdk/engine_launch.py +45 -0
- pycforge_sdk/engine_worker.py +53 -0
- pycforge_sdk/gui/__init__.py +1 -0
- pycforge_sdk/gui/__main__.py +3 -0
- pycforge_sdk/gui/app.py +36 -0
- pycforge_sdk/gui/dialogs.py +310 -0
- pycforge_sdk/gui/editor.py +412 -0
- pycforge_sdk/gui/help/index.html +9 -0
- pycforge_sdk/gui/identity.py +139 -0
- pycforge_sdk/gui/jobs.py +146 -0
- pycforge_sdk/gui/reference.py +39 -0
- pycforge_sdk/gui/resources/sdk-mark.png +0 -0
- pycforge_sdk/gui/scanner.py +134 -0
- pycforge_sdk/gui/startup.py +118 -0
- pycforge_sdk/gui/visual/__init__.py +1 -0
- pycforge_sdk/gui/visual/action_contract.py +550 -0
- pycforge_sdk/gui/visual/icons.py +117 -0
- pycforge_sdk/gui/visual/qt_actions.py +501 -0
- pycforge_sdk/gui/visual/qt_branding.py +47 -0
- pycforge_sdk/gui/visual/qt_dialog_chrome.py +337 -0
- pycforge_sdk/gui/visual/qt_file_icons.py +130 -0
- pycforge_sdk/gui/visual/qt_icons.py +80 -0
- pycforge_sdk/gui/visual/qt_screen_geometry.py +166 -0
- pycforge_sdk/gui/visual/qt_title_controls.py +333 -0
- pycforge_sdk/gui/visual/qt_window_chrome.py +314 -0
- pycforge_sdk/gui/visual/qt_window_resize.py +93 -0
- pycforge_sdk/gui/visual/qt_workspace_widgets.py +448 -0
- pycforge_sdk/gui/visual/resources/icons/128/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/128/whitespace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/16/whitespace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/20/whitespace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/24/whitespace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/256/whitespace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/32/whitespace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/48/whitespace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/64/whitespace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/add-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/brand-mark.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/bundle-search.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/cancel.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/check.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/chevron-right.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/close.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/collapse-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/command-palette.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/convert.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/copy.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/cut.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/decision-trace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/details.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/diagnostics.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/duplicate-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/exit.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/expand-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/export.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/find.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/go-to-line.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/go-to-output.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/go-to-source.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/help.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/history.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/indent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/link-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/mappings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/move-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/move-line-down.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/move-line-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/move-up.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/next.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/open.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/outdent.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/outline.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/paste.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/previous.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/primary-module.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/redo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/remove-document.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/replace.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/save-as.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/save.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/select-all.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/settings.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/show-c.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/split-view.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/summary.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/telemetry.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/toggle-comment.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/toggle-fold.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/tools.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/undo.png +0 -0
- pycforge_sdk/gui/visual/resources/icons/96/whitespace.png +0 -0
- pycforge_sdk/gui/visual/theme.py +204 -0
- pycforge_sdk/gui/visual/theme_steel_stylesheet.py +901 -0
- pycforge_sdk/gui/visual/theme_stylesheet.py +597 -0
- pycforge_sdk/gui/visual/theme_workspace_stylesheet.py +169 -0
- pycforge_sdk/gui/visual/visual_tokens.py +146 -0
- pycforge_sdk/gui/widgets.py +68 -0
- pycforge_sdk/gui/window.py +559 -0
- pycforge_sdk/native.py +42 -0
- pycforge_sdk/package_metadata.py +21 -0
- pycforge_sdk/process.py +58 -0
- pycforge_sdk/project.py +143 -0
- pycforge_sdk/readiness.py +106 -0
- pycforge_sdk/results.py +45 -0
- pycforge_sdk/service.py +454 -0
- pycforge_sdk/workbench.py +88 -0
- pycforge_sdk-0.6.3.dist-info/METADATA +221 -0
- pycforge_sdk-0.6.3.dist-info/RECORD +559 -0
- pycforge_sdk-0.6.3.dist-info/WHEEL +5 -0
- pycforge_sdk-0.6.3.dist-info/entry_points.txt +5 -0
- pycforge_sdk-0.6.3.dist-info/licenses/LICENSE +674 -0
- pycforge_sdk-0.6.3.dist-info/top_level.txt +1 -0
pycforge_sdk/__init__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""PyCForge SDK public API. Importing this module requires neither Qt nor PyCForge."""
|
|
2
|
+
from .results import Outcome, SDKError, SDK_VERSION as __version__
|
|
3
|
+
from .service import SDK
|
|
4
|
+
from .discovery import discover_engines
|
|
5
|
+
|
|
6
|
+
__all__ = ["SDK", "Outcome", "SDKError", "__version__", "discover_engines"]
|
pycforge_sdk/__main__.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"""Independent, headless catalogue service (no engine imports).
|
|
2
|
+
|
|
3
|
+
Adapted from the PyCForge 1.3.60 independent reference reader. XML is validated against the closed grammar below. The supplied
|
|
4
|
+
XSD is a separate validation artifact; ElementTree does not validate XSD.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
import argparse
|
|
9
|
+
import ast
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import re
|
|
15
|
+
import shutil
|
|
16
|
+
import subprocess
|
|
17
|
+
import tempfile
|
|
18
|
+
from xml.etree import ElementTree as ET
|
|
19
|
+
|
|
20
|
+
MAX_FILE = 8 * 1024 * 1024
|
|
21
|
+
MAX_TOTAL = 32 * 1024 * 1024
|
|
22
|
+
MAX_INDEX = 1024 * 1024
|
|
23
|
+
VERSIONS = ("engine_api", "extension_api", "protocol", "catalogue_schema")
|
|
24
|
+
FEATURES = {"sha256-inventory", "closed-xml-v1"}
|
|
25
|
+
SHA = re.compile(r"[0-9a-f]{64}\Z")
|
|
26
|
+
NAME = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}\Z")
|
|
27
|
+
# Independent reader grammar: required attributes and ordered children with
|
|
28
|
+
# inclusive occurrence bounds. None means unbounded (still under node limits).
|
|
29
|
+
GRAMMAR = {
|
|
30
|
+
"pycforge-api": (("schema", "engine-version", "engine-api", "extension-api", "protocol"), tuple((s,1,1) for s in ("methods","operations","types","packs","capabilities","diagnostics","resources"))),
|
|
31
|
+
"methods": ((), (("method",1,None),)),
|
|
32
|
+
"method": (("id","kind","import-path","signature","status"), (("summary",1,1),)),
|
|
33
|
+
"summary": ((), ()),
|
|
34
|
+
"operations": ((), (("operation",1,None),)),
|
|
35
|
+
"operation": (("id","version","arity","summary"), (("route",2,2),("semantic",1,None))),
|
|
36
|
+
"route": (("name","target","lowering","unsupported"), (("guard",0,None),)),
|
|
37
|
+
"guard": (("id",), ()), "semantic": ((),()),
|
|
38
|
+
"types": ((), (("type",1,None),)),
|
|
39
|
+
"type": (("id","route","representation"), (("ownership",1,1),)),
|
|
40
|
+
"ownership": (("passing","ownership","lifetime","retains-input","mutates-input","allocation","cleanup"),()),
|
|
41
|
+
"packs": ((), (("pack",1,None),)),
|
|
42
|
+
"pack": (("id","version","namespace","profile","activation","author","created","license","description","verification-producer"), (("function",1,None),("constant",0,None))),
|
|
43
|
+
"function": (("operation-id","export","native-symbol","arity","result","domain","summary"),()),
|
|
44
|
+
"constant": (("export","value"),()),
|
|
45
|
+
"capabilities": ((), (("capability",1,None),)),
|
|
46
|
+
"capability": (("id","status","detail"),()),
|
|
47
|
+
"diagnostics": ((), (("diagnostic",1,None),)),
|
|
48
|
+
"diagnostic": (("code","message"),()),
|
|
49
|
+
"resources": ((), (("resource",1,None),)),
|
|
50
|
+
"resource": (("path",),()),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class CatalogueError(ValueError):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _require(condition, message):
|
|
59
|
+
if not condition:
|
|
60
|
+
raise CatalogueError(message)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _json(data):
|
|
64
|
+
def unique(items):
|
|
65
|
+
result = {}
|
|
66
|
+
for key, value in items:
|
|
67
|
+
_require(key not in result, "Duplicate JSON key")
|
|
68
|
+
result[key] = value
|
|
69
|
+
return result
|
|
70
|
+
try:
|
|
71
|
+
return json.loads(data, object_pairs_hook=unique,
|
|
72
|
+
parse_constant=lambda _: (_ for _ in ()).throw(CatalogueError("Nonfinite JSON")))
|
|
73
|
+
except (ValueError, UnicodeError, RecursionError) as exc:
|
|
74
|
+
raise CatalogueError("Invalid manifest JSON") from exc
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _read(folder, name, limit):
|
|
78
|
+
_require(isinstance(name, str) and NAME.fullmatch(name), "Invalid inventory path")
|
|
79
|
+
path = folder / name
|
|
80
|
+
_require(not path.is_symlink() and path.is_file(), "Missing or symbolic-link resource: " + name)
|
|
81
|
+
with path.open("rb") as stream:
|
|
82
|
+
data = stream.read(limit + 1)
|
|
83
|
+
_require(len(data) <= limit, "Resource exceeds size budget: " + name)
|
|
84
|
+
return data
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def validate_xml(data, manifest):
|
|
88
|
+
_require(len(data) <= MAX_FILE, "XML exceeds size limit")
|
|
89
|
+
try:
|
|
90
|
+
text = data.decode("utf-8")
|
|
91
|
+
except UnicodeError as exc:
|
|
92
|
+
raise CatalogueError("Catalogue requires UTF-8 XML") from exc
|
|
93
|
+
# No DTDs, entity declarations, comments, secondary processing instructions,
|
|
94
|
+
# namespace/XInclude constructs, external schemas or encoding indirection.
|
|
95
|
+
_require("<!" not in text, "XML declarations/DTDs/entities/comments are forbidden")
|
|
96
|
+
body = re.sub(r"\A<\?xml version=['\"]1.0['\"] encoding=['\"]utf-8['\"]\?>", "", text, count=1)
|
|
97
|
+
_require("<?" not in body, "XML processing instructions are forbidden")
|
|
98
|
+
try:
|
|
99
|
+
root = ET.fromstring(text)
|
|
100
|
+
except (ET.ParseError, ValueError) as exc:
|
|
101
|
+
raise CatalogueError("Invalid XML") from exc
|
|
102
|
+
_require(root.tag == "pycforge-api", "Unexpected XML root")
|
|
103
|
+
stack = [(root, 1)]
|
|
104
|
+
count = 0
|
|
105
|
+
while stack:
|
|
106
|
+
node, depth = stack.pop()
|
|
107
|
+
count += 1
|
|
108
|
+
_require(depth <= 16 and count <= 50000, "XML structural budget exceeded")
|
|
109
|
+
_require(node.tag in GRAMMAR, "Unknown required XML element")
|
|
110
|
+
attrs, children = GRAMMAR[node.tag]
|
|
111
|
+
if node.tag == "function" and manifest["catalogue_schema"] in ("0.2", "0.3", "0.4"):
|
|
112
|
+
attrs = (*attrs, "errors", "integer-passthrough")
|
|
113
|
+
if node.tag == "function" and manifest["catalogue_schema"] in ("0.3", "0.4"):
|
|
114
|
+
attrs = (*attrs, "parameters", "implementation")
|
|
115
|
+
_require(set(node.attrib) == set(attrs), "Unexpected XML attributes: " + node.tag)
|
|
116
|
+
_require(node.tag in ("summary", "semantic") or not (node.text or "").strip(), "Unexpected XML text")
|
|
117
|
+
_require(not (node.tail or "").strip(), "Unexpected XML tail")
|
|
118
|
+
index = 0
|
|
119
|
+
for name, minimum, maximum in children:
|
|
120
|
+
start = index
|
|
121
|
+
while index < len(node) and node[index].tag == name:
|
|
122
|
+
index += 1
|
|
123
|
+
_require(index-start >= minimum and (maximum is None or index-start <= maximum), "Wrong XML child count: " + name)
|
|
124
|
+
_require(index == len(node), "Unexpected XML child or order: " + node.tag)
|
|
125
|
+
stack.extend((child, depth + 1) for child in node)
|
|
126
|
+
for xml_key, json_key in (("schema","catalogue_schema"),("engine-version","engine_version"),
|
|
127
|
+
("engine-api","engine_api"),("extension-api","extension_api"),("protocol","protocol")):
|
|
128
|
+
_require(root.get(xml_key) == manifest[json_key], "XML/manifest identity mismatch")
|
|
129
|
+
for parent, tag, key in (("methods","method","id"),("operations","operation","id"),
|
|
130
|
+
("packs","pack","namespace"),("capabilities","capability","id"),("diagnostics","diagnostic","code"),
|
|
131
|
+
("resources","resource","path")):
|
|
132
|
+
values = [e.get(key) for e in root.find(parent).findall(tag)]
|
|
133
|
+
_require(len(values) == len(set(values)) and all(values), "Duplicate or empty catalogue identity")
|
|
134
|
+
for operation in root.find("operations"):
|
|
135
|
+
_require({r.get("name") for r in operation.findall("route")} == {"typed","runtime"}, "Incomplete route decisions")
|
|
136
|
+
for route in operation.findall("route"):
|
|
137
|
+
_require(bool(route.get("lowering")) != bool(route.get("unsupported")), "Ambiguous route decision")
|
|
138
|
+
_require({e.get("path") for e in root.find("resources")} == {e["path"] for e in manifest["files"]}, "XML inventory mismatch")
|
|
139
|
+
return tuple(dict(m.attrib, summary=m.findtext("summary")) for m in root.find("methods"))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass(frozen=True)
|
|
143
|
+
class Snapshot:
|
|
144
|
+
identity: dict
|
|
145
|
+
files: dict[str, bytes]
|
|
146
|
+
methods: tuple[dict, ...]
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def revision(self):
|
|
150
|
+
return self.identity["manifest_sha256"]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def read_snapshot(discovery):
|
|
154
|
+
_require(type(discovery) is dict, "Expected discovery object")
|
|
155
|
+
_require(discovery.get("engine") == "PyCForge", "Unexpected engine")
|
|
156
|
+
for key in ("engine_api", "protocol"):
|
|
157
|
+
_require(discovery.get(key) == "0.1", "Incompatible " + key)
|
|
158
|
+
_require((discovery.get("extension_api"), discovery.get("catalogue_schema")) in
|
|
159
|
+
(("0.1", "0.1"), ("0.2", "0.2"), ("0.3", "0.3"), ("0.4", "0.4")), "Incompatible extension API/catalogue schema")
|
|
160
|
+
_require(type(discovery.get("folder")) is str and Path(discovery["folder"]).is_absolute(), "Discovery requires an absolute folder")
|
|
161
|
+
folder = Path(discovery["folder"])
|
|
162
|
+
index = _read(folder, "index.json", MAX_INDEX)
|
|
163
|
+
_require(hashlib.sha256(index).hexdigest() == discovery.get("manifest_sha256"), "Discovery manifest changed")
|
|
164
|
+
manifest = _json(index)
|
|
165
|
+
_require(type(manifest) is dict and set(manifest) == {"engine","engine_version",*VERSIONS,"required_features","files"}, "Unknown manifest contract")
|
|
166
|
+
for key in ("engine", "engine_version", *VERSIONS):
|
|
167
|
+
_require(manifest[key] == discovery.get(key), "Engine identity mismatch: " + key)
|
|
168
|
+
_require(type(manifest["engine_version"]) is str and re.fullmatch(r"\d+\.\d+\.\d+", manifest["engine_version"]), "Malformed engine version")
|
|
169
|
+
features = manifest["required_features"]
|
|
170
|
+
_require(type(features) is list and all(type(f) is str for f in features), "Malformed required features")
|
|
171
|
+
_require(set(features) == FEATURES and len(features) == len(FEATURES), "Unsupported or missing required features")
|
|
172
|
+
entries = manifest["files"]
|
|
173
|
+
_require(type(entries) is list and 5 <= len(entries) <= 2048, "Invalid inventory count")
|
|
174
|
+
files = {"index.json": index}
|
|
175
|
+
total = len(index)
|
|
176
|
+
for entry in entries:
|
|
177
|
+
_require(type(entry) is dict and set(entry) == {"path","bytes","sha256"}, "Malformed inventory entry")
|
|
178
|
+
name = entry["path"]
|
|
179
|
+
_require(type(name) is str and name not in files, "Duplicate inventory path")
|
|
180
|
+
_require(type(entry["bytes"]) is int and 0 <= entry["bytes"] <= MAX_FILE, "Invalid resource size")
|
|
181
|
+
_require(type(entry["sha256"]) is str and SHA.fullmatch(entry["sha256"]), "Invalid SHA256")
|
|
182
|
+
data = _read(folder, name, MAX_FILE)
|
|
183
|
+
total += len(data)
|
|
184
|
+
_require(total <= MAX_TOTAL, "Inventory exceeds total size limit")
|
|
185
|
+
_require(len(data) == entry["bytes"] and hashlib.sha256(data).hexdigest() == entry["sha256"], "Resource hash/size mismatch: " + name)
|
|
186
|
+
files[name] = data
|
|
187
|
+
_require({"api.xml","api.xsd","guide.txt","scalar_template.py","requirements.txt"} <= files.keys(), "Incomplete landscape")
|
|
188
|
+
methods = validate_xml(files["api.xml"], manifest)
|
|
189
|
+
_require(_read(folder, "index.json", MAX_INDEX) == index, "Engine updated during refresh")
|
|
190
|
+
return Snapshot(dict(discovery), files, methods)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def signature_compatible(previous, current):
|
|
194
|
+
"""Accept only preserved call shapes plus new optional parameters; never eval."""
|
|
195
|
+
if previous == current:
|
|
196
|
+
return True
|
|
197
|
+
try:
|
|
198
|
+
old = ast.parse("def api" + previous + ": pass").body[0]
|
|
199
|
+
new = ast.parse("def api" + current + ": pass").body[0]
|
|
200
|
+
except (SyntaxError, ValueError, TypeError, RecursionError):
|
|
201
|
+
return False
|
|
202
|
+
dump = lambda node: ast.dump(node) if node is not None else None
|
|
203
|
+
if dump(old.returns) != dump(new.returns): return False
|
|
204
|
+
a, b = old.args, new.args
|
|
205
|
+
if len(a.posonlyargs) != len(b.posonlyargs): return False
|
|
206
|
+
if dump(a.vararg) != dump(b.vararg) or dump(a.kwarg) != dump(b.kwarg): return False
|
|
207
|
+
# A new parameter could consume values previously delivered to *args/**kwargs.
|
|
208
|
+
if a.kwarg is not None: return False
|
|
209
|
+
old_pos, new_pos = a.posonlyargs + a.args, b.posonlyargs + b.args
|
|
210
|
+
if len(new_pos) < len(old_pos) or a.vararg is not None and len(old_pos) != len(new_pos): return False
|
|
211
|
+
old_defaults = [None] * (len(old_pos)-len(a.defaults)) + a.defaults
|
|
212
|
+
new_defaults = [None] * (len(new_pos)-len(b.defaults)) + b.defaults
|
|
213
|
+
for i, arg in enumerate(old_pos):
|
|
214
|
+
if dump(arg) != dump(new_pos[i]) or dump(old_defaults[i]) != dump(new_defaults[i]): return False
|
|
215
|
+
if any(d is None for d in new_defaults[len(old_pos):]): return False
|
|
216
|
+
old_kw = {arg.arg: (dump(arg), dump(d)) for arg, d in zip(a.kwonlyargs, a.kw_defaults)}
|
|
217
|
+
new_kw = {arg.arg: (dump(arg), dump(d)) for arg, d in zip(b.kwonlyargs, b.kw_defaults)}
|
|
218
|
+
if any(new_kw.get(k) != v for k, v in old_kw.items()): return False
|
|
219
|
+
if any(v[1] is None for k, v in new_kw.items() if k not in old_kw): return False
|
|
220
|
+
return True
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def refresh(discovery, cache):
|
|
224
|
+
"""Validate every byte, stage it, then replace one atomic active pointer."""
|
|
225
|
+
snapshot = read_snapshot(discovery)
|
|
226
|
+
cache = Path(cache)
|
|
227
|
+
if (cache / "current.json").exists():
|
|
228
|
+
previous = cached_snapshot(cache)
|
|
229
|
+
methods = {m["id"]: m for m in snapshot.methods}
|
|
230
|
+
for method in previous.methods:
|
|
231
|
+
replacement = methods.get(method["id"])
|
|
232
|
+
_require(replacement is not None and all(replacement[k] == method[k]
|
|
233
|
+
for k in ("kind", "import-path"))
|
|
234
|
+
and signature_compatible(method["signature"], replacement["signature"]),
|
|
235
|
+
"Incompatible public method change; retain previous cache")
|
|
236
|
+
cache.mkdir(parents=True, exist_ok=True)
|
|
237
|
+
stage = Path(tempfile.mkdtemp(prefix="staging-", dir=cache))
|
|
238
|
+
pointer = None
|
|
239
|
+
try:
|
|
240
|
+
for name, data in snapshot.files.items():
|
|
241
|
+
(stage / name).write_bytes(data)
|
|
242
|
+
cached_identity = {**snapshot.identity, "folder": str(stage.resolve())}
|
|
243
|
+
read_snapshot(cached_identity)
|
|
244
|
+
destination = cache / snapshot.revision
|
|
245
|
+
if destination.exists():
|
|
246
|
+
# An existing cache must also be valid; never trust just its name.
|
|
247
|
+
read_snapshot({**snapshot.identity, "folder": str(destination.resolve())})
|
|
248
|
+
else:
|
|
249
|
+
os.replace(stage, destination)
|
|
250
|
+
fd, spelling = tempfile.mkstemp(prefix="pointer-", dir=cache)
|
|
251
|
+
pointer = Path(spelling)
|
|
252
|
+
with os.fdopen(fd, "w", encoding="utf-8") as stream:
|
|
253
|
+
json.dump({**snapshot.identity, "folder": str(destination.resolve())}, stream, sort_keys=True)
|
|
254
|
+
stream.flush()
|
|
255
|
+
os.fsync(stream.fileno())
|
|
256
|
+
os.replace(pointer, cache / "current.json")
|
|
257
|
+
return snapshot
|
|
258
|
+
finally:
|
|
259
|
+
if stage.exists():
|
|
260
|
+
shutil.rmtree(stage)
|
|
261
|
+
if pointer is not None and pointer.exists():
|
|
262
|
+
pointer.unlink()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def cached_snapshot(cache):
|
|
266
|
+
"""Last valid metadata for OFFLINE display only; not execution authority."""
|
|
267
|
+
cache = Path(cache)
|
|
268
|
+
discovery = _json(_read(cache, "current.json", MAX_INDEX))
|
|
269
|
+
_require(type(discovery) is dict and type(discovery.get("manifest_sha256")) is str
|
|
270
|
+
and SHA.fullmatch(discovery["manifest_sha256"]), "Invalid cache pointer")
|
|
271
|
+
_require(discovery.get("folder") == str((cache / discovery["manifest_sha256"]).resolve()), "Cache pointer escapes cache")
|
|
272
|
+
return read_snapshot(discovery)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def ensure_current(snapshot, discovery):
|
|
276
|
+
"""Recheck immediately before a later SDK operation against this engine."""
|
|
277
|
+
current = read_snapshot(discovery)
|
|
278
|
+
_require(current.identity == snapshot.identity, "Stale engine/catalogue identity; refresh and revalidate before use")
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def discover_engine(python):
|
|
282
|
+
"""Run only the explicitly selected installed engine's public endpoint."""
|
|
283
|
+
completed = subprocess.run([str(python), "-I", "-m", "pycforge", "--format", "json", "sdk-api"],
|
|
284
|
+
check=True, capture_output=True, timeout=30)
|
|
285
|
+
_require(len(completed.stdout) <= MAX_INDEX, "Oversized engine discovery")
|
|
286
|
+
return _json(completed.stdout)
|
|
287
|
+
|
pycforge_sdk/cli.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Thin console client; all authoring operations use the public SDK service."""
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import sys
|
|
6
|
+
import threading
|
|
7
|
+
from . import SDK, Outcome, __version__, discover_engines
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parser():
|
|
11
|
+
root = argparse.ArgumentParser(prog="pycforge-sdk", description="Author conversion add-ons using a selected PyCForge engine.")
|
|
12
|
+
root.add_argument("--version", action="version", version=__version__)
|
|
13
|
+
root.add_argument("--engine-python", help="Python interpreter containing the selected PyCForge installation")
|
|
14
|
+
root.add_argument("--cache", type=Path, default=Path.home()/".pycforge-sdk"/"cache")
|
|
15
|
+
root.add_argument("--timeout", type=float, default=30, help="Per-process deadline in seconds, at most 300")
|
|
16
|
+
root.add_argument("--json", action="store_true", help="Emit the versioned result object for automation")
|
|
17
|
+
commands = root.add_subparsers(dest="command", required=True)
|
|
18
|
+
discover = commands.add_parser("discover", help="Find installed PyCForge engines without changing the reference cache")
|
|
19
|
+
discover.add_argument("--location", action="append", help="Restrict the search to this environment or installed support folder; repeat for multiple locations")
|
|
20
|
+
discover.add_argument("--search-timeout", type=float, default=20, help="Total search deadline in seconds")
|
|
21
|
+
commands.add_parser("connect", help="Discover the engine and refresh its reference")
|
|
22
|
+
commands.add_parser("templates", help="Choose from concise engine starter descriptions")
|
|
23
|
+
commands.add_parser("workbench", help="Read engine-owned forms, default imports and Help")
|
|
24
|
+
methods = commands.add_parser("methods", help="Browse the authoring method tree")
|
|
25
|
+
methods.add_argument("--advanced", action="store_true")
|
|
26
|
+
describe = commands.add_parser("describe", help="Explain one authoring method")
|
|
27
|
+
describe.add_argument("method")
|
|
28
|
+
new = commands.add_parser("new", help="Create a starter extension project")
|
|
29
|
+
new.add_argument("directory", type=Path); new.add_argument("--author", required=True)
|
|
30
|
+
new.add_argument("--template", default="scalar-cube-root")
|
|
31
|
+
inspect = commands.add_parser("inspect", help="Inspect a saved declaration without executing it")
|
|
32
|
+
inspect.add_argument("directory", type=Path)
|
|
33
|
+
scan = commands.add_parser("scan", help="Show documentation readiness and editable field issues; do not run tests")
|
|
34
|
+
scan.add_argument("directory", type=Path)
|
|
35
|
+
ready = commands.add_parser("readiness", help="Check current packaging evidence without running fixtures")
|
|
36
|
+
ready.add_argument("directory", type=Path); ready.add_argument("--compiler")
|
|
37
|
+
document = commands.add_parser("document", help="Add missing author fields while preserving prose; explicitly upgrade legacy projects")
|
|
38
|
+
document.add_argument("directory", type=Path)
|
|
39
|
+
convert = commands.add_parser("convert", help="Convert one fixture without running it")
|
|
40
|
+
convert.add_argument("directory", type=Path); convert.add_argument("--case", required=True)
|
|
41
|
+
verify = commands.add_parser("verify", help="Explicitly compile and execute the complete fixture suite")
|
|
42
|
+
verify.add_argument("directory", type=Path); verify.add_argument("--compiler", default="cc")
|
|
43
|
+
build = commands.add_parser("build", help="Package only a currently verified snapshot")
|
|
44
|
+
build.add_argument("directory", type=Path); build.add_argument("--output", type=Path, required=True)
|
|
45
|
+
build.add_argument("--compiler", help="Require evidence for this selected compiler")
|
|
46
|
+
register = commands.add_parser("register", help="Explicitly register an import with PyCForge")
|
|
47
|
+
register.add_argument("package", type=Path); register.add_argument("--store", type=Path)
|
|
48
|
+
return root
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def render(result):
|
|
52
|
+
data = result.data
|
|
53
|
+
if "workbench" in data:
|
|
54
|
+
workbench = data["workbench"] or {}
|
|
55
|
+
return "\n".join(["Connection: " + data["connection"]["state"],
|
|
56
|
+
"Workbench: " + workbench.get("schema", "legacy catalogue"),
|
|
57
|
+
*[item["namespace"] + " " + item["version"] + ": " + item["description"] for item in workbench.get("imports", [])],
|
|
58
|
+
"Help: " + ", ".join(doc["title"] for doc in data.get("documents", []))])
|
|
59
|
+
if "method" in data:
|
|
60
|
+
method = data["method"]
|
|
61
|
+
lines = [method["title"], method["public_path"] + method["signature"], method["summary"], ""]
|
|
62
|
+
for parameter in method["parameters"]:
|
|
63
|
+
marker = "required" if parameter["required"] else "default=" + repr(parameter["default"])
|
|
64
|
+
lines.append(f"{parameter['name']} ({parameter['type']}, {marker}): {parameter['description']}")
|
|
65
|
+
if parameter["choices"]: lines.append(" Choices: " + ", ".join(map(str,parameter["choices"])))
|
|
66
|
+
lines += ["", "Returns: " + method["returns"], "Effects: " + method["effects"]]
|
|
67
|
+
for error in method["errors"]: lines.append(f"{error['code']}: {error['meaning']} {error['remedy']}")
|
|
68
|
+
lines += ["", "Example:", *method["examples"], "", *method["limits"]]
|
|
69
|
+
if data["connection"]["state"] != "connected": lines.insert(0,"OFFLINE REFERENCE — execution unavailable")
|
|
70
|
+
return "\n".join(lines)
|
|
71
|
+
if "templates" in data:
|
|
72
|
+
return "\n".join(["Connection: " + data["connection"]["state"], *[t["id"] + ": " + t["title"] + "\n " + t["description"] for t in data["templates"]]])
|
|
73
|
+
if "methods" in data:
|
|
74
|
+
rows = data["methods"]
|
|
75
|
+
lines = ["Connection: " + data["connection"]["state"]]
|
|
76
|
+
if "categories" in data:
|
|
77
|
+
for category in data["categories"]:
|
|
78
|
+
lines.append("\n" + category["title"])
|
|
79
|
+
lines.extend(f" {row['public_path']}: {row['summary']}" for row in rows if row["category"] == category["id"])
|
|
80
|
+
else: lines.extend(row["id"] + row["signature"] for row in rows)
|
|
81
|
+
return "\n".join(lines)
|
|
82
|
+
return json.dumps(result.to_dict(), indent=2, sort_keys=True)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def main(argv=None):
|
|
86
|
+
args = parser().parse_args(argv)
|
|
87
|
+
cancel = threading.Event()
|
|
88
|
+
sdk = SDK(engine_python=args.engine_python or sys.executable, cache=args.cache, timeout=args.timeout, cancel=cancel)
|
|
89
|
+
try:
|
|
90
|
+
match args.command:
|
|
91
|
+
case "discover": result = discover_engines(preferred=args.engine_python, locations=args.location, timeout=min(args.timeout, 3),
|
|
92
|
+
search_timeout=args.search_timeout, cancel=cancel)
|
|
93
|
+
case "connect": result = sdk.connect()
|
|
94
|
+
case "templates": result = sdk.templates()
|
|
95
|
+
case "workbench": result = sdk.workbench()
|
|
96
|
+
case "methods": result = sdk.methods(advanced=args.advanced)
|
|
97
|
+
case "describe": result = sdk.describe(args.method)
|
|
98
|
+
case "new": result = sdk.create_project(args.directory, author=args.author, template=args.template)
|
|
99
|
+
case "inspect": result = sdk.inspect(args.directory)
|
|
100
|
+
case "scan": result = sdk.scan(args.directory)
|
|
101
|
+
case "readiness": result = sdk.readiness(args.directory, compiler=args.compiler)
|
|
102
|
+
case "document": result = sdk.prepare_documentation(args.directory)
|
|
103
|
+
case "convert": result = sdk.convert(args.directory, args.case)
|
|
104
|
+
case "verify": result = sdk.verify(args.directory, compiler=args.compiler)
|
|
105
|
+
case "build": result = sdk.build(args.directory, args.output, compiler=args.compiler)
|
|
106
|
+
case "register": result = sdk.register(args.package, store=args.store)
|
|
107
|
+
except KeyboardInterrupt:
|
|
108
|
+
cancel.set()
|
|
109
|
+
result = Outcome("canceled", diagnostics=({"code":"SDK_CANCELED", "message":"Interrupted by the caller."},))
|
|
110
|
+
if args.json:
|
|
111
|
+
print(json.dumps(result.to_dict(), sort_keys=True, allow_nan=False))
|
|
112
|
+
else:
|
|
113
|
+
print(render(result))
|
|
114
|
+
return {"succeeded":0, "rejected":2, "incomplete":3, "canceled":130, "internal_failure":4}[result.status]
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Selected-engine discovery, offline metadata and a narrow public-service bridge."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import json
|
|
5
|
+
from . import catalogue
|
|
6
|
+
from .process import run
|
|
7
|
+
from .project import canonical, parse
|
|
8
|
+
from .results import SDKError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Connection:
|
|
12
|
+
def __init__(self, python, cache, *, timeout=30, cancel=None):
|
|
13
|
+
self.python = str(python)
|
|
14
|
+
self.cache = Path(cache)
|
|
15
|
+
self.timeout, self.cancel = timeout, cancel
|
|
16
|
+
self.snapshot = None
|
|
17
|
+
self.state = "unavailable"
|
|
18
|
+
self.problem = None
|
|
19
|
+
self._authoring_ready = False
|
|
20
|
+
|
|
21
|
+
def discover(self):
|
|
22
|
+
result = run([self.python, "-I", Path(__file__).with_name("engine_launch.py"), "discover"],
|
|
23
|
+
timeout=self.timeout, cancel=self.cancel, max_output=1024*1024)
|
|
24
|
+
if result.returncode:
|
|
25
|
+
raise SDKError("SDK_ENGINE_UNAVAILABLE", "Selected Python cannot discover PyCForge.", status="incomplete",
|
|
26
|
+
details={"stderr": result.stderr.decode("utf-8", "replace")})
|
|
27
|
+
return parse(result.stdout)
|
|
28
|
+
|
|
29
|
+
def connect(self, *, offline=False):
|
|
30
|
+
if offline:
|
|
31
|
+
self._authoring_ready = False
|
|
32
|
+
self.problem = "Working offline; cached reference cannot authorize execution."
|
|
33
|
+
try:
|
|
34
|
+
self.snapshot = catalogue.cached_snapshot(self.cache)
|
|
35
|
+
self.state = "offline"
|
|
36
|
+
except (catalogue.CatalogueError, OSError):
|
|
37
|
+
self.snapshot, self.state = None, "unavailable"
|
|
38
|
+
return self.description()
|
|
39
|
+
try:
|
|
40
|
+
discovery = self.discover()
|
|
41
|
+
# Validate supported authoring metadata before committing the cache.
|
|
42
|
+
self.snapshot = catalogue.read_snapshot(discovery)
|
|
43
|
+
self._authoring_ready = False
|
|
44
|
+
if "authoring.json" in self.snapshot.files:
|
|
45
|
+
self.reference()
|
|
46
|
+
self.workbench()
|
|
47
|
+
self.snapshot = catalogue.refresh(discovery, self.cache)
|
|
48
|
+
self.state, self.problem = "connected", None
|
|
49
|
+
except (catalogue.CatalogueError, OSError, SDKError) as exc:
|
|
50
|
+
if isinstance(exc, SDKError) and exc.status == "canceled": raise
|
|
51
|
+
self.problem = str(exc)
|
|
52
|
+
try:
|
|
53
|
+
self.snapshot = catalogue.cached_snapshot(self.cache)
|
|
54
|
+
self.state = "offline"
|
|
55
|
+
except (catalogue.CatalogueError, OSError):
|
|
56
|
+
self.snapshot, self.state = None, "unavailable"
|
|
57
|
+
return self.description()
|
|
58
|
+
|
|
59
|
+
def description(self):
|
|
60
|
+
return {"state": self.state, "engine": None if self.snapshot is None else dict(self.snapshot.identity),
|
|
61
|
+
"problem": self.problem, "execution_available": self.state == "connected" and self._authoring_ready}
|
|
62
|
+
|
|
63
|
+
def metadata(self):
|
|
64
|
+
if self.snapshot is None:
|
|
65
|
+
self.connect()
|
|
66
|
+
if self.snapshot is None:
|
|
67
|
+
raise SDKError("SDK_ENGINE_UNAVAILABLE", self.problem or "No engine catalogue is available.", status="incomplete")
|
|
68
|
+
return self.snapshot
|
|
69
|
+
|
|
70
|
+
def ensure_current(self):
|
|
71
|
+
self.metadata()
|
|
72
|
+
if self.state != "connected":
|
|
73
|
+
raise SDKError("SDK_OFFLINE", "Cached metadata is for browsing; connect to a compatible engine before authoring operations.", status="incomplete")
|
|
74
|
+
try:
|
|
75
|
+
catalogue.ensure_current(self.snapshot, self.discover())
|
|
76
|
+
except (catalogue.CatalogueError, OSError) as exc:
|
|
77
|
+
self.state = "offline"
|
|
78
|
+
raise SDKError("SDK_STALE_ENGINE", str(exc), status="incomplete") from exc
|
|
79
|
+
|
|
80
|
+
def reference(self):
|
|
81
|
+
snapshot = self.metadata()
|
|
82
|
+
if "authoring.json" not in snapshot.files:
|
|
83
|
+
raise SDKError("SDK_CAPABILITY", "This engine supports catalogue browsing but lacks the scalar authoring service; select PyCForge 1.3.61 or a compatible later build.")
|
|
84
|
+
data = parse(snapshot.files["authoring.json"])
|
|
85
|
+
if (type(data) is not dict or data.get("schema") != "pycforge.authoring-reference/0.1"
|
|
86
|
+
or data.get("service_api") != "0.1"):
|
|
87
|
+
raise SDKError("SDK_CAPABILITY", "Unsupported authoring reference contract.")
|
|
88
|
+
required = {"categories", "entries", "templates", "native_test", "limits"}
|
|
89
|
+
if not required <= data.keys() or not all(type(data[k]) is list for k in ("categories","entries","templates","limits")):
|
|
90
|
+
raise SDKError("SDK_CAPABILITY", "Incomplete engine authoring guidance.")
|
|
91
|
+
if not 1 <= len(data["entries"]) <= 1024 or not 1 <= len(data["templates"]) <= 512:
|
|
92
|
+
raise SDKError("SDK_CAPABILITY", "Invalid authoring catalogue size.")
|
|
93
|
+
identifiers = set()
|
|
94
|
+
categories = {c["id"] for c in data["categories"] if type(c) is dict and type(c.get("id")) is str}
|
|
95
|
+
for entry in data["entries"]:
|
|
96
|
+
fields = {"id","public_path","title","category","summary","parameters","returns","effects","errors","examples","limits","status","since"}
|
|
97
|
+
if type(entry) is not dict or not fields <= entry.keys() or entry["id"] in identifiers or entry["category"] not in categories:
|
|
98
|
+
raise SDKError("SDK_CAPABILITY", "Malformed authoring reference entry.")
|
|
99
|
+
identifiers.add(entry["id"])
|
|
100
|
+
for key in ("id","public_path","title","category","summary","returns","effects","status","since"):
|
|
101
|
+
if type(entry[key]) is not str or not entry[key]: raise SDKError("SDK_CAPABILITY", "Missing reference explanation: " + key)
|
|
102
|
+
for key in ("parameters","errors","examples","limits"):
|
|
103
|
+
if type(entry[key]) is not list: raise SDKError("SDK_CAPABILITY", "Malformed reference details: " + key)
|
|
104
|
+
suffix = entry["public_path"].removeprefix("pycforge.crucible.")
|
|
105
|
+
matches = [m for m in snapshot.methods if m["id"].endswith("." + suffix)]
|
|
106
|
+
if len(matches) != 1:
|
|
107
|
+
raise SDKError("SDK_CAPABILITY", "Reference does not resolve to one published public method: " + suffix)
|
|
108
|
+
entry["signature"] = matches[0]["signature"]
|
|
109
|
+
self._authoring_ready = True
|
|
110
|
+
return data
|
|
111
|
+
|
|
112
|
+
def workbench(self):
|
|
113
|
+
from .workbench import validate
|
|
114
|
+
return validate(self.metadata())
|
|
115
|
+
|
|
116
|
+
def call(self, action, payload):
|
|
117
|
+
self.ensure_current()
|
|
118
|
+
reference = self.reference()
|
|
119
|
+
identity = self.snapshot.identity
|
|
120
|
+
request = canonical({"identity": identity, "action": action, "payload": payload})
|
|
121
|
+
if len(request) > 8*1024*1024:
|
|
122
|
+
raise SDKError("SDK_LIMIT", "Engine request exceeds the bridge budget.")
|
|
123
|
+
result = run([self.python, "-I", Path(__file__).with_name("engine_launch.py"), "call"], input_bytes=request,
|
|
124
|
+
timeout=self.timeout, cancel=self.cancel, max_output=16*1024*1024)
|
|
125
|
+
if result.returncode:
|
|
126
|
+
raise SDKError("SDK_ENGINE_FAILURE", "Selected engine worker failed.", status="internal_failure",
|
|
127
|
+
details={"stderr": result.stderr.decode("utf-8", "replace")})
|
|
128
|
+
answer = parse(result.stdout)
|
|
129
|
+
if answer.get("schema") != "pycforge-sdk.bridge/0.1":
|
|
130
|
+
raise SDKError("SDK_PROTOCOL", "Unexpected engine bridge response.", status="internal_failure")
|
|
131
|
+
if not answer.get("ok"):
|
|
132
|
+
failure = answer.get("diagnostic", {})
|
|
133
|
+
raise SDKError(failure.get("code", "SDK_ENGINE_ERROR"), failure.get("message", "Engine operation failed."))
|
|
134
|
+
if answer.get("identity") != identity:
|
|
135
|
+
raise SDKError("SDK_STALE_ENGINE", "Engine identity changed during the operation.", status="incomplete")
|
|
136
|
+
return answer["result"]
|