Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8e8083f
Hooks(refactor[typing]): Narrow hook dict values
tony Jan 3, 2026
84db95c
Temporary(refactor[typing]): Tighten contextmanager generator types
tony Jan 3, 2026
0cbc72e
Tests(refactor[typing]): Tighten version compare fixtures
tony Jan 3, 2026
5e6f2ea
Tests(refactor[typing]): Use options dict alias for sparse array cases
tony Jan 3, 2026
cf53190
Tests(refactor[typing]): Tighten deprecated window method args
tony Jan 3, 2026
27a0ac5
Tests(refactor[typing]): Narrow dataclasses output alias
tony Jan 3, 2026
49a2fb2
Tests(refactor[typing]): Narrow option fixture expected type
tony Jan 3, 2026
645eecf
Tests(refactor[typing]): Tighten hook fixture kwarg shapes
tony Jan 3, 2026
2e77b8f
Tests(refactor[typing]): Narrow option test case annotations
tony Jan 3, 2026
d757a61
Tests(refactor[typing]): Tighten tmux_cmd test mocks
tony Jan 3, 2026
f8ebb84
PytestPlugin(refactor[typing]): Type session params fixture
tony Jan 3, 2026
7444453
TestHelpers(refactor[typing]): Type temp session/window helpers
tony Jan 3, 2026
537623f
Tests(refactor[typing]): Tighten QueryList filter param types
tony Jan 3, 2026
b657a36
TestHelpers(refactor[typing]): Tighten temp helper kwargs
tony Jan 3, 2026
27fbffe
Exc(refactor[typing]): Narrow VariableUnpackingError variable type
tony Jan 3, 2026
23890a2
Session(refactor[typing]): Narrow deprecated lookup signatures
tony Jan 3, 2026
f2eb0d6
Window(refactor[typing]): Narrow deprecated lookup signatures
tony Jan 3, 2026
d12c067
Pane(refactor[typing]): Narrow deprecated lookup signatures
tony Jan 3, 2026
3f393a6
Server(refactor[typing]): Narrow deprecated filter signatures
tony Jan 3, 2026
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: 1 addition & 1 deletion src/libtmux/exc.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ class WaitTimeout(LibTmuxException):
class VariableUnpackingError(LibTmuxException):
"""Error unpacking variable."""

def __init__(self, variable: t.Any | None = None, *args: object) -> None:
def __init__(self, variable: object | None = None, *args: object) -> None:
return super().__init__(f"Unexpected variable: {variable!s}")


Expand Down
2 changes: 1 addition & 1 deletion src/libtmux/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
if t.TYPE_CHECKING:
from typing_extensions import Self

HookDict = dict[str, t.Any]
HookDict = dict[str, int | str]
HookValues = dict[int, str] | SparseArray[str] | list[str]

logger = logging.getLogger(__name__)
Expand Down
4 changes: 2 additions & 2 deletions src/libtmux/pane.py
Original file line number Diff line number Diff line change
Expand Up @@ -2671,7 +2671,7 @@ def split_window(
version="0.33.0",
)

def get(self, key: str, default: t.Any | None = None) -> t.Any:
def get(self, key: str, default: object | None = None) -> object:
"""Return key-based lookup. Deprecated by attributes.

.. deprecated:: 0.17
Expand All @@ -2686,7 +2686,7 @@ def get(self, key: str, default: t.Any | None = None) -> t.Any:
version="0.17.0",
)

def __getitem__(self, key: str) -> t.Any:
def __getitem__(self, key: str) -> object:
"""Return item lookup by key. Deprecated in favor of attributes.

.. deprecated:: 0.17
Expand Down
17 changes: 15 additions & 2 deletions src/libtmux/pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ def _reap_test_server(socket_name: str | None) -> None:
socket_path.unlink(missing_ok=True)


class SessionParams(t.TypedDict, total=False):
"""Keyword arguments for :meth:`libtmux.Server.new_session` in tests."""

kill_session: bool
attach: bool
start_directory: os.PathLike[str] | str | None
window_name: str | None
window_command: str | None
x: int | t.Literal["-"] | None
y: int | t.Literal["-"] | None
environment: dict[str, str] | None


@pytest.fixture(scope="session")
def home_path(tmp_path_factory: pytest.TempPathFactory) -> pathlib.Path:
"""Temporary `/home/` path."""
Expand Down Expand Up @@ -183,7 +196,7 @@ def fin() -> None:


