Skip to content

Backport from master (5.3.0b5) #3506

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ runs:

# Mapping of redis version to stack version
declare -A redis_stack_version_mapping=(
["7.4.1"]="7.4.0-v1"
["7.2.6"]="7.2.0-v13"
["6.2.16"]="6.2.6-v17"
["7.4.2"]="7.4.0-v3"
["7.2.7"]="7.2.0-v15"
["6.2.17"]="6.2.6-v19"
)

if [[ -v redis_stack_version_mapping[$REDIS_VERSION] ]]; then
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ env:
COVERAGE_CORE: sysmon
REDIS_IMAGE: redis:latest
REDIS_STACK_IMAGE: redis/redis-stack-server:latest
CURRENT_REDIS_VERSION: '7.4.1'
CURRENT_REDIS_VERSION: '7.4.2'

jobs:
dependency-audit:
Expand Down Expand Up @@ -74,7 +74,7 @@ jobs:
max-parallel: 15
fail-fast: false
matrix:
redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}', '7.2.6', '6.2.16']
redis-version: [ '${{ needs.redis_version.outputs.CURRENT }}', '7.2.7', '6.2.17']
python-version: ['3.8', '3.12']
parser-backend: ['plain']
event-loop: ['asyncio']
Expand Down
2 changes: 1 addition & 1 deletion dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ uvloop
vulture>=2.3.0
wheel>=0.30.0
numpy>=1.24.0
redis-entraid==0.1.0b1
redis-entraid==0.3.0b1
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ services:
- all

redis-stack:
image: ${REDIS_STACK_IMAGE:-redis/redis-stack-server:edge}
image: ${REDIS_STACK_IMAGE:-redis/redis-stack-server:latest}
container_name: redis-stack
ports:
- 6479:6379
Expand All @@ -112,6 +112,7 @@ services:
profiles:
- standalone
- all-stack
- all

redis-stack-graph:
image: redis/redis-stack-server:6.2.6-v15
Expand Down
15 changes: 7 additions & 8 deletions docs/advanced_features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -471,31 +471,30 @@ command is received.
Token-based authentication
~~~~~~~~~~~~~~~~~~~~~~~~~~

Since redis-py version 5.3.0 new StreamableCredentialProvider interface was introduced.
This interface describes a CredentialProvider with an ability to stream an events that will be handled by listener.
Since redis-py version 5.3.0 new `StreamableCredentialProvider` interface was introduced.
This interface describes a `CredentialProvider` with an ability to stream an events that will be handled by listener.

To keep redis-py with minimal dependencies needed to run it, we decided to separate StreamableCredentialProvider
To keep redis-py with minimal dependencies needed to run it, we decided to separate `StreamableCredentialProvider`
implementations in a separate packages. So If you're interested to try this feature please add them as a separate
dependency to your project.

`EntraIdCredentialProvider` is a first implementation that allows you to integrate redis-py with Azure Cache for Redis
service. It will allows you to obtain a tokens from Microsoft EntraID and authenticate/re-authenticate your connections
service. It will allows you to obtain a tokens from `Microsoft EntraID` and authenticate/re-authenticate your connections
with it in a background mode.

To get `EntraIdCredentialProvider` you need to install following package:

`pip install redis-entraid`

To setup a credential provider, first you have to create and configure an IdentityProvider and provide
TokenAuthConfig object.
To setup a credential provider, please use one of the factory methods bundled with package.
`Here's a quick guide how to do this
<https://github.com/redis-developer/redispy-entra-credentials?tab=readme-ov-file#usage>`_
<https://github.com/redis/redis-py-entraid/blob/main/README.md>`_

Now all you have to do is to pass an instance of `EntraIdCredentialProvider` via constructor,
available for sync and async clients:

.. code:: python

