8000 snapshot(refactor[typing]): Improve type overrides with generics by tony · Pull Request #590 · tmux-python/libtmux · GitHub
[go: up one dir, main page]

Skip to content

snapshot(refactor[typing]): Improve type overrides with generics #590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 44 commits into
base: snapshots
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
ea5b0c4
.tool-versions(uv,python) uv 0.6.12 -> 0.6.14, python 3.13.2 -> 3.13.3
tony Apr 12, 2025
88e62d8
py(deps[dev]) Bump dev packages
tony Apr 12, 2025
4c49754
py(deps[dev]) Bump dev packages
tony Apr 19, 2025
93f2e75
py(deps[dev]) Bump dev packages
tony Apr 26, 2025
b9a5cb7
pyproject(mypy) Add mypy override for frozen_dataclass method-assign
tony Feb 28, 2025
477e24b
pyproject(ruff) Ignore B010 set-attr-with-constant rule for frozen_da…
tony Feb 28, 2025
19a331c
pyproject(mypy) Add mypy override for `frozen_dataclass_sealable` `me…
tony Feb 28, 2025
e5be3f4
pyproject.toml(chore[mypy]): Exclude frozen_dataclass_sealable test f…
tony Mar 1, 2025
a929424
pyproject.toml(chore[lint,types]): Exclude frozen_dataclass_sealable …
tony Mar 1, 2025
7046533
frozen_dataclass(feat): Add `frozen_dataclass`
tony Feb 28, 2025
112709f
frozen_dataclass_sealable(feat): Add `frozen_dataclass_sealable`
tony Feb 28, 2025
b6d41dc
docs(frozen_dataclass) Add to `internals`
tony Feb 28, 2025
c244be5
docs(frozen_dataclass_sealable) Add to `internals`
tony Feb 28, 2025
50be574
WIP: Snapshot
tony Feb 28, 2025
7e75203
test(Snapshot): Replace MagicMock with pytest fixtures
tony Mar 1, 2025
d9a6d8a
docs(ServerSnapshot): Fix doctest examples in snapshot.py
tony Mar 1, 2025
df905f6
docs(Pane): Fix send_keys method doctest example
tony Mar 1, 2025
239d401
src/libtmux/snapshot.py uv run ruff check --select ALL src/libtmux/sn…
tony Mar 1, 2025
82deff8
test/test_snapshot.py: uv run ruff check --select ALL src/libtmux/sna…
tony Mar 1, 2025
9a940df
chore[mypy]: Add snapshot module override
tony Mar 1, 2025
e806d88
refactor(snapshot): Add explicit type ignores for seal methods
tony Mar 1, 2025
c8b202d
test(snapshot): Add type annotation to mock_filter function
tony Mar 1, 2025
f52f617
Revert "chore[mypy]: Add snapshot module override"
tony Mar 1, 2025
8997ba1
snapshot(refactor[Snapshot]): Fix dataclass field order and enhance s…
tony Mar 1, 2025
b42672b
mypy(config[snapshot]): Add override for property/field conflicts
tony Mar 1, 2025
13f7624
test(fix[PaneSnapshot]): Specify capture_content flag in tests
tony Mar 1, 2025
1eb0307
snapshot.py(style[exceptions]): Fix linting issues identified by ruff
tony Mar 1, 2025
b4f2db5
snapshot.py(refactor[performance]): Extract helper function for sessi…
tony Mar 1, 2025
00151d7
notes(2025-03-02) Add architecture notes
tony Mar 2, 2025
d6421c8
frozen_dataclass_sealable fix imports from `typing`
tony Mar 2, 2025
47aaec8
pyproject(mypy) Add mypy override for frozen_dataclass method-assign
tony Feb 28, 2025
83088eb
snapshot(refactor[typing]): Improve type overrides with generics
tony Mar 2, 2025
314a89e
docs: proposal for snapshot.py refactoring into package structure
tony Mar 2, 2025
a30f674
snapshot: New architecture, part 0: Remove old snapshot.py
tony Mar 2, 2025
0a6b46d
snapshot: New architecture, part 1: Add new architecture
tony Mar 2, 2025
2d12f0e
mypy(config[snapshot]): Update module pattern to include submodules
tony Mar 2, 2025
5237944
notes(2025-03-02) Updates to architecture notes
tony Mar 2, 2025
91b57f7
notes(2025-03-02[architecture-plan]) Update with typing ideas
tony Mar 2, 2025
ec6db9b
snapshot(factory): Implement type-safe factory and fluent API
tony Mar 2, 2025
729430d
notes(2025-03-02[architecture-plan]) Update for latest changes
tony Mar 2, 2025
2077a36
docs(snapshot[README]) Add README
tony Mar 2, 2025
fe23292
docs(snapshot[README]): add comprehensive doctest-based README for sn…
tony Mar 2, 2025
f6fde52
docs(snapshot/README): Comprehensive overhaul with doctest integration
tony Mar 2, 2025
ac85da4
docs(snapshot/README): Add Capabilities and Limitations table
tony Mar 2, 2025
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
Prev Previous commit
Next Next commit
docs: proposal for snapshot.py refactoring into package structure
This commit adds a detailed proposal for refactoring the current monolithic
snapshot.py module into a structured package to improve maintainability,
testability, and extensibility.

Key improvements proposed:
- Split into smaller, focused modules with clear responsibilities
- Separate base classes, concrete implementations, and utility functions
- Establish clear type boundaries with a dedicated types.py
- Maintain backward compatibility via public API re-exports

The proposal includes a four-phase implementation plan with timeline estimates,
benefits and tradeoffs analysis, backward compatibility strategy, and success
metrics for evaluating the refactoring.
  • Loading branch information
tony committed Apr 26, 2025
commit 314a89e780d16bf9b3c2e8c9133e62ec7b55b89c
244 changes: 244 additions & 0 deletions notes/2025-03-02-snapshot-structure-redesign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# Snapshot Module Redesign Proposal

**Date**: March 2, 2025
**Author**: Development Team
**Status**: Draft Proposal

## Executive Summary

This document proposes refactoring the current monolithic `snapshot.py` module (approximately 650 lines) into a structured package to improve maintainability, testability, and extensibility. The primary goal is to separate concerns, reduce file size, and establish a clear API boundary while maintaining backward compatibility.

## Current State Analysis

### Structure

The current `snapshot.py` module contains:

- 4 base classes with sealable mixin functionality
- 4 concrete snapshot classes (Server, Session, Window, Pane)
- Several utility functions for filtering and transformations
- Type definitions and aliases
- Complex inter-dependencies between classes

### Pain Points

1. **Size**: The file is large (~650 lines) and challenging to navigate
2. **Tight coupling**: Classes reference each other directly, creating complex dependencies
3. **Mixed concerns**: Type definitions, base classes, implementations, and utilities are intermingled
4. **Testing complexity**: Testing specific components requires loading the entire module
5. **Future maintenance**: Adding new features or making changes affects the entire module

## Proposed Structure

We propose refactoring into a dedicated package with this structure:

```
src/libtmux/snapshot/
├── __init__.py # Module documentation only, no exports
├── base.py # Base classes with Sealable mixins
├── types.py # Type definitions, exports, and annotations
├── models/
│ ├── __init__.py # Package documentation only, no exports
│ ├── pane.py # PaneSnapshot implementation
│ ├── window.py # WindowSnapshot implementation
│ ├── session.py # SessionSnapshot implementation
│ └── server.py # ServerSnapshot implementation
└── utils.py # Utility functions (filter_snapshot, snapshot_to_dict, etc.)
```

### Key Components

1. **`__init__.py`**: Document the module purpose and structure, but without exporting classes
```python
"""Hierarchical snapshots of tmux objects.

libtmux.snapshot
~~~~~~~~~~~~~~

This module provides hierarchical snapshots of tmux objects (Server, Session,
Window, Pane) that are immutable and maintain the relationships between objects.
"""
```

2. **`types.py`**: Centralizes all type definitions
```python
from __future__ import annotations

