Skip to content

Commit 6e94d38

Browse files
committed
Server, Session, Window, Pane: Add ContextManagers
1 parent f79993a commit 6e94d38

File tree

4 files changed

+170
-0
lines changed

4 files changed

+170
-0
lines changed

src/libtmux/pane.py

+44
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import typing as t
1414
import warnings
1515

16+
from typing_extensions import Self
17+
1618
from libtmux.common import has_gte_version, has_lt_version, tmux_cmd
1719
from libtmux.constants import (
1820
PANE_DIRECTION_FLAG_MAP,
@@ -26,6 +28,8 @@
2628
from . import exc
2729

2830
if t.TYPE_CHECKING:
31+
import types
32+
2933
from .server import Server
3034
from .session import Session
3135
from .window import Window
@@ -59,6 +63,13 @@ class Pane(Obj):
5963
>>> pane.session
6064
Session($1 ...)
6165
66+
The pane can be used as a context manager to ensure proper cleanup:
67+
68+
>>> with window.split() as pane:
69+
... pane.send_keys('echo "Hello"')
70+
... # Do work with the pane
71+
... # Pane will be killed automatically when exiting the context
72+
6273
Notes
6374
-----
6475
.. versionchanged:: 0.8
@@ -77,6 +88,39 @@ class Pane(Obj):
7788

7889
server: Server
7990

91+
def __enter__(self) -> Self:
92+
"""Enter the context, returning self.
93+
94+
Returns
95+
-------
96+
:class:`Pane`
97+
The pane instance
98+
"""
99+
return self
100+
101+
def __exit__(
102+
self,
103+
exc_type: type[BaseException] | None,
104+
exc_value: BaseException | None,
105+
exc_tb: types.TracebackType | None,
106+
) -> None:
107+
"""Exit the context, killing the pane if it exists.
108+
109+
Parameters
110+
----------
111+
exc_type : type[BaseException] | None
112+
The type of the exception that was raised
113+
exc_value : BaseException | None
114+
The instance of the exception that was raised
115+
exc_tb : types.TracebackType | None
116+
The traceback of the exception that was raised
117+
"""
118+
if (
119+
self.pane_id is not None
120+
and len(self.window.panes.filter(pane_id=self.pane_id)) > 0
121+
):
122+
self.kill()
123+
80124
def refresh(self) -> None:
81125
"""Refresh pane attributes from tmux."""
82126
assert isinstance(self.pane_id, str)

src/libtmux/server.py

+41
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import typing as t
1616
import warnings
1717

18+
from typing_extensions import Self
19+
1820
from libtmux._internal.query_list import QueryList
1921
from libtmux.common import tmux_cmd
2022
from libtmux.neo import fetch_objs
@@ -33,6 +35,8 @@
3335
)
3436

3537
if t.TYPE_CHECKING:
38+
import types
39+
3640
from typing_extensions import TypeAlias
3741

