meltygui 0.1.0__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.
- meltygui/__init__.py +107 -0
- meltygui/accounts/__init__.py +0 -0
- meltygui/accounts/internet_accounts.py +1355 -0
- meltygui/chat/__init__.py +91 -0
- meltygui/chat/activity.py +75 -0
- meltygui/chat/backends.py +36 -0
- meltygui/chat/chat_interface.py +732 -0
- meltygui/chat/chat_proxy.py +352 -0
- meltygui/chat/codex_proxy.py +592 -0
- meltygui/chat/codex_settings.py +100 -0
- meltygui/chat/codex_transport.py +60 -0
- meltygui/chat/command_parser.py +204 -0
- meltygui/chat/images.py +227 -0
- meltygui/chat/messages.py +417 -0
- meltygui/chat/metadata.py +139 -0
- meltygui/chat/writer_locks.py +64 -0
- meltygui/code/__init__.py +0 -0
- meltygui/code/basic_converters.py +533 -0
- meltygui/code/chain_converters.py +2111 -0
- meltygui/code/code_checks.py +2209 -0
- meltygui/code/core_syntax.py +1430 -0
- meltygui/code/file_converters.py +1933 -0
- meltygui/code/fileref.py +702 -0
- meltygui/code/hotswap_guard.py +144 -0
- meltygui/code/libcst_conversion.py +9724 -0
- meltygui/code/live_instrument.py +392 -0
- meltygui/code/live_view.py +2490 -0
- meltygui/code/melty_scan.py +2684 -0
- meltygui/code/new_codecs.py +1255 -0
- meltygui/code/new_converters.py +3017 -0
- meltygui/code/project_code.py +278 -0
- meltygui/code/source_context.py +63 -0
- meltygui/code/symbol_roster.py +1588 -0
- meltygui/code/syntax_check.py +34 -0
- meltygui/code/syntax_check_worker.py +114 -0
- meltygui/completion/__init__.py +0 -0
- meltygui/completion/fim.py +1232 -0
- meltygui/completion/fim_context.py +481 -0
- meltygui/completion/providers/__init__.py +0 -0
- meltygui/completion/providers/anthropic_oauth.py +446 -0
- meltygui/completion/providers/anthropic_requests.py +69 -0
- meltygui/completion/providers/claude.py +203 -0
- meltygui/completion/providers/claude_usage.py +499 -0
- meltygui/completion/providers/codex_accounts.py +180 -0
- meltygui/completion/providers/copilot.py +617 -0
- meltygui/completion/providers/oauth_popup.py +220 -0
- meltygui/completion/providers/ollama.py +320 -0
- meltygui/completion/providers/profiles.py +23 -0
- meltygui/core/README.md +88 -0
- meltygui/core/__init__.py +1 -0
- meltygui/core/automation/__init__.py +1 -0
- meltygui/core/automation/action_core.py +153 -0
- meltygui/core/automation/collection_action.py +45 -0
- meltygui/core/automation/mcp_eval.py +167 -0
- meltygui/core/automation/mcp_hotswap.py +107 -0
- meltygui/core/automation/mcp_query.py +552 -0
- meltygui/core/automation/mcp_server.py +657 -0
- meltygui/core/automation/orchestration_core.py +2636 -0
- meltygui/core/automation/query_core.py +124 -0
- meltygui/core/automation/search_core.py +26 -0
- meltygui/core/automation/selector_core.py +340 -0
- meltygui/core/automation/value_core.py +1752 -0
- meltygui/core/cache/__init__.py +1 -0
- meltygui/core/cache/cache_diagnostics.py +0 -0
- meltygui/core/cache/invalidation_decoration.py +153 -0
- meltygui/core/cache/invalidation_tracker.py +43 -0
- meltygui/core/cache/tile_cache.py +5968 -0
- meltygui/core/conversion/__init__.py +1 -0
- meltygui/core/conversion/bubbling.py +599 -0
- meltygui/core/conversion/cache_tree.py +188 -0
- meltygui/core/conversion/chain.py +113 -0
- meltygui/core/conversion/converter_register.py +145 -0
- meltygui/core/conversion/data_decoration.py +67 -0
- meltygui/core/conversion/dict_conversion.py +1815 -0
- meltygui/core/conversion/dict_conversion_util.py +177 -0
- meltygui/core/conversion/dynamic_obj.py +89 -0
- meltygui/core/conversion/graph_compare.py +183 -0
- meltygui/core/conversion/load_save_v2.py +1032 -0
- meltygui/core/conversion/missing_saved_class.py +39 -0
- meltygui/core/conversion/path_finder.py +606 -0
- meltygui/core/conversion/render_host.py +1083 -0
- meltygui/core/core_render.py +6537 -0
- meltygui/core/definition_hotswap.py +231 -0
- meltygui/core/diagnostics/__init__.py +1 -0
- meltygui/core/diagnostics/attribute_churn.py +38 -0
- meltygui/core/diagnostics/fps_counter.py +39 -0
- meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
- meltygui/core/diagnostics/inspection_core.py +169 -0
- meltygui/core/diagnostics/monitor_core.py +105 -0
- meltygui/core/diagnostics/notifications.py +706 -0
- meltygui/core/diagnostics/perf_trace.py +281 -0
- meltygui/core/diagnostics/profile_decoration.py +88 -0
- meltygui/core/diagnostics/resize_trace.py +62 -0
- meltygui/core/diagnostics/screenshot_core.py +247 -0
- meltygui/core/diagnostics/session_status.py +98 -0
- meltygui/core/diagnostics/trace_core.py +445 -0
- meltygui/core/files/__init__.py +1 -0
- meltygui/core/files/file_core.py +208 -0
- meltygui/core/files/file_explorer_core.py +104 -0
- meltygui/core/files/file_tree_core.py +198 -0
- meltygui/core/files/file_watch_core.py +43 -0
- meltygui/core/files/import_graph_core.py +43 -0
- meltygui/core/files/metadata_core.py +51 -0
- meltygui/core/graphics/__init__.py +1 -0
- meltygui/core/graphics/cuda_context_core.py +166 -0
- meltygui/core/graphics/cuda_interop_core.py +136 -0
- meltygui/core/graphics/cuda_kernel_core.py +91 -0
- meltygui/core/graphics/framebuffer_recorder.py +337 -0
- meltygui/core/graphics/gl_state.py +658 -0
- meltygui/core/graphics/lut_core.py +52 -0
- meltygui/core/graphics/overlay_renderer.py +984 -0
- meltygui/core/graphics/scene_target.py +180 -0
- meltygui/core/graphics/screenshot.py +439 -0
- meltygui/core/graphics/shader_func.py +478 -0
- meltygui/core/graphics/tensor_core.py +45 -0
- meltygui/core/graphics/text_texture.py +329 -0
- meltygui/core/graphics/wayland_color.py +635 -0
- meltygui/core/input/__init__.py +1 -0
- meltygui/core/input/collision.py +165 -0
- meltygui/core/input/drag_drop_core.py +1525 -0
- meltygui/core/input/hypr_left_drag.py +323 -0
- meltygui/core/input/input_core.py +245 -0
- meltygui/core/input/input_handler.py +1101 -0
- meltygui/core/input/mouse_cursor.py +355 -0
- meltygui/core/input/pynput_backend.py +1054 -0
- meltygui/core/input/space_mouse.py +338 -0
- meltygui/core/input/touchpad_backend.py +393 -0
- meltygui/core/input/view_selection.py +177 -0
- meltygui/core/layout/__init__.py +1 -0
- meltygui/core/layout/column_core.py +2153 -0
- meltygui/core/layout/cursor_core.py +161 -0
- meltygui/core/layout/dropdown_core.py +278 -0
- meltygui/core/layout/edge_constraints.py +155 -0
- meltygui/core/layout/grid_core.py +117 -0
- meltygui/core/layout/header_core.py +23 -0
- meltygui/core/layout/header_runtime.py +67 -0
- meltygui/core/layout/layout_core.py +87 -0
- meltygui/core/layout/tile_manager_core.py +591 -0
- meltygui/core/melty.py +6726 -0
- meltygui/core/module_map.json +896 -0
- meltygui/core/module_names.py +19 -0
- meltygui/core/rendering/__init__.py +1 -0
- meltygui/core/rendering/core_decoration.py +431 -0
- meltygui/core/rendering/core_render_helpers.py +328 -0
- meltygui/core/rendering/func_metadata.py +398 -0
- meltygui/core/rendering/mode.py +818 -0
- meltygui/core/rendering/mode_defaults.py +41 -0
- meltygui/core/rendering/modes.py +136 -0
- meltygui/core/rendering/parameter_core.py +1665 -0
- meltygui/core/rendering/render_dispatch.py +1891 -0
- meltygui/core/rendering/render_funcs.py +273 -0
- meltygui/core/rendering/shaped.py +312 -0
- meltygui/core/rendering/window_decoration.py +25 -0
- meltygui/core/runtime/__init__.py +1 -0
- meltygui/core/runtime/app.py +735 -0
- meltygui/core/runtime/app_session.py +140 -0
- meltygui/core/runtime/background.py +564 -0
- meltygui/core/runtime/extensions.py +53 -0
- meltygui/core/runtime/gc_manager.py +1163 -0
- meltygui/core/runtime/lifecycle.py +21 -0
- meltygui/core/runtime/paths.py +27 -0
- meltygui/core/runtime/settings.py +14 -0
- meltygui/core/runtime/singleton.py +16 -0
- meltygui/core/runtime/thread_safe_bool.py +24 -0
- meltygui/core/runtime/thread_signal.py +30 -0
- meltygui/core/runtime/toggles.py +3115 -0
- meltygui/core/services/__init__.py +1 -0
- meltygui/core/services/account_core.py +10 -0
- meltygui/core/services/chat_core.py +19 -0
- meltygui/core/services/claude_terminal_core.py +346 -0
- meltygui/core/services/terminal_core.py +458 -0
- meltygui/core/services/terminal_runtime.py +78 -0
- meltygui/core/styling/__init__.py +1 -0
- meltygui/core/styling/color_core.py +46 -0
- meltygui/core/styling/fonts.py +639 -0
- meltygui/core/styling/global_style.py +338 -0
- meltygui/core/styling/style.py +198 -0
- meltygui/core/styling/style_core.py +522 -0
- meltygui/core/styling/warm_start.py +149 -0
- meltygui/core/windowing/__init__.py +1 -0
- meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
- meltygui/core/windowing/backends/__init__.py +1 -0
- meltygui/core/windowing/backends/imgui_renderer.py +138 -0
- meltygui/core/windowing/backends/native_wayland.py +850 -0
- meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
- meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
- meltygui/core/windowing/backends/wayland_protocol.py +101 -0
- meltygui/core/windowing/dock_core.py +182 -0
- meltygui/core/windowing/frame_geometry.py +42 -0
- meltygui/core/windowing/geometry_feed.py +864 -0
- meltygui/core/windowing/glfw_utils.py +1343 -0
- meltygui/core/windowing/os_frame.py +1552 -0
- meltygui/core/windowing/surface.py +643 -0
- meltygui/core/windowing/titlebar.py +1560 -0
- meltygui/core/windowing/titlebar_buttons.py +281 -0
- meltygui/core/windowing/wayland_move.py +932 -0
- meltygui/core/windowing/window_api.py +62 -0
- meltygui/core/windowing/window_constants.py +339 -0
- meltygui/core/windowing/window_visibility.py +162 -0
- meltygui/debug/__init__.py +0 -0
- meltygui/debug/app_view_utils.py +9 -0
- meltygui/editor/__init__.py +0 -0
- meltygui/editor/bash_syntax.py +30 -0
- meltygui/editor/code_line_fast.py +152 -0
- meltygui/editor/diff.py +139 -0
- meltygui/editor/external_changes.py +159 -0
- meltygui/editor/file_header.py +41 -0
- meltygui/editor/live_usage.py +169 -0
- meltygui/editor/live_view_views.py +1803 -0
- meltygui/editor/pending_save.py +1351 -0
- meltygui/editor/roster_tints.py +547 -0
- meltygui/editor/source_preview.py +16 -0
- meltygui/editor/source_tools.py +10 -0
- meltygui/editor/source_ui.py +70 -0
- meltygui/editor/spell_check.py +77 -0
- meltygui/editor/text_editor.py +9076 -0
- meltygui/editor/usage_picker.py +538 -0
- meltygui/events/__init__.py +0 -0
- meltygui/events/example.py +118 -0
- meltygui/examples/__init__.py +0 -0
- meltygui/examples/columns_demo.py +33 -0
- meltygui/examples/columns_window_demo.py +60 -0
- meltygui/examples/context_menu_demo.py +24 -0
- meltygui/examples/context_menu_window_demo.py +49 -0
- meltygui/examples/gui_playground.py +127 -0
- meltygui/examples/live_view_playground.py +256 -0
- meltygui/examples/lora.py +43 -0
- meltygui/examples/lora_data.py +59 -0
- meltygui/examples/lora_policies.py +67 -0
- meltygui/examples/mode_demo.py +49 -0
- meltygui/examples/modifies_demo.py +119 -0
- meltygui/examples/scalar_policies.py +60 -0
- meltygui/examples/style_layouts.py +149 -0
- meltygui/examples/tile_manager_demo.py +74 -0
- meltygui/examples/tint_demo.py +160 -0
- meltygui/examples/tint_functions.py +56 -0
- meltygui/examples/trace_demo.py +88 -0
- meltygui/examples/two_windows.py +36 -0
- meltygui/files/__init__.py +0 -0
- meltygui/files/fast_file_explorer.py +439 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
- meltygui/graphics/__init__.py +6 -0
- meltygui/graphics/base.py +85 -0
- meltygui/graphics/examples.py +507 -0
- meltygui/graphics/executor.py +520 -0
- meltygui/graphics/filter.py +667 -0
- meltygui/graphics/filter.pyi +856 -0
- meltygui/graphics/generate_stubs.py +22 -0
- meltygui/graphics/registry.py +203 -0
- meltygui/graphics/shader_compiler.py +155 -0
- meltygui/graphics/shaders.py +1302 -0
- meltygui/graphics/stub_generator.py +250 -0
- meltygui/graphics/texture_manager.py +170 -0
- meltygui/graphics/texture_min_max.py +295 -0
- meltygui/hdr_color.py +757 -0
- meltygui/image_load.py +308 -0
- meltygui/model/__init__.py +1 -0
- meltygui/model/account_model.py +142 -0
- meltygui/model/camera_model.py +159 -0
- meltygui/model/chat_model.py +86 -0
- meltygui/model/code_model.py +62 -0
- meltygui/model/code_proxy_model.py +876 -0
- meltygui/model/collection_model.py +41 -0
- meltygui/model/color_model.py +127 -0
- meltygui/model/cuda_tensor_model.py +36 -0
- meltygui/model/cuda_texture_model.py +149 -0
- meltygui/model/dropdown_model.py +137 -0
- meltygui/model/file_metadata_model.py +73 -0
- meltygui/model/file_model.py +289 -0
- meltygui/model/format_model.py +390 -0
- meltygui/model/graph_model.py +98 -0
- meltygui/model/icon_model.py +1024 -0
- meltygui/model/import_graph_model.py +468 -0
- meltygui/model/layout_model.py +15 -0
- meltygui/model/lut_model.py +266 -0
- meltygui/model/search_model.py +173 -0
- meltygui/model/tensor_model.py +402 -0
- meltygui/model/terminal_model.py +329 -0
- meltygui/model/texture_model.py +146 -0
- meltygui/model/tile_model.py +24 -0
- meltygui/model/trace_model.py +198 -0
- meltygui/model/trace_report_model.py +146 -0
- meltygui/models/__init__.py +0 -0
- meltygui/models/file_meta.py +613 -0
- meltygui/models/function_console.py +184 -0
- meltygui/models/orchestration.py +48 -0
- meltygui/pbr.py +1576 -0
- meltygui/png_unfilter.c +49 -0
- meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
- meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
- meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
- meltygui/resources/dejavu/LICENSE.txt +78 -0
- meltygui/resources/fontawesome-LICENSE.txt +121 -0
- meltygui/resources/fontawesome-webfont.ttf +0 -0
- meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
- meltygui/resources/jetbrains-weights/OFL.txt +93 -0
- meltygui/resources/jetbrains-weights/README.md +2 -0
- meltygui/state/__init__.py +0 -0
- meltygui/state/account_state.py +22 -0
- meltygui/state/animation_state.py +97 -0
- meltygui/state/annotation_state.py +22 -0
- meltygui/state/chat_state.py +36 -0
- meltygui/state/code_state.py +10 -0
- meltygui/state/core_enums.py +59 -0
- meltygui/state/core_markers.py +115 -0
- meltygui/state/core_undo.py +1075 -0
- meltygui/state/file_state.py +75 -0
- meltygui/state/graph_state.py +26 -0
- meltygui/state/inspection_state.py +47 -0
- meltygui/state/menu_state.py +10 -0
- meltygui/state/model_enums.py +11 -0
- meltygui/state/new_core_model.py +2394 -0
- meltygui/state/orchestration_state.py +14 -0
- meltygui/state/query_state.py +24 -0
- meltygui/state/tensor_state.py +10 -0
- meltygui/state/terminal_state.py +23 -0
- meltygui/state/trace_state.py +32 -0
- meltygui/state/voxel_state.py +12 -0
- meltygui/text_index.py +816 -0
- meltygui/utils/__init__.py +0 -0
- meltygui/utils/jump_to_code.py +344 -0
- meltygui/utils/pkl_inspect.py +90 -0
- meltygui/utils/render_utils.py +1419 -0
- meltygui/view/__init__.py +1 -0
- meltygui/view/account_view.py +524 -0
- meltygui/view/action_view.py +73 -0
- meltygui/view/chat_decoration_view.py +112 -0
- meltygui/view/chat_view.py +1703 -0
- meltygui/view/code_view.py +2677 -0
- meltygui/view/collection_view.py +1185 -0
- meltygui/view/color_view.py +824 -0
- meltygui/view/control_view.py +559 -0
- meltygui/view/decoration_view.py +439 -0
- meltygui/view/diagnostic_view.py +177 -0
- meltygui/view/dropdown_view.py +1082 -0
- meltygui/view/file_view.py +1599 -0
- meltygui/view/graph_cuda_view.py +105 -0
- meltygui/view/graph_view.py +882 -0
- meltygui/view/header_view.py +848 -0
- meltygui/view/input_view.py +238 -0
- meltygui/view/inspection_view.py +1783 -0
- meltygui/view/layout_view.py +596 -0
- meltygui/view/lut_view.py +45 -0
- meltygui/view/menu_view.py +212 -0
- meltygui/view/orchestration_view.py +658 -0
- meltygui/view/query_view.py +118 -0
- meltygui/view/search_view.py +342 -0
- meltygui/view/tab_view.py +258 -0
- meltygui/view/tensor_view.py +430 -0
- meltygui/view/terminal_view.py +355 -0
- meltygui/view/text_view.py +8031 -0
- meltygui/view/texture_view.py +480 -0
- meltygui/view/tile_view.py +38 -0
- meltygui/view/trace_view.py +923 -0
- meltygui/view/voxel_cuda_view.py +824 -0
- meltygui/view/voxel_view.py +1860 -0
- meltygui/view/window_view.py +111 -0
- meltygui-0.1.0.dist-info/METADATA +143 -0
- meltygui-0.1.0.dist-info/RECORD +372 -0
- meltygui-0.1.0.dist-info/WHEEL +4 -0
- meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""Transparent function instrumentation: run an in-memory TWIN of a function
|
|
2
|
+
whose every assignment also publishes its value through live_view. The source
|
|
3
|
+
file never changes — the editor keeps showing the original code — and the
|
|
4
|
+
captured values key and attach exactly as hand-written live_view() calls
|
|
5
|
+
would, so the whole marker/window/watcher stack works unchanged.
|
|
6
|
+
|
|
7
|
+
How the rendezvous holds with NO live_view tokens in the file: every injected
|
|
8
|
+
call is stamped with the ORIGINAL line number of the assignment it follows
|
|
9
|
+
(ast.copy_location), and the twin compiles against the real co_filename. At
|
|
10
|
+
publish time, capture-side site resolution for a line that has no live_view
|
|
11
|
+
call falls back to the statement's own CST key — ('x',), ('if##0', 'z'), or a
|
|
12
|
+
('line:N',) key for statements the dict conversion doesn't surface (aug-
|
|
13
|
+
assigns, while/with bodies — which therefore get coverage here that manual
|
|
14
|
+
live_view() calls can't anchor). The store object resolves by co_filename +
|
|
15
|
+
lineno to the ORIGINAL function, not the twin.
|
|
16
|
+
|
|
17
|
+
The twin is rebuilt per code version: the cache keys on the original
|
|
18
|
+
function's CURRENT __code__ identity, and hotswap replaces __code__ in place,
|
|
19
|
+
so an edit + recompile transparently re-instruments from the new source.
|
|
20
|
+
|
|
21
|
+
Out of scope (the twin falls back to running the ORIGINAL uninstrumented
|
|
22
|
+
function): closures (free variables can't be rebuilt standalone), generators
|
|
23
|
+
and async functions. Nested defs/classes/lambdas inside an instrumented
|
|
24
|
+
function run uninstrumented, and recursive calls re-enter the ORIGINAL
|
|
25
|
+
function — only the top frame snapshots.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import ast
|
|
29
|
+
import inspect
|
|
30
|
+
import linecache
|
|
31
|
+
import sys
|
|
32
|
+
import textwrap
|
|
33
|
+
import weakref
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
from meltygui.code.live_view import live_view
|
|
37
|
+
from meltygui.code.live_view import twin_snap
|
|
38
|
+
from meltygui.code.live_view import twin_ret
|
|
39
|
+
from meltygui.code.live_view import _loop_name
|
|
40
|
+
|
|
41
|
+
# id(original __code__) -> ((source mtime, pending gen), twin function |
|
|
42
|
+
# original on fallback). Identity-keyed for the same reason as
|
|
43
|
+
# live_view._sites: code objects compare equal across files; weakref.finalize
|
|
44
|
+
# evicts when hotswap drops the lastref. The signature is the SEAMLESS-EDIT
|
|
45
|
+
# half: the twin builds from the file's IN-MEMORY text (disk + PendingSave
|
|
46
|
+
# splices - deferred saves never reach disk before shutdown), so both a plain
|
|
47
|
+
# disk save (mtime) and a queued editor edit (pending gen) rebuild the twin
|
|
48
|
+
# even though fn.__code__ hasn't been hotswapped - otherwise Run keeps
|
|
49
|
+
# replaying the pre-edit code and "nothing updates".
|
|
50
|
+
_twins = {}
|
|
51
|
+
|
|
52
|
+
_SNAP_NAME = "__lv_view__"
|
|
53
|
+
_RET_NAME = "__lv_ret__"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def run_instrumented(fn, *args, **kwargs):
|
|
57
|
+
"""Call `fn` with assignment snapshots publishing to its live_view store.
|
|
58
|
+
Equivalent to fn(*args, **kwargs) — same return value, same exceptions —
|
|
59
|
+
with the original function running un-instrumented when the source can't
|
|
60
|
+
be transformed (closure/generator/async/unparseable).
|
|
61
|
+
|
|
62
|
+
A successful instrumented run also PRUNES the store (run_capture): the
|
|
63
|
+
twin republishes every assignment the function still contains, so keys
|
|
64
|
+
it didn't touch belong to removed lines and are dropped — values,
|
|
65
|
+
markers, and their orphaned value windows. The uninstrumented fallback
|
|
66
|
+
republishes nothing and must not prune."""
|
|
67
|
+
try:
|
|
68
|
+
target = inspect.unwrap(fn)
|
|
69
|
+
except Exception:
|
|
70
|
+
target = fn
|
|
71
|
+
twin = instrumented_twin(fn)
|
|
72
|
+
try:
|
|
73
|
+
if twin is target:
|
|
74
|
+
return fn(*args, **kwargs)
|
|
75
|
+
from meltygui.code.live_view import run_capture
|
|
76
|
+
# Fresh run, fresh exit line: a raise (or an edit that removed the
|
|
77
|
+
# return the last run took) must not leave a stale green line, and a
|
|
78
|
+
# now-passing run must not keep the previous failure's red one.
|
|
79
|
+
try:
|
|
80
|
+
vars(target).pop("__live_return_line__", None)
|
|
81
|
+
vars(target).pop("__live_error_line__", None)
|
|
82
|
+
except TypeError:
|
|
83
|
+
pass
|
|
84
|
+
with run_capture(target):
|
|
85
|
+
try:
|
|
86
|
+
return twin(*args, **kwargs)
|
|
87
|
+
except BaseException as e:
|
|
88
|
+
_stamp_error_line(target, twin, e)
|
|
89
|
+
# An out-of-memory run leaves the live set holding onto VRAM;
|
|
90
|
+
# arm the deferred sweep (runs after these frames unwind).
|
|
91
|
+
try:
|
|
92
|
+
from meltygui.core.runtime.gc_manager import respond_to_cuda_oom
|
|
93
|
+
respond_to_cuda_oom(e, where=getattr(target, "__name__", "run"))
|
|
94
|
+
except Exception:
|
|
95
|
+
pass
|
|
96
|
+
raise
|
|
97
|
+
finally:
|
|
98
|
+
pass
|
|
99
|
+
# # Retire the PREVIOUS run's garbage: the GUI's gc_manager kee
|
|
100
|
+
# gen2 out of auto-reach, so the cycle-trapped graphs each run
|
|
101
|
+
# replaces (deepcopied components, the old ForwardPassResult) pin
|
|
102
|
+
# their CUDA tensors until an explicit collect. This runs on the
|
|
103
|
+
# run's worker thread, win or lose - the garbage exists either way.ps
|
|
104
|
+
# try:
|
|
105
|
+
# from meltygui.core.runtime.gc_manager import collect_after_run
|
|
106
|
+
# collect_after_run(getattr(target, "__name__", "run"))
|
|
107
|
+
# except Exception:
|
|
108
|
+
# pass
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _stamp_error_line(target, twin, exc):
|
|
112
|
+
"""Stamp `__live_error_line__` = (absolute file line, message, run-time
|
|
113
|
+
line text) on the
|
|
114
|
+
store function for an exception raised during an instrumented run: the
|
|
115
|
+
DEEPEST traceback frame that is the twin's own code — the twin compiles
|
|
116
|
+
against the real co_filename with original linenos, so tb_lineno is the
|
|
117
|
+
file line the editor should wash red (the live-run twin of the routed
|
|
118
|
+
error markers). An error that never entered the twin (bad args, etc.)
|
|
119
|
+
stamps nothing."""
|
|
120
|
+
try:
|
|
121
|
+
from meltygui.code.live_view import stamp_run_marker
|
|
122
|
+
from meltygui.code.live_view import _line_text_at
|
|
123
|
+
code = getattr(twin, "__code__", None)
|
|
124
|
+
lineno = None
|
|
125
|
+
tb = exc.__traceback__
|
|
126
|
+
while tb is not None:
|
|
127
|
+
if tb.tb_frame.f_code is code:
|
|
128
|
+
lineno = tb.tb_lineno
|
|
129
|
+
tb = tb.tb_next
|
|
130
|
+
if lineno is not None:
|
|
131
|
+
stamp_run_marker(target, "__live_error_line__",
|
|
132
|
+
(lineno, f"{type(exc).__name__}: {exc}",
|
|
133
|
+
_line_text_at(target, lineno)))
|
|
134
|
+
except Exception:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def instrumented_twin(fn):
|
|
139
|
+
"""The instrumented twin of `fn` for its CURRENT SOURCE — cached, rebuilt
|
|
140
|
+
after a hotswap (new __code__), a plain disk save (new mtime), OR a
|
|
141
|
+
deferred in-editor edit (new PendingSave gen: the twin compiles from the
|
|
142
|
+
file's in-memory text, the same text Ctrl+Enter's hotswap compiles, so
|
|
143
|
+
Run executes the latest code even though deferred saves never reach
|
|
144
|
+
disk). Returns `fn` itself when instrumentation isn't possible, so
|
|
145
|
+
callers never need a fallback."""
|
|
146
|
+
try:
|
|
147
|
+
fn = inspect.unwrap(fn)
|
|
148
|
+
except Exception:
|
|
149
|
+
return fn
|
|
150
|
+
code = getattr(fn, "__code__", None)
|
|
151
|
+
if code is None:
|
|
152
|
+
return fn
|
|
153
|
+
try:
|
|
154
|
+
mtime = Path(code.co_filename).stat().st_mtime
|
|
155
|
+
except OSError:
|
|
156
|
+
mtime = None
|
|
157
|
+
sig = (mtime, _pending_gen(code.co_filename))
|
|
158
|
+
cached = _twins.get(id(code))
|
|
159
|
+
if cached is not None and cached[0] == sig:
|
|
160
|
+
return cached[1]
|
|
161
|
+
pending = _pending_state(code.co_filename)[1] # O(file) - miss only
|
|
162
|
+
try:
|
|
163
|
+
linecache.checkcache(code.co_filename) # getsourcelines must see the save
|
|
164
|
+
twin = _build_twin(fn, pending)
|
|
165
|
+
except Exception as e:
|
|
166
|
+
print(f"live_instrument: falling back to uninstrumented "
|
|
167
|
+
f"{getattr(fn, '__qualname__', fn)}: {e!r}", file=sys.stderr)
|
|
168
|
+
twin = fn
|
|
169
|
+
if cached is None:
|
|
170
|
+
weakref.finalize(code, _twins.pop, id(code), None)
|
|
171
|
+
_twins[id(code)] = (sig, twin)
|
|
172
|
+
return twin
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _pending_gen(path):
|
|
176
|
+
"""Combined pending-edit generation for `path` — the CHEAP half of the
|
|
177
|
+
cache signature (dict lookups only; never builds text). Sums the raw and
|
|
178
|
+
resolved Path keys: queue_save keys the counter on the address's own
|
|
179
|
+
path value (new_converters does the same dual lookup); only monotonicity
|
|
180
|
+
matters."""
|
|
181
|
+
try:
|
|
182
|
+
from meltygui.editor.pending_save import PendingSave
|
|
183
|
+
p = Path(path)
|
|
184
|
+
gen = PendingSave.pending_gen_for(p)
|
|
185
|
+
try:
|
|
186
|
+
rp = p.resolve()
|
|
187
|
+
if rp != p:
|
|
188
|
+
gen += PendingSave.pending_gen_for(rp)
|
|
189
|
+
except OSError:
|
|
190
|
+
pass
|
|
191
|
+
return gen
|
|
192
|
+
except Exception:
|
|
193
|
+
return 0
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _pending_state(path):
|
|
197
|
+
"""(pending edit generation, in-memory file text | None) for a
|
|
198
|
+
co_filename. The text build is O(file) (current_file_text splices) — a
|
|
199
|
+
MISS-only cost: callers compare a (mtime, _pending_gen) signature first
|
|
200
|
+
and only then call this. gen 0 → nothing queued → (0, None) and the twin
|
|
201
|
+
builds straight from disk via getsourcelines."""
|
|
202
|
+
gen = _pending_gen(path)
|
|
203
|
+
if not gen:
|
|
204
|
+
return 0, None
|
|
205
|
+
try:
|
|
206
|
+
from meltygui.editor.pending_save import PendingSave
|
|
207
|
+
return gen, PendingSave.current_file_text(Path(path))
|
|
208
|
+
except Exception:
|
|
209
|
+
return gen, None
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _delta_above(path, lineno):
|
|
213
|
+
"""Net line-count change of queued span edits fully ABOVE 1-indexed disk
|
|
214
|
+
line `lineno` — bridges the live code's DISK coordinates (the
|
|
215
|
+
co_firstlineno invariant) to positions in the pending text; the same sum
|
|
216
|
+
text_editor's _pending_line_delta computes, without its buffer-cache
|
|
217
|
+
plumbing."""
|
|
218
|
+
from meltygui.editor.pending_save import PendingSave
|
|
219
|
+
try:
|
|
220
|
+
rp = Path(path).resolve()
|
|
221
|
+
except OSError:
|
|
222
|
+
return 0
|
|
223
|
+
delta = 0
|
|
224
|
+
for addr, (codec, kwargs) in list(PendingSave.pending_saves.items()):
|
|
225
|
+
data = kwargs.get("data")
|
|
226
|
+
start, end = getattr(addr, "start", None), getattr(addr, "end", None)
|
|
227
|
+
if (not isinstance(data, str) or start is None or end is None
|
|
228
|
+
or end > lineno - 1):
|
|
229
|
+
continue
|
|
230
|
+
try:
|
|
231
|
+
if Path(addr.path).resolve() != rp:
|
|
232
|
+
continue
|
|
233
|
+
except OSError:
|
|
234
|
+
continue
|
|
235
|
+
d = data[:-1] if data.endswith("\n") else data
|
|
236
|
+
delta += (d.count("\n") + 1) - (end - start)
|
|
237
|
+
return delta
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _fn_source(fn, pending):
|
|
241
|
+
"""(source text incl. decorators, 1-indexed anchor line in DISK coords)
|
|
242
|
+
of the function's CURRENT code. With nothing queued (`pending` is None)
|
|
243
|
+
this is exactly inspect.getsourcelines. Otherwise the def is located in
|
|
244
|
+
the pending text — by name, nearest its expected (delta-shifted) line —
|
|
245
|
+
and the anchor maps BACK to disk coordinates so the injected snapshot
|
|
246
|
+
linenos keep matching the hotswapped code's disk-coord stamps and the
|
|
247
|
+
editor overlay's line math (disk span start + pending-relative offset)."""
|
|
248
|
+
if pending is None:
|
|
249
|
+
src_lines, start = inspect.getsourcelines(fn)
|
|
250
|
+
return "".join(src_lines), start
|
|
251
|
+
code = fn.__code__
|
|
252
|
+
delta = _delta_above(code.co_filename, code.co_firstlineno)
|
|
253
|
+
want = code.co_firstlineno + delta
|
|
254
|
+
best = None
|
|
255
|
+
for node in ast.walk(ast.parse(pending)):
|
|
256
|
+
if (isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
257
|
+
and node.name == code.co_name):
|
|
258
|
+
score = abs(node.lineno - want)
|
|
259
|
+
if best is None or score < best[0]:
|
|
260
|
+
best = (score, node)
|
|
261
|
+
if best is None: # def renamed/deleted in the pending text
|
|
262
|
+
src_lines, start = inspect.getsourcelines(fn)
|
|
263
|
+
return "".join(src_lines), start
|
|
264
|
+
node = best[1]
|
|
265
|
+
start = min([node.lineno] + [d.lineno for d in node.decorator_list])
|
|
266
|
+
lines = pending.split("\n")
|
|
267
|
+
return ("\n".join(lines[start - 1:node.end_lineno]) + "\n",
|
|
268
|
+
start - delta)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _build_twin(fn, pending=None):
|
|
272
|
+
code = fn.__code__
|
|
273
|
+
if code.co_freevars:
|
|
274
|
+
return fn # a standalone def can't rebind another frame's cells
|
|
275
|
+
if code.co_flags & (inspect.CO_GENERATOR | inspect.CO_COROUTINE
|
|
276
|
+
| inspect.CO_ASYNC_GENERATOR):
|
|
277
|
+
return fn
|
|
278
|
+
|
|
279
|
+
src, start = _fn_source(fn, pending)
|
|
280
|
+
tree = ast.parse(textwrap.dedent(src))
|
|
281
|
+
ast.increment_lineno(tree, start - 1)
|
|
282
|
+
fdef = tree.body[0]
|
|
283
|
+
if not isinstance(fdef, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
284
|
+
return fn
|
|
285
|
+
# The twin is a plain function: re-running decorators would re-register
|
|
286
|
+
# wrappers (render_funcs, etc.) against the twin.
|
|
287
|
+
fdef.decorator_list = []
|
|
288
|
+
|
|
289
|
+
fdef.body = _inject_snaps(fdef.body)
|
|
290
|
+
ast.fix_missing_locations(tree)
|
|
291
|
+
code_obj = compile(tree, code.co_filename, "exec")
|
|
292
|
+
|
|
293
|
+
# A COPY of the function's globals: the twin's def lands here instead of
|
|
294
|
+
# the real module (no name pollution). The snap name is twin_snap - the
|
|
295
|
+
# fast line-keyed publish path (no per-span libcst site resolution; see
|
|
296
|
+
# live_view.twin_snap) - bound directly and crucially with NO wrapper
|
|
297
|
+
# call in between: it reads sys._getframe(1), so the injected call must
|
|
298
|
+
# be the twin's own frame for the stamp to carry the original
|
|
299
|
+
# assignment's file + line. Manual live_view() calls in the body still
|
|
300
|
+
# resolve through the real/builtin live_view binding.
|
|
301
|
+
namespace = dict(fn.__globals__)
|
|
302
|
+
namespace[_SNAP_NAME] = twin_snap
|
|
303
|
+
namespace[_RET_NAME] = twin_ret
|
|
304
|
+
exec(code_obj, namespace)
|
|
305
|
+
twin = namespace[fdef.name]
|
|
306
|
+
twin.__qualname__ = fn.__qualname__ + ".<instrumented>"
|
|
307
|
+
return twin
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
_BLOCK_FIELDS = ("body", "orelse", "finalbody")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _inject_snaps(body, loops=()):
|
|
314
|
+
"""Insert `__lv_view__(<name>, name='<name>')` after every single-Name
|
|
315
|
+
assignment in `body`, recursing into control-flow blocks but NOT into
|
|
316
|
+
nested defs/classes (their locals live in other frames). Each injected
|
|
317
|
+
call copies the assignment's location, which IS the key rendezvous.
|
|
318
|
+
|
|
319
|
+
`loops` is the static chain of enclosing-loop dim names (outermost
|
|
320
|
+
first): recursing into a For/While BODY extends it with the loop's auto
|
|
321
|
+
name (live_view._loop_name — the loop variable, 'iter' for while), and a
|
|
322
|
+
snap under a non-empty chain gains `dims=(<names>…)`, which switches its
|
|
323
|
+
publish from overwrite to accumulation (live_view._accumulate). A loop's
|
|
324
|
+
`else:` runs once and keeps the outer chain."""
|
|
325
|
+
out = []
|
|
326
|
+
for stmt in body:
|
|
327
|
+
if isinstance(stmt, ast.Return):
|
|
328
|
+
# `return X` → `return __lv_ret__(X)` (bare return passes None):
|
|
329
|
+
# the hook stamps the store module's __live_return_line__ with
|
|
330
|
+
# this statement's disk line - see live_view.twin_ret.
|
|
331
|
+
_rcall = ast.Call(
|
|
332
|
+
func=ast.Name(id=_RET_NAME, ctx=ast.Load()),
|
|
333
|
+
args=[stmt.value if stmt.value is not None
|
|
334
|
+
else ast.Constant(value=None)],
|
|
335
|
+
keywords=[])
|
|
336
|
+
ast.copy_location(_rcall, stmt)
|
|
337
|
+
ast.copy_location(_rcall.func, stmt)
|
|
338
|
+
stmt.value = _rcall
|
|
339
|
+
out.append(stmt)
|
|
340
|
+
name = _snap_target(stmt)
|
|
341
|
+
if name is not None:
|
|
342
|
+
keywords = [ast.keyword(arg="name",
|
|
343
|
+
value=ast.Constant(value=name))]
|
|
344
|
+
if loops:
|
|
345
|
+
keywords.append(ast.keyword(
|
|
346
|
+
arg="dims",
|
|
347
|
+
value=ast.Tuple(elts=[ast.Constant(value=d)
|
|
348
|
+
for d in loops],
|
|
349
|
+
ctx=ast.Load())))
|
|
350
|
+
call = ast.Expr(value=ast.Call(
|
|
351
|
+
func=ast.Name(id=_SNAP_NAME, ctx=ast.Load()),
|
|
352
|
+
args=[ast.Name(id=name, ctx=ast.Load())],
|
|
353
|
+
keywords=keywords))
|
|
354
|
+
ast.copy_location(call, stmt)
|
|
355
|
+
for child in ast.walk(call):
|
|
356
|
+
ast.copy_location(child, stmt)
|
|
357
|
+
out.append(call)
|
|
358
|
+
continue
|
|
359
|
+
if isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef,
|
|
360
|
+
ast.ClassDef)):
|
|
361
|
+
continue
|
|
362
|
+
if isinstance(stmt, (ast.For, ast.AsyncFor, ast.While)):
|
|
363
|
+
stmt.body = _inject_snaps(stmt.body, loops + (_loop_name(stmt),))
|
|
364
|
+
stmt.orelse = _inject_snaps(stmt.orelse, loops)
|
|
365
|
+
else:
|
|
366
|
+
for field in _BLOCK_FIELDS:
|
|
367
|
+
sub = getattr(stmt, field, None)
|
|
368
|
+
if isinstance(sub, list) and sub:
|
|
369
|
+
setattr(stmt, field, _inject_snaps(sub, loops))
|
|
370
|
+
for handler in getattr(stmt, "handlers", None) or []:
|
|
371
|
+
handler.body = _inject_snaps(handler.body, loops)
|
|
372
|
+
for case in getattr(stmt, "cases", None) or []:
|
|
373
|
+
case.body = _inject_snaps(case.body, loops)
|
|
374
|
+
return out
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _snap_target(stmt):
|
|
378
|
+
"""The name to snapshot after `stmt`: single-Name (or first-Name chained)
|
|
379
|
+
assignment targets only — tuple unpacks and attribute/subscript targets
|
|
380
|
+
have no single frame-local to read."""
|
|
381
|
+
if isinstance(stmt, ast.Assign):
|
|
382
|
+
for target in stmt.targets:
|
|
383
|
+
if isinstance(target, ast.Name):
|
|
384
|
+
return target.id
|
|
385
|
+
return None
|
|
386
|
+
if isinstance(stmt, (ast.AugAssign, ast.AnnAssign)):
|
|
387
|
+
if isinstance(stmt.target, ast.Name):
|
|
388
|
+
# An AnnAssign without a value (`x: int`) binds rather to read.
|
|
389
|
+
if isinstance(stmt, ast.AnnAssign) and stmt.value is None:
|
|
390
|
+
return None
|
|
391
|
+
return stmt.target.id
|
|
392
|
+
return None
|