import typing as t

from libtmux.pane import Pane
from libtmux.server import Server
from libtmux.session import Session
from libtmux.window import Window

# Type variables for generic typing
PaneT = t.TypeVar("PaneT", bound=Pane, covariant=True)
WindowT = t.TypeVar("WindowT", bound=Window, covariant=True)
SessionT = t.TypeVar("SessionT", bound=Session, covariant=True)
ServerT = t.TypeVar("ServerT", bound=Server, covariant=True)

# Forward references for snapshot classes
if t.TYPE_CHECKING:
from libtmux.snapshot.models.pane import PaneSnapshot
from libtmux.snapshot.models.window import WindowSnapshot
from libtmux.snapshot.models.session import SessionSnapshot
from libtmux.snapshot.models.server import ServerSnapshot

# Union type for snapshot classes
SnapshotType = t.Union[ServerSnapshot, SessionSnapshot, WindowSnapshot, PaneSnapshot]
else:
# Runtime placeholder - will be properly defined after imports
SnapshotType = t.Any
```

3. **`base.py`**: Base classes that implement sealable behavior
```python
from __future__ import annotations

import typing as t

from libtmux._internal.frozen_dataclass_sealable import Sealable
from libtmux._internal.query_list import QueryList
from libtmux.pane import Pane
from libtmux.server import Server
from libtmux.session import Session
from libtmux.window import Window

