Skip to content
8 changes: 8 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ $ pip install --user --upgrade --pre libtmux

_Maintenance only, no bug fixes, or new features_

### Development

- Code quality improved via [ruff] rules (#488)

This includes fixes made by hand, and with ruff's automated fixes. Despite
selecting additional rules, which include import sorting, ruff runs nearly
instantaneously when checking the whole codebase.

## libtmux 0.22.2 (2023-08-20)

### Development
Expand Down
1 change: 0 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import typing as t

import pytest

from _pytest.doctest import DoctestItem

from libtmux.pytest_plugin import USING_ZSH
Expand Down
18 changes: 6 additions & 12 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# flake8: NOQA: E501
import contextlib
import inspect
import sys
from os.path import relpath
import pathlib
import sys
import typing as t
from os.path import relpath

import libtmux # NOQA
from libtmux import test # NOQA
import libtmux

if t.TYPE_CHECKING:
from sphinx.application import Sphinx
Expand Down Expand Up @@ -166,9 +165,7 @@
}


def linkcode_resolve(
domain: str, info: t.Dict[str, str]
) -> t.Union[None, str]: # NOQA: C901
def linkcode_resolve(domain: str, info: t.Dict[str, str]) -> t.Union[None, str]:
"""
Determine the URL corresponding to Python object

Expand All @@ -191,7 +188,7 @@ def linkcode_resolve(
for part in fullname.split("."):
try:
obj = getattr(obj, part)
except Exception:
except Exception: # noqa: PERF203
return None

# strip decorators, which would resolve to the source of the decorator
Expand All @@ -216,10 +213,7 @@ def linkcode_resolve(
except Exception:
lineno = None

if lineno:
linespec = "#L%d-L%d" % (lineno, lineno + len(source) - 1)
else:
linespec = ""
linespec = "#L%d-L%d" % (lineno, lineno + len(source) - 1) if lineno else ""

fn = relpath(fn, start=pathlib.Path(libtmux.__file__).parent)

Expand Down
27 changes: 27 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,33 @@ exclude_lines = [
"@overload( |$)",
]

[tool.ruff]
target-version = "py37"
select = [
"E", # pycodestyle
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"Q", # flake8-quotes
"PTH", # flake8-use-pathlib
"ERA", # eradicate
"SIM", # flake8-simplify
"TRY", # Trycertatops
"PERF", # Perflint
"RUF" # Ruff-specific rules
]

[tool.ruff.isort]
known-first-party = [
"libtmux"
]
combine-as-imports = true

[tool.ruff.per-file-ignores]
"*/__init__.py" = ["F401"]

[build-system]
requires = ["poetry_core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
27 changes: 16 additions & 11 deletions src/libtmux/_internal/query_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ def keygetter(
elif hasattr(dct, sub_field):
dct = getattr(dct, sub_field)

return dct
except Exception as e:
traceback.print_stack()
print(f"Above error was {e}")
return None
return None

return dct


def parse_lookup(obj: "Mapping[str, Any]", path: str, lookup: str) -> Optional[Any]:
Expand Down Expand Up @@ -123,7 +124,7 @@ def lookup_icontains(
if isinstance(data, str):
return rhs.lower() in data.lower()
if isinstance(data, Mapping):
return rhs.lower() in [k.lower() for k in data.keys()]
return rhs.lower() in [k.lower() for k in data]

return False

Expand Down Expand Up @@ -183,7 +184,6 @@ def lookup_in(
return rhs in data
# TODO: Add a deep Mappingionary matcher
# if isinstance(rhs, Mapping) and isinstance(data, Mapping):
# return rhs.items() not in data.items()
except Exception:
return False
return False
Expand All @@ -205,7 +205,6 @@ def lookup_nin(
return rhs not in data
# TODO: Add a deep Mappingionary matcher
# if isinstance(rhs, Mapping) and isinstance(data, Mapping):
# return rhs.items() not in data.items()
except Exception:
return False
return False
Expand Down Expand Up @@ -246,6 +245,16 @@ def lookup_iregex(
}


class PKRequiredException(Exception):
def __init__(self, *args: object):
return super().__init__("items() require a pk_key exists")


class OpNotFound(ValueError):
def __init__(self, op: str, *args: object):
return super().__init__(f"{op} not in LOOKUP_NAME_MAP")


class QueryList(List[T]):
"""Filter list of object/dictionaries. For small, local datasets.

Expand Down Expand Up @@ -286,17 +295,13 @@ class QueryList(List[T]):

