Skip to content

Commit 651cd9e

Browse files
authored
Make the client ready for the 1.3.0 release (#58)
* Make the client ready for the 1.3.0 release - Reduce the dependency count from 2 to 1 by using sync and async compatible httpx. This is what we do for our other SDKs. To match the settings of the requests, we use infinite timeout similarly for httpx. - Add ruff checks to CI and reformat code with ruff. - Fix JSON tests, which was failing due to some duplicate test file names. - Bump version to 1.3.0 * fix type hints in json commands and do some minor refactors * make json.toggle return boolean list * make the return type of json.type simpler * refactor formatters and fix the return type of hscan and sscan * simplify environment variable handling in tests * fix json.mget return type
1 parent 69dff26 commit 651cd9e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+916
-982
lines changed

.env.example

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
UPSTASH_REDIS_REST_URL="<UPSTASH_REDIS_REST_URL>"
2+
UPSTASH_REDIS_REST_TOKEN="<UPSTASH_REDIS_REST_TOKEN>"

.github/workflows/test.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ jobs:
2424
- name: Set up Poetry environment
2525
run: poetry install --no-root
2626

27+
- name: Run ruff
28+
run: |
29+
poetry run ruff check .
30+
31+
- name: Run ruff format
32+
run: |
33+
poetry run ruff format --check .
34+
2735
- name: Run mypy
2836
run: |
2937
poetry run mypy --show-error-codes .

pyproject.toml

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "upstash-redis"
3-
version = "1.2.0"
3+
version = "1.3.0"
44
description = "Serverless Redis SDK from Upstash"
55
license = "MIT"
66
authors = ["Upstash <support@upstash.com>", "Zgîmbău Tudor <tudor.zgimbau@gmail.com>"]
@@ -21,6 +21,7 @@ classifiers = [
2121
"Programming Language :: Python :: 3.10",
2222
"Programming Language :: Python :: 3.11",
2323
"Programming Language :: Python :: 3.12",
24+
"Programming Language :: Python :: 3.13",
2425
"Programming Language :: Python :: Implementation :: CPython",
2526
"Topic :: Database",
2627
"Topic :: Database :: Front-Ends",
@@ -30,14 +31,14 @@ packages = [{ include = "upstash_redis" }]
3031

3132
[tool.poetry.dependencies]
3233
python = "^3.8"
33-
aiohttp = "^3.8.4"
34-
requests = "^2.31.0"
34+
httpx = ">=0.23.0, <1"
3535

3636
[tool.poetry.group.dev.dependencies]
37-
pytest = "^7.3.0"
38-
pytest-asyncio = "^0.21.0"
39-
mypy = "^1.4.1"
40-
types-requests = "^2.31.0"
37+
pytest = "^8.3.4"
38+
pytest-asyncio = "^0.24.0"
39+
python-dotenv = "^1.0.1"
40+
mypy = "^1.14.1"
41+
ruff = "^0.9.7"
4142

4243
[build-system]
4344
requires = ["poetry-core"]

tests/commands/json/test_arrappend.py renamed to tests/commands/json/test_json_arrappend.py

+11-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
@pytest.fixture(autouse=True)
88
def setup_json(redis: Redis):
99
json_key = "json_arrappend"
10-
value: JSONValueT = {"int": 1, "array": [], "object": {"array": [ 1 ]}}
10+
value: JSONValueT = {"int": 1, "array": [], "object": {"array": [1]}}
1111
redis.json.set(json_key, "$", value)
1212
yield
1313
redis.delete(json_key)
@@ -18,30 +18,34 @@ def test_arrappend_single_element(redis: Redis):
1818
path = "$.array"
1919

2020
assert redis.json.arrappend(key, path, 1) == [1]
21-
assert redis.json.arrappend(key, path, 'new val') == [2]
21+
assert redis.json.arrappend(key, path, "new val") == [2]
2222
assert redis.json.arrappend(key, path, 1.5) == [3]
2323
assert redis.json.arrappend(key, path, True) == [4]
2424
assert redis.json.arrappend(key, path, [1]) == [5]
2525
assert redis.json.arrappend(key, path, {"key": "value"}) == [6]
26-
assert redis.json.get(key, path) == [[1, 'new val', 1.5, True, [1], {"key": "value"} ]]
26+
assert redis.json.get(key, path) == [
27+
[1, "new val", 1.5, True, [1], {"key": "value"}]
28+
]
2729

2830

2931
def test_arrappend_multiple_elements(redis: Redis):
3032
key = "json_arrappend"
3133
path = "$.array"
32-
new_values: List[JSONValueT] = [1, 'new val', 1.5, True, [1], {"key": "value"}]
34+
new_values: List[JSONValueT] = [1, "new val", 1.5, True, [1], {"key": "value"}]
3335

3436
assert redis.json.arrappend(key, path, *new_values) == [6]
35-
assert redis.json.get(key, path) == [[1, 'new val', 1.5, True, [1], {"key": "value"}]]
37+
assert redis.json.get(key, path) == [
38+
[1, "new val", 1.5, True, [1], {"key": "value"}]
39+
]
3640

3741

3842
def test_arrappend_nonarray_path(redis: Redis):
3943
key = "json_arrappend"
4044
path = "$.int"
4145
new_value = 9
4246

43-
assert redis.json.arrappend(key, path, new_value) == [ None ]
44-
assert redis.json.get(key, path) == [ 1 ]
47+
assert redis.json.arrappend(key, path, new_value) == [None]
48+
assert redis.json.get(key, path) == [1]
4549

4650

4751
def test_arrappend_wildcard(redis: Redis):

tests/commands/json/test_arrindex.py renamed to tests/commands/json/test_json_arrindex.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,26 @@
66
@pytest.fixture(autouse=True)
77
def setup_json(redis: Redis):
88
json_key = "json_arrindex"
9-
value: JSONValueT = {"array": [1, 'test', ['a'], 1.5, {'test': 1}], "int": 1, "object": {"array": [ 0, 1 ]}}
9+
value: JSONValueT = {
10+
"array": [1, "test", ["a"], 1.5, {"test": 1}],
11+
"int": 1,
12+
"object": {"array": [0, 1]},
13+
}
1014
redis.json.set(json_key, "$", value)
1115
yield
1216
redis.delete(json_key)
1317

18+
1419
def test_arrindex_existing_element(redis: Redis):
1520
key = "json_arrindex"
1621
path = "$.array"
1722

1823
assert redis.json.arrindex(key, path, 1) == [0]
19-
assert redis.json.arrindex(key, path, 'test') == [1]
20-
assert redis.json.arrindex(key, path, ['a']) == [2]
24+
assert redis.json.arrindex(key, path, "test") == [1]
25+
assert redis.json.arrindex(key, path, ["a"]) == [2]
2126
assert redis.json.arrindex(key, path, 1.5) == [3]
22-
assert redis.json.arrindex(key, path, {'test': 1}) == [4]
27+
assert redis.json.arrindex(key, path, {"test": 1}) == [4]
28+
2329

2430
def test_arrindex_nonexisting_element(redis: Redis):
2531
key = "json_arrindex"
@@ -28,13 +34,15 @@ def test_arrindex_nonexisting_element(redis: Redis):
2834

2935
assert redis.json.arrindex(key, path, value) == [-1]
3036

37+
3138
def test_arrindex_nonarray_path(redis: Redis):
3239
key = "json_arrindex"
3340
path = "$.int"
3441
value = 4
3542

3643
assert redis.json.arrindex(key, path, value) == [None]
3744

45+
3846
def test_arrindex_wildcard(redis: Redis):
3947
key = "json_arrindex"
4048
path = "$..array"

tests/commands/json/test_arrinsert.py renamed to tests/commands/json/test_json_arrinsert.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ def test_arrinsert_single_element(redis: Redis):
1616
key = "json_arrinsert"
1717
path = "$.array"
1818

19-
assert redis.json.arrinsert(key, path, 0,1) == [1]
20-
assert redis.json.arrinsert(key, path, 0, 'new val') == [2]
21-
assert redis.json.arrinsert(key, path, 0, 1.5) == [3]
22-
assert redis.json.arrinsert(key, path, 0, True) == [4]
23-
assert redis.json.arrinsert(key, path, 0, [1]) == [5]
24-
assert redis.json.arrinsert(key, path, 0, {"key": "value"}) == [6]
25-
assert redis.json.get(key, path) == [[ {"key": "value"}, [1],True , 1.5, 'new val', 1 ]]
19+
assert redis.json.arrinsert(key, path, 0, 1) == [1]
20+
assert redis.json.arrinsert(key, path, 0, "new val") == [2]
21+
assert redis.json.arrinsert(key, path, 0, 1.5) == [3]
22+
assert redis.json.arrinsert(key, path, 0, True) == [4]
23+
assert redis.json.arrinsert(key, path, 0, [1]) == [5]
24+
assert redis.json.arrinsert(key, path, 0, {"key": "value"}) == [6]
25+
assert redis.json.get(key, path) == [
26+
[{"key": "value"}, [1], True, 1.5, "new val", 1]
27+
]
2628

2729

2830
def test_arrinsert_multiple_elements(redis: Redis):

tests/commands/json/test_arrlen.py renamed to tests/commands/json/test_json_arrlen.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,35 @@
66
@pytest.fixture(autouse=True)
77
def setup_json(redis: Redis):
88
json_key = "json_arrlen"
9-
value: JSONValueT = {"array": [1, 2, 3, 4], "int": 1, "object": {"array": [1, 2, 3]}}
9+
value: JSONValueT = {
10+
"array": [1, 2, 3, 4],
11+
"int": 1,
12+
"object": {"array": [1, 2, 3]},
13+
}
1014
redis.json.set(json_key, "$", value)
1115
yield
1216
redis.delete(json_key)
1317

18+
1419
def test_arrlen_existing_element(redis: Redis):
1520
key = "json_arrlen"
1621
path = "$.array"
1722

1823
length = redis.json.arrlen(key, path)
1924
assert length == [4]
2025

26+
2127
def test_arrlen_nonarray_path(redis: Redis):
2228
key = "json_arrlen"
2329
path = "$.int"
2430

2531
length = redis.json.arrlen(key, path)
2632
assert length == [None]
2733

34+
2835
def test_arrlen_wildcard(redis: Redis):
2936
key = "json_arrlen"
3037
path = "$..array"
3138

3239
length = redis.json.arrlen(key, path)
33-
assert length == [3, 4]
40+
assert length == [3, 4]

tests/commands/json/test_arrpop.py renamed to tests/commands/json/test_json_arrpop.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
@pytest.fixture(autouse=True)
77
def setup_json(redis: Redis):
88
json_key = "json_arrpop"
9-
value: JSONValueT = {"int": 1, "array": [1, 2, 3, 4], "object": {"array": [5, 6, 7]}}
9+
value: JSONValueT = {
10+
"int": 1,
11+
"array": [1, 2, 3, 4],
12+
"object": {"array": [5, 6, 7]},
13+
}
1014
redis.json.set(json_key, "$", value)
1115
yield
1216
redis.delete(json_key)

tests/commands/json/test_arrtrim.py renamed to tests/commands/json/test_json_arrtrim.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
@pytest.fixture(autouse=True)
77
def setup_json(redis: Redis):
88
json_key = "json_arrtrim"
9-
value: JSONValueT = {"int": 1, "array": [1, 2, 3, 4], "object": {"array": [5, 6, 7]}}
9+
value: JSONValueT = {
10+
"int": 1,
11+
"array": [1, 2, 3, 4],
12+
"object": {"array": [5, 6, 7]},
13+
}
1014
redis.json.set(json_key, "$", value)
1115
yield
1216
redis.delete(json_key)

tests/commands/json/test_clear.py renamed to tests/commands/json/test_json_clear.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
@pytest.fixture(autouse=True)
77
def setup_json(redis: Redis):
88
json_key = "json_clear"
9-
value: JSONValueT = {"int": 1, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}}
9+
value: JSONValueT = {
10+
"int": 1,
11+
"array": [1, 2, 3, 4],
12+
"object": {"array": [1, 2, 3]},
13+
}
1014
redis.json.set(json_key, "$", value)
1115
yield
1216
redis.delete(json_key)
@@ -18,7 +22,9 @@ def test_clear_single_element(redis: Redis):
1822

1923
removed_element = redis.json.clear(key, path)
2024
assert removed_element == 1
21-
assert redis.json.get(key) == [{"int": 1, "array": [], "object": {"array": [1, 2, 3]}}]
25+
assert redis.json.get(key) == [
26+
{"int": 1, "array": [], "object": {"array": [1, 2, 3]}}
27+
]
2228

2329

2430
def test_clear_wildcard(redis: Redis):

tests/commands/json/test_delete_json.py renamed to tests/commands/json/test_json_delete.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
@pytest.fixture(autouse=True)
77
def setup_json(redis: Redis):
88
json_key = "json_delete"
9-
value: JSONValueT = {"int": 1, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}}
9+
value: JSONValueT = {
10+
"int": 1,
11+
"array": [1, 2, 3, 4],
12+
"object": {"array": [1, 2, 3]},
13+
}
1014
redis.json.set(json_key, "$", value)
1115
yield
1216
redis.delete(json_key)

