fromscratch-pycoldb 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. fromscratch_pycoldb-0.4.0/.github/workflows/ci.yml +14 -0
  2. fromscratch_pycoldb-0.4.0/.github/workflows/docs.yml +16 -0
  3. fromscratch_pycoldb-0.4.0/.github/workflows/release.yml +58 -0
  4. fromscratch_pycoldb-0.4.0/.gitignore +11 -0
  5. fromscratch_pycoldb-0.4.0/.pre-commit-config.yaml +56 -0
  6. fromscratch_pycoldb-0.4.0/CLAUDE.md +52 -0
  7. fromscratch_pycoldb-0.4.0/LICENSE +21 -0
  8. fromscratch_pycoldb-0.4.0/PKG-INFO +155 -0
  9. fromscratch_pycoldb-0.4.0/README.md +123 -0
  10. fromscratch_pycoldb-0.4.0/conftest.py +21 -0
  11. fromscratch_pycoldb-0.4.0/docs/concepts/column_families.md +32 -0
  12. fromscratch_pycoldb-0.4.0/docs/concepts/columns.md +35 -0
  13. fromscratch_pycoldb-0.4.0/docs/concepts/compaction.md +47 -0
  14. fromscratch_pycoldb-0.4.0/docs/concepts/keyspaces.md +38 -0
  15. fromscratch_pycoldb-0.4.0/docs/concepts/rows.md +35 -0
  16. fromscratch_pycoldb-0.4.0/docs/concepts/saving-safely.md +17 -0
  17. fromscratch_pycoldb-0.4.0/docs/concepts/tombstones.md +52 -0
  18. fromscratch_pycoldb-0.4.0/docs/concepts/ttl.md +38 -0
  19. fromscratch_pycoldb-0.4.0/docs/index.md +76 -0
  20. fromscratch_pycoldb-0.4.0/docs/property-testing.md +392 -0
  21. fromscratch_pycoldb-0.4.0/mkdocs.yml +83 -0
  22. fromscratch_pycoldb-0.4.0/overrides/main.html +7 -0
  23. fromscratch_pycoldb-0.4.0/pyproject.toml +156 -0
  24. fromscratch_pycoldb-0.4.0/src/pycoldb/__init__.py +65 -0
  25. fromscratch_pycoldb-0.4.0/src/pycoldb/_version.py +24 -0
  26. fromscratch_pycoldb-0.4.0/src/pycoldb/atomic.py +66 -0
  27. fromscratch_pycoldb-0.4.0/src/pycoldb/column.py +73 -0
  28. fromscratch_pycoldb-0.4.0/src/pycoldb/column_family.py +294 -0
  29. fromscratch_pycoldb-0.4.0/src/pycoldb/compaction.py +81 -0
  30. fromscratch_pycoldb-0.4.0/src/pycoldb/errors.py +86 -0
  31. fromscratch_pycoldb-0.4.0/src/pycoldb/keyspace.py +200 -0
  32. fromscratch_pycoldb-0.4.0/src/pycoldb/persistence.py +191 -0
  33. fromscratch_pycoldb-0.4.0/src/pycoldb/py.typed +0 -0
  34. fromscratch_pycoldb-0.4.0/src/pycoldb/query.py +140 -0
  35. fromscratch_pycoldb-0.4.0/src/pycoldb/row.py +249 -0
  36. fromscratch_pycoldb-0.4.0/src/pycoldb/tombstone.py +73 -0
  37. fromscratch_pycoldb-0.4.0/src/pycoldb/ttl.py +291 -0
  38. fromscratch_pycoldb-0.4.0/tests/__init__.py +1 -0
  39. fromscratch_pycoldb-0.4.0/tests/strategies.py +140 -0
  40. fromscratch_pycoldb-0.4.0/tests/test_atomic.py +75 -0
  41. fromscratch_pycoldb-0.4.0/tests/test_column.py +86 -0
  42. fromscratch_pycoldb-0.4.0/tests/test_column_family.py +284 -0
  43. fromscratch_pycoldb-0.4.0/tests/test_compaction.py +87 -0
  44. fromscratch_pycoldb-0.4.0/tests/test_errors.py +59 -0
  45. fromscratch_pycoldb-0.4.0/tests/test_keyspace.py +146 -0
  46. fromscratch_pycoldb-0.4.0/tests/test_model.py +303 -0
  47. fromscratch_pycoldb-0.4.0/tests/test_persistence.py +247 -0
  48. fromscratch_pycoldb-0.4.0/tests/test_properties.py +251 -0
  49. fromscratch_pycoldb-0.4.0/tests/test_query.py +141 -0
  50. fromscratch_pycoldb-0.4.0/tests/test_row.py +232 -0
  51. fromscratch_pycoldb-0.4.0/tests/test_tombstone.py +49 -0
  52. fromscratch_pycoldb-0.4.0/tests/test_ttl.py +275 -0
  53. fromscratch_pycoldb-0.4.0/uv.lock +920 -0