@pytest.fixture
def session_params() -> dict[str, t.Any]:
def session_params() -> SessionParams:
"""Return default session creation parameters.

>>> import pytest
Expand Down Expand Up @@ -223,7 +236,7 @@ def session_params() -> dict[str, t.Any]:
@pytest.fixture
def session(
request: pytest.FixtureRequest,
session_params: dict[str, t.Any],
session_params: SessionParams,
server: Server,
) -> Session:
"""Return new, temporary :class:`libtmux.Session`.
Expand Down
4 changes: 2 additions & 2 deletions src/libtmux/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2714,7 +2714,7 @@ def get_by_id(self, session_id: str) -> Session | None:
version="0.16.0",
)

def where(self, kwargs: dict[str, t.Any]) -> list[Session]:
def where(self, kwargs: dict[str, object]) -> list[Session]:
"""Filter through sessions, return list of :class:`Session`.

.. deprecated:: 0.17
Expand All @@ -2728,7 +2728,7 @@ def where(self, kwargs: dict[str, t.Any]) -> list[Session]:
version="0.17.0",
)

def find_where(self, kwargs: dict[str, t.Any]) -> Session | None:
def find_where(self, kwargs: dict[str, object]) -> Session | None:
"""Filter through sessions, return first :class:`Session`.

.. deprecated:: 0.17
Expand Down
8 changes: 4 additions & 4 deletions src/libtmux/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ def kill_session(self) -> None:
version="0.30.0",
)

def get(self, key: str, default: t.Any | None = None) -> t.Any:
def get(self, key: str, default: object | None = None) -> object:
"""Return key-based lookup. Deprecated by attributes.

.. deprecated:: 0.17
Expand All @@ -1016,7 +1016,7 @@ def get(self, key: str, default: t.Any | None = None) -> t.Any:
version="0.17.0",
)

def __getitem__(self, key: str) -> t.Any:
def __getitem__(self, key: str) -> object:
"""Return item lookup by key. Deprecated in favor of attributes.

.. deprecated:: 0.17
Expand Down Expand Up @@ -1045,7 +1045,7 @@ def get_by_id(self, session_id: str) -> Window | None:
version="0.16.0",
)

def where(self, kwargs: dict[str, t.Any]) -> list[Window]:
def where(self, kwargs: dict[str, object]) -> list[Window]:
"""Filter through windows, return list of :class:`Window`.

.. deprecated:: 0.17
Expand All @@ -1059,7 +1059,7 @@ def where(self, kwargs: dict[str, t.Any]) -> list[Window]:
version="0.17.0",
)

def find_where(self, kwargs: dict[str, t.Any]) -> Window | None:
def find_where(self, kwargs: dict[str, object]) -> Window | None:
"""Filter through windows, return first :class:`Window`.

.. deprecated:: 0.17
Expand Down
181 changes: 149 additions & 32 deletions src/libtmux/test/temporary.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@

if t.TYPE_CHECKING:
import sys
from collections.abc import Generator

from typing_extensions import Unpack

from libtmux._internal.types import StrPath
from libtmux.constants import WindowDirection
from libtmux.server import Server
from libtmux.session import Session
from libtmux.window import Window
Expand All @@ -19,12 +22,107 @@
pass


class TempSessionParams(t.TypedDict, total=False):
"""Keyword arguments for :func:`temp_session`."""

session_name: str | None
kill_session: bool
attach: bool
start_directory: StrPath | None
window_name: str | None
window_command: str | None
x: int | t.Literal["-"] | None
y: int | t.Literal["-"] | None
environment: dict[str, str] | None


class TempSessionKwargs(t.TypedDict, total=False):
"""Keyword arguments forwarded to :meth:`Server.new_session`."""

kill_session: bool
attach: bool
start_directory: StrPath | None
window_name: str | None
window_command: str | None
x: int | t.Literal["-"] | None
y: int | t.Literal["-"] | None
environment: dict[str, str] | None


class TempWindowParams(t.TypedDict, total=False):
"""Keyword arguments for :func:`temp_window`."""

window_name: str | None
start_directory: StrPath | None
attach: bool
window_index: str
window_shell: str | None
environment: dict[str, str] | None
direction: WindowDirection | None
target_window: str | None


class TempWindowKwargs(t.TypedDict, total=False):
"""Keyword arguments forwarded to :meth:`Session.new_window`."""

start_directory: StrPath | None
attach: bool
window_index: str
window_shell: str | None
environment: dict[str, str] | None
direction: WindowDirection | None
target_window: str | None


@contextlib.contextmanager
def _temp_session(
server: Server,
*args: t.Any,
**kwargs: object,
) -> t.Iterator[Session]:
kwargs_typed = t.cast("TempSessionParams", dict(kwargs))
if "session_name" in kwargs_typed:
session_name = kwargs_typed["session_name"]
else:
session_name = get_test_session_name(server)
kwargs_no_name = t.cast(
"TempSessionKwargs",
{k: v for k, v in kwargs_typed.items() if k != "session_name"},
)