from libtmux.snapshot.types import PaneT, WindowT, SessionT, PaneT

class SealablePaneBase(Pane, Sealable):
"""Base class for sealable pane classes."""

class SealableWindowBase(Window, Sealable, t.Generic[PaneT]):
"""Base class for sealable window classes with generic pane type."""

# Implementation of properties with proper typing

class SealableSessionBase(Session, Sealable, t.Generic[WindowT, PaneT]):
"""Base class for sealable session classes with generic window and pane types."""

# Implementation of properties with proper typing

class SealableServerBase(Server, Sealable, t.Generic[SessionT, WindowT, PaneT]):
"""Generic base for sealable server with typed session, window, and pane."""

# Implementation of properties with proper typing
```

4. **Model classes**: Individual implementations in separate files
- Each file contains a single snapshot class with focused responsibility
- Clear imports and dependencies between modules
- Proper type annotations

5. **`utils.py`**: Utility functions separated from model implementations
```python
from __future__ import annotations

import copy
import datetime
import typing as t

from libtmux.snapshot.types import SnapshotType
from libtmux.snapshot.models.server import ServerSnapshot
from libtmux.snapshot.models.session import SessionSnapshot
from libtmux.snapshot.models.window import WindowSnapshot
from libtmux.snapshot.models.pane import PaneSnapshot

def filter_snapshot(
snapshot: SnapshotType,
filter_func: t.Callable[[SnapshotType], bool],
) -> SnapshotType | None:
"""Filter a snapshot hierarchy based on a filter function."""
# Implementation...

def snapshot_to_dict(
snapshot: SnapshotType | t.Any,
) -> dict[str, t.Any]:
"""Convert a snapshot to a dictionary, avoiding circular references."""
# Implementation...

def snapshot_active_only(
full_snapshot: ServerSnapshot,
) -> ServerSnapshot:
"""Return a filtered snapshot containing only active sessions, windows, and panes."""
# Implementation...
```

## Implementation Plan

We propose a phased approach with the following steps:

### Phase 1: Setup Package Structure (Week 1)

1. Create the package directory structure
2. Set up module files with appropriate documentation
3. Create the types.py module with all type definitions
4. Draft the base.py module with base classes

### Phase 2: Migrate Models (Week 2-3)

1. Move PaneSnapshot to its own module
2. Move WindowSnapshot to its own module
3. Move SessionSnapshot to its own module
4. Move ServerSnapshot to its own module
5. Update imports and references between modules

### Phase 3: Extract Utilities (Week 3)

1. Move utility functions to utils.py
2. Update imports and references

### Phase 4: Testing and Finalization (Week 4)

1. Add/update tests to verify all functionality works correctly
2. Update documentation
3. Final code review
4. Merge to main branch

## Benefits and Tradeoffs

### Benefits

1. **Improved maintainability**: Smaller, focused files with clear responsibilities
2. **Better organization**: Separation of concerns between different components
3. **Simplified testing**: Ability to test components in isolation
4. **Enhanced discoverability**: Easier for new developers to understand the codebase
5. **Clearer API boundary**: Direct imports encourage explicit dependencies
6. **Future extensibility**: Easier to add new snapshot types or modify existing ones

### Tradeoffs

1. **Initial effort**: Significant upfront work to refactor and test
2. **Complexity**: More files to navigate and understand
3. **Risk**: Potential for regressions during refactoring
4. **Import overhead**: Slightly more verbose import statements
5. **Learning curve**: Team needs to adapt to the new structure

## Backward Compatibility

The proposed changes maintain backward compatibility through:

1. **Direct imports**: Users will need to update imports to reference specific modules
2. **Same behavior**: No functional changes to how snapshots work
3. **Same type definitions**: Type hints remain compatible with existing code

## Success Metrics

The success of this refactoring will be measured by:

1. **Code coverage**: Maintain or improve current test coverage
2. **File sizes**: No file should exceed 200 lines
3. **Import clarity**: Clear and direct imports between modules
4. **Maintainability**: Reduction in complexity metrics
5. **Developer feedback**: Team survey on code navigability

## Conclusion

This redesign addresses the current maintainability issues with the snapshot module while preserving all functionality and backward compatibility. The modular approach will make future maintenance easier and allow for more focused testing. We recommend proceeding with this refactoring as outlined in the implementation plan.

## Next Steps

1. Review and finalize this proposal
2. Create implementation tickets in the issue tracker
3. Assign resources for implementation
4. Schedule code reviews at each phase completion
0