From a9678ad3007168254875e9ccca0531dd18c360e7 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 9 Nov 2017 19:33:24 -0600 Subject: [PATCH 1/2] Split init / exec of tmux_cmd for debuggability tmux_cmd initialization composes the command the command will be available in the instance attribute .cmd .execute() instance method will run tmux_cmd see Server.cmd for example of new usage Related #77 --- libtmux/common.py | 12 ++++++++++-- libtmux/server.py | 2 +- tests/test_common.py | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/libtmux/common.py b/libtmux/common.py index 05b85229f..fc2c63d58 100644 --- a/libtmux/common.py +++ b/libtmux/common.py @@ -165,7 +165,12 @@ class tmux_cmd: .. code-block:: python - proc = tmux_cmd('new-session', '-s%' % 'my session') + c = tmux_cmd('new-session', '-s%' % 'my session') + + # You can actually see the command in the .cmd attribute + print(c.cmd) + + proc = c.execute() if proc.stderr: raise exc.LibTmuxException( @@ -205,6 +210,8 @@ def __init__(self, *args, **kwargs): self.cmd = cmd + def execute(self): + cmd = self.cmd try: self.process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE @@ -229,6 +236,7 @@ def __init__(self, *args, **kwargs): self.stdout = self.stderr[0] logger.debug("self.stdout for {}: \n{}".format(" ".join(cmd), self.stdout)) + return self class TmuxMappingObject(MutableMapping): @@ -462,7 +470,7 @@ def get_version() -> LooseVersion: :class:`distutils.version.LooseVersion` tmux version according to :func:`libtmux.common.which`'s tmux """ - proc = tmux_cmd("-V") + proc = tmux_cmd("-V").execute() if proc.stderr: if proc.stderr[0] == "tmux: unknown option -- V": if sys.platform.startswith("openbsd"): # openbsd has no tmux -V diff --git a/libtmux/server.py b/libtmux/server.py index 11f1c1c90..0a0a82e45 100644 --- a/libtmux/server.py +++ b/libtmux/server.py @@ -127,7 +127,7 @@ def cmd(self, *args, **kwargs): else: raise ValueError("Server.colors must equal 88 or 256") - return tmux_cmd(*args, **kwargs) + return tmux_cmd(*args, **kwargs).execute() def _list_sessions(self) -> t.List[SessionDict]: """ diff --git a/tests/test_common.py b/tests/test_common.py index a86da3bb0..7e3b0b7e6 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -33,6 +33,9 @@ class Hi: stdout = ["tmux master"] stderr = None + def execute(self): + return self + return Hi() monkeypatch.setattr(libtmux.common, "tmux_cmd", mock_tmux_cmd) @@ -51,6 +54,9 @@ class Hi: stdout = ["tmux next-2.9"] stderr = None + def execute(self): + return self + return Hi() monkeypatch.setattr(libtmux.common, "tmux_cmd", mock_tmux_cmd) @@ -66,6 +72,9 @@ def mock_tmux_cmd(param): class Hi: stderr = ["tmux: unknown option -- V"] + def execute(self): + return self + return Hi() monkeypatch.setattr(libtmux.common, "tmux_cmd", mock_tmux_cmd) @@ -83,6 +92,9 @@ def mock_tmux_cmd(param): class Hi: stderr = ["tmux: unknown option -- V"] + def execute(self): + return self + return Hi() monkeypatch.setattr(libtmux.common, "tmux_cmd", mock_tmux_cmd) @@ -183,6 +195,12 @@ def test_tmux_cmd_unicode(session): session.cmd("new-window", "-t", 3, "-n", "юникод", "-F", "Ελληνικά") +def test_tmux_cmd_makes_cmd_available(): + """tmux_cmd objects should make .cmd attribute available.""" + command = tmux_cmd("-V") + assert hasattr(command, "cmd") + + @pytest.mark.parametrize( "session_name,raises,exc_msg_regex", [ From 8e00da880d700ec1e07f5d0c0aa21154a5c902e4 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 25 Oct 2020 08:59:36 -0500 Subject: [PATCH 2/2] Add TmuxCommand, wrap tmux_cmd in old behavior --- libtmux/common.py | 41 ++++++++++++++++++++++++++++++++++++----- libtmux/server.py | 10 +++++----- tests/test_common.py | 16 +++++++++++++--- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/libtmux/common.py b/libtmux/common.py index fc2c63d58..181c77368 100644 --- a/libtmux/common.py +++ b/libtmux/common.py @@ -147,8 +147,7 @@ def show_environment(self, name=None): return vars_dict -class tmux_cmd: - +class TmuxCommand: """ :term:`tmux(1)` command via :py:mod:`subprocess`. @@ -188,8 +187,8 @@ class tmux_cmd: Notes ----- - .. versionchanged:: 0.8 - Renamed from ``tmux`` to ``tmux_cmd``. + .. versionadded:: 0.8.4 + Wrap to split execution from command from instance of it """ def __init__(self, *args, **kwargs): @@ -239,6 +238,38 @@ def execute(self): return self +def tmux_cmd(*args, **kwargs): + """Wrapper around TmuxCommand. Executes instantly. + + Examples + -------- + + .. code-block:: python + + proc = tmux_cmd('new-session', '-s%' % 'my session') + + if proc.stderr: + raise exc.LibTmuxException( + 'Command: %s returned error: %s' % (proc.cmd, proc.stderr) + ) + + print('tmux command returned %s' % proc.stdout) + + Equivalent to: + + .. code-block:: bash + + $ tmux new-session -s my session + + Notes + ----- + + .. versionchanged:: 0.8 + Renamed from ``tmux`` to ``tmux_cmd``. + """ + return TmuxCommand(*args, **kwargs).execute() + + class TmuxMappingObject(MutableMapping): r"""Base: :py:class:`MutableMapping`. @@ -470,7 +501,7 @@ def get_version() -> LooseVersion: :class:`distutils.version.LooseVersion` tmux version according to :func:`libtmux.common.which`'s tmux """ - proc = tmux_cmd("-V").execute() + proc = tmux_cmd("-V") if proc.stderr: if proc.stderr[0] == "tmux: unknown option -- V": if sys.platform.startswith("openbsd"): # openbsd has no tmux -V diff --git a/libtmux/server.py b/libtmux/server.py index 0a0a82e45..40615eb10 100644 --- a/libtmux/server.py +++ b/libtmux/server.py @@ -13,11 +13,11 @@ EnvironmentMixin, PaneDict, SessionDict, + TmuxCommand, TmuxRelationalObject, WindowDict, has_gte_version, session_check_name, - tmux_cmd, ) from .session import Session @@ -127,7 +127,7 @@ def cmd(self, *args, **kwargs): else: raise ValueError("Server.colors must equal 88 or 256") - return tmux_cmd(*args, **kwargs).execute() + return TmuxCommand(*args, **kwargs).execute() def _list_sessions(self) -> t.List[SessionDict]: """ @@ -136,7 +136,7 @@ def _list_sessions(self) -> t.List[SessionDict]: Retrieved from ``$ tmux(1) list-sessions`` stdout. The :py:obj:`list` is derived from ``stdout`` in - :class:`common.tmux_cmd` which wraps :py:class:`subprocess.Popen`. + :class:`common.TmuxCommand` which wraps :py:class:`subprocess.Popen`. Returns ------- @@ -199,7 +199,7 @@ def _list_windows(self) -> t.List[WindowDict]: Retrieved from ``$ tmux(1) list-windows`` stdout. The :py:obj:`list` is derived from ``stdout`` in - :class:`common.tmux_cmd` which wraps :py:class:`subprocess.Popen`. + :class:`common.TmuxCommand` which wraps :py:class:`subprocess.Popen`. Returns ------- @@ -261,7 +261,7 @@ def _list_panes(self) -> t.List[PaneDict]: Retrieved from ``$ tmux(1) list-panes`` stdout. The :py:obj:`list` is derived from ``stdout`` in - :class:`util.tmux_cmd` which wraps :py:class:`subprocess.Popen`. + :class:`util.TmuxCommand` which wraps :py:class:`subprocess.Popen`. Returns ------- diff --git a/tests/test_common.py b/tests/test_common.py index 7e3b0b7e6..b44b6bd00 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -27,8 +27,9 @@ version_regex = re.compile(r"([0-9]\.[0-9])|(master)") -def test_allows_master_version(monkeypatch): - def mock_tmux_cmd(param): +@pytest.mark.parametrize("executor", ["mock_tmux_cmd", "mock_TmuxCommand"]) +def test_allows_master_version(monkeypatch, executor): + def mock_TmuxCommand(param): class Hi: stdout = ["tmux master"] stderr = None @@ -38,7 +39,16 @@ def execute(self): return Hi() - monkeypatch.setattr(libtmux.common, "tmux_cmd", mock_tmux_cmd) + def mock_tmux_cmd(param): + class Hi(object): + stdout = ["tmux master"] + stderr = None + + return Hi() + + mock_cmd = locals()[executor] + + monkeypatch.setattr(libtmux.common, "tmux_cmd", mock_cmd) assert has_minimum_version() assert has_gte_version(TMUX_MIN_VERSION)