Thread-safe, time-versioned key-value store with snapshots and diffs.
Project description
ChronoMap
ChronoMap is a thread-safe, time-versioned key-value store for Python that maintains complete history of all changes. Perfect for configuration management, audit trails, time-series data, and any application requiring historical data tracking.
โจ Features
- โฑ๏ธ Time-Versioned Storage - Every value change is timestamped and preserved
- ๐ Thread-Safe - All operations are protected with locks for concurrent access
- ๐ธ Snapshots & Rollback - Create snapshots and rollback to any previous state
- โฐ TTL Support - Automatic key expiration with time-to-live
- ๐ Batch Operations - Efficient
put_many()anddelete_many()operations - ๐ Advanced Queries - Range queries, find latest keys, search by value
- ๐ค Merge & Diff - Merge multiple maps and track differences
- ๐พ Persistence - Save/load from JSON or Pickle
- ๐ Pythonic API - Dictionary-like interface with magic methods
- ๐ Comprehensive Testing - 65 tests with >90% coverage
๐ฆ Installation
pip install chronomap
๐ Quick Start
from chronomap import ChronoMap
cm = ChronoMap()
cm['user'] = 'alice'
cm['status'] = 'active'
print(cm['user'])
if 'user' in cm:
print(f"Found {len(cm)} keys")
for key, value in cm.items():
print(f"{key}: {value}")
๐ Documentation
Basic Operations
cm.put('key', 'value')
value = cm.get('key')
cm.delete('key')
cm['key'] = 'value'
value = cm['key']
del cm['key']
if 'key' in cm:
print("Key exists")
value = cm.get('nonexistent', default='default_value')
try:
value = cm.get('nonexistent', strict=True)
except ChronoMapKeyError:
print("Key not found")
Time-Versioned Storage
cm.put('temperature', 20.5, timestamp=1609459200)
cm.put('temperature', 21.0, timestamp=1609462800)
cm.put('temperature', 22.5, timestamp=1609466400)
temp_at_1am = cm.get('temperature', timestamp=1609462800)
print(temp_at_1am)
latest = cm.get('temperature')
print(latest)
history = cm.history('temperature')
for timestamp, value in cm.iter_history('temperature'):
print(f"{timestamp}: {value}")
TTL and Auto-Expiry
cm.put('session_token', 'abc123', ttl=3600)
print(cm.get('session_token'))
removed_count = cm.clean_expired_keys()
print(f"Removed {removed_count} expired keys")
Snapshots and Rollback
cm['counter'] = 10
cm['status'] = 'active'
snapshot = cm.snapshot()
cm['counter'] = 100
cm['status'] = 'inactive'
cm['new_key'] = 'new_value'
cm.rollback(snapshot)
print(cm['counter'])
print(cm['status'])
print(cm.get('new_key'))
Batch Operations
users = {
'user:1': {'name': 'Alice', 'role': 'admin'},
'user:2': {'name': 'Bob', 'role': 'user'},
'user:3': {'name': 'Charlie', 'role': 'user'}
}
cm.put_many(users)
cm.put_many(users, ttl=3600)
cm.put_many(users, timestamp=1609459200)
deleted_count = cm.delete_many(['user:2', 'user:3'])
print(f"Deleted {deleted_count} keys")
Advanced Queries
cm.put('sensor', 10, timestamp=100)
cm.put('sensor', 15, timestamp=200)
cm.put('sensor', 20, timestamp=300)
readings = cm.get_range('sensor', start_ts=150, end_ts=250)
latest = cm.get_latest_keys(2)
for key, timestamp, value in latest:
print(f"{key}: {value} (updated at {timestamp})")
cm.put_many({'user1': 'active', 'user2': 'active', 'user3': 'inactive'})
active_users = cm.get_keys_by_value('active')
Merge and Diff
cm1 = ChronoMap()
cm2 = ChronoMap()
cm1.put('shared', 'v1', timestamp=100)
cm2.put('shared', 'v2', timestamp=200)
cm2.put('unique', 'data')
cm1.merge(cm2, strategy='timestamp')
cm1.merge(cm2, strategy='overwrite')
changed_keys = cm1.diff(cm2)
changes = cm1.diff_detailed(cm2)
for key, old_val, new_val in changes:
print(f"{key}: {old_val} -> {new_val}")
Persistence
cm.save_json('state.json')
cm_loaded = ChronoMap.load_json('state.json')
cm.save_pickle('state.pkl')
cm_loaded = ChronoMap.load_pickle('state.pkl')
data = cm.to_dict()
cm_restored = ChronoMap.from_dict(data)
Iteration
cm.put_many({'a': 1, 'b': 2, 'c': 3})
for key in cm:
print(key)
for key in cm.keys():
print(key)
for value in cm.values():
print(value)
for key, value in cm.items():
print(f"{key}: {value}")
latest = cm.latest()
Utility Models
print(len(cm))
if not cm:
print("Map is empty")
cm.clear()
print(repr(cm))
snap = cm.snapshot()
print(snap.snapshot_time)
๐งช Testing
pytest tests/test_chronomap.py -v
pytest tests/test_chronomap.py --cov=chronomap --cov-report=html
pytest tests/test_chronomap.py::TestBasicOperations -v
pytest tests/test_chronomap.py -v -s
Test coverage includes:
- โ Basic operations (put, get, delete)
- โ TTL and expiry
- โ Batch operations
- โ Advanced queries
- โ Snapshots and rollback
- โ Merge and diff
- โ Magic methods
- โ Iteration
- โ Persistence (JSON, Pickle)
- โ Thread safety
- โ Edge cases
- โ Integration scenarios
Project Structure
chronomap/
โโโ chronomap/
โ โโโ __init__.py
โ โโโ chronomap.py # Core implementation
โ โโโ __main__.py # CLI interface
โโโ tests/
โ โโโ test_chronomap.py # Test suite
โโโ examples/
โ โโโ config_manager.py
โ โโโ session_store.py
โ โโโ metrics_store.py
โโโ docs/
โ โโโ examples.md
โโโ README.md
โโโ setup.py
โโโ pyproject.toml
โโโ LICENSE
Contributing
- Fork the repository
- Write tests for your changes
- Ensure all tests pass (
pytest tests/ -v) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
๐ Requirements
- Python 3.7+
- No external dependencies for core functionality
- Development dependencies: pytest, pytest-cov
๐บ๏ธ Roadmap
- Async/await support
- SQLite backend for persistence
- Compression for large histories
- Query language for complex searches
- Web UI for visualization
- Export to various formats (CSV, Parquet)
- Integration with popular frameworks (Django, Flask)
๐ Changelog
v2.0.0 (2025)
- Complete rewrite with enhanced features
- Added TTL/expiry support
- Added batch operations
- Added advanced queries (range, latest keys, search by value)
- Added merge and diff functionality
- Added comprehensive test suite (65 tests)
- Improved thread safety
- Enhanced documentation
v1.0.0
- Initial release
- Basic time-versioned storage
- Snapshot and rollback
- Persistence support
๐ก Tips and Best Practices
- Use TTL for temporary data - Session tokens, cache entries, temporary flags
- Take snapshots before risky operations - Database migrations, bulk updates
- Use batch operations - More efficient than individual operations
- Clean expired keys regularly - Call
clean_expired_keys()during maintenance - Leverage history for auditing - Track configuration changes, document versions
- Use descriptive key naming -
user:123:profileis better thanu123p - Persist regularly - Save state periodically using
save_json()orsave_pickle() - Monitor map size - Large histories may need archiving or cleanup
โ FAQ
Q: Is ChronoMap suitable for production use?
A: Yes! ChronoMap is thread-safe, well-tested, and used in production environments.
Q: How much memory does ChronoMap use?
A: Memory usage depends on the number of keys and history size. Each value change is stored, so keys with frequent updates will use more memory.
Q: Can I use ChronoMap as a database?
A: ChronoMap is an in-memory store. For persistence, use save_json() or save_pickle(). For large-scale data, consider a proper database.
Q: How do I limit history size?
A: Currently, you need to manually manage history. Consider periodically archiving old data or implementing custom cleanup logic.
Q: Is ChronoMap compatible with multiprocessing?
A: ChronoMap is thread-safe but not process-safe. For multiprocessing, use separate instances or implement inter-process communication.
Q: Can I use ChronoMap with Django/Flask?
A: Yes! ChronoMap works well as a cache layer or session store in web applications.
๐ License
This project is licensed under the MIT License
Made with ๐ by Devansh Singh
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chronomap-2.0.2.tar.gz.
File metadata
- Download URL: chronomap-2.0.2.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a49f375bd0a06a309057f77d8a11bdabab926fff424be6f4c5fc754d5bc8632
|
|
| MD5 |
b531ed5923057be86e66df7d344e32e3
|
|
| BLAKE2b-256 |
16515ecff594c38fe6a433051e2199081ef2bff7f588333525e6c5408f46e0e3
|
File details
Details for the file chronomap-2.0.2-py3-none-any.whl.
File metadata
- Download URL: chronomap-2.0.2-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a155924bb18fda9076a3f38ae0658bba9caed76042aa735b7ffc15eedc5a07c0
|
|
| MD5 |
08f5933b0a8080b96bf511ee3c0d2e01
|
|
| BLAKE2b-256 |
c34ffdff9f2c258464c3e1eb0f540aee0dbe73775a25a4be405b30641642c995
|