Skip to content

Commit

Permalink
Finally support redis-py 3.* package or later
Browse files Browse the repository at this point in the history
This commit adds compatibility layer, so old plugins do not need
to adapt changes to support >3.* python3 package of redis.
Some portions of that layer code is copy-pasted from shinqlx.

Co-authored-By: Markus Gärtner <shino@shino.de>
  • Loading branch information
em92 and mgaertne committed Aug 10, 2024
1 parent 0341ece commit 5dd394a
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 1 deletion.
28 changes: 28 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Test

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

env:
PIP_BREAK_SYSTEM_PACKAGES: "1"

jobs:
test_redis:

strategy:
matrix:
redis-package-version: ["2.*", "3.*", "4.*", "5.*"]
os: ["ubuntu-24.04", "ubuntu-22.04", "ubuntu-20.04"]

runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Start Redis
uses: superchargejs/redis-github-action@1.1.0
- name: Install redis package
run: python3 -m pip install redis==${{ matrix.redis-package-version }}
- name: Run test
run: python3 -m unittest discover python/tests/
48 changes: 47 additions & 1 deletion python/minqlx/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
# You should have received a copy of the GNU General Public License
# along with minqlx. If not, see <http://www.gnu.org/licenses/>.

import minqlx
try:
import minqlx
except ImportError:
pass

import redis

# ====================================================================
Expand Down Expand Up @@ -313,3 +317,45 @@ def close(self):
if Redis._pool:
Redis._pool.disconnect()
Redis._pool = None

if redis.VERSION > (3,):
def zincrby(self, name, value, amount=1):
return self.r.zincrby(name, amount, value)

def zadd(self, name, *args, **kwargs):
if len(args) == 1 and isinstance(args[0], dict):
# redis >= 3.* arguments given
return self.r.zadd(name, *args, **kwargs)

if len(args) > 0 and len(args) % 2 != 0:
raise redis.RedisError("ZADD requires an equal number of values and scores")

mapping = {}
for i in range(0, len(args), 2):
mapping[args[i + 1]] = args[i]

return self.r.zadd(name, mapping, **kwargs)

def msetnx(self, *args, **kwargs):
mapping = {}
if args:
if len(args) != 1 or not isinstance(args[0], dict):
raise redis.RedisError("MSETNX requires **kwargs or a single dict arg")
mapping.update(args[0])

if kwargs:
mapping.update(kwargs)

return self.r.msetnx(mapping)

def mset(self, *args, **kwargs):
mapping = {}
if args:
if len(args) != 1 or not isinstance(args[0], dict):
raise redis.RedisError("MSET requires **kwargs or a single dict arg")
mapping.update(args[0])

if kwargs:
mapping.update(kwargs)

return self.r.mset(mapping)
1 change: 1 addition & 0 deletions python/tests/database.py
154 changes: 154 additions & 0 deletions python/tests/test_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import random
import string
from time import sleep
from unittest import TestCase

import redis

import database


def random_string():
return "".join([random.choice(string.ascii_letters) for x in range(10)])


class RedisRegressionTestCase(TestCase):
def setUp(self):
self.c = database.Redis(None)
self.c.connect("127.0.0.1:6379")

def test_zadd_01(self):
c = self.c
key = "zadd_01_key_" + random_string()

c.zadd(key, 5, "player1", 6, "player2")
res = dict(c.zrange(key, 0, -1, withscores=True))
self.assertEqual(res["player1"], 5)
self.assertEqual(res["player2"], 6)

c.zadd(key, 2, "player1")
res = dict(c.zrange(key, 0, -1, withscores=True))
self.assertEqual(res["player1"], 2)
self.assertEqual(res["player2"], 6)

c.zadd(key, 4, "player2")
res = dict(c.zrange(key, 0, -1, withscores=True))
self.assertEqual(res["player1"], 2)
self.assertEqual(res["player2"], 4)

def test_zadd_02(self):
c = self.c
key = "zadd_02_key_" + random_string()

# redis-py 3.* introduced xx, nx, ch and incr kwarg params
# this codes deliberately does not work in redis-py 2.*
if redis.VERSION < (3,):
self.skipTest("Old version of redis package: %s" % (redis.VERSION,))
c.zadd(key, 5, "player1", 6, "player2")
c.zadd(key, {"player1": 3}, xx=True)
res = dict(c.zrange(key, 0, -1, withscores=True))
self.assertEqual(res["player1"], 3)

c.zadd(key, 3, "player_does_not_exist", xx=True)
res = dict(c.zrange(key, 0, -1, withscores=True))
self.assertNotIn("player_does_not_exist", res)

def test_zincrby_01(self):
key = "zincrby_01_key_" + random_string()
c = self.c

c.zincrby(key, "FIELD1")
c.zincrby(key, "FIELD2", 2)
res = dict(c.zrange(key, 0, -1, withscores=True))
self.assertEqual(res["FIELD1"], 1)
self.assertEqual(res["FIELD2"], 2)

c.zincrby(key, "FIELD1")
c.zincrby(key, "FIELD2", 3)
res = dict(c.zrange(key, 0, -1, withscores=True))
self.assertEqual(res["FIELD1"], 2)
self.assertEqual(res["FIELD2"], 5)

def test_lrem_01(self):
key = "lrem_01_key_" + random_string()
c = self.c

c.rpush(
key,
"Cat",
"Dog",
"Horse",
"Cat",
"Dog",
"Cat",
"Monkey",
"Dog",
"Cat",
"Dog",
"Buffalo",
)
c.lrem(key, 2, "Cat")
result = c.lrange(key, 0, -1)
self.assertEqual(
result,
["Dog", "Horse", "Dog", "Cat", "Monkey", "Dog", "Cat", "Dog", "Buffalo"],
)

def test_setex_01(self):
key = "setex_01_key_" + random_string()
c = self.c
test_value = "test value"
timeout = 1

c.setex(key, timeout, test_value)
self.assertEqual(c.get(key), test_value, "Too small timeout value?")
sleep(timeout + 0.5)
self.assertEqual(c.get(key), None)

def test_mset_01(self):
c = self.c
c.mset(field1="example1", field2="example2")
self.assertEqual(["example1", "example2"], c.mget("field1", "field2"))

def test_mset_02(self):
c = self.c
c.mset({"field3": "example1", "field4": "example2"})
self.assertEqual(["example1", "example2"], c.mget("field3", "field4"))

def test_msetnx_01_kwargs(self):
c = self.c
prefix = "msetnx_01_key_" + random_string()
key1 = prefix + "_field1"
key2 = prefix + "_field2"
d = {
key1: "example1",
key2: "example2",
}
c.msetnx(**d)
self.assertEqual(["example1", "example2"], c.mget(key1, key2))

d = {
key1: "value should not be used",
key2: "same here",
}
c.msetnx(**d)
self.assertEqual(["example1", "example2"], c.mget(key1, key2))

def test_msetnx_02_dict(self):
c = self.c
prefix = "msetnx_02_key_" + random_string()
key1 = prefix + "_field1"
key2 = prefix + "_field2"
d = {
key1: "example1",
key2: "example2",
}
c.msetnx(d)
self.assertEqual(["example1", "example2"], c.mget(key1, key2))

d = {
key1: "value should not be used",
key2: "same here",
}
c.msetnx(d)
self.assertEqual(["example1", "example2"], c.mget(key1, key2))

0 comments on commit 5dd394a

Please sign in to comment.