tests/commands/json/test_forget.py renamed to tests/commands/json/test_json_forget.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
@pytest.fixture(autouse=True)
77
def setup_json(redis: Redis):
88
json_key = "json_forget"
9-
value: JSONValueT = {"int": 1, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}}
9+
value: JSONValueT = {
10+
"int": 1,
11+
"array": [1, 2, 3, 4],
12+
"object": {"array": [1, 2, 3]},
13+
}
1014
redis.json.set(json_key, "$", value)
1115
yield
1216
redis.delete(json_key)

tests/commands/json/test_get_json.py renamed to tests/commands/json/test_json_get.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
@pytest.fixture(autouse=True)
77
def setup_json(redis: Redis):
88
json_key = "json_get"
9-
value: JSONValueT = {"int": 1, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}}
9+
value: JSONValueT = {
10+
"int": 1,
11+
"array": [1, 2, 3, 4],
12+
"object": {"array": [1, 2, 3]},
13+
}
1014
redis.json.set(json_key, "$", value)
1115
yield
1216
redis.delete(json_key)
@@ -15,7 +19,9 @@ def setup_json(redis: Redis):
1519
def test_get(redis: Redis):
1620
key = "json_get"
1721

18-
assert redis.json.get(key) == [{"int": 1, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}}]
22+
assert redis.json.get(key) == [
23+
{"int": 1, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}}
24+
]
1925

