Skip to content

New command: tmuxp shell #636

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

Merged
merged 20 commits into from
Nov 1, 2020
Merged
Show file tree
Hide file tree
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
35 changes: 34 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,42 @@ Here you can find the recent changes to tmuxp
current
-------
- *Insert changes/features/fixes for next release here*
- :issue:`636` New command: ``tmuxp shell``

Automatically preloads session, window, and pane via `libtmux`_
api objects and makes them available in a python console.

.. image:: _static/tmuxp-shell.gif
:width: 100%

In python 3.7+, supports ``PYTHONBREAKPOINT``:

.. code-block:: sh

$ pip install ipdb
$ env PYTHONBREAKPOINT=ipdb.set_trace tmuxp shell

You can execute python directly via ``-c``:

.. code-block:: sh

$ tmuxp shell -c 'print(session.name); print(window.name)'
my_server
my_window

$ tmuxp shell my_server -c 'print(session.name); print(window.name)'
my_server
my_window

$ tmuxp shell my_server my_window -c 'print(session.name); print(window.name)'
my_server
my_window

$ tmuxp shell my_server my_window -c 'print(window.name.upper())'
MY_WINDOW

tmuxp 1.5.8 (2020-10-31)
-----------------------
------------------------
- :issue:`639` Passes start_directory through to new tmux session
Fixes :issue:`631`, thank you @joseph-flinn!

Expand Down
44 changes: 44 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,50 @@ Load your tmuxp config from anywhere by using the filename, assuming

See `author's tmuxp configs`_ and the projects' `tmuxp.yaml`_.

Shell
-----
*New in 1.6.0*:

``tmuxp shell`` launches into a python console preloaded with the attached server,
session, and window in `libtmux`_ objects.

.. code-block:: shell

$ tmuxp shell

