mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-10-31 05:43:41 +00:00 
			
		
		
		
	fix: suppress traceback in shell if no ipython installed
Old implementation was showing import error, new implementation instead hides it behind separated call and if-else check
This commit is contained in:
		| @ -18,6 +18,7 @@ | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from code import InteractiveConsole | ||||
| from importlib.util import find_spec | ||||
| from typing import Any | ||||
|  | ||||
|  | ||||
| @ -26,6 +27,19 @@ class InteractiveShell(InteractiveConsole): | ||||
|     wrapper around :class:`code.InteractiveConsole` to pass :func:`interact()` to IPython shell | ||||
|     """ | ||||
|  | ||||
|     @staticmethod | ||||
|     def has_ipython() -> bool: | ||||
|         """ | ||||
|         check if IPython shell is available | ||||
|  | ||||
|         Returns: | ||||
|             bool: ``True`` if IPython shell is available, ``False`` otherwise | ||||
|         """ | ||||
|         try: | ||||
|             return find_spec("IPython.terminal.embed") is not None | ||||
|         except ModuleNotFoundError: | ||||
|             return False | ||||
|  | ||||
|     def interact(self, *args: Any, **kwargs: Any) -> None: | ||||
|         """ | ||||
|         pass controller to IPython shell | ||||
| @ -34,13 +48,13 @@ class InteractiveShell(InteractiveConsole): | ||||
|             *args(Any): positional arguments | ||||
|             **kwargs(Any): keyword arguments | ||||
|         """ | ||||
|         try: | ||||
|         if self.has_ipython(): | ||||
|             from IPython.terminal.embed import InteractiveShellEmbed | ||||
|  | ||||
|             shell = InteractiveShellEmbed(user_ns=self.locals)  # type: ignore[no-untyped-call] | ||||
|             shell.show_banner()  # type: ignore[no-untyped-call] | ||||
|             shell.interact()  # type: ignore[no-untyped-call] | ||||
|         except ImportError: | ||||
|         else: | ||||
|             # fallback to default | ||||
|             import readline  # pylint: disable=unused-import | ||||
|             InteractiveConsole.interact(self, *args, **kwargs) | ||||
|  | ||||
| @ -1,14 +1,30 @@ | ||||
| import pytest | ||||
|  | ||||
| from pytest_mock import MockerFixture | ||||
|  | ||||
| from ahriman.application.interactive_shell import InteractiveShell | ||||
|  | ||||
|  | ||||
| def test_has_ipython(mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must correctly check if IPython is installed | ||||
|     """ | ||||
|     find_spec_mock = mocker.patch("ahriman.application.interactive_shell.find_spec") | ||||
|     assert InteractiveShell.has_ipython() | ||||
|     find_spec_mock.assert_called_once_with("IPython.terminal.embed") | ||||
|  | ||||
|  | ||||
| def test_has_ipython_module_not_found(mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must return False if IPython is not installed | ||||
|     """ | ||||
|     mocker.patch("ahriman.application.interactive_shell.find_spec", side_effect=ModuleNotFoundError) | ||||
|     assert not InteractiveShell.has_ipython() | ||||
|  | ||||
|  | ||||
| def test_interact(mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must call IPython shell | ||||
|     """ | ||||
|     mocker.patch("ahriman.application.interactive_shell.InteractiveShell.has_ipython", return_value=True) | ||||
|     banner_mock = mocker.patch("IPython.terminal.embed.InteractiveShellEmbed.show_banner") | ||||
|     interact_mock = mocker.patch("IPython.terminal.embed.InteractiveShellEmbed.interact") | ||||
|  | ||||
| @ -18,11 +34,11 @@ def test_interact(mocker: MockerFixture) -> None: | ||||
|     interact_mock.assert_called_once_with() | ||||
|  | ||||
|  | ||||
| def test_interact_import_error(mocker: MockerFixture) -> None: | ||||
| def test_interact_no_ipython(mocker: MockerFixture) -> None: | ||||
|     """ | ||||
|     must call builtin shell if no IPython available | ||||
|     """ | ||||
|     pytest.helpers.import_error("IPython.terminal.embed", ["InteractiveShellEmbed"], mocker) | ||||
|     mocker.patch("ahriman.application.interactive_shell.InteractiveShell.has_ipython", return_value=None) | ||||
|     interact_mock = mocker.patch("code.InteractiveConsole.interact") | ||||
|  | ||||
|     shell = InteractiveShell() | ||||
|  | ||||
		Reference in New Issue
	
	Block a user