From fdbc6d5452850cdb01da07f67432ba523538e5d6 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 22 Sep 2022 20:39:09 -0500 Subject: [PATCH] !experiment shell --- src/libvcs/shell.py | 293 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 src/libvcs/shell.py diff --git a/src/libvcs/shell.py b/src/libvcs/shell.py new file mode 100644 index 000000000..78f899d7b --- /dev/null +++ b/src/libvcs/shell.py @@ -0,0 +1,293 @@ +import logging +import os +import sys + +PY3 = sys.version_info[0] == 3 +PYMINOR = sys.version_info[1] +PYPATCH = sys.version_info[2] + + +if PY3 and PYMINOR >= 7: + breakpoint = breakpoint +else: + import pdb + + breakpoint = pdb.set_trace # type: ignore + + +logger = logging.getLogger(__name__) + + +def has_ipython(): + try: + from IPython import start_ipython # NOQA F841 + except ImportError: + try: + from IPython.Shell import IPShell # NOQA F841 + except ImportError: + return False + + return True + + +def has_ptpython(): + try: + from ptpython.repl import embed, run_config # NOQA F841 + except ImportError: + try: + from prompt_toolkit.contrib.repl import embed, run_config # NOQA F841 + except ImportError: + return False + + return True + + +def has_ptipython(): + try: + from ptpython.ipython import embed # NOQA F841 + from ptpython.repl import run_config # NOQA F841 + except ImportError: + try: + from prompt_toolkit.contrib.ipython import embed # NOQA F841 + from prompt_toolkit.contrib.repl import run_config # NOQA F841 + except ImportError: + return False + + return True + + +def has_bpython(): + try: + from bpython import embed # NOQA F841 + except ImportError: + return False + return True + + +def detect_best_shell(): + if has_ptipython(): + return "ptipython" + elif has_ptpython(): + return "ptpython" + elif has_ipython(): + return "ipython" + elif has_bpython(): + return "bpython" + return "code" + + +def get_bpython(options, extra_args=None): + if extra_args is None: + extra_args = {} + + from bpython import embed # NOQA F841 + + def launch_bpython(): + imported_objects = get_launch_args(**options) + kwargs = {} + if extra_args: + kwargs["args"] = extra_args + embed(imported_objects, **kwargs) + + return launch_bpython + + +def get_ipython_arguments(): + ipython_args = "IPYTHON_ARGUMENTS" + return os.environ.get(ipython_args, "").split() + + +def get_ipython(options, **extra_args): + try: + from IPython import start_ipython + + def launch_ipython(): + imported_objects = get_launch_args(**options) + ipython_arguments = extra_args or get_ipython_arguments() + start_ipython(argv=ipython_arguments, user_ns=imported_objects) + + return launch_ipython + except ImportError: + # IPython < 0.11 + # Explicitly pass an empty list as arguments, because otherwise + # IPython would use sys.argv from this script. + # Notebook not supported for IPython < 0.11. + from IPython.Shell import IPShell + + def launch_ipython(): + imported_objects = get_launch_args(**options) + shell = IPShell(argv=[], user_ns=imported_objects) + shell.mainloop() + + return launch_ipython + + +def get_ptpython(options, vi_mode=False): + try: + from ptpython.repl import embed, run_config + except ImportError: + from prompt_toolkit.contrib.repl import embed, run_config + + def launch_ptpython(): + imported_objects = get_launch_args(**options) + history_filename = os.path.expanduser("~/.ptpython_history") + embed( + globals=imported_objects, + history_filename=history_filename, + vi_mode=vi_mode, + configure=run_config, + ) + + return launch_ptpython + + +def get_ptipython(options, vi_mode=False): + """Based on django-extensions + + Run renamed to launch, get_imported_objects renamed to get_launch_args + """ + try: + from ptpython.ipython import embed + from ptpython.repl import run_config + except ImportError: + # prompt_toolkit < v0.27 + from prompt_toolkit.contrib.ipython import embed + from prompt_toolkit.contrib.repl import run_config + + def launch_ptipython(): + imported_objects = get_launch_args(**options) + history_filename = os.path.expanduser("~/.ptpython_history") + embed( + user_ns=imported_objects, + history_filename=history_filename, + vi_mode=vi_mode, + configure=run_config, + ) + + return launch_ptipython + + +def get_launch_args(**kwargs): + import libtmux + + return { + "libtmux": libtmux, + "Server": libtmux.Server, + "Session": libtmux.Session, + "Window": libtmux.Window, + "Pane": libtmux.Pane, + "server": kwargs.get("server"), + "session": kwargs.get("session"), + "window": kwargs.get("window"), + "pane": kwargs.get("pane"), + } + + +def get_code(use_pythonrc, imported_objects): + import code + + try: + # Try activating rlcompleter, because it's handy. + import readline + except ImportError: + pass + else: + # We don't have to wrap the following import in a 'try', because + # we already know 'readline' was imported successfully. + import rlcompleter + + readline.set_completer(rlcompleter.Completer(imported_objects).complete) + # Enable tab completion on systems using libedit (e.g. macOS). + # These lines are copied from Lib/site.py on Python 3.4. + readline_doc = getattr(readline, "__doc__", "") + if readline_doc is not None and "libedit" in readline_doc: + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("tab:complete") + + # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system + # conventions and get $PYTHONSTARTUP first then .pythonrc.py. + if use_pythonrc: + for pythonrc in { + os.environ.get("PYTHONSTARTUP"), + os.path.expanduser("~/.pythonrc.py"), + }: + if not pythonrc: + continue + if not os.path.isfile(pythonrc): + continue + with open(pythonrc) as handle: + pythonrc_code = handle.read() + # Match the behavior of the cpython shell where an error in + # PYTHONSTARTUP prints an exception and continues. + exec(compile(pythonrc_code, pythonrc, "exec"), imported_objects) + + def launch_code(): + code.interact(local=imported_objects) + + return launch_code + + +def launch(shell="best", use_pythonrc=False, use_vi_mode=False, **kwargs): + # Also allowing passing shell='code' to force using code.interact + imported_objects = get_launch_args(**kwargs) + + if shell == "best": + shell = detect_best_shell() + + if shell == "ptipython": + launch = get_ptipython(options=kwargs, vi_mode=use_vi_mode) + elif shell == "ptpython": + launch = get_ptpython(options=kwargs, vi_mode=use_vi_mode) + elif shell == "ipython": + launch = get_ipython(options=kwargs) + elif shell == "bpython": + launch = get_bpython(options=kwargs) + else: + launch = get_code(use_pythonrc=use_pythonrc, imported_objects=imported_objects) + + launch() + + +def command_shell( + session_name, + window_name, + socket_name, + socket_path, + command, + shell, + use_pythonrc, + use_vi_mode, +): + """Launch python shell for tmux server, session, window and pane. + + Priority given to loaded session/window/pane objects: + + - session_name and window_name arguments + - current shell: envvar ``TMUX_PANE`` for determining window and session + - :attr:`libtmux.Server.attached_sessions`, :attr:`libtmux.Session.attached_window`, + :attr:`libtmux.Window.attached_pane` + """ + server = Server(socket_name=socket_name, socket_path=socket_path) + + if command is not None: + exec(command) + else: + if shell == "pdb" or (os.getenv("PYTHONBREAKPOINT") and PY3 and PYMINOR >= 7): + from tmuxp._compat import breakpoint as tmuxp_breakpoint + + tmuxp_breakpoint() + return + else: + from ..shell import launch + + launch( + shell=shell, + use_pythonrc=use_pythonrc, # shell: code + use_vi_mode=use_vi_mode, # shell: ptpython, ptipython + # tmux environment / libtmux variables + server=server, + session=session, + window=window, + pane=pane, + )