>>> cred_provider = EntraIdCredentialProvider(auth_config)
>>> cred_provider = create_from_service_principal(CLIENT_ID, CLIENT_SECRET, TENANT_ID)
>>> r = Redis(credential_provider=cred_provider)
>>> r.ping()
36 changes: 36 additions & 0 deletions doctests/cmds_cnxmgmt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# EXAMPLE: cmds_cnxmgmt
# HIDE_START
import redis

r = redis.Redis(decode_responses=True)
# HIDE_END

# STEP_START auth1
# REMOVE_START
r.config_set("requirepass", "temp_pass")
# REMOVE_END
res1 = r.auth(password="temp_pass")
print(res1) # >>> True

res2 = r.auth(password="temp_pass", username="default")
print(res2) # >>> True

# REMOVE_START
assert res1 == True
assert res2 == True
r.config_set("requirepass", "")
# REMOVE_END
# STEP_END

# STEP_START auth2
# REMOVE_START
r.acl_setuser("test-user", enabled=True, passwords=["+strong_password"], commands=["+acl"])
# REMOVE_END
res = r.auth(username="test-user", password="strong_password")
print(res) # >>> True

# REMOVE_START
assert res == True
r.acl_deluser("test-user")
# REMOVE_END
# STEP_END
24 changes: 24 additions & 0 deletions doctests/cmds_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,27 @@
r.delete("myhash")
# REMOVE_END
# STEP_END

# STEP_START hgetall
res10 = r.hset("myhash", mapping={"field1": "Hello", "field2": "World"})

res11 = r.hgetall("myhash")
print(res11) # >>> { "field1": "Hello", "field2": "World" }

# REMOVE_START
assert res11 == { "field1": "Hello", "field2": "World" }
r.delete("myhash")
# REMOVE_END
# STEP_END

# STEP_START hvals
res10 = r.hset("myhash", mapping={"field1": "Hello", "field2": "World"})

res11 = r.hvals("myhash")
print(res11) # >>> [ "Hello", "World" ]

# REMOVE_START
assert res11 == [ "Hello", "World" ]
r.delete("myhash")
# REMOVE_END
# STEP_END
123 changes: 123 additions & 0 deletions doctests/cmds_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# EXAMPLE: cmds_list
# HIDE_START
import redis

r = redis.Redis(decode_responses=True)
# HIDE_END

# STEP_START lpush
res1 = r.lpush("mylist", "world")
print(res1) # >>> 1

res2 = r.lpush("mylist", "hello")
print(res2) # >>> 2

res3 = r.lrange("mylist", 0, -1)
print(res3) # >>> [ "hello", "world" ]

# REMOVE_START
assert res3 == [ "hello", "world" ]
r.delete("mylist")
# REMOVE_END
# STEP_END

# STEP_START lrange
res4 = r.rpush("mylist", "one");
print(res4) # >>> 1

res5 = r.rpush("mylist", "two")
print(res5) # >>> 2

res6 = r.rpush("mylist", "three")
print(res6) # >>> 3

res7 = r.lrange('mylist', 0, 0)
print(res7) # >>> [ 'one' ]

res8 = r.lrange('mylist', -3, 2)
print(res8) # >>> [ 'one', 'two', 'three' ]

res9 = r.lrange('mylist', -100, 100)
print(res9) # >>> [ 'one', 'two', 'three' ]

res10 = r.lrange('mylist', 5, 10)
print(res10) # >>> []

# REMOVE_START
assert res7 == [ 'one' ]
assert res8 == [ 'one', 'two', 'three' ]
assert res9 == [ 'one', 'two', 'three' ]
assert res10 == []
r.delete('mylist')
# REMOVE_END
# STEP_END

# STEP_START llen
res11 = r.lpush("mylist", "World")
print(res11) # >>> 1

res12 = r.lpush("mylist", "Hello")
print(res12) # >>> 2

res13 = r.llen("mylist")
print(res13) # >>> 2

# REMOVE_START
assert res13 == 2
r.delete("mylist")
# REMOVE_END
# STEP_END

# STEP_START rpush
res14 = r.rpush("mylist", "hello")
print(res14) # >>> 1

