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,398 @@
|
|
|
1
|
+
"""Runtime metadata about view-function scopes, captured from live evaluation.
|
|
2
|
+
|
|
3
|
+
Keyed by the function object itself, ``FuncsMetadata.metadata[func]`` maps each
|
|
4
|
+
in-scope name (the function's parameters / eval locals) to a :class:`VarMeta`
|
|
5
|
+
carrying the observed runtime type and member names. The studio's eval REPL holds
|
|
6
|
+
the real call-time locals, so it records EXACT types here -- closing the gap left
|
|
7
|
+
by un-annotated signatures (a ``draw_state`` param with no hint is still known to
|
|
8
|
+
be a ``DrawState``). The eval tab's autocomplete reads this back for scope-aware,
|
|
9
|
+
type-accurate suggestions; the code editors can tap the same cache later for
|
|
10
|
+
better hints without re-deriving types statically.
|
|
11
|
+
|
|
12
|
+
The cache key is the *wrapper* function (``draw_state._view_func``) so callers can
|
|
13
|
+
look it up straight off a draw_state. Globals are resolved against the unwrapped
|
|
14
|
+
body's ``__globals__`` (the wrapper lives in core_render, with the wrong globals),
|
|
15
|
+
so :func:`scope_names` unwraps before reading them.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import builtins as _builtins
|
|
19
|
+
import inspect
|
|
20
|
+
import keyword as _keyword
|
|
21
|
+
|
|
22
|
+
# Numbers carry only object/number dunders that nobody worth completing, and a
|
|
23
|
+
# full dir() per kwarg adds up, so we skip the member snapshot for them.
|
|
24
|
+
_PRIMITIVE_NO_MEMBERS = (int, float, bool, complex)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _safe_dir(obj):
|
|
28
|
+
"""``dir(obj)`` as a list, swallowing anything that misbehaves."""
|
|
29
|
+
try:
|
|
30
|
+
return list(dir(obj))
|
|
31
|
+
except Exception:
|
|
32
|
+
return []
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _safe_members(value):
|
|
36
|
+
"""dir() snapshot of a live value as a tuple (empty for bare numbers/None)."""
|
|
37
|
+
if value is None or isinstance(value, _PRIMITIVE_NO_MEMBERS):
|
|
38
|
+
return ()
|
|
39
|
+
return tuple(_safe_dir(value))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _type_name(t):
|
|
43
|
+
"""Short display name for a type (``DrawState``), or '' when unknown."""
|
|
44
|
+
if t is None:
|
|
45
|
+
return ""
|
|
46
|
+
return getattr(t, "__name__", None) or str(t)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class VarMeta:
|
|
50
|
+
"""What runtime observation told us about one in-scope name.
|
|
51
|
+
|
|
52
|
+
``type`` is the observed class (exact, even when the signature is unhinted).
|
|
53
|
+
``members`` is dir() of the observed VALUE at capture time -- so it includes
|
|
54
|
+
instance attributes set in ``__init__``, which ``dir(type)`` alone would miss.
|
|
55
|
+
Both are cheap, GC-safe snapshots (a class ref + a tuple of strings); the live
|
|
56
|
+
object itself is never retained."""
|
|
57
|
+
|
|
58
|
+
__slots__ = ("type", "members")
|
|
59
|
+
|
|
60
|
+
def __init__(self, type=None, members=()):
|
|
61
|
+
self.type = type
|
|
62
|
+
self.members = members
|
|
63
|
+
|
|
64
|
+
def __repr__(self):
|
|
65
|
+
return f"VarMeta(type={_type_name(self.type)!r}, {len(self.members)} members)"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _meta_key(func):
|
|
69
|
+
"""Stable cache key for ``func``: ``(co_filename, co_qualname)`` of the
|
|
70
|
+
unwrapped body when it has code, else the object itself. This folds the
|
|
71
|
+
render_func WRAPPER and its inner body into one slot (writers hold either
|
|
72
|
+
identity — the eval REPL records under the wrapper, the stack-capture
|
|
73
|
+
recorder resolves frames to the inner), and the slot SURVIVES HOTSWAP: the
|
|
74
|
+
replacement function keeps its file/qualname while the old object dies, so
|
|
75
|
+
types recorded before a swap still answer afterwards."""
|
|
76
|
+
inner = _unwrap(func)
|
|
77
|
+
code = getattr(inner, "__code__", None)
|
|
78
|
+
if code is not None:
|
|
79
|
+
return (code.co_filename, code.co_qualname)
|
|
80
|
+
return func
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class FuncsMetadata:
|
|
84
|
+
"""Process-wide cache of observed scope metadata, keyed per function via
|
|
85
|
+
``_meta_key`` (file + qualname when available — wrapper/inner-agnostic and
|
|
86
|
+
hotswap-stable)."""
|
|
87
|
+
|
|
88
|
+
metadata = {} # _meta_key(func) -> {name: VarMeta}
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def record(cls, func, scope):
|
|
92
|
+
"""Merge observed runtime types/members for ``func`` from a ``{name:
|
|
93
|
+
value}`` scope. Later observations refine earlier ones (the eval tab's
|
|
94
|
+
approximate pre-capture is overwritten by run_scoped_eval's exact ns).
|
|
95
|
+
Individual names are skipped silently when introspection raises."""
|
|
96
|
+
if func is None or not scope:
|
|
97
|
+
return None
|
|
98
|
+
key = _meta_key(func)
|
|
99
|
+
slot = cls.metadata.get(key)
|
|
100
|
+
if slot is None:
|
|
101
|
+
slot = cls.metadata[key] = {}
|
|
102
|
+
for name, value in scope.items():
|
|
103
|
+
if not isinstance(name, str):
|
|
104
|
+
continue
|
|
105
|
+
try:
|
|
106
|
+
slot[name] = VarMeta(type=type(value), members=_safe_members(value))
|
|
107
|
+
except Exception:
|
|
108
|
+
continue
|
|
109
|
+
return slot
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def record_value(cls, func, name, value):
|
|
113
|
+
"""Record ONE observed name — the per-publish entry point for live_view
|
|
114
|
+
captures. Unlike ``record``, this is called from hot paths (an
|
|
115
|
+
instrumented run republishes every assignment; a training-loop publisher
|
|
116
|
+
fires per iteration), so it skips the ``dir()`` member snapshot whenever
|
|
117
|
+
the name's TYPE is unchanged since the last record — steady-state cost
|
|
118
|
+
is two dict hits and a type compare."""
|
|
119
|
+
if func is None or not isinstance(name, str) or not name:
|
|
120
|
+
return
|
|
121
|
+
key = _meta_key(func)
|
|
122
|
+
slot = cls.metadata.get(key)
|
|
123
|
+
if slot is None:
|
|
124
|
+
slot = cls.metadata[key] = {}
|
|
125
|
+
prev = slot.get(name)
|
|
126
|
+
if prev is not None and prev.type is type(value):
|
|
127
|
+
return
|
|
128
|
+
try:
|
|
129
|
+
slot[name] = VarMeta(type=type(value), members=_safe_members(value))
|
|
130
|
+
except Exception:
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def get(cls, func):
|
|
135
|
+
"""``{name: VarMeta}`` observed for ``func``, or ``{}`` if never
|
|
136
|
+
recorded. The ``_meta_key`` normalization means a reader holding the
|
|
137
|
+
wrapper, the inner body, or a post-hotswap replacement all find the
|
|
138
|
+
same slot."""
|
|
139
|
+
if func is None:
|
|
140
|
+
return {}
|
|
141
|
+
return cls.metadata.get(_meta_key(func), {})
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def clear(cls, func=None):
|
|
145
|
+
"""Drop one function's metadata, or the whole cache when ``func`` is None."""
|
|
146
|
+
if func is None:
|
|
147
|
+
cls.metadata.clear()
|
|
148
|
+
else:
|
|
149
|
+
cls.metadata.pop(_meta_key(func), None)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# ── Completion providers (read side) ─────────────────────────────────────────
|
|
153
|
+
# Both return ordered ``[(name, kind)]`` lists, best-first. ``kind`` is the dim
|
|
154
|
+
# tag the editor shows on the right of each row. The eval tab's payoff is that a
|
|
155
|
+
# scope var's tag is its OBSERVED TYPE NAME -- so the popup reads
|
|
156
|
+
# ``draw_state DrawState`` even though the signature had no annotation.
|
|
157
|
+
|
|
158
|
+
_MISSING = object()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _unwrap(func):
|
|
162
|
+
try:
|
|
163
|
+
return inspect.unwrap(func)
|
|
164
|
+
except Exception:
|
|
165
|
+
return func
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _lookup_live(func, name, globals_ns=None):
|
|
169
|
+
"""The live object a free ``name`` resolves to in ``func``'s scope -- a module
|
|
170
|
+
global or a builtin -- or ``_MISSING``. Lets ``imgui.``/``len.`` complete
|
|
171
|
+
against the real object (exact, chainable) without holding anything: the
|
|
172
|
+
module/builtin already lives forever. ``globals_ns`` overrides the globals
|
|
173
|
+
dict consulted (the code editor passes the edited file's live module dict;
|
|
174
|
+
the eval box has a real ``func`` and passes nothing)."""
|
|
175
|
+
g = globals_ns if globals_ns is not None else (
|
|
176
|
+
getattr(_unwrap(func), "__globals__", None) or {})
|
|
177
|
+
if name in g:
|
|
178
|
+
return g[name]
|
|
179
|
+
if hasattr(_builtins, name):
|
|
180
|
+
return getattr(_builtins, name)
|
|
181
|
+
return _MISSING
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _live_member_kind(obj, name):
|
|
185
|
+
"""method/class/mod/attr tag for member ``name`` of live object ``obj``.
|
|
186
|
+
Resolved with getattr_static so completion never fires a property/descriptor
|
|
187
|
+
(a DrawState geometry property, a lazy loader) just to classify its row."""
|
|
188
|
+
try:
|
|
189
|
+
attr = inspect.getattr_static(obj, name)
|
|
190
|
+
except Exception:
|
|
191
|
+
try:
|
|
192
|
+
attr = inspect.getattr_static(type(obj), name)
|
|
193
|
+
except Exception:
|
|
194
|
+
return "attr"
|
|
195
|
+
if isinstance(attr, (staticmethod, classmethod)):
|
|
196
|
+
return "method"
|
|
197
|
+
if (inspect.isfunction(attr) or inspect.ismethod(attr)
|
|
198
|
+
or inspect.isbuiltin(attr) or inspect.ismethoddescriptor(attr)):
|
|
199
|
+
return "method"
|
|
200
|
+
if isinstance(attr, type):
|
|
201
|
+
return "class"
|
|
202
|
+
if inspect.ismodule(attr):
|
|
203
|
+
return "mod"
|
|
204
|
+
return "attr"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _type_member_kind(t, name):
|
|
208
|
+
"""method/attr tag for ``name`` on class ``t`` without an instance."""
|
|
209
|
+
try:
|
|
210
|
+
attr = inspect.getattr_static(t, name)
|
|
211
|
+
except Exception:
|
|
212
|
+
try:
|
|
213
|
+
attr = getattr(t, name, None)
|
|
214
|
+
except Exception:
|
|
215
|
+
attr = None
|
|
216
|
+
if attr is None:
|
|
217
|
+
return "attr"
|
|
218
|
+
if isinstance(attr, (staticmethod, classmethod)):
|
|
219
|
+
return "method"
|
|
220
|
+
if (inspect.isfunction(attr) or inspect.ismethod(attr)
|
|
221
|
+
or inspect.isbuiltin(attr) or inspect.ismethoddescriptor(attr)):
|
|
222
|
+
return "method"
|
|
223
|
+
return "attr"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _attr_type(t, name):
|
|
227
|
+
"""Best-effort TYPE of attribute ``name`` on class ``t`` (annotation, else the
|
|
228
|
+
class-level value's type). ``None`` when undeterminable -- the chain stops."""
|
|
229
|
+
try:
|
|
230
|
+
import typing
|
|
231
|
+
hints = typing.get_type_hints(t)
|
|
232
|
+
h = hints.get(name)
|
|
233
|
+
if isinstance(h, type):
|
|
234
|
+
return h
|
|
235
|
+
except Exception:
|
|
236
|
+
pass
|
|
237
|
+
try:
|
|
238
|
+
attr = inspect.getattr_static(t, name)
|
|
239
|
+
except Exception:
|
|
240
|
+
attr = getattr(t, name, None)
|
|
241
|
+
if isinstance(attr, type):
|
|
242
|
+
return attr
|
|
243
|
+
if attr is not None and not callable(attr):
|
|
244
|
+
return type(attr)
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _defining_class(t, name):
|
|
249
|
+
"""The class in ``t``'s MRO whose __dict__ defines ``name``, or None —
|
|
250
|
+
None for a member seen on a VALUE but on no class, i.e. an instance
|
|
251
|
+
attribute (set in ``__init__``)."""
|
|
252
|
+
for c in getattr(t, "__mro__", ()) or ():
|
|
253
|
+
if name in getattr(c, "__dict__", {}):
|
|
254
|
+
return c
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _ranked_member_rows(t, names, kind_fn, instance_names=()):
|
|
259
|
+
"""``[(name, kind)]`` ordered and tagged by WHERE each member lives:
|
|
260
|
+
instance attributes first (tag ``self`` — set in ``__init__``, on no
|
|
261
|
+
class), then members defined on ``t`` itself (their method/attr/... kind),
|
|
262
|
+
then inherited members grouped by MRO distance (tag ``↑Base`` names the
|
|
263
|
+
defining superclass; ``object``'s dunders land last). Within each group
|
|
264
|
+
public names precede ``_private`` precede ``__dunder``, keeping dir()'s
|
|
265
|
+
alphabetical order otherwise. The popup's usage re-rank only reorders rows
|
|
266
|
+
with buffer usage counts, so this ordering survives for the rest. A ``t``
|
|
267
|
+
with no MRO (module member lists, odd proxies) returns flat rows."""
|
|
268
|
+
mro = list(getattr(t, "__mro__", ()) or ())
|
|
269
|
+
if not mro:
|
|
270
|
+
return [(n, kind_fn(n)) for n in names]
|
|
271
|
+
keyed = []
|
|
272
|
+
for n in names:
|
|
273
|
+
cls = _defining_class(t, n)
|
|
274
|
+
if cls is None or n in instance_names:
|
|
275
|
+
group, tag = 0, "self"
|
|
276
|
+
elif cls is mro[0]:
|
|
277
|
+
group, tag = 1, kind_fn(n)
|
|
278
|
+
else:
|
|
279
|
+
group, tag = 2 + mro.index(cls), "↑" + (getattr(cls, "__name__", None) or "?")
|
|
280
|
+
sub = 2 if n.startswith("__") else 1 if n.startswith("_") else 0
|
|
281
|
+
keyed.append(((group, sub), (n, tag)))
|
|
282
|
+
keyed.sort(key=lambda r: r[0])
|
|
283
|
+
return [r[1] for r in keyed]
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def member_completions(func, receiver, globals_ns=None):
|
|
287
|
+
"""``[(name, kind)]`` for ``receiver.``<caret>. The receiver resolves either
|
|
288
|
+
to a LIVE object (module global / builtin -> walked with getattr, exact and
|
|
289
|
+
chainable) or to a recorded scope var (-> its captured member snapshot for the
|
|
290
|
+
first hop, a static type-chain walk for deeper hops). ``[]`` when unresolved
|
|
291
|
+
(the caller falls through to jedi / keeps the popup closed). ``globals_ns``
|
|
292
|
+
substitutes the globals the receiver head resolves in -- the code editor
|
|
293
|
+
passes the edited file's live module dict, so any module-level name in the
|
|
294
|
+
file being edited completes instantly against the running process.
|
|
295
|
+
|
|
296
|
+
Rows are ordered/tagged by origin via ``_ranked_member_rows``: instance
|
|
297
|
+
attrs (``self``), then the class's own members, then inherited ones
|
|
298
|
+
(``↑Base``). Module receivers keep flat dir() order — no MRO to rank by."""
|
|
299
|
+
if not receiver:
|
|
300
|
+
return []
|
|
301
|
+
segs = receiver.split(".")
|
|
302
|
+
head, rest = segs[0], segs[1:]
|
|
303
|
+
|
|
304
|
+
live = _lookup_live(func, head, globals_ns)
|
|
305
|
+
if live is not _MISSING:
|
|
306
|
+
obj = live
|
|
307
|
+
for seg in rest:
|
|
308
|
+
try:
|
|
309
|
+
obj = getattr(obj, seg)
|
|
310
|
+
except Exception:
|
|
311
|
+
return []
|
|
312
|
+
names = _safe_dir(obj)
|
|
313
|
+
kind_fn = lambda n: _live_member_kind(obj, n)
|
|
314
|
+
if inspect.ismodule(obj):
|
|
315
|
+
return [(n, kind_fn(n)) for n in names]
|
|
316
|
+
if isinstance(obj, type):
|
|
317
|
+
return _ranked_member_rows(obj, names, kind_fn)
|
|
318
|
+
try:
|
|
319
|
+
inst = frozenset(vars(obj))
|
|
320
|
+
except TypeError:
|
|
321
|
+
inst = () # __slots__ / CObject - slots classify by class
|
|
322
|
+
return _ranked_member_rows(type(obj), names, kind_fn, inst)
|
|
323
|
+
|
|
324
|
+
vm = FuncsMetadata.get(func).get(head)
|
|
325
|
+
if vm is None:
|
|
326
|
+
return []
|
|
327
|
+
if not rest:
|
|
328
|
+
# First hop off a recorded scope var: use its dir(value) snapshot.
|
|
329
|
+
# Instance attrs fall out naturally - they're in the snapshot but on no
|
|
330
|
+
# MRO class, so _defining_class(name) groups them first.
|
|
331
|
+
names = vm.members or _safe_dir(vm.type)
|
|
332
|
+
return _ranked_member_rows(vm.type, names,
|
|
333
|
+
lambda n: _type_member_kind(vm.type, n))
|
|
334
|
+
cur = vm.type
|
|
335
|
+
for seg in rest:
|
|
336
|
+
cur = _attr_type(cur, seg)
|
|
337
|
+
if cur is None:
|
|
338
|
+
return []
|
|
339
|
+
return _ranked_member_rows(cur, _safe_dir(cur),
|
|
340
|
+
lambda n: _type_member_kind(cur, n))
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def scope_names(func):
|
|
344
|
+
"""``[(name, kind)]`` for bare-identifier completion in ``func``'s scope: the
|
|
345
|
+
recorded scope vars first (kind = observed type name), then the function's
|
|
346
|
+
module globals (kind ``global``), then builtins and keywords. De-duplicated,
|
|
347
|
+
first occurrence wins -- so a scope var shadows a same-named global."""
|
|
348
|
+
out, seen = [], set()
|
|
349
|
+
for name, vm in FuncsMetadata.get(func).items():
|
|
350
|
+
if name not in seen:
|
|
351
|
+
seen.add(name)
|
|
352
|
+
out.append((name, _type_name(vm.type) or "local"))
|
|
353
|
+
g = getattr(_unwrap(func), "__globals__", None) or {}
|
|
354
|
+
for name in g:
|
|
355
|
+
if isinstance(name, str) and name not in seen and not name.startswith("__"):
|
|
356
|
+
seen.add(name)
|
|
357
|
+
out.append((name, "global"))
|
|
358
|
+
for name in dir(_builtins):
|
|
359
|
+
if not name.startswith("_") and name not in seen:
|
|
360
|
+
seen.add(name)
|
|
361
|
+
out.append((name, "builtin"))
|
|
362
|
+
for kw in _keyword.kwlist:
|
|
363
|
+
if kw not in seen:
|
|
364
|
+
seen.add(kw)
|
|
365
|
+
out.append((kw, "kw"))
|
|
366
|
+
return out
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _receiver_before(text, anchor):
|
|
370
|
+
"""The dotted receiver expression ending at the '.' just left of ``anchor``
|
|
371
|
+
(the index where the half-typed member begins). ``'draw_state.foo.|'`` ->
|
|
372
|
+
``'draw_state.foo'``; ``''`` when no identifier precedes the dot."""
|
|
373
|
+
dot = anchor - 1 # the '.' that triggered member access
|
|
374
|
+
i = dot - 1
|
|
375
|
+
while i >= 0 and (text[i].isalnum() or text[i] in "_."):
|
|
376
|
+
i -= 1
|
|
377
|
+
return text[i + 1:dot]
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
_eval_sources = {}
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def eval_completion_source(func):
|
|
384
|
+
"""The callable draw_text's autocomplete invokes for the eval box. Memoized
|
|
385
|
+
per ``func`` so the SAME object is handed in every frame (a fresh closure each
|
|
386
|
+
frame would look like a changed kwarg and defeat the box's render cache). It
|
|
387
|
+
reads the cache live, so it always reflects the latest recorded scope.
|
|
388
|
+
|
|
389
|
+
Contract (matches draw_text): ``(text, anchor, prefix, dot_trigger) ->
|
|
390
|
+
[(name, kind)]``. The prefix filtering is draw_text's job, not ours."""
|
|
391
|
+
src = _eval_sources.get(func)
|
|
392
|
+
if src is None:
|
|
393
|
+
def src(text, anchor, prefix, dot_trigger, _f=func):
|
|
394
|
+
if dot_trigger:
|
|
395
|
+
return member_completions(_f, _receiver_before(text, anchor))
|
|
396
|
+
return scope_names(_f)
|
|
397
|
+
_eval_sources[func] = src
|
|
398
|
+
return src
|