session = server.new_session(
session_name,
*args,
**kwargs_no_name,
)

try:
yield session
finally:
if isinstance(session_name, str) and server.has_session(session_name):
session.kill()


@t.overload
def temp_session(
server: Server,
**kwargs: Unpack[TempSessionParams],
) -> contextlib.AbstractContextManager[Session]: ...


@t.overload
def temp_session(
server: Server,
*args: t.Any,
**kwargs: t.Any,
) -> Generator[Session, t.Any, t.Any]:
**kwargs: object,
) -> contextlib.AbstractContextManager[Session]: ...


def temp_session(
server: Server,
*args: t.Any,
**kwargs: object,
) -> contextlib.AbstractContextManager[Session]:
"""
Return a context manager with a temporary session.

Expand Down Expand Up @@ -55,27 +153,63 @@ 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")
return _temp_session(server, *args, **kwargs)


@contextlib.contextmanager
def _temp_window(
session: Session,
*args: t.Any,
**kwargs: object,
) -> t.Iterator[Window]:
kwargs_typed = t.cast("TempWindowParams", dict(kwargs))
if "window_name" in kwargs_typed:
window_name = kwargs_typed["window_name"]
else:
session_name = get_test_session_name(server)
window_name = get_test_window_name(session)
kwargs_no_name = t.cast(
"TempWindowKwargs",
{k: v for k, v in kwargs_typed.items() if k != "window_name"},
)

session = server.new_session(session_name, *args, **kwargs)
window = session.new_window(
window_name,
*args,
**kwargs_no_name,
)

# Get ``window_id`` before returning it, it may be killed within context.
window_id = window.window_id
assert window_id is not None
assert isinstance(window_id, str)

try:
yield session
yield window
finally:
if server.has_session(session_name):
session.kill()
return
if len(session.windows.filter(window_id=window_id)) > 0:
window.kill()


@contextlib.contextmanager
@t.overload
def temp_window(
session: Session,
**kwargs: Unpack[TempWindowParams],
) -> contextlib.AbstractContextManager[Window]: ...


@t.overload
def temp_window(
session: Session,
*args: t.Any,
**kwargs: t.Any,
) -> Generator[Window, t.Any, t.Any]:
**kwargs: object,
) -> contextlib.AbstractContextManager[Window]: ...


def temp_window(
session: Session,
*args: t.Any,
**kwargs: object,
) -> contextlib.AbstractContextManager[Window]:
"""
Return a context manager with a temporary window.

Expand Down Expand Up @@ -112,21 +246,4 @@ def temp_window(
... window.split()
Pane(%4 Window(@3 2:libtmux_..., Session($1 libtmux_...)))
"""
if "window_name" not in kwargs:
window_name = get_test_window_name(session)
else:
window_name = kwargs.pop("window_name")

window = session.new_window(window_name, *args, **kwargs)

# Get ``window_id`` before returning it, it may be killed within context.
window_id = window.window_id
assert window_id is not None
assert isinstance(window_id, str)

try:
yield window
finally:
if len(session.windows.filter(window_id=window_id)) > 0:
window.kill()
return
return _temp_window(session, *args, **kwargs)
8 changes: 4 additions & 4 deletions src/libtmux/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -1673,7 +1673,7 @@ def show_window_option(
global_=g,
)

def get(self, key: str, default: t.Any | None = None) -> t.Any:
def get(self, key: str, default: object | None = None) -> object:
"""Return key-based lookup. Deprecated by attributes.

.. deprecated:: 0.17
Expand All @@ -1688,7 +1688,7 @@ def get(self, key: str, default: t.Any | None = None) -> t.Any:
version="0.17.0",
)

def __getitem__(self, key: str) -> t.Any:
def __getitem__(self, key: str) -> object:
"""Return item lookup by key. Deprecated in favor of attributes.

.. deprecated:: 0.17
Expand Down Expand Up @@ -1717,7 +1717,7 @@ def get_by_id(self, pane_id: str) -> Pane | None:
version="0.16.0",
)

def where(self, kwargs: dict[str, t.Any]) -> list[Pane]:
def where(self, kwargs: dict[str, object]) -> list[Pane]:
"""Filter through panes, return list of :class:`Pane`.

.. deprecated:: 0.17
Expand All @@ -1731,7 +1731,7 @@ def where(self, kwargs: dict[str, t.Any]) -> list[Pane]:
version="0.17.0",
)

def find_where(self, kwargs: dict[str, t.Any]) -> Pane | None:
def find_where(self, kwargs: dict[str, object]) -> Pane | None:
"""Filter through panes, return first :class:`Pane`.

.. deprecated:: 0.17
Expand Down
Loading
Loading