trpc-redis-python 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trpc_redis/__init__.py +30 -0
- trpc_redis/_client.py +265 -0
- trpc_redis/_codec.py +69 -0
- trpc_redis/_constants.py +52 -0
- trpc_redis/_transport.py +296 -0
- trpc_redis/_types.py +65 -0
- trpc_redis/version.py +12 -0
- trpc_redis_python-1.0.0.dist-info/METADATA +637 -0
- trpc_redis_python-1.0.0.dist-info/RECORD +10 -0
- trpc_redis_python-1.0.0.dist-info/WHEEL +4 -0
trpc_redis/__init__.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making tRPC-Python-Database available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2026 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# tRPC-Python-Database is licensed under Apache-2.0.
|
|
6
|
+
"""Redis client for the tRPC Python framework.
|
|
7
|
+
|
|
8
|
+
Importing this package registers the Redis codec and transport plugins and
|
|
9
|
+
re-exports the public client types.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from ._client import create_rsp_object
|
|
13
|
+
from ._client import get_options
|
|
14
|
+
from ._client import Client
|
|
15
|
+
from ._codec import ClientCodec
|
|
16
|
+
from ._constants import RequestMode
|
|
17
|
+
from ._transport import RedisClientTransport
|
|
18
|
+
from ._types import Request
|
|
19
|
+
from ._types import Response
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
'Request',
|
|
23
|
+
'Response',
|
|
24
|
+
'RequestMode',
|
|
25
|
+
'create_rsp_object',
|
|
26
|
+
'get_options',
|
|
27
|
+
'Client',
|
|
28
|
+
'RedisClientTransport',
|
|
29
|
+
'ClientCodec',
|
|
30
|
+
]
|
trpc_redis/_client.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making tRPC-Python-Database available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2026 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# tRPC-Python-Database is licensed under Apache-2.0.
|
|
6
|
+
"""
|
|
7
|
+
Redis client module for accessing Redis servers via the tRPC framework.
|
|
8
|
+
|
|
9
|
+
This module provides a Client class that wraps the tRPC client for Redis operations, supporting command, pipeline,
|
|
10
|
+
and pubsub modes. It also provides utilities for configuring Redis client options.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from trpc_redis import Client as RedisClient
|
|
14
|
+
client = RedisClient("redis", opts)
|
|
15
|
+
await client.execute_command(ctx, "set", ["key", "value"])
|
|
16
|
+
await client.create_pipeline(ctx)
|
|
17
|
+
await client.create_pubsub(ctx, "channel")
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import threading
|
|
21
|
+
from typing import Callable
|
|
22
|
+
from typing import Dict
|
|
23
|
+
from typing import List
|
|
24
|
+
from typing import Optional
|
|
25
|
+
|
|
26
|
+
from redis.asyncio.client import Pipeline
|
|
27
|
+
from redis.asyncio.client import PubSub
|
|
28
|
+
from redis.typing import ResponseT as RedisResponse
|
|
29
|
+
from redis.typing import KeyT as RedisKey
|
|
30
|
+
from redis.typing import ChannelT as RedisChannelT
|
|
31
|
+
|
|
32
|
+
from trpc_sdk.abc import TrpcContext
|
|
33
|
+
from trpc_sdk.client import Client as TrpcClient
|
|
34
|
+
from trpc_sdk.client import Options
|
|
35
|
+
from trpc_sdk.codec import clone_client_message
|
|
36
|
+
from trpc_sdk.config import TrpcConfig
|
|
37
|
+
from trpc_sdk.serialization import SerializationType
|
|
38
|
+
from ._constants import PASSWORD
|
|
39
|
+
from ._constants import REDIS_NAME
|
|
40
|
+
from ._constants import RequestMode
|
|
41
|
+
from ._types import Request
|
|
42
|
+
from ._types import Response
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def create_rsp_object():
|
|
46
|
+
"""
|
|
47
|
+
Construct a new response object. This returns None because the transport will override it.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
None
|
|
51
|
+
"""
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def redis_config_option(redis_config: Dict) -> Callable:
|
|
56
|
+
"""
|
|
57
|
+
Option setter for specifying Redis backend configuration.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
redis_config (Dict): Redis configuration dictionary.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Callable: Option setter function.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def func(opts: Options):
|
|
67
|
+
setattr(opts.call_options, REDIS_NAME, redis_config)
|
|
68
|
+
|
|
69
|
+
return func
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_options(opts: List) -> List:
|
|
73
|
+
"""
|
|
74
|
+
Get options for the Redis client, including required Redis fields.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
opts (List): Additional options to include.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
List: List of options for the client.
|
|
81
|
+
"""
|
|
82
|
+
needed = (Options()
|
|
83
|
+
.set_protocol(REDIS_NAME)
|
|
84
|
+
.set_serialization_type(SerializationType.Noop)
|
|
85
|
+
.set_disable_service_router(True))
|
|
86
|
+
options = [needed]
|
|
87
|
+
if opts:
|
|
88
|
+
options.extend(opts)
|
|
89
|
+
return options
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Client:
|
|
93
|
+
"""
|
|
94
|
+
Redis client wrapper for the tRPC framework.
|
|
95
|
+
|
|
96
|
+
This class wraps the tRPC client to provide Redis command, pipeline, and pubsub operations.
|
|
97
|
+
|
|
98
|
+
Attributes:
|
|
99
|
+
client (TrpcClient): The underlying tRPC client object.
|
|
100
|
+
__service_name (str): The service name.
|
|
101
|
+
__opts (List): List of client options.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self, name: str, opts: Optional[List] = None):
|
|
105
|
+
"""
|
|
106
|
+
Initialize the Redis client.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
name (str): Service name.
|
|
110
|
+
opts (Optional[List]): List of options.
|
|
111
|
+
"""
|
|
112
|
+
self.client = TrpcClient()
|
|
113
|
+
self.__service_name = name
|
|
114
|
+
self.__opts = get_options(opts or [])
|
|
115
|
+
self._is_load_config = False
|
|
116
|
+
self._config_lock = threading.Lock()
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def service_name(self):
|
|
120
|
+
"""
|
|
121
|
+
Get the service name.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
str: The service name.
|
|
125
|
+
"""
|
|
126
|
+
return self.__service_name
|
|
127
|
+
|
|
128
|
+
def __getattr__(self, item):
|
|
129
|
+
"""
|
|
130
|
+
Override attribute access to provide dynamic command methods.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
item (str): The attribute name (command name).
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Callable: An async function that calls execute_command(ctx, item, args).
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
async def func(ctx, *args, **kwargs):
|
|
140
|
+
return await self.execute_command(ctx, item, *args, **kwargs)
|
|
141
|
+
|
|
142
|
+
return func
|
|
143
|
+
|
|
144
|
+
def _get_config(self):
|
|
145
|
+
"""
|
|
146
|
+
Load configuration from global config if available.
|
|
147
|
+
|
|
148
|
+
Safe to call from several handler threads at once: the options are
|
|
149
|
+
appended once, before the loaded flag is set.
|
|
150
|
+
"""
|
|
151
|
+
if self._is_load_config:
|
|
152
|
+
return
|
|
153
|
+
with self._config_lock:
|
|
154
|
+
if self._is_load_config:
|
|
155
|
+
return
|
|
156
|
+
self.__opts.extend(self._config_options())
|
|
157
|
+
self._is_load_config = True
|
|
158
|
+
|
|
159
|
+
def _config_options(self) -> List:
|
|
160
|
+
"""
|
|
161
|
+
Build the options that the global config adds for this service.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
List: Password and Redis backend options; empty when not configured.
|
|
165
|
+
"""
|
|
166
|
+
parser = TrpcConfig.config_parser()
|
|
167
|
+
if parser is None:
|
|
168
|
+
return []
|
|
169
|
+
client_redis_config_dict = {}
|
|
170
|
+
client_conf_dict = parser.get("client", {}) or {}
|
|
171
|
+
services = client_conf_dict.get('service', []) or []
|
|
172
|
+
for service in services:
|
|
173
|
+
if service.get('name') == self.__service_name:
|
|
174
|
+
client_redis_config_dict = service
|
|
175
|
+
break
|
|
176
|
+
if not client_redis_config_dict:
|
|
177
|
+
return []
|
|
178
|
+
options = []
|
|
179
|
+
password = client_redis_config_dict.get(PASSWORD, None)
|
|
180
|
+
if password:
|
|
181
|
+
options.append(Options().set_password(password))
|
|
182
|
+
redis_config = client_redis_config_dict.get(REDIS_NAME, {})
|
|
183
|
+
if redis_config:
|
|
184
|
+
options.append(redis_config_option(redis_config))
|
|
185
|
+
return options
|
|
186
|
+
|
|
187
|
+
async def execute_command(self, ctx: TrpcContext, cmd: str, *args, **kwargs) -> bytes:
|
|
188
|
+
"""
|
|
189
|
+
Execute a Redis command via tRPC.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
ctx (TrpcContext): The client context.
|
|
193
|
+
cmd (str): The Redis command.
|
|
194
|
+
args (Optional[List]): Arguments for the command.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
bytes: The reply as bytes.
|
|
198
|
+
"""
|
|
199
|
+
self._get_config()
|
|
200
|
+
req = Request(command=cmd, mode=RequestMode.COMMAND, args=args, kwargs=kwargs)
|
|
201
|
+
ctx, msg = clone_client_message(ctx)
|
|
202
|
+
msg.set_compress_type(0)
|
|
203
|
+
msg.set_serialization_type(-1)
|
|
204
|
+
msg.set_client_rpc_name(f"/{self.__service_name}/{cmd}")
|
|
205
|
+
msg.set_callee_service_name(self.__service_name)
|
|
206
|
+
msg.set_client_req_head(req)
|
|
207
|
+
|
|
208
|
+
await self.client.invoke(ctx, req, create_rsp_object, self.__opts)
|
|
209
|
+
return msg.client_rsp_head.reply
|
|
210
|
+
|
|
211
|
+
async def delete(self, ctx: TrpcContext, *names: RedisKey) -> RedisResponse:
|
|
212
|
+
"""
|
|
213
|
+
Delete a key from Redis.
|
|
214
|
+
"""
|
|
215
|
+
return await self.execute_command(ctx, "DEL", *names)
|
|
216
|
+
|
|
217
|
+
async def create_pipeline(self, ctx: TrpcContext) -> Pipeline:
|
|
218
|
+
"""
|
|
219
|
+
Create a Redis pipeline via tRPC.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
ctx (TrpcContext): The client context.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Pipeline: The pipeline object.
|
|
226
|
+
"""
|
|
227
|
+
self._get_config()
|
|
228
|
+
req = Request(mode=RequestMode.PIPELINE)
|
|
229
|
+
resp = Response(None)
|
|
230
|
+
ctx, msg = clone_client_message(ctx)
|
|
231
|
+
msg.set_compress_type(0)
|
|
232
|
+
msg.set_serialization_type(-1)
|
|
233
|
+
msg.set_client_rpc_name(f"/{self.__service_name}/pipeline")
|
|
234
|
+
msg.set_callee_service_name(self.__service_name)
|
|
235
|
+
msg.set_client_req_head(req)
|
|
236
|
+
msg.set_client_rsp_head(resp)
|
|
237
|
+
|
|
238
|
+
await self.client.invoke(ctx, req, create_rsp_object, self.__opts)
|
|
239
|
+
return msg.client_rsp_head.reply
|
|
240
|
+
|
|
241
|
+
async def create_pubsub(self, ctx: TrpcContext, *args: RedisChannelT, **kwargs: Callable) -> PubSub:
|
|
242
|
+
"""
|
|
243
|
+
Create a Redis pubsub object via tRPC and subscribe to the given channels.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
ctx (TrpcContext): The client context.
|
|
247
|
+
*args (RedisChannelT): Channels to subscribe to.
|
|
248
|
+
**kwargs (Callable): Channels mapped to their message handlers.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
PubSub: The pubsub object.
|
|
252
|
+
"""
|
|
253
|
+
self._get_config()
|
|
254
|
+
req = Request(mode=RequestMode.PUBSUB, args=args, kwargs=kwargs)
|
|
255
|
+
resp = Response(None)
|
|
256
|
+
ctx, msg = clone_client_message(ctx)
|
|
257
|
+
msg.set_compress_type(0)
|
|
258
|
+
msg.set_serialization_type(-1)
|
|
259
|
+
msg.set_client_rpc_name(f"/{self.__service_name}/pubsub")
|
|
260
|
+
msg.set_callee_service_name(self.__service_name)
|
|
261
|
+
msg.set_client_req_head(req)
|
|
262
|
+
msg.set_client_rsp_head(resp)
|
|
263
|
+
|
|
264
|
+
await self.client.invoke(ctx, req, create_rsp_object, self.__opts)
|
|
265
|
+
return msg.client_rsp_head.reply
|
trpc_redis/_codec.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making tRPC-Python-Database available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2026 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# tRPC-Python-Database is licensed under Apache-2.0.
|
|
6
|
+
"""
|
|
7
|
+
Redis protocol codec module for tRPC framework.
|
|
8
|
+
|
|
9
|
+
This module provides a codec for encoding and decoding messages using the Redis protocol.
|
|
10
|
+
It registers a custom codec plugin for Redis, which is used by the tRPC client to handle Redis-specific message
|
|
11
|
+
serialization and deserialization.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
The codec is registered automatically as a plugin and is used internally by the tRPC Redis client.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from trpc_sdk.abc import BaseCodec
|
|
21
|
+
from trpc_sdk.abc import PluginInitPos
|
|
22
|
+
from trpc_sdk.abc import PluginType
|
|
23
|
+
from trpc_sdk.abc import TrpcContext
|
|
24
|
+
from trpc_sdk.codec import get_message
|
|
25
|
+
from trpc_sdk.plugin import register_plugin
|
|
26
|
+
|
|
27
|
+
from ._constants import REDIS_NAME
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@register_plugin(PluginType.CLIENT_CODEC, REDIS_NAME, init_pos=PluginInitPos.WORKER)
|
|
31
|
+
class ClientCodec(BaseCodec):
|
|
32
|
+
"""Unary client codec for the Redis protocol.
|
|
33
|
+
|
|
34
|
+
Redis I/O is done entirely by the Redis transport through ``redis-py``,
|
|
35
|
+
so this codec produces no wire bytes. It only fills in client metadata on
|
|
36
|
+
encode and turns the transport reply into bytes on decode.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def encode_data(self, context: TrpcContext, body: Any) -> bytes:
|
|
40
|
+
"""Encode a request frame; the entry the client invoke path calls.
|
|
41
|
+
|
|
42
|
+
Sets a default caller service name when the message has none.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
context: RPC context whose client message is encoded.
|
|
46
|
+
body: Request body. Unused, the request lives in
|
|
47
|
+
``message.client_req_head``.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Empty bytes; the transport sends the actual Redis command.
|
|
51
|
+
"""
|
|
52
|
+
message = get_message(context)
|
|
53
|
+
if message.caller_service_name == "":
|
|
54
|
+
message.set_caller_service_name(f"trpc.redis.{os.path.basename(os.getcwd())}.service")
|
|
55
|
+
return b""
|
|
56
|
+
|
|
57
|
+
def decode_data(self, context: TrpcContext, buffer: Any) -> bytes:
|
|
58
|
+
"""Decode the transport reply into bytes; the entry the client invoke path calls.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
context: RPC context of the call. Unused.
|
|
62
|
+
buffer: Reply returned by the Redis transport; may be ``None``.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
``b""`` for ``None``, otherwise ``str(buffer)`` encoded as UTF-8.
|
|
66
|
+
"""
|
|
67
|
+
if buffer is None:
|
|
68
|
+
return b""
|
|
69
|
+
return bytes(str(buffer), encoding='utf8')
|
trpc_redis/_constants.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making tRPC-Python-Database available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2026 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# tRPC-Python-Database is licensed under Apache-2.0.
|
|
6
|
+
"""
|
|
7
|
+
Redis constants module for tRPC framework.
|
|
8
|
+
|
|
9
|
+
This module defines constants used in the Redis client implementation.
|
|
10
|
+
It includes configuration keys and default values for Redis connections.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
The constants are used to configure Redis client options and connection parameters.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from enum import Enum
|
|
17
|
+
|
|
18
|
+
# Plugin name of the codec and transport, the client protocol name, and the
|
|
19
|
+
# key of the extra ConnectionPool kwargs in YAML and ``call_options``.
|
|
20
|
+
REDIS_NAME: str = "redis"
|
|
21
|
+
|
|
22
|
+
# ``redis.asyncio.ConnectionPool`` kwargs the transport fills with a default
|
|
23
|
+
# when the ``redis:`` config block does not set them.
|
|
24
|
+
MAX_CONNECTIONS: str = "max_connections"
|
|
25
|
+
SOCKET_CONNECT_TIMEOUT: str = "socket_connect_timeout"
|
|
26
|
+
DECODE_RESPONSES: str = "decode_responses"
|
|
27
|
+
|
|
28
|
+
# ConnectionPool kwargs always overwritten from the service target and password.
|
|
29
|
+
# ``PASSWORD`` is also the password key of a client service config entry.
|
|
30
|
+
HOST: str = "host"
|
|
31
|
+
PORT: str = "port"
|
|
32
|
+
PASSWORD: str = "password"
|
|
33
|
+
|
|
34
|
+
# Maximum number of connections in one pool.
|
|
35
|
+
MAX_CONNECTIONS_DEFAULT: int = 1024
|
|
36
|
+
# Seconds. Used as the default ``socket_connect_timeout``, not as an idle timeout.
|
|
37
|
+
IDLE_TIMEOUT_DEFAULT: float = 3.0 * 60
|
|
38
|
+
# Replies stay bytes unless the config enables decoding.
|
|
39
|
+
DECODE_RESPONSES_DEFAULT: bool = False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RequestMode(Enum):
|
|
43
|
+
"""Dispatch key of a Redis request.
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
COMMAND: Run one Redis command and return its reply.
|
|
47
|
+
PUBSUB: Subscribe and return a ``PubSub`` object.
|
|
48
|
+
PIPELINE: Return a ``Pipeline`` object for batched commands.
|
|
49
|
+
"""
|
|
50
|
+
COMMAND = "command"
|
|
51
|
+
PUBSUB = "pubsub"
|
|
52
|
+
PIPELINE = "pipeline"
|