def items(self) -> List[T]:
if self.pk_key is None:
raise Exception("items() require a pk_key exists")
raise PKRequiredException()
return [(getattr(item, self.pk_key), item) for item in self]

def __eq__(
self,
other: object,
# other: Union[
# "QueryList[T]",
# List[Mapping[str, str]],
# List[Mapping[str, int]],
# List[Mapping[str, Union[str, Mapping[str, Union[List[str], str]]]]],
# ],
) -> bool:
data = other
Expand Down Expand Up @@ -330,7 +335,7 @@ def filter_lookup(obj: Any) -> bool:
lhs, op = path.rsplit("__", 1)

if op not in LOOKUP_NAME_MAP:
raise ValueError(f"{op} not in LOOKUP_NAME_MAP")
raise OpNotFound(op=op)
except ValueError:
lhs = path
op = "exact"
Expand Down
5 changes: 4 additions & 1 deletion src/libtmux/_vendor/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ class InvalidVersion(ValueError):
libtmux._vendor.version.InvalidVersion: Invalid version: 'invalid'
"""

def __init__(self, version: str, *args: object):
return super().__init__(f"Invalid version: '{version}'")


class _BaseVersion:
_key: CmpKey
Expand Down Expand Up @@ -195,7 +198,7 @@ def __init__(self, version: str) -> None:
# Validate the version and parse it into pieces
match = self._regex.search(version)
if not match:
raise InvalidVersion(f"Invalid version: '{version}'")
raise InvalidVersion(version=version)

# Store the parsed out pieces of the version
self._version = _Version(
Expand Down
27 changes: 12 additions & 15 deletions src/libtmux/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def show_environment(self) -> Dict[str, Union[bool, str]]:
elif len(_t) == 1:
vars_dict[_t[0]] = True
else:
raise ValueError(f"unexpected variable {_t}")
raise exc.VariableUnpackingError(variable=_t)

return vars_dict

Expand All @@ -172,7 +172,7 @@ def getenv(self, name: str) -> Optional[t.Union[str, bool]]:
str
Value of environment variable
"""
tmux_args: t.Tuple[t.Union[str, int], ...] = tuple()
tmux_args: t.Tuple[t.Union[str, int], ...] = ()

tmux_args += ("show-environment",)
if self._add_option:
Expand All @@ -188,7 +188,7 @@ def getenv(self, name: str) -> Optional[t.Union[str, bool]]:
elif len(_t) == 1:
vars_dict[_t[0]] = True
else:
raise ValueError(f"unexpected variable {_t}")
raise exc.VariableUnpackingError(variable=_t)

return vars_dict.get(name)

Expand Down Expand Up @@ -242,8 +242,8 @@ def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
)
stdout, stderr = self.process.communicate()
returncode = self.process.returncode
except Exception as e:
logger.error(f"Exception for {subprocess.list2cmdline(cmd)}: \n{e}")
except Exception:
logger.exception(f"Exception for {subprocess.list2cmdline(cmd)}")
raise

self.returncode = returncode
Expand Down Expand Up @@ -425,9 +425,10 @@ def has_minimum_version(raises: bool = True) -> bool:
if get_version() < LooseVersion(TMUX_MIN_VERSION):
if raises:
raise exc.VersionTooLow(
"libtmux only supports tmux %s and greater. This system"
" has %s installed. Upgrade your tmux to use libtmux."
% (TMUX_MIN_VERSION, get_version())
"libtmux only supports tmux {} and greater. This system"
" has {} installed. Upgrade your tmux to use libtmux.".format(
TMUX_MIN_VERSION, get_version()
)
)
else:
return False
Expand All @@ -452,15 +453,11 @@ def session_check_name(session_name: t.Optional[str]) -> None:
Invalid session name.
"""
if session_name is None or len(session_name) == 0:
raise exc.BadSessionName("tmux session names may not be empty.")
raise exc.BadSessionName(reason="empty", session_name=session_name)
elif "." in session_name:
raise exc.BadSessionName(
'tmux session name "%s" may not contain periods.', session_name
)
raise exc.BadSessionName(reason="contains periods", session_name=session_name)
elif ":" in session_name:
raise exc.BadSessionName(
'tmux session name "%s" may not contain colons.', session_name
)
raise exc.BadSessionName(reason="contains colons", session_name=session_name)


def handle_option_error(error: str) -> t.Type[exc.OptionError]:
Expand Down
Loading