1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
from __future__ import annotations
from collections.abc import Callable
from functools import wraps
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from collections.abc import Iterable
EmbedFuncT = Callable[..., None]
KnownShellsT = dict[str, Callable[..., EmbedFuncT]]
def _embed_ipython_shell(
namespace: dict[str, Any] = {}, banner: str = ""
) -> EmbedFuncT:
"""Start an IPython Shell"""
try:
from IPython.terminal.embed import InteractiveShellEmbed # noqa: T100
from IPython.terminal.ipapp import load_default_config
except ImportError:
from IPython.frontend.terminal.embed import ( # type: ignore[no-redef] # noqa: T100
InteractiveShellEmbed,
)
from IPython.frontend.terminal.ipapp import ( # type: ignore[no-redef]
load_default_config,
)
@wraps(_embed_ipython_shell)
def wrapper(namespace: dict[str, Any] = namespace, banner: str = "") -> None:
config = load_default_config()
# Always use .instance() to ensure _instance propagation to all parents
# this is needed for <TAB> completion works well for new imports
# and clear the instance to always have the fresh env
# on repeated breaks like with inspect_response()
InteractiveShellEmbed.clear_instance()
shell = InteractiveShellEmbed.instance(
banner1=banner, user_ns=namespace, config=config
)
shell()
return wrapper
def _embed_bpython_shell(
namespace: dict[str, Any] = {}, banner: str = ""
) -> EmbedFuncT:
"""Start a bpython shell"""
import bpython
@wraps(_embed_bpython_shell)
def wrapper(namespace: dict[str, Any] = namespace, banner: str = "") -> None:
bpython.embed(locals_=namespace, banner=banner)
return wrapper
def _embed_ptpython_shell(
namespace: dict[str, Any] = {}, banner: str = ""
) -> EmbedFuncT:
"""Start a ptpython shell"""
import ptpython.repl # pylint: disable=import-error
@wraps(_embed_ptpython_shell)
def wrapper(namespace: dict[str, Any] = namespace, banner: str = "") -> None:
print(banner)
ptpython.repl.embed(locals=namespace)
return wrapper
def _embed_standard_shell(
namespace: dict[str, Any] = {}, banner: str = ""
) -> EmbedFuncT:
"""Start a standard python shell"""
import code
try: # readline module is only available on unix systems
import readline
except ImportError:
pass
else:
import rlcompleter # noqa: F401
readline.parse_and_bind("tab:complete") # type: ignore[attr-defined]
@wraps(_embed_standard_shell)
def wrapper(namespace: dict[str, Any] = namespace, banner: str = "") -> None:
code.interact(banner=banner, local=namespace)
return wrapper
DEFAULT_PYTHON_SHELLS: KnownShellsT = {
"ptpython": _embed_ptpython_shell,
"ipython": _embed_ipython_shell,
"bpython": _embed_bpython_shell,
"python": _embed_standard_shell,
}
def get_shell_embed_func(
shells: Iterable[str] | None = None, known_shells: KnownShellsT | None = None
) -> EmbedFuncT | None:
"""Return the first acceptable shell-embed function
from a given list of shell names.
"""
if shells is None: # list, preference order of shells
shells = DEFAULT_PYTHON_SHELLS.keys()
if known_shells is None: # available embeddable shells
known_shells = DEFAULT_PYTHON_SHELLS.copy()
for shell in shells:
if shell in known_shells:
try:
# function test: run all setup code (imports),
# but dont fall into the shell
return known_shells[shell]()
except ImportError:
continue
return None
def start_python_console(
namespace: dict[str, Any] | None = None,
banner: str = "",
shells: Iterable[str] | None = None,
) -> None:
"""Start Python console bound to the given namespace.
Readline support and tab completion will be used on Unix, if available.
"""
if namespace is None:
namespace = {}
try:
shell = get_shell_embed_func(shells)
if shell is not None:
shell(namespace=namespace, banner=banner)
except SystemExit: # raised when using exit() in python code.interact
pass
|