Skip to content

Commit 9052045

Browse files
committed
feat(server): Add socket_name_factory and on_init callbacks
Add two new optional parameters to Server constructor: - socket_name_factory: Callable[[], str] Generates unique socket names for new servers. Used when socket_name is not provided. Useful for creating multiple servers with unique names. - on_init: Callable[[Server], None] Callback that runs after server initialization. Useful for tracking server instances and performing cleanup in tests. The socket_name_factory is tried after socket_name, maintaining backward compatibility while adding flexibility for dynamic socket name generation. Example: def socket_name_factory() -> str: return f"tmux_{next(counter)}" server = Server(socket_name_factory=socket_name_factory) This enables better testing patterns and more flexible server creation, particularly in test environments where unique socket names are needed.
1 parent f2fb144 commit 9052045

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

src/libtmux/server.py

+9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class Server(EnvironmentMixin):
5959
socket_path : str, optional
6060
config_file : str, optional
6161
colors : str, optional
62+
on_init : callable, optional
63+
socket_name_factory : callable, optional
6264
6365
Examples
6466
--------
@@ -110,6 +112,8 @@ def __init__(
110112
socket_path: str | pathlib.Path | None = None,
111113
config_file: str | None = None,
112114
colors: int | None = None,
115+
on_init: t.Callable[[Server], None] | None = None,
116+
socket_name_factory: t.Callable[[], str] | None = None,
113117
**kwargs: t.Any,
114118
) -> None:
115119
EnvironmentMixin.__init__(self, "-g")
@@ -120,6 +124,8 @@ def __init__(
120124
self.socket_path = socket_path
121125
elif socket_name is not None:
122126
self.socket_name = socket_name
127+
elif socket_name_factory is not None:
128+
self.socket_name = socket_name_factory()
123129

124130
tmux_tmpdir = pathlib.Path(os.getenv("TMUX_TMPDIR", "/tmp"))
125131
socket_name = self.socket_name or "default"
@@ -137,6 +143,9 @@ def __init__(
137143
if colors:
138144
self.colors = colors
139145

146+
if on_init is not None:
147+
on_init(self)
148+
140149
def is_alive(self) -> bool:
141150
"""Return True if tmux server alive.
142151

tests/test_server.py

+68
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,71 @@ def test_raise_if_dead_does_not_raise_if_alive(server: Server) -> None:
228228
"""Verify new_session() does not raise if tmux server is alive."""
229229
server.new_session()
230230
server.raise_if_dead()
231+
232+
233+
def test_on_init(server: Server) -> None:
234+
"""Verify on_init callback is called during Server initialization."""
235+
called_with: list[Server] = []
236+
237+
def on_init(server: Server) -> None:
238+
called_with.append(server)
239+
240+
myserver = Server(socket_name="test_on_init", on_init=on_init)
241+
try:
242+
assert len(called_with) == 1
243+
assert called_with[0] is myserver
244+
finally:
245+
if myserver.is_alive():
246+
myserver.kill()
247+
248+
249+
def test_socket_name_factory(server: Server) -> None:
250+
"""Verify socket_name_factory generates socket names."""
251+
socket_names: list[str] = []
252+
253+
def socket_name_factory() -> str:
254+
name = f"test_socket_{len(socket_names)}"
255+
socket_names.append(name)
256+
return name
257+
258+
myserver = Server(socket_name_factory=socket_name_factory)
259+
try:
260+
assert myserver.socket_name == "test_socket_0"
261+
assert socket_names == ["test_socket_0"]
262+
263+
# Creating another server should use factory again
264+
myserver2 = Server(socket_name_factory=socket_name_factory)
265+
try:
266+
assert myserver2.socket_name == "test_socket_1"
267+
assert socket_names == ["test_socket_0", "test_socket_1"]
268+
finally:
269+
if myserver2.is_alive():
270+
myserver2.kill()
271+
finally:
272+
if myserver.is_alive():
273+
myserver.kill()
274+
if myserver2.is_alive():
275+
myserver2.kill()
276+
277+
278+
def test_socket_name_precedence(server: Server) -> None:
279+
"""Verify socket_name takes precedence over socket_name_factory."""
280+
281+
def socket_name_factory() -> str:
282+
return "from_factory"
283+
284+
myserver = Server(
285+
socket_name="explicit_name",
286+
socket_name_factory=socket_name_factory,
287+
)
288+
myserver2 = Server(socket_name_factory=socket_name_factory)
289+
try:
290+
assert myserver.socket_name == "explicit_name"
291+
292+
# Without socket_name, factory is used
293+
assert myserver2.socket_name == "from_factory"
294+
finally:
295+
if myserver.is_alive():
296+
myserver.kill()
297+
if myserver2.is_alive():
298+
myserver2.kill()

0 commit comments

Comments
 (0)