lvkit 0.5.2__tar.gz → 0.5.4__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.
- lvkit-0.5.4/.pre-commit-config.yaml +64 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/PKG-INFO +47 -40
- {lvkit-0.5.2 → lvkit-0.5.4}/README.md +46 -39
- {lvkit-0.5.2 → lvkit-0.5.4}/editors/vscode/README.md +3 -3
- {lvkit-0.5.2 → lvkit-0.5.4}/pyproject.toml +9 -1
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/__init__.py +1 -1
- lvkit-0.5.4/src/lvkit/__main__.py +7 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/_pylabview_patches.py +20 -5
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/cli.py +57 -10
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/subvi.py +2 -2
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/extractor.py +126 -96
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/construction.py +22 -4
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/core.py +13 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/diff.py +242 -144
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/loading.py +18 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/models.py +2 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/queries.py +4 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/type_resolution.py +1 -1
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/vi.py +1 -1
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/primitive_resolver.py +1 -1
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/project_store.py +18 -1
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/__init__.py +60 -11
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/backend.py +18 -10
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/diff_viewer.py +2 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/draw.py +81 -43
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/glyph.py +7 -5
- lvkit-0.5.4/src/lvkit/render/help_tip.py +169 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/nodes.py +73 -28
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/render_viewer.py +2 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/scene.py +50 -2
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/templates/diff_viewer.html +266 -39
- lvkit-0.5.4/src/lvkit/render/templates/render_viewer.html +224 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/theme_control.py +16 -9
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/terminal_collector.py +2 -2
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/vilib_resolver.py +11 -11
- lvkit-0.5.4/tests/conftest.py +67 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_diff.py +25 -5
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_diff_viewer.py +1 -1
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_driver_codegen.py +3 -0
- lvkit-0.5.4/tests/test_extraction_cache.py +334 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_frame_attribution.py +3 -0
- lvkit-0.5.4/tests/test_help_tip.py +45 -0
- lvkit-0.5.4/tests/test_mcp_cache.py +93 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_parser_regression.py +3 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_render.py +73 -13
- lvkit-0.5.4/tests/test_subvi_nav.py +75 -0
- lvkit-0.5.2/.pre-commit-config.yaml +0 -32
- lvkit-0.5.2/scripts/build_animatic.py +0 -173
- lvkit-0.5.2/scripts/render_video.py +0 -220
- lvkit-0.5.2/src/lvkit/render/templates/render_viewer.html +0 -123
- lvkit-0.5.2/tests/conftest.py +0 -28
- {lvkit-0.5.2 → lvkit-0.5.4}/.gitignore +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/LICENSE +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/NOTICE +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/deploy/cloudrun/README.md +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/docs/_internal/xml-schema/README.md +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/editors/vscode/LICENSE +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/audit_primitive_consistency.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/audit_terminal_order.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/audit_unresolved.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/build_hero.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/build_ni_function_catalog.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/dump_primitive_context.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/extract_corpus.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/gen_semantics_probe.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/generate_docs.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/generate_driver_data.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/generate_python.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/list_unknown_primitives.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/populate_vilib.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/pull_samples.sh +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/recategorize_vilib.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/render_gallery.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/reproduce_openg_corpus.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/sync_skills.sh +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/scripts/wire_decode_probe.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/_data.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/README.md +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/ast_optimizer.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/ast_utils.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/builder.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/class_builder.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/condition_builder.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/context.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/dataflow.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/elementwise.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/error_handler.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/expressions.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/fragment.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/function.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/imports.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/base.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/case.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/compound.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/constant.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/event.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/first_call.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/formula.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/in_place.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/invoke_node.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/loop.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/nmux.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/primitive.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/printf.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/property_node.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/queue_ops.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/nodes/sequence.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/stubs.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/codegen/unresolved.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/_index.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/daqmx.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/nidcpower.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/nidigital.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/nidmm.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/nifgen.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/niscope.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/niswitch.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/serial.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/drivers/visa.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/labview_error_codes.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/openg/_index.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/openg/array.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/openg/file.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/openg/string.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/openg/time.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/openg/variant.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/primitives.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/_index.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/_pending_terminals.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/_types.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/addons.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/application-control.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/array.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/boolean.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/cluster-class-variant.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/collection.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/comparison.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/connectivity.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/control-simulation.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/data-communication.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/dialog-user-interface.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/express.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/file-io.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/graphics-sound.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/instrument-io.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/mathematics.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/measurement-io.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/numeric.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/report-generation.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/signal-processing.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/string.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/structures.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/synchronization.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/timing.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/data/vilib/waveform.json +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/docs/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/docs/generate.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/docs/html_generator.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/docs/template.css +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/docs/utils.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/formula/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/formula/cparser.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/formula/emit.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/formula/lexer.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/formula/nodes.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/analysis.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/builders/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/builders/context.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/builders/operations.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/builders/refs.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/builders/structures.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/describe.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/netlist.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/op_walk.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/graph/operations.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/labview_error.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/labview_error_codes.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/labview_queue.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/lv_detect.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/mcp/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/mcp/schemas.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/mcp/server.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/mcp/tools.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/models.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/num_format.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/constants.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/defaults.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/flags.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/front_panel.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/layout.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/metadata.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/models.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/naming.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/node_types.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/base.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/case.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/constant.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/decompose.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/disable.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/event.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/loop.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/nodes/sequence.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/type_mapping.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/utils.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/parser/wire_table.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/pipeline.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/py.typed +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/icons.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/lane_pass.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/style.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/theme_web.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/render/wire_router.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/runtime/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/runtime/lv.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/skill_templates/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/skill_templates/lvkit-convert/SKILL.md +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/skill_templates/lvkit-describe/SKILL.md +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/skill_templates/lvkit-idiomatic/SKILL.md +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/skill_templates/lvkit-resolve-primitive/SKILL.md +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/skill_templates/lvkit-resolve-vilib/SKILL.md +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/structure.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/src/lvkit/type_defaults.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/__init__.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/helpers.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_array_ops_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_ast_builder.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_ast_optimizer.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_case_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_case_parser.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_class_field_walkup.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_cli.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_codegen_context.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_compound_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_condition_builder.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_constant_decoding.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_dependency_path_refs.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_determinism.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_disable_graph.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_disable_parser.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_docs_class_hierarchy.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_docs_render.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_dynamic_dispatch.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_e2e_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_elementwise.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_error_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_error_handling.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_event_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_event_parser.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_first_call_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_formula_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_formula_runtime.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_formula_semantics.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_formula_transpile.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_frame_zplane.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_glyphs.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_graceful_degradation.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_in_place_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_labview_queue.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_load_mode.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_loop_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_lv_detect.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_lvpy.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_naming.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_netlist.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_new_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_parallel_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_parser.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_primitive_name_uniqueness.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_primitive_resolutions.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_project_store.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_queue_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_render_viewer.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_sequence.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_soft_codegen.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_structure_lvproj.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_type_driven_fixes.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_type_fields.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_type_loading.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_type_mapping.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_unknown_nodes.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_vi_graph.py +0 -0
- {lvkit-0.5.2 → lvkit-0.5.4}/tests/test_vilib_docurls.py +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# See https://pre-commit.com for usage and configuration
|
|
2
|
+
#
|
|
3
|
+
# Install hooks once per clone:
|
|
4
|
+
# uv run pre-commit install
|
|
5
|
+
#
|
|
6
|
+
# Run against all files (e.g. after editing config):
|
|
7
|
+
# uv run pre-commit run --all-files
|
|
8
|
+
#
|
|
9
|
+
# pytest runs the FULL suite locally, where the sample corpus is present
|
|
10
|
+
# (scripts/pull_samples.sh). On a fresh clone without it, `needs_samples` tests
|
|
11
|
+
# auto-skip (tests/conftest.py) rather than fail. CI mirrors these checks and
|
|
12
|
+
# additionally pulls the corpus — see .github/workflows/ci.yml.
|
|
13
|
+
|
|
14
|
+
repos:
|
|
15
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
16
|
+
rev: v5.0.0
|
|
17
|
+
hooks:
|
|
18
|
+
- id: trailing-whitespace
|
|
19
|
+
- id: end-of-file-fixer
|
|
20
|
+
- id: check-yaml
|
|
21
|
+
- id: check-toml
|
|
22
|
+
- id: check-added-large-files
|
|
23
|
+
args: ["--maxkb=500"]
|
|
24
|
+
- id: check-merge-conflict
|
|
25
|
+
- id: mixed-line-ending
|
|
26
|
+
args: ["--fix=lf"]
|
|
27
|
+
|
|
28
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
29
|
+
rev: v0.14.14
|
|
30
|
+
hooks:
|
|
31
|
+
- id: ruff-check
|
|
32
|
+
args: ["--fix"]
|
|
33
|
+
|
|
34
|
+
# Pyright and pytest run against the project's own venv (via `uv run`) so they
|
|
35
|
+
# see the full resolved dependency tree — identical to CI. lvkit type-checks
|
|
36
|
+
# src/ (its shipped package); scripts/ and branding/ are auxiliary, not gated.
|
|
37
|
+
- repo: local
|
|
38
|
+
hooks:
|
|
39
|
+
- id: pyright
|
|
40
|
+
name: pyright
|
|
41
|
+
entry: uv run pyright src/
|
|
42
|
+
language: system
|
|
43
|
+
types: [python]
|
|
44
|
+
pass_filenames: false
|
|
45
|
+
require_serial: true
|
|
46
|
+
|
|
47
|
+
- id: pytest
|
|
48
|
+
name: pytest
|
|
49
|
+
entry: uv run pytest -q
|
|
50
|
+
language: system
|
|
51
|
+
types: [python]
|
|
52
|
+
pass_filenames: false
|
|
53
|
+
require_serial: true
|
|
54
|
+
stages: [pre-commit]
|
|
55
|
+
|
|
56
|
+
- id: sync-skills
|
|
57
|
+
name: sync skill templates
|
|
58
|
+
entry: scripts/sync_skills.sh
|
|
59
|
+
language: system
|
|
60
|
+
# Trigger on any SKILL.md change under either tree. The script
|
|
61
|
+
# discovers skills dynamically from src/lvkit/skill_templates/ —
|
|
62
|
+
# adding a new packaged skill requires no hook-config change.
|
|
63
|
+
files: '^(\.claude/skills/.*/SKILL\.md|src/lvkit/skill_templates/claude/.*/SKILL\.md)$'
|
|
64
|
+
pass_filenames: false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lvkit
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: Understand and convert LabVIEW VIs to Python without a LabVIEW license
|
|
5
5
|
Project-URL: Repository, https://github.com/pragmatest-dev/lvkit
|
|
6
6
|
Project-URL: Issues, https://github.com/pragmatest-dev/lvkit/issues
|
|
@@ -35,22 +35,20 @@ Description-Content-Type: text/markdown
|
|
|
35
35
|
|
|
36
36
|
# lvkit
|
|
37
37
|
|
|
38
|
-
Read,
|
|
38
|
+
Read, render, document, and diff LabVIEW VI files — no LabVIEW license required.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
lvkit parses `.vi`, `.ctl`, `.lvclass`, `.lvlib`, and `.lvproj` files directly into queryable dependency and dataflow graphs. From that graph it renders block diagrams, describes what a VI does, generates documentation, and diffs two versions — from a terminal, in CI, or through an AI agent. It can also generate a Python transliteration of a VI, though that path is experimental and how far it gets varies from VI to VI.
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
> **Reads, never writes.** lvkit only ever reads a VI — it never modifies, re-saves, or edits one. Every capability, including convert, parses the source and writes to a *separate* output file; your `.vi` files are never touched, and LabVIEW stays the only thing that authors them.
|
|
43
43
|
|
|
44
|
-
> **
|
|
45
|
-
|
|
46
|
-
> **Independent, clean-room project — not affiliated with NI.** lvkit was built using **only publicly available information**: public NI documentation, the open-source [pylabview](https://github.com/mefistotelis/pylabview) project, and observation of VI files. It was developed **without installing or running LabVIEW or any NI software**, and with **no NI source code, internal or non-public specifications, or confidential or proprietary materials**. LabVIEW, NI, and National Instruments are trademarks of National Instruments Corporation, used here only to identify the format lvkit interoperates with; lvkit is not affiliated with, authorized by, endorsed by, or sponsored by NI. See [Cleanroom approach](#cleanroom-approach), [`NOTICE`](NOTICE), and [`PROVENANCE.md`](PROVENANCE.md).
|
|
44
|
+
> **Independent, clean-room project — not affiliated with NI.** Built from public NI documentation, the open-source [pylabview](https://github.com/mefistotelis/pylabview) parser, and observation of VI files — with no LabVIEW install and no NI source or non-public specifications. Details in [Cleanroom approach](#cleanroom-approach); see also [`NOTICE`](NOTICE) and [`PROVENANCE.md`](PROVENANCE.md).
|
|
47
45
|
|
|
48
46
|
## Contents
|
|
49
47
|
|
|
50
48
|
- [Quick Start](#quick-start)
|
|
51
49
|
- [What you can do with it](#what-you-can-do-with-it)
|
|
52
50
|
- [How it works](#how-it-works)
|
|
53
|
-
- [
|
|
51
|
+
- [Editor and AI integration](#editor-and-ai-integration)
|
|
54
52
|
- [Cleanroom approach](#cleanroom-approach)
|
|
55
53
|
- [Development](#development)
|
|
56
54
|
|
|
@@ -71,22 +69,23 @@ For a global install: `pipx install lvkit` or `uv tool install lvkit`.
|
|
|
71
69
|
|
|
72
70
|
| Command | Description |
|
|
73
71
|
|---------|-------------|
|
|
74
|
-
| `lvkit describe` | Human-readable VI
|
|
72
|
+
| `lvkit describe` | Human-readable VI summary: signature, operations, dataflow, structures |
|
|
73
|
+
| `lvkit render` | Render a VI's block diagram to a faithful SVG (or interactive HTML) |
|
|
74
|
+
| `lvkit diff` | Compare two VI versions — operations, structures, wiring, constants |
|
|
75
75
|
| `lvkit docs` | Generate cross-referenced HTML documentation |
|
|
76
|
-
| `lvkit
|
|
77
|
-
| `lvkit visualize` | Interactive dependency or data flow graph HTML |
|
|
76
|
+
| `lvkit visualize` | Interactive dataflow or dependency graph (HTML) |
|
|
78
77
|
| `lvkit generate` | Generate Python from a VI, library, or class (experimental — see [Cleanroom approach](#cleanroom-approach)) |
|
|
79
|
-
| `lvkit
|
|
80
|
-
| `lvkit
|
|
81
|
-
| `lvkit setup` | Install AI agent skills; create `.lvkit/` resolution store |
|
|
78
|
+
| `lvkit structure` | Inspect `.lvproj`, `.lvlib`, or `.lvclass` structure |
|
|
79
|
+
| `lvkit detect` | Detect a locally installed LabVIEW and its `vi.lib` / `user.lib` |
|
|
80
|
+
| `lvkit setup` | Install AI agent skills; create the `.lvkit/` resolution store |
|
|
82
81
|
| `lvkit mcp` | Start the MCP server for IDE integration |
|
|
83
82
|
|
|
84
|
-
`
|
|
83
|
+
`describe`, `render`, `diff`, `docs`, and `visualize` work from the graph alone and need no semantic mappings. Only `generate` requires primitive/vi.lib mappings. `lvkit visualize` needs the pyvis extra: `pip install lvkit[visualize]`.
|
|
85
84
|
|
|
86
85
|
## What you can do with it
|
|
87
86
|
|
|
88
87
|
### Describe what a VI does
|
|
89
|
-
|
|
88
|
+
A human-readable signature, inputs/outputs, operations, and control flow — without opening LabVIEW.
|
|
90
89
|
|
|
91
90
|
```
|
|
92
91
|
lvkit describe <path-to.vi> [--search-path <libraries/>] [--verbose]
|
|
@@ -94,24 +93,33 @@ lvkit describe <path-to.vi> [--search-path <libraries/>] [--verbose]
|
|
|
94
93
|
|
|
95
94
|
`--verbose` adds a full netlist — a text projection of the block diagram.
|
|
96
95
|
|
|
97
|
-
###
|
|
98
|
-
|
|
96
|
+
### Render the block diagram
|
|
97
|
+
Render a VI's block diagram to a faithful, self-contained SVG — nodes, wires, structures, and SubVI icons, all drawn from the parsed graph. No LabVIEW, no screenshots.
|
|
99
98
|
|
|
100
99
|
```
|
|
101
|
-
lvkit
|
|
100
|
+
lvkit render <path-to.vi> [--format {svg,html}] [--theme {light,dark,auto}] [--search-path <libraries/>]
|
|
102
101
|
```
|
|
103
102
|
|
|
103
|
+
`--format html` writes an interactive single-VI viewer with zoom/pan and a light/dark diagram-theme toggle; hover a node to see its connector pane and documentation. The same renderer powers `diff --format html` and the VS Code extension.
|
|
104
|
+
|
|
104
105
|
### Diff two versions of a VI
|
|
105
106
|
See what changed between two `.vi` files — added/removed operations and structures, rewired connections, changed constants. Useful in code review and CI.
|
|
106
107
|
|
|
107
108
|
```
|
|
108
|
-
lvkit diff <vi-a> <vi-b> [--format {text,json,html}]
|
|
109
|
+
lvkit diff <vi-a> <vi-b> [--format {text,json,html}] [--open]
|
|
109
110
|
```
|
|
110
111
|
|
|
111
|
-
Text
|
|
112
|
+
Text (the default) is a concise, logical change summary for stdout or CI. `--format json` emits a UID-correlated change map for scripts or an AI agent. `--format html` (or `--open`) writes an **interactive diff viewer** — synced before/after panes, click a change to spotlight it, deep-linkable.
|
|
113
|
+
|
|
114
|
+
### Generate documentation
|
|
115
|
+
Cross-referenced HTML docs for a `.vi`, `.lvlib`, or `.lvclass` — inputs, outputs, operations, wiring diagrams.
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
lvkit docs <input-path> <output-dir> [--search-path <libraries/>]
|
|
119
|
+
```
|
|
112
120
|
|
|
113
|
-
### Generate Python
|
|
114
|
-
|
|
121
|
+
### Generate Python (experimental)
|
|
122
|
+
This is lvkit's least mature capability, and the one most likely to need hand-finishing. It walks the graph to emit a Python transliteration of a `.vi`, `.lvlib`, or `.lvclass`. The transliteration is deterministic (same VI in, same Python out, no LLM), but coverage is incremental: a VI only converts cleanly when every primitive and vi.lib VI it uses is already mapped, so how far it gets varies widely. Expect to resolve unknowns and clean up the output — see [Cleanroom approach](#cleanroom-approach).
|
|
115
123
|
|
|
116
124
|
```
|
|
117
125
|
lvkit generate <input-path> -o <output-dir> [--search-path <libraries>] [--placeholder-on-unresolved]
|
|
@@ -119,23 +127,23 @@ lvkit generate <input-path> -o <output-dir> [--search-path <libraries>] [--place
|
|
|
119
127
|
|
|
120
128
|
`--placeholder-on-unresolved` lets the build succeed when mappings are missing — unresolved calls become inline `raise PrimitiveResolutionNeeded(...)` in the output so you can track them down at runtime.
|
|
121
129
|
|
|
122
|
-
Coverage is incremental and results will vary — see [Cleanroom approach](#cleanroom-approach) for what that means in practice.
|
|
123
|
-
|
|
124
130
|
## How it works
|
|
125
131
|
|
|
126
132
|
lvkit reads VI binaries directly — no LabVIEW installation required. The pipeline has three stages:
|
|
127
133
|
|
|
128
134
|
1. **Parse** — the VI binary is extracted to XML (via [pylabview](https://github.com/mefistotelis/pylabview)), then parsed into a typed representation of the block diagram: nodes, wires, constants, types, and front panel terminals.
|
|
129
135
|
|
|
130
|
-
2. **Graph** — all loaded VIs are linked into a graph that captures two things: the dependency tree (which VIs call which) and the dataflow within each VI (how data moves between operations). This is what `describe`, `
|
|
136
|
+
2. **Graph** — all loaded VIs are linked into a graph that captures two things: the dependency tree (which VIs call which) and the dataflow within each VI (how data moves between operations). This is what `describe`, `render`, `diff`, `docs`, and `visualize` query — no semantic mappings needed.
|
|
137
|
+
|
|
138
|
+
3. **Generate** — the graph is walked deterministically to produce Python source, HTML documentation, or diagrams. Code generation is pure AST construction: same VI in, same output every run, no LLM.
|
|
131
139
|
|
|
132
|
-
|
|
140
|
+
See the [command reference](docs/reference/index.md) for full, flag-level docs on every command.
|
|
133
141
|
|
|
134
|
-
|
|
142
|
+
## Editor and AI integration
|
|
135
143
|
|
|
136
|
-
|
|
144
|
+
The CLI works standalone from any terminal or CI script. For deeper integration, lvkit ships three optional layers.
|
|
137
145
|
|
|
138
|
-
|
|
146
|
+
**VS Code extension** — [**LVKit** on the Marketplace](https://marketplace.visualstudio.com/items?itemName=pragmatest.lvkit). Opening a `.vi` renders its block diagram inline instead of the "binary file" placeholder, and clicking a SubVI opens it. **Open Visual Diff** — right-click a changed `.vi` in Source Control, the Explorer, or the editor tab — shows a before/after block-diagram diff for code review. It ships a bundled lvkit, so it works with no separate install; point the `lvkit.path` setting at a project venv or a PATH install to use your own instead. `.vi` files only today.
|
|
139
147
|
|
|
140
148
|
**AI agent skills** — install lvkit's built-in workflows into Claude Code or Copilot so your AI agent can describe VIs, convert them, and resolve unknowns without you writing prompts. All five workflows call the CLI under the hood — no MCP server required.
|
|
141
149
|
|
|
@@ -160,16 +168,16 @@ Five workflows ship: `lvkit-describe`, `lvkit-convert`, `lvkit-resolve-primitive
|
|
|
160
168
|
|
|
161
169
|
| Tool | Description |
|
|
162
170
|
|------|-------------|
|
|
163
|
-
| `load` | Load VI into the in-memory graph |
|
|
171
|
+
| `load` | Load a VI into the in-memory graph |
|
|
164
172
|
| `list_loaded` | List loaded VIs |
|
|
173
|
+
| `clear` | Clear the in-memory graph |
|
|
165
174
|
| `get_context` | Full VI context: inputs, outputs, operations, wires |
|
|
166
|
-
| `generate_ast_code` | Generate Python from a loaded VI |
|
|
167
175
|
| `describe` | Human-readable VI description |
|
|
168
176
|
| `get_operations` | List operations in a VI |
|
|
169
177
|
| `get_dataflow` | Show wire connections |
|
|
170
178
|
| `get_structure` | Inspect a structure node (loop, case, sequence) |
|
|
171
179
|
| `get_constants` | List constant values |
|
|
172
|
-
| `
|
|
180
|
+
| `generate_ast_code` | Generate Python from a loaded VI |
|
|
173
181
|
| `generate_documents` | Generate HTML docs for VIs/libraries (stateless) |
|
|
174
182
|
| `generate_python` | Generate Python from a VI (stateless) |
|
|
175
183
|
|
|
@@ -182,7 +190,9 @@ These mappings are **lvkit's own definitions, derived purely by inference** from
|
|
|
182
190
|
1. **Public NI documentation** — pages published openly on ni.com, accessed with no login, partner portal, or NDA gate.
|
|
183
191
|
2. **The VI XML produced by [pylabview](https://github.com/mefistotelis/pylabview)** — the open-source parser that extracts the VI binary to XML. That XML is lvkit's *only* window into the format; lvkit has no other view of it.
|
|
184
192
|
|
|
185
|
-
The definitions are open source and fully inspectable, and many carry a note recording how each was inferred (the public doc consulted, the observed terminal signature, a `verified`/`guess_reason` marker). They are **best-effort inferences, not authoritative** — they have been wrong and corrected over time. That imperfection is a direct consequence of working from *only* public documentation and the pylabview XML, with **no access to any internal or authoritative NI specification
|
|
193
|
+
The definitions are open source and fully inspectable, and many carry a note recording how each was inferred (the public doc consulted, the observed terminal signature, a `verified`/`guess_reason` marker). They are **best-effort inferences, not authoritative** — they have been wrong and corrected over time. That imperfection is a direct consequence of working from *only* public documentation and the pylabview XML, with **no access to any internal or authoritative NI specification**.
|
|
194
|
+
|
|
195
|
+
Coverage is incremental. When `lvkit generate` encounters an unmapped primitive or vi.lib VI, it raises an error with diagnostic context so the mapping can be added. `describe`, `render`, `diff`, `docs`, and `visualize` are unaffected — they work from the graph, not the semantic mappings.
|
|
186
196
|
|
|
187
197
|
### Provenance
|
|
188
198
|
|
|
@@ -192,8 +202,6 @@ lvkit was developed **using only publicly available information** and clean-room
|
|
|
192
202
|
|
|
193
203
|
LabVIEW, NI, and National Instruments are trademarks of National Instruments Corporation. lvkit is an independent project and is not affiliated with, authorized by, endorsed by, or sponsored by NI. Those names are used only to identify the file format and software lvkit interoperates with (nominative use).
|
|
194
204
|
|
|
195
|
-
Coverage is incremental. When `lvkit generate` encounters an unmapped primitive or vi.lib VI, it raises an error with diagnostic context so the mapping can be added. `describe`, `docs`, `diff`, and `visualize` are unaffected — they work from the graph, not the semantic mappings.
|
|
196
|
-
|
|
197
205
|
### Project-local resolution store (`.lvkit/`)
|
|
198
206
|
|
|
199
207
|
You can supplement the bundled mappings with a `.lvkit/` directory in your project root. lvkit reads `.lvkit/` first and falls back to its bundled data.
|
|
@@ -222,8 +230,7 @@ Apache-2.0. See [LICENSE](LICENSE).
|
|
|
222
230
|
|
|
223
231
|
## Further reading
|
|
224
232
|
|
|
225
|
-
- [`docs/
|
|
226
|
-
- [`docs/
|
|
227
|
-
- [`docs/
|
|
228
|
-
- [`docs/demo-script.md`](docs/demo-script.md) — 30-minute demo script / tutorial
|
|
233
|
+
- [`docs/reference/`](docs/reference/index.md) — per-command reference for the full CLI
|
|
234
|
+
- [`docs/reference/netlist.md`](docs/reference/netlist.md) — the netlist text syntax (`describe --verbose`, `diff`)
|
|
235
|
+
- [`docs/reference/subvi-resolution.md`](docs/reference/subvi-resolution.md) — how lvkit finds SubVIs and libraries
|
|
229
236
|
- [pylabview](https://github.com/mefistotelis/pylabview) — the VI binary parser lvkit builds on
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
# lvkit
|
|
2
2
|
|
|
3
|
-
Read,
|
|
3
|
+
Read, render, document, and diff LabVIEW VI files — no LabVIEW license required.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
lvkit parses `.vi`, `.ctl`, `.lvclass`, `.lvlib`, and `.lvproj` files directly into queryable dependency and dataflow graphs. From that graph it renders block diagrams, describes what a VI does, generates documentation, and diffs two versions — from a terminal, in CI, or through an AI agent. It can also generate a Python transliteration of a VI, though that path is experimental and how far it gets varies from VI to VI.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> **Reads, never writes.** lvkit only ever reads a VI — it never modifies, re-saves, or edits one. Every capability, including convert, parses the source and writes to a *separate* output file; your `.vi` files are never touched, and LabVIEW stays the only thing that authors them.
|
|
8
8
|
|
|
9
|
-
> **
|
|
10
|
-
|
|
11
|
-
> **Independent, clean-room project — not affiliated with NI.** lvkit was built using **only publicly available information**: public NI documentation, the open-source [pylabview](https://github.com/mefistotelis/pylabview) project, and observation of VI files. It was developed **without installing or running LabVIEW or any NI software**, and with **no NI source code, internal or non-public specifications, or confidential or proprietary materials**. LabVIEW, NI, and National Instruments are trademarks of National Instruments Corporation, used here only to identify the format lvkit interoperates with; lvkit is not affiliated with, authorized by, endorsed by, or sponsored by NI. See [Cleanroom approach](#cleanroom-approach), [`NOTICE`](NOTICE), and [`PROVENANCE.md`](PROVENANCE.md).
|
|
9
|
+
> **Independent, clean-room project — not affiliated with NI.** Built from public NI documentation, the open-source [pylabview](https://github.com/mefistotelis/pylabview) parser, and observation of VI files — with no LabVIEW install and no NI source or non-public specifications. Details in [Cleanroom approach](#cleanroom-approach); see also [`NOTICE`](NOTICE) and [`PROVENANCE.md`](PROVENANCE.md).
|
|
12
10
|
|
|
13
11
|
## Contents
|
|
14
12
|
|
|
15
13
|
- [Quick Start](#quick-start)
|
|
16
14
|
- [What you can do with it](#what-you-can-do-with-it)
|
|
17
15
|
- [How it works](#how-it-works)
|
|
18
|
-
- [
|
|
16
|
+
- [Editor and AI integration](#editor-and-ai-integration)
|
|
19
17
|
- [Cleanroom approach](#cleanroom-approach)
|
|
20
18
|
- [Development](#development)
|
|
21
19
|
|
|
@@ -36,22 +34,23 @@ For a global install: `pipx install lvkit` or `uv tool install lvkit`.
|
|
|
36
34
|
|
|
37
35
|
| Command | Description |
|
|
38
36
|
|---------|-------------|
|
|
39
|
-
| `lvkit describe` | Human-readable VI
|
|
37
|
+
| `lvkit describe` | Human-readable VI summary: signature, operations, dataflow, structures |
|
|
38
|
+
| `lvkit render` | Render a VI's block diagram to a faithful SVG (or interactive HTML) |
|
|
39
|
+
| `lvkit diff` | Compare two VI versions — operations, structures, wiring, constants |
|
|
40
40
|
| `lvkit docs` | Generate cross-referenced HTML documentation |
|
|
41
|
-
| `lvkit
|
|
42
|
-
| `lvkit visualize` | Interactive dependency or data flow graph HTML |
|
|
41
|
+
| `lvkit visualize` | Interactive dataflow or dependency graph (HTML) |
|
|
43
42
|
| `lvkit generate` | Generate Python from a VI, library, or class (experimental — see [Cleanroom approach](#cleanroom-approach)) |
|
|
44
|
-
| `lvkit
|
|
45
|
-
| `lvkit
|
|
46
|
-
| `lvkit setup` | Install AI agent skills; create `.lvkit/` resolution store |
|
|
43
|
+
| `lvkit structure` | Inspect `.lvproj`, `.lvlib`, or `.lvclass` structure |
|
|
44
|
+
| `lvkit detect` | Detect a locally installed LabVIEW and its `vi.lib` / `user.lib` |
|
|
45
|
+
| `lvkit setup` | Install AI agent skills; create the `.lvkit/` resolution store |
|
|
47
46
|
| `lvkit mcp` | Start the MCP server for IDE integration |
|
|
48
47
|
|
|
49
|
-
`
|
|
48
|
+
`describe`, `render`, `diff`, `docs`, and `visualize` work from the graph alone and need no semantic mappings. Only `generate` requires primitive/vi.lib mappings. `lvkit visualize` needs the pyvis extra: `pip install lvkit[visualize]`.
|
|
50
49
|
|
|
51
50
|
## What you can do with it
|
|
52
51
|
|
|
53
52
|
### Describe what a VI does
|
|
54
|
-
|
|
53
|
+
A human-readable signature, inputs/outputs, operations, and control flow — without opening LabVIEW.
|
|
55
54
|
|
|
56
55
|
```
|
|
57
56
|
lvkit describe <path-to.vi> [--search-path <libraries/>] [--verbose]
|
|
@@ -59,24 +58,33 @@ lvkit describe <path-to.vi> [--search-path <libraries/>] [--verbose]
|
|
|
59
58
|
|
|
60
59
|
`--verbose` adds a full netlist — a text projection of the block diagram.
|
|
61
60
|
|
|
62
|
-
###
|
|
63
|
-
|
|
61
|
+
### Render the block diagram
|
|
62
|
+
Render a VI's block diagram to a faithful, self-contained SVG — nodes, wires, structures, and SubVI icons, all drawn from the parsed graph. No LabVIEW, no screenshots.
|
|
64
63
|
|
|
65
64
|
```
|
|
66
|
-
lvkit
|
|
65
|
+
lvkit render <path-to.vi> [--format {svg,html}] [--theme {light,dark,auto}] [--search-path <libraries/>]
|
|
67
66
|
```
|
|
68
67
|
|
|
68
|
+
`--format html` writes an interactive single-VI viewer with zoom/pan and a light/dark diagram-theme toggle; hover a node to see its connector pane and documentation. The same renderer powers `diff --format html` and the VS Code extension.
|
|
69
|
+
|
|
69
70
|
### Diff two versions of a VI
|
|
70
71
|
See what changed between two `.vi` files — added/removed operations and structures, rewired connections, changed constants. Useful in code review and CI.
|
|
71
72
|
|
|
72
73
|
```
|
|
73
|
-
lvkit diff <vi-a> <vi-b> [--format {text,json,html}]
|
|
74
|
+
lvkit diff <vi-a> <vi-b> [--format {text,json,html}] [--open]
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
Text
|
|
77
|
+
Text (the default) is a concise, logical change summary for stdout or CI. `--format json` emits a UID-correlated change map for scripts or an AI agent. `--format html` (or `--open`) writes an **interactive diff viewer** — synced before/after panes, click a change to spotlight it, deep-linkable.
|
|
78
|
+
|
|
79
|
+
### Generate documentation
|
|
80
|
+
Cross-referenced HTML docs for a `.vi`, `.lvlib`, or `.lvclass` — inputs, outputs, operations, wiring diagrams.
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
lvkit docs <input-path> <output-dir> [--search-path <libraries/>]
|
|
84
|
+
```
|
|
77
85
|
|
|
78
|
-
### Generate Python
|
|
79
|
-
|
|
86
|
+
### Generate Python (experimental)
|
|
87
|
+
This is lvkit's least mature capability, and the one most likely to need hand-finishing. It walks the graph to emit a Python transliteration of a `.vi`, `.lvlib`, or `.lvclass`. The transliteration is deterministic (same VI in, same Python out, no LLM), but coverage is incremental: a VI only converts cleanly when every primitive and vi.lib VI it uses is already mapped, so how far it gets varies widely. Expect to resolve unknowns and clean up the output — see [Cleanroom approach](#cleanroom-approach).
|
|
80
88
|
|
|
81
89
|
```
|
|
82
90
|
lvkit generate <input-path> -o <output-dir> [--search-path <libraries>] [--placeholder-on-unresolved]
|
|
@@ -84,23 +92,23 @@ lvkit generate <input-path> -o <output-dir> [--search-path <libraries>] [--place
|
|
|
84
92
|
|
|
85
93
|
`--placeholder-on-unresolved` lets the build succeed when mappings are missing — unresolved calls become inline `raise PrimitiveResolutionNeeded(...)` in the output so you can track them down at runtime.
|
|
86
94
|
|
|
87
|
-
Coverage is incremental and results will vary — see [Cleanroom approach](#cleanroom-approach) for what that means in practice.
|
|
88
|
-
|
|
89
95
|
## How it works
|
|
90
96
|
|
|
91
97
|
lvkit reads VI binaries directly — no LabVIEW installation required. The pipeline has three stages:
|
|
92
98
|
|
|
93
99
|
1. **Parse** — the VI binary is extracted to XML (via [pylabview](https://github.com/mefistotelis/pylabview)), then parsed into a typed representation of the block diagram: nodes, wires, constants, types, and front panel terminals.
|
|
94
100
|
|
|
95
|
-
2. **Graph** — all loaded VIs are linked into a graph that captures two things: the dependency tree (which VIs call which) and the dataflow within each VI (how data moves between operations). This is what `describe`, `
|
|
101
|
+
2. **Graph** — all loaded VIs are linked into a graph that captures two things: the dependency tree (which VIs call which) and the dataflow within each VI (how data moves between operations). This is what `describe`, `render`, `diff`, `docs`, and `visualize` query — no semantic mappings needed.
|
|
102
|
+
|
|
103
|
+
3. **Generate** — the graph is walked deterministically to produce Python source, HTML documentation, or diagrams. Code generation is pure AST construction: same VI in, same output every run, no LLM.
|
|
96
104
|
|
|
97
|
-
|
|
105
|
+
See the [command reference](docs/reference/index.md) for full, flag-level docs on every command.
|
|
98
106
|
|
|
99
|
-
|
|
107
|
+
## Editor and AI integration
|
|
100
108
|
|
|
101
|
-
|
|
109
|
+
The CLI works standalone from any terminal or CI script. For deeper integration, lvkit ships three optional layers.
|
|
102
110
|
|
|
103
|
-
|
|
111
|
+
**VS Code extension** — [**LVKit** on the Marketplace](https://marketplace.visualstudio.com/items?itemName=pragmatest.lvkit). Opening a `.vi` renders its block diagram inline instead of the "binary file" placeholder, and clicking a SubVI opens it. **Open Visual Diff** — right-click a changed `.vi` in Source Control, the Explorer, or the editor tab — shows a before/after block-diagram diff for code review. It ships a bundled lvkit, so it works with no separate install; point the `lvkit.path` setting at a project venv or a PATH install to use your own instead. `.vi` files only today.
|
|
104
112
|
|
|
105
113
|
**AI agent skills** — install lvkit's built-in workflows into Claude Code or Copilot so your AI agent can describe VIs, convert them, and resolve unknowns without you writing prompts. All five workflows call the CLI under the hood — no MCP server required.
|
|
106
114
|
|
|
@@ -125,16 +133,16 @@ Five workflows ship: `lvkit-describe`, `lvkit-convert`, `lvkit-resolve-primitive
|
|
|
125
133
|
|
|
126
134
|
| Tool | Description |
|
|
127
135
|
|------|-------------|
|
|
128
|
-
| `load` | Load VI into the in-memory graph |
|
|
136
|
+
| `load` | Load a VI into the in-memory graph |
|
|
129
137
|
| `list_loaded` | List loaded VIs |
|
|
138
|
+
| `clear` | Clear the in-memory graph |
|
|
130
139
|
| `get_context` | Full VI context: inputs, outputs, operations, wires |
|
|
131
|
-
| `generate_ast_code` | Generate Python from a loaded VI |
|
|
132
140
|
| `describe` | Human-readable VI description |
|
|
133
141
|
| `get_operations` | List operations in a VI |
|
|
134
142
|
| `get_dataflow` | Show wire connections |
|
|
135
143
|
| `get_structure` | Inspect a structure node (loop, case, sequence) |
|
|
136
144
|
| `get_constants` | List constant values |
|
|
137
|
-
| `
|
|
145
|
+
| `generate_ast_code` | Generate Python from a loaded VI |
|
|
138
146
|
| `generate_documents` | Generate HTML docs for VIs/libraries (stateless) |
|
|
139
147
|
| `generate_python` | Generate Python from a VI (stateless) |
|
|
140
148
|
|
|
@@ -147,7 +155,9 @@ These mappings are **lvkit's own definitions, derived purely by inference** from
|
|
|
147
155
|
1. **Public NI documentation** — pages published openly on ni.com, accessed with no login, partner portal, or NDA gate.
|
|
148
156
|
2. **The VI XML produced by [pylabview](https://github.com/mefistotelis/pylabview)** — the open-source parser that extracts the VI binary to XML. That XML is lvkit's *only* window into the format; lvkit has no other view of it.
|
|
149
157
|
|
|
150
|
-
The definitions are open source and fully inspectable, and many carry a note recording how each was inferred (the public doc consulted, the observed terminal signature, a `verified`/`guess_reason` marker). They are **best-effort inferences, not authoritative** — they have been wrong and corrected over time. That imperfection is a direct consequence of working from *only* public documentation and the pylabview XML, with **no access to any internal or authoritative NI specification
|
|
158
|
+
The definitions are open source and fully inspectable, and many carry a note recording how each was inferred (the public doc consulted, the observed terminal signature, a `verified`/`guess_reason` marker). They are **best-effort inferences, not authoritative** — they have been wrong and corrected over time. That imperfection is a direct consequence of working from *only* public documentation and the pylabview XML, with **no access to any internal or authoritative NI specification**.
|
|
159
|
+
|
|
160
|
+
Coverage is incremental. When `lvkit generate` encounters an unmapped primitive or vi.lib VI, it raises an error with diagnostic context so the mapping can be added. `describe`, `render`, `diff`, `docs`, and `visualize` are unaffected — they work from the graph, not the semantic mappings.
|
|
151
161
|
|
|
152
162
|
### Provenance
|
|
153
163
|
|
|
@@ -157,8 +167,6 @@ lvkit was developed **using only publicly available information** and clean-room
|
|
|
157
167
|
|
|
158
168
|
LabVIEW, NI, and National Instruments are trademarks of National Instruments Corporation. lvkit is an independent project and is not affiliated with, authorized by, endorsed by, or sponsored by NI. Those names are used only to identify the file format and software lvkit interoperates with (nominative use).
|
|
159
169
|
|
|
160
|
-
Coverage is incremental. When `lvkit generate` encounters an unmapped primitive or vi.lib VI, it raises an error with diagnostic context so the mapping can be added. `describe`, `docs`, `diff`, and `visualize` are unaffected — they work from the graph, not the semantic mappings.
|
|
161
|
-
|
|
162
170
|
### Project-local resolution store (`.lvkit/`)
|
|
163
171
|
|
|
164
172
|
You can supplement the bundled mappings with a `.lvkit/` directory in your project root. lvkit reads `.lvkit/` first and falls back to its bundled data.
|
|
@@ -187,8 +195,7 @@ Apache-2.0. See [LICENSE](LICENSE).
|
|
|
187
195
|
|
|
188
196
|
## Further reading
|
|
189
197
|
|
|
190
|
-
- [`docs/
|
|
191
|
-
- [`docs/
|
|
192
|
-
- [`docs/
|
|
193
|
-
- [`docs/demo-script.md`](docs/demo-script.md) — 30-minute demo script / tutorial
|
|
198
|
+
- [`docs/reference/`](docs/reference/index.md) — per-command reference for the full CLI
|
|
199
|
+
- [`docs/reference/netlist.md`](docs/reference/netlist.md) — the netlist text syntax (`describe --verbose`, `diff`)
|
|
200
|
+
- [`docs/reference/subvi-resolution.md`](docs/reference/subvi-resolution.md) — how lvkit finds SubVIs and libraries
|
|
194
201
|
- [pylabview](https://github.com/mefistotelis/pylabview) — the VI binary parser lvkit builds on
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# LVKit
|
|
1
|
+
# LVKit - View and diff VIs without LabVIEW
|
|
2
2
|
|
|
3
3
|
View `.vi` block diagrams and **visual before/after diffs** directly in VS Code, on a
|
|
4
4
|
machine with **no LabVIEW installed**. Powered by [LVKit](https://pragmatest.com), an open-source tool from **[PragmaTest](https://pragmatest.com)**.
|
|
@@ -12,11 +12,11 @@ Both open **read-only** in an editor tab, with zoom/pan and a light/dark diagram
|
|
|
12
12
|
|
|
13
13
|
Nodes LVKit doesn't have a glyph for yet render as labeled boxes.
|
|
14
14
|
|
|
15
|
-
**View**
|
|
15
|
+
**View** - click a `.vi` in the Explorer.
|
|
16
16
|
|
|
17
17
|

|
|
18
18
|
|
|
19
|
-
**Diff**
|
|
19
|
+
**Diff** - right-click a changed `.vi` and select "Open Visual Diff"
|
|
20
20
|
|
|
21
21
|

|
|
22
22
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lvkit"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.4"
|
|
8
8
|
description = "Understand and convert LabVIEW VIs to Python without a LabVIEW license"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -107,5 +107,13 @@ target-version = "py310"
|
|
|
107
107
|
[tool.ruff.lint]
|
|
108
108
|
select = ["E", "F", "I", "UP"]
|
|
109
109
|
|
|
110
|
+
[tool.ruff.lint.per-file-ignores]
|
|
111
|
+
# Auxiliary generators/utilities (not shipped in the package) whose long lines
|
|
112
|
+
# are inherent literal content — SVG path/format strings.
|
|
113
|
+
"branding/*.py" = ["E501"]
|
|
114
|
+
|
|
110
115
|
[tool.pytest.ini_options]
|
|
111
116
|
testpaths = ["tests"]
|
|
117
|
+
markers = [
|
|
118
|
+
"needs_samples: test reads the local-only sample VI corpus (.lvkit/cache/samples). Auto-skipped when the corpus is absent; pull it with scripts/pull_samples.sh.",
|
|
119
|
+
]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Enable ``python -m lvkit`` — the invocation the VS Code extension uses when
|
|
2
|
+
running lvkit through a bundled (signed) Python interpreter, since a generated
|
|
3
|
+
console-script ``.exe`` gets blocked by Windows Application Control policies."""
|
|
4
|
+
|
|
5
|
+
from lvkit.cli import main
|
|
6
|
+
|
|
7
|
+
raise SystemExit(main())
|
|
@@ -25,13 +25,28 @@ import). It is idempotent.
|
|
|
25
25
|
|
|
26
26
|
from __future__ import annotations
|
|
27
27
|
|
|
28
|
+
import warnings
|
|
28
29
|
from decimal import Decimal
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
import
|
|
31
|
+
# pylabview's LVheap.py uses invalid escape sequences in plain strings (e.g.
|
|
32
|
+
# re.match("^\\(...")), which Python 3.12+ emits as a *compile-time*
|
|
33
|
+
# SyntaxWarning — and a future Python turns into a hard SyntaxError. A runtime
|
|
34
|
+
# monkeypatch can't touch a compile-time warning; it fires the moment pylabview
|
|
35
|
+
# is first compiled, which is the import below (this module is imported ahead of
|
|
36
|
+
# pylabview by lvkit.extractor). A ``module=``-scoped filter does NOT catch it —
|
|
37
|
+
# a compile-time SyntaxWarning is attributed to the *importer's* frame, not
|
|
38
|
+
# "pylabview.LVheap", so only a category filter active during the compile
|
|
39
|
+
# matches (verified on Windows: module-scoped passes through, category-only
|
|
40
|
+
# suppresses). We scope that category filter to exactly this import with
|
|
41
|
+
# catch_warnings, leaving the process-wide filter state untouched. The real fix
|
|
42
|
+
# is owning the pylabview source (fork/vendor) and correcting it — task #81.
|
|
43
|
+
with warnings.catch_warnings():
|
|
44
|
+
warnings.simplefilter("ignore", SyntaxWarning)
|
|
45
|
+
import pylabview.LVblock as _lv_block # type: ignore[import-untyped]
|
|
46
|
+
import pylabview.LVdatatype as _lv_datatype # type: ignore[import-untyped]
|
|
47
|
+
import pylabview.LVmisc as _lv_misc # type: ignore[import-untyped]
|
|
48
|
+
import pylabview.LVrsrcontainer as _lv_rsrc # type: ignore[import-untyped]
|
|
49
|
+
import pylabview.LVxml as _lv_xml # type: ignore[import-untyped]
|
|
35
50
|
|
|
36
51
|
_installed = False
|
|
37
52
|
|