res15 = r.rpush("mylist", "world")
print(res15) # >>> 2

res16 = r.lrange("mylist", 0, -1)
print(res16) # >>> [ "hello", "world" ]

# REMOVE_START
assert res16 == [ "hello", "world" ]
r.delete("mylist")
# REMOVE_END
# STEP_END

# STEP_START lpop
res17 = r.rpush("mylist", *["one", "two", "three", "four", "five"])
print(res17) # >>> 5

res18 = r.lpop("mylist")
print(res18) # >>> "one"

res19 = r.lpop("mylist", 2)
print(res19) # >>> ['two', 'three']

res17 = r.lrange("mylist", 0, -1)
print(res17) # >>> [ "four", "five" ]

# REMOVE_START
assert res17 == [ "four", "five" ]
r.delete("mylist")
# REMOVE_END
# STEP_END

# STEP_START rpop
res18 = r.rpush("mylist", *["one", "two", "three", "four", "five"])
print(res18) # >>> 5

res19 = r.rpop("mylist")
print(res19) # >>> "five"

res20 = r.rpop("mylist", 2)
print(res20) # >>> ['four', 'three']

res21 = r.lrange("mylist", 0, -1)
print(res21) # >>> [ "one", "two" ]

# REMOVE_START
assert res21 == [ "one", "two" ]
r.delete("mylist")
# REMOVE_END
# STEP_END
30 changes: 30 additions & 0 deletions doctests/cmds_servermgmt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# EXAMPLE: cmds_servermgmt
# HIDE_START
import redis

r = redis.Redis(decode_responses=True)
# HIDE_END

# STEP_START flushall
# REMOVE_START
r.set("foo", "1")
r.set("bar", "2")
r.set("baz", "3")
# REMOVE_END
res1 = r.flushall(asynchronous=False)
print(res1) # >>> True

res2 = r.keys()
print(res2) # >>> []

# REMOVE_START
assert res1 == True
assert res2 == []
# REMOVE_END
# STEP_END

# STEP_START info
res3 = r.info()
print(res3)
# >>> {'redis_version': '7.4.0', 'redis_git_sha1': 'c9d29f6a',...}
# STEP_END
35 changes: 35 additions & 0 deletions doctests/cmds_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# EXAMPLE: cmds_set
# HIDE_START
import redis

r = redis.Redis(decode_responses=True)
# HIDE_END

# STEP_START sadd
res1 = r.sadd("myset", "Hello", "World")
print(res1) # >>> 2

res2 = r.sadd("myset", "World")
print(res2) # >>> 0

res3 = r.smembers("myset")
print(res3) # >>> {'Hello', 'World'}

# REMOVE_START
assert res3 == {'Hello', 'World'}
r.delete('myset')
# REMOVE_END
# STEP_END

# STEP_START smembers
res4 = r.sadd("myset", "Hello", "World")
print(res4) # >>> 2

res5 = r.smembers("myset")
print(res5) # >>> {'Hello', 'World'}

# REMOVE_START
assert res5 == {'Hello', 'World'}
r.delete('myset')
# REMOVE_END
# STEP_END
6 changes: 3 additions & 3 deletions doctests/dt_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,20 @@
# REMOVE_END

# STEP_START ltrim
res27 = r.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5")
res27 = r.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5")
print(res27) # >>> 5

res28 = r.ltrim("bikes:repairs", 0, 2)
print(res28) # >>> True

res29 = r.lrange("bikes:repairs", 0, -1)
print(res29) # >>> ['bike:5', 'bike:4', 'bike:3']
print(res29) # >>> ['bike:1', 'bike:2', 'bike:3']
# STEP_END

# REMOVE_START
assert res27 == 5
assert res28 is True
assert res29 == ["bike:5", "bike:4", "bike:3"]
assert res29 == ["bike:1", "bike:2", "bike:3"]
r.delete("bikes:repairs")
# REMOVE_END

Expand Down
Loading
Loading