isa-common 0.1.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.
Files changed (34) hide show
  1. isa_common/__init__.py +183 -0
  2. isa_common/base_client.py +145 -0
  3. isa_common/duckdb_client.py +399 -0
  4. isa_common/events/__init__.py +61 -0
  5. isa_common/events/base_event_models.py +107 -0
  6. isa_common/events/base_event_publisher.py +199 -0
  7. isa_common/events/base_event_subscriber.py +500 -0
  8. isa_common/events/billing_event_publisher.py +451 -0
  9. isa_common/events/billing_events.py +286 -0
  10. isa_common/minio_client.py +223 -0
  11. isa_common/mqtt_client.py +220 -0
  12. isa_common/nats_client.py +260 -0
  13. isa_common/proto/__init__.py +22 -0
  14. isa_common/proto/common_pb2.py +63 -0
  15. isa_common/proto/common_pb2_grpc.py +24 -0
  16. isa_common/proto/duckdb_service_pb2.py +208 -0
  17. isa_common/proto/duckdb_service_pb2_grpc.py +1310 -0
  18. isa_common/proto/loki_service_pb2.py +178 -0
  19. isa_common/proto/loki_service_pb2_grpc.py +930 -0
  20. isa_common/proto/minio_service_pb2.py +162 -0
  21. isa_common/proto/minio_service_pb2_grpc.py +959 -0
  22. isa_common/proto/mqtt_service_pb2.py +174 -0
  23. isa_common/proto/mqtt_service_pb2_grpc.py +1137 -0
  24. isa_common/proto/nats_service_pb2.py +220 -0
  25. isa_common/proto/nats_service_pb2_grpc.py +1436 -0
  26. isa_common/proto/redis_service_pb2.py +308 -0
  27. isa_common/proto/redis_service_pb2_grpc.py +2725 -0
  28. isa_common/proto/supabase_service_pb2.py +96 -0
  29. isa_common/proto/supabase_service_pb2_grpc.py +671 -0
  30. isa_common/supabase_client.py +428 -0
  31. isa_common-0.1.0.dist-info/METADATA +180 -0
  32. isa_common-0.1.0.dist-info/RECORD +34 -0
  33. isa_common-0.1.0.dist-info/WHEEL +5 -0
  34. isa_common-0.1.0.dist-info/top_level.txt +1 -0
