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