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,533 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Usage examples for the Melty converter system + auto-chaining.
|
|
3
|
+
|
|
4
|
+
This file includes a minimal registry and @converter decorator so
|
|
5
|
+
everything runs standalone. Replace with your real implementation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from types import SimpleNamespace, ModuleType
|
|
9
|
+
from datetime import datetime, timedelta
|
|
10
|
+
from decimal import Decimal
|
|
11
|
+
from fractions import Fraction
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from meltygui.core.melty import Melty
|
|
16
|
+
from meltygui.code.libcst_conversion import register
|
|
17
|
+
from meltygui.core.conversion.path_finder import convert
|
|
18
|
+
from meltygui.core.conversion.path_finder import explain_chain
|
|
19
|
+
from meltygui.core.conversion.path_finder import all_paths
|
|
20
|
+
from meltygui.core.conversion.path_finder import all_reachable_from
|
|
21
|
+
from meltygui.core.conversion.path_finder import T
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
25
|
+
# ║ Define a subset of converters (enough to show interesting chains) ║
|
|
26
|
+
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
27
|
+
|
|
28
|
+
@register
|
|
29
|
+
def int_to_str(value: int) -> str:
|
|
30
|
+
return str(value)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@register
|
|
34
|
+
def str_to_int(value: str) -> int:
|
|
35
|
+
return int(value.strip())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@register
|
|
39
|
+
def float_to_str(value: float) -> str:
|
|
40
|
+
return str(value)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@register
|
|
44
|
+
def str_to_float(value: str) -> float:
|
|
45
|
+
return float(value.strip().replace(",", ""))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@register
|
|
49
|
+
def int_to_float(value: int) -> float:
|
|
50
|
+
return float(value)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@register
|
|
54
|
+
def float_to_int(value: float) -> int:
|
|
55
|
+
return int(value)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@register
|
|
59
|
+
def float_to_decimal(value: float) -> Decimal:
|
|
60
|
+
return Decimal(str(value))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@register
|
|
64
|
+
def decimal_to_float(value: Decimal) -> float:
|
|
65
|
+
return float(value)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@register
|
|
69
|
+
def str_to_decimal(value: str) -> Decimal:
|
|
70
|
+
return Decimal(value.strip())
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@register
|
|
74
|
+
def decimal_to_str(value: Decimal) -> str:
|
|
75
|
+
return str(value)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@register
|
|
79
|
+
def float_to_fraction(value: float) -> Fraction:
|
|
80
|
+
return Fraction(value).limit_denominator()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@register
|
|
84
|
+
def fraction_to_float(value: Fraction) -> float:
|
|
85
|
+
return float(value)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@register
|
|
89
|
+
def str_to_bytes(value: str) -> bytes:
|
|
90
|
+
return value.encode("utf-8")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@register
|
|
94
|
+
def bytes_to_str(value: bytes) -> str:
|
|
95
|
+
return value.decode("utf-8")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@register
|
|
99
|
+
def int_to_datetime(value: int) -> datetime:
|
|
100
|
+
return datetime.fromtimestamp(value)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@register
|
|
104
|
+
def datetime_to_int(value: datetime) -> int:
|
|
105
|
+
return int(value.timestamp())
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@register
|
|
109
|
+
def datetime_to_str(value: datetime) -> str:
|
|
110
|
+
return value.isoformat()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@register
|
|
114
|
+
def str_to_datetime(value: str) -> datetime:
|
|
115
|
+
return datetime.fromisoformat(value.strip())
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@register
|
|
119
|
+
def int_to_timedelta(value: int) -> timedelta:
|
|
120
|
+
return timedelta(seconds=value)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@register
|
|
124
|
+
def timedelta_to_int(value: timedelta) -> int:
|
|
125
|
+
return int(value.total_seconds())
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@register
|
|
129
|
+
def dict_to_str(value: dict) -> str:
|
|
130
|
+
import json
|
|
131
|
+
return json.dumps(value, indent=2, default=str)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@register
|
|
135
|
+
def str_to_dict(value: str) -> dict:
|
|
136
|
+
import json
|
|
137
|
+
return json.loads(value)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@register
|
|
141
|
+
def list_to_dict(value: list) -> dict:
|
|
142
|
+
return dict(value)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@register
|
|
146
|
+
def dict_to_list(value: dict) -> list:
|
|
147
|
+
return [list(pair) for pair in value.items()]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@register
|
|
151
|
+
def dict_to_namespace(value: dict) -> SimpleNamespace:
|
|
152
|
+
return SimpleNamespace(**value)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@register
|
|
156
|
+
def namespace_to_dict(value: SimpleNamespace) -> dict:
|
|
157
|
+
return vars(value).copy()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@register
|
|
161
|
+
def list_to_tuple(value: list) -> tuple:
|
|
162
|
+
return tuple(value)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@register
|
|
166
|
+
def tuple_to_list(value: tuple) -> list:
|
|
167
|
+
return list(value)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@register
|
|
171
|
+
def list_to_set(value: list) -> set:
|
|
172
|
+
return set(value)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@register
|
|
176
|
+
def set_to_list(value: set) -> list:
|
|
177
|
+
return sorted(value, key=repr)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@register
|
|
181
|
+
def exception_to_dict(value: Exception) -> dict:
|
|
182
|
+
return {
|
|
183
|
+
"__type__": type(value).__name__,
|
|
184
|
+
"__str__": str(value),
|
|
185
|
+
"args": [repr(a) for a in value.args],
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
"""
|
|
190
|
+
Reversible object ↔ dict converters.
|
|
191
|
+
|
|
192
|
+
object_to_dict puts actual attributes as top-level dict keys so they
|
|
193
|
+
display naturally in a UI. Reconstruction metadata is tucked under
|
|
194
|
+
a single "__meta__" key that the UI filters out.
|
|
195
|
+
|
|
196
|
+
dict_to_object tries to instantiate the real class, falling back to
|
|
197
|
+
SimpleNamespace if the class can't be found or constructed.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
import sys
|
|
201
|
+
from types import SimpleNamespace
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _serialize_value(v):
|
|
205
|
+
"""Store the actual value when possible, fall back to a tagged repr."""
|
|
206
|
+
if v is None or isinstance(v, (str, int, float, bool)):
|
|
207
|
+
return v
|
|
208
|
+
if isinstance(v, (list, tuple, set, frozenset)):
|
|
209
|
+
converted = [_serialize_value(x) for x in v]
|
|
210
|
+
if isinstance(v, tuple):
|
|
211
|
+
return {"__type__": "tuple", "__items__": converted}
|
|
212
|
+
if isinstance(v, set):
|
|
213
|
+
return {"__type__": "set", "__items__": converted}
|
|
214
|
+
if isinstance(v, frozenset):
|
|
215
|
+
return {"__type__": "frozenset", "__items__": converted}
|
|
216
|
+
return converted
|
|
217
|
+
if isinstance(v, dict):
|
|
218
|
+
return {"__type__": "dict", "__items__": {str(k): _serialize_value(val) for k, val in v.items()}}
|
|
219
|
+
return {"__type__": "repr", "__repr__": repr(v), "__class__": type(v).__qualname__}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _deserialize_value(v):
|
|
223
|
+
"""Reverse of _serialize_value."""
|
|
224
|
+
if v is None or isinstance(v, (str, int, float, bool)):
|
|
225
|
+
return v
|
|
226
|
+
if isinstance(v, list):
|
|
227
|
+
return [_deserialize_value(x) for x in v]
|
|
228
|
+
if isinstance(v, dict):
|
|
229
|
+
tag = v.get("__type__")
|
|
230
|
+
if tag == "tuple":
|
|
231
|
+
return tuple(_deserialize_value(x) for x in v["__items__"])
|
|
232
|
+
if tag == "set":
|
|
233
|
+
return set(_deserialize_value(x) for x in v["__items__"])
|
|
234
|
+
if tag == "frozenset":
|
|
235
|
+
return frozenset(_deserialize_value(x) for x in v["__items__"])
|
|
236
|
+
if tag == "dict":
|
|
237
|
+
return {k: _deserialize_value(val) for k, val in v["__items__"].items()}
|
|
238
|
+
if tag == "repr":
|
|
239
|
+
try:
|
|
240
|
+
return eval(v["__repr__"]) # noqa: S307
|
|
241
|
+
except Exception:
|
|
242
|
+
return v["__repr__"]
|
|
243
|
+
return {k: _deserialize_value(val) for k, val in v.items()}
|
|
244
|
+
return v
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@register
|
|
248
|
+
def object_to_dict(value: object) -> dict:
|
|
249
|
+
"""Generic object → dict with attributes as top-level keys.
|
|
250
|
+
|
|
251
|
+
The dict looks like:
|
|
252
|
+
{
|
|
253
|
+
"color": "red",
|
|
254
|
+
"size": 42,
|
|
255
|
+
"items": [1, 2, 3],
|
|
256
|
+
"__meta__": {"__type__": "Widget", "__module__": "my_app.models"}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
The UI sees color, size, items. __meta__ is filtered out by
|
|
260
|
+
the dunder convention but carries everything needed for dict_to_object.
|
|
261
|
+
"""
|
|
262
|
+
cls = type(value)
|
|
263
|
+
result = {}
|
|
264
|
+
|
|
265
|
+
# Value types (int, float, str, bool, bytearray, bytes, etc.) don't
|
|
266
|
+
# store their payload in __dict__ or __slots__ - it lives in the
|
|
267
|
+
# C struct. Capture it explicitly so the round-trip preserves it.
|
|
268
|
+
_VALUE_TYPES = (int, float, str, bool, bytes, bytearray, complex)
|
|
269
|
+
if isinstance(value, _VALUE_TYPES):
|
|
270
|
+
result["value"] = value
|
|
271
|
+
|
|
272
|
+
# Hoist __dict__ attributes to top level
|
|
273
|
+
if hasattr(value, "__dict__"):
|
|
274
|
+
for k, v in value.__dict__.items():
|
|
275
|
+
if k.startswith("__") and k.endswith("__"):
|
|
276
|
+
continue
|
|
277
|
+
result[k] = _serialize_value(v)
|
|
278
|
+
|
|
279
|
+
# Hoist __slots__ attributes to top level
|
|
280
|
+
slots = set()
|
|
281
|
+
for klass in cls.__mro__:
|
|
282
|
+
slots.update(getattr(klass, "__slots__", ()))
|
|
283
|
+
for s in sorted(slots):
|
|
284
|
+
if s.startswith("__") and s.endswith("__"):
|
|
285
|
+
continue
|
|
286
|
+
try:
|
|
287
|
+
result[s] = _serialize_value(getattr(value, s))
|
|
288
|
+
except AttributeError:
|
|
289
|
+
pass
|
|
290
|
+
|
|
291
|
+
# Tuck reconstruction metadata under a single dunder key
|
|
292
|
+
result["__meta__"] = {
|
|
293
|
+
"__type__": cls.__qualname__,
|
|
294
|
+
"__module__": cls.__module__,
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return result
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@register
|
|
301
|
+
def dict_to_object(value: dict) -> object:
|
|
302
|
+
"""Reconstruct an object from an object_to_dict snapshot.
|
|
303
|
+
|
|
304
|
+
Resolution strategy:
|
|
305
|
+
1. Look up the real class from __meta__.__module__ + __meta__.__type__
|
|
306
|
+
2. Instantiate with __new__ (bypassing __init__) and set attributes
|
|
307
|
+
3. Fall back to SimpleNamespace if the class can't be found
|
|
308
|
+
"""
|
|
309
|
+
meta = value.get("__meta__", {})
|
|
310
|
+
type_name = meta.get("__type__", "Unknown")
|
|
311
|
+
module_name = meta.get("__module__", "")
|
|
312
|
+
|
|
313
|
+
# Everything except dunders is an attribute to restore
|
|
314
|
+
attrs = {k: _deserialize_value(v) for k, v in value.items()
|
|
315
|
+
if not (k.startswith("__") and k.endswith("__"))}
|
|
316
|
+
|
|
317
|
+
# Try to find and instantiate the real class
|
|
318
|
+
cls = _resolve_class(module_name, type_name)
|
|
319
|
+
if cls is not None:
|
|
320
|
+
try:
|
|
321
|
+
# Value types (int, float, str, etc.) need the value passed
|
|
322
|
+
# to the constructor - you can't instantiate them via __new__
|
|
323
|
+
if "value" in attrs and issubclass(cls, (int, float, str, bool, bytes, bytearray, complex)):
|
|
324
|
+
obj = cls(attrs.pop("value"))
|
|
325
|
+
# Subclasses of builtins might still have other attrs
|
|
326
|
+
for k, v in attrs.items():
|
|
327
|
+
setattr(obj, k, v)
|
|
328
|
+
return obj
|
|
329
|
+
|
|
330
|
+
obj = cls.__new__(cls)
|
|
331
|
+
for k, v in attrs.items():
|
|
332
|
+
setattr(obj, k, v)
|
|
333
|
+
return obj
|
|
334
|
+
except Exception:
|
|
335
|
+
pass
|
|
336
|
+
|
|
337
|
+
# Fallback
|
|
338
|
+
ns = SimpleNamespace(**attrs)
|
|
339
|
+
ns.__original_type__ = type_name
|
|
340
|
+
ns.__original_module__ = module_name
|
|
341
|
+
return ns
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _resolve_class(module_name: str, qualname: str):
|
|
345
|
+
"""Try to find a class by module + qualname, handling nested classes."""
|
|
346
|
+
mod = sys.modules.get(module_name)
|
|
347
|
+
if mod is None:
|
|
348
|
+
return None
|
|
349
|
+
obj = mod
|
|
350
|
+
for part in qualname.split("."):
|
|
351
|
+
obj = getattr(obj, part, None)
|
|
352
|
+
if obj is None:
|
|
353
|
+
return None
|
|
354
|
+
return obj if isinstance(obj, type) else None
|
|
355
|
+
|
|
356
|
+
#
|
|
357
|
+
# @register
|
|
358
|
+
# def float_to_dict(value: float) -> dict:
|
|
359
|
+
# return {
|
|
360
|
+
# "value": value,
|
|
361
|
+
# }
|
|
362
|
+
#
|
|
363
|
+
# @register
|
|
364
|
+
# def dict_to_float(value: dict) -> float:
|
|
365
|
+
# return float(value["value"])
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
369
|
+
# ║ Import the chaining system ║
|
|
370
|
+
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
371
|
+
|
|
372
|
+
# Assuming chaining.py is in the same directory or on the path.
|
|
373
|
+
# Adjust the import to match your setup.
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def section(title: str):
|
|
377
|
+
print(f"\n{'─' * 60}")
|
|
378
|
+
print(f" {title}")
|
|
379
|
+
print(f"{'─' * 60}")
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
# ╔══════════════════════════════════════════════════════════════════════════════╗
|
|
383
|
+
# ║ Examples ║
|
|
384
|
+
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
385
|
+
#
|
|
386
|
+
# if __name__ == "__main__":
|
|
387
|
+
#
|
|
388
|
+
# print(f"Registry: {Melty}")
|
|
389
|
+
#
|
|
390
|
+
# # ── 1. Direct conversions (single hop) ───────────────────────────
|
|
391
|
+
#
|
|
392
|
+
# section("Direct conversions")
|
|
393
|
+
#
|
|
394
|
+
# print(f" int → str: {convert(42, str, registry=Melty)!r}")
|
|
395
|
+
# print(f" str → float: {convert('3.14', float, registry=Melty)!r}")
|
|
396
|
+
# print(f" float → Decimal: {convert(0.1, Decimal, registry=Melty)!r}")
|
|
397
|
+
# print(f" int → datetime: {convert(0, datetime, registry=Melty)!r}")
|
|
398
|
+
#
|
|
399
|
+
# # ── 2. Auto-chained conversions (multi-hop) ─────────────────────
|
|
400
|
+
#
|
|
401
|
+
# section("Auto-chained conversions")
|
|
402
|
+
#
|
|
403
|
+
# # int → float → Decimal (no direct int→Decimal registered)
|
|
404
|
+
# result = convert(42, Decimal, registry=Melty)
|
|
405
|
+
# print(f" int → Decimal: {result!r} (type: {type(result).__name__})")
|
|
406
|
+
# print(f" chain: {explain_chain(int, Decimal, registry=Melty)}")
|
|
407
|
+
#
|
|
408
|
+
# # int → float → Fraction
|
|
409
|
+
# result = convert(3, Fraction, registry=Melty)
|
|
410
|
+
# print(f" int → Fraction: {result!r}")
|
|
411
|
+
# print(f" chain: {explain_chain(int, Fraction, registry=Melty)}")
|
|
412
|
+
#
|
|
413
|
+
# # int → str → bytes
|
|
414
|
+
# result = convert(12345, bytes, registry=Melty)
|
|
415
|
+
# print(f" int → bytes: {result!r}")
|
|
416
|
+
# print(f" chain: {explain_chain(int, bytes, registry=Melty)}")
|
|
417
|
+
#
|
|
418
|
+
# # int → str → dict (via JSON - "42" isn't valid JSON, but timestamps are fun)
|
|
419
|
+
# # Let's do datetime → str → dict instead
|
|
420
|
+
# # Actually: int → datetime → str
|
|
421
|
+
# result = convert(1700000000, str, registry=Melty)
|
|
422
|
+
# print(f" int → str: {result!r} (direct)")
|
|
423
|
+
#
|
|
424
|
+
# # ── 3. Longer chains ─────────────────────────────────────────────
|
|
425
|
+
#
|
|
426
|
+
# section("Longer chains")
|
|
427
|
+
#
|
|
428
|
+
# # dict → str → bytes
|
|
429
|
+
# data = {"name": "Alice", "balance": 100}
|
|
430
|
+
# result = convert(data, bytes, registry=Melty)
|
|
431
|
+
# print(f" dict → bytes: {result[:50]!r}...")
|
|
432
|
+
# print(f" chain: {explain_chain(dict, bytes, registry=Melty)}")
|
|
433
|
+
#
|
|
434
|
+
# # dict → SimpleNamespace (direct)
|
|
435
|
+
# ns = convert({"x": 1, "y": 2}, SimpleNamespace, registry=Melty)
|
|
436
|
+
# print(f" dict → NS: {ns!r}")
|
|
437
|
+
#
|
|
438
|
+
# # dict → list → tuple
|
|
439
|
+
# result = convert({"a": 1, "b": 2}, tuple, registry=Melty)
|
|
440
|
+
# print(f" dict → tuple: {result!r}")
|
|
441
|
+
# print(f" chain: {explain_chain(dict, tuple, registry=Melty)}")
|
|
442
|
+
#
|
|
443
|
+
# # tuple → list → set
|
|
444
|
+
# result = convert((3, 1, 4, 1, 5, 9), set, registry=Melty)
|
|
445
|
+
# print(f" tuple → set: {result!r}")
|
|
446
|
+
# print(f" chain: {explain_chain(tuple, set, registry=Melty)}")
|
|
447
|
+
#
|
|
448
|
+
# # ── 4. Subclass resolution ───────────────────────────────────────
|
|
449
|
+
#
|
|
450
|
+
# section("Subclass resolution")
|
|
451
|
+
#
|
|
452
|
+
# # ValueError is a subclass of Exception - should hit Exception→dict
|
|
453
|
+
# err = ValueError("something broke")
|
|
454
|
+
# result = convert(err, dict, registry=Melty)
|
|
455
|
+
# print(f" ValueError → dict: {result}")
|
|
456
|
+
# print(f" chain: {explain_chain(ValueError, dict, registry=Melty)}")
|
|
457
|
+
#
|
|
458
|
+
# # And longer chain down: ValueError → dict → str
|
|
459
|
+
# result = convert(err, str, registry=Melty)
|
|
460
|
+
# print(f" ValueError → str: {result[:60]!r}...")
|
|
461
|
+
# print(f" chain: {explain_chain(ValueError, str, registry=Melty)}")
|
|
462
|
+
#
|
|
463
|
+
#
|
|
464
|
+
# # A custom type hits object→dict via MRO
|
|
465
|
+
# class Widget:
|
|
466
|
+
# def __init__(self, color, size):
|
|
467
|
+
# self.color = color
|
|
468
|
+
# self.size = size
|
|
469
|
+
#
|
|
470
|
+
#
|
|
471
|
+
# w = Widget("red", 42)
|
|
472
|
+
# result = convert(w, dict, registry=Melty)
|
|
473
|
+
# print(f" Widget → dict: {result}")
|
|
474
|
+
# print(f" chain: {explain_chain(Widget, dict, registry=Melty)}")
|
|
475
|
+
#
|
|
476
|
+
# # Widget → dict → str
|
|
477
|
+
# result = convert(w, str, registry=Melty)
|
|
478
|
+
# print(f" Widget → str: {result[:60]!r}...")
|
|
479
|
+
#
|
|
480
|
+
# # Widget → dict → SimpleNamespace
|
|
481
|
+
# result = convert(w, SimpleNamespace, registry=Melty)
|
|
482
|
+
# print(f" Widget → NS: {result!r}")
|
|
483
|
+
# print(f" chain: {explain_chain(Widget, SimpleNamespace, registry=Melty)}")
|
|
484
|
+
#
|
|
485
|
+
# # ── 5. Round-trip demonstration ──────────────────────────────────
|
|
486
|
+
#
|
|
487
|
+
# section("Round-trips (showing lossy-ness)")
|
|
488
|
+
#
|
|
489
|
+
# original = 3.14159
|
|
490
|
+
# as_fraction = convert(original, Fraction, registry=Melty)
|
|
491
|
+
# back = convert(as_fraction, float, registry=Melty)
|
|
492
|
+
# print(f" float → Fraction → float: {original} → {as_fraction} → {back}")
|
|
493
|
+
# print(f" lost precision: {original != back}")
|
|
494
|
+
#
|
|
495
|
+
# original_dt = datetime(2024, 6, 15, 12, 30, 45)
|
|
496
|
+
# as_int = convert(original_dt, int, registry=Melty)
|
|
497
|
+
# back_dt = convert(as_int, datetime, registry=Melty)
|
|
498
|
+
# print(f" datetime → int → datetime: {original_dt} → {as_int} → {back_dt}")
|
|
499
|
+
# print(f" lost subsecond: {original_dt != back_dt}")
|
|
500
|
+
#
|
|
501
|
+
# # ── 6. Introspection tools ───────────────────────────────────────
|
|
502
|
+
#
|
|
503
|
+
# section("Introspection: all_reachable_from(int)")
|
|
504
|
+
#
|
|
505
|
+
# reachable = all_reachable_from(int, registry=Melty)
|
|
506
|
+
# print(f" int can reach {len(reachable)} types:")
|
|
507
|
+
# for t in sorted(reachable, key=lambda t: t.__name__):
|
|
508
|
+
# print(f" → {t.__name__}")
|
|
509
|
+
#
|
|
510
|
+
# section("Introspection: all_paths(int, bytes)")
|
|
511
|
+
#
|
|
512
|
+
# paths = all_paths(int, bytes, registry=Melty)
|
|
513
|
+
# for i, p in enumerate(paths):
|
|
514
|
+
# names = " → ".join(t.__name__ for t in p)
|
|
515
|
+
# print(f" path {i + 1} ({len(p) - 1} hops): {names}")
|
|
516
|
+
#
|
|
517
|
+
# section("Introspection: all_paths(tuple, set)")
|
|
518
|
+
#
|
|
519
|
+
# paths = all_paths(tuple, set, registry=Melty)
|
|
520
|
+
# for i, p in enumerate(paths):
|
|
521
|
+
# names = " → ".join(t.__name__ for t in p)
|
|
522
|
+
# print(f" path {i + 1} ({len(p) - 1} hops): {names}")
|
|
523
|
+
#
|
|
524
|
+
# # ── 7. Error case ────────────────────────────────────────────────
|
|
525
|
+
#
|
|
526
|
+
# section("Error: no path exists")
|
|
527
|
+
#
|
|
528
|
+
# try:
|
|
529
|
+
# convert(42, ModuleType, registry=Melty)
|
|
530
|
+
# except TypeError as e:
|
|
531
|
+
# print(f" {e}")
|
|
532
|
+
#
|
|
533
|
+
# print()
|