2026

2127
def test_get_path(redis: Redis):
@@ -27,7 +33,11 @@ def test_get_path(redis: Redis):
2733
def test_get_multiple_path(redis: Redis):
2834
key = "json_get"
2935
paths = ["$.array", "$.object", "$.int"]
30-
assert redis.json.get(key, *paths) == {"$.array": [[1, 2, 3, 4]], "$.object": [{"array": [1, 2, 3]}], "$.int": [1]}
36+
assert redis.json.get(key, *paths) == {
37+
"$.array": [[1, 2, 3, 4]],
38+
"$.object": [{"array": [1, 2, 3]}],
39+
"$.int": [1],
40+
}
3141

3242

3343
def test_get_nonexisting_key(redis: Redis):
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import pytest
2+
3+
from upstash_redis import Redis
4+
from upstash_redis.typing import JSONValueT
5+
6+
7+
@pytest.fixture(autouse=True)
8+
def setup_json(redis: Redis):
9+
json_key = "json_merge"
10+
value: JSONValueT = {
11+
"int": 1,
12+
"array": [1, 2, 3, 4],
13+
"object": {"array": [1, 2, 3]},
14+
}
15+
redis.json.set(json_key, "$", value)
16+
yield
17+
redis.delete(json_key)
18+
19+
20+
def test_merge(redis: Redis):
21+
key = "json_merge"
22+
23+
assert redis.json.merge(key, "$", {"str": "test", "int": 2}) is True
24+
assert redis.json.get(key) == [
25+
{"int": 2, "array": [1, 2, 3, 4], "object": {"array": [1, 2, 3]}, "str": "test"}
26+
]
27+
28+
29+
def test_merge_nonexisting_key(redis: Redis):
30+
key = "json_merge_nonexisting"
31+
32+
assert redis.json.merge(key, "$", {"str": "test", "int": 2}) is True
33+
assert redis.json.get(key) == [{"int": 2, "str": "test"}]
34+
35+
36+
def test_merge_wildcard(redis: Redis):
37+
key = "json_merge"
38+
path = "$..array"
39+
40+
assert redis.json.merge(key, path, [2, 2, 3, 4]) is True
41+
assert redis.json.get(key) == [
42+
{"int": 1, "array": [2, 2, 3, 4], "object": {"array": [2, 2, 3, 4]}}
43+
]

0 commit comments

Comments
 (0)