python-storage-abstraction
Python Protocol-based storage abstraction pattern enabling dependency injection and testable file operations.Use this skill when implementing storage backends, adding file operations to services, or writing teststhat need to avoid filesystem side effects. The pattern uses Python's typing.Protocol for structuralsubtyping, allowing any class with matching methods to satisfy the interface without explicit inheritance.
When & Why to Use This Skill
This Claude skill provides a robust Python Protocol-based storage abstraction pattern designed to enhance software architecture through dependency injection. By leveraging structural subtyping with typing.Protocol, it enables developers to decouple business logic from concrete file system implementations, facilitating seamless transitions between local, cloud, and in-memory storage while ensuring high testability and code maintainability.
Use Cases
- Unit Testing: Use the MemoryStorage implementation to run fast, isolated tests that avoid real filesystem side effects and I/O overhead.
- Cloud Integration: Easily swap local file storage for cloud-based backends (like AWS S3 or Google Cloud Storage) by implementing the protocol without modifying existing service logic.
- Clean Architecture: Implement dependency injection by injecting storage abstractions into services, keeping the core logic infrastructure-agnostic and easier to refactor.
- Mocking File Operations: Simulate complex file system states or failures in a controlled environment using the protocol-compliant mock objects.
- Cross-Platform Compatibility: Abstract away OS-specific file path handling and permissions by using a unified storage interface across different development and production environments.
| name | python-storage-abstraction |
|---|---|
| description | | |
| version | 1.0.0 |
Python Storage Protocol Pattern
Overview
The Storage Protocol Pattern provides a clean abstraction layer for file system operations in Python. It uses Python's typing.Protocol to define a structural interface that implementations can satisfy without explicit inheritance (duck typing with type safety).
This pattern enables:
- Testability: Use
MemoryStoragein tests to avoid real filesystem operations - Flexibility: Swap storage backends (file, memory, cloud) without changing business logic
- Clean Architecture: Services depend on abstractions, not concrete implementations
When to Use This Skill
Activate this skill when:
- Implementing a new storage backend (cloud storage, database-backed, etc.)
- Adding file I/O operations to any service
- Writing unit tests for code that reads/writes files
- Refactoring existing code to be more testable
- Understanding dependency injection patterns in Python
Protocol Definition
The StorageProtocol defines the contract all storage implementations must follow:
from typing import Iterator, List, Optional, Protocol, Union
from pathlib import Path
PathLike = Union[str, Path]
class StorageProtocol(Protocol):
"""Protocol for storage operations.
This protocol defines the contract for all storage backends.
Implementations can be filesystem-based, in-memory, cloud-based, etc.
"""
def read(self, path: PathLike) -> Optional[str]:
"""Read content from a path. Returns None if not found."""
...
def read_bytes(self, path: PathLike) -> Optional[bytes]:
"""Read binary content from a path."""
...
def write(self, path: PathLike, content: str, mode: int = 0o644) -> None:
"""Write content to a path. Creates parent directories."""
...
def delete(self, path: PathLike) -> bool:
"""Delete a file. Returns True if deleted, False if not found."""
...
def exists(self, path: PathLike) -> bool:
"""Check if a path exists."""
...
def is_file(self, path: PathLike) -> bool:
"""Check if path is a file."""
...
def is_dir(self, path: PathLike) -> bool:
"""Check if path is a directory."""
...
def mkdir(self, path: PathLike, mode: int = 0o755, parents: bool = True) -> None:
"""Create a directory."""
...
def glob(self, pattern: str, path: PathLike = ".") -> Iterator[str]:
"""Find files matching a glob pattern."""
...
@property
def root(self) -> Path:
"""Get the root/base path for this storage."""
...
Key Design Decisions
- Return
Noneinstead of raising exceptions for missing files - Return
boolfor deletion operations to indicate success/failure - Auto-create parent directories in write operations
- All paths relative to
rootfor isolation - Use
PathLiketype alias for flexibility (str or Path)
Implementations
FileStorage (Production)
Uses the real filesystem via pathlib:
from context_harness.storage import FileStorage
# Storage rooted at home directory
storage = FileStorage(Path.home())
storage.write(".context-harness/config.json", '{"key": "value"}')
# Storage rooted at current directory (default)
storage = FileStorage()
storage.write("data/file.txt", "content")
MemoryStorage (Testing)
In-memory implementation using dictionaries:
from context_harness.storage import MemoryStorage
storage = MemoryStorage()
storage.write("config.json", '{"key": "value"}')
assert storage.read("config.json") == '{"key": "value"}'
assert storage.exists("config.json")
# Clean up between tests
storage.clear()
# Get snapshot of all contents
snapshot = storage.snapshot() # {"config.json": "{...}"}
Key Features:
- No filesystem side effects
- Fast execution (no I/O)
clear()method for test isolationsnapshot()for debugging/assertions
Using the Pattern in Services
Service with Storage Dependency
from context_harness.storage import StorageProtocol, FileStorage
class MyService:
"""Service that depends on storage abstraction."""
def __init__(self, storage: StorageProtocol):
self._storage = storage
def save_data(self, name: str, data: dict) -> None:
import json
content = json.dumps(data, indent=2)
self._storage.write(f"data/{name}.json", content)
def load_data(self, name: str) -> Optional[dict]:
import json
content = self._storage.read(f"data/{name}.json")
if content is None:
return None
return json.loads(content)
# Production usage
service = MyService(FileStorage())
# Test usage
from context_harness.storage import MemoryStorage
test_service = MyService(MemoryStorage())
Testing with MemoryStorage
Basic Test Pattern
import pytest
from context_harness.storage import MemoryStorage
class TestMyService:
def test_save_and_load(self) -> None:
storage = MemoryStorage()
service = MyService(storage)
service.save_data("user", {"name": "Alice"})
result = service.load_data("user")
assert result == {"name": "Alice"}
def test_load_missing_returns_none(self) -> None:
storage = MemoryStorage()
service = MyService(storage)
result = service.load_data("nonexistent")
assert result is None
Using Snapshot for Assertions
def test_multiple_writes() -> None:
storage = MemoryStorage()
storage.write("a.txt", "content a")
storage.write("b.txt", "content b")
snapshot = storage.snapshot()
assert snapshot == {
"a.txt": "content a",
"b.txt": "content b",
}
Best Practices
DO:
- Accept
StorageProtocolin constructors for dependency injection - Default to
FileStoragein production code - Use
MemoryStoragein all unit tests for speed and isolation - Call
storage.clear()in test setup/teardown if reusing storage - Use
Optional[str]returns for reads (None means not found)
DON'T:
- Don't hardcode
FileStoragein service implementations - Don't catch exceptions in storage methods - let implementations handle them
- Don't assume filesystem layout - use
rootproperty for base path - Don't use
tmp_pathfixture unnecessarily whenMemoryStoragesuffices
Common Patterns
Pattern: Factory Method for Testing
class MyService:
@classmethod
def create_for_testing(cls) -> "MyService":
return cls(storage=MemoryStorage())
Pattern: Pytest Fixture
import pytest
from context_harness.storage import MemoryStorage
@pytest.fixture
def storage() -> MemoryStorage:
return MemoryStorage()
@pytest.fixture
def service(storage: MemoryStorage) -> MyService:
return MyService(storage=storage)
References
Python Documentation
Project Files
src/context_harness/storage/protocol.py- Protocol definitionsrc/context_harness/storage/file_storage.py- FileStorage implementationsrc/context_harness/storage/memory_storage.py- MemoryStorage implementationtests/unit/storage/test_memory_storage.py- Comprehensive tests
Skill: python-storage-abstraction v1.0.0 | Last updated: 2025-12-30