redis-lua-py 0.2.1__tar.gz → 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 (71) hide show
  1. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/workflows/ci.yml +16 -1
  2. redis_lua_py-0.4.0/.release-please-manifest.json +3 -0
  3. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/CHANGELOG.md +19 -0
  4. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/PKG-INFO +5 -4
  5. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/README.md +1 -1
  6. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/calling-redis-commands.md +27 -2
  7. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/keys-and-arguments.md +36 -0
  8. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/installation.md +2 -2
  9. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/reference/api.md +4 -2
  10. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/reference/errors.md +4 -5
  11. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/reference/lua-vs-python.md +35 -4
  12. redis_lua_py-0.4.0/docs/reference/supported-subset.md +43 -0
  13. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/pyproject.toml +8 -5
  14. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/src/redis_lua_py/__init__.py +1 -1
  15. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/src/redis_lua_py/_compile.py +603 -65
  16. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/src/redis_lua_py/_lua.py +52 -2
  17. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/src/redis_lua_py/_script.py +46 -8
  18. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/conftest.py +2 -2
  19. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_commands.py +6 -2
  20. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_errors.py +22 -18
  21. redis_lua_py-0.4.0/tests/test_mistranslations.py +113 -0
  22. redis_lua_py-0.4.0/tests/test_table_stakes.py +377 -0
  23. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_typing.py +6 -1
  24. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/uv.lock +75 -3
  25. redis_lua_py-0.2.1/.release-please-manifest.json +0 -3
  26. redis_lua_py-0.2.1/docs/reference/supported-subset.md +0 -31
  27. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/assets/README.md +0 -0
  28. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/assets/banner-dark.svg +0 -0
  29. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/assets/banner-light.svg +0 -0
  30. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/assets/generate.py +0 -0
  31. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/assets/logo.svg +0 -0
  32. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/assets/logomark.svg +0 -0
  33. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/assets/social-preview.png +0 -0
  34. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/assets/social-preview.svg +0 -0
  35. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/workflows/docs.yml +0 -0
  36. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/workflows/pr-title.yml +0 -0
  37. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.github/workflows/release.yml +0 -0
  38. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.gitignore +0 -0
  39. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/.python-version +0 -0
  40. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/CONTRIBUTING.md +0 -0
  41. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/LICENSE +0 -0
  42. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/assets/logo.svg +0 -0
  43. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/contributing.md +0 -0
  44. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/development.md +0 -0
  45. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/examples.md +0 -0
  46. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/async.md +0 -0
  47. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/binary-values.md +0 -0
  48. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/binding-a-client.md +0 -0
  49. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/constants.md +0 -0
  50. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/generated-lua.md +0 -0
  51. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/return-values.md +0 -0
  52. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/guide/testing.md +0 -0
  53. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/index.md +0 -0
  54. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/quickstart.md +0 -0
  55. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/docs/stylesheets/extra.css +0 -0
  56. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/release-please-config.json +0 -0
  57. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/scripts/generate_commands.py +0 -0
  58. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/src/redis_lua_py/_commands.py +0 -0
  59. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/src/redis_lua_py/_runtime.py +0 -0
  60. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/src/redis_lua_py/errors.py +0 -0
  61. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/src/redis_lua_py/py.typed +0 -0
  62. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/module_with_client.py +0 -0
  63. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/module_without_import.py +0 -0
  64. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_binary.py +0 -0
  65. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_binding.py +0 -0
  66. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_compile.py +0 -0
  67. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_constants.py +0 -0
  68. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_execute.py +0 -0
  69. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_namespace.py +0 -0
  70. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/tests/test_semantics.py +0 -0
  71. {redis_lua_py-0.2.1 → redis_lua_py-0.4.0}/zensical.toml +0 -0
@@ -25,7 +25,7 @@ jobs:
25
25
  strategy:
26
26
  fail-fast: false
27
27
  matrix:
28
- python-version: ["3.11", "3.12", "3.13"]
28
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
29
29
  steps:
30
30
  - uses: actions/checkout@v4
31
31
  - uses: astral-sh/setup-uv@v5
@@ -34,6 +34,21 @@ jobs:
34
34
  - run: uv sync --dev --python ${{ matrix.python-version }}
35
35
  - run: uv run pytest
36
36
 
