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

Stricter code quality rules (via ruff) #488

Merged
merged 13 commits into from Aug 20, 2023
8 changes: 8 additions & 0 deletions CHANGES
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
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
@@ -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
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
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
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
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