redis-queen 0.2.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 (43) hide show
  1. redis_queen-0.2.0/.github/workflows/ci.yml +44 -0
  2. redis_queen-0.2.0/.github/workflows/conventional-commits.yml +15 -0
  3. redis_queen-0.2.0/.github/workflows/release-please.yml +40 -0
  4. redis_queen-0.2.0/.gitignore +5 -0
  5. redis_queen-0.2.0/.pre-commit-config.yaml +21 -0
  6. redis_queen-0.2.0/.python-version +1 -0
  7. redis_queen-0.2.0/.release-please-manifest.json +3 -0
  8. redis_queen-0.2.0/CHANGELOG.md +42 -0
  9. redis_queen-0.2.0/LICENSE +21 -0
  10. redis_queen-0.2.0/PKG-INFO +216 -0
  11. redis_queen-0.2.0/README.md +197 -0
  12. redis_queen-0.2.0/pyproject.toml +65 -0
  13. redis_queen-0.2.0/release-please-config.json +18 -0
  14. redis_queen-0.2.0/src/redis_queen/__init__.py +31 -0
  15. redis_queen-0.2.0/src/redis_queen/__main__.py +11 -0
  16. redis_queen-0.2.0/src/redis_queen/_codegen.py +285 -0
  17. redis_queen-0.2.0/src/redis_queen/_config.py +200 -0
  18. redis_queen-0.2.0/src/redis_queen/_decorator.py +168 -0
  19. redis_queen-0.2.0/src/redis_queen/_diff.py +56 -0
  20. redis_queen-0.2.0/src/redis_queen/_discovery.py +122 -0
  21. redis_queen-0.2.0/src/redis_queen/_migration.py +219 -0
  22. redis_queen-0.2.0/src/redis_queen/_revision.py +188 -0
  23. redis_queen-0.2.0/src/redis_queen/_state.py +37 -0
  24. redis_queen-0.2.0/src/redis_queen/_types.py +71 -0
  25. redis_queen-0.2.0/src/redis_queen/_util.py +125 -0
  26. redis_queen-0.2.0/src/redis_queen/commands/__init__.py +33 -0
  27. redis_queen-0.2.0/src/redis_queen/commands/diff.py +55 -0
  28. redis_queen-0.2.0/src/redis_queen/commands/down.py +70 -0
  29. redis_queen-0.2.0/src/redis_queen/commands/reset.py +97 -0
  30. redis_queen-0.2.0/src/redis_queen/commands/revision.py +79 -0
  31. redis_queen-0.2.0/src/redis_queen/commands/show.py +47 -0
  32. redis_queen-0.2.0/src/redis_queen/commands/up.py +67 -0
  33. redis_queen-0.2.0/src/redis_queen/py.typed +0 -0
  34. redis_queen-0.2.0/src/redis_queen/types/__init__.py +27 -0
  35. redis_queen-0.2.0/tests/__init__.py +0 -0
  36. redis_queen-0.2.0/tests/conftest.py +55 -0
  37. redis_queen-0.2.0/tests/test_config.py +140 -0
  38. redis_queen-0.2.0/tests/test_decorator.py +171 -0
  39. redis_queen-0.2.0/tests/test_diff.py +106 -0
  40. redis_queen-0.2.0/tests/test_migration.py +103 -0
  41. redis_queen-0.2.0/tests/test_revision.py +106 -0
  42. redis_queen-0.2.0/tests/test_util.py +132 -0
  43. redis_queen-0.2.0/uv.lock +459 -0
