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.
Files changed (372) hide show
  1. meltygui/__init__.py +107 -0
  2. meltygui/accounts/__init__.py +0 -0
  3. meltygui/accounts/internet_accounts.py +1355 -0
  4. meltygui/chat/__init__.py +91 -0
  5. meltygui/chat/activity.py +75 -0
  6. meltygui/chat/backends.py +36 -0
  7. meltygui/chat/chat_interface.py +732 -0
  8. meltygui/chat/chat_proxy.py +352 -0
  9. meltygui/chat/codex_proxy.py +592 -0
  10. meltygui/chat/codex_settings.py +100 -0
  11. meltygui/chat/codex_transport.py +60 -0
  12. meltygui/chat/command_parser.py +204 -0
  13. meltygui/chat/images.py +227 -0
  14. meltygui/chat/messages.py +417 -0
  15. meltygui/chat/metadata.py +139 -0
  16. meltygui/chat/writer_locks.py +64 -0
  17. meltygui/code/__init__.py +0 -0
  18. meltygui/code/basic_converters.py +533 -0
  19. meltygui/code/chain_converters.py +2111 -0
  20. meltygui/code/code_checks.py +2209 -0
  21. meltygui/code/core_syntax.py +1430 -0
  22. meltygui/code/file_converters.py +1933 -0
  23. meltygui/code/fileref.py +702 -0
  24. meltygui/code/hotswap_guard.py +144 -0
  25. meltygui/code/libcst_conversion.py +9724 -0
  26. meltygui/code/live_instrument.py +392 -0
  27. meltygui/code/live_view.py +2490 -0
  28. meltygui/code/melty_scan.py +2684 -0
  29. meltygui/code/new_codecs.py +1255 -0
  30. meltygui/code/new_converters.py +3017 -0
  31. meltygui/code/project_code.py +278 -0
  32. meltygui/code/source_context.py +63 -0
  33. meltygui/code/symbol_roster.py +1588 -0
  34. meltygui/code/syntax_check.py +34 -0
  35. meltygui/code/syntax_check_worker.py +114 -0
  36. meltygui/completion/__init__.py +0 -0
  37. meltygui/completion/fim.py +1232 -0
  38. meltygui/completion/fim_context.py +481 -0
  39. meltygui/completion/providers/__init__.py +0 -0
  40. meltygui/completion/providers/anthropic_oauth.py +446 -0
  41. meltygui/completion/providers/anthropic_requests.py +69 -0
  42. meltygui/completion/providers/claude.py +203 -0
  43. meltygui/completion/providers/claude_usage.py +499 -0
  44. meltygui/completion/providers/codex_accounts.py +180 -0
  45. meltygui/completion/providers/copilot.py +617 -0
  46. meltygui/completion/providers/oauth_popup.py +220 -0
  47. meltygui/completion/providers/ollama.py +320 -0
  48. meltygui/completion/providers/profiles.py +23 -0
  49. meltygui/core/README.md +88 -0
  50. meltygui/core/__init__.py +1 -0
  51. meltygui/core/automation/__init__.py +1 -0
  52. meltygui/core/automation/action_core.py +153 -0
  53. meltygui/core/automation/collection_action.py +45 -0
  54. meltygui/core/automation/mcp_eval.py +167 -0
  55. meltygui/core/automation/mcp_hotswap.py +107 -0
  56. meltygui/core/automation/mcp_query.py +552 -0
  57. meltygui/core/automation/mcp_server.py +657 -0
  58. meltygui/core/automation/orchestration_core.py +2636 -0
  59. meltygui/core/automation/query_core.py +124 -0
  60. meltygui/core/automation/search_core.py +26 -0
  61. meltygui/core/automation/selector_core.py +340 -0
  62. meltygui/core/automation/value_core.py +1752 -0
  63. meltygui/core/cache/__init__.py +1 -0
  64. meltygui/core/cache/cache_diagnostics.py +0 -0
  65. meltygui/core/cache/invalidation_decoration.py +153 -0
  66. meltygui/core/cache/invalidation_tracker.py +43 -0
  67. meltygui/core/cache/tile_cache.py +5968 -0
  68. meltygui/core/conversion/__init__.py +1 -0
  69. meltygui/core/conversion/bubbling.py +599 -0
  70. meltygui/core/conversion/cache_tree.py +188 -0
  71. meltygui/core/conversion/chain.py +113 -0
  72. meltygui/core/conversion/converter_register.py +145 -0
  73. meltygui/core/conversion/data_decoration.py +67 -0
  74. meltygui/core/conversion/dict_conversion.py +1815 -0
  75. meltygui/core/conversion/dict_conversion_util.py +177 -0
  76. meltygui/core/conversion/dynamic_obj.py +89 -0
  77. meltygui/core/conversion/graph_compare.py +183 -0
  78. meltygui/core/conversion/load_save_v2.py +1032 -0
  79. meltygui/core/conversion/missing_saved_class.py +39 -0
  80. meltygui/core/conversion/path_finder.py +606 -0
  81. meltygui/core/conversion/render_host.py +1083 -0
  82. meltygui/core/core_render.py +6537 -0
  83. meltygui/core/definition_hotswap.py +231 -0
  84. meltygui/core/diagnostics/__init__.py +1 -0
  85. meltygui/core/diagnostics/attribute_churn.py +38 -0
  86. meltygui/core/diagnostics/fps_counter.py +39 -0
  87. meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
  88. meltygui/core/diagnostics/inspection_core.py +169 -0
  89. meltygui/core/diagnostics/monitor_core.py +105 -0
  90. meltygui/core/diagnostics/notifications.py +706 -0
  91. meltygui/core/diagnostics/perf_trace.py +281 -0
  92. meltygui/core/diagnostics/profile_decoration.py +88 -0
  93. meltygui/core/diagnostics/resize_trace.py +62 -0
  94. meltygui/core/diagnostics/screenshot_core.py +247 -0
  95. meltygui/core/diagnostics/session_status.py +98 -0
  96. meltygui/core/diagnostics/trace_core.py +445 -0
  97. meltygui/core/files/__init__.py +1 -0
  98. meltygui/core/files/file_core.py +208 -0
  99. meltygui/core/files/file_explorer_core.py +104 -0
  100. meltygui/core/files/file_tree_core.py +198 -0
  101. meltygui/core/files/file_watch_core.py +43 -0
  102. meltygui/core/files/import_graph_core.py +43 -0
  103. meltygui/core/files/metadata_core.py +51 -0
  104. meltygui/core/graphics/__init__.py +1 -0
  105. meltygui/core/graphics/cuda_context_core.py +166 -0
  106. meltygui/core/graphics/cuda_interop_core.py +136 -0
  107. meltygui/core/graphics/cuda_kernel_core.py +91 -0
  108. meltygui/core/graphics/framebuffer_recorder.py +337 -0
  109. meltygui/core/graphics/gl_state.py +658 -0
  110. meltygui/core/graphics/lut_core.py +52 -0
  111. meltygui/core/graphics/overlay_renderer.py +984 -0
  112. meltygui/core/graphics/scene_target.py +180 -0
  113. meltygui/core/graphics/screenshot.py +439 -0
  114. meltygui/core/graphics/shader_func.py +478 -0
  115. meltygui/core/graphics/tensor_core.py +45 -0
  116. meltygui/core/graphics/text_texture.py +329 -0
  117. meltygui/core/graphics/wayland_color.py +635 -0
  118. meltygui/core/input/__init__.py +1 -0
  119. meltygui/core/input/collision.py +165 -0
  120. meltygui/core/input/drag_drop_core.py +1525 -0
  121. meltygui/core/input/hypr_left_drag.py +323 -0
  122. meltygui/core/input/input_core.py +245 -0
  123. meltygui/core/input/input_handler.py +1101 -0
  124. meltygui/core/input/mouse_cursor.py +355 -0
  125. meltygui/core/input/pynput_backend.py +1054 -0
  126. meltygui/core/input/space_mouse.py +338 -0
  127. meltygui/core/input/touchpad_backend.py +393 -0
  128. meltygui/core/input/view_selection.py +177 -0
  129. meltygui/core/layout/__init__.py +1 -0
  130. meltygui/core/layout/column_core.py +2153 -0
  131. meltygui/core/layout/cursor_core.py +161 -0
  132. meltygui/core/layout/dropdown_core.py +278 -0
  133. meltygui/core/layout/edge_constraints.py +155 -0
  134. meltygui/core/layout/grid_core.py +117 -0
  135. meltygui/core/layout/header_core.py +23 -0
  136. meltygui/core/layout/header_runtime.py +67 -0
  137. meltygui/core/layout/layout_core.py +87 -0
  138. meltygui/core/layout/tile_manager_core.py +591 -0
  139. meltygui/core/melty.py +6726 -0
  140. meltygui/core/module_map.json +896 -0
  141. meltygui/core/module_names.py +19 -0
  142. meltygui/core/rendering/__init__.py +1 -0
  143. meltygui/core/rendering/core_decoration.py +431 -0
  144. meltygui/core/rendering/core_render_helpers.py +328 -0
  145. meltygui/core/rendering/func_metadata.py +398 -0
  146. meltygui/core/rendering/mode.py +818 -0
  147. meltygui/core/rendering/mode_defaults.py +41 -0
  148. meltygui/core/rendering/modes.py +136 -0
  149. meltygui/core/rendering/parameter_core.py +1665 -0
  150. meltygui/core/rendering/render_dispatch.py +1891 -0
  151. meltygui/core/rendering/render_funcs.py +273 -0
  152. meltygui/core/rendering/shaped.py +312 -0
  153. meltygui/core/rendering/window_decoration.py +25 -0
  154. meltygui/core/runtime/__init__.py +1 -0
  155. meltygui/core/runtime/app.py +735 -0
  156. meltygui/core/runtime/app_session.py +140 -0
  157. meltygui/core/runtime/background.py +564 -0
  158. meltygui/core/runtime/extensions.py +53 -0
  159. meltygui/core/runtime/gc_manager.py +1163 -0
  160. meltygui/core/runtime/lifecycle.py +21 -0
  161. meltygui/core/runtime/paths.py +27 -0
  162. meltygui/core/runtime/settings.py +14 -0
  163. meltygui/core/runtime/singleton.py +16 -0
  164. meltygui/core/runtime/thread_safe_bool.py +24 -0
  165. meltygui/core/runtime/thread_signal.py +30 -0
  166. meltygui/core/runtime/toggles.py +3115 -0
  167. meltygui/core/services/__init__.py +1 -0
  168. meltygui/core/services/account_core.py +10 -0
  169. meltygui/core/services/chat_core.py +19 -0
  170. meltygui/core/services/claude_terminal_core.py +346 -0
  171. meltygui/core/services/terminal_core.py +458 -0
  172. meltygui/core/services/terminal_runtime.py +78 -0
  173. meltygui/core/styling/__init__.py +1 -0
  174. meltygui/core/styling/color_core.py +46 -0
  175. meltygui/core/styling/fonts.py +639 -0
  176. meltygui/core/styling/global_style.py +338 -0
  177. meltygui/core/styling/style.py +198 -0
  178. meltygui/core/styling/style_core.py +522 -0
  179. meltygui/core/styling/warm_start.py +149 -0
  180. meltygui/core/windowing/__init__.py +1 -0
  181. meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
  182. meltygui/core/windowing/backends/__init__.py +1 -0
  183. meltygui/core/windowing/backends/imgui_renderer.py +138 -0
  184. meltygui/core/windowing/backends/native_wayland.py +850 -0
  185. meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
  186. meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
  187. meltygui/core/windowing/backends/wayland_protocol.py +101 -0
  188. meltygui/core/windowing/dock_core.py +182 -0
  189. meltygui/core/windowing/frame_geometry.py +42 -0
  190. meltygui/core/windowing/geometry_feed.py +864 -0
  191. meltygui/core/windowing/glfw_utils.py +1343 -0
  192. meltygui/core/windowing/os_frame.py +1552 -0
  193. meltygui/core/windowing/surface.py +643 -0
  194. meltygui/core/windowing/titlebar.py +1560 -0
  195. meltygui/core/windowing/titlebar_buttons.py +281 -0
  196. meltygui/core/windowing/wayland_move.py +932 -0
  197. meltygui/core/windowing/window_api.py +62 -0
  198. meltygui/core/windowing/window_constants.py +339 -0
  199. meltygui/core/windowing/window_visibility.py +162 -0
  200. meltygui/debug/__init__.py +0 -0
  201. meltygui/debug/app_view_utils.py +9 -0
  202. meltygui/editor/__init__.py +0 -0
  203. meltygui/editor/bash_syntax.py +30 -0
  204. meltygui/editor/code_line_fast.py +152 -0
  205. meltygui/editor/diff.py +139 -0
  206. meltygui/editor/external_changes.py +159 -0
  207. meltygui/editor/file_header.py +41 -0
  208. meltygui/editor/live_usage.py +169 -0
  209. meltygui/editor/live_view_views.py +1803 -0
  210. meltygui/editor/pending_save.py +1351 -0
  211. meltygui/editor/roster_tints.py +547 -0
  212. meltygui/editor/source_preview.py +16 -0
  213. meltygui/editor/source_tools.py +10 -0
  214. meltygui/editor/source_ui.py +70 -0
  215. meltygui/editor/spell_check.py +77 -0
  216. meltygui/editor/text_editor.py +9076 -0
  217. meltygui/editor/usage_picker.py +538 -0
  218. meltygui/events/__init__.py +0 -0
  219. meltygui/events/example.py +118 -0
  220. meltygui/examples/__init__.py +0 -0
  221. meltygui/examples/columns_demo.py +33 -0
  222. meltygui/examples/columns_window_demo.py +60 -0
  223. meltygui/examples/context_menu_demo.py +24 -0
  224. meltygui/examples/context_menu_window_demo.py +49 -0
  225. meltygui/examples/gui_playground.py +127 -0
  226. meltygui/examples/live_view_playground.py +256 -0
  227. meltygui/examples/lora.py +43 -0
  228. meltygui/examples/lora_data.py +59 -0
  229. meltygui/examples/lora_policies.py +67 -0
  230. meltygui/examples/mode_demo.py +49 -0
  231. meltygui/examples/modifies_demo.py +119 -0
  232. meltygui/examples/scalar_policies.py +60 -0
  233. meltygui/examples/style_layouts.py +149 -0
  234. meltygui/examples/tile_manager_demo.py +74 -0
  235. meltygui/examples/tint_demo.py +160 -0
  236. meltygui/examples/tint_functions.py +56 -0
  237. meltygui/examples/trace_demo.py +88 -0
  238. meltygui/examples/two_windows.py +36 -0
  239. meltygui/files/__init__.py +0 -0
  240. meltygui/files/fast_file_explorer.py +439 -0
  241. meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
  242. meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
  243. meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
  244. meltygui/graphics/__init__.py +6 -0
  245. meltygui/graphics/base.py +85 -0
  246. meltygui/graphics/examples.py +507 -0
  247. meltygui/graphics/executor.py +520 -0
  248. meltygui/graphics/filter.py +667 -0
  249. meltygui/graphics/filter.pyi +856 -0
  250. meltygui/graphics/generate_stubs.py +22 -0
  251. meltygui/graphics/registry.py +203 -0
  252. meltygui/graphics/shader_compiler.py +155 -0
  253. meltygui/graphics/shaders.py +1302 -0
  254. meltygui/graphics/stub_generator.py +250 -0
  255. meltygui/graphics/texture_manager.py +170 -0
  256. meltygui/graphics/texture_min_max.py +295 -0
  257. meltygui/hdr_color.py +757 -0
  258. meltygui/image_load.py +308 -0
  259. meltygui/model/__init__.py +1 -0
  260. meltygui/model/account_model.py +142 -0
  261. meltygui/model/camera_model.py +159 -0
  262. meltygui/model/chat_model.py +86 -0
  263. meltygui/model/code_model.py +62 -0
  264. meltygui/model/code_proxy_model.py +876 -0
  265. meltygui/model/collection_model.py +41 -0
  266. meltygui/model/color_model.py +127 -0
  267. meltygui/model/cuda_tensor_model.py +36 -0
  268. meltygui/model/cuda_texture_model.py +149 -0
  269. meltygui/model/dropdown_model.py +137 -0
  270. meltygui/model/file_metadata_model.py +73 -0
  271. meltygui/model/file_model.py +289 -0
  272. meltygui/model/format_model.py +390 -0
  273. meltygui/model/graph_model.py +98 -0
  274. meltygui/model/icon_model.py +1024 -0
  275. meltygui/model/import_graph_model.py +468 -0
  276. meltygui/model/layout_model.py +15 -0
  277. meltygui/model/lut_model.py +266 -0
  278. meltygui/model/search_model.py +173 -0
  279. meltygui/model/tensor_model.py +402 -0
  280. meltygui/model/terminal_model.py +329 -0
  281. meltygui/model/texture_model.py +146 -0
  282. meltygui/model/tile_model.py +24 -0
  283. meltygui/model/trace_model.py +198 -0
  284. meltygui/model/trace_report_model.py +146 -0
  285. meltygui/models/__init__.py +0 -0
  286. meltygui/models/file_meta.py +613 -0
  287. meltygui/models/function_console.py +184 -0
  288. meltygui/models/orchestration.py +48 -0
  289. meltygui/pbr.py +1576 -0
  290. meltygui/png_unfilter.c +49 -0
  291. meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
  292. meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
  293. meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
  294. meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
  295. meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
  296. meltygui/resources/dejavu/LICENSE.txt +78 -0
  297. meltygui/resources/fontawesome-LICENSE.txt +121 -0
  298. meltygui/resources/fontawesome-webfont.ttf +0 -0
  299. meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
  300. meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
  301. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
  302. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
  303. meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
  304. meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
  305. meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
  306. meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
  307. meltygui/resources/jetbrains-weights/OFL.txt +93 -0
  308. meltygui/resources/jetbrains-weights/README.md +2 -0
  309. meltygui/state/__init__.py +0 -0
  310. meltygui/state/account_state.py +22 -0
  311. meltygui/state/animation_state.py +97 -0
  312. meltygui/state/annotation_state.py +22 -0
  313. meltygui/state/chat_state.py +36 -0
  314. meltygui/state/code_state.py +10 -0
  315. meltygui/state/core_enums.py +59 -0
  316. meltygui/state/core_markers.py +115 -0
  317. meltygui/state/core_undo.py +1075 -0
  318. meltygui/state/file_state.py +75 -0
  319. meltygui/state/graph_state.py +26 -0
  320. meltygui/state/inspection_state.py +47 -0
  321. meltygui/state/menu_state.py +10 -0
  322. meltygui/state/model_enums.py +11 -0
  323. meltygui/state/new_core_model.py +2394 -0
  324. meltygui/state/orchestration_state.py +14 -0
  325. meltygui/state/query_state.py +24 -0
  326. meltygui/state/tensor_state.py +10 -0
  327. meltygui/state/terminal_state.py +23 -0
  328. meltygui/state/trace_state.py +32 -0
  329. meltygui/state/voxel_state.py +12 -0
  330. meltygui/text_index.py +816 -0
  331. meltygui/utils/__init__.py +0 -0
  332. meltygui/utils/jump_to_code.py +344 -0
  333. meltygui/utils/pkl_inspect.py +90 -0
  334. meltygui/utils/render_utils.py +1419 -0
  335. meltygui/view/__init__.py +1 -0
  336. meltygui/view/account_view.py +524 -0
  337. meltygui/view/action_view.py +73 -0
  338. meltygui/view/chat_decoration_view.py +112 -0
  339. meltygui/view/chat_view.py +1703 -0
  340. meltygui/view/code_view.py +2677 -0
  341. meltygui/view/collection_view.py +1185 -0
  342. meltygui/view/color_view.py +824 -0
  343. meltygui/view/control_view.py +559 -0
  344. meltygui/view/decoration_view.py +439 -0
  345. meltygui/view/diagnostic_view.py +177 -0
  346. meltygui/view/dropdown_view.py +1082 -0
  347. meltygui/view/file_view.py +1599 -0
  348. meltygui/view/graph_cuda_view.py +105 -0
  349. meltygui/view/graph_view.py +882 -0
  350. meltygui/view/header_view.py +848 -0
  351. meltygui/view/input_view.py +238 -0
  352. meltygui/view/inspection_view.py +1783 -0
  353. meltygui/view/layout_view.py +596 -0
  354. meltygui/view/lut_view.py +45 -0
  355. meltygui/view/menu_view.py +212 -0
  356. meltygui/view/orchestration_view.py +658 -0
  357. meltygui/view/query_view.py +118 -0
  358. meltygui/view/search_view.py +342 -0
  359. meltygui/view/tab_view.py +258 -0
  360. meltygui/view/tensor_view.py +430 -0
  361. meltygui/view/terminal_view.py +355 -0
  362. meltygui/view/text_view.py +8031 -0
  363. meltygui/view/texture_view.py +480 -0
  364. meltygui/view/tile_view.py +38 -0
  365. meltygui/view/trace_view.py +923 -0
  366. meltygui/view/voxel_cuda_view.py +824 -0
  367. meltygui/view/voxel_view.py +1860 -0
  368. meltygui/view/window_view.py +111 -0
  369. meltygui-0.1.0.dist-info/METADATA +143 -0
  370. meltygui-0.1.0.dist-info/RECORD +372 -0
  371. meltygui-0.1.0.dist-info/WHEEL +4 -0
  372. 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."""