Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

!experiment shell #419

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 293 additions & 0 deletions src/libvcs/shell.py
Original file line number Diff line number Diff line change
@@ -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,
)