joouha / euporie

Jupyter notebooks in the terminal
https://euporie.readthedocs.io
MIT License
1.54k stars 36 forks source link

euproie makes ptpython not runnable due to an AssertionError #110

Closed wookayin closed 1 month ago

wookayin commented 1 month ago

Problem: When running ptpython, an AssertionError is thrown from the euporie side (which patches some part of ipython or prompt_toolkit), making ptpython completely unusable.

python -m ptpython.entry_points.run_ptipython

Error:

AssertionError on euporie/core/layout/containers.py:311,
in Window.write_to_screen(self, screen, mouse_handlers, write_position, parent_style, erase_bg, z_index):
--> 311     assert isinstance(write_position, BoundedWritePosition)
Detailed Stacktrace: ``` --------------------------------------------------------------------------- AssertionError Traceback (most recent call last) File :198, in _run_module_as_main(mod_name, alter_argv) File :88, in _run_code(code, run_globals, init_globals, mod_name, mod_spec, pkg_name, script_name) File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/ptpython/entry_points/run_ptipython.py:82 72 embed( 73 vi_mode=a.vi, 74 history_filename=history_file, (...) 77 title="IPython REPL (ptipython)", 78 ) 81 if __name__ == "__main__": ---> 82 run() File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/ptpython/entry_points/run_ptipython.py:72, in run(user_ns) 69 run_config(repl, config_file) 71 # Run interactive shell. ---> 72 embed( 73 vi_mode=a.vi, 74 history_filename=history_file, 75 configure=configure, 76 user_ns=user_ns, 77 title="IPython REPL (ptipython)", 78 ) File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/ptpython/ipython.py:323, in embed(**kwargs) 321 run_exec_lines(shell, config["InteractiveShellApp"]["exec_lines"]) 322 run_startup_scripts(shell) --> 323 shell(header=header, stack_depth=2, compile_flags=compile_flags) File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/IPython/terminal/embed.py:251, in InteractiveShellEmbed.__call__(self, header, local_ns, module, dummy, stack_depth, compile_flags, **kw) 247 self.show_banner() 249 # Call the embedding code with a stack depth of 1 so it can skip over 250 # our call and get the original caller's namespaces. --> 251 self.mainloop( 252 local_ns, module, stack_depth=stack_depth, compile_flags=compile_flags 253 ) 255 self.banner2 = self.old_banner2 257 if self.exit_msg is not None: File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/IPython/terminal/embed.py:343, in InteractiveShellEmbed.mainloop(self, local_ns, module, stack_depth, compile_flags) 340 self.set_completer_frame() 342 with self.builtin_trap, self.display_trap: --> 343 self.interact() 345 # now, purge out the local namespace of IPython's hidden variables. 346 if local_ns is not None: File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/IPython/terminal/interactiveshell.py:896, in TerminalInteractiveShell.interact(self) 893 print(self.separate_in, end='') 895 try: --> 896 code = self.prompt_for_code() 897 except EOFError: 898 if (not self.confirm_exit) \ 899 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'): File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/ptpython/ipython.py:262, in InteractiveShellEmbed.prompt_for_code(self) 260 def prompt_for_code(self) -> str: 261 try: --> 262 return self.python_input.app.run() 263 except KeyboardInterrupt: 264 self.python_input.default_buffer.document = Document() File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/application/application.py:1002, in Application.run(self, pre_run, set_exception_handler, handle_sigint, in_thread, inputhook) 998 return loop.run_until_complete(coro) 1000 else: 1001 # No loop installed. Run like usual. -> 1002 return asyncio.run(coro) File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/asyncio/runners.py:190, in run(main, debug) 186 raise RuntimeError( 187 "asyncio.run() cannot be called from a running event loop") 189 with Runner(debug=debug) as runner: --> 190 return runner.run(main) File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/asyncio/runners.py:118, in Runner.run(self, coro, context) 116 self._interrupt_count = 0 117 try: --> 118 return self._loop.run_until_complete(task) 119 except exceptions.CancelledError: 120 if self._interrupt_count > 0: File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/asyncio/base_events.py:654, in BaseEventLoop.run_until_complete(self, future) 651 if not future.done(): 652 raise RuntimeError('Event loop stopped before Future completed.') --> 654 return future.result() File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/application/application.py:886, in Application.run_async(self, pre_run, set_exception_handler, handle_sigint, slow_callback_duration) 883 f = stack.enter_context(create_future(loop)) 885 try: --> 886 return await _run_async(f) 887 finally: 888 # Wait for the background tasks to be done. This needs to 889 # go in the finally! If `_run_async` raises 890 # `KeyboardInterrupt`, we still want to wait for the 891 # background tasks. 892 await self.cancel_and_wait_for_background_tasks() File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/application/application.py:739, in Application.run_async.._run_async(f) 734 with self.input.raw_mode(), self.input.attach( 735 read_from_input_in_context 736 ), attach_winch_signal_handler(self._on_resize): 737 # Draw UI. 738 self._request_absolute_cursor_position() --> 739 self._redraw() 740 self._start_auto_refresh_task() 742 self.create_background_task(self._poll_output_size()) File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/application/application.py:543, in Application._redraw(self, render_as_done) 535 # NOTE: We want to make sure this Application is the active one. The 536 # invalidate function is often called from a context where this 537 # application is not the active one. (Like the (...) 540 # prevent RuntimeErrors. (The rendering is not supposed to change 541 # any context variables.) 542 if self.context is not None: --> 543 self.context.copy().run(run_in_context) File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/application/application.py:526, in Application._redraw..run_in_context() 524 self.renderer.render(self, self.layout, is_done=render_as_done) 525 else: --> 526 self.renderer.render(self, self.layout) 528 self.layout.update_parents_relations() 530 # Fire render event. File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/renderer.py:675, in Renderer.render(self, app, layout, is_done) 672 self._last_transformation_hash = app.style_transformation.invalidation_hash() 673 self._last_color_depth = app.color_depth --> 675 layout.container.write_to_screen( 676 screen, 677 mouse_handlers, 678 WritePosition(xpos=0, ypos=0, width=size.columns, height=height), 679 parent_style="", 680 erase_bg=False, 681 z_index=None, 682 ) 683 screen.draw_all_floats() 685 # When grayed. Replace all styles in the new screen. File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/layout/containers.py:392, in HSplit.write_to_screen(self, screen, mouse_handlers, write_position, parent_style, erase_bg, z_index) 390 # Draw child panes. 391 for s, c in zip(sizes, self._all_children): --> 392 c.write_to_screen( 393 screen, 394 mouse_handlers, 395 WritePosition(xpos, ypos, width, s), 396 style, 397 erase_bg, 398 z_index, 399 ) 400 ypos += s 402 # Fill in the remaining space. This happens when a child control 403 # refuses to take more space and we don't have any padding. Adding a 404 # dummy child control for this (in `self._all_children`) is not 405 # desired, because in some situations, it would take more space, even 406 # when it's not required. This is required to apply the styling. File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/layout/containers.py:708, in VSplit.write_to_screen(self, screen, mouse_handlers, write_position, parent_style, erase_bg, z_index) 706 # Draw all child panes. 707 for s, c in zip(sizes, children): --> 708 c.write_to_screen( 709 screen, 710 mouse_handlers, 711 WritePosition(xpos, ypos, s, height), 712 style, 713 erase_bg, 714 z_index, 715 ) 716 xpos += s 718 # Fill in the remaining space. This happens when a child control 719 # refuses to take more space and we don't have any padding. Adding a 720 # dummy child control for this (in `self._all_children`) is not 721 # desired, because in some situations, it would take more space, even 722 # when it's not required. This is required to apply the styling. File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/prompt_toolkit/layout/containers.py:392, in HSplit.write_to_screen(self, screen, mouse_handlers, write_position, parent_style, erase_bg, z_index) 390 # Draw child panes. 391 for s, c in zip(sizes, self._all_children): --> 392 c.write_to_screen( 393 screen, 394 mouse_handlers, 395 WritePosition(xpos, ypos, width, s), 396 style, 397 erase_bg, 398 z_index, 399 ) 400 ypos += s 402 # Fill in the remaining space. This happens when a child control 403 # refuses to take more space and we don't have any padding. Adding a 404 # dummy child control for this (in `self._all_children`) is not 405 # desired, because in some situations, it would take more space, even 406 # when it's not required. This is required to apply the styling. File ~/.mambaforge/envs/VENV_NAME/lib/python3.11/site-packages/euporie/core/layout/containers.py:311, in Window.write_to_screen(self, screen, mouse_handlers, write_position, parent_style, erase_bg, z_index) 301 def write_to_screen( 302 self, 303 screen: Screen, (...) 308 z_index: int | None, 309 ) -> None: 310 """Write window to screen.""" --> 311 assert isinstance(write_position, BoundedWritePosition) 312 # If dont_extend_width/height was given, then reduce width/height in 313 # WritePosition, if the parent wanted us to paint in a bigger area. 314 # (This happens if this window is bundled with another window in a 315 # HSplit/VSplit, but with different size requirements.) 316 write_position = BoundedWritePosition( 317 xpos=write_position.xpos, 318 ypos=write_position.ypos, (...) 321 bbox=write_position.bbox, 322 ) AssertionError: ```

Package installed in my env:

# CPython 3.11

ptpython==3.0.26
prompt-toolkit==3.0.43
euporie==2.8.1
ipykernel==6.29.4
ipython==8.23.0

Peeking at the source code, it seems a problem of euporie rather than prompt_toolkit or ptpython.

joouha commented 1 month ago

Hi,

Apologies, this is not intentional. Euporie registers a custom pygments, which gets loaded by pygments when pyipython is run. This causes euporie.core to be imported, which triggers the patching of prompty_toolkit.

I've published a new release with a fix for this (v2.8.2), where prompt_toolkit is patched when a euporie app is lanuched rather than at import.

I hope that fixes the problem for you!

wookayin commented 1 month ago

Thanks for the prompt fix! I can confirm that euporie>=2.8.2 works well with ptpython.

Ref: https://github.com/joouha/euporie/commit/78d09b152ae510dee57b7f5ed649abf6910238a4