37
+ oldest:
38
+ name: Tests (oldest supported Python and redis-py)
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+ - uses: astral-sh/setup-uv@v5
43
+ with:
44
+ enable-cache: true
45
+ - run: uv sync --dev --python 3.10
46
+ # The floor in pyproject.toml is only a claim until something runs on it.
47
+ # fakeredis declares redis>=4.3 but works on 4.2, the first release with
48
+ # redis.asyncio, so the pin goes in after the resolver is done.
49
+ - run: uv pip install --no-deps "redis==4.2.0" async-timeout deprecated
50
+ - run: uv run --no-sync pytest
51
+
37
52
  live:
38
53
  name: Tests against a real Redis
39
54
  runs-on: ubuntu-latest
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.4.0"
3
+ }
@@ -7,6 +7,25 @@ are written by [release-please](https://github.com/googleapis/release-please)
7
7
  from the conventional commit subjects on `main`; see
8
8
  [CONTRIBUTING.md](CONTRIBUTING.md) for how a release is cut.
9
9
 
10
+ ## [0.4.0](https://github.com/IgnaceMaes/redis-lua-py/compare/v0.3.0...v0.4.0) (2026-09-12)
11
+
12
+
13
+ ### Added
14
+
15
+ * compile variable keys, splat calls, and/or values, helper functions and dict loops ([#10](https://github.com/IgnaceMaes/redis-lua-py/issues/10)) ([271f3f6](https://github.com/IgnaceMaes/redis-lua-py/commit/271f3f6be23d78246d91cde5de4fec05cee28832))
16
+
17
+
18
+ ### Fixed
19
+
20
+ * stop silently mistranslating reassigned parameters, infinity, == None and set_repl ([#8](https://github.com/IgnaceMaes/redis-lua-py/issues/8)) ([930c382](https://github.com/IgnaceMaes/redis-lua-py/commit/930c38283f366fd9042c4cc20cdf7eeb34e2d8a5))
21
+
22
+ ## [0.3.0](https://github.com/IgnaceMaes/redis-lua-py/compare/v0.2.1...v0.3.0) (2026-09-12)
23
+
24
+
25
+ ### Added
26
+
27
+ * support python 3.10 and redis-py 4.2 ([#6](https://github.com/IgnaceMaes/redis-lua-py/issues/6)) ([6ce6249](https://github.com/IgnaceMaes/redis-lua-py/commit/6ce6249d219756a598e83f18f3bf44e8a58722eb))
28
+
10
29
  ## [0.2.1](https://github.com/IgnaceMaes/redis-lua-py/compare/v0.2.0...v0.2.1) (2026-09-12)
11
30
 
12
31
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: redis-lua-py
3
- Version: 0.2.1
3
+ Version: 0.4.0
4
4
  Summary: Write Redis Lua scripts as real Python functions, not strings.
5
5
  Project-URL: Homepage, https://ignacemaes.com/redis-lua-py/
6
6
  Project-URL: Documentation, https://ignacemaes.com/redis-lua-py/
@@ -14,13 +14,14 @@ Keywords: eval,evalsha,lua,redis,scripting,transpiler
14
14
  Classifier: Development Status :: 3 - Alpha
15
15
  Classifier: Intended Audience :: Developers
16
16
  Classifier: Programming Language :: Lua
17
+ Classifier: Programming Language :: Python :: 3.10
17
18
  Classifier: Programming Language :: Python :: 3.11
18
19
  Classifier: Programming Language :: Python :: 3.12
19
20
  Classifier: Programming Language :: Python :: 3.13
20
21
  Classifier: Topic :: Database
21
22
  Classifier: Typing :: Typed
22
- Requires-Python: >=3.11
23
- Requires-Dist: redis>=5.0
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: redis>=4.2
24
25
  Description-Content-Type: text/markdown
25
26
 
26
27
  <p align="center">
@@ -88,7 +89,7 @@ for the script namespace, so the two never collide.
88
89
  uv add redis-lua-py
89
90
  ```
90
91
 
91
- Python 3.11+, and redis-py 5.0+ as the only dependency.
92
+ Python 3.10+, and redis-py 4.2+ as the only dependency.
92
93
 
93
94
  ## What it compiles to
94
95
 
@@ -63,7 +63,7 @@ for the script namespace, so the two never collide.
63
63
  uv add redis-lua-py
64
64
  ```
65
65
 
66
- Python 3.11+, and redis-py 5.0+ as the only dependency.
66
+ Python 3.10+, and redis-py 4.2+ as the only dependency.
67
67
 
68
68
  ## What it compiles to
69
69
 
@@ -5,8 +5,33 @@ split into subcommand tokens, so `redis.script_load(x)` compiles to
5
5
  `redis.call('SCRIPT', 'LOAD', x)`.
6
6
 
7
7
  `redis.pcall`, `redis.error_reply`, `redis.status_reply`, `redis.sha1hex`,
8
- `redis.log` and `cjson.encode` / `cjson.decode` pass through under their own
9
- names.
8
+ `redis.log`, `redis.set_repl`, `redis.acl_check_cmd` and `cjson.encode` /
9
+ `cjson.decode` pass through under their own names. So do the constants they
10
+ take: `redis.LOG_WARNING` and the other log levels, `redis.REPL_ALL` and the
11
+ other replication modes, and `redis.REDIS_VERSION`.
12
+
13
+ ## Splatting a list into a command
14
+
15
+ A starred argument compiles to `unpack`, which is how a list becomes the
16
+ arguments of a command:
17
+
18
+ ```python
19
+ @script
20
+ def push_all(queue: Key, jobs: list[str]) -> int:
21
+ return redis.rpush(queue, *jobs)
22
+ ```
23
+
24
+ ```lua
25
+ return redis.call('RPUSH', queue, unpack(jobs))
26
+ ```
27
+
28
+ It has to be the last argument, because Lua's `unpack` only expands there.
29
+
30
+ !!! warning "unpack has a limit"
31
+
32
+ `unpack` puts every element on Lua's stack at once, and Redis' Lua refuses
33
+ past roughly eight thousand values with "too many results to unpack".
34
+ Split a list that can grow that large into chunks, one command per chunk.
10
35
 
11
36
  ## Names are checked, not just uppercased
12
37
 
@@ -46,6 +46,42 @@ the value is only being carried.
46
46
  `bool` encodes to `"1"` or `"0"`. Paired with an `int` annotation that deletes
47
47
  the `1 if flag else 0` from the call site: pass `True`, and the body gets `1`.
48
48
 
49
+ ## A variable number of keys or arguments
50
+
51
+ Annotate a parameter `list[Key]` to take any number of keys, or `list[str]`,
52
+ `list[int]` and so on to take any number of arguments. Its elements fill
53
+ whatever is left of `KEYS` or `ARGV` after the fixed parameters, wherever the
54
+ list is declared, and arrive in the body as a table:
55
+
56
+ ```python
57
+ @script
58
+ def delete_tagged(tag: Key, keys: list[Key], stamp: int) -> int:
59
+ redis.set(tag, stamp)
60
+ return redis.delete(*keys)
61
+
62
+
63
+ delete_tagged(client, tag="purged", keys=["a", "b", "c"], stamp=1700000000)
64
+ ```
65
+
66
+ ```lua
67
+ local tag = KEYS[1]
68
+ local stamp = tonumber(ARGV[1])
69
+ local keys = {}
70
+ for __i1 = 2, #KEYS do
71
+ keys[#keys + 1] = KEYS[__i1]
72
+ end
73
+ redis.call('SET', tag, stamp)
74
+ return redis.call('DEL', unpack(keys))
75
+ ```
76
+
77
+ A script can take one list of keys and one list of arguments, since `KEYS`
78
+ and `ARGV` only have positions to tell them apart. Every element of a
79
+ `list[Key]` is a declared key, so Redis Cluster routes on all of them. An
80
+ element annotation of `int` or `float` converts each element, as it would a
81
+ single argument. Passing a string where a list is expected raises
82
+ [`ScriptArgumentError`](../reference/errors.md#scriptargumenterror) rather
83
+ than spreading it into characters.
84
+
49
85
  ## Positional or keyword
50
86
 
51
87
  Scripts accept either; keyword is clearer at the call site and is what the
@@ -12,8 +12,8 @@ pip install redis-lua-py
12
12
 
13
13
  ## Requirements
14
14
 
15
- - Python 3.11 or newer
16
- - [redis-py](https://github.com/redis/redis-py) 5.0 or newer — the only
15
+ - Python 3.10 or newer
16
+ - [redis-py](https://github.com/redis/redis-py) 4.2 or newer — the only
17
17
  dependency, and installed with the package
18
18
 
19
19
  Script caching, `EVALSHA` and the `NOSCRIPT` reload are handled by redis-py's
@@ -105,8 +105,10 @@ script(client, /, *positional, **keyword) -> Awaitable[R] # async client
105
105
  | `name` | `str` | the script's name, in the header and in errors |
106
106
  | `lua` | `str` | the full Lua source, exactly as sent to Redis |
107
107
  | `params` | `tuple[str, ...]` | every parameter, in declaration order |
108
- | `keys` | `tuple[str, ...]` | the parameters annotated `Key` |
109
- | `args` | `tuple[str, ...]` | everything else |
108
+ | `keys` | `tuple[str, ...]` | the parameters annotated `Key` or `list[Key]`, in `KEYS` order |
109
+ | `args` | `tuple[str, ...]` | everything else, in `ARGV` order |
110
+ | `variadic_key` | `str \| None` | the `list[Key]` parameter, which fills the rest of `KEYS` |
111
+ | `variadic_arg` | `str \| None` | the list parameter that fills the rest of `ARGV` |
110
112
  | `doc` | `str \| None` | the function's docstring |
111
113
  | `source` | `str` | where it was defined, repo-relative |
112
114
 
@@ -4,12 +4,11 @@ Every error this package raises carries a caret under the line at fault, and a
4
4
  hint saying what to write instead.
5
5
 
6
6
  ```
7
- 'and'/'or' are only supported in an if or while condition
7
+ Lua 5.1 has no 'continue' statement
8
8
  File "/srv/app/limits.py", line 12
9
- flag = a and b
10
- ^
11
- hint: In Python these return an operand, which does not survive the
12
- difference in truthiness. Use an if statement instead.
9
+ continue
10
+ ^
11
+ hint: Invert the condition and put the rest of the loop body inside the if.
13
12
  ```
14
13
 
15
14
  ## The hierarchy
@@ -14,7 +14,8 @@ A Redis command with nothing to return hands Lua `false`, not `nil`. This is
14
14
  the classic trap: a hand-written `== nil` never matches, so the branch silently
15
15
  never runs. `x is None` compiles to a helper accepting both, which also takes
16
16
  `x` as an argument — so `if redis.hget(k, f) is None:` does not run the command
17
- twice.
17
+ twice. `x == None` and `x != None` compile to the same helper, since that is
18
+ plainly what they mean.
18
19
 
19
20
  ## Indexing is closed
20
21
 
@@ -32,10 +33,40 @@ the top of the script, so it does not silently read back `nil`.
32
33
 
33
34
  Use an f-string, which compiles to Lua's `..`.
34
35
 
35
- ## `and` / `or` work only in conditions
36
+ ## `and`, `or` and conditional expressions are closed
36
37
 
37
- In Python they return an operand, not a boolean, and that does not survive the
38
- truthiness difference. Use an `if`.
38
+ In Python, `a or b` returns one of its operands, chosen by Python's
39
+ truthiness. Lua's own `or` uses Lua's truthiness, where `0` and `''` are true,
40
+ so the Lua idiom `tonumber(x) or 0` means something different there.
41
+
42
+ A right side that is a name or a literal compiles to a small `__or` / `__and`
43
+ helper. Anything else is wrapped in a function called on the spot, so that it
44
+ only runs when Python would run it. `flag or redis.incr(k)` does not
45
+ increment when `flag` is set.
46
+
47
+ `a if c else b` compiles to `c and a or b` when `a` is a literal. Otherwise it
48
+ becomes the same kind of function, because `c and a or b` is wrong whenever
49
+ `a` can be false or nil.
50
+
51
+ ## Dicts iterate in no particular order
52
+
53
+ `.items()`, `.keys()` and `.values()` compile to Lua's `pairs()`, which visits
54
+ entries in no fixed order. Sort the result if the order reaches the caller.
55
+
56
+ Iterating a dict directly with `for k in d` walks its array part, which a dict
57
+ does not have, so the loop never runs. Say `.keys()`.
58
+
59
+ ## Redis replies are flat lists, not dicts
60
+
61
+ To Lua, `HGETALL` returns `[field, value, field, value, ...]`, not a table
62
+ keyed by field, so `.items()` on it does not mean what it would in redis-py.
63
+ Walk it in pairs instead:
64
+
65
+ ```python
66
+ fields = redis.hgetall(k)
67
+ for i in range(0, len(fields), 2):
68
+ redis.hset(copy, fields[i], fields[i + 1])
69
+ ```
39
70
 
40
71
  ## There is no `continue`
41
72
 
@@ -0,0 +1,43 @@
1
+ # The supported subset
2
+
3
+ A script body is Python that Python never runs, so only the part of the
4
+ language with a faithful Lua meaning is accepted.
5
+
6
+ **Supported:**
7
+
8
+ - **Statements:** assignment, including unpacking (`a, b = b, a`);
9
+ augmented assignment; `if`/`elif`/`else`; `while`; `break`; `return`.
10
+ - **Loops:** `for ... in` over a table, `range()`, `enumerate()`, or a dict's
11
+ `.items()`, `.keys()` and `.values()`, binding a name or a tuple of names.
12
+ - **Helper functions** defined with `def` at the top level of the body. They
13
+ can call each other and themselves.
14
+ - **Expressions:** comparisons; arithmetic; `and`/`or`, and `a if c else b`,
15
+ both as values; f-strings; list and dict literals.
16
+ - **Builtins and methods:** `len()`, `int()`, `float()`, `str()`, `min()`,
17
+ `max()`, `abs()`, `.append()`, `.insert()`, `.pop()` and `str.join()`.
18
+ - **The `math` module:** `floor`, `ceil`, `sqrt`, `fabs`, `fmod`, `exp`,
19
+ `log`, `log10` and `pow`, imported either way.
20
+ - **Calls:** into `redis` and `cjson`, with `*xs` allowed as the last argument.
21
+ - **Parameters:** `list[Key]` and `list[...]`, for a variable number of keys
22
+ and arguments.
23
+ - **Module-level constants.**
24
+
25
+ Everything else raises
26
+ [`UnsupportedSyntax`](errors.md#unsupportedsyntax) when the module is imported,
27
+ with a caret under the line at fault:
28
+
29
+ ```
30
+ Lua 5.1 has no 'continue' statement
31
+ File "/srv/app/limits.py", line 12
32
+ continue
33
+ ^
34
+ hint: Invert the condition and put the rest of the loop body inside the if.
35
+ ```
36
+
37
+ Failing at import, loudly, is deliberate. A body that looks like Python but is
38
+ never run by Python is exactly where a quiet mistranslation would cost the
39
+ most.
40
+
41
+ The gaps that a supported construct still carries — truthiness, 1-based
42
+ indexing, block scope — are covered in
43
+ [Where Lua differs from Python](lua-vs-python.md).
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "redis-lua-py"
3
- version = "0.2.1"
3
+ version = "0.4.0"
4
4
  description = "Write Redis Lua scripts as real Python functions, not strings."
5
5
  readme = "README.md"
6
- requires-python = ">=3.11"
6
+ requires-python = ">=3.10"
7
7
  license = "MIT"
8
8
  license-files = ["LICENSE"]
9
9
  authors = [{ name = "Ignace Maes" }]
@@ -11,6 +11,7 @@ keywords = ["redis", "lua", "eval", "evalsha", "transpiler", "scripting"]
11
11
  classifiers = [
12
12
  "Development Status :: 3 - Alpha",
13
13
  "Intended Audience :: Developers",
14
+ "Programming Language :: Python :: 3.10",
14
15
  "Programming Language :: Python :: 3.11",
15
16
  "Programming Language :: Python :: 3.12",
16
17
  "Programming Language :: Python :: 3.13",
@@ -18,7 +19,7 @@ classifiers = [
18
19
  "Topic :: Database",
19
20
  "Typing :: Typed",
20
21
  ]
21
- dependencies = ["redis>=5.0"]
22
+ dependencies = ["redis>=4.2"]
22
23
 
23
24
  [project.urls]
24
25
  Homepage = "https://ignacemaes.com/redis-lua-py/"
@@ -35,6 +36,8 @@ dev = [
35
36
  "ruff>=0.16",
36
37
  "mypy>=1.15",
37
38
  "zensical>=0.0.61",
39
+ # typing.assert_type, for tests/test_typing.py, arrived in Python 3.11.
40
+ "typing_extensions>=4.2; python_version < '3.11'",
38
41
  ]
39
42
 
40
43
  [build-system]
@@ -52,7 +55,7 @@ addopts = "-q"
52
55
  [tool.ruff]
53
56
  src = ["src", "tests"]
54
57
  line-length = 100
55
- target-version = "py311"
58
+ target-version = "py310"
56
59
 
57
60
  [tool.ruff.lint]
58
61
  select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "N", "C4", "PT"]
@@ -68,7 +71,7 @@ select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "N", "C4", "PT"]
68
71
  "tests/*.py" = ["B007", "F841", "SIM108"]
69
72
 
70
73
  [tool.mypy]
71
- python_version = "3.11"
74
+ python_version = "3.10"
72
75
  strict = true
73
76
  # test_typing.py carries assert_type() claims about what a call returns, which
74
77
  # are only worth anything if mypy actually reads them.
@@ -62,7 +62,7 @@ __all__ = [
62
62
  "script",
63
63
  ]
64
64
 
65
- __version__ = "0.2.1" # x-release-please-version
65
+ __version__ = "0.4.0" # x-release-please-version
66
66
 
67
67
 
68
68
  @overload