isa_common/__init__.py ADDED
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ gRPC Clients Package
4
+ 统一的 gRPC 客户端接口
5
+
6
+ 使用示例:
7
+ from core.clients import get_client, SupabaseClient, MinIOClient
8
+
9
+ # 方式 1: 使用工厂函数
10
+ supabase = get_client('supabase', user_id='user_123')
11
+
12
+ # 方式 2: 直接实例化
13
+ minio = MinIOClient(host='localhost', port=50051, user_id='user_123')
14
+
15
+ # 方式 3: 使用 with 语句自动管理连接
16
+ with SupabaseClient() as client:
17
+ client.query('users', select='*')
18
+ """
19
+
20
+ from typing import Optional, Dict
21
+ from .base_client import BaseGRPCClient
22
+ from .supabase_client import SupabaseClient
23
+ from .minio_client import MinIOClient
24
+ from .duckdb_client import DuckDBClient
25
+ from .mqtt_client import MQTTClient
26
+ from .nats_client import NATSClient
27
+
28
+ # Import events module (but don't unpack, keep as submodule)
29
+ from . import events
30
+
31
+ # 导出所有客户端
32
+ __all__ = [
33
+ 'BaseGRPCClient',
34
+ 'SupabaseClient',
35
+ 'MinIOClient',
36
+ 'DuckDBClient',
37
+ 'MQTTClient',
38
+ 'NATSClient',
39
+ 'get_client',
40
+ 'ClientFactory',
41
+ 'events', # Export events submodule
42
+ ]
43
+
44
+ # 默认配置
45
+ DEFAULT_PORTS: Dict[str, int] = {
46
+ 'minio': 50051,
47
+ 'duckdb': 50052,
48
+ 'mqtt': 50053,
49
+ 'loki': 50054,
50
+ 'redis': 50055,
51
+ 'nats': 50056,
52
+ 'supabase': 50057,
53
+ }
54
+
55
+ DEFAULT_HOST = 'localhost'
56
+
57
+
58
+ class ClientFactory:
59
+ """gRPC 客户端工厂"""
60
+
61
+ # 客户端映射
62
+ _clients = {
63
+ 'supabase': SupabaseClient,
64
+ 'minio': MinIOClient,
65
+ 'duckdb': DuckDBClient,
66
+ 'mqtt': MQTTClient,
67
+ 'nats': NATSClient,
68
+ # TODO: 添加其他客户端
69
+ # 'redis': RedisClient,
70
+ # 'loki': LokiClient,
71
+ }
72
+
73
+ @classmethod
74
+ def create(cls, service_name: str, host: Optional[str] = None,
75
+ port: Optional[int] = None, user_id: Optional[str] = None) -> BaseGRPCClient:
76
+ """
77
+ 创建 gRPC 客户端
78
+
79
+ Args:
80
+ service_name: 服务名称 (supabase, minio, duckdb, etc.)
81
+ host: 服务地址 (默认: localhost)
82
+ port: 服务端口 (默认: 根据服务自动选择)
83
+ user_id: 用户 ID
84
+
85
+ Returns:
86
+ 客户端实例
87
+
88
+ Raises:
89
+ ValueError: 如果服务名称不支持
90
+
91
+ 示例:
92
+ client = ClientFactory.create('supabase', user_id='user_123')
93
+ client = ClientFactory.create('minio', host='192.168.1.100', port=50051)
94
+ """
95
+ service_name = service_name.lower()
96
+
97
+ if service_name not in cls._clients:
98
+ available = ', '.join(cls._clients.keys())
99
+ raise ValueError(f"不支持的服务: {service_name}. 可用服务: {available}")
100
+
101
+ # 使用默认值
102
+ if host is None:
103
+ host = DEFAULT_HOST
104
+ if port is None:
105
+ port = DEFAULT_PORTS.get(service_name, 50051)
106
+
107
+ client_class = cls._clients[service_name]
108
+ return client_class(host=host, port=port, user_id=user_id)
109
+
110
+ @classmethod
111
+ def register_client(cls, service_name: str, client_class):
112
+ """
113
+ 注册新的客户端类
114
+
115
+ Args:
116
+ service_name: 服务名称
117
+ client_class: 客户端类 (必须继承 BaseGRPCClient)
118
+ """
119
+ if not issubclass(client_class, BaseGRPCClient):
120
+ raise TypeError(f"{client_class} 必须继承 BaseGRPCClient")
121
+
122
+ cls._clients[service_name.lower()] = client_class
123
+ print(f"✅ 注册客户端: {service_name} -> {client_class.__name__}")
124
+
125
+ @classmethod
126
+ def list_services(cls) -> list:
127
+ """列出所有可用的服务"""
128
+ return list(cls._clients.keys())
129
+
130
+
131
+ # 便捷函数
132
+ def get_client(service_name: str, host: Optional[str] = None,
133
+ port: Optional[int] = None, user_id: Optional[str] = None) -> BaseGRPCClient:
134
+ """
135
+ 获取 gRPC 客户端 (便捷函数)
136
+
137
+ Args:
138
+ service_name: 服务名称
139
+ host: 服务地址
140
+ port: 服务端口
141
+ user_id: 用户 ID
142
+
143
+ Returns:
144
+ 客户端实例
145
+
146
+ 示例:
147
+ from core.clients import get_client
148
+
149
+ supabase = get_client('supabase', user_id='user_123')
150
+ minio = get_client('minio', host='192.168.1.100')
151
+ """
152
+ return ClientFactory.create(service_name, host, port, user_id)
153
+
154
+
155
+ # 显示所有可用服务
156
+ def show_available_services():
157
+ """显示所有可用的 gRPC 服务"""
158
+ print("📦 可用的 gRPC 服务:")
159
+ print()
160
+ for service in sorted(ClientFactory.list_services()):
161
+ port = DEFAULT_PORTS.get(service, 'N/A')
162
+ client_class = ClientFactory._clients[service]
163
+ print(f" • {service:12} (端口: {port}) -> {client_class.__name__}")
164
+ print()
165
+
166
+
167
+ if __name__ == '__main__':
168
+ # 显示可用服务
169
+ show_available_services()
170
+
171
+ # 测试客户端创建
172
+ print("测试客户端创建:")
173
+ print("-" * 60)
174
+
175
+ # 使用工厂创建客户端
176
+ with get_client('supabase', user_id='test_user') as supabase:
177
+ supabase.health_check()
178
+
179
+ print()
180
+
181
+ with get_client('minio', user_id='test_user') as minio:
182
+ minio.health_check()
183
+
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Base gRPC Client
4
+ 所有 gRPC 客户端的基类,提供统一的连接管理和错误处理
5
+ """
6
+
7
+ import grpc
8
+ import logging
9
+ import threading
10
+ from typing import Optional
11
+ from abc import ABC, abstractmethod
12
+ from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
13
+
14
+ # Configure logging
15
+ logging.basicConfig(
16
+ level=logging.INFO,
17
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
18
+ )
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class BaseGRPCClient(ABC):
23
+ """gRPC 客户端基类"""
24
+
25
+ def __init__(self, host: str = 'localhost', port: int = 50051, user_id: Optional[str] = None,
26
+ lazy_connect: bool = True, enable_compression: bool = True, enable_retry: bool = True):
27
+ """
28
+ 初始化 gRPC 客户端
29
+
30
+ Args:
31
+ host: 服务地址
32
+ port: 服务端口
33
+ user_id: 用户 ID (用于多租户隔离)
34
+ lazy_connect: 是否延迟连接 (默认: True, 更快的启动速度)
35
+ enable_compression: 是否启用 gRPC 压缩 (默认: True)
36
+ enable_retry: 是否启用重试逻辑 (默认: True)
37
+ """
38
+ self.host = host
39
+ self.port = port
40
+ self.user_id = user_id or 'default_user'
41
+ self.address = f'{host}:{port}'
42
+ self.enable_compression = enable_compression
43
+ self.enable_retry = enable_retry
44
+
45
+ # Lazy initialization
46
+ self.channel = None
47
+ self.stub = None
48
+ self._connect_lock = threading.Lock()
49
+ self._connected = False
50
+
51
+ # Connect immediately if not lazy
52
+ if not lazy_connect:
53
+ self._ensure_connected()
54
+
55
+ def _ensure_connected(self):
56
+ """确保已连接(线程安全的延迟连接)"""
57
+ if self._connected and self.channel is not None:
58
+ return
59
+
60
+ with self._connect_lock:
61
+ # Double-check after acquiring lock
62
+ if self._connected and self.channel is not None:
63
+ return
64
+
65
+ logger.info(f"[{self.service_name()}] Connecting to {self.address}...")
66
+
67
+ # Build channel options
68
+ options = [
69
+ ('grpc.max_receive_message_length', 100 * 1024 * 1024), # 100MB
70
+ ('grpc.max_send_message_length', 100 * 1024 * 1024), # 100MB
71
+ ('grpc.keepalive_time_ms', 10000),
72
+ ('grpc.keepalive_timeout_ms', 5000),
73
+ ]
74
+
75
+ # Add compression if enabled
76
+ if self.enable_compression:
77
+ options.append(('grpc.default_compression_algorithm', grpc.Compression.Gzip))
78
+ options.append(('grpc.default_compression_level', grpc.Compression.Gzip))
79
+
80
+ # Create channel
81
+ self.channel = grpc.insecure_channel(self.address, options=options)
82
+
83
+ # Create stub
84
+ self.stub = self._create_stub()
85
+
86
+ # Mark as connected
87
+ self._connected = True
88
+ logger.info(f"[{self.service_name()}] Connected successfully to {self.address}")
89
+
90
+ @abstractmethod
91
+ def _create_stub(self):
92
+ """子类实现:创建特定服务的 stub"""
93
+ pass
94
+
95
+ @abstractmethod
96
+ def service_name(self) -> str:
97
+ """子类实现:返回服务名称"""
98
+ pass
99
+
100
+ def _call_with_retry(self, func, *args, **kwargs):
101
+ """带重试的 RPC 调用"""
102
+ if not self.enable_retry:
103
+ return func(*args, **kwargs)
104
+
105
+ @retry(
106
+ stop=stop_after_attempt(3),
107
+ wait=wait_exponential(multiplier=1, min=1, max=10),
108
+ retry=retry_if_exception_type(grpc.RpcError),
109
+ reraise=True
110
+ )
111
+ def _retry_wrapper():
112
+ self._ensure_connected()
113
+ return func(*args, **kwargs)
114
+
115
+ try:
116
+ return _retry_wrapper()
117
+ except grpc.RpcError as e:
118
+ logger.error(f"[{self.service_name()}] RPC failed after retries: {e.code()} - {e.details()}")
119
+ raise
120
+
121
+ def handle_error(self, e: Exception, operation: str = "操作"):
122
+ """统一错误处理"""
123
+ logger.error(f"[{self.service_name()}] {operation} 失败:")
124
+ if isinstance(e, grpc.RpcError):
125
+ logger.error(f" 错误代码: {e.code()}")
126
+ logger.error(f" 错误详情: {e.details()}")
127
+ else:
128
+ logger.error(f" 错误: {e}")
129
+ return None
130
+
131
+ def close(self):
132
+ """关闭连接"""
133
+ if self.channel is not None:
134
+ self.channel.close()
135
+ self._connected = False
136
+ logger.info(f"[{self.service_name()}] Connection closed")
137
+
138
+ def __enter__(self):
139
+ """支持 with 语句"""
140
+ return self
141
+
142
+ def __exit__(self, exc_type, exc_val, exc_tb):
143
+ """退出时自动关闭连接"""
144
+ self.close()
145
+