3842
DashLiteral: TypeAlias = t.Literal["-"]
@@ -79,6 +83,13 @@ class Server(EnvironmentMixin):
7983
>>> server.sessions[0].active_pane
8084
Pane(%1 Window(@1 1:..., Session($1 ...)))
8185
86+
The server can be used as a context manager to ensure proper cleanup:
87+
88+
>>> with Server() as server:
89+
... session = server.new_session()
90+
... # Do work with the session
91+
... # Server will be killed automatically when exiting the context
92+
8293
References
8394
----------
8495
.. [server_manual] CLIENTS AND SESSIONS. openbsd manpage for TMUX(1)
@@ -146,6 +157,36 @@ def __init__(
146157
if on_init is not None:
147158
on_init(self)
148159

160+
def __enter__(self) -> Self:
161+
"""Enter the context, returning self.
162+
163+
Returns
164+
-------
165+
:class:`Server`
166+
The server instance
167+
"""
168+
return self
169+
170+
def __exit__(
171+
self,
172+
exc_type: type[BaseException] | None,
173+
exc_value: BaseException | None,
174+
exc_tb: types.TracebackType | None,
175+
) -> None:
176+
"""Exit the context, killing the server if it exists.
177+
178+
Parameters
179+
----------
180+
exc_type : type[BaseException] | None
181+
The type of the exception that was raised
182+
exc_value : BaseException | None
183+
The instance of the exception that was raised
184+
exc_tb : types.TracebackType | None
185+
The traceback of the exception that was raised
186+
"""
187+
if self.is_alive():
188+
self.kill()
189+
149190
def is_alive(self) -> bool:
150191
"""Return True if tmux server alive.
151192

src/libtmux/session.py

+41
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import typing as t
1414
import warnings
1515

16+
from typing_extensions import Self
17+
1618
from libtmux._internal.query_list import QueryList
1719
from libtmux.constants import WINDOW_DIRECTION_FLAG_MAP, WindowDirection
1820
from libtmux.formats import FORMAT_SEPARATOR
@@ -31,6 +33,8 @@
3133
)
3234

3335
if t.TYPE_CHECKING:
36+
import types
37+
3438
from libtmux.common import tmux_cmd
3539

3640
from .server import Server
@@ -63,6 +67,13 @@ class Session(Obj, EnvironmentMixin):
6367
>>> session.active_pane
6468
Pane(%1 Window(@1 ...:..., Session($1 ...)))
6569
70+
The session can be used as a context manager to ensure proper cleanup:
71+
72+
>>> with server.new_session() as session:
73+
... window = session.new_window()
74+
... # Do work with the window
75+
... # Session will be killed automatically when exiting the context
76+
6677
References
6778
----------
6879
.. [session_manual] tmux session. openbsd manpage for TMUX(1).
@@ -78,6 +89,36 @@ class Session(Obj, EnvironmentMixin):
7889

7990
server: Server
8091

92+
def __enter__(self) -> Self:
93+
"""Enter the context, returning self.
94+
95+
Returns
96+
-------
97+
:class:`Session`
98+
The session instance
99+
"""
100+
return self
101+
102+
def __exit__(
103+
self,
104+
exc_type: type[BaseException] | None,
105+
exc_value: BaseException | None,
106+
exc_tb: types.TracebackType | None,
107+
) -> None:
108+
"""Exit the context, killing the session if it exists.
109+
110+
Parameters
111+
----------
112+
exc_type : type[BaseException] | None
113+
The type of the exception that was raised
114+
exc_value : BaseException | None
115+
The instance of the exception that was raised
116+
exc_tb : types.TracebackType | None
117+
The traceback of the exception that was raised
118+
"""
119+
if self.session_name is not None and self.server.has_session(self.session_name):
120+
self.kill()
121+
81122
def refresh(self) -> None:
82123
"""Refresh session attributes from tmux."""
83124
assert isinstance(self.session_id, str)

src/libtmux/window.py

+44
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import typing as t
1414
import warnings
1515

16+
from typing_extensions import Self
17+
1618
from libtmux._internal.query_list import QueryList
1719
from libtmux.common import has_gte_version, tmux_cmd
1820
from libtmux.constants import (
@@ -28,6 +30,8 @@
2830
from .common import PaneDict, WindowOptionDict, handle_option_error
2931

3032
if t.TYPE_CHECKING:
33+
import types
34+
3135
from .server import Server
3236
from .session import Session
3337

@@ -73,6 +77,13 @@ class Window(Obj):
7377
>>> window in session.windows
7478
True
7579
80+
The window can be used as a context manager to ensure proper cleanup:
81+
82+
>>> with session.new_window() as window:
83+
... pane = window.split()
84+
... # Do work with the pane
85+
... # Window will be killed automatically when exiting the context
86+
7687
References
7788
----------
7889
.. [window_manual] tmux window. openbsd manpage for TMUX(1).
@@ -85,6 +96,39 @@ class Window(Obj):
8596

8697
server: Server
8798

99+
def __enter__(self) -> Self:
100+
"""Enter the context, returning self.
101+
102+
Returns
103+
-------
104+
:class:`Window`
105+
The window instance
106+
"""
107+
return self
108+
109+
def __exit__(
110+
self,
111+
exc_type: type[BaseException] | None,
112+
exc_value: BaseException | None,
113+
exc_tb: types.TracebackType | None,
114+
) -> None:
115+
"""Exit the context, killing the window if it exists.
116+
117+
Parameters
118+
----------
119+
exc_type : type[BaseException] | None
120+
The type of the exception that was raised
121+
exc_value : BaseException | None
122+
The instance of the exception that was raised
123+
exc_tb : types.TracebackType | None
124+
The traceback of the exception that was raised
125+
"""
126+
if (
127+
self.window_id is not None
128+
and len(self.session.windows.filter(window_id=self.window_id)) > 0
129+
):
130+
self.kill()
131+
88132
def refresh(self) -> None:
89133
"""Refresh window attributes from tmux."""
90134
assert isinstance(self.window_id, str)

0 commit comments

Comments
 (0)