(Pdb) server
<libtmux.server.Server object at 0x7f7dc8e69d10>
(Pdb) server.sessions
[Session($1 your_project)]
(Pdb) session
Session($1 your_project)
(Pdb) session.name
'your_project'
(Pdb) window
Window(@3 1:your_window, Session($1 your_project))
(Pdb) window.name
'your_window'
(Pdb) window.panes
[Pane(%6 Window(@3 1:your_window, Session($1 your_project)))
(Pdb) pane
Pane(%6 Window(@3 1:your_window, Session($1 your_project))

Python 3.7+ supports `PEP 553`_ ``breakpoint()`` (including
``PYTHONBREAKPOINT``). Also supports direct commands via ``-c``:

.. code-block:: shell

$ tmuxp shell -c 'print(window.name)'
my_window

$ tmuxp shell -c 'print(window.name.upper())'
MY_WINDOW

Read more on `tmuxp shell`_ in the CLI docs.

.. _PEP 553: https://www.python.org/dev/peps/pep-0553/
.. _tmuxp shell: http://localhost:8031/cli.html#shell

Pre-load hook
-------------
Run custom startup scripts (such as installing project dependencies before
Expand Down
Binary file added docs/_static/tmuxp-shell.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,85 @@ In zsh (``~/.zshrc``):

eval "$(_TMUXP_COMPLETE=source_zsh tmuxp)"

.. _cli_shell:

Shell
-----

::

tmuxp shell

tmuxp shell <session_name>

tmuxp shell <session_name> <window_name>

tmuxp shell -c 'python code'

Launch into a python console with `libtmux`_ objects. Compare to django's shell.

.. image:: _static/tmuxp-shell.gif
:width: 100%

Automatically preloads current tmux :class:`server <libtmux.Server>`,
:class:`session <libtmux.Session>`, :class:`window <libtmux.Window>`
:class:`pane <libtmux.Pane>`. Pass additional arguments to select a
specific one of your choice::

(Pdb) server
<libtmux.server.Server object at 0x7f7dc8e69d10>
(Pdb) server.sessions
[Session($1 your_project)]
(Pdb) session
Session($1 your_project)
(Pdb) session.name
'your_project'
(Pdb) window
Window(@3 1:your_window, Session($1 your_project))
(Pdb) window.name
'your_window'
(Pdb) window.panes
[Pane(%6 Window(@3 1:your_window, Session($1 your_project)))
(Pdb) pane
Pane(%6 Window(@3 1:your_window, Session($1 your_project)))

Python 3.7 supports `PEP 553`_'s ``PYTHONBREAKPOINT`` and supports
compatible debuggers, for instance `ipdb`_:

.. code-block:: sh

$ pip install ipdb
$ env PYTHONBREAKPOINT=ipdb.set_trace tmuxp shell

You can also pass in python code directly, similar to ``python -c``, do
this via ``tmuxp -c``:

.. code-block:: shell

$ tmuxp shell -c 'print(session.name); print(window.name)'
my_server
my_window

$ tmuxp shell my_server -c 'print(session.name); print(window.name)'
my_server
my_window

$ tmuxp shell my_server my_window -c 'print(session.name); print(window.name)'
my_server
my_window

$ tmuxp shell my_server my_window -c 'print(window.name.upper())'
MY_WINDOW

# Assuming inside a tmux pane or one is attached on default server
$ tmuxp shell -c 'print(pane.id); print(pane.window.name)'
%2
my_window

.. _PEP 553: https://www.python.org/dev/peps/pep-0553/
.. _ipdb: https://pypi.org/project/ipdb/
.. _libtmux: https://libtmux.git-pull.com

.. _cli_freeze:

Freeze sessions
Expand Down
9 changes: 7 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@


@pytest.fixture(scope='function')
def server(request):
def socket_name(request):
return 'tmuxp_test%s' % next(namer)


@pytest.fixture(scope='function')
def server(request, socket_name):
t = Server()
t.socket_name = 'tmuxp_test%s' % next(namer)
t.socket_name = socket_name

def fin():
t.kill_server()
Expand Down
137 changes: 137 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import libtmux
from libtmux.common import has_lt_version
from libtmux.exc import LibTmuxException
from tmuxp import cli, config
from tmuxp.cli import (
command_ls,
Expand Down Expand Up @@ -406,6 +407,142 @@ def test_load_zsh_autotitle_warning(cli_args, tmpdir, monkeypatch):
assert 'Please set' not in result.output


@pytest.mark.parametrize(
"cli_args,inputs,env,expected_output",
[
(
['shell', '-L{SOCKET_NAME}', '-c', 'print(str(server.socket_name))'],
[],
{},
'{SERVER_SOCKET_NAME}',
),
(
[
'shell',
'-L{SOCKET_NAME}',
'{SESSION_NAME}',
'-c',
'print(session.name)',
],
[],
{},
'{SESSION_NAME}',
),
(
[
'shell',
'-L{SOCKET_NAME}',
'{SESSION_NAME}',
'{WINDOW_NAME}',
'-c',
'print(server.has_session(session.name))',
],
[],
{},
'True',
),
(
[
'shell',
'-L{SOCKET_NAME}',
'{SESSION_NAME}',
'{WINDOW_NAME}',
'-c',
'print(window.name)',
],
[],
{},
'{WINDOW_NAME}',
),
(
[
'shell',
'-L{SOCKET_NAME}',
'{SESSION_NAME}',
'{WINDOW_NAME}',
'-c',
'print(pane.id)',
],
[],
{},
'{PANE_ID}',
),
(
[
'shell',
'-L{SOCKET_NAME}',
'-c',
'print(pane.id)',
],
[],
{'TMUX_PANE': '{PANE_ID}'},
'{PANE_ID}',
),
],
)
def test_shell(
cli_args, inputs, expected_output, env, tmpdir, monkeypatch, server, session
):
monkeypatch.setenv('HOME', str(tmpdir))
window_name = 'my_window'
window = session.new_window(window_name=window_name)
window.split_window()

template_ctx = dict(
SOCKET_NAME=server.socket_name,
SOCKET_PATH=server.socket_path,
SESSION_NAME=session.name,
WINDOW_NAME=window_name,
PANE_ID=window.attached_pane.id,
SERVER_SOCKET_NAME=server.socket_name,
)

cli_args[:] = [cli_arg.format(**template_ctx) for cli_arg in cli_args]
for k, v in env.items():
monkeypatch.setenv(k, v.format(**template_ctx))

with tmpdir.as_cwd():
runner = CliRunner()

result = runner.invoke(
cli.cli, cli_args, input=''.join(inputs), catch_exceptions=False
)
assert expected_output.format(**template_ctx) in result.output


@pytest.mark.parametrize(
"cli_args,inputs,env,exception, message",
[
(
['shell', '-L{SOCKET_NAME}', '-c', 'print(str(server.socket_name))'],
[],
{},
LibTmuxException,
r'.*{SOCKET_NAME}\s\(No such file or directory\).*',
),
],
)
def test_shell_no_server(
cli_args, inputs, env, exception, message, tmpdir, monkeypatch, socket_name
):
monkeypatch.setenv('HOME', str(tmpdir))
template_ctx = dict(
SOCKET_NAME=socket_name,
)

cli_args[:] = [cli_arg.format(**template_ctx) for cli_arg in cli_args]
for k, v in env.items():
monkeypatch.setenv(k, v.format(**template_ctx))

with tmpdir.as_cwd():
runner = CliRunner()

with pytest.raises(exception, match=message.format(**template_ctx)):
runner.invoke(
cli.cli, cli_args, input=''.join(inputs), catch_exceptions=False
)


@pytest.mark.parametrize(
"cli_args",
[
Expand Down
11 changes: 11 additions & 0 deletions tmuxp/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@
import sys

PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
PYMINOR = sys.version_info[1]
PYPATCH = sys.version_info[2]

_identity = lambda x: x


if PY3 and PYMINOR >= 7:
breakpoint = breakpoint
else:
import pdb

breakpoint = pdb.set_trace


if PY2:
unichr = unichr
text_type = unicode
Expand Down
Loading