-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from use-py/set
✨ feat: add set
- Loading branch information
Showing
4 changed files
with
196 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}" | ||
} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |