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,273 @@
|
|
|
1
|
+
"""Symbol access to @render_func renderers without importing their module.
|
|
2
|
+
|
|
3
|
+
`RenderFuncs.draw_type` returns a lazy handle that resolves at call time from the
|
|
4
|
+
render-func registry (auto-populated by the @render_func decorator). This
|
|
5
|
+
sidesteps import cycles: referencing a render func here never triggers an import
|
|
6
|
+
of whatever heavy module defines it.
|
|
7
|
+
|
|
8
|
+
This module never imports Melty — the dependency runs the other way. Melty owns
|
|
9
|
+
and manages the registry; we reach it through the lightweight Core.melty handle
|
|
10
|
+
so that referencing render funcs stays a low-level concern, not one that reaches
|
|
11
|
+
back up into the Melty hub.
|
|
12
|
+
|
|
13
|
+
CodeGenerator regenerates the _RenderFuncs class in THIS file so every registered
|
|
14
|
+
render func shows up as a real, IDE-visible member — kept in its own file so the
|
|
15
|
+
generator only ever rewrites this small module, never meltygui.py.
|
|
16
|
+
"""
|
|
17
|
+
from meltygui.core.rendering.core_decoration import Core
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class _LazyRenderFunc:
|
|
21
|
+
"""A by-name handle to a @render_func, resolved on first use.
|
|
22
|
+
|
|
23
|
+
Behaves like the render func itself: callable with the same signature and
|
|
24
|
+
carrying the right __name__ (the view system reads that). Resolution is
|
|
25
|
+
deferred to call time, by which point every module has imported and
|
|
26
|
+
self-registered into the registry (reached via Core.melty) — so referencing
|
|
27
|
+
one of these never triggers an import and never participates in an import cycle.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
__slots__ = ("__name__", "_fn")
|
|
31
|
+
|
|
32
|
+
def __init__(self, name):
|
|
33
|
+
self.__name__ = name
|
|
34
|
+
self._fn = None
|
|
35
|
+
|
|
36
|
+
def _resolve(self):
|
|
37
|
+
# Always re-read the registry - never serve a pinned static resolution.
|
|
38
|
+
# A recompile/hotload re-registers the name (and the reconcile pass
|
|
39
|
+
# redirected the entry point at the live wrapper, see
|
|
40
|
+
# _redirect_function_registrations); a static cache here would pin
|
|
41
|
+
# whatever object happened to be registered at first call and keep
|
|
42
|
+
# serving its stale address after later recompiles. One dict get per call
|
|
43
|
+
# is noise next to a render. _fn exists only as a fallback for a
|
|
44
|
+
# transient gap (mid-recompile) where the name is momentarily missing.
|
|
45
|
+
fn = Core.melty.render_funcs_by_name.get(self.__name__)
|
|
46
|
+
if fn is not None:
|
|
47
|
+
self._fn = fn
|
|
48
|
+
return fn
|
|
49
|
+
if self._fn is not None:
|
|
50
|
+
return self._fn
|
|
51
|
+
print(f"Registered render funcs: {list(Core.melty.render_funcs_by_name)}")
|
|
52
|
+
raise AttributeError(
|
|
53
|
+
f"No @render_func named {self.__name__!r} is registered")
|
|
54
|
+
|
|
55
|
+
def __call__(self, *args, **kwargs):
|
|
56
|
+
return self._resolve()(*args, **kwargs)
|
|
57
|
+
|
|
58
|
+
def __getattr__(self, item):
|
|
59
|
+
# Forward real attributes (e.g. _load_data/_save_data) to the resolved
|
|
60
|
+
# func, but never dunders; copy/pickle probes (__deepcopy__, etc.) must
|
|
61
|
+
# fall through to default handling rather than force early resolution.
|
|
62
|
+
if item.startswith("__") and item.endswith("__"):
|
|
63
|
+
raise AttributeError(item)
|
|
64
|
+
return getattr(self._resolve(), item)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class _RenderFuncs:
|
|
68
|
+
"""`RenderFuncs.draw_type` -> a _LazyRenderFunc handle, no import required.
|
|
69
|
+
|
|
70
|
+
The members below are generated by CodeGenerator (one per registered
|
|
71
|
+
@render_func) so IDEs autocomplete RenderFuncs.<name>. Each is a real class
|
|
72
|
+
attribute; __getattr__ is the fallback for any render func added since the
|
|
73
|
+
last regeneration. Regenerate via CodeGenerator.update_render_funcs().
|
|
74
|
+
"""
|
|
75
|
+
draw_view_func_selector = _LazyRenderFunc("draw_view_func_selector")
|
|
76
|
+
_do_save = _LazyRenderFunc("_do_save")
|
|
77
|
+
address_to_call_parse = _LazyRenderFunc("address_to_call_parse")
|
|
78
|
+
address_to_class = _LazyRenderFunc("address_to_class")
|
|
79
|
+
address_to_function = _LazyRenderFunc("address_to_function")
|
|
80
|
+
address_to_general_parse = _LazyRenderFunc("address_to_general_parse")
|
|
81
|
+
address_to_module = _LazyRenderFunc("address_to_module")
|
|
82
|
+
button = _LazyRenderFunc("button")
|
|
83
|
+
call_dict_to_save = _LazyRenderFunc("call_dict_to_save")
|
|
84
|
+
caller_to_address = _LazyRenderFunc("caller_to_address")
|
|
85
|
+
chain_cls_load = _LazyRenderFunc("chain_cls_load")
|
|
86
|
+
class_to_address = _LazyRenderFunc("class_to_address")
|
|
87
|
+
class_to_address_incl_overrides = _LazyRenderFunc("class_to_address_incl_overrides")
|
|
88
|
+
class_to_var_dict = _LazyRenderFunc("class_to_var_dict")
|
|
89
|
+
cls_to_cst = _LazyRenderFunc("cls_to_cst")
|
|
90
|
+
cst_to_address = _LazyRenderFunc("cst_to_address")
|
|
91
|
+
cst_to_cls = _LazyRenderFunc("cst_to_cls")
|
|
92
|
+
cst_to_fn = _LazyRenderFunc("cst_to_fn")
|
|
93
|
+
cst_to_mod = _LazyRenderFunc("cst_to_mod")
|
|
94
|
+
cst_to_ref = _LazyRenderFunc("cst_to_ref")
|
|
95
|
+
dict_to_cst = _LazyRenderFunc("dict_to_cst")
|
|
96
|
+
do_recompile = _LazyRenderFunc("do_recompile")
|
|
97
|
+
draw_blank = _LazyRenderFunc("draw_blank")
|
|
98
|
+
draw_bool = _LazyRenderFunc("draw_bool")
|
|
99
|
+
draw_collection = _LazyRenderFunc("draw_collection")
|
|
100
|
+
draw_collection_as_tabs = _LazyRenderFunc("draw_collection_as_tabs")
|
|
101
|
+
draw_comment = _LazyRenderFunc("draw_comment")
|
|
102
|
+
draw_context_menu = _LazyRenderFunc("draw_context_menu")
|
|
103
|
+
draw_debug_label = _LazyRenderFunc("draw_debug_label")
|
|
104
|
+
draw_drag_drop_target = _LazyRenderFunc("draw_drag_drop_target")
|
|
105
|
+
draw_draw_state = _LazyRenderFunc("draw_draw_state")
|
|
106
|
+
draw_draw_state_info = _LazyRenderFunc("draw_draw_state_info")
|
|
107
|
+
draw_drop_down_item = _LazyRenderFunc("draw_drop_down_item")
|
|
108
|
+
draw_dropdown = _LazyRenderFunc("draw_dropdown")
|
|
109
|
+
draw_enum = _LazyRenderFunc("draw_enum")
|
|
110
|
+
draw_enum_tabs = _LazyRenderFunc("draw_enum_tabs")
|
|
111
|
+
draw_float = _LazyRenderFunc("draw_float")
|
|
112
|
+
draw_float_ctx = _LazyRenderFunc("draw_float_ctx")
|
|
113
|
+
draw_function = _LazyRenderFunc("draw_function")
|
|
114
|
+
draw_int = _LazyRenderFunc("draw_int")
|
|
115
|
+
draw_jump_to = _LazyRenderFunc("draw_jump_to")
|
|
116
|
+
draw_managed_window = _LazyRenderFunc("draw_managed_window")
|
|
117
|
+
draw_mapping_proxy = _LazyRenderFunc("draw_mapping_proxy")
|
|
118
|
+
draw_module = _LazyRenderFunc("draw_module")
|
|
119
|
+
draw_none = _LazyRenderFunc("draw_none")
|
|
120
|
+
draw_parameter = _LazyRenderFunc("draw_parameter")
|
|
121
|
+
draw_pending = _LazyRenderFunc("draw_pending")
|
|
122
|
+
draw_pending_texture = _LazyRenderFunc("draw_pending_texture")
|
|
123
|
+
draw_property = _LazyRenderFunc("draw_property")
|
|
124
|
+
draw_search = _LazyRenderFunc("draw_search")
|
|
125
|
+
draw_single = _LazyRenderFunc("draw_single")
|
|
126
|
+
draw_str = _LazyRenderFunc("draw_str")
|
|
127
|
+
draw_style_manager = _LazyRenderFunc("draw_style_manager")
|
|
128
|
+
draw_tab_bar = _LazyRenderFunc("draw_tab_bar")
|
|
129
|
+
draw_tensor = _LazyRenderFunc("draw_tensor")
|
|
130
|
+
draw_text = _LazyRenderFunc("draw_text")
|
|
131
|
+
draw_texture = _LazyRenderFunc("draw_texture")
|
|
132
|
+
draw_tint_context = _LazyRenderFunc("draw_tint_context")
|
|
133
|
+
draw_tuple = _LazyRenderFunc("draw_tuple")
|
|
134
|
+
draw_type = _LazyRenderFunc("draw_type")
|
|
135
|
+
draw_usage = _LazyRenderFunc("draw_usage")
|
|
136
|
+
draw_with_modes = _LazyRenderFunc("draw_with_modes")
|
|
137
|
+
empty = _LazyRenderFunc("empty")
|
|
138
|
+
eval_function = _LazyRenderFunc("eval_function")
|
|
139
|
+
fn_to_cst = _LazyRenderFunc("fn_to_cst")
|
|
140
|
+
focus = _LazyRenderFunc("focus")
|
|
141
|
+
function_to_address = _LazyRenderFunc("function_to_address")
|
|
142
|
+
general_parse_to_address = _LazyRenderFunc("general_parse_to_address")
|
|
143
|
+
general_parse_to_str = _LazyRenderFunc("general_parse_to_str")
|
|
144
|
+
load_cst_module = _LazyRenderFunc("load_cst_module")
|
|
145
|
+
mod_to_cst = _LazyRenderFunc("mod_to_cst")
|
|
146
|
+
module_to_address = _LazyRenderFunc("module_to_address")
|
|
147
|
+
parse_source_to_general = _LazyRenderFunc("parse_source_to_general")
|
|
148
|
+
path_to_address = _LazyRenderFunc("path_to_address")
|
|
149
|
+
pending_window = _LazyRenderFunc("pending_window")
|
|
150
|
+
recompile_caller_fn = _LazyRenderFunc("recompile_caller_fn")
|
|
151
|
+
recompile_cls_fn = _LazyRenderFunc("recompile_cls_fn")
|
|
152
|
+
recompile_fn = _LazyRenderFunc("recompile_fn")
|
|
153
|
+
recompile_mod_fn = _LazyRenderFunc("recompile_mod_fn")
|
|
154
|
+
ref_to_cst = _LazyRenderFunc("ref_to_cst")
|
|
155
|
+
rf_address_to_dict = _LazyRenderFunc("rf_address_to_dict")
|
|
156
|
+
rf_bytes_to_str = _LazyRenderFunc("rf_bytes_to_str")
|
|
157
|
+
rf_dict_to_address = _LazyRenderFunc("rf_dict_to_address")
|
|
158
|
+
rf_dict_to_path = _LazyRenderFunc("rf_dict_to_path")
|
|
159
|
+
rf_dict_to_str = _LazyRenderFunc("rf_dict_to_str")
|
|
160
|
+
rf_path_to_bytes = _LazyRenderFunc("rf_path_to_bytes")
|
|
161
|
+
rf_path_to_dict = _LazyRenderFunc("rf_path_to_dict")
|
|
162
|
+
rf_str_to_bytes = _LazyRenderFunc("rf_str_to_bytes")
|
|
163
|
+
rf_str_to_dict = _LazyRenderFunc("rf_str_to_dict")
|
|
164
|
+
run_button = _LazyRenderFunc("run_button")
|
|
165
|
+
run_chain = _LazyRenderFunc("run_chain")
|
|
166
|
+
save_file_fn = _LazyRenderFunc("save_file_fn")
|
|
167
|
+
save_span_fn = _LazyRenderFunc("save_span_fn")
|
|
168
|
+
str_to_general_parse = _LazyRenderFunc("str_to_general_parse")
|
|
169
|
+
test_columns = _LazyRenderFunc("test_columns")
|
|
170
|
+
test_widget = _LazyRenderFunc("test_widget")
|
|
171
|
+
text = _LazyRenderFunc("text")
|
|
172
|
+
unsort_dict_alphabetically = _LazyRenderFunc("unsort_dict_alphabetically")
|
|
173
|
+
var_dict_to_class = _LazyRenderFunc("var_dict_to_class")
|
|
174
|
+
|
|
175
|
+
def __getattr__(self, name):
|
|
176
|
+
# Dunders must NOT fabricate or handle. No render func is named like a
|
|
177
|
+
# dunder, and fabricating would break copy/pickle of anything holding
|
|
178
|
+
# RenderFuncs (deepcopy probes __deepcopy__ -> a _LazyRenderFunc whose
|
|
179
|
+
# _resolve() raises + spams the registry to stdout) and would detonate
|
|
180
|
+
# inspect.unwrap the moment _LazyRenderFunc gains a __class__ property.
|
|
181
|
+
# See _LazyRenderFunc.__getattr__'s own guard (and _Modes in modes.py).
|
|
182
|
+
if name.startswith("__") and name.endswith("__"):
|
|
183
|
+
raise AttributeError(name)
|
|
184
|
+
return _LazyRenderFunc(name)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# Reference render funcs by symbol without importing their module:
|
|
189
|
+
# from meltygui.core.rendering.render_funcs import RenderFuncs
|
|
190
|
+
# @window(render_func=RenderFuncs.draw_type)
|
|
191
|
+
RenderFuncs = _RenderFuncs()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class CodeGenerator:
|
|
195
|
+
"""Headless code generation over the app's own cst↔dict round-trip.
|
|
196
|
+
|
|
197
|
+
update_render_funcs() rewrites the _RenderFuncs class above so every
|
|
198
|
+
registered @render_func shows up as a real, IDE-visible member — that's what
|
|
199
|
+
makes `RenderFuncs.<name>` autocomplete. The edit runs through the same
|
|
200
|
+
converters the in-app editor uses, just without imgui:
|
|
201
|
+
|
|
202
|
+
source span → cst.ClassDef → cst_classdef_to_dict → (add a key per name)
|
|
203
|
+
→ dict_to_cst_classdef → source span
|
|
204
|
+
|
|
205
|
+
Each member is a real assignment (`name = _LazyRenderFunc("name")` /
|
|
206
|
+
`name = _LazyMode("name")`), so it is a genuine class attribute the IDE can
|
|
207
|
+
see; __getattr__ stays as the fallback for anything added since the last
|
|
208
|
+
regeneration. Nothing to recompile or hotswap — runtime behaviour is unchanged.
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def _regenerate(target_cls, names, expr_for):
|
|
213
|
+
"""Rewrite `target_cls`'s body in this file so it has one assignment per
|
|
214
|
+
name, via the same cst↔dict round-trip the in-app editor uses:
|
|
215
|
+
|
|
216
|
+
source span → cst.ClassDef → cst_classdef_to_dict → (add a key per
|
|
217
|
+
name) → dict_to_cst_classdef → source span
|
|
218
|
+
|
|
219
|
+
Existing class vars are stripped first so only the Add path runs (clean +
|
|
220
|
+
idempotent). `expr_for(name)` returns the assignment's RHS source.
|
|
221
|
+
Returns the written Address."""
|
|
222
|
+
import libcst as cst
|
|
223
|
+
from meltygui.code.fileref import to_address
|
|
224
|
+
from meltygui.code.file_converters import load_text
|
|
225
|
+
from meltygui.code.file_converters import save_span_fn
|
|
226
|
+
from meltygui.code.libcst_conversion import cst_classdef_to_dict
|
|
227
|
+
from meltygui.code.libcst_conversion import dict_to_cst_classdef
|
|
228
|
+
|
|
229
|
+
class _StripClassVars(cst.CSTTransformer):
|
|
230
|
+
def leave_SimpleStatementLine(self, original, updated):
|
|
231
|
+
if len(updated.body) == 1 and isinstance(
|
|
232
|
+
updated.body[0], (cst.Assign, cst.AnnAssign)):
|
|
233
|
+
return cst.RemoveFromParent()
|
|
234
|
+
return updated
|
|
235
|
+
|
|
236
|
+
ref = to_address(target_cls)
|
|
237
|
+
if ref is None:
|
|
238
|
+
raise RuntimeError(
|
|
239
|
+
f"Could not resolve the source location of {target_cls.__name__}")
|
|
240
|
+
|
|
241
|
+
module = cst.parse_module(load_text(ref))
|
|
242
|
+
classdef = next(s for s in module.body if isinstance(s, cst.ClassDef))
|
|
243
|
+
classdef = classdef.visit(_StripClassVars())
|
|
244
|
+
|
|
245
|
+
members = cst_classdef_to_dict(classdef)
|
|
246
|
+
for name in names:
|
|
247
|
+
members[name] = cst.parse_expression(expr_for(name))
|
|
248
|
+
new_classdef = dict_to_cst_classdef(members)
|
|
249
|
+
|
|
250
|
+
new_module = module.with_changes(body=[
|
|
251
|
+
new_classdef if isinstance(s, cst.ClassDef) else s for s in module.body])
|
|
252
|
+
|
|
253
|
+
# save_span_fn is a @render_func; .__wrapped__ is the bare span-spelle
|
|
254
|
+
# (no imgui / draw_state) - the headless edge of the same save node the
|
|
255
|
+
# in-app editor saves through.
|
|
256
|
+
save_span_fn.__wrapped__(new_module.code, ref=ref)
|
|
257
|
+
return ref
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def update_render_funcs():
|
|
261
|
+
"""Regenerate _RenderFuncs from the live @render_func registry."""
|
|
262
|
+
names = sorted(n for n in Core.melty.render_funcs_by_name if n.isidentifier())
|
|
263
|
+
return CodeGenerator._regenerate(
|
|
264
|
+
_RenderFuncs, names, lambda n: f'_LazyRenderFunc("{n}")')
|
|
265
|
+
|
|
266
|
+
@staticmethod
|
|
267
|
+
def update_modes():
|
|
268
|
+
"""Regenerate _Modes (in modes.py) from the live Mode enum."""
|
|
269
|
+
from meltygui.core.rendering.mode import Mode
|
|
270
|
+
from meltygui.core.rendering.modes import _Modes
|
|
271
|
+
names = sorted(n for n in Mode.__members__ if n.isidentifier())
|
|
272
|
+
return CodeGenerator._regenerate(
|
|
273
|
+
_Modes, names, lambda n: f'_LazyMode("{n}")')
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Shape-refined default routing: `Shaped(of, shape, dtype)`.
|
|
2
|
+
|
|
3
|
+
`is_default_for` / `is_lens_for` route a value to a view by its TYPE or type
|
|
4
|
+
NAME. A `Shaped` entry refines that by the value's SHAPE (and optionally its
|
|
5
|
+
dtype), so "tuples of 3 floats" and "tensors with 3+ dims" are both one
|
|
6
|
+
registration:
|
|
7
|
+
|
|
8
|
+
@render_func(is_default_for=('tint', Shaped(tuple, (3,), float),
|
|
9
|
+
Shaped(tuple, (4,), float)))
|
|
10
|
+
@render_func(is_default_for=(Shaped("Tensor", (None, None)),)) # 2-D
|
|
11
|
+
@render_func(is_default_for=("GLTexture", Shaped("Tensor", (None, None, None, ...))))
|
|
12
|
+
|
|
13
|
+
The unifying idea: every value has a signature `(type, shape, dtype)` and a
|
|
14
|
+
tuple is a tiny 1-D tensor — `(0.2, 0.5, 1.0)` is `(tuple, (3,), float)`,
|
|
15
|
+
`torch.randn(8, 64, 64)` is `(Tensor, (8, 64, 64), float)`. One pattern
|
|
16
|
+
grammar covers both.
|
|
17
|
+
|
|
18
|
+
Shape PATTERN grammar — plain tuples, Python's own shape idiom:
|
|
19
|
+
|
|
20
|
+
3 this axis has extent exactly 3
|
|
21
|
+
None this axis has any extent (TensorShape([None, 3]))
|
|
22
|
+
... any number of further axes; must be LAST (tuple[int, ...])
|
|
23
|
+
{3, 4} this axis has one of these extents
|
|
24
|
+
|
|
25
|
+
(3,) len-3 tuple / 1-D tensor of 3
|
|
26
|
+
(None, None) exactly 2-D
|
|
27
|
+
(None, None, None, ...) 3-D or more
|
|
28
|
+
({3, 4},) len 3 or 4
|
|
29
|
+
|
|
30
|
+
`dtype` is a Python KIND — `float`, `int`, `bool`, `complex` — or a tuple of
|
|
31
|
+
kinds (any-of), or None (don't care). Tensor/ndarray dtypes map to their kind
|
|
32
|
+
(bf16/f16/f32/f64 → float, every int width → int). A SEQUENCE's dtype is its
|
|
33
|
+
PROMOTED kind, like torch.result_type: any float present → float, all ints →
|
|
34
|
+
int, all bools → bool, anything else (str, nested, mixed) → None. So
|
|
35
|
+
`Shaped(tuple, (3,), float)` takes `(0, 0, 0, 0.1)` (a real tint) but not
|
|
36
|
+
`(1, 2, 3)` (a version / int triple), which keeps int positions and extents
|
|
37
|
+
off the colour picker.
|
|
38
|
+
|
|
39
|
+
Cost contract: shape extraction is O(1) — `.shape` on tensors, `len()` on
|
|
40
|
+
sequences. The only O(n) step is the sequence dtype promotion and it runs ONLY
|
|
41
|
+
after the shape pattern already matched (so the pattern bounds n) and never
|
|
42
|
+
past `SEQ_DTYPE_SCAN_CAP` elements (dtype = None beyond it). No value-RANGE
|
|
43
|
+
constraints on purpose: they'd be an O(content) scan on tensors.
|
|
44
|
+
|
|
45
|
+
Precedence among Shaped entries is by `score()`: the more constrained pattern
|
|
46
|
+
wins (fixed axis 2, `None` axis 1, `...` 0, dtype +1), so a generic
|
|
47
|
+
`(None,)` never steals a `(3,), float` colour. Ties → closer MRO match →
|
|
48
|
+
registration order. This module is dependency-free so it can be imported from
|
|
49
|
+
anywhere (registrations live in view modules; resolution lives in meltygui).
|
|
50
|
+
"""
|
|
51
|
+
from dataclasses import dataclass
|
|
52
|
+
from typing import Any, Iterable, Optional
|
|
53
|
+
|
|
54
|
+
# Sequences longer than this never get a dtype (pattern-matched first, so only
|
|
55
|
+
# an unbounded `(None,)`/`(...)` pattern can reach it).
|
|
56
|
+
SEQ_DTYPE_SCAN_CAP = 64
|
|
57
|
+
|
|
58
|
+
_KINDS = (float, int, bool, complex)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _freeze_axis(axis):
|
|
62
|
+
if axis is None or axis is Ellipsis:
|
|
63
|
+
return axis
|
|
64
|
+
if isinstance(axis, bool):
|
|
65
|
+
raise TypeError(f"shape axis must be int/None/.../set, got bool {axis!r}")
|
|
66
|
+
if isinstance(axis, int):
|
|
67
|
+
if axis < 0:
|
|
68
|
+
raise ValueError(f"shape axis extent must be >= 0, got {axis}")
|
|
69
|
+
return axis
|
|
70
|
+
if isinstance(axis, (set, frozenset, list, tuple)):
|
|
71
|
+
members = frozenset(axis)
|
|
72
|
+
if not members or any(not isinstance(m, int) or isinstance(m, bool) or m < 0
|
|
73
|
+
for m in members):
|
|
74
|
+
raise TypeError(f"one-of axis must hold non-negative ints, got {axis!r}")
|
|
75
|
+
return members
|
|
76
|
+
raise TypeError(f"unsupported shape axis {axis!r}")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass(frozen=True)
|
|
80
|
+
class Shaped:
|
|
81
|
+
"""A (type, shape-pattern, dtype) matcher. Hashable → usable as a registry
|
|
82
|
+
key alongside plain types / type names in `is_default_for`."""
|
|
83
|
+
of: Any # type or type NAME ("Tensor", "ndarray")
|
|
84
|
+
shape: tuple # pattern, see module doc
|
|
85
|
+
dtype: Any = None # kind, tuple of kinds, or None
|
|
86
|
+
|
|
87
|
+
def __post_init__(self):
|
|
88
|
+
if not isinstance(self.of, (type, str)):
|
|
89
|
+
raise TypeError(f"Shaped.of must be a type or a type name, got {self.of!r}")
|
|
90
|
+
if isinstance(self.shape, (int, type(None))):
|
|
91
|
+
shape = (self.shape,)
|
|
92
|
+
else:
|
|
93
|
+
shape = tuple(self.shape)
|
|
94
|
+
shape = tuple(_freeze_axis(a) for a in shape)
|
|
95
|
+
if Ellipsis in shape[:-1]:
|
|
96
|
+
raise ValueError("`...` may only appear as the LAST axis of a shape pattern")
|
|
97
|
+
object.__setattr__(self, "shape", shape)
|
|
98
|
+
dtype = self.dtype
|
|
99
|
+
if dtype is not None:
|
|
100
|
+
kinds = tuple(dtype) if isinstance(dtype, (tuple, list, set, frozenset)) else (dtype,)
|
|
101
|
+
for k in kinds:
|
|
102
|
+
if k not in _KINDS:
|
|
103
|
+
raise TypeError(f"Shaped.dtype must be one of {_KINDS}, got {k!r}")
|
|
104
|
+
object.__setattr__(self, "dtype", kinds if len(kinds) > 1 else kinds[0])
|
|
105
|
+
|
|
106
|
+
# -- display ---------------------------------------------------------
|
|
107
|
+
def __repr__(self):
|
|
108
|
+
of = self.of.__name__ if isinstance(self.of, type) else repr(self.of)
|
|
109
|
+
axes = []
|
|
110
|
+
for a in self.shape:
|
|
111
|
+
if a is Ellipsis:
|
|
112
|
+
axes.append("...")
|
|
113
|
+
elif isinstance(a, frozenset):
|
|
114
|
+
axes.append("{" + ", ".join(str(m) for m in sorted(a)) + "}")
|
|
115
|
+
else:
|
|
116
|
+
axes.append(str(a))
|
|
117
|
+
shape = "(" + ", ".join(axes) + ("," if len(axes) == 1 else "") + ")"
|
|
118
|
+
if self.dtype is None:
|
|
119
|
+
return f"Shaped({of}, {shape})"
|
|
120
|
+
dt = self.dtype
|
|
121
|
+
dt = ("(" + ", ".join(k.__name__ for k in dt) + ")") if isinstance(dt, tuple) else dt.__name__
|
|
122
|
+
return f"Shaped({of}, {shape}, {dt})"
|
|
123
|
+
|
|
124
|
+
# -- matching --------------------------------------------------------
|
|
125
|
+
@property
|
|
126
|
+
def score(self) -> int:
|
|
127
|
+
"""Constraint strength; higher = more specific. Fixed / one-of axis 2,
|
|
128
|
+
`None` axis 1 (still pins ndim), `...` 0, dtype +1."""
|
|
129
|
+
s = 0
|
|
130
|
+
for a in self.shape:
|
|
131
|
+
if a is Ellipsis:
|
|
132
|
+
continue
|
|
133
|
+
s += 1 if a is None else 2
|
|
134
|
+
if self.dtype is not None:
|
|
135
|
+
s += 1
|
|
136
|
+
return s
|
|
137
|
+
|
|
138
|
+
def matches_type(self, real_type: type) -> int:
|
|
139
|
+
"""MRO distance of the closest class matching `of` (0 = exact), or -1."""
|
|
140
|
+
return mro_distance(self.of, real_type)
|
|
141
|
+
|
|
142
|
+
def matches_shape(self, shape) -> bool:
|
|
143
|
+
return shape is not None and shape_matches(self.shape, shape)
|
|
144
|
+
|
|
145
|
+
def matches_dtype(self, value) -> bool:
|
|
146
|
+
if self.dtype is None:
|
|
147
|
+
return True
|
|
148
|
+
kind = value_dtype(value)
|
|
149
|
+
if kind is None:
|
|
150
|
+
return False
|
|
151
|
+
return kind in self.dtype if isinstance(self.dtype, tuple) else kind is self.dtype
|
|
152
|
+
|
|
153
|
+
def matches(self, value, real_type: Optional[type] = None) -> bool:
|
|
154
|
+
"""Full match: type (by MRO or name), then shape, then dtype — in that
|
|
155
|
+
order so the O(n) sequence dtype scan only ever runs on a value whose
|
|
156
|
+
shape already fit the pattern."""
|
|
157
|
+
if self.matches_type(real_type if real_type is not None else type(value)) < 0:
|
|
158
|
+
return False
|
|
159
|
+
if not self.matches_shape(value_shape(value)):
|
|
160
|
+
return False
|
|
161
|
+
return self.matches_dtype(value)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ---------------------------------------------------------------------------
|
|
165
|
+
# value → (shape, dtype)
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
|
|
168
|
+
def mro_distance(of, real_type: type) -> int:
|
|
169
|
+
"""Index in `real_type.__mro__` of the class `of` names (a type, or a
|
|
170
|
+
type's __name__), or -1 when no class in the MRO matches."""
|
|
171
|
+
mro = getattr(real_type, "__mro__", None)
|
|
172
|
+
if mro is None:
|
|
173
|
+
return -1
|
|
174
|
+
if isinstance(of, str):
|
|
175
|
+
for i, t in enumerate(mro):
|
|
176
|
+
if t.__name__ == of:
|
|
177
|
+
return i
|
|
178
|
+
return -1
|
|
179
|
+
for i, t in enumerate(mro):
|
|
180
|
+
if t is of:
|
|
181
|
+
return i
|
|
182
|
+
return -1
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def value_shape(value) -> Optional[tuple]:
|
|
186
|
+
"""The value's shape tuple, or None when it has none.
|
|
187
|
+
|
|
188
|
+
Array-likes (anything with a tuple-able `.shape`: torch tensors, ndarrays,
|
|
189
|
+
GLTexture, CudaVolumeView …) report it directly; tuples/lists report
|
|
190
|
+
`(len,)` — a sequence is a 1-D array. str/bytes/dicts are not shaped."""
|
|
191
|
+
shape = getattr(value, "shape", None)
|
|
192
|
+
if shape is not None:
|
|
193
|
+
try:
|
|
194
|
+
return tuple(int(d) for d in shape)
|
|
195
|
+
except (TypeError, ValueError):
|
|
196
|
+
return None
|
|
197
|
+
if isinstance(value, (tuple, list)):
|
|
198
|
+
return (len(value),)
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _kind_of_dtype(dtype):
|
|
203
|
+
"""Map a torch / numpy dtype object to a Python kind, duck-typed so this
|
|
204
|
+
module never imports either library."""
|
|
205
|
+
# numpy: dtype.kind in 'biufcmMOSUV'
|
|
206
|
+
kind_char = getattr(dtype, "kind", None)
|
|
207
|
+
if isinstance(kind_char, str):
|
|
208
|
+
return {"b": bool, "i": int, "u": int, "f": float, "c": complex}.get(kind_char)
|
|
209
|
+
# torch: is_floating_point / is_complex properties; bool by name.
|
|
210
|
+
if getattr(dtype, "is_floating_point", False):
|
|
211
|
+
return float
|
|
212
|
+
if getattr(dtype, "is_complex", False):
|
|
213
|
+
return complex
|
|
214
|
+
name = str(dtype)
|
|
215
|
+
if name.endswith("bool"):
|
|
216
|
+
return bool
|
|
217
|
+
if "int" in name:
|
|
218
|
+
return int
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def value_dtype(value):
|
|
223
|
+
"""The value's KIND (float / int / bool / complex) or None.
|
|
224
|
+
|
|
225
|
+
Array-likes map their dtype. Sequences promote like torch.result_type:
|
|
226
|
+
any float → float, else all int → int, else all bool → bool; any other
|
|
227
|
+
element kind → None. Sequences past SEQ_DTYPE_SCAN_CAP are None."""
|
|
228
|
+
dtype = getattr(value, "dtype", None)
|
|
229
|
+
if dtype is not None:
|
|
230
|
+
return _kind_of_dtype(dtype)
|
|
231
|
+
if isinstance(value, (tuple, list)):
|
|
232
|
+
if len(value) > SEQ_DTYPE_SCAN_CAP:
|
|
233
|
+
return None
|
|
234
|
+
rank = 0 # 0 empty, 1 bool, 2 int, 3 float, 4 complex - promotion order
|
|
235
|
+
for c in value:
|
|
236
|
+
# bool first: it's an int subclass. duck-type checks are the fast
|
|
237
|
+
# path; the isinstance fallbacks catch numpy scalars & subclasses.
|
|
238
|
+
t = type(c)
|
|
239
|
+
if t is bool or isinstance(c, bool):
|
|
240
|
+
r = 1
|
|
241
|
+
elif t is int or isinstance(c, int):
|
|
242
|
+
r = 2
|
|
243
|
+
elif t is float or isinstance(c, float):
|
|
244
|
+
r = 3
|
|
245
|
+
elif isinstance(c, complex):
|
|
246
|
+
r = 4
|
|
247
|
+
else:
|
|
248
|
+
# numpy / torch 0-d scalars carry a dtype of their kind.
|
|
249
|
+
k = _kind_of_dtype(getattr(c, "dtype", None))
|
|
250
|
+
if k is None:
|
|
251
|
+
return None
|
|
252
|
+
r = {bool: 1, int: 2, float: 3, complex: 4}[k]
|
|
253
|
+
if r > rank:
|
|
254
|
+
rank = r
|
|
255
|
+
return (None, bool, int, float, complex)[rank]
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def shape_matches(pattern: tuple, shape: tuple) -> bool:
|
|
260
|
+
"""Does the concrete `shape` fit the `pattern`? See the module doc."""
|
|
261
|
+
if pattern and pattern[-1] is Ellipsis:
|
|
262
|
+
fixed = pattern[:-1]
|
|
263
|
+
if len(shape) < len(fixed):
|
|
264
|
+
return False
|
|
265
|
+
else:
|
|
266
|
+
fixed = pattern
|
|
267
|
+
if len(shape) != len(fixed):
|
|
268
|
+
return False
|
|
269
|
+
for want, got in zip(fixed, shape):
|
|
270
|
+
if want is None:
|
|
271
|
+
continue
|
|
272
|
+
if isinstance(want, frozenset):
|
|
273
|
+
if got not in want:
|
|
274
|
+
return False
|
|
275
|
+
elif got != want:
|
|
276
|
+
return False
|
|
277
|
+
return True
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
# Searching over a registry
|
|
282
|
+
# ---------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
def best_match(entries: Iterable, value, real_type: Optional[type] = None):
|
|
285
|
+
"""Pick the registered function whose `Shaped` key best fits `value`.
|
|
286
|
+
|
|
287
|
+
`entries` iterates `(Shaped, func)` pairs (a dict's `.items()`). Returns
|
|
288
|
+
the func or None. Most constrained pattern (score) wins; ties go to the
|
|
289
|
+
closest MRO match, then to the earliest registered. The value's shape is
|
|
290
|
+
extracted at most once, and only when some entry's type matched."""
|
|
291
|
+
if real_type is None:
|
|
292
|
+
real_type = type(value)
|
|
293
|
+
best = None
|
|
294
|
+
best_key = None
|
|
295
|
+
shape = _UNSET = object()
|
|
296
|
+
for shaped, func in entries:
|
|
297
|
+
if func is None or not isinstance(shaped, Shaped):
|
|
298
|
+
continue
|
|
299
|
+
dist = shaped.matches_type(real_type)
|
|
300
|
+
if dist < 0:
|
|
301
|
+
continue
|
|
302
|
+
if shape is _UNSET:
|
|
303
|
+
shape = value_shape(value)
|
|
304
|
+
if shape is None or not shaped.matches_shape(shape):
|
|
305
|
+
continue
|
|
306
|
+
# (score desc, mro distance asc) - compared as a tuple, first wins on ties.
|
|
307
|
+
key = (shaped.score, -dist)
|
|
308
|
+
if best_key is None or key > best_key:
|
|
309
|
+
if not shaped.matches_dtype(value):
|
|
310
|
+
continue
|
|
311
|
+
best, best_key = func, key
|
|
312
|
+
return best
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
_register = None
|
|
4
|
+
_pending = []
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def set_window_registrar(register_fn):
|
|
8
|
+
global _register
|
|
9
|
+
_register = register_fn
|
|
10
|
+
for cls, kwargs in _pending:
|
|
11
|
+
register_fn(cls, kwargs)
|
|
12
|
+
_pending.clear()
|
|
13
|
+
|
|
14
|
+
#[tint=(0.0, 0.541, 0.85)]
|
|
15
|
+
def window(cls=None, **kwargs):
|
|
16
|
+
def wrap(cls):
|
|
17
|
+
if _register is None:
|
|
18
|
+
_pending.append((cls, kwargs))
|
|
19
|
+
else:
|
|
20
|
+
_register(cls, kwargs)
|
|
21
|
+
return cls
|
|
22
|
+
|
|
23
|
+
if cls is None:
|
|
24
|
+
return wrap
|
|
25
|
+
return wrap(cls)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shared framework runtime machinery."""
|