Skip to content

fix(tmux_cmd): use shutil.which and only PATH to discover tmux #407

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 4 commits into from
Aug 28, 2022
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
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ $ pip install --user --upgrade --pre libtmux

### Breaking changes

- Remove `common.which()` in favor of {func}`shutil.which`, Credit:
@rocksandska, via #407
- Fixes #402: {func}`common.tmux_cmd` will only strip _trailing_ empty lines. Before this change,
all empty lines were filtered out. This will lead to a more accurate behavior when using
{meth}`Pane.capture_pane`. Credit: @rockandska, via #405.
Expand Down
87 changes: 3 additions & 84 deletions libtmux/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import logging
import os
import re
import shutil
import subprocess
import sys
import typing as t
Expand Down Expand Up @@ -203,14 +204,6 @@ class tmux_cmd:
"""
:term:`tmux(1)` command via :py:mod:`subprocess`.

Parameters
----------
tmux_search_paths : list, optional
Default PATHs to search tmux for, defaults to ``default_paths`` used
in :func:`which`.
append_env_path : bool
Append environment PATHs to tmux search paths. True by default.

Examples
--------

Expand Down Expand Up @@ -239,14 +232,7 @@ class tmux_cmd:
"""

def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
tmux_bin = which(
"tmux",
default_paths=kwargs.get(
"tmux_search_paths",
["/bin", "/sbin", "/usr/bin", "/usr/sbin", "/usr/local/bin"],
),
append_env_path=kwargs.get("append_env_path", True),
)
tmux_bin = shutil.which("tmux")
if not tmux_bin:
raise (exc.TmuxCommandNotFound)

Expand Down Expand Up @@ -461,73 +447,6 @@ def get_by_id(self, id: str) -> Optional[O]:
return None


def which(
exe: str,
default_paths: t.List[str] = [
"/bin",
"/sbin",
"/usr/bin",
"/usr/sbin",
"/usr/local/bin",
],
append_env_path: bool = True,
) -> t.Optional[str]:
"""
Return path of bin. Python clone of /usr/bin/which.

Parameters
----------
exe : str
Application to search PATHs for.
default_paths : list
Paths to check inside of
append_env_path : bool, optional
Append list of directories to check in from PATH environment variable.
Default True. Setting False only for testing / diagnosing.

Returns
-------
str
path of application, if found in paths.

Notes
-----
from salt.util - https://www.github.com/saltstack/salt - license apache
"""

def _is_executable_file_or_link(exe: str) -> bool:
# check for os.X_OK doesn't suffice because directory may executable
return os.access(exe, os.X_OK) and (os.path.isfile(exe) or os.path.islink(exe))

if _is_executable_file_or_link(exe):
# executable in cwd or fullpath
return exe

# Enhance POSIX path for the reliability at some environments, when
# $PATH is changing. This also keeps order, where 'first came, first
# win' for cases to find optional alternatives
if append_env_path:
search_path = (
os.environ.get("PATH") and os.environ["PATH"].split(os.pathsep) or list()
)
else:
search_path = []

for default_path in default_paths:
if default_path not in search_path:
search_path.append(default_path)
for path in search_path:
full_path = os.path.join(path, exe)
if _is_executable_file_or_link(full_path):
return full_path
logger.info(
"'{}' could not be found in the following search path: "
"'{}'".format(exe, search_path)
)

return None


def get_version() -> LooseVersion:
"""
Return tmux version.
Expand All @@ -541,7 +460,7 @@ def get_version() -> LooseVersion:
Returns
-------
:class:`distutils.version.LooseVersion`
tmux version according to :func:`libtmux.common.which`'s tmux
tmux version according to :func:`shtuil.which`'s tmux
"""
proc = tmux_cmd("-V")
if proc.stderr:
Expand Down
4 changes: 2 additions & 2 deletions libtmux/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import os
import typing as t
import shutil

import pytest

Expand All @@ -9,7 +10,6 @@
from _pytest.monkeypatch import MonkeyPatch

from libtmux import exc
from libtmux.common import which
from libtmux.server import Server
from libtmux.test import TEST_SESSION_PREFIX, get_test_session_name, namer

Expand Down Expand Up @@ -109,7 +109,7 @@ def add_doctest_fixtures(
request: SubRequest,
doctest_namespace: t.Dict[str, t.Any],
) -> None:
if isinstance(request._pyfuncitem, DoctestItem) and which("tmux"):
if isinstance(request._pyfuncitem, DoctestItem) and shutil.which("tmux"):
doctest_namespace["server"] = request.getfixturevalue("server")
session: "Session" = request.getfixturevalue("session")
doctest_namespace["session"] = session
Expand Down
15 changes: 3 additions & 12 deletions tests/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
has_version,
session_check_name,
tmux_cmd,
which,
)
from libtmux.exc import BadSessionName, LibTmuxException, TmuxCommandNotFound
from libtmux.session import Session
Expand Down Expand Up @@ -174,18 +173,10 @@ def test_has_lte_version() -> None:
assert not has_lte_version("1.7b")


def test_which_no_bin_found() -> None:
assert which("top")
assert which("top", default_paths=[])
assert not which("top", default_paths=[], append_env_path=False)
assert not which("top", default_paths=["/"], append_env_path=False)


def test_tmux_cmd_raises_on_not_found() -> None:
def test_tmux_cmd_raises_on_not_found(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("PATH", "")
with pytest.raises(TmuxCommandNotFound):
tmux_cmd("-V", tmux_search_paths=[], append_env_path=False)

tmux_cmd("-V")
tmux_cmd("-V")


def test_tmux_cmd_unicode(session: Session) -> None:
Expand Down