fromscratch-pykv 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 (51) hide show
  1. fromscratch_pykv-0.4.0/.github/workflows/ci.yml +14 -0
  2. fromscratch_pykv-0.4.0/.github/workflows/docs.yml +16 -0
  3. fromscratch_pykv-0.4.0/.github/workflows/release.yml +58 -0
  4. fromscratch_pykv-0.4.0/.gitignore +11 -0
  5. fromscratch_pykv-0.4.0/.pre-commit-config.yaml +56 -0
  6. fromscratch_pykv-0.4.0/CLAUDE.md +52 -0
  7. fromscratch_pykv-0.4.0/LICENSE +21 -0
  8. fromscratch_pykv-0.4.0/PKG-INFO +108 -0
  9. fromscratch_pykv-0.4.0/README.md +76 -0
  10. fromscratch_pykv-0.4.0/conftest.py +21 -0
  11. fromscratch_pykv-0.4.0/docs/concepts/basics.md +78 -0
  12. fromscratch_pykv-0.4.0/docs/concepts/batch.md +68 -0
  13. fromscratch_pykv-0.4.0/docs/concepts/counters.md +75 -0
  14. fromscratch_pykv-0.4.0/docs/concepts/namespaces.md +157 -0
  15. fromscratch_pykv-0.4.0/docs/concepts/patterns.md +77 -0
  16. fromscratch_pykv-0.4.0/docs/concepts/persistence.md +110 -0
  17. fromscratch_pykv-0.4.0/docs/concepts/ttl.md +89 -0
  18. fromscratch_pykv-0.4.0/docs/concepts/types.md +76 -0
  19. fromscratch_pykv-0.4.0/docs/index.md +70 -0
  20. fromscratch_pykv-0.4.0/docs/property-testing.md +375 -0
  21. fromscratch_pykv-0.4.0/mkdocs.yml +83 -0
  22. fromscratch_pykv-0.4.0/overrides/main.html +7 -0
  23. fromscratch_pykv-0.4.0/pyproject.toml +156 -0
  24. fromscratch_pykv-0.4.0/src/pykv/__init__.py +73 -0
  25. fromscratch_pykv-0.4.0/src/pykv/_version.py +24 -0
  26. fromscratch_pykv-0.4.0/src/pykv/atomic.py +66 -0
  27. fromscratch_pykv-0.4.0/src/pykv/batch.py +110 -0
  28. fromscratch_pykv-0.4.0/src/pykv/counters.py +137 -0
  29. fromscratch_pykv-0.4.0/src/pykv/errors.py +68 -0
  30. fromscratch_pykv-0.4.0/src/pykv/namespace.py +234 -0
  31. fromscratch_pykv-0.4.0/src/pykv/patterns.py +85 -0
  32. fromscratch_pykv-0.4.0/src/pykv/persistence.py +133 -0
  33. fromscratch_pykv-0.4.0/src/pykv/py.typed +0 -0
  34. fromscratch_pykv-0.4.0/src/pykv/store.py +173 -0
  35. fromscratch_pykv-0.4.0/src/pykv/ttl.py +264 -0
  36. fromscratch_pykv-0.4.0/src/pykv/types.py +102 -0
  37. fromscratch_pykv-0.4.0/tests/__init__.py +1 -0
  38. fromscratch_pykv-0.4.0/tests/strategies.py +57 -0
  39. fromscratch_pykv-0.4.0/tests/test_atomic.py +75 -0
  40. fromscratch_pykv-0.4.0/tests/test_batch.py +98 -0
  41. fromscratch_pykv-0.4.0/tests/test_counters.py +116 -0
  42. fromscratch_pykv-0.4.0/tests/test_errors.py +75 -0
  43. fromscratch_pykv-0.4.0/tests/test_model.py +213 -0
  44. fromscratch_pykv-0.4.0/tests/test_namespace.py +246 -0
  45. fromscratch_pykv-0.4.0/tests/test_patterns.py +100 -0
  46. fromscratch_pykv-0.4.0/tests/test_persistence.py +207 -0
  47. fromscratch_pykv-0.4.0/tests/test_properties.py +113 -0
  48. fromscratch_pykv-0.4.0/tests/test_store.py +192 -0
  49. fromscratch_pykv-0.4.0/tests/test_ttl.py +204 -0
  50. fromscratch_pykv-0.4.0/tests/test_types.py +157 -0
  51. fromscratch_pykv-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-pykv/
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 80% 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,108 @@
1
+ Metadata-Version: 2.5
2
+ Name: fromscratch-pykv
3
+ Version: 0.4.0
4
+ Summary: An educational key-value store library built from scratch in Python
5
+ Project-URL: Homepage, https://github.com/robcox2910/pykv
6
+ Project-URL: Repository, https://github.com/robcox2910/pykv
7
+ Project-URL: Issues, https://github.com/robcox2910/pykv/issues
8
+ Project-URL: Documentation, https://robcox2910.github.io/pykv/
9
+ Author: Rob Cox
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: cache,education,key-value,store
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
+ # PyKV
34
+
35
+ An educational key-value store built from scratch in Python.
36
+
37
+ Part of the [PyLearn](https://github.com/robcox2910) series — learning computer science by building real tools.
38
+
39
+ ## What Is a Key-Value Store?
40
+
41
+ Think of a **phonebook**: you look up a name (the *key*) and get back a phone number (the *value*). A key-value store works the same way — it's the simplest kind of database.
42
+
43
+ ## Features
44
+
45
+ - **Get / Set / Delete** — basic phonebook operations
46
+ - **TTL (Time-To-Live)** — keys that expire, like milk with a use-by date
47
+ - **Multiple Data Types** — store strings, numbers, lists, and dicts
48
+ - **Persistence** — save to disk so data survives a restart
49
+ - **Namespaces** — group keys like folders in a filing cabinet
50
+ - **Batch Operations** — handle many keys at once, like a grocery list
51
+ - **Atomic Counters** — thread-safe increment/decrement, like a tally counter
52
+ - **Pattern Matching** — find keys matching a glob pattern
53
+
54
+ ## Quick Start
55
+
56
+ ```python
57
+ from pykv.store import BasicStore
58
+
59
+ store = BasicStore()
60
+ store.set("greeting", "hello")
61
+ print(store.get("greeting")) # "hello"
62
+ ```
63
+
64
+ ## Releasing
65
+
66
+ Other projects in the series ([PyStack](https://github.com/robcox2910/pystack)) use a pinned release of this one,
67
+ not whatever is on `main`. To publish a change for them:
68
+
69
+ 1. Merge the change to `main`.
70
+ 2. Tag it: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`.
71
+ A change to the public API bumps the minor version (`v0.1.0` → `v0.2.0`);
72
+ anything else bumps the patch (`v0.1.0` → `v0.1.1`).
73
+ 3. In each project that uses it, edit the tag in `[tool.uv.sources]`, run
74
+ `uv lock --upgrade-package <name>`, and open a pull request so CI checks
75
+ the pair together.
76
+
77
+ <!-- series:start -->
78
+ ## Related Projects
79
+
80
+ 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.
81
+
82
+ | Project | What it teaches | Repository | Docs |
83
+ | --- | --- | --- | --- |
84
+ | PyOS | Operating systems | [py-os](https://github.com/robcox2910/py-os) | [Docs](https://robcox2910.github.io/py-os/) |
85
+ | Pebble | Compilers and programming languages | [pebble-lang](https://github.com/robcox2910/pebble-lang) | [Docs](https://robcox2910.github.io/pebble-lang/) |
86
+ | PyDB | Relational databases | [pydb](https://github.com/robcox2910/pydb) | [Docs](https://robcox2910.github.io/pydb/) |
87
+ | **PyKV** | **Key-value stores** | [pykv](https://github.com/robcox2910/pykv) | [Docs](https://robcox2910.github.io/pykv/) |
88
+ | PyDocDB | Document databases | [pydoc-db](https://github.com/robcox2910/pydoc-db) | [Docs](https://robcox2910.github.io/pydoc-db/) |
89
+ | PyGraphDB | Graph databases | [pygraphdb](https://github.com/robcox2910/pygraphdb) | [Docs](https://robcox2910.github.io/pygraphdb/) |
90
+ | PyTSDB | Time-series databases | [pytsdb](https://github.com/robcox2910/pytsdb) | [Docs](https://robcox2910.github.io/pytsdb/) |
91
+ | PyVecDB | Vector databases | [pyvecdb](https://github.com/robcox2910/pyvecdb) | [Docs](https://robcox2910.github.io/pyvecdb/) |
92
+ | PyColDB | Column-family databases | [pycoldb](https://github.com/robcox2910/pycoldb) | [Docs](https://robcox2910.github.io/pycoldb/) |
93
+ | PyCrypt | Cryptography | [pycrypt](https://github.com/robcox2910/pycrypt) | [Docs](https://robcox2910.github.io/pycrypt/) |
94
+ | PyNet | Computer networking | [pynet](https://github.com/robcox2910/pynet) | [Docs](https://robcox2910.github.io/pynet/) |
95
+ | PyWeb | HTTP web servers | [pyweb](https://github.com/robcox2910/pyweb) | [Docs](https://robcox2910.github.io/pyweb/) |
96
+ | PyGit | Version control | [pygit](https://github.com/robcox2910/pygit) | [Docs](https://robcox2910.github.io/pygit/) |
97
+ | PySearch | Search engines | [pysearch](https://github.com/robcox2910/pysearch) | [Docs](https://robcox2910.github.io/pysearch/) |
98
+ | PyMQ | Message queues | [pymq](https://github.com/robcox2910/pymq) | [Docs](https://robcox2910.github.io/pymq/) |
99
+ | PyLLM | Language models (the brain) | [pyllm](https://github.com/robcox2910/pyllm) | [Docs](https://robcox2910.github.io/pyllm/) |
100
+ | PyScheduler | Job scheduling | [pyscheduler](https://github.com/robcox2910/pyscheduler) | [Docs](https://robcox2910.github.io/pyscheduler/) |
101
+ | PyStack | Full-stack integration | [pystack](https://github.com/robcox2910/pystack) | [Docs](https://robcox2910.github.io/pystack/) |
102
+ <!-- series:end -->
103
+
104
+ <!-- docs:start -->
105
+ ## Documentation
106
+
107
+ Full docs (with kid-friendly explanations of every idea): https://robcox2910.github.io/pykv/
108
+ <!-- docs:end -->
@@ -0,0 +1,76 @@
1
+ # PyKV
2
+
3
+ An educational key-value store built from scratch in Python.
4
+
5
+ Part of the [PyLearn](https://github.com/robcox2910) series — learning computer science by building real tools.
6
+
7
+ ## What Is a Key-Value Store?
8
+
9
+ Think of a **phonebook**: you look up a name (the *key*) and get back a phone number (the *value*). A key-value store works the same way — it's the simplest kind of database.
10
+
11
+ ## Features
12
+
13
+ - **Get / Set / Delete** — basic phonebook operations
14
+ - **TTL (Time-To-Live)** — keys that expire, like milk with a use-by date
15
+ - **Multiple Data Types** — store strings, numbers, lists, and dicts
16
+ - **Persistence** — save to disk so data survives a restart
17
+ - **Namespaces** — group keys like folders in a filing cabinet
18
+ - **Batch Operations** — handle many keys at once, like a grocery list
19
+ - **Atomic Counters** — thread-safe increment/decrement, like a tally counter
20
+ - **Pattern Matching** — find keys matching a glob pattern
21
+
22
+ ## Quick Start
23
+
24
+ ```python
25
+ from pykv.store import BasicStore
26
+
27
+ store = BasicStore()
28
+ store.set("greeting", "hello")
29
+ print(store.get("greeting")) # "hello"
30
+ ```
31
+
32
+ ## Releasing
33
+
34
+ Other projects in the series ([PyStack](https://github.com/robcox2910/pystack)) use a pinned release of this one,
35
+ not whatever is on `main`. To publish a change for them:
36
+
37
+ 1. Merge the change to `main`.
38
+ 2. Tag it: `git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`.
39
+ A change to the public API bumps the minor version (`v0.1.0` → `v0.2.0`);
40
+ anything else bumps the patch (`v0.1.0` → `v0.1.1`).
41
+ 3. In each project that uses it, edit the tag in `[tool.uv.sources]`, run
42
+ `uv lock --upgrade-package <name>`, and open a pull request so CI checks
43
+ the pair together.
44
+
45
+ <!-- series:start -->
46
+ ## Related Projects
47
+
48
+ 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.
49
+
50
+ | Project | What it teaches | Repository | Docs |
51
+ | --- | --- | --- | --- |
52
+ | PyOS | Operating systems | [py-os](https://github.com/robcox2910/py-os) | [Docs](https://robcox2910.github.io/py-os/) |
53
+ | Pebble | Compilers and programming languages | [pebble-lang](https://github.com/robcox2910/pebble-lang) | [Docs](https://robcox2910.github.io/pebble-lang/) |
54
+ | PyDB | Relational databases | [pydb](https://github.com/robcox2910/pydb) | [Docs](https://robcox2910.github.io/pydb/) |
55
+ | **PyKV** | **Key-value stores** | [pykv](https://github.com/robcox2910/pykv) | [Docs](https://robcox2910.github.io/pykv/) |
56
+ | PyDocDB | Document databases | [pydoc-db](https://github.com/robcox2910/pydoc-db) | [Docs](https://robcox2910.github.io/pydoc-db/) |
57
+ | PyGraphDB | Graph databases | [pygraphdb](https://github.com/robcox2910/pygraphdb) | [Docs](https://robcox2910.github.io/pygraphdb/) |
58
+ | PyTSDB | Time-series databases | [pytsdb](https://github.com/robcox2910/pytsdb) | [Docs](https://robcox2910.github.io/pytsdb/) |
59
+ | PyVecDB | Vector databases | [pyvecdb](https://github.com/robcox2910/pyvecdb) | [Docs](https://robcox2910.github.io/pyvecdb/) |
60
+ | PyColDB | Column-family databases | [pycoldb](https://github.com/robcox2910/pycoldb) | [Docs](https://robcox2910.github.io/pycoldb/) |
61
+ | PyCrypt | Cryptography | [pycrypt](https://github.com/robcox2910/pycrypt) | [Docs](https://robcox2910.github.io/pycrypt/) |
62
+ | PyNet | Computer networking | [pynet](https://github.com/robcox2910/pynet) | [Docs](https://robcox2910.github.io/pynet/) |
63
+ | PyWeb | HTTP web servers | [pyweb](https://github.com/robcox2910/pyweb) | [Docs](https://robcox2910.github.io/pyweb/) |
64
+ | PyGit | Version control | [pygit](https://github.com/robcox2910/pygit) | [Docs](https://robcox2910.github.io/pygit/) |
65
+ | PySearch | Search engines | [pysearch](https://github.com/robcox2910/pysearch) | [Docs](https://robcox2910.github.io/pysearch/) |
66
+ | PyMQ | Message queues | [pymq](https://github.com/robcox2910/pymq) | [Docs](https://robcox2910.github.io/pymq/) |
67
+ | PyLLM | Language models (the brain) | [pyllm](https://github.com/robcox2910/pyllm) | [Docs](https://robcox2910.github.io/pyllm/) |
68
+ | PyScheduler | Job scheduling | [pyscheduler](https://github.com/robcox2910/pyscheduler) | [Docs](https://robcox2910.github.io/pyscheduler/) |
69
+ | PyStack | Full-stack integration | [pystack](https://github.com/robcox2910/pystack) | [Docs](https://robcox2910.github.io/pystack/) |
70
+ <!-- series:end -->
71
+
72
+ <!-- docs:start -->
73
+ ## Documentation
74
+
75
+ Full docs (with kid-friendly explanations of every idea): https://robcox2910.github.io/pykv/
76
+ <!-- docs:end -->
@@ -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,78 @@
1
+ # Basics: Get, Set, Delete
2
+
3
+ ## The Notebook Analogy
4
+
5
+ A `BasicStore` is like a notebook where you write labels and information:
6
+
7
+ - **Set** = Write a new entry (or erase and rewrite an existing one)
8
+ - **Get** = Look up an entry by its label
9
+ - **Delete** = Erase an entry
10
+ - **Exists** = Check if a label is in the notebook (without reading it)
11
+ - **Keys** = List every label in the notebook
12
+
13
+ ## Creating a Store
14
+
15
+ ```python
16
+ from pykv import BasicStore
17
+
18
+ store = BasicStore()
19
+ ```
20
+
21
+ That's it! You now have an empty notebook ready to use.
22
+
23
+ ## Setting Values
24
+
25
+ ```python
26
+ store.set("name", "Alice")
27
+ store.set("age", 13)
28
+ store.set("favorite_colors", ["blue", "green"])
29
+ ```
30
+
31
+ If you set a key that already exists, the old value is replaced:
32
+
33
+ ```python
34
+ store.set("name", "Alice")
35
+ store.set("name", "Bob") # Overwrites "Alice"
36
+ store.get("name") # "Bob"
37
+ ```
38
+
39
+ ## Getting Values
40
+
41
+ ```python
42
+ name = store.get("name") # "Bob"
43
+ ```
44
+
45
+ If the key doesn't exist, you get an error:
46
+
47
+ ```python
48
+ store.get("nonexistent") # Raises KeyNotFoundError!
49
+ ```
50
+
51
+ To avoid the error, use `get_or_default`:
52
+
53
+ ```python
54
+ store.get_or_default("nickname", "N/A") # "N/A" (no error)
55
+ ```
56
+
57
+ ## Deleting Values
58
+
59
+ ```python
60
+ store.delete("name") # Returns True (it existed)
61
+ store.delete("name") # Returns False (already gone)
62
+ ```
63
+
64
+ ## Checking Existence
65
+
66
+ ```python
67
+ store.exists("age") # True
68
+ store.exists("ghost") # False
69
+ ```
70
+
71
+ ## Other Useful Methods
72
+
73
+ ```python
74
+ store.keys() # List all key names: ["age", "favorite_colors"]
75
+ store.size() # How many entries: 2
76
+ store.clear() # Erase everything
77
+ store.size() # 0
78
+ ```
@@ -0,0 +1,68 @@
1
+ # Batch Operations: Working with Many Keys at Once
2
+
3
+ ## The Grocery List Analogy
4
+
5
+ Imagine going to the grocery store. You could make a separate trip for each item -- one trip for milk, another for bread, another for eggs. That would be exhausting!
6
+
7
+ Instead, you write a **grocery list** and grab everything in one trip. Batch operations work the same way: instead of calling `set()` or `get()` one key at a time, you handle multiple keys in a single call.
8
+
9
+ > **Honest note:** Batch operations are about *convenience*, not raw speed. Under the hood they are a plain loop that still calls `set()` or `get()` one key at a time, and they are **not** atomic (they do not all-succeed-or-all-fail together). The win is cleaner, shorter code -- one call instead of many.
10
+
11
+ ## Batch Set
12
+
13
+ ```python
14
+ from pykv import BasicStore, batch_set
15
+
16
+ store = BasicStore()
17
+
18
+ # Set many keys at once (like filling your cart from a list)
19
+ batch_set(store, {
20
+ "player": "Alice",
21
+ "score": 9001,
22
+ "level": 42,
23
+ "health": 100,
24
+ })
25
+ ```
26
+
27
+ ## Batch Get
28
+
29
+ ```python
30
+ from pykv import batch_get
31
+
32
+ # Get many keys at once
33
+ result = batch_get(store, ["player", "score", "level"])
34
+ # {"player": "Alice", "score": 9001, "level": 42}
35
+ ```
36
+
37
+ If a key is missing, you get an error. To skip missing keys instead:
38
+
39
+ ```python
40
+ result = batch_get(store, ["player", "nonexistent"], skip_missing=True)
41
+ # {"player": "Alice"} (missing key silently skipped)
42
+ ```
43
+
44
+ ## Batch Delete
45
+
46
+ ```python
47
+ from pykv import batch_delete
48
+
49
+ # Delete many keys at once
50
+ deleted = batch_delete(store, ["score", "level", "ghost"])
51
+ print(deleted) # 2 (only 2 keys actually existed)
52
+ ```
53
+
54
+ ## Batch Exists
55
+
56
+ ```python
57
+ from pykv import batch_exists
58
+
59
+ result = batch_exists(store, ["player", "health", "ghost"])
60
+ # {"player": True, "health": True, "ghost": False}
61
+ ```
62
+
63
+ ## When to Use Batch Operations
64
+
65
+ - Loading initial data into the store
66
+ - Saving/restoring a group of related settings
67
+ - Cleaning up a group of temporary keys
68
+ - Checking which keys exist before processing
@@ -0,0 +1,75 @@
1
+ # Atomic Counters: Thread-Safe Counting
2
+
3
+ ## The Tally Counter Analogy
4
+
5
+ Have you seen those clicky tally counters that bouncers use to count people entering a venue? Every click adds exactly 1 to the count. Even if two bouncers click at the exact same time, the counter never gets confused -- each click is a single, unbreakable action.
6
+
7
+ That's what "atomic" means in programming: an operation that cannot be interrupted or corrupted, even when multiple things happen at the same time.
8
+
9
+ ## The Problem Without Atomic Operations
10
+
11
+ Imagine two threads (workers) both trying to increment the same counter:
12
+
13
+ ```
14
+ Counter = 5
15
+
16
+ Thread A reads: 5 Thread B reads: 5
17
+ Thread A writes: 6 Thread B writes: 6 (should be 7!)
18
+ ```
19
+
20
+ Both threads read 5 and wrote 6, so we lost an increment! The counter should be 7 but it's only 6. This is called a **race condition**.
21
+
22
+ ## Using AtomicCounter
23
+
24
+ ```python
25
+ from pykv import BasicStore, AtomicCounter
26
+
27
+ store = BasicStore()
28
+ counter = AtomicCounter(store)
29
+
30
+ # Increment (starts at 0 if the key doesn't exist)
31
+ counter.increment("visitors") # Returns 1
32
+ counter.increment("visitors") # Returns 2
33
+ counter.increment("visitors", 5) # Returns 7 (add 5 at once)
34
+
35
+ # Decrement
36
+ counter.decrement("visitors") # Returns 6
37
+ counter.decrement("visitors", 3) # Returns 3
38
+
39
+ # Check current value
40
+ counter.get_value("visitors") # 3
41
+
42
+ # Reset to zero
43
+ counter.reset("visitors")
44
+ counter.get_value("visitors") # 0
45
+ ```
46
+
47
+ ## Thread Safety
48
+
49
+ AtomicCounter uses a lock internally, so multiple threads can safely increment/decrement the same counter without race conditions:
50
+
51
+ ```python
52
+ import threading
53
+
54
+ counter = AtomicCounter(BasicStore())
55
+
56
+ def count_clicks():
57
+ for _ in range(1000):
58
+ counter.increment("clicks")
59
+
60
+ # Run 4 threads simultaneously
61
+ threads = [threading.Thread(target=count_clicks) for _ in range(4)]
62
+ for t in threads:
63
+ t.start()
64
+ for t in threads:
65
+ t.join()
66
+
67
+ print(counter.get_value("clicks")) # Always exactly 4000!
68
+ ```
69
+
70
+ ## Use Cases
71
+
72
+ - **Page view counters**: Track how many times a page is visited
73
+ - **Rate limiting**: Count requests per minute
74
+ - **Game scores**: Safely update scores from multiple sources
75
+ - **Inventory**: Track stock levels with concurrent updates