@@ -0,0 +1,44 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ ruff:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v6
15
+ - uses: astral-sh/setup-uv@v7
16
+ with:
17
+ enable-cache: true
18
+ - run: uv sync --dev
19
+ - run: uv run ruff check .
20
+ - run: uv run ruff format --check .
21
+
22
+ ty:
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - uses: actions/checkout@v6
26
+ - uses: astral-sh/setup-uv@v7
27
+ with:
28
+ enable-cache: true
29
+ - run: uv sync --dev
30
+ - run: uv run ty check src/
31
+
32
+ pytest:
33
+ runs-on: ubuntu-latest
34
+ strategy:
35
+ matrix:
36
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
37
+ steps:
38
+ - uses: actions/checkout@v6
39
+ - uses: astral-sh/setup-uv@v7
40
+ with:
41
+ enable-cache: true
42
+ - run: uv python install ${{ matrix.python-version }}
43
+ - run: uv sync --dev --python ${{ matrix.python-version }}
44
+ - run: uv run --python ${{ matrix.python-version }} pytest
@@ -0,0 +1,15 @@
1
+ name: Conventional Commits
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ conventional-commits:
9
+ if: ${{ !startsWith(github.head_ref, 'release-please--') }}
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v6
13
+ with:
14
+ fetch-depth: 0
15
+ - uses: webiny/action-conventional-commits@v1.3.0
@@ -0,0 +1,40 @@
1
+ name: Release Please
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: write
9
+ pull-requests: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ release-please:
14
+ runs-on: ubuntu-latest
15
+ outputs:
16
+ release_created: ${{ steps.release.outputs.release_created }}
17
+ steps:
18
+ - uses: googleapis/release-please-action@v4
19
+ id: release
20
+ with:
21
+ token: ${{ secrets.GITHUB_TOKEN }}
22
+ config-file: release-please-config.json
23
+ manifest-file: .release-please-manifest.json
24
+
25
+ publish:
26
+ needs: release-please
27
+ if: needs.release-please.outputs.release_created == 'true'
28
+ runs-on: ubuntu-latest
29
+ environment:
30
+ name: pypi
31
+ url: https://pypi.org/p/redis-queen
32
+ permissions:
33
+ id-token: write
34
+ steps:
35
+ - uses: actions/checkout@v6
36
+ - uses: astral-sh/setup-uv@v7
37
+ with:
38
+ enable-cache: true
39
+ - run: uv build
40
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,5 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-check
5
+ name: ruff check
6
+ entry: uv run ruff check --fix
7
+ language: system
8
+ types: [python]
9
+
10
+ - id: ruff-format
11
+ name: ruff format
12
+ entry: uv run ruff format
13
+ language: system
14
+ types: [python]
15
+
16
+ - id: ty
17
+ name: ty check
18
+ entry: uv run ty check src/
19
+ language: system
20
+ pass_filenames: false
21
+ files: ^src/
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.2.0"
3
+ }
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ ## [0.2.0](https://github.com/mahdilamb/redis-queen/compare/redis-queen-v0.1.2...redis-queen-v0.2.0) (2026-04-12)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * Package name, import module, CLI command, config section, and environment variable have all been renamed.
9
+
10
+ ### Features
11
+
12
+ * initial release of red-queen ([2c69149](https://github.com/mahdilamb/redis-queen/commit/2c691498b342112aed5f7af48e8cce83cee3b587))
13
+
14
+
15
+ ### Documentation
16
+
17
+ * add badges to README ([5c2d6d4](https://github.com/mahdilamb/redis-queen/commit/5c2d6d4dcd0f350f359d4f32085d626b8df13c34))
18
+ * add badges, classifiers, and 3.14 to CI matrix ([0b5b397](https://github.com/mahdilamb/redis-queen/commit/0b5b39786ee459576a231232639127f755fcabd0))
19
+ * remove alias-as-rename example from README ([2d3ac74](https://github.com/mahdilamb/redis-queen/commit/2d3ac74838922cb4d74d5026cfd7273796e8315d))
20
+ * remove alias-as-rename example from README ([5b5ecc3](https://github.com/mahdilamb/redis-queen/commit/5b5ecc38d5e40f492924a9cace1315d7fb066b30))
21
+
22
+
23
+ ### Code Refactoring
24
+
25
+ * rename package from red-queen to redis-queen ([13532d6](https://github.com/mahdilamb/redis-queen/commit/13532d6b6be956f643d5dc4ec1f92f98f3f19019))
26
+
27
+ ## [0.1.2](https://github.com/mahdilamb/redis-queen/compare/redis-queen-v0.1.1...redis-queen-v0.1.2) (2026-04-12)
28
+
29
+
30
+ ### Documentation
31
+
32
+ * add badges to README ([5c2d6d4](https://github.com/mahdilamb/redis-queen/commit/5c2d6d4dcd0f350f359d4f32085d626b8df13c34))
33
+ * add badges, classifiers, and 3.14 to CI matrix ([0b5b397](https://github.com/mahdilamb/redis-queen/commit/0b5b39786ee459576a231232639127f755fcabd0))
34
+ * remove alias-as-rename example from README ([2d3ac74](https://github.com/mahdilamb/redis-queen/commit/2d3ac74838922cb4d74d5026cfd7273796e8315d))
35
+ * remove alias-as-rename example from README ([5b5ecc3](https://github.com/mahdilamb/redis-queen/commit/5b5ecc38d5e40f492924a9cace1315d7fb066b30))
36
+
37
+ ## [0.1.1](https://github.com/mahdilamb/redis-queen/compare/redis-queen-v0.1.0...redis-queen-v0.1.1) (2026-04-12)
38
+
39
+
40
+ ### Features
41
+
42
+ * initial release of redis-queen ([2c69149](https://github.com/mahdilamb/redis-queen/commit/2c691498b342112aed5f7af48e8cce83cee3b587))
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mahdi Lamb
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,216 @@
1
+ Metadata-Version: 2.4
2
+ Name: redis-queen
3
+ Version: 0.2.0
4
+ Summary: Redis schema migration tool with Pydantic model tracking
5
+ Author-email: Mahdi Lamb <mahdilamb@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: click>=8.1.0
15
+ Requires-Dist: pydantic>=2.0.0
16
+ Requires-Dist: redis>=7.4.0
17
+ Requires-Dist: tomli>=2.0.0; python_version < '3.11'
18
+ Description-Content-Type: text/markdown
19
+
20
+ # redis-queen
21
+
22
+ [![CI](https://github.com/mahdilamb/redis-queen/actions/workflows/ci.yml/badge.svg)](https://github.com/mahdilamb/redis-queen/actions/workflows/ci.yml)
23
+ [![PyPI](https://img.shields.io/pypi/v/redis-queen)](https://pypi.org/project/redis-queen/)
24
+ [![Python](https://img.shields.io/pypi/pyversions/redis-queen)](https://pypi.org/project/redis-queen/)
25
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
26
+
27
+ Schema migration tool for Redis-backed Pydantic models. Track model changes, generate versioned migration scripts, and apply them using async SCAN-based key iteration.
28
+
29
+ ## Installation
30
+
31
+ ```toml
32
+ # pyproject.toml
33
+ dependencies = ["redis-queen"]
34
+ ```
35
+
36
+ ## Quick start
37
+
38
+ ### 1. Decorate your models
39
+
40
+ ```python
41
+ from pydantic import BaseModel, Field, RootModel
42
+ from redis_queen import redis_queen, migrates_from
43
+ from typing import Annotated
44
+
45
+ @redis_queen(key="user:{user_id}:profile:")
46
+ class UserProfile(BaseModel):
47
+ name: str
48
+ email: str
49
+
50
+ @redis_queen(key="agent:session:{session_id}:display")
51
+ class DisplayMessages(RootModel[list[DisplayMessage]]):
52
+ """Stored as a JSON array."""
53
+
54
+ # Field renames
55
+ @redis_queen(key="item:{item_id}")
56
+ class Item(BaseModel):
57
+ title: Annotated[str, migrates_from("name")] # tracks rename from "name"
58
+ ```
59
+
60
+ Key patterns use f-string style placeholders. `match` is auto-derived by replacing `{...}` with `*` for SCAN.
61
+
62
+ ### 2. Configure in pyproject.toml
63
+
64
+ ```toml
65
+ [tool.redis-queen]
66
+ migrations_dir = "migrations"
67
+ deletion_protection = false # or true, or an integer (TTL in seconds)
68
+
69
+ [tool.redis-queen.profiles.default]
70
+ default = true
71
+ host = "localhost" # defaults to localhost
72
+ port = 6379 # defaults to 6379
73
+ db = 0 # defaults to 0
74
+ migrations_collection = "my_collection"
75
+ model_search_path = ["myapp.models"]
76
+ ```
77
+
78
+ `host`, `port`, and `db` default to `localhost`, `6379`, and `0`. All string values support `$ENV_VAR` expansion.
79
+
80
+ **Profile resolution order:** `--profile` CLI flag > `REDIS_QUEEN_PROFILE` env var > profile with `default = true`.
81
+
82
+ Multiple profiles (e.g. local + docker):
83
+
84
+ ```toml
85
+ [tool.redis-queen.profiles.default]
86
+ default = true
87
+ migrations_collection = "agent"
88
+ model_search_path = ["agent.types"]
89
+
90
+ [tool.redis-queen.profiles.docker]
91
+ host = "redis"
92
+ migrations_collection = "agent"
93
+ model_search_path = ["agent.types"]
94
+ ```
95
+
96
+ ### 3. Generate and apply migrations
97
+
98
+ ```bash
99
+ # Create the initial schema snapshot
100
+ redis-queen revision -m "initial"
101
+
102
+ # Show current state
103
+ redis-queen show
104
+
105
+ # After changing models, generate a new revision
106
+ redis-queen revision -m "add email field"
107
+
108
+ # Check if there are pending changes (exit 1 if none, useful in CI)
109
+ redis-queen revision --check
110
+
111
+ # Show what changed
112
+ redis-queen diff
113
+
114
+ # Apply pending migrations (stages first, prompts for confirmation)
115
+ redis-queen up
116
+
117
+ # Apply without confirmation
118
+ redis-queen up --auto-apply
119
+
120
+ # Downgrade to a specific revision
121
+ redis-queen down 0001
122
+ redis-queen down --root # revert all
123
+
124
+ # Reset to a specific revision (up or down as needed)
125
+ redis-queen reset 0002
126
+ redis-queen reset --root
127
+ ```
128
+
129
+ All mutation commands (`up`, `down`, `reset`) support `--auto-apply` to skip the staging confirmation prompt.
130
+
131
+ ## Model discovery
132
+
133
+ `model_search_path` accepts:
134
+
135
+ - **Directories:** `"src/myapp/models"` -- walks all `.py` files
136
+ - **Single files:** `"src/myapp/models.py"`
137
+ - **Dotted module paths:** `"myapp.models"` -- imports the module and recursively walks all submodules
138
+
139
+ ## Schema tracking
140
+
141
+ Snapshots use `model_json_schema()`, which captures the full recursive schema including nested models. A change to any nested model triggers a new revision.
142
+
143
+ ## Generated migrations
144
+
145
+ Revisions auto-generate `upgrade` and `downgrade` functions:
146
+
147
+ - **Field added with default** -- sets the default value
148
+ - **Field added, optional (factory)** -- infers zero-value from type (`[]`, `{}`, `""`, `0`, etc.)
149
+ - **Field added, required, no default** -- `# TODO` comment
150
+ - **Field removed** -- backs up values, deletes from data
151
+
152
+ ## Deletion protection
153
+
154
+ Controls what happens when fields are removed:
155
+
156
+ ```toml
157
+ [tool.redis-queen]
158
+ deletion_protection = false # backup without TTL (default)
159
+ deletion_protection = true # generate TODO, don't delete
160
+ deletion_protection = 3600 # backup with 1-hour TTL
161
+ ```
162
+
163
+ When using an integer TTL, backups expire after the specified seconds. If a downgrade runs after the TTL, a warning is emitted for each key where the backup has expired and fields cannot be restored.
164
+
165
+ ## Python API
166
+
167
+ ```python
168
+ from redis_queen import (
169
+ redis_queen,
170
+ migrates_from,
171
+ auto_migrate_up,
172
+ migrate_up,
173
+ migrate_down,
174
+ apply_plan,
175
+ find_one,
176
+ get_one,
177
+ )
178
+ ```
179
+
180
+ ### Auto-migrate on startup
181
+
182
+ ```python
183
+ from redis_queen import auto_migrate_up
184
+
185
+ # Resolves config, connects to Redis, applies all pending migrations.
186
+ # Uses REDIS_QUEEN_PROFILE env var or default profile.
187
+ await auto_migrate_up()
188
+ ```
189
+
190
+ ### FastAPI lifespan example
191
+
192
+ ```python
193
+ from redis_queen import auto_migrate_up
194
+
195
+ @asynccontextmanager
196
+ async def lifespan(app: FastAPI):
197
+ await auto_migrate_up()
198
+ yield
199
+ ```
200
+
201
+ ### Query utilities
202
+
203
+ ```python
204
+ # find_one: format key pattern with args/kwargs, then GET
205
+ profile = await find_one(UserProfile, "123")
206
+ profile = await find_one(UserProfile, user_id="123")
207
+ raw = await find_one(UserProfile, "123", return_raw=True)
208
+
209
+ # get_one: GET by full literal key
210
+ profile = await get_one(UserProfile, "user:123:profile:")
211
+ raw = await get_one(UserProfile, "user:123:profile:", return_raw=True)
212
+ ```
213
+
214
+ ### Low-level API
215
+
216
+ For full control, use `migrate_up` / `migrate_down` directly with an explicit Redis client, migrations dir, and model list. See the CLI command implementations for examples.
@@ -0,0 +1,197 @@
1
+ # redis-queen
2
+
3
+ [![CI](https://github.com/mahdilamb/redis-queen/actions/workflows/ci.yml/badge.svg)](https://github.com/mahdilamb/redis-queen/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/redis-queen)](https://pypi.org/project/redis-queen/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/redis-queen)](https://pypi.org/project/redis-queen/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ Schema migration tool for Redis-backed Pydantic models. Track model changes, generate versioned migration scripts, and apply them using async SCAN-based key iteration.
9
+
10
+ ## Installation
11
+
12
+ ```toml
13
+ # pyproject.toml
14
+ dependencies = ["redis-queen"]
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ ### 1. Decorate your models
20
+
21
+ ```python
22
+ from pydantic import BaseModel, Field, RootModel
23
+ from redis_queen import redis_queen, migrates_from
24
+ from typing import Annotated
25
+
26
+ @redis_queen(key="user:{user_id}:profile:")
27
+ class UserProfile(BaseModel):
28
+ name: str
29
+ email: str
30
+
31
+ @redis_queen(key="agent:session:{session_id}:display")
32
+ class DisplayMessages(RootModel[list[DisplayMessage]]):
33
+ """Stored as a JSON array."""
34
+
35
+ # Field renames
36
+ @redis_queen(key="item:{item_id}")
37
+ class Item(BaseModel):
38
+ title: Annotated[str, migrates_from("name")] # tracks rename from "name"
39
+ ```
40
+
41
+ Key patterns use f-string style placeholders. `match` is auto-derived by replacing `{...}` with `*` for SCAN.
42
+
43
+ ### 2. Configure in pyproject.toml
44
+
45
+ ```toml
46
+ [tool.redis-queen]
47
+ migrations_dir = "migrations"
48
+ deletion_protection = false # or true, or an integer (TTL in seconds)
49
+
50
+ [tool.redis-queen.profiles.default]
51
+ default = true
52
+ host = "localhost" # defaults to localhost
53
+ port = 6379 # defaults to 6379
54
+ db = 0 # defaults to 0
55
+ migrations_collection = "my_collection"
56
+ model_search_path = ["myapp.models"]
57
+ ```
58
+
59
+ `host`, `port`, and `db` default to `localhost`, `6379`, and `0`. All string values support `$ENV_VAR` expansion.
60
+
61
+ **Profile resolution order:** `--profile` CLI flag > `REDIS_QUEEN_PROFILE` env var > profile with `default = true`.
62
+
63
+ Multiple profiles (e.g. local + docker):
64
+
65
+ ```toml
66
+ [tool.redis-queen.profiles.default]
67
+ default = true
68
+ migrations_collection = "agent"
69
+ model_search_path = ["agent.types"]
70
+
71
+ [tool.redis-queen.profiles.docker]
72
+ host = "redis"
73
+ migrations_collection = "agent"
74
+ model_search_path = ["agent.types"]
75
+ ```
76
+
77
+ ### 3. Generate and apply migrations
78
+
79
+ ```bash
80
+ # Create the initial schema snapshot
81
+ redis-queen revision -m "initial"
82
+
83
+ # Show current state
84
+ redis-queen show
85
+
86
+ # After changing models, generate a new revision
87
+ redis-queen revision -m "add email field"
88
+
89
+ # Check if there are pending changes (exit 1 if none, useful in CI)
90
+ redis-queen revision --check
91
+
92
+ # Show what changed
93
+ redis-queen diff
94
+
95
+ # Apply pending migrations (stages first, prompts for confirmation)
96
+ redis-queen up
97
+
98
+ # Apply without confirmation
99
+ redis-queen up --auto-apply
100
+
101
+ # Downgrade to a specific revision
102
+ redis-queen down 0001
103
+ redis-queen down --root # revert all
104
+
105
+ # Reset to a specific revision (up or down as needed)
106
+ redis-queen reset 0002
107
+ redis-queen reset --root
108
+ ```
109
+
110
+ All mutation commands (`up`, `down`, `reset`) support `--auto-apply` to skip the staging confirmation prompt.
111
+
112
+ ## Model discovery
113
+
114
+ `model_search_path` accepts:
115
+
116
+ - **Directories:** `"src/myapp/models"` -- walks all `.py` files
117
+ - **Single files:** `"src/myapp/models.py"`
118
+ - **Dotted module paths:** `"myapp.models"` -- imports the module and recursively walks all submodules
119
+
120
+ ## Schema tracking
121
+
122
+ Snapshots use `model_json_schema()`, which captures the full recursive schema including nested models. A change to any nested model triggers a new revision.
123
+
124
+ ## Generated migrations
125
+
126
+ Revisions auto-generate `upgrade` and `downgrade` functions:
127
+
128
+ - **Field added with default** -- sets the default value
129
+ - **Field added, optional (factory)** -- infers zero-value from type (`[]`, `{}`, `""`, `0`, etc.)
130
+ - **Field added, required, no default** -- `# TODO` comment
131
+ - **Field removed** -- backs up values, deletes from data
132
+
133
+ ## Deletion protection
134
+
135
+ Controls what happens when fields are removed:
136
+
137
+ ```toml
138
+ [tool.redis-queen]
139
+ deletion_protection = false # backup without TTL (default)
140
+ deletion_protection = true # generate TODO, don't delete
141
+ deletion_protection = 3600 # backup with 1-hour TTL
142
+ ```
143
+
144
+ When using an integer TTL, backups expire after the specified seconds. If a downgrade runs after the TTL, a warning is emitted for each key where the backup has expired and fields cannot be restored.
145
+
146
+ ## Python API
147
+
148
+ ```python
149
+ from redis_queen import (
150
+ redis_queen,
151
+ migrates_from,
152
+ auto_migrate_up,
153
+ migrate_up,
154
+ migrate_down,
155
+ apply_plan,
156
+ find_one,
157
+ get_one,
158
+ )
159
+ ```
160
+
161
+ ### Auto-migrate on startup
162
+
163
+ ```python
164
+ from redis_queen import auto_migrate_up
165
+
166
+ # Resolves config, connects to Redis, applies all pending migrations.
167
+ # Uses REDIS_QUEEN_PROFILE env var or default profile.
168
+ await auto_migrate_up()
169
+ ```
170
+
171
+ ### FastAPI lifespan example
172
+
173
+ ```python
174
+ from redis_queen import auto_migrate_up
175
+
176
+ @asynccontextmanager
177
+ async def lifespan(app: FastAPI):
178
+ await auto_migrate_up()
179
+ yield
180
+ ```
181
+
182
+ ### Query utilities
183
+
184
+ ```python
185
+ # find_one: format key pattern with args/kwargs, then GET
186
+ profile = await find_one(UserProfile, "123")
187
+ profile = await find_one(UserProfile, user_id="123")
188
+ raw = await find_one(UserProfile, "123", return_raw=True)
189
+
190
+ # get_one: GET by full literal key
191
+ profile = await get_one(UserProfile, "user:123:profile:")
192
+ raw = await get_one(UserProfile, "user:123:profile:", return_raw=True)
193
+ ```
194
+
195
+ ### Low-level API
196
+
197
+ For full control, use `migrate_up` / `migrate_down` directly with an explicit Redis client, migrations dir, and model list. See the CLI command implementations for examples.
@@ -0,0 +1,65 @@
1
+ [project]
2
+ name = "redis-queen"
3
+ version = "0.2.0"
4
+ description = "Redis schema migration tool with Pydantic model tracking"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Mahdi Lamb", email = "mahdilamb@gmail.com" }
8
+ ]
9
+ license = "MIT"
10
+ requires-python = ">=3.10"
11
+ classifiers = [
12
+ "Programming Language :: Python :: 3.10",
13
+ "Programming Language :: Python :: 3.11",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
17
+ ]
18
+ dependencies = [
19
+ "click>=8.1.0",
20
+ "pydantic>=2.0.0",
21
+ "redis>=7.4.0",
22
+ "tomli>=2.0.0; python_version < '3.11'",
23
+ ]
24
+
25
+ [project.scripts]
26
+ redis-queen = "redis_queen.__main__:main"
27
+
28
+ [build-system]
29
+ requires = ["hatchling"]
30
+ build-backend = "hatchling.build"
31
+
32
+ [tool.ruff]
33
+ target-version = "py310"
34
+ extend-exclude = ["**/migrations"]
35
+
36
+ [tool.ruff.lint]
37
+ select = [
38
+ "E", # pycodestyle errors
39
+ "W", # pycodestyle warnings
40
+ "F", # pyflakes
41
+ "I", # isort
42
+ "N", # pep8-naming
43
+ "UP", # pyupgrade
44
+ "B", # flake8-bugbear
45
+ "SIM", # flake8-simplify
46
+ "TCH", # flake8-type-checking
47
+ "RUF", # ruff-specific rules
48
+ ]
49
+
50
+ [tool.ruff.lint.isort]
51
+ known-first-party = ["redis_queen"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+ asyncio_mode = "auto"
56
+ addopts = "--import-mode=importlib"
57
+
58
+ [dependency-groups]
59
+ dev = [
60
+ "fakeredis>=2.35.0",
61
+ "pytest>=9.0.3",
62
+ "pytest-asyncio>=1.3.0",
63
+ "ruff>=0.15.10",
64
+ "ty>=0.0.29",
65
+ ]