@@ -0,0 +1,14 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ ci:
14
+ uses: robcox2910/robcox2910.github.io/.github/workflows/python-ci.yml@v1
@@ -0,0 +1,16 @@
1
+ name: Deploy Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch: {}
7
+
8
+ jobs:
9
+ docs:
10
+ uses: robcox2910/robcox2910.github.io/.github/workflows/docs.yml@v1
11
+ permissions:
12
+ contents: read
13
+ pages: write
14
+ id-token: write
15
+ with:
16
+ coverage-report: false
@@ -0,0 +1,58 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ build:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v7
17
+ with:
18
+ fetch-depth: 0
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v7
22
+ with:
23
+ version: "latest"
24
+
25
+ - name: Set up Python
26
+ run: uv python install 3.14
27
+
28
+ - name: Build distributions
29
+ run: uv build
30
+
31
+ - name: Upload build artifacts
32
+ uses: actions/upload-artifact@v7
33
+ with:
34
+ name: dist
35
+ path: dist/
36
+
37
+ - name: Upload to GitHub Release
38
+ uses: softprops/action-gh-release@v3
39
+ with:
40
+ files: dist/*
41
+
42
+ publish:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: pypi
47
+ url: https://pypi.org/project/fromscratch-pycoldb/
48
+ permissions:
49
+ id-token: write
50
+ steps:
51
+ - name: Download build artifacts
52
+ uses: actions/download-artifact@v8
53
+ with:
54
+ name: dist
55
+ path: dist/
56
+
57
+ - name: Publish to PyPI
58
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .coverage
8
+ htmlcov/
9
+ .ruff_cache/
10
+ _version.py
11
+ .hypothesis/
@@ -0,0 +1,56 @@
1
+ default_language_version:
2
+ python: python3.14
3
+
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks
6
+ rev: v5.0.0
7
+ hooks:
8
+ # Pages paste real pytest output, trailing spaces included.
9
+ - id: trailing-whitespace
10
+ exclude: ^docs/property-testing\.md$
11
+ - id: end-of-file-fixer
12
+ exclude: ^docs/property-testing\.md$
13
+ - id: check-yaml
14
+ args: ['--unsafe']
15
+ - id: check-added-large-files
16
+ - id: check-json
17
+ - id: check-toml
18
+ - id: check-merge-conflict
19
+ - id: debug-statements
20
+
21
+ - repo: https://github.com/astral-sh/ruff-pre-commit
22
+ rev: v0.15.4
23
+ hooks:
24
+ - id: ruff
25
+ args: [--fix, --exit-non-zero-on-fix]
26
+ - id: ruff-format
27
+
28
+ - repo: https://github.com/RobertCraigie/pyright-python
29
+ rev: v1.1.408
30
+ hooks:
31
+ - id: pyright
32
+
33
+ - repo: local
34
+ hooks:
35
+ - id: pip-audit
36
+ name: pip-audit
37
+ language: system
38
+ pass_filenames: false
39
+ files: ^(pyproject\.toml|uv\.lock)$
40
+ entry: >-
41
+ bash -c 'f=$(mktemp) &&
42
+ uv export --frozen --no-emit-project --all-extras --no-hashes -o "$f" >/dev/null &&
43
+ uvx pip-audit==2.10.0 -r "$f" --disable-pip --no-deps;
44
+ status=$?; rm -f "$f"; exit $status'
45
+
46
+ - repo: https://github.com/rhysd/actionlint
47
+ rev: v1.7.7
48
+ hooks:
49
+ - id: actionlint
50
+
51
+ - repo: https://github.com/commitizen-tools/commitizen
52
+ rev: v4.13.8
53
+ hooks:
54
+ - id: commitizen
55
+ stages: [commit-msg]
56
+ additional_dependencies: [typing_extensions]
@@ -0,0 +1,52 @@
1
+ # Claude Code Instructions
2
+
3
+ <!-- standard:start -->
4
+ ## Code Style
5
+
6
+ - All public functions, methods, classes, and modules **must** have a docstring
7
+ - Use imperative mood: "Return the result." not "Returns the result."
8
+ - One-line docstrings for simple functions; multi-line with summary + details for complex ones
9
+ - Follow the Google docstring convention (D211, D212 selected via ruff)
10
+
11
+ ## Linting
12
+
13
+ - Ruff enforces a comprehensive rule set -- run `uv run ruff check .` before committing
14
+ - Pyright runs in strict mode -- run `uv run pyright src tests` before committing
15
+ - All violations must be fixed, not ignored. Exceptions are the repo-owned `ignore` and `per-file-ignores` lists in `pyproject.toml`, which belong to this repo; the series sync never edits them
16
+
17
+ ## Testing
18
+
19
+ - TDD workflow: Red-Green-Refactor
20
+ - Coverage is enforced by pytest-cov; the floor in `pyproject.toml` is repo-owned
21
+ - Test classes and methods also require docstrings
22
+ - Use named constants instead of magic values (PLR2004)
23
+ - Run `uv run pytest` before committing
24
+ - Property-based tests (Hypothesis) live in `tests/test_properties.py` and `tests/test_model.py`; the shared profiles are in the root `conftest.py` (owned by `series sync`). Run `uv run pytest --hypothesis-profile=thorough` before a release.
25
+
26
+ ## Commits
27
+
28
+ - Use conventional commits: `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `ci:`, `chore:`
29
+ - All changes go via PR from a feature branch (main is protected); merge when CI is green
30
+
31
+ ## Dependencies
32
+
33
+ - Package manager: uv
34
+ - Run `uv sync --all-extras` after modifying dependencies
35
+ - `uv.lock` is committed; CI and pre-commit audit it with pip-audit through `uvx`, so pip-audit is not a project dependency
36
+ - Sibling projects, where used, are pinned release tags in `[tool.uv.sources]` (git + tag); never use `path` sources
37
+
38
+ ## Releasing
39
+
40
+ - A release is a `vX.Y.Z` tag on main after the PR is merged
41
+ - Docs-only and tooling-only changes do not get a release tag
42
+ - Docs are built with `mkdocs build --strict` and deployed to GitHub Pages by the docs workflow
43
+ <!-- standard:end -->
44
+
45
+ ## Project notes
46
+
47
+ - Pyright in strict mode -- run `uv run pyright src tests` before committing
48
+ - All violations must be fixed, not ignored (except D203/D213 incompatible pair)
49
+ - Minimum 90% coverage enforced by pytest-cov
50
+ - Use conventional commits (enforced by commitizen): `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `ci:`, `chore:`
51
+ - All changes go via PR from a feature branch (main is protected)
52
+ - Add production deps to `[project.dependencies]`
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rob Cox
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.5
2
+ Name: fromscratch-pycoldb
3
+ Version: 0.4.0
4
+ Summary: An educational column-family store library built from scratch in Python
5
+ Project-URL: Homepage, https://github.com/robcox2910/pycoldb
6
+ Project-URL: Repository, https://github.com/robcox2910/pycoldb
7
+ Project-URL: Issues, https://github.com/robcox2910/pycoldb/issues
8
+ Project-URL: Documentation, https://robcox2910.github.io/pycoldb/
9
+ Author: Rob Cox
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: column-family,database,education,wide-column
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Education
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.14
22
+ Provides-Extra: dev
23
+ Requires-Dist: commitizen>=4.4.0; extra == 'dev'
24
+ Requires-Dist: hypothesis>=6.170; extra == 'dev'
25
+ Requires-Dist: pip-audit>=2.10.0; extra == 'dev'
26
+ Requires-Dist: pre-commit>=4.0.1; extra == 'dev'
27
+ Requires-Dist: pyright>=1.1.400; extra == 'dev'
28
+ Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
29
+ Requires-Dist: pytest>=8.3.4; extra == 'dev'
30
+ Requires-Dist: ruff>=0.9.3; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # PyColDB
34
+
35
+ An educational column-family store (wide-column database) built from scratch in Python. Part of the **PyLearn** platform for kids aged 12+.
36
+
37
+ ## What is a Column-Family Store?
38
+
39
+ A column-family store is like a spreadsheet where every row can have different columns. Instead of forcing every record to have the same fields, each row picks and chooses what data it stores. Real-world examples include Apache Cassandra and HBase.
40
+
41
+ ## Features
42
+
43
+ - **Columns** -- Name-value pairs with timestamps
44
+ - **Rows** -- Flexible records where each row has its own set of columns
45
+ - **Column Families** -- Named groups of rows (like gradebooks)
46
+ - **Keyspaces** -- Top-level containers for column families (like a school)
47
+ - **CRUD** -- Insert, read, update, and delete columns within rows
48
+ - **Range Queries** -- Get rows by key range (e.g., keys from "A" to "M")
49
+ - **Column Filtering** -- Get only specific columns from a row
50
+ - **TTL** -- Columns can expire after a time-to-live
51
+ - **Tombstones** -- Soft deletion with markers (like Cassandra)
52
+ - **Persistence** -- Save and load to JSON files
53
+ - **Compaction** -- Remove expired and deleted entries to reclaim space
54
+
55
+ ## Quick Start
56
+
57
+ ```python
58
+ from pycoldb import Keyspace, Column
59
+
60
+ # Create a keyspace (like a school)
61
+ school = Keyspace(name="school")
62
+
63
+ # Create a column family (like a gradebook)
64
+ school.create_column_family("grades")
65
+
66
+ # Add grades -- each student can have different columns!
67
+ school.put("grades", "alice", Column(name="math", value="A+"))
68
+ school.put("grades", "alice", Column(name="science", value="A"))
69
+ school.put("grades", "bob", Column(name="math", value="B"))
70
+ school.put("grades", "bob", Column(name="music", value="A"))
71
+
72
+ # Read a grade
73
+ grade = school.get_column("grades", "alice", "math")
74
+ print(f"Alice's math grade: {grade.value}") # A+
75
+
76
+ # Range query -- get students from "alice" to "bob"
77
+ from pycoldb import range_query
78
+ cf = school.get_column_family("grades")
79
+ rows = range_query(cf, start_key="alice", end_key="bob")
80
+ for row in rows:
81
+ print(f"{row.key}: {row.column_names()}")
82
+
83
+ # Save to disk
84
+ from pycoldb import save_keyspace, load_keyspace
85
+ save_keyspace(school, "school.json")
86
+ restored = load_keyspace("school.json")
87
+ ```
88
+
89
+ ## Installation
90
+
91
+ ```bash
92
+ # From source
93
+ git clone https://github.com/robcox2910/pycoldb.git
94
+ cd pycoldb
95
+ uv sync --all-extras
96
+ ```
97
+
98
+ ## Development
99
+
100
+ ```bash
101
+ uv run ruff check src/ tests/ # Lint
102
+ uv run ruff format src/ tests/ # Format
103
+ uv run pyright src tests # Type check
104
+ uv run pytest # Test (90%+ coverage)
105
+ ```
106
+
107
+ ## Releasing
108
+
109
+ Other projects in the series ([PyStack](https://github.com/robcox2910/pystack)) use a pinned release of this one,
110
+ not whatever is on `main`. To publish a change for them:
111
+
112
+ 1. Merge the change to `main`.
113
+ 2. Tag it: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`.
114
+ A change to the public API bumps the minor version (`v0.1.0` → `v0.2.0`);
115
+ anything else bumps the patch (`v0.1.0` → `v0.1.1`).
116
+ 3. In each project that uses it, edit the tag in `[tool.uv.sources]`, run
117
+ `uv lock --upgrade-package <name>`, and open a pull request so CI checks
118
+ the pair together.
119
+
120
+ <!-- series:start -->
121
+ ## Related Projects
122
+
123
+ This project is one of a series that builds real systems "from scratch in Python" to teach how they work. See the [series home](https://robcox2910.github.io/) for the full story.
124
+
125
+ | Project | What it teaches | Repository | Docs |
126
+ | --- | --- | --- | --- |
127
+ | PyOS | Operating systems | [py-os](https://github.com/robcox2910/py-os) | [Docs](https://robcox2910.github.io/py-os/) |
128
+ | Pebble | Compilers and programming languages | [pebble-lang](https://github.com/robcox2910/pebble-lang) | [Docs](https://robcox2910.github.io/pebble-lang/) |
129
+ | PyDB | Relational databases | [pydb](https://github.com/robcox2910/pydb) | [Docs](https://robcox2910.github.io/pydb/) |
130
+ | PyKV | Key-value stores | [pykv](https://github.com/robcox2910/pykv) | [Docs](https://robcox2910.github.io/pykv/) |
131
+ | PyDocDB | Document databases | [pydoc-db](https://github.com/robcox2910/pydoc-db) | [Docs](https://robcox2910.github.io/pydoc-db/) |
132
+ | PyGraphDB | Graph databases | [pygraphdb](https://github.com/robcox2910/pygraphdb) | [Docs](https://robcox2910.github.io/pygraphdb/) |
133
+ | PyTSDB | Time-series databases | [pytsdb](https://github.com/robcox2910/pytsdb) | [Docs](https://robcox2910.github.io/pytsdb/) |
134
+ | PyVecDB | Vector databases | [pyvecdb](https://github.com/robcox2910/pyvecdb) | [Docs](https://robcox2910.github.io/pyvecdb/) |
135
+ | **PyColDB** | **Column-family databases** | [pycoldb](https://github.com/robcox2910/pycoldb) | [Docs](https://robcox2910.github.io/pycoldb/) |
136
+ | PyCrypt | Cryptography | [pycrypt](https://github.com/robcox2910/pycrypt) | [Docs](https://robcox2910.github.io/pycrypt/) |
137
+ | PyNet | Computer networking | [pynet](https://github.com/robcox2910/pynet) | [Docs](https://robcox2910.github.io/pynet/) |
138
+ | PyWeb | HTTP web servers | [pyweb](https://github.com/robcox2910/pyweb) | [Docs](https://robcox2910.github.io/pyweb/) |
139
+ | PyGit | Version control | [pygit](https://github.com/robcox2910/pygit) | [Docs](https://robcox2910.github.io/pygit/) |
140
+ | PySearch | Search engines | [pysearch](https://github.com/robcox2910/pysearch) | [Docs](https://robcox2910.github.io/pysearch/) |
141
+ | PyMQ | Message queues | [pymq](https://github.com/robcox2910/pymq) | [Docs](https://robcox2910.github.io/pymq/) |
142
+ | PyLLM | Language models (the brain) | [pyllm](https://github.com/robcox2910/pyllm) | [Docs](https://robcox2910.github.io/pyllm/) |
143
+ | PyScheduler | Job scheduling | [pyscheduler](https://github.com/robcox2910/pyscheduler) | [Docs](https://robcox2910.github.io/pyscheduler/) |
144
+ | PyStack | Full-stack integration | [pystack](https://github.com/robcox2910/pystack) | [Docs](https://robcox2910.github.io/pystack/) |
145
+ <!-- series:end -->
146
+
147
+ <!-- docs:start -->
148
+ ## Documentation
149
+
150
+ Full docs (with kid-friendly explanations of every idea): https://robcox2910.github.io/pycoldb/
151
+ <!-- docs:end -->
152
+
153
+ ## License
154
+
155
+ MIT
@@ -0,0 +1,123 @@
1
+ # PyColDB
2
+
3
+ An educational column-family store (wide-column database) built from scratch in Python. Part of the **PyLearn** platform for kids aged 12+.
4
+
5
+ ## What is a Column-Family Store?
6
+
7
+ A column-family store is like a spreadsheet where every row can have different columns. Instead of forcing every record to have the same fields, each row picks and chooses what data it stores. Real-world examples include Apache Cassandra and HBase.
8
+
9
+ ## Features
10
+
11
+ - **Columns** -- Name-value pairs with timestamps
12
+ - **Rows** -- Flexible records where each row has its own set of columns
13
+ - **Column Families** -- Named groups of rows (like gradebooks)
14
+ - **Keyspaces** -- Top-level containers for column families (like a school)
15
+ - **CRUD** -- Insert, read, update, and delete columns within rows
16
+ - **Range Queries** -- Get rows by key range (e.g., keys from "A" to "M")
17
+ - **Column Filtering** -- Get only specific columns from a row
18
+ - **TTL** -- Columns can expire after a time-to-live
19
+ - **Tombstones** -- Soft deletion with markers (like Cassandra)
20
+ - **Persistence** -- Save and load to JSON files
21
+ - **Compaction** -- Remove expired and deleted entries to reclaim space
22
+
23
+ ## Quick Start
24
+
25
+ ```python
26
+ from pycoldb import Keyspace, Column
27
+
28
+ # Create a keyspace (like a school)
29
+ school = Keyspace(name="school")
30
+
31
+ # Create a column family (like a gradebook)
32
+ school.create_column_family("grades")
33
+
34
+ # Add grades -- each student can have different columns!
35
+ school.put("grades", "alice", Column(name="math", value="A+"))
36
+ school.put("grades", "alice", Column(name="science", value="A"))
37
+ school.put("grades", "bob", Column(name="math", value="B"))
38
+ school.put("grades", "bob", Column(name="music", value="A"))
39
+
40
+ # Read a grade
41
+ grade = school.get_column("grades", "alice", "math")
42
+ print(f"Alice's math grade: {grade.value}") # A+
43
+
44
+ # Range query -- get students from "alice" to "bob"
45
+ from pycoldb import range_query
46
+ cf = school.get_column_family("grades")
47
+ rows = range_query(cf, start_key="alice", end_key="bob")
48
+ for row in rows:
49
+ print(f"{row.key}: {row.column_names()}")
50
+
51
+ # Save to disk
52
+ from pycoldb import save_keyspace, load_keyspace
53
+ save_keyspace(school, "school.json")
54
+ restored = load_keyspace("school.json")
55
+ ```
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ # From source
61
+ git clone https://github.com/robcox2910/pycoldb.git
62
+ cd pycoldb
63
+ uv sync --all-extras
64
+ ```
65
+
66
+ ## Development
67
+
68
+ ```bash
69
+ uv run ruff check src/ tests/ # Lint
70
+ uv run ruff format src/ tests/ # Format
71
+ uv run pyright src tests # Type check
72
+ uv run pytest # Test (90%+ coverage)
73
+ ```
74
+
75
+ ## Releasing
76
+
77
+ Other projects in the series ([PyStack](https://github.com/robcox2910/pystack)) use a pinned release of this one,
78
+ not whatever is on `main`. To publish a change for them:
79
+
80
+ 1. Merge the change to `main`.
81
+ 2. Tag it: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`.
82
+ A change to the public API bumps the minor version (`v0.1.0` → `v0.2.0`);
83
+ anything else bumps the patch (`v0.1.0` → `v0.1.1`).
84
+ 3. In each project that uses it, edit the tag in `[tool.uv.sources]`, run
85
+ `uv lock --upgrade-package <name>`, and open a pull request so CI checks
86
+ the pair together.
87
+
88
+ <!-- series:start -->
89
+ ## Related Projects
90
+
91
+ This project is one of a series that builds real systems "from scratch in Python" to teach how they work. See the [series home](https://robcox2910.github.io/) for the full story.
92
+
93
+ | Project | What it teaches | Repository | Docs |
94
+ | --- | --- | --- | --- |
95
+ | PyOS | Operating systems | [py-os](https://github.com/robcox2910/py-os) | [Docs](https://robcox2910.github.io/py-os/) |
96
+ | Pebble | Compilers and programming languages | [pebble-lang](https://github.com/robcox2910/pebble-lang) | [Docs](https://robcox2910.github.io/pebble-lang/) |
97
+ | PyDB | Relational databases | [pydb](https://github.com/robcox2910/pydb) | [Docs](https://robcox2910.github.io/pydb/) |
98
+ | PyKV | Key-value stores | [pykv](https://github.com/robcox2910/pykv) | [Docs](https://robcox2910.github.io/pykv/) |
99
+ | PyDocDB | Document databases | [pydoc-db](https://github.com/robcox2910/pydoc-db) | [Docs](https://robcox2910.github.io/pydoc-db/) |
100
+ | PyGraphDB | Graph databases | [pygraphdb](https://github.com/robcox2910/pygraphdb) | [Docs](https://robcox2910.github.io/pygraphdb/) |
101
+ | PyTSDB | Time-series databases | [pytsdb](https://github.com/robcox2910/pytsdb) | [Docs](https://robcox2910.github.io/pytsdb/) |
102
+ | PyVecDB | Vector databases | [pyvecdb](https://github.com/robcox2910/pyvecdb) | [Docs](https://robcox2910.github.io/pyvecdb/) |
103
+ | **PyColDB** | **Column-family databases** | [pycoldb](https://github.com/robcox2910/pycoldb) | [Docs](https://robcox2910.github.io/pycoldb/) |
104
+ | PyCrypt | Cryptography | [pycrypt](https://github.com/robcox2910/pycrypt) | [Docs](https://robcox2910.github.io/pycrypt/) |
105
+ | PyNet | Computer networking | [pynet](https://github.com/robcox2910/pynet) | [Docs](https://robcox2910.github.io/pynet/) |
106
+ | PyWeb | HTTP web servers | [pyweb](https://github.com/robcox2910/pyweb) | [Docs](https://robcox2910.github.io/pyweb/) |
107
+ | PyGit | Version control | [pygit](https://github.com/robcox2910/pygit) | [Docs](https://robcox2910.github.io/pygit/) |
108
+ | PySearch | Search engines | [pysearch](https://github.com/robcox2910/pysearch) | [Docs](https://robcox2910.github.io/pysearch/) |
109
+ | PyMQ | Message queues | [pymq](https://github.com/robcox2910/pymq) | [Docs](https://robcox2910.github.io/pymq/) |
110
+ | PyLLM | Language models (the brain) | [pyllm](https://github.com/robcox2910/pyllm) | [Docs](https://robcox2910.github.io/pyllm/) |
111
+ | PyScheduler | Job scheduling | [pyscheduler](https://github.com/robcox2910/pyscheduler) | [Docs](https://robcox2910.github.io/pyscheduler/) |
112
+ | PyStack | Full-stack integration | [pystack](https://github.com/robcox2910/pystack) | [Docs](https://robcox2910.github.io/pystack/) |
113
+ <!-- series:end -->
114
+
115
+ <!-- docs:start -->
116
+ ## Documentation
117
+
118
+ Full docs (with kid-friendly explanations of every idea): https://robcox2910.github.io/pycoldb/
119
+ <!-- docs:end -->
120
+
121
+ ## License
122
+
123
+ MIT
@@ -0,0 +1,21 @@
1
+ """Shared Hypothesis profiles for the series.
2
+
3
+ Two profiles are registered, and ``ci`` is loaded by default:
4
+
5
+ - ``ci`` runs 100 examples with no deadline, a fixed seed and no example
6
+ database, so the same code gives the same result on every machine and a
7
+ green build can be trusted.
8
+ - ``thorough`` runs 2000 examples with a random seed, to hunt for the rare
9
+ counterexample before a release.
10
+
11
+ Run the long one with ``uv run pytest --hypothesis-profile=thorough``.
12
+
13
+ This file is owned by ``series sync``. Edit it in the series home repo
14
+ (robcox2910.github.io), not here, or the next sync will put it back.
15
+ """
16
+
17
+ from hypothesis import settings
18
+
19
+ settings.register_profile("ci", max_examples=100, deadline=None, derandomize=True, database=None)
20
+ settings.register_profile("thorough", max_examples=2000, deadline=None, derandomize=False)
21
+ settings.load_profile("ci")
@@ -0,0 +1,32 @@
1
+ # Column Families -- Gradebooks
2
+
3
+ ## What is a Column Family?
4
+
5
+ A column family is like a **teacher's gradebook**. It's a named collection of rows (student records), where each row can have different columns.
6
+
7
+ Think of your school having different gradebooks:
8
+ - The **"grades"** gradebook has math scores, science scores, etc.
9
+ - The **"attendance"** gradebook has Monday, Tuesday, Wednesday entries
10
+ - The **"contacts"** gradebook has phone numbers and emails
11
+
12
+ Each gradebook (column family) groups related data together.
13
+
14
+ ```python
15
+ from pycoldb import ColumnFamily, Column
16
+
17
+ # Create a gradebook for grades
18
+ grades = ColumnFamily(name="grades")
19
+
20
+ # Add student grades
21
+ grades.put("alice", Column(name="math", value="A+"))
22
+ grades.put("alice", Column(name="science", value="A"))
23
+ grades.put("bob", Column(name="math", value="B"))
24
+
25
+ # How many students have grades?
26
+ print(grades.row_count) # 2
27
+ print(grades.row_keys()) # ['alice', 'bob']
28
+ ```
29
+
30
+ ## Why Not Just One Big Table?
31
+
32
+ Separating data into column families makes things faster. When you look up someone's grades, you don't have to read through their attendance records too. It's like having separate filing cabinets for different types of documents.
@@ -0,0 +1,35 @@
1
+ # Columns -- The Building Blocks
2
+
3
+ ## What is a Column?
4
+
5
+ A column is like a **single cell** in a spreadsheet, but smarter. Each column has three parts:
6
+
7
+ 1. **Name** -- The column header (like "Math Grade")
8
+ 2. **Value** -- The actual data (like "A+" or 95)
9
+ 3. **Timestamp** -- When this data was written (like a date stamp)
10
+
11
+ ## Why Timestamps?
12
+
13
+ Imagine you and your friend both try to update Alice's math grade at the same time. You write "A+" and your friend writes "A-". Which one wins?
14
+
15
+ The **timestamp** solves this! Whichever write happened most recently wins. It's like a "last edited" date on a Google Doc.
16
+
17
+ ```python
18
+ from pycoldb import Column
19
+
20
+ # A grade recorded at time 1000
21
+ grade_v1 = Column(name="math", value="B", timestamp=1000)
22
+
23
+ # Updated later at time 2000
24
+ grade_v2 = Column(name="math", value="A+", timestamp=2000)
25
+
26
+ # The column with timestamp 2000 is the most recent
27
+ ```
28
+
29
+ ## What Can Go in a Column?
30
+
31
+ Columns can hold different types of values:
32
+ - **Strings**: `"A+"`, `"present"`, `"blue"`
33
+ - **Numbers**: `95`, `3.14`
34
+ - **Booleans**: `True`, `False`
35
+ - **None**: For "no value" / "unknown"
@@ -0,0 +1,47 @@
1
+ # Compaction -- Cleaning Up
2
+
3
+ ## What is Compaction?
4
+
5
+ After a while, your data can get messy. You've got:
6
+ - **Tombstones** (crossed-out data waiting to be cleaned up)
7
+ - **Expired columns** (sticky notes whose time has run out)
8
+
9
+ **Compaction** is like going through your messy notebook and rewriting everything on a fresh page, leaving out all the crossed-out stuff and expired sticky notes.
10
+
11
+ ## Why Compact?
12
+
13
+ 1. **Save space** -- Tombstones and expired data take up room
14
+ 2. **Speed up reads** -- Less clutter means faster lookups
15
+ 3. **Keep things tidy** -- A clean database is a happy database
16
+
17
+ ```python
18
+ from pycoldb import ColumnFamily, Column
19
+ from pycoldb.compaction import compact_column_family
20
+
21
+ grades = ColumnFamily(name="grades")
22
+
23
+ # Add some data
24
+ grades.put("alice", Column(name="math", value="A+"))
25
+ grades.put("alice", Column(name="science", value="A"))
26
+ grades.put("bob", Column(name="math", value="B"))
27
+
28
+ # Delete some data (creates tombstones)
29
+ grades.delete_column("alice", "science")
30
+ grades.delete_column("bob", "math")
31
+
32
+ # Compact! Returns how many entries were cleaned up
33
+ cleaned = compact_column_family(grades)
34
+ print(f"Cleaned up {cleaned} entries") # Cleaned up 2 entries
35
+ ```
36
+
37
+ ## Compaction Never Changes an Answer
38
+
39
+ Compaction is tidying, not editing. Whatever a read returned just before
40
+ compaction, it returns just after. That's why a row whose grades have all
41
+ been deleted or have expired already counts as missing *before* compaction:
42
+ `get_row` raises `RowNotFoundError`, exactly as it will once compaction has
43
+ thrown the empty record card away.
44
+
45
+ ## When Does Compaction Happen?
46
+
47
+ In real databases like Cassandra, compaction runs automatically in the background. In our educational version, you call it manually whenever you want to clean up. This lets you see exactly what's happening and learn how it works!