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,564 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import threading
|
|
3
|
+
from collections import OrderedDict
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from meltygui.core.runtime.toggles import Toggles
|
|
8
|
+
from meltygui.core.windowing.glfw_utils import print_stack_trace
|
|
9
|
+
from meltygui.core.windowing.glfw_utils import get_live_frames
|
|
10
|
+
from meltygui.core.windowing.glfw_utils import _print_lock
|
|
11
|
+
from meltygui.core.windowing.glfw_utils import trace_group
|
|
12
|
+
from meltygui.core.windowing.glfw_utils import request_render
|
|
13
|
+
from meltygui.core.conversion.path_finder import Pending
|
|
14
|
+
from meltygui.core.conversion.path_finder import PendingState
|
|
15
|
+
from meltygui.core.cache.invalidation_tracker import Note
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Background:
|
|
19
|
+
_user_cache = {} # user_id -> OrderedDict{hash -> result}
|
|
20
|
+
_cache_size = 1
|
|
21
|
+
_active = set()
|
|
22
|
+
_user_tasks = {}
|
|
23
|
+
_lock = threading.Lock()
|
|
24
|
+
_pool = ThreadPoolExecutor(max_workers=16)
|
|
25
|
+
_debounce_timers = {} # debounce_key -> Timer
|
|
26
|
+
_debounce_latest = {} # debounce_key -> dict of latest call params
|
|
27
|
+
_hash_times = {} # type_name -> [total_time_sec, count]
|
|
28
|
+
_dict_key_times = {} # dict key name -> [total_time_sec, count]
|
|
29
|
+
_task_times = {} # func_name -> [total_time_sec, count]
|
|
30
|
+
did_shutdown = False
|
|
31
|
+
@classmethod
|
|
32
|
+
def _record_dict_key_time(cls, key: str, elapsed: float):
|
|
33
|
+
# No lock here - called from simple_hash which may recurse deeply;
|
|
34
|
+
# some inaccuracy from races is acceptable for profiling.
|
|
35
|
+
entry = cls._dict_key_times.get(key)
|
|
36
|
+
if entry is None:
|
|
37
|
+
cls._dict_key_times[key] = [elapsed, 1]
|
|
38
|
+
else:
|
|
39
|
+
entry[0] += elapsed
|
|
40
|
+
entry[1] += 1
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def _timed_hash(cls, value, user_id):
|
|
44
|
+
import time
|
|
45
|
+
type_name = type(value).__name__
|
|
46
|
+
t0 = time.perf_counter()
|
|
47
|
+
h = cls.simple_hash(value=value) + user_id
|
|
48
|
+
elapsed = time.perf_counter() - t0
|
|
49
|
+
|
|
50
|
+
with cls._lock:
|
|
51
|
+
if type_name not in cls._hash_times:
|
|
52
|
+
cls._hash_times[type_name] = [0.0, 0]
|
|
53
|
+
cls._hash_times[type_name][0] += elapsed
|
|
54
|
+
cls._hash_times[type_name][1] += 1
|
|
55
|
+
|
|
56
|
+
return h
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def shutdown(cls):
|
|
60
|
+
print("Shutting down background system...")
|
|
61
|
+
# with cls._lock:
|
|
62
|
+
cls._active.clear()
|
|
63
|
+
cls._user_tasks.clear()
|
|
64
|
+
cls._user_cache.clear()
|
|
65
|
+
for timer in cls._debounce_timers.values():
|
|
66
|
+
timer.cancel()
|
|
67
|
+
cls._debounce_timers.clear()
|
|
68
|
+
cls._debounce_latest.clear()
|
|
69
|
+
|
|
70
|
+
print_timings = False
|
|
71
|
+
|
|
72
|
+
if print_timings:
|
|
73
|
+
print("\n--- Hash timing averages (by type) ---")
|
|
74
|
+
for type_name, (total, count) in sorted(cls._hash_times.items()):
|
|
75
|
+
avg_ms = (total / count) * 1000
|
|
76
|
+
print(f" {type_name:<30} avg={avg_ms:.3f}ms n={count}")
|
|
77
|
+
print("--------------------------------------\n")
|
|
78
|
+
|
|
79
|
+
print("--- Hash timing averages (dict keys, sorted by avg) ---")
|
|
80
|
+
sorted_keys = sorted(cls._dict_key_times.items(), key=lambda x: x[1][0] / x[1][1], reverse=True)
|
|
81
|
+
for key_name, (total, count) in sorted_keys:
|
|
82
|
+
avg_ms = (total / count) * 1000
|
|
83
|
+
print(f" {key_name:<40} avg={avg_ms:.3f}ms n={count}")
|
|
84
|
+
print("-------------------------------------------------------\n")
|
|
85
|
+
|
|
86
|
+
print("--- Task timing averages (by function, sorted by avg) ---")
|
|
87
|
+
sorted_tasks = sorted(cls._task_times.items(), key=lambda x: x[1][0] / x[1][1], reverse=True)
|
|
88
|
+
for func_name, (total, count) in sorted_tasks:
|
|
89
|
+
avg_ms = (total / count) * 1000
|
|
90
|
+
print(f" {func_name:<50} avg={avg_ms:.3f}ms n={count}")
|
|
91
|
+
print("----------------------------------------------------------\n")
|
|
92
|
+
|
|
93
|
+
print("Shutting down background thread pool...")
|
|
94
|
+
|
|
95
|
+
cls._pool.shutdown(wait=True)
|
|
96
|
+
print("Background thread pool shut down successfully.")
|
|
97
|
+
cls.did_shutdown = True
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def run(cls, func, user_id, func_kwargs=None, *,
|
|
101
|
+
stateful=False, no_cache=False, invalidate_id=None,
|
|
102
|
+
on_frame=None, frames=None, debounce=10):
|
|
103
|
+
"""Run func with func_kwargs, with caching, debouncing, and background dispatch.
|
|
104
|
+
|
|
105
|
+
Background.run's own parameters (user_id, stateful, no_cache, etc.) are
|
|
106
|
+
cleanly separated from the function's kwargs via the func_kwargs dict.
|
|
107
|
+
"""
|
|
108
|
+
if func_kwargs is None:
|
|
109
|
+
func_kwargs = {}
|
|
110
|
+
|
|
111
|
+
if "input_value" in func_kwargs:
|
|
112
|
+
h = cls._timed_hash(func_kwargs.get("input_value", None), user_id)
|
|
113
|
+
else:
|
|
114
|
+
print(f"Warning no 'value' or 'input_value' in func_kwargs for {func.__name__} with user_id {user_id}, using 0 as hash")
|
|
115
|
+
h = "0_" + str(user_id)
|
|
116
|
+
|
|
117
|
+
# A side-effect payload (e.g. a save's `code_str`) isn't the hashed input
|
|
118
|
+
# but DOES change the operation. The hashed input for _do_save is the
|
|
119
|
+
# Address, which is stable across edits - so without folding the payload
|
|
120
|
+
# in, two writes to the same location share a hash and the debounce /
|
|
121
|
+
# in-flight dedup below sees the second as a duplicate and drops its
|
|
122
|
+
# content. That's the intermittent "lost save". Fold it in so distinct
|
|
123
|
+
# content gets a distinct key (identical content still dedups, correctly).
|
|
124
|
+
if "code_str" in func_kwargs:
|
|
125
|
+
h = f"{h}|{cls.simple_hash(value=func_kwargs.get('code_str'))}"
|
|
126
|
+
|
|
127
|
+
# if "search_text" in func_kwargs:
|
|
128
|
+
# h_s = cls._timed_hash(func_kwargs.get('search_text', None), user_id)
|
|
129
|
+
# h = str(h) + "_" + str(h_s)
|
|
130
|
+
# print(f"{func_kwargs.get('search_text')}")
|
|
131
|
+
|
|
132
|
+
# Inline path for stateful converters (no apply, short chains)
|
|
133
|
+
if stateful:
|
|
134
|
+
if not no_cache:
|
|
135
|
+
cache = cls._user_cache.get(user_id)
|
|
136
|
+
if cache and h in cache:
|
|
137
|
+
cache.move_to_end(h)
|
|
138
|
+
return cache[h]
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
result = func(**func_kwargs)
|
|
142
|
+
except Exception:
|
|
143
|
+
result = Pending(originated=cls, status="Read Only", state=PendingState.ERROR)
|
|
144
|
+
|
|
145
|
+
if user_id not in cls._user_cache:
|
|
146
|
+
cls._user_cache[user_id] = OrderedDict()
|
|
147
|
+
cls._user_cache[user_id][h] = result
|
|
148
|
+
cls._user_cache[user_id].move_to_end(h)
|
|
149
|
+
while len(cls._user_cache[user_id]) > cls._cache_size:
|
|
150
|
+
cls._user_cache[user_id].popitem(last=False)
|
|
151
|
+
|
|
152
|
+
return result
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
from meltygui.core.melty import Melty
|
|
156
|
+
if Melty.frame_count < 2:
|
|
157
|
+
debounce = None
|
|
158
|
+
|
|
159
|
+
# --- debounce path ---
|
|
160
|
+
if debounce is not None:
|
|
161
|
+
debounce_key = user_id
|
|
162
|
+
|
|
163
|
+
cache = cls._user_cache.get(user_id)
|
|
164
|
+
|
|
165
|
+
stale_value = None
|
|
166
|
+
if cache and h in cache:
|
|
167
|
+
if no_cache:
|
|
168
|
+
stale_value = cache[h]
|
|
169
|
+
else:
|
|
170
|
+
cache.move_to_end(h)
|
|
171
|
+
return cache[h]
|
|
172
|
+
|
|
173
|
+
if h in cls._active:
|
|
174
|
+
return stale_value if stale_value is not None else Pending(originated=cls, status="background thread active", state=PendingState.BACKGROUND)
|
|
175
|
+
|
|
176
|
+
latest = cls._debounce_latest.get(debounce_key)
|
|
177
|
+
if latest is not None and latest.get("hash") == h:
|
|
178
|
+
return stale_value if stale_value is not None else Pending(originated=cls, status="debounce waiting", state=PendingState.BACKGROUND)
|
|
179
|
+
|
|
180
|
+
prev = cls._debounce_timers.pop(debounce_key, None)
|
|
181
|
+
if prev is not None:
|
|
182
|
+
prev.cancel()
|
|
183
|
+
|
|
184
|
+
cls._debounce_latest[debounce_key] = dict(
|
|
185
|
+
hash=h,
|
|
186
|
+
func=func, user_id=user_id, no_cache=no_cache,
|
|
187
|
+
invalidate_id=invalidate_id, on_frame=on_frame,
|
|
188
|
+
func_kwargs=func_kwargs,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if Toggles.debug_threads:
|
|
192
|
+
frames = get_live_frames()
|
|
193
|
+
|
|
194
|
+
def _fire():
|
|
195
|
+
cls._debounce_timers.pop(debounce_key, None)
|
|
196
|
+
latest = cls._debounce_latest.pop(debounce_key, None)
|
|
197
|
+
if latest is not None:
|
|
198
|
+
cls.run(
|
|
199
|
+
latest["func"], latest["user_id"],
|
|
200
|
+
func_kwargs=latest["func_kwargs"],
|
|
201
|
+
no_cache=latest["no_cache"],
|
|
202
|
+
invalidate_id=latest["invalidate_id"],
|
|
203
|
+
on_frame=latest["on_frame"],
|
|
204
|
+
debounce=None, frames=frames,
|
|
205
|
+
)
|
|
206
|
+
if Toggles.InvalidateTracker.invalidate_stack_trace:
|
|
207
|
+
print_stack_trace(frames=frames)
|
|
208
|
+
|
|
209
|
+
timer = threading.Timer(debounce / 1000.0, _fire)
|
|
210
|
+
cls._debounce_timers[debounce_key] = timer
|
|
211
|
+
timer.start()
|
|
212
|
+
|
|
213
|
+
return stale_value if stale_value is not None else Pending(originated=cls, status="debounce waiting", state=PendingState.BACKGROUND)
|
|
214
|
+
|
|
215
|
+
# --- normal (background thread) path ---
|
|
216
|
+
|
|
217
|
+
cache = cls._user_cache.get(user_id)
|
|
218
|
+
stale_value = None
|
|
219
|
+
if cache and h in cache:
|
|
220
|
+
if no_cache:
|
|
221
|
+
stale_value = cache[h]
|
|
222
|
+
else:
|
|
223
|
+
cache.move_to_end(h)
|
|
224
|
+
return cache[h]
|
|
225
|
+
|
|
226
|
+
cls._user_tasks[user_id] = h
|
|
227
|
+
|
|
228
|
+
if h in cls._active:
|
|
229
|
+
return stale_value if stale_value is not None else Pending(originated=cls, status="background thread active", state=PendingState.BACKGROUND)
|
|
230
|
+
|
|
231
|
+
if Toggles.debug_threads and frames is None:
|
|
232
|
+
frames = get_live_frames()
|
|
233
|
+
cls._active.add(h)
|
|
234
|
+
|
|
235
|
+
def _task():
|
|
236
|
+
# Check for shutdown
|
|
237
|
+
if cls.did_shutdown:
|
|
238
|
+
print("Background system is shut down; cannot run new tasks.")
|
|
239
|
+
print_stack_trace()
|
|
240
|
+
return Pending(originated=cls, status="shutdown", state=PendingState.ERROR)
|
|
241
|
+
|
|
242
|
+
import time
|
|
243
|
+
func_name = getattr(func, "__qualname__", None) or getattr(func, "__name__", repr(func))
|
|
244
|
+
sig = func_name
|
|
245
|
+
_t0 = time.perf_counter()
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
result = func(**func_kwargs)
|
|
249
|
+
except Exception as e:
|
|
250
|
+
with trace_group(f"JOB {user_id}", hash=h) as g:
|
|
251
|
+
print_stack_trace(frames=frames, section="UI Thread",
|
|
252
|
+
group=g, watch=["draw_state.name", "input_value", "convert_path", "fn", "clean_args.input_value"])
|
|
253
|
+
print_stack_trace(exception=e, section="Background Thread",
|
|
254
|
+
group=g, watch=["value", "path", "watch", "watch.original_data", "data", "result", "result", "fn"])
|
|
255
|
+
|
|
256
|
+
result = Pending(originated=cls, status="Read Only", state=PendingState.ERROR)
|
|
257
|
+
|
|
258
|
+
elapsed = time.perf_counter() - _t0
|
|
259
|
+
entry = cls._task_times.get(sig)
|
|
260
|
+
if entry is None:
|
|
261
|
+
cls._task_times[sig] = [elapsed, 1]
|
|
262
|
+
else:
|
|
263
|
+
entry[0] += elapsed
|
|
264
|
+
entry[1] += 1
|
|
265
|
+
if on_frame is not None:
|
|
266
|
+
result = (result, on_frame)
|
|
267
|
+
for uid, task_h in cls._user_tasks.items():
|
|
268
|
+
if task_h == h:
|
|
269
|
+
if no_cache:
|
|
270
|
+
cls._user_cache.pop(uid, None)
|
|
271
|
+
if uid not in cls._user_cache:
|
|
272
|
+
cls._user_cache[uid] = OrderedDict()
|
|
273
|
+
cls._user_cache[uid][h] = result
|
|
274
|
+
cls._user_cache[uid].move_to_end(h)
|
|
275
|
+
|
|
276
|
+
while len(cls._user_cache[uid]) > cls._cache_size:
|
|
277
|
+
cls._user_cache[uid].popitem(last=False)
|
|
278
|
+
cls._active.discard(h)
|
|
279
|
+
if invalidate_id is not None:
|
|
280
|
+
from meltygui.core.melty import Melty
|
|
281
|
+
from meltygui.core.windowing.glfw_utils import request_render
|
|
282
|
+
if on_frame is None or abs(Melty.frame_count - on_frame) >= 1:
|
|
283
|
+
note = Note(name=f"Background Invalidate {invalidate_id}", reason=f"func={func_name}", tint=(0,0,1))
|
|
284
|
+
Melty.cache.invalidate_up(invalidate_id, max_depth=5, note=note)
|
|
285
|
+
request_render()
|
|
286
|
+
|
|
287
|
+
cls._pool.submit(_task)
|
|
288
|
+
return stale_value if stale_value is not None else Pending(originated=cls, status="background thread", state=PendingState.BACKGROUND)
|
|
289
|
+
|
|
290
|
+
@staticmethod
|
|
291
|
+
def compute_hash(cls, exclude=None, memo=None, depth=0, do_print=False, include_hidden=False):
|
|
292
|
+
"""
|
|
293
|
+
Create a hash of the instance's content with custom attribute exclusions.
|
|
294
|
+
Recursively handles DictConversion objects, collections, and primitive types.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
exclude: List of attribute names to exclude from hashing.
|
|
298
|
+
memo: Dictionary of already-processed objects to avoid infinite recursion.
|
|
299
|
+
depth: Current recursion depth for debugging.
|
|
300
|
+
do_print: Whether to print debug information.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
A 16-bit float value representing the instance's content.
|
|
304
|
+
"""
|
|
305
|
+
if exclude is None:
|
|
306
|
+
exclude = set()
|
|
307
|
+
|
|
308
|
+
if memo is None:
|
|
309
|
+
memo = {}
|
|
310
|
+
|
|
311
|
+
# Check if self is already in memo to avoid infinite recursion
|
|
312
|
+
if isinstance(cls, (int, float, str, bool)):
|
|
313
|
+
return str(cls)
|
|
314
|
+
|
|
315
|
+
try:
|
|
316
|
+
if id(cls) in memo:
|
|
317
|
+
if memo[id(cls)] != "processing":
|
|
318
|
+
return memo[id(cls)]
|
|
319
|
+
except Exception as e:
|
|
320
|
+
print(f"Error checking memo for id(self): {e}")
|
|
321
|
+
return None
|
|
322
|
+
|
|
323
|
+
# Add self to memo immediately with a temporary value
|
|
324
|
+
# This is crucial to break recursion loops
|
|
325
|
+
memo[id(cls)] = "processing" # Temporary value
|
|
326
|
+
|
|
327
|
+
# Create a string builder for this object
|
|
328
|
+
content_str = f"{cls.__class__.__name__}:"
|
|
329
|
+
|
|
330
|
+
# Create set of attributes to exclude
|
|
331
|
+
excluded_attrs = {'outliner_expanded_h', 'expanded', 'hash', '_parent', '_children', 'kwargs', "tensor",
|
|
332
|
+
"tensor_b", "tensor_c", 'buffer', 'ctx',
|
|
333
|
+
'texture', "texture3D", "cuda_buffer", "xy_renderer", "xyz_renderer",
|
|
334
|
+
'previous_mouse_x', 'previous_mouse_y', 'last_mouse_x', 'last_mouse_y'}
|
|
335
|
+
if exclude:
|
|
336
|
+
for excl in exclude:
|
|
337
|
+
excluded_attrs.add(excl)
|
|
338
|
+
|
|
339
|
+
# Add all non-excluded attributes to the string representation
|
|
340
|
+
if hasattr(cls, '__dict__'):
|
|
341
|
+
for key, value in cls.__dict__.items():
|
|
342
|
+
key = str(key)
|
|
343
|
+
if not include_hidden:
|
|
344
|
+
if key.startswith('_'):
|
|
345
|
+
continue
|
|
346
|
+
if key in excluded_attrs or value is None:
|
|
347
|
+
continue
|
|
348
|
+
value_str = cls._hash_value_to_str(value, exclude, memo, depth, do_print,
|
|
349
|
+
include_hidden=include_hidden)
|
|
350
|
+
content_str += f"{value_str}"
|
|
351
|
+
else:
|
|
352
|
+
content_str = cls._hash_value_to_str(cls, exclude, memo, depth, do_print,
|
|
353
|
+
include_hidden=include_hidden)
|
|
354
|
+
|
|
355
|
+
# Calculate hash
|
|
356
|
+
hash_result = hashlib.sha256(content_str.encode('utf-8')).hexdigest()
|
|
357
|
+
|
|
358
|
+
# Convert the hash to a 16-bit float (float16)
|
|
359
|
+
# Take the first 4 hex chars (16 bits) and convert to integer, then normalize to float16 range
|
|
360
|
+
hash_int = int(hash_result[:4], 16)
|
|
361
|
+
|
|
362
|
+
# Float16 has 1 sign bit, 5 exponent bits, and 10 mantissa bits
|
|
363
|
+
# We'll use the range -65504 to +65504 (full range for float16)
|
|
364
|
+
float_value = (hash_int / 0xFFFF) * 65504 * 2 - 65504
|
|
365
|
+
|
|
366
|
+
# Update the memo with the final value
|
|
367
|
+
memo[id(cls)] = float_value
|
|
368
|
+
|
|
369
|
+
if do_print:
|
|
370
|
+
print(f"Depth: {depth}, Class: {cls.__class__.__name__}, Hash: {hash_result[:8]}..., Float16: {float_value}")
|
|
371
|
+
|
|
372
|
+
if hasattr(cls, 'hash'):
|
|
373
|
+
cls.hash = float_value
|
|
374
|
+
return float_value
|
|
375
|
+
|
|
376
|
+
@staticmethod
|
|
377
|
+
def simple_hash(value, exclude=None, memo=None, depth=0, do_print=False, include_hidden=False, internal=False):
|
|
378
|
+
"""
|
|
379
|
+
Helper method to convert a value to a string representation based on its type.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
value: The value to convert to string
|
|
383
|
+
exclude: List of attribute names to exclude from hashing
|
|
384
|
+
memo: Dictionary of already-processed objects
|
|
385
|
+
depth: Current recursion depth for debugging
|
|
386
|
+
do_print: Whether to print debug information
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
A string representation of the value
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
# if not internal:
|
|
393
|
+
# return_val = Background.simple_hash(value=value, exclude=exclude, memo=memo, depth=depth, do_print=do_print,
|
|
394
|
+
# include_hidden=include_hidden, internal=True)
|
|
395
|
+
# hash_result = hashlib.sha256(return_val.encode('utf-8')).hexdigest()
|
|
396
|
+
#
|
|
397
|
+
# # Convert the hash to a 16-bit float (Float16)
|
|
398
|
+
# # Take the first 4 hex chars (16 bits) and convert to integer, then normalize to float16 range
|
|
399
|
+
# hash_int = int(hash_result[:4], 16)
|
|
400
|
+
# return str(hash_int)
|
|
401
|
+
|
|
402
|
+
if exclude is None:
|
|
403
|
+
exclude = {}
|
|
404
|
+
depth += 1
|
|
405
|
+
|
|
406
|
+
if memo is None:
|
|
407
|
+
memo = {}
|
|
408
|
+
|
|
409
|
+
# Check if hashable first. Catch everything, not just TypeError: the
|
|
410
|
+
# structural hashes are user objects (live-view frame buffers), and a
|
|
411
|
+
# buggy __hash__ - e.g. reading an attribute a mid-__init__ capture
|
|
412
|
+
# never set - must fall through to the structural path, not kill the
|
|
413
|
+
# render.
|
|
414
|
+
try:
|
|
415
|
+
standard_hash = hash(value)
|
|
416
|
+
return str(standard_hash)
|
|
417
|
+
except Exception:
|
|
418
|
+
pass
|
|
419
|
+
|
|
420
|
+
if do_print:
|
|
421
|
+
torch = sys.modules.get('torch') # lazy: never load torch just for a debug print
|
|
422
|
+
if torch is not None and hasattr(torch, 'cuda') and torch.cuda.is_available():
|
|
423
|
+
mem_str = ""
|
|
424
|
+
for i in range(torch.cuda.device_count()):
|
|
425
|
+
mem_alloc = torch.cuda.memory_allocated(i) / 1024 ** 3
|
|
426
|
+
mem_str += f"GPU {i}: {mem_alloc:.2f} GB\n"
|
|
427
|
+
print(f"Depth: {depth}, Value: {value}, Type: {type(value)}, Memory: {mem_str}")
|
|
428
|
+
else:
|
|
429
|
+
print(f"Depth: {depth}, Value: {value}, Type: {type(value)}")
|
|
430
|
+
|
|
431
|
+
# Check if value is already in memo - crucial for avoiding infinite recursion
|
|
432
|
+
if id(value) in memo:
|
|
433
|
+
return f"ref:{value}" # Return a reference indicator instead of recursing
|
|
434
|
+
|
|
435
|
+
# Handle None
|
|
436
|
+
if value is None:
|
|
437
|
+
return ""
|
|
438
|
+
|
|
439
|
+
# Handle tensors and other special types by using their type and shape/identity
|
|
440
|
+
if hasattr(value, "__class__") and value.__class__.__name__ == "Tensor":
|
|
441
|
+
try:
|
|
442
|
+
tensor_repr = f"Tensor:shape={list(value.shape)}:dtype={value.dtype}"
|
|
443
|
+
except:
|
|
444
|
+
tensor_repr = f"Tensor:{id(value)}"
|
|
445
|
+
memo[id(value)] = tensor_repr
|
|
446
|
+
return tensor_repr
|
|
447
|
+
|
|
448
|
+
if hasattr(value, "__class__") and "PreTrainedTokenizerBase" in str(value.__class__.__mro__):
|
|
449
|
+
tokenizer_repr = f"Tokenizer:{value.__class__.__name__}"
|
|
450
|
+
memo[id(value)] = tokenizer_repr
|
|
451
|
+
return tokenizer_repr
|
|
452
|
+
|
|
453
|
+
if hasattr(value, "__class__") and "Module" in str(value.__class__.__mro__):
|
|
454
|
+
module_repr = f"Module:{value.__class__.__name__}"
|
|
455
|
+
memo[id(value)] = module_repr
|
|
456
|
+
return module_repr
|
|
457
|
+
|
|
458
|
+
# Handle DictConversion objects - pass the depth argument correctly
|
|
459
|
+
if hasattr(value, "compute_hash") and isinstance(value, Background):
|
|
460
|
+
memo[id(value)] = "processing" # Add immediately to avoid recursion
|
|
461
|
+
result = Background.compute_hash(self=value, exclude=exclude, memo=memo, depth=depth,
|
|
462
|
+
do_print=do_print, include_hidden=include_hidden)
|
|
463
|
+
memo[id(value)] = result # Update with actual result
|
|
464
|
+
return str(result)
|
|
465
|
+
|
|
466
|
+
# Handle Enums
|
|
467
|
+
if hasattr(value, "__class__") and hasattr(value.__class__,
|
|
468
|
+
"__module__") and "enum" in value.__class__.__module__:
|
|
469
|
+
try:
|
|
470
|
+
enum_repr = f"Enum:{value.__class__.__name__}.{value.name}"
|
|
471
|
+
except:
|
|
472
|
+
enum_repr = f"Enum:{value.__class__.__name__}"
|
|
473
|
+
memo[id(value)] = enum_repr
|
|
474
|
+
return enum_repr
|
|
475
|
+
|
|
476
|
+
if isinstance(value, bytes):
|
|
477
|
+
bytes_repr = f"bytes:{len(value)}"
|
|
478
|
+
memo[id(value)] = bytes_repr
|
|
479
|
+
return bytes_repr
|
|
480
|
+
|
|
481
|
+
if isinstance(value, bytearray):
|
|
482
|
+
bytearray_repr = f"bytearray:{len(value)}"
|
|
483
|
+
memo[id(value)] = bytearray_repr
|
|
484
|
+
return bytearray_repr
|
|
485
|
+
|
|
486
|
+
# Handle lists
|
|
487
|
+
if isinstance(value, list):
|
|
488
|
+
memo[id(value)] = "list:processing" # Add immediately to avoid recursion
|
|
489
|
+
items_str = "["
|
|
490
|
+
for item in value:
|
|
491
|
+
items_str += Background.simple_hash(value=item, exclude=exclude, memo=memo, depth=depth,
|
|
492
|
+
do_print=do_print,
|
|
493
|
+
include_hidden=include_hidden) + ","
|
|
494
|
+
items_str += "]"
|
|
495
|
+
memo[id(value)] = items_str
|
|
496
|
+
return items_str
|
|
497
|
+
|
|
498
|
+
if isinstance(value, Pending):
|
|
499
|
+
memo[id(value)] = "tuple:processing" # Add immediately to avoid recursion
|
|
500
|
+
items_str = "("
|
|
501
|
+
items_str += Background.simple_hash(value=Pending.wrapped, exclude=exclude, memo=memo, depth=depth,
|
|
502
|
+
do_print=do_print, include_hidden=include_hidden) + ","
|
|
503
|
+
items_str += ")"
|
|
504
|
+
memo[id(value)] = items_str
|
|
505
|
+
return items_str
|
|
506
|
+
|
|
507
|
+
# Handle tuples
|
|
508
|
+
if isinstance(value, tuple):
|
|
509
|
+
memo[id(value)] = "tuple:processing" # Add immediately to avoid recursion
|
|
510
|
+
items_str = "("
|
|
511
|
+
for item in value:
|
|
512
|
+
items_str += Background.simple_hash(value=item, exclude=exclude, memo=memo, depth=depth,
|
|
513
|
+
do_print=do_print, include_hidden=include_hidden) + ","
|
|
514
|
+
items_str += ")"
|
|
515
|
+
memo[id(value)] = items_str
|
|
516
|
+
return items_str
|
|
517
|
+
|
|
518
|
+
# Fast path for dict subclasses that know how to hash themselves cheaply.
|
|
519
|
+
# Implement __bg_hash__(self) -> str on any dict subclass to skip this recursion.
|
|
520
|
+
if isinstance(value, dict) and hasattr(value, "__bg_hash__"):
|
|
521
|
+
result = value.__bg_hash__()
|
|
522
|
+
memo[id(value)] = result
|
|
523
|
+
return result
|
|
524
|
+
|
|
525
|
+
# Handle dictionaries
|
|
526
|
+
if isinstance(value, dict):
|
|
527
|
+
import time
|
|
528
|
+
memo[id(value)] = "dict:processing" # Add immediately to avoid recursion
|
|
529
|
+
items_str = "{"
|
|
530
|
+
for k, v in value.items():
|
|
531
|
+
k = str(k)
|
|
532
|
+
if not include_hidden and k.startswith('_'):
|
|
533
|
+
continue
|
|
534
|
+
if k in exclude:
|
|
535
|
+
continue
|
|
536
|
+
_t0 = time.perf_counter()
|
|
537
|
+
val_str = Background.simple_hash(value=v, exclude=exclude, memo=memo, depth=depth,
|
|
538
|
+
do_print=do_print,
|
|
539
|
+
include_hidden=include_hidden)
|
|
540
|
+
Background._record_dict_key_time(k, time.perf_counter() - _t0)
|
|
541
|
+
items_str += f"{val_str}"
|
|
542
|
+
items_str += "}"
|
|
543
|
+
memo[id(value)] = items_str
|
|
544
|
+
return items_str
|
|
545
|
+
|
|
546
|
+
if isinstance(value, set):
|
|
547
|
+
memo[id(value)] = "set:processing" # Add immediately to avoid recursion
|
|
548
|
+
items_str = "{"
|
|
549
|
+
for item in value:
|
|
550
|
+
items_str += str(item) + ","
|
|
551
|
+
items_str += "}"
|
|
552
|
+
memo[id(value)] = items_str
|
|
553
|
+
return items_str
|
|
554
|
+
|
|
555
|
+
# Handle simple types (int, float, str, bool)
|
|
556
|
+
if isinstance(value, (int, float, str, bool)):
|
|
557
|
+
result = str(value)
|
|
558
|
+
memo[id(value)] = result
|
|
559
|
+
return result
|
|
560
|
+
|
|
561
|
+
# Any other types - use their string representation
|
|
562
|
+
other_repr = f"{str(type(value).__name__)}" # Just use ID to prevent recursion
|
|
563
|
+
memo[id(value)] = other_repr
|
|
564
|
+
return other_repr
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Optional application services; the UI toolkit never imports their providers.
|
|
2
|
+
|
|
3
|
+
Providers register callbacks when imported. Lookups happen at call time so live
|
|
4
|
+
reloads replace callbacks without pinning an earlier function or module.
|
|
5
|
+
"""
|
|
6
|
+
_services = globals().get('_services', {})
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register(name, callback):
|
|
10
|
+
_services[name] = callback
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get(name):
|
|
14
|
+
return _services.get(name)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def call(name, *args, **kwargs):
|
|
18
|
+
callback = get(name)
|
|
19
|
+
return callback(*args, **kwargs) if callback is not None else None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def source_folders():
|
|
23
|
+
return call('source_folders') or ()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def framework_folders():
|
|
27
|
+
"""Folders of component packages whose call sites count as framework code."""
|
|
28
|
+
return call('framework_folders') or ()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def source_window(instance=0):
|
|
32
|
+
return call('source_window', instance)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def source_views():
|
|
36
|
+
return call('source_views') or {}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def open_source(path, line_number=None, **kwargs):
|
|
40
|
+
callback = get('source_open')
|
|
41
|
+
if callback is not None:
|
|
42
|
+
return callback(path, line_number=line_number, **kwargs)
|
|
43
|
+
from meltygui.editor.source_preview import open_source_preview
|
|
44
|
+
return open_source_preview(path, line_number, kwargs.get('token'))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def jump_to_symbol(obj, path):
|
|
48
|
+
import inspect
|
|
49
|
+
try:
|
|
50
|
+
line = inspect.getsourcelines(inspect.unwrap(obj))[1] if obj is not None else None
|
|
51
|
+
except (TypeError, OSError):
|
|
52
|
+
line = None
|
|
53
|
+
return open_source(path, line_number=line, token=getattr(obj, '__name__', None))
|