diff --git a/CHANGES b/CHANGES index 502d46c81..0bb3419d3 100644 --- a/CHANGES +++ b/CHANGES @@ -32,7 +32,15 @@ $ uvx --from 'libtmux' --prerelease allow python -_Future release notes will be placed here_ +_Upcoming changes will be written here._ + +### Documentation (#612) + +- Normalize docs headings and Sphinx module directives to fix anchor and index generation issues. +- Tweak Sphinx type-hints configuration to avoid RST indentation conflicts and suppress forward-reference warnings. +- Refresh docstrings and cross-references (pane/window APIs, environment helpers, pytest plugin) for clearer return types and stable anchors. +- Fix incorrect return type annotations for `capture_pane()` and `display_message()` methods + (changed from `str | list[str]` to `list[str]` - the methods always return a list). ## libtmux 0.50.0 (2025-11-30) @@ -55,7 +63,7 @@ unified, typed API for managing tmux options and hooks across all object types. ### What's New -#### Unified Options API (#516) +### Unified Options API (#516) All tmux objects now share a consistent options interface through {class}`~options.OptionsMixin`: @@ -104,7 +112,7 @@ window.unset_option('automatic-rename') | `suppress_warnings` | `-q` | Suppress warnings | | `append` | `-a` | Append to existing value | -#### Hook Management (#516) +### Hook Management (#516) New {class}`~hooks.HooksMixin` provides programmatic control over tmux hooks: @@ -151,7 +159,7 @@ session.set_hooks('session-renamed', { | `run_hook(hook)` | Run a hook immediately | | `set_hooks(hook, values)` | Set multiple indexed hooks at once | -#### SparseArray for Indexed Options (#516) +### SparseArray for Indexed Options (#516) tmux uses sparse indexed arrays for options like `command-alias[0]`, `command-alias[99]`, `terminal-features[0]`. Python lists can't represent @@ -173,7 +181,7 @@ gaps in indices, so libtmux introduces {class}`~_internal.sparse_array.SparseArr ['first', 'ninety-ninth'] ``` -#### New Constants (#516) +### New Constants (#516) - {class}`~constants.OptionScope` enum: `Server`, `Session`, `Window`, `Pane` - `OPTION_SCOPE_FLAG_MAP`: Maps scope to tmux flags (`-s`, `-w`, `-p`) @@ -181,7 +189,7 @@ gaps in indices, so libtmux introduces {class}`~_internal.sparse_array.SparseArr ### Breaking changes -#### Deprecated Window methods (#516) +### Deprecated Window methods (#516) The following methods are deprecated and will be removed in a future release: @@ -214,7 +222,7 @@ window.set_option('automatic-rename', True) ### Breaking Changes -#### tmux 1.8 to 3.1c support removed (#608) +### tmux 1.8 to 3.1c support removed (#608) Support for tmux versions below 3.2a has been removed. This completes the deprecation announced in v0.48.0. @@ -228,7 +236,7 @@ deprecation announced in v0.48.0. ### Breaking Changes -#### Minimum tmux version bumped to 3.2+ (606) +### Minimum tmux version bumped to 3.2+ (606) tmux versions below 3.2a are now deprecated. libtmux 0.48.0 will be the last version to support tmux below 3.2. This is to ensure support for hooks, options, @@ -243,7 +251,7 @@ release. Set `LIBTMUX_SUPPRESS_VERSION_WARNING=1` to suppress the warning. ### What's new -#### tmux 3.6 support (#607) +### tmux 3.6 support (#607) Added tmux 3.6 to test grid and `TMUX_MAX_VERSION` 3.4 -> 3.6. @@ -288,7 +296,7 @@ be a few months in waiting (watchers / snapshots are in development in #587). ### Breaking -#### Imports removed from libtmux.test (#580) +### Imports removed from libtmux.test (#580) Root-level of imports from `libtmux.test` are no longer possible. @@ -324,7 +332,7 @@ from libtmux.test.constants import ( ### Development -#### Test helpers: Increased coverage (#580) +### Test helpers: Increased coverage (#580) Several improvements to the test helper modules: @@ -338,7 +346,7 @@ Several improvements to the test helper modules: ### Breaking Changes -#### Test helpers: Refactor +### Test helpers: Refactor Test helper functionality has been split into focused modules (#578): @@ -403,7 +411,7 @@ from libtmux.test.temporary import temp_session, temp_window ### New Features -#### Context Managers support (#566) +### Context Managers support (#566) Added context manager support for all major object types: @@ -430,7 +438,7 @@ This makes it easier to write clean, safe code that properly cleans up tmux reso ### New Features -#### Server Initialization Callbacks +### Server Initialization Callbacks Server now accepts 2 new optional params, `socket_name_factory` and `on_init` callbacks (#565): @@ -439,7 +447,7 @@ Server now accepts 2 new optional params, `socket_name_factory` and `on_init` ca - Useful for creating multiple servers with unique names and tracking server instances - Socket name factory is tried after socket_name, maintaining backward compatibility -#### New test fixture: `TestServer` +### New test fixture: `TestServer` Add `TestServer` pytest fixture for creating temporary tmux servers (#565): @@ -493,7 +501,7 @@ Add `TestServer` pytest fixture for creating temporary tmux servers (#565): ### Development -#### chore: Implement PEP 563 deferred annotation resolution (#555) +### chore: Implement PEP 563 deferred annotation resolution (#555) - Add `from __future__ import annotations` to defer annotation resolution and reduce unnecessary runtime computations during type checking. - Enable Ruff checks for PEP-compliant annotations: @@ -549,13 +557,13 @@ _Maintenance only, no bug fixes or new features_ ### Breaking changes -#### Project and package management: poetry to uv (#547) +### Project and package management: poetry to uv (#547) [uv] is the new package and project manager for the project, replacing Poetry. [uv]: https://github.com/astral-sh/uv -#### Build system: poetry to hatchling (#547) +### Build system: poetry to hatchling (#547) [Build system] moved from [poetry] to [hatchling]. @@ -569,8 +577,6 @@ _Maintenance only, no bug fixes or new features_ via [ruff 0.4.2](https://github.com/astral-sh/ruff/blob/v0.4.2/CHANGELOG.md). -[uv]: https://github.com/astral-sh/uv - ### Documentation - Fix docstrings in `query_list` for `MultipleObjectsReturned` and @@ -659,7 +665,7 @@ _Maintenance only, no bug fixes or new features_ ### Breaking changes -#### Command target change (#535) +### Command target change (#535) Commands: All `cmd()` methods using custom or overridden targets must use the keyword argument `target`. This avoids entanglement with inner shell values that include `-t` for @@ -674,7 +680,7 @@ other purposes. These methods include: ### Breaking changes -#### Improved new sessions (#532) +### Improved new sessions (#532) - `Session.new_window()`: @@ -686,7 +692,7 @@ other purposes. These methods include: [PEP 3102]: https://www.python.org/dev/peps/pep-3102/ -#### Improved window splitting (#532) +### Improved window splitting (#532) - `Window.split_window()` to {meth}`Window.split()` @@ -701,7 +707,7 @@ other purposes. These methods include: - Learned `zoom` -#### Tweak: Pane position (#532) +### Tweak: Pane position (#532) It's now possible to retrieve the position of a pane in a window via a `bool` helper:: @@ -811,11 +817,11 @@ _Maintenance only, no bug fixes or new features_ ## libtmux 0.29.0 (2024-02-16) -#### Fixes +### Fixes - Use {exc}`DeprecationWarning` for APIs set to be deprecated (#526) -#### Testing +### Testing - pytest: Ignore {exc}`DeprecationWarning` by default (#526) @@ -823,11 +829,11 @@ _Maintenance only, no bug fixes or new features_ _Maintenance only, no bug fixes or new features_ -#### Testing +### Testing - CI: Bump actions to node 20+ versions -#### Documentation +### Documentation - Refine docs and add migration for v0.28.0 @@ -835,7 +841,7 @@ _Maintenance only, no bug fixes or new features_ ### Breaking changes -#### Detached / unselected by default (#523) +### Detached / unselected by default (#523) To ensure consistency and principle of least surprise, keep these set to not use `-a` unless explicitly specified. @@ -1240,7 +1246,7 @@ _Maintenance only, no bug fixes or new features_ ### New features -#### Detect if server active (#448) +### Detect if server active (#448) - `Server.is_alive()` - `Server.raise_if_dead()` @@ -1583,10 +1589,8 @@ _Maintenance only, no bug fixes or new features_ - Python 3.7 and 3.8 returns in 0.12.0 - ~~Final python 3.7 and 3.8 release~~ - - ~~Fixes and security updates will go to - [`v0.11.x`](https://github.com/tmux-python/libtmux/tree/v0.11.x)~~ + *Note: This was not the final Python 3.7 and 3.8 release as originally stated. + Python 3.7 and 3.8 support was extended in 0.12.0.* - Internal: Use new separator to split `tmux(1)` formatting information (#289, #343) diff --git a/MIGRATION b/MIGRATION index 77fd07dc8..a983d061b 100644 --- a/MIGRATION +++ b/MIGRATION @@ -114,7 +114,7 @@ Using the old `g` parameter will emit a {class}`DeprecationWarning`. ## libtmux 0.46.0 (2025-02-25) -#### Imports removed from libtmux.test (#580) +### Imports removed from libtmux.test (#580) Root-level of imports from `libtmux.test` are no longer possible. @@ -230,7 +230,7 @@ from libtmux.test.temporary import temp_session, temp_window ## 0.28.0: Resizing and detached by default (2024-02-15) -#### Detach by default +### Detach by default - {meth}`Session.new_window()` + {meth}`Window.split_window()` no longer attaches by default (#523) @@ -239,7 +239,7 @@ from libtmux.test.temporary import temp_session, temp_window For the old behavior in 0.28.0 and beyond, pass `attach=True` explicitly. -#### Resizing panes +### Resizing panes - `Pane.resize_pane()` renamed to {meth}`Pane.resize()` (via #523) diff --git a/docs/about.md b/docs/about.md index 3a5c91bba..898d2fa4c 100644 --- a/docs/about.md +++ b/docs/about.md @@ -1,5 +1,3 @@ -(internals)= - (about)= # About diff --git a/docs/api/windows.md b/docs/api/windows.md index 728554b0d..384879ae6 100644 --- a/docs/api/windows.md +++ b/docs/api/windows.md @@ -7,7 +7,7 @@ - Identified by `@`, e.g. `@313` ```{module} libtmux - +:no-index: ``` ```{eval-rst} diff --git a/docs/conf.py b/docs/conf.py index db39b88fc..f3a8107b6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -118,6 +118,13 @@ autodoc_class_signature = "separated" toc_object_entries_show_parents = "hide" +# sphinx-autodoc-typehints +# Suppress warnings for forward references that can't be resolved +# (types in TYPE_CHECKING blocks used for circular import avoidance) +suppress_warnings = [ + "sphinx_autodoc_typehints.forward_reference", +] + # sphinx-copybutton copybutton_prompt_text = ( r">>> |\.\.\. |> |\$ |\# | In \[\d*\]: | {2,5}\.\.\.: | {5,8}: " diff --git a/docs/pytest-plugin/index.md b/docs/pytest-plugin/index.md index 8f8dca41d..82d55dd2f 100644 --- a/docs/pytest-plugin/index.md +++ b/docs/pytest-plugin/index.md @@ -12,10 +12,8 @@ your case, we won't stabilize APIs until we're sure everything is by the book. [connect with us]: https://github.com/tmux-python/libtmux/discussions -``` - ```{module} libtmux.pytest_plugin - +:no-index: ``` ## Usage @@ -59,6 +57,8 @@ These fixtures are automatically used when the plugin is enabled and `pytest` is These are set to ensure panes and windows can be reliably referenced and asserted. +(setting_a_tmux_configuration)= + ## Setting a tmux configuration If you would like {func}`session fixture ` to automatically use a configuration, you have a few @@ -108,7 +108,7 @@ def test_something(TestServer): assert server.is_alive() ``` -You can also use it with custom configurations, similar to the {ref}`server fixture `: +You can also use it with custom configurations, similar to the {ref}`server fixture `: ```python def test_with_config(TestServer, tmp_path): diff --git a/docs/quickstart.md b/docs/quickstart.md index 2350bdf97..39b11aa70 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -129,7 +129,7 @@ $ ptpython ``` ```{module} libtmux - +:no-index: ``` First, we can grab a {class}`Server`. @@ -491,4 +491,3 @@ and our [test suite] (see {ref}`development`.) [workspacebuilder.py]: https://github.com/tmux-python/libtmux/blob/master/libtmux/workspacebuilder.py [test suite]: https://github.com/tmux-python/libtmux/tree/master/tests -[ptpython]: https://github.com/prompt-toolkit/ptpython diff --git a/src/libtmux/_internal/constants.py b/src/libtmux/_internal/constants.py index 99693c958..d344c9cd9 100644 --- a/src/libtmux/_internal/constants.py +++ b/src/libtmux/_internal/constants.py @@ -26,6 +26,8 @@ class ServerOptions( SkipDefaultFieldsReprMixin, ): + """Container for tmux server options.""" + backspace: str | None = field(default=None) buffer_limit: int | None = field(default=None) command_alias: SparseArray[str] = field(default_factory=SparseArray) @@ -59,6 +61,8 @@ def __init__(self, **kwargs: object) -> None: class SessionOptions( SkipDefaultFieldsReprMixin, ): + """Container for tmux session options.""" + activity_action: t.Literal["any", "none", "current", "other"] | None = field( default=None, ) @@ -134,6 +138,8 @@ def __init__(self, **kwargs: object) -> None: class WindowOptions( SkipDefaultFieldsReprMixin, ): + """Container for tmux window options.""" + aggressive_resize: t.Literal["on", "off"] | None = field(default=None) automatic_rename: t.Literal["on", "off"] | None = field(default=None) automatic_rename_format: str | None = field(default=None) @@ -197,6 +203,8 @@ def __init__(self, **kwargs: object) -> None: class PaneOptions( SkipDefaultFieldsReprMixin, ): + """Container for tmux pane options.""" + allow_passthrough: t.Literal["on", "off", "all"] | None = field(default=None) allow_rename: t.Literal["on", "off"] | None = field(default=None) alternate_screen: t.Literal["on", "off"] | None = field(default=None) @@ -239,6 +247,8 @@ class Options( PaneOptions, SkipDefaultFieldsReprMixin, ): + """Container for all tmux options (server, session, window, and pane).""" + def __init__(self, **kwargs: object) -> None: # Convert hyphenated keys to underscored attribute names and assign values # Remove asaterisk from inherited options diff --git a/src/libtmux/common.py b/src/libtmux/common.py index d15ddc02e..60a3b49c7 100644 --- a/src/libtmux/common.py +++ b/src/libtmux/common.py @@ -65,9 +65,14 @@ def set_environment(self, name: str, value: str) -> None: Parameters ---------- name : str - the environment variable name. such as 'PATH'. + The environment variable name, e.g. 'PATH'. value : str - environment value. + Environment value. + + Raises + ------ + ValueError + If tmux returns an error. """ args = ["set-environment"] if self._add_option: @@ -92,7 +97,12 @@ def unset_environment(self, name: str) -> None: Parameters ---------- name : str - the environment variable name. such as 'PATH'. + The environment variable name, e.g. 'PATH'. + + Raises + ------ + ValueError + If tmux returns an error. """ args = ["set-environment"] if self._add_option: @@ -116,7 +126,12 @@ def remove_environment(self, name: str) -> None: Parameters ---------- name : str - the environment variable name. such as 'PATH'. + The environment variable name, e.g. 'PATH'. + + Raises + ------ + ValueError + If tmux returns an error. """ args = ["set-environment"] if self._add_option: diff --git a/src/libtmux/pane.py b/src/libtmux/pane.py index e1ab4cbdb..40964f39f 100644 --- a/src/libtmux/pane.py +++ b/src/libtmux/pane.py @@ -321,7 +321,7 @@ def capture_pane( self, start: t.Literal["-"] | int | None = None, end: t.Literal["-"] | int | None = None, - ) -> str | list[str]: + ) -> list[str]: """Capture text from pane. ``$ tmux capture-pane`` to pane. @@ -345,6 +345,11 @@ def capture_pane( Negative numbers are lines in the history. ``-`` is the end of the visible pane. Default: None + + Returns + ------- + list[str] + Captured pane content. """ cmd = ["capture-pane", "-p"] if start is not None: @@ -411,7 +416,7 @@ def display_message( self, cmd: str, get_text: t.Literal[True], - ) -> str | list[str]: ... + ) -> list[str]: ... @t.overload def display_message(self, cmd: str, get_text: t.Literal[False]) -> None: ... @@ -420,7 +425,7 @@ def display_message( self, cmd: str, get_text: bool = False, - ) -> str | list[str] | None: + ) -> list[str] | None: """Display message to pane. Displays a message in target-client status line. @@ -432,6 +437,11 @@ def display_message( get_text : bool, optional Returns only text without displaying a message in target-client status line. + + Returns + ------- + list[str] | None + Message output if get_text is True, otherwise None. """ if get_text: return self.cmd("display-message", "-p", cmd).stdout @@ -714,7 +724,12 @@ def set_width(self, width: int) -> Pane: Parameters ---------- width : int - pane width, in cells + Pane width, in cells. + + Returns + ------- + :class:`Pane` + Self, for method chaining. """ self.resize_pane(width=width) return self @@ -725,7 +740,12 @@ def set_height(self, height: int) -> Pane: Parameters ---------- height : int - height of pain, in cells + Pane height, in cells. + + Returns + ------- + :class:`Pane` + Self, for method chaining. """ self.resize_pane(height=height) return self diff --git a/src/libtmux/pytest_plugin.py b/src/libtmux/pytest_plugin.py index 8b2d6589f..cc45ce7a9 100644 --- a/src/libtmux/pytest_plugin.py +++ b/src/libtmux/pytest_plugin.py @@ -7,6 +7,7 @@ import getpass import logging import os +import pathlib import typing as t import pytest @@ -17,8 +18,6 @@ from libtmux.test.random import get_test_session_name, namer if t.TYPE_CHECKING: - import pathlib - from libtmux.session import Session logger = logging.getLogger(__name__) diff --git a/src/libtmux/server.py b/src/libtmux/server.py index e400ef12b..b09c58c50 100644 --- a/src/libtmux/server.py +++ b/src/libtmux/server.py @@ -313,7 +313,7 @@ def cmd( @property def attached_sessions(self) -> list[Session]: - """Return active :class:`Session`s. + """Return active :class:`Session` instances. Examples -------- @@ -322,7 +322,8 @@ def attached_sessions(self) -> list[Session]: Returns ------- - list of :class:`Session` + list[:class:`Session`] + Sessions that are attached. """ return self.sessions.filter(session_attached__noeq="1") diff --git a/src/libtmux/session.py b/src/libtmux/session.py index 83c7ed6bf..003beeaca 100644 --- a/src/libtmux/session.py +++ b/src/libtmux/session.py @@ -578,7 +578,12 @@ def kill_window(self, target_window: str | None = None) -> None: Parameters ---------- target_window : str, optional - window to kill + Window to kill. + + Raises + ------ + :exc:`libtmux.exc.LibTmuxException` + If tmux returns an error. """ if target_window: if isinstance(target_window, int): diff --git a/src/libtmux/test/environment.py b/src/libtmux/test/environment.py index c08ba377f..20a399a71 100644 --- a/src/libtmux/test/environment.py +++ b/src/libtmux/test/environment.py @@ -21,19 +21,13 @@ class EnvironmentVarGuard: """Mock environmental variables safely. - Helps rotect the environment variable properly. Can be used as context + Helps protect the environment variable properly. Can be used as context manager. Notes ----- Vendorized to fix issue with Anaconda Python 2 not including test module, - see #121 [1]_ - - References - ---------- - .. [1] Just installed, "ImportError: cannot import name test_support". - GitHub issue for tmuxp. https://github.com/tmux-python/tmuxp/issues/121. - Created October 12th, 2015. Accessed April 7th, 2018. + see `tmuxp#121 `_. """ def __init__(self) -> None: diff --git a/src/libtmux/window.py b/src/libtmux/window.py index 5aab420c9..d578d90d7 100644 --- a/src/libtmux/window.py +++ b/src/libtmux/window.py @@ -284,27 +284,32 @@ def split( Parameters ---------- attach : bool, optional - make new window the current window after creating it, default + Make new window the current window after creating it, default True. start_directory : str or PathLike, optional - specifies the working directory in which the new window is created. + Specifies the working directory in which the new window is created. direction : PaneDirection, optional - split in direction. If none is specified, assume down. - full_window_split: bool, optional - split across full window width or height, rather than active pane. - zoom: bool, optional - expand pane + Split in direction. If none is specified, assume down. + full_window_split : bool, optional + Split across full window width or height, rather than active pane. + zoom : bool, optional + Expand pane. shell : str, optional - execute a command on splitting the window. The pane will close + Execute a command on splitting the window. The pane will close when the command exits. - NOTE: When this command exits the pane will close. This feature + NOTE: When this command exits the pane will close. This feature is useful for long-running processes where the closing of the window upon completion is desired. - size: int, optional + size : int, optional Cell/row or percentage to occupy with respect to current window. - environment: dict, optional + environment : dict, optional Environmental variables for new pane. Passthrough to ``-e``. + + Returns + ------- + :class:`Pane` + The newly created pane. """ active_pane = self.active_pane or self.panes[0] return active_pane.split( @@ -409,7 +414,7 @@ def select_layout(self, layout: str | None = None) -> Window: Parameters ---------- layout : str, optional - string of the layout, 'even-horizontal', 'tiled', etc. Entering + String of the layout, 'even-horizontal', 'tiled', etc. Entering None (leaving this blank) is same as ``select-layout`` with no layout. In recent tmux versions, it picks the most recently set layout. @@ -430,7 +435,17 @@ def select_layout(self, layout: str | None = None) -> Window: Panes are spread out as evenly as possible over the window in both rows and columns. 'custom' - custom dimensions (see :term:`tmux(1)` manpages). + Custom dimensions (see :term:`tmux(1)` manpages). + + Returns + ------- + :class:`Window` + Self, for method chaining. + + Raises + ------ + :exc:`libtmux.exc.LibTmuxException` + If tmux returns an error. """ cmd = ["select-layout"] @@ -539,11 +554,21 @@ def move_window( Parameters ---------- destination : str, optional - the ``target window`` or index to move the window to, default: - empty string + The ``target window`` or index to move the window to, default: + empty string. session : str, optional - the ``target session`` or index to move the window to, default: + The ``target session`` or index to move the window to, default: current session. + + Returns + ------- + :class:`Window` + Self, for method chaining. + + Raises + ------ + :exc:`libtmux.exc.LibTmuxException` + If tmux returns an error. """ session = session or self.session_id proc = self.cmd(