Skip to content

Commit

Permalink
Merge pull request #2 from use-py/set
Browse files Browse the repository at this point in the history
✨ feat: add set
  • Loading branch information
mic1on authored Sep 16, 2024
2 parents 4f71473 + 960cac6 commit d52edb3
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 4 deletions.
32 changes: 32 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File with Arguments",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": "${command:pickArgs}"
},
{
"name": "Python: pytest",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"-v",
"-s",
"${workspaceFolder}/tests"
],
"console": "integratedTerminal",
"cwd": "${workspaceFolder}",
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
}
]
}
6 changes: 2 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ version = "0.1.8"
description = ""
authors = ["miclon <jcnd@163.com>"]
readme = "README.md"
packages = [
{ include = 'use_redis', from = 'src' }
]
packages = [{ include = 'use_redis', from = 'src' }]

[tool.poetry.dependencies]
python = "^3.8"
Expand All @@ -15,6 +13,7 @@ redis = ">=4.6.0,<6.0.0"
[tool.poetry.group.test.dependencies]
pylint = "*"
pytest = "*"
pytest-mock = "*"
black = "*"
flake8 = "*"
isort = "*"
Expand All @@ -24,4 +23,3 @@ pre-commit-hooks = "*"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

72 changes: 72 additions & 0 deletions src/use_redis/set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from .store import RedisStore


class RedisSetStore(RedisStore):
def __init__(self, key, **kwargs):
super().__init__(**kwargs)
self._key = key

@property
def key(self):
return self._key

def add(self, *values):
"""向集合中添加一个或多个成员"""
return self.connection.sadd(self.key, *values)

def remove(self, *values):
"""从集合中移除一个或多个成员"""
return self.connection.srem(self.key, *values)

def members(self):
"""返回集合中的所有成员"""
return self.connection.smembers(self.key)

def is_member(self, value):
"""判断value是否是集合的成员"""
return self.connection.sismember(self.key, value)

def are_members(self, *values):
"""判断多个value是否是集合的成员"""
return self.connection.sismember(self.key, *values)

def size(self):
"""返回集合的成员数"""
return self.connection.scard(self.key)

def pop(self):
"""随机移除并返回集合中的一个成员"""
return self.connection.spop(self.key)

def move(self, dst_key, value):
"""将成员从当前集合移动到另一个集合"""
return self.connection.smove(self.key, dst_key, value)

def intersection(self, *other_keys):
"""返回当前集合与其他集合的交集"""
return self.connection.sinter(self.key, *other_keys)

def union(self, *other_keys):
"""返回当前集合与其他集合的并集"""
return self.connection.sunion(self.key, *other_keys)

def difference(self, *other_keys):
"""返回当前集合与其他集合的差集"""
return self.connection.sdiff(self.key, *other_keys)

def random_member(self):
"""随机返回集合中的一个成员,但不删除"""
return self.connection.srandmember(self.key)

def scan(self, cursor=0, match=None, count=None):
"""迭代集合中的元素"""
return self.connection.sscan(self.key, cursor, match, count)

def __getattr__(self, name):
"""动态处理未实现的方法"""

def method(*args, **kwargs):
redis_method = getattr(self.connection, name)
return redis_method(self.key, *args, **kwargs)

return method
90 changes: 90 additions & 0 deletions tests/test_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import pytest
from use_redis.set import RedisSetStore


@pytest.fixture
def mock_redis(mocker):
mock = mocker.patch("redis.Redis")
return mock.return_value


@pytest.fixture
def set_store(mock_redis):
return RedisSetStore("test_set")


def test_add(set_store, mock_redis):
set_store.add("value1", "value2")
mock_redis.sadd.assert_called_once_with("test_set", "value1", "value2")


def test_remove(set_store, mock_redis):
set_store.remove("value1", "value2")
mock_redis.srem.assert_called_once_with("test_set", "value1", "value2")


def test_members(set_store, mock_redis):
mock_redis.smembers.return_value = {"value1", "value2"}
result = set_store.members()
assert result == {"value1", "value2"}
mock_redis.smembers.assert_called_once_with("test_set")


def test_is_member(set_store, mock_redis):
mock_redis.sismember.return_value = True
result = set_store.is_member("value1")
assert result is True
mock_redis.sismember.assert_called_once_with("test_set", "value1")


def test_size(set_store, mock_redis):
mock_redis.scard.return_value = 2
result = set_store.size()
assert result == 2
mock_redis.scard.assert_called_once_with("test_set")


def test_pop(set_store, mock_redis):
mock_redis.spop.return_value = "value1"
result = set_store.pop()
assert result == "value1"
mock_redis.spop.assert_called_once_with("test_set")


def test_move(set_store, mock_redis):
set_store.move("dst_set", "value1")
mock_redis.smove.assert_called_once_with("test_set", "dst_set", "value1")


def test_intersection(set_store, mock_redis):
set_store.intersection("other_set1", "other_set2")
mock_redis.sinter.assert_called_once_with("test_set", "other_set1", "other_set2")


def test_union(set_store, mock_redis):
set_store.union("other_set1", "other_set2")
mock_redis.sunion.assert_called_once_with("test_set", "other_set1", "other_set2")


def test_difference(set_store, mock_redis):
set_store.difference("other_set1", "other_set2")
mock_redis.sdiff.assert_called_once_with("test_set", "other_set1", "other_set2")


def test_random_member(set_store, mock_redis):
mock_redis.srandmember.return_value = "value1"
result = set_store.random_member()
assert result == "value1"
mock_redis.srandmember.assert_called_once_with("test_set")


def test_scan(set_store, mock_redis):
mock_redis.sscan.return_value = (0, ["value1", "value2"])
result = set_store.scan(cursor=0, match="val*", count=2)
assert result == (0, ["value1", "value2"])
mock_redis.sscan.assert_called_once_with("test_set", 0, "val*", 2)


def test_dynamic_method(set_store, mock_redis):
set_store.srandmember(3)
mock_redis.srandmember.assert_called_once_with("test_set", 3)

0 comments on commit d52edb3

Please sign in to comment.