diff --git a/CHANGES b/CHANGES index 4bb643a2a..481ee22c3 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,14 @@ $ pip install --user --upgrade --pre libtmux Thank you @m1guelperez for `Window.__eq__`! (#505) +### Development + +- ci: Add pydocstyle rule to ruff (#509) + +### Documentation + +- Add docstrings to functions, methods, classes, and packages (#509) + ## libtmux 0.24.1 (2023-11-23) ### Packaging diff --git a/conftest.py b/conftest.py index 299041497..876090be4 100644 --- a/conftest.py +++ b/conftest.py @@ -1,4 +1,4 @@ -"""Conftest.py (root-level) +"""Conftest.py (root-level). We keep this in root pytest fixtures in pytest's doctest plugin to be available, as well as avoiding conftest.py from being included in the wheel, in addition to pytest_plugin @@ -27,6 +27,7 @@ def add_doctest_fixtures( request: pytest.FixtureRequest, doctest_namespace: t.Dict[str, t.Any], ) -> None: + """Configure doctest fixtures for pytest-doctest.""" if isinstance(request._pyfuncitem, DoctestItem) and shutil.which("tmux"): request.getfixturevalue("set_home") doctest_namespace["server"] = request.getfixturevalue("server") @@ -42,6 +43,7 @@ def set_home( monkeypatch: pytest.MonkeyPatch, user_path: pathlib.Path, ) -> None: + """Configure home directory for pytest tests.""" monkeypatch.setenv("HOME", str(user_path)) @@ -51,5 +53,6 @@ def setup( request: pytest.FixtureRequest, config_file: pathlib.Path, ) -> None: + """Configure test fixtures for pytest.""" if USING_ZSH: request.getfixturevalue("zshrc") diff --git a/docs/__init__.py b/docs/__init__.py index e69de29bb..3cea6d1af 100644 --- a/docs/__init__.py +++ b/docs/__init__.py @@ -0,0 +1 @@ +"""Documentation for the libtmux package.""" diff --git a/docs/conf.py b/docs/conf.py index dcb51b2ca..fbc808157 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,5 @@ # flake8: NOQA: E501 +"""Sphinx configuration for libtmux.""" import contextlib import inspect import pathlib @@ -132,7 +133,7 @@ def linkcode_resolve(domain: str, info: t.Dict[str, str]) -> t.Union[None, str]: """ - Determine the URL corresponding to Python object + Determine the URL corresponding to Python object. Notes ----- @@ -202,6 +203,7 @@ def linkcode_resolve(domain: str, info: t.Dict[str, str]) -> t.Union[None, str]: def remove_tabs_js(app: "Sphinx", exc: Exception) -> None: + """Remove tabs.js from _static after build.""" # Fix for sphinx-inline-tabs#18 if app.builder.format == "html" and not exc: tabs_js = pathlib.Path(app.builder.outdir) / "_static" / "tabs.js" @@ -210,4 +212,5 @@ def remove_tabs_js(app: "Sphinx", exc: Exception) -> None: def setup(app: "Sphinx") -> None: + """Configure Sphinx app hooks.""" app.connect("build-finished", remove_tabs_js) diff --git a/pyproject.toml b/pyproject.toml index 2c7d757a9..ecbd633c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -128,6 +128,7 @@ select = [ "TRY", # Trycertatops "PERF", # Perflint "RUF", # Ruff-specific rules + "D", # pydocstyle ] [tool.ruff.isort] @@ -136,6 +137,9 @@ known-first-party = [ ] combine-as-imports = true +[tool.ruff.pydocstyle] +convention = "numpy" + [tool.ruff.per-file-ignores] "*/__init__.py" = ["F401"] diff --git a/src/libtmux/__about__.py b/src/libtmux/__about__.py index 2c5925846..b2594c061 100644 --- a/src/libtmux/__about__.py +++ b/src/libtmux/__about__.py @@ -1,3 +1,4 @@ +"""Metadata package for libtmux.""" __title__ = "libtmux" __package_name__ = "libtmux" __version__ = "0.24.1" diff --git a/src/libtmux/_internal/dataclasses.py b/src/libtmux/_internal/dataclasses.py index aeddfc0f2..1fa53f18e 100644 --- a/src/libtmux/_internal/dataclasses.py +++ b/src/libtmux/_internal/dataclasses.py @@ -13,19 +13,20 @@ class SkipDefaultFieldsReprMixin: - r"""Skip default fields in :func:`~dataclasses.dataclass` - :func:`object representation `. + r"""Skip default fields in :func:`~dataclasses.dataclass` object representation. + + See Also + -------- + :func:`object representation ` Notes ----- - Credit: Pietro Oldrati, 2022-05-08, Unilicense https://stackoverflow.com/a/72161437/1396928 Examples -------- - >>> @dataclasses.dataclass() ... class Item: ... name: str diff --git a/src/libtmux/_internal/query_list.py b/src/libtmux/_internal/query_list.py index 07f10ef6a..1113a0151 100644 --- a/src/libtmux/_internal/query_list.py +++ b/src/libtmux/_internal/query_list.py @@ -19,7 +19,7 @@ def __call__( data: t.Union[str, t.List[str], "Mapping[str, str]"], rhs: t.Union[str, t.List[str], "Mapping[str, str]", "re.Pattern[str]"], ) -> bool: - """Callback for :class:`QueryList` filtering operators.""" + """Return callback for :class:`QueryList` filtering operators.""" ... @@ -29,7 +29,7 @@ def __call__( class MultipleObjectsReturned(Exception): - """The requested object does not exist""" + """The requested object does not exist.""" class ObjectDoesNotExist(Exception): @@ -40,7 +40,7 @@ def keygetter( obj: "Mapping[str, t.Any]", path: str, ) -> t.Union[None, t.Any, str, t.List[str], "Mapping[str, str]"]: - """obj, "foods__breakfast", obj['foods']['breakfast'] + """obj, "foods__breakfast", obj['foods']['breakfast']. >>> keygetter({ "foods": { "breakfast": "cereal" } }, "foods__breakfast") 'cereal' diff --git a/src/libtmux/_vendor/version.py b/src/libtmux/_vendor/version.py index 0aac18ff4..edeba78a3 100644 --- a/src/libtmux/_vendor/version.py +++ b/src/libtmux/_vendor/version.py @@ -2,7 +2,8 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -""" +"""Backport of the ``packaging.version`` module from Python 3.8. + .. testsetup:: from packaging.version import parse, Version @@ -159,7 +160,7 @@ def __ne__(self, other: object) -> bool: class Version(_BaseVersion): - """This class abstracts handling of a project's versions. + """Class abstracts handling of a project's versions. A :class:`Version` instance is comparison aware and can be compared and sorted using the standard Python interfaces. @@ -194,7 +195,6 @@ def __init__(self, version: str) -> None: If the ``version`` does not conform to PEP 440 in any way then this exception will be raised. """ - # Validate the version and parse it into pieces match = self._regex.search(version) if not match: @@ -223,7 +223,7 @@ def __init__(self, version: str) -> None: ) def __repr__(self) -> str: - """A representation of the Version that shows all internal state. + """Return representation of the Version that shows all internal state. >>> Version('1.0.0') @@ -231,7 +231,7 @@ def __repr__(self) -> str: return f"" def __str__(self) -> str: - """A string representation of the version that can be rounded-tripped. + """Return string representation of the version that can be rounded-tripped. >>> str(Version("1.0a5")) '1.0a5' @@ -492,9 +492,7 @@ def _parse_letter_version( def _parse_local_version(local: str) -> Optional[LocalType]: - """ - Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). - """ + """Take a string like abc.1.twelve and turns it into ("abc", 1, "twelve").""" if local is not None: return tuple( part.lower() if not part.isdigit() else int(part) diff --git a/src/libtmux/common.py b/src/libtmux/common.py index beae1fed8..81e5cb514 100644 --- a/src/libtmux/common.py +++ b/src/libtmux/common.py @@ -1,5 +1,5 @@ # flake8: NOQA: W605 -"""Helper methods and mixins. +"""Helper methods and mixins for libtmux. libtmux.common ~~~~~~~~~~~~~~ @@ -36,11 +36,7 @@ class EnvironmentMixin: - - """ - Mixin class for managing session and server level environment variables in - tmux. - """ + """Mixin for manager session and server level environment variables in tmux.""" _add_option = None @@ -194,13 +190,11 @@ def getenv(self, name: str) -> Optional[t.Union[str, bool]]: class tmux_cmd: - """ :term:`tmux(1)` command via :py:mod:`subprocess`. Examples -------- - .. code-block:: python proc = tmux_cmd('new-session', '-s%' % 'my session') @@ -220,7 +214,6 @@ class tmux_cmd: Notes ----- - .. versionchanged:: 0.8 Renamed from ``tmux`` to ``tmux_cmd``. """ @@ -413,7 +406,6 @@ def has_minimum_version(raises: bool = True) -> bool: Notes ----- - .. versionchanged:: 0.7.0 No longer returns version, returns True or False @@ -436,8 +428,7 @@ def has_minimum_version(raises: bool = True) -> bool: def session_check_name(session_name: t.Optional[str]) -> None: - """ - Raises exception session name invalid, modeled after tmux function. + """Raise exception session name invalid, modeled after tmux function. tmux(1) session names may not be empty, or include periods or colons. These delimiters are reserved for noting session, window and pane. @@ -461,7 +452,7 @@ def session_check_name(session_name: t.Optional[str]) -> None: def handle_option_error(error: str) -> t.Type[exc.OptionError]: - """Raises exception if error in option command found. + """Raise exception if error in option command found. In tmux 3.0, show-option and show-window-option return invalid option instead of unknown option. See https://github.com/tmux/tmux/blob/3.0/cmd-show-options.c. diff --git a/src/libtmux/exc.py b/src/libtmux/exc.py index 0023cb3eb..bf35a73f4 100644 --- a/src/libtmux/exc.py +++ b/src/libtmux/exc.py @@ -13,17 +13,14 @@ class LibTmuxException(Exception): - """Base Exception for libtmux Errors.""" class TmuxSessionExists(LibTmuxException): - """Session does not exist in the server.""" class TmuxCommandNotFound(LibTmuxException): - """Application binary for tmux not found.""" @@ -47,12 +44,10 @@ def __init__( class VersionTooLow(LibTmuxException): - """Raised if tmux below the minimum version to use libtmux.""" class BadSessionName(LibTmuxException): - """Disallowed session name for tmux (empty, contains periods or colons).""" def __init__( @@ -65,17 +60,14 @@ def __init__( class OptionError(LibTmuxException): - """Root error for any error involving invalid, ambiguous or bad options.""" class UnknownOption(OptionError): - """Option unknown to tmux show-option(s) or show-window-option(s).""" class UnknownColorOption(UnknownOption): - """Unknown color option.""" def __init__(self, *args: object): @@ -83,34 +75,30 @@ def __init__(self, *args: object): class InvalidOption(OptionError): - """Option invalid to tmux, introduced in tmux v2.4.""" class AmbiguousOption(OptionError): - """Option that could potentially match more than one.""" class WaitTimeout(LibTmuxException): - - """Function timed out without meeting condition""" + """Function timed out without meeting condition.""" class VariableUnpackingError(LibTmuxException): - - """Error unpacking variable""" + """Error unpacking variable.""" def __init__(self, variable: t.Optional[t.Any] = None, *args: object): return super().__init__(f"Unexpected variable: {variable!s}") class PaneError(LibTmuxException): - """Any type of pane related error""" + """Any type of pane related error.""" class PaneNotFound(PaneError): - """Pane not found""" + """Pane not found.""" def __init__(self, pane_id: t.Optional[str] = None, *args: object): if pane_id is not None: @@ -119,29 +107,25 @@ def __init__(self, pane_id: t.Optional[str] = None, *args: object): class WindowError(LibTmuxException): - - """Any type of window related error""" + """Any type of window related error.""" class MultipleActiveWindows(WindowError): - - """Multiple active windows""" + """Multiple active windows.""" def __init__(self, count: int, *args: object): return super().__init__(f"Multiple active windows: {count} found") class NoActiveWindow(WindowError): - - """No active window found""" + """No active window found.""" def __init__(self, *args: object): return super().__init__("No active windows found") class NoWindowsExist(WindowError): - - """No windows exist for object""" + """No windows exist for object.""" def __init__(self, *args: object): return super().__init__("No windows exist for object") diff --git a/src/libtmux/neo.py b/src/libtmux/neo.py index 65b2a83a7..3ad975e7d 100644 --- a/src/libtmux/neo.py +++ b/src/libtmux/neo.py @@ -1,3 +1,4 @@ +"""Tools for hydrating tmux data into python dataclass objects.""" import dataclasses import logging import typing as t @@ -31,6 +32,8 @@ @dataclasses.dataclass() class Obj: + """Dataclass of generic tmux object.""" + server: "Server" active_window_index: t.Union[str, None] = None @@ -180,6 +183,7 @@ def fetch_objs( list_cmd: "ListCmd", list_extra_args: "t.Optional[ListExtraArgs]" = None, ) -> OutputsRaw: + """Fetch a listing of raw data from a tmux command.""" formats = list(Obj.__dataclass_fields__.keys()) cmd_args: t.List[t.Union[str, int]] = [] @@ -227,6 +231,7 @@ def fetch_obj( list_cmd: "ListCmd" = "list-panes", list_extra_args: "t.Optional[ListExtraArgs]" = None, ) -> OutputRaw: + """Fetch raw data from tmux command.""" obj_formatters_filtered = fetch_objs( server=server, list_cmd=list_cmd, list_extra_args=list_extra_args ) diff --git a/src/libtmux/pane.py b/src/libtmux/pane.py index 23c3f434c..c3c49a4f3 100644 --- a/src/libtmux/pane.py +++ b/src/libtmux/pane.py @@ -53,13 +53,11 @@ class Pane(Obj): Notes ----- - .. versionchanged:: 0.8 Renamed from ``.tmux`` to ``.cmd``. References ---------- - .. [pane_manual] tmux pane. openbsd manpage for TMUX(1). "Each window displayed by tmux may be split into one or more panes; each pane takes up a certain area of the display and is @@ -72,11 +70,13 @@ class Pane(Obj): server: "Server" def refresh(self) -> None: + """Refresh pane attributes from tmux.""" assert isinstance(self.pane_id, str) return super()._refresh(obj_key="pane_id", obj_id=self.pane_id) @classmethod def from_pane_id(cls, server: "Server", pane_id: str) -> "Pane": + """Create Pane from existing pane_id.""" pane = fetch_obj( obj_key="pane_id", obj_id=pane_id, @@ -91,6 +91,7 @@ def from_pane_id(cls, server: "Server", pane_id: str) -> "Pane": # @property def window(self) -> "Window": + """Parent window of pane.""" assert isinstance(self.window_id, str) from libtmux.window import Window @@ -98,6 +99,7 @@ def window(self) -> "Window": @property def session(self) -> "Session": + """Parent session of pane.""" return self.window.session """ @@ -370,7 +372,6 @@ def clear(self) -> "Pane": def reset(self) -> "Pane": """Reset and clear pane history.""" - self.cmd("send-keys", r"-R \; clear-history") return self @@ -378,11 +379,13 @@ def reset(self) -> "Pane": # Dunder # def __eq__(self, other: object) -> bool: + """Equal operator for :class:`Pane` object.""" if isinstance(other, Pane): return self.pane_id == other.pane_id return False def __repr__(self) -> str: + """Representation of :class:`Pane` object.""" return f"{self.__class__.__name__}({self.pane_id} {self.window})" # @@ -390,7 +393,7 @@ def __repr__(self) -> str: # @property def id(self) -> t.Optional[str]: - """Alias of :attr:`Pane.pane_id` + """Alias of :attr:`Pane.pane_id`. >>> pane.id '%1' @@ -402,7 +405,7 @@ def id(self) -> t.Optional[str]: @property def index(self) -> t.Optional[str]: - """Alias of :attr:`Pane.pane_index` + """Alias of :attr:`Pane.pane_index`. >>> pane.index '0' @@ -414,7 +417,7 @@ def index(self) -> t.Optional[str]: @property def height(self) -> t.Optional[str]: - """Alias of :attr:`Pane.pane_height` + """Alias of :attr:`Pane.pane_height`. >>> pane.height.isdigit() True @@ -426,7 +429,7 @@ def height(self) -> t.Optional[str]: @property def width(self) -> t.Optional[str]: - """Alias of :attr:`Pane.pane_width` + """Alias of :attr:`Pane.pane_width`. >>> pane.width.isdigit() True @@ -437,18 +440,28 @@ def width(self) -> t.Optional[str]: return self.pane_width # - # Legacy + # Legacy: Redundant stuff we want to remove # def get(self, key: str, default: t.Optional[t.Any] = None) -> t.Any: - """ + """Return key-based lookup. Deprecated by attributes. + .. deprecated:: 0.16 + + Deprecated by attribute lookup.e.g. ``pane['window_name']`` is now + accessed via ``pane.window_name``. + """ warnings.warn("Pane.get() is deprecated", stacklevel=2) return getattr(self, key, default) def __getitem__(self, key: str) -> t.Any: - """ + """Return item lookup by key. Deprecated in favor of attributes. + .. deprecated:: 0.16 + + Deprecated in favor of attributes. e.g. ``pane['window_name']`` is now + accessed via ``pane.window_name``. + """ warnings.warn(f"Item lookups, e.g. pane['{key}'] is deprecated", stacklevel=2) return getattr(self, key) diff --git a/src/libtmux/pytest_plugin.py b/src/libtmux/pytest_plugin.py index 0cfb1c594..4fabf8174 100644 --- a/src/libtmux/pytest_plugin.py +++ b/src/libtmux/pytest_plugin.py @@ -1,3 +1,4 @@ +"""libtmux pytest plugin.""" import contextlib import getpass import logging @@ -26,13 +27,13 @@ def home_path(tmp_path_factory: pytest.TempPathFactory) -> pathlib.Path: @pytest.fixture(scope="session") def home_user_name() -> str: - """Default username to set for :func:`user_path` fixture.""" + """Return default username to set for :func:`user_path` fixture.""" return getpass.getuser() @pytest.fixture(scope="session") def user_path(home_path: pathlib.Path, home_user_name: str) -> pathlib.Path: - """Default temporary user directory. + """Ensure and return temporary user directory. Used by: :func:`config_file`, :func:`zshrc` @@ -46,7 +47,7 @@ def user_path(home_path: pathlib.Path, home_user_name: str) -> pathlib.Path: @pytest.mark.skipif(USING_ZSH, reason="Using ZSH") @pytest.fixture(scope="session") def zshrc(user_path: pathlib.Path) -> pathlib.Path: - """This quiets ZSH default message. + """Suppress ZSH default message. Needs a startup file .zshenv, .zprofile, .zshrc, .zlogin. """ @@ -57,7 +58,7 @@ def zshrc(user_path: pathlib.Path) -> pathlib.Path: @pytest.fixture(scope="session") def config_file(user_path: pathlib.Path) -> pathlib.Path: - """Default `.tmux.conf` configuration. + """Return fixture for ``.tmux.conf`` configuration. - ``base-index -g 1`` @@ -109,7 +110,7 @@ def server( monkeypatch: pytest.MonkeyPatch, config_file: pathlib.Path, ) -> Server: - """Returns a new, temporary :class:`libtmux.Server` + """Return new, temporary :class:`libtmux.Server`. >>> from libtmux.server import Server @@ -146,7 +147,7 @@ def fin() -> None: @pytest.fixture(scope="function") def session_params() -> t.Dict[str, t.Any]: - """Returns a new, temporary :class:`libtmux.Session` + """Return new, temporary :class:`libtmux.Session`. >>> import pytest >>> from libtmux.session import Session @@ -186,7 +187,7 @@ def session_params() -> t.Dict[str, t.Any]: def session( request: pytest.FixtureRequest, session_params: t.Dict[str, t.Any], server: Server ) -> "Session": - """Returns a new, temporary :class:`libtmux.Session` + """Return new, temporary :class:`libtmux.Session`. >>> from libtmux.session import Session diff --git a/src/libtmux/server.py b/src/libtmux/server.py index da0f8db6e..46ffc1363 100644 --- a/src/libtmux/server.py +++ b/src/libtmux/server.py @@ -193,7 +193,6 @@ def cmd(self, *args: t.Any, **kwargs: t.Any) -> tmux_cmd: Renamed from ``.tmux`` to ``.cmd``. """ - cmd_args: t.List[t.Union[str, int]] = list(args) if self.socket_name: cmd_args.insert(0, f"-L{self.socket_name}") @@ -560,6 +559,7 @@ def panes(self) -> QueryList[Pane]: # type:ignore # Dunder # def __eq__(self, other: object) -> bool: + """Equal operator for :class:`Server` object.""" if isinstance(other, Server): return ( self.socket_name == other.socket_name @@ -568,6 +568,7 @@ def __eq__(self, other: object) -> bool: return False def __repr__(self) -> str: + """Representation of :class:`Server` object.""" if self.socket_name is not None: return ( f"{self.__class__.__name__}" @@ -590,6 +591,9 @@ def _list_panes(self) -> t.List[PaneDict]: :class:`util.tmux_cmd` which wraps :py:class:`subprocess.Popen`. .. deprecated:: 0.16 + + Deprecated in favor of :attr:`.panes`. + """ warnings.warn("Server._list_panes() is deprecated", stacklevel=2) return [p.__dict__ for p in self.panes] @@ -603,21 +607,32 @@ def _update_panes(self) -> "Server": :class:`Server` .. deprecated:: 0.16 + + Deprecated in favor of :attr:`.panes` and returning ``self``. + """ warnings.warn("Server._update_panes() is deprecated", stacklevel=2) self._list_panes() return self def get_by_id(self, id: str) -> t.Optional[Session]: - """ + """Return session by id. Deprecated in favor of :meth:`.sessions.get()`. + .. deprecated:: 0.16 + + Deprecated by :meth:`.sessions.get()`. + """ warnings.warn("Server.get_by_id() is deprecated", stacklevel=2) return self.sessions.get(session_id=id, default=None) def where(self, kwargs: t.Dict[str, t.Any]) -> t.List[Session]: - """ + """Filter through sessions, return list of :class:`Session`. + .. deprecated:: 0.16 + + Deprecated by :meth:`.session.filter()`. + """ warnings.warn("Server.find_where() is deprecated", stacklevel=2) try: @@ -626,8 +641,12 @@ def where(self, kwargs: t.Dict[str, t.Any]) -> t.List[Session]: return [] def find_where(self, kwargs: t.Dict[str, t.Any]) -> t.Optional[Session]: - """ + """Filter through sessions, return first :class:`Session`. + .. deprecated:: 0.16 + + Slated to be removed in favor of :meth:`.sessions.get()`. + """ warnings.warn("Server.find_where() is deprecated", stacklevel=2) return self.sessions.get(default=None, **kwargs) @@ -641,6 +660,9 @@ def _list_windows(self) -> t.List[WindowDict]: :class:`common.tmux_cmd` which wraps :py:class:`subprocess.Popen`. .. deprecated:: 0.16 + + Slated to be removed in favor of :attr:`.windows`. + """ warnings.warn("Server._list_windows() is deprecated", stacklevel=2) return [w.__dict__ for w in self.windows] @@ -649,6 +671,9 @@ def _update_windows(self) -> "Server": """Update internal window data and return ``self`` for chainability. .. deprecated:: 0.16 + + Deprecated in favor of :attr:`.windows` and returning ``self``. + """ warnings.warn("Server._update_windows() is deprecated", stacklevel=2) self._list_windows() @@ -659,13 +684,19 @@ def _sessions(self) -> t.List[SessionDict]: """Property / alias to return :meth:`~._list_sessions`. .. deprecated:: 0.16 + + Slated to be removed in favor of :attr:`.sessions`. + """ warnings.warn("Server._sessions is deprecated", stacklevel=2) return self._list_sessions() def _list_sessions(self) -> t.List["SessionDict"]: - """ + """Return list of session object dictionaries. + .. deprecated:: 0.16 + + Slated to be removed in favor of :attr:`.sessions`. """ warnings.warn("Server._list_sessions() is deprecated", stacklevel=2) return [s.__dict__ for s in self.sessions] @@ -675,6 +706,8 @@ def list_sessions(self) -> t.List[Session]: .. deprecated:: 0.16 + Slated to be removed in favor of :attr:`.sessions`. + Returns ------- list of :class:`Session` @@ -684,9 +717,12 @@ def list_sessions(self) -> t.List[Session]: @property def children(self) -> QueryList["Session"]: # type:ignore - """Was used by TmuxRelationalObject (but that's longer used in this class) + """Was used by TmuxRelationalObject (but that's longer used in this class). .. deprecated:: 0.16 + + Slated to be removed in favor of :attr:`.sessions`. + """ warnings.warn("Server.children is deprecated", stacklevel=2) return self.sessions diff --git a/src/libtmux/session.py b/src/libtmux/session.py index fd0285bee..4b9cdece7 100644 --- a/src/libtmux/session.py +++ b/src/libtmux/session.py @@ -75,6 +75,7 @@ class Session(Obj, EnvironmentMixin): server: "Server" def refresh(self) -> None: + """Refresh session attributes from tmux.""" assert isinstance(self.session_id, str) return super()._refresh( obj_key="session_id", obj_id=self.session_id, list_cmd="list-sessions" @@ -82,6 +83,7 @@ def refresh(self) -> None: @classmethod def from_session_id(cls, server: "Server", session_id: str) -> "Session": + """Create Session from existing session_id.""" session = fetch_obj( obj_key="session_id", obj_id=session_id, @@ -290,7 +292,6 @@ def show_option( Test and return True/False for on/off string. """ - tmux_args: t.Tuple[str, ...] = () if _global: @@ -337,7 +338,6 @@ def select_window(self, target_window: t.Union[str, int]) -> "Window": assure ``-l``, ``-n``, ``-p`` work. """ - # Note that we also provide the session ID here, since cmd() # will not automatically add it as there is already a '-t' # argument provided. @@ -355,9 +355,7 @@ def select_window(self, target_window: t.Union[str, int]) -> "Window": # @property def attached_window(self) -> "Window": - """ - Return active :class:`Window` object. - """ + """Return active :class:`Window` object.""" active_windows = [ window for window in self.windows if window.window_active == "1" ] @@ -383,7 +381,6 @@ def attach_session(self) -> "Session": def kill_session(self) -> None: """``$ tmux kill-session``.""" - proc = self.cmd("kill-session", "-t%s" % self.session_id) if proc.stderr: @@ -524,7 +521,7 @@ def new_window( ) def kill_window(self, target_window: t.Optional[str] = None) -> None: - """Close a tmux window, and all panes inside it, ``$ tmux kill-window`` + """Close a tmux window, and all panes inside it, ``$ tmux kill-window``. Kill the current window or the window at ``target-window``. removing it from any sessions to which it is linked. @@ -534,7 +531,6 @@ def kill_window(self, target_window: t.Optional[str] = None) -> None: target_window : str, optional window to kill """ - if target_window: if isinstance(target_window, int): target = "-t%s:%d" % (self.window_name, target_window) @@ -552,18 +548,19 @@ def kill_window(self, target_window: t.Optional[str] = None) -> None: @property def attached_pane(self) -> t.Optional["Pane"]: """Return active :class:`Pane` object.""" - return self.attached_window.attached_pane # # Dunder # def __eq__(self, other: object) -> bool: + """Equal operator for :class:`Session` object.""" if isinstance(other, Session): return self.session_id == other.session_id return False def __repr__(self) -> str: + """Representation of :class:`Session` object.""" return f"{self.__class__.__name__}({self.session_id} {self.session_name})" # @@ -571,7 +568,7 @@ def __repr__(self) -> str: # @property def id(self) -> t.Optional[str]: - """Alias of :attr:`Session.session_id` + """Alias of :attr:`Session.session_id`. >>> session.id '$1' @@ -583,7 +580,7 @@ def id(self) -> t.Optional[str]: @property def name(self) -> t.Optional[str]: - """Alias of :attr:`Session.session_name` + """Alias of :attr:`Session.session_name`. >>> session.name 'libtmux_...' @@ -597,15 +594,25 @@ def name(self) -> t.Optional[str]: # Legacy: Redundant stuff we want to remove # def get(self, key: str, default: t.Optional[t.Any] = None) -> t.Any: - """ + """Return key-based lookup. Deprecated by attributes. + .. deprecated:: 0.16 + + Deprecated by attribute lookup.e.g. ``session['session_name']`` is now + accessed via ``session.session_name``. + """ warnings.warn("Session.get() is deprecated", stacklevel=2) return getattr(self, key, default) def __getitem__(self, key: str) -> t.Any: - """ + """Return item lookup by key. Deprecated in favor of attributes. + .. deprecated:: 0.16 + + Deprecated in favor of attributes. e.g. ``session['session_name']`` is now + accessed via ``session.session_name``. + """ warnings.warn( f"Item lookups, e.g. session['{key}'] is deprecated", stacklevel=2 @@ -613,15 +620,23 @@ def __getitem__(self, key: str) -> t.Any: return getattr(self, key) def get_by_id(self, id: str) -> t.Optional[Window]: - """ + """Return window by id. Deprecated in favor of :meth:`.windows.get()`. + .. deprecated:: 0.16 + + Deprecated by :meth:`.windows.get()`. + """ warnings.warn("Session.get_by_id() is deprecated", stacklevel=2) return self.windows.get(window_id=id, default=None) def where(self, kwargs: t.Dict[str, t.Any]) -> t.List[Window]: - """ + """Filter through windows, return list of :class:`Window`. + .. deprecated:: 0.16 + + Deprecated by :meth:`.windows.filter()`. + """ warnings.warn("Session.where() is deprecated", stacklevel=2) try: @@ -630,15 +645,23 @@ def where(self, kwargs: t.Dict[str, t.Any]) -> t.List[Window]: return [] def find_where(self, kwargs: t.Dict[str, t.Any]) -> t.Optional[Window]: - """ + """Filter through windows, return first :class:`Window`. + .. deprecated:: 0.16 + + Slated to be removed in favor of :meth:`.windows.get()`. + """ warnings.warn("Session.find_where() is deprecated", stacklevel=2) return self.windows.get(default=None, **kwargs) def _list_windows(self) -> t.List["WindowDict"]: - """ + """Return list of windows (deprecated in favor of :meth:`.windows`). + .. deprecated:: 0.16 + + Slated to be removed in favor of :attr:`.windows`. + """ warnings.warn("Session._list_windows() is deprecated", stacklevel=2) return [w.__dict__ for w in self.windows] @@ -648,6 +671,9 @@ def _windows(self) -> t.List["WindowDict"]: """Property / alias to return :meth:`Session._list_windows`. .. deprecated:: 0.16 + + Slated to be removed in favor of :attr:`.windows`. + """ warnings.warn("Session._windows is deprecated", stacklevel=2) return self._list_windows() @@ -656,15 +682,21 @@ def list_windows(self) -> t.List["Window"]: """Return a list of :class:`Window` from the ``tmux(1)`` session. .. deprecated:: 0.16 + + Slated to be removed in favor of :attr:`.windows`. + """ warnings.warn("Session.list_windows() is deprecated", stacklevel=2) return self.windows @property def children(self) -> QueryList["Window"]: # type:ignore - """Was used by TmuxRelationalObject (but that's longer used in this class) + """Was used by TmuxRelationalObject (but that's longer used in this class). .. deprecated:: 0.16 + + Slated to be removed in favor of :attr:`.windows`. + """ warnings.warn("Session.children is deprecated", stacklevel=2) return self.windows diff --git a/src/libtmux/test.py b/src/libtmux/test.py index c8458f63c..08747d7b5 100644 --- a/src/libtmux/test.py +++ b/src/libtmux/test.py @@ -25,6 +25,8 @@ class RandomStrSequence: + """Factory to generate random string.""" + def __init__( self, characters: str = "abcdefghijklmnopqrstuvwxyz0123456789_" ) -> None: @@ -41,9 +43,11 @@ def __init__( self.characters: str = characters def __iter__(self) -> "RandomStrSequence": + """Return self.""" return self def __next__(self) -> str: + """Return next random string.""" return "".join(random.sample(self.characters, k=8)) @@ -209,7 +213,6 @@ def temp_session( ... session.new_window(window_name='my window') Window(@3 2:my window, Session($... ...)) """ - if "session_name" in kwargs: session_name = kwargs.pop("session_name") else: @@ -309,6 +312,7 @@ def __init__(self) -> None: self._reset: t.Dict[str, str] = {} def set(self, envvar: str, value: str) -> None: + """Set environment variable.""" if envvar not in self._environ: self._unset.add(envvar) else: @@ -316,11 +320,13 @@ def set(self, envvar: str, value: str) -> None: self._environ[envvar] = value def unset(self, envvar: str) -> None: + """Unset environment variable.""" if envvar in self._environ: self._reset[envvar] = self._environ[envvar] del self._environ[envvar] def __enter__(self) -> "EnvironmentVarGuard": + """Return context for for context manager.""" return self def __exit__( @@ -329,6 +335,7 @@ def __exit__( exc_value: t.Optional[BaseException], exc_tb: t.Optional[types.TracebackType], ) -> None: + """Cleanup to run after context manager finishes.""" for envvar, value in self._reset.items(): self._environ[envvar] = value for unset in self._unset: diff --git a/src/libtmux/window.py b/src/libtmux/window.py index d52b00f2a..2d19eabc6 100644 --- a/src/libtmux/window.py +++ b/src/libtmux/window.py @@ -80,6 +80,7 @@ class Window(Obj): server: "Server" def refresh(self) -> None: + """Refresh window attributes from tmux.""" assert isinstance(self.window_id, str) return super()._refresh( obj_key="window_id", @@ -89,6 +90,7 @@ def refresh(self) -> None: @classmethod def from_window_id(cls, server: "Server", window_id: str) -> "Window": + """Create Window from existing window_id.""" window = fetch_obj( obj_key="window_id", obj_id=window_id, @@ -100,6 +102,7 @@ def from_window_id(cls, server: "Server", window_id: str) -> "Window": @property def session(self) -> "Session": + """Parent session of window.""" assert isinstance(self.session_id, str) from libtmux.session import Session @@ -159,7 +162,6 @@ def select_pane(self, target_pane: t.Union[str, int]) -> t.Optional["Pane"]: ------ :class:`Pane` """ - if target_pane in ["-l", "-U", "-D", "-L", "-R"]: proc = self.cmd("select-pane", "-t%s" % self.window_id, target_pane) else: @@ -210,7 +212,6 @@ def split_window( Notes ----- - :term:`tmux(1)` will move window to the new pane if the ``split-window`` target is off screen. tmux handles the ``-d`` the same way as ``new-window`` and ``attach`` in @@ -284,7 +285,9 @@ def last_pane(self) -> t.Optional["Pane"]: return self.select_pane("-l") def select_layout(self, layout: t.Optional[str] = None) -> "Window": - """Wrapper for ``$ tmux select-layout ``. + """Select layout for window. + + Wrapper for ``$ tmux select-layout ``. Parameters ---------- @@ -325,8 +328,9 @@ def select_layout(self, layout: t.Optional[str] = None) -> "Window": return self def set_window_option(self, option: str, value: t.Union[int, str]) -> "Window": - """ - Wrapper for ``$ tmux set-window-option