Skip to content

Commit 2077a36

Browse files
committed
docs(snapshot[README]) Add README
1 parent 729430d commit 2077a36

File tree

1 file changed

+287
-0
lines changed

1 file changed

+287
-0
lines changed

src/libtmux/snapshot/README.md

+287
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
# libtmux Snapshot Module
2+
3+
The snapshot module provides a powerful way to capture the state of tmux objects (Server, Session, Window, Pane) as immutable, hierarchical snapshots. These snapshots preserve the structure and relationships between tmux objects while allowing for inspection, filtering, and serialization.
4+
5+
## Value Proposition
6+
7+
Snapshots provide several key benefits for tmux automation and management:
8+
9+
- **Point-in-time Captures**: Create immutable records of tmux state at specific moments
10+
- **State Inspection**: Examine the structure of sessions, windows, and panes without modifying them
11+
- **Testing Support**: Build reliable tests with deterministic tmux state snapshots
12+
- **Comparison & Diff**: Compare configurations between different sessions or before/after changes
13+
- **State Serialization**: Convert tmux state to dictionaries for storage or analysis
14+
- **Safety & Predictability**: Work with tmux state without modifying the actual tmux server
15+
- **Content Preservation**: Optionally capture pane content to preserve terminal output
16+
- **Filtering & Searching**: Find specific components within complex tmux arrangements
17+
18+
## Installation
19+
20+
The snapshot module is included with libtmux:
21+
22+
```bash
23+
pip install libtmux
24+
```
25+
26+
## Basic Usage
27+
28+
Creating snapshots is straightforward using the factory functions:
29+
30+
```python
31+
from libtmux import Server
32+
from libtmux.snapshot.factory import create_snapshot, create_snapshot_active
33+
34+
# Connect to the tmux server
35+
server = Server()
36+
37+
# Create a complete snapshot of the entire tmux server
38+
server_snapshot = create_snapshot(server)
39+
40+
# Create a snapshot that only includes active sessions, windows, and panes
41+
active_snapshot = create_snapshot_active(server)
42+
43+
# Create a snapshot with pane content captured
44+
content_snapshot = create_snapshot(server, capture_content=True)
45+
46+
# Create a snapshot of a specific session
47+
session = server.find_where({"session_name": "dev"})
48+
if session:
49+
session_snapshot = create_snapshot(session)
50+
51+
# Create a snapshot of a specific window
52+
window = session.attached_window
53+
if window:
54+
window_snapshot = create_snapshot(window)
55+
56+
# Create a snapshot of a specific pane
57+
pane = window.attached_pane
58+
if pane:
59+
pane_snapshot = create_snapshot(pane)
60+
```
61+
62+
## Working with Snapshots
63+
64+
Once you have a snapshot, you can navigate its hierarchy just like regular tmux objects:
65+
66+
```python
67+
# Inspecting the server snapshot
68+
server_snapshot = create_snapshot(server)
69+
print(f"Server has {len(server_snapshot.sessions)} sessions")
70+
71+
# Navigate the snapshot hierarchy
72+
for session in server_snapshot.sessions:
73+
print(f"Session: {session.name} ({len(session.windows)} windows)")
74+
75+
for window in session.windows:
76+
print(f" Window: {window.name} (index: {window.index})")
77+
78+
for pane in window.panes:
79+
print(f" Pane: {pane.pane_id} (active: {pane.active})")
80+
81+
# If content was captured
82+
if pane.pane_content:
83+
print(f" Content lines: {len(pane.pane_content)}")
84+
```
85+
86+
## Filtering Snapshots
87+
88+
The snapshot API provides powerful filtering capabilities:
89+
90+
```python
91+
# Filter a snapshot to only include a particular session
92+
dev_snapshot = server_snapshot.filter(
93+
lambda s: getattr(s, "name", "") == "dev" or getattr(s, "session_name", "") == "dev"
94+
)
95+
96+
# Filter for a specific window
97+
target_window_snapshot = server_snapshot.filter(
98+
lambda s: getattr(s, "window_id", "") == "$1"
99+
)
100+
101+
# Filter for active panes only
102+
active_panes_snapshot = server_snapshot.filter(
103+
lambda s: getattr(s, "active", False) is True
104+
)
105+
106+
# Complex filtering: sessions with at least one window containing "test" in the name
107+
def has_test_window(obj):
108+
if hasattr(obj, "windows"):
109+
return any("test" in w.name.lower() for w in obj.windows)
110+
return "test" in getattr(obj, "name", "").lower()
111+
112+
test_snapshot = server_snapshot.filter(has_test_window)
113+
```
114+
115+
## Serializing to Dictionaries
116+
117+
Snapshots can be easily converted to dictionaries for storage or analysis:
118+
119+
```python
120+
# Convert a snapshot to a dictionary for serialization or inspection
121+
snapshot_dict = server_snapshot.to_dict()
122+
123+
# Pretty print the structure
124+
import json
125+
print(json.dumps(snapshot_dict, indent=2))
126+
127+
# Selective dictionary conversion
128+
session = server_snapshot.sessions[0]
129+
session_dict = session.to_dict()
130+
```
131+
132+
## Common Use Cases
133+
134+
### Testing tmux Applications
135+
136+
Snapshots make it easy to verify that tmux automations produce the expected state:
137+
138+
```python
139+
def test_my_tmux_function():
140+
# Setup
141+
server = Server()
142+
session = server.new_session("test-session")
143+
144+
# Take a snapshot before
145+
before_snapshot = create_snapshot(server)
146+
147+
# Run the function being tested
148+
my_tmux_function(session)
149+
150+
# Take a snapshot after
151+
after_snapshot = create_snapshot(server)
152+
153+
# Assert expected changes
154+
assert len(after_snapshot.sessions) == len(before_snapshot.sessions) + 1
155+
156+
# Find the newly created session
157+
new_session = next(
158+
(s for s in after_snapshot.sessions if s not in before_snapshot.sessions),
159+
None
160+
)
161+
assert new_session is not None
162+
assert new_session.name == "expected-name"
163+
assert len(new_session.windows) == 3 # Expected window count
164+
```
165+
166+
### Creating Reattachable Sessions
167+
168+
```python
169+
# Take a snapshot before making changes
170+
snapshot = create_snapshot(server)
171+
172+
# Make changes to tmux
173+
# ...
174+
175+
# Find a session from the snapshot to reattach
176+
original_session = snapshot.filter(lambda s: getattr(s, "name", "") == "main")
177+
if original_session and hasattr(original_session, "name"):
178+
# Reattach to that session using its name
179+
server.cmd("attach-session", "-t", original_session.name)
180+
```
181+
182+
### Comparing Window Configurations
183+
184+
```python
185+
# Take a snapshot of two different sessions
186+
session1 = server.find_where({"session_name": "dev"})
187+
session2 = server.find_where({"session_name": "prod"})
188+
189+
if session1 and session2:
190+
snapshot1 = create_snapshot(session1)
191+
snapshot2 = create_snapshot(session2)
192+
193+
# Compare window layouts
194+
for window1 in snapshot1.windows:
195+
# Find matching window in session2 by name
196+
matching_windows = [w for w in snapshot2.windows if w.name == window1.name]
197+
if matching_windows:
198+
window2 = matching_windows[0]
199+
print(f"Window {window1.name}:")
200+
print(f" Session 1 layout: {window1.layout}")
201+
print(f" Session 2 layout: {window2.layout}")
202+
print(f" Layouts match: {window1.layout == window2.layout}")
203+
```
204+
205+
### Monitoring Pane Content Changes
206+
207+
```python
208+
import time
209+
210+
# Create a snapshot with pane content
211+
pane = server.sessions[0].attached_window.attached_pane
212+
snapshot1 = create_snapshot(pane, capture_content=True)
213+
214+
# Wait for potential changes
215+
time.sleep(5)
216+
217+
# Take another snapshot
218+
snapshot2 = create_snapshot(pane, capture_content=True)
219+
220+
# Compare content
221+
if snapshot1.pane_content and snapshot2.pane_content:
222+
content1 = "\n".join(snapshot1.pane_content)
223+
content2 = "\n".join(snapshot2.pane_content)
224+
225+
if content1 != content2:
226+
print("Content changed!")
227+
228+
# Show a simple diff
229+
import difflib
230+
diff = difflib.unified_diff(
231+
snapshot1.pane_content,
232+
snapshot2.pane_content,
233+
fromfile="before",
234+
tofile="after",
235+
)
236+
print("\n".join(diff))
237+
```
238+
239+
### Saving and Restoring Window Arrangements
240+
241+
```python
242+
import json
243+
import os
244+
245+
# Save the current tmux session arrangement
246+
def save_arrangement(session_name, filepath):
247+
session = server.find_where({"session_name": session_name})
248+
if session:
249+
snapshot = create_snapshot(session)
250+
with open(filepath, "w") as f:
251+
json.dump(snapshot.to_dict(), f, indent=2)
252+
print(f"Saved arrangement to {filepath}")
253+
else:
254+
print(f"Session '{session_name}' not found")
255+
256+
# Example usage
257+
save_arrangement("dev", "dev_arrangement.json")
258+
259+
# This function could be paired with a restore function that
260+
# recreates the session from the saved arrangement
261+
```
262+
263+
## Best Practices
264+
265+
- **Immutability**: Remember that snapshots are immutable - methods return new objects rather than modifying the original
266+
- **Timing**: Snapshots represent the state at the time they were created - they don't update automatically
267+
- **Memory Usage**: Be cautious with `capture_content=True` on many panes, as this captures all pane content and can use significant memory
268+
- **Filtering**: The `filter()` method is powerful for finding specific objects within the snapshot hierarchy
269+
- **Type Safety**: The API uses strong typing - take advantage of type hints in your code
270+
271+
## API Overview
272+
273+
The snapshot module follows this structure:
274+
275+
- Factory functions in `factory.py`:
276+
- `create_snapshot(obj)`: Create a snapshot of any tmux object
277+
- `create_snapshot_active(server)`: Create a snapshot with only active components
278+
279+
- Snapshot classes in `models/`:
280+
- `ServerSnapshot`: Snapshot of a tmux server
281+
- `SessionSnapshot`: Snapshot of a tmux session
282+
- `WindowSnapshot`: Snapshot of a tmux window
283+
- `PaneSnapshot`: Snapshot of a tmux pane
284+
285+
- Common methods on all snapshot classes:
286+
- `to_dict()`: Convert to a dictionary
287+
- `filter(predicate)`: Apply a filter function to this snapshot and its children

0 commit comments

Comments
 (0)