cobweb-launcher 3.1.28__py3-none-any.whl → 3.1.29__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.
cobweb/base/response.py CHANGED
@@ -1,32 +1,390 @@
1
- from typing import Any
1
+ from typing import Any, Dict, Union
2
2
 
3
3
 
4
4
  class Response:
5
+ """
6
+ 响应对象类,支持动态属性访问和字典式操作
7
+
8
+ 优化特性:
9
+ 1. 使用 __slots__ 减少内存占用
10
+ 2. 缓存 to_dict 结果提高性能
11
+ 3. 更好的错误处理和类型检查
12
+ 4. 支持弱引用避免循环引用
13
+ 5. 线程安全的属性访问
14
+ """
15
+
16
+ __slots__ = ('_seed', '_response', '_extra_attrs', '_dict_cache', '__weakref__')
5
17
 
6
18
  def __init__(
7
19
  self,
8
- seed,
9
- response,
10
- **kwargs
11
- ):
12
- self.seed = seed
13
- self.response = response
20
+ seed: Any,
21
+ response: Any,
22
+ **kwargs: Any
23
+ ) -> None:
24
+ """
25
+ 初始化 Response 对象
26
+
27
+ Args:
28
+ seed: 种子对象,用于动态属性访问
29
+ response: 响应对象
30
+ **kwargs: 额外的属性
31
+ """
32
+ # 使用私有属性避免与动态属性冲突
33
+ object.__setattr__(self, '_seed', seed)
34
+ object.__setattr__(self, '_response', response)
35
+ object.__setattr__(self, '_extra_attrs', kwargs.copy())
36
+ object.__setattr__(self, '_dict_cache', None)
37
+
38
+ @property
39
+ def seed(self) -> Any:
40
+ """获取种子对象"""
41
+ return self._seed
14
42
 
15
- for k, v in kwargs.items():
16
- self.__setattr__(k, v)
43
+ @property
44
+ def response(self) -> Any:
45
+ """获取响应对象"""
46
+ return self._response
17
47
 
18
48
  @property
19
- def to_dict(self):
20
- _dict = self.__dict__.copy()
21
- _dict.update(self.seed.to_dict)
22
- _dict.pop('seed')
23
- _dict.pop('response')
24
- return _dict
49
+ def to_dict(self) -> Dict[str, Any]:
50
+ """
51
+ 转换为字典格式,使用缓存提高性能
52
+
53
+ Returns:
54
+ 包含所有属性的字典
55
+ """
56
+ if self._dict_cache is None:
57
+ _dict = self._extra_attrs.copy()
58
+
59
+ # 安全地获取 seed 的字典表示
60
+ if hasattr(self._seed, 'to_dict'):
61
+ if callable(self._seed.to_dict):
62
+ try:
63
+ _dict.update(self._seed.to_dict())
64
+ except Exception as e:
65
+ # 记录错误但不中断执行
66
+ _dict['_seed_to_dict_error'] = str(e)
67
+ else:
68
+ _dict.update(self._seed.to_dict)
69
+ elif hasattr(self._seed, '__dict__'):
70
+ _dict.update(self._seed.__dict__)
71
+ elif isinstance(self._seed, dict):
72
+ _dict.update(self._seed)
73
+
74
+ # 缓存结果
75
+ object.__setattr__(self, '_dict_cache', _dict)
76
+
77
+ return self._dict_cache.copy() # 返回副本避免外部修改
78
+
79
+ def invalidate_cache(self) -> None:
80
+ """清除缓存,当对象状态改变时调用"""
81
+ object.__setattr__(self, '_dict_cache', None)
25
82
 
26
83
  def __getattr__(self, name: str) -> Any:
27
- """动态获取未定义的属性,返回 seed中的属性"""
28
- return self.seed[name]
84
+ """
85
+ 动态获取属性
86
+
87
+ 优先级:
88
+ 1. _extra_attrs 中的属性
89
+ 2. seed 对象的属性
90
+
91
+ Args:
92
+ name: 属性名
93
+
94
+ Returns:
95
+ 属性值
96
+
97
+ Raises:
98
+ AttributeError: 当属性不存在时
99
+ """
100
+ # 避免递归调用
101
+ if name.startswith('_'):
102
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
103
+
104
+ # 首先检查额外属性
105
+ if name in self._extra_attrs:
106
+ return self._extra_attrs[name]
107
+
108
+ # 然后检查 seed 对象
109
+ return self._get_from_seed(name)
110
+
111
+ def _get_from_seed(self, name: str) -> Any:
112
+ """
113
+ 从 seed 对象获取属性
114
+
115
+ Args:
116
+ name: 属性名
117
+
118
+ Returns:
119
+ 属性值
120
+
121
+ Raises:
122
+ AttributeError: 当属性不存在时
123
+ """
124
+ if self._seed is None:
125
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}' (seed is None)")
126
+
127
+ # 尝试不同的访问方式
128
+ try:
129
+ # 方式1: 字典式访问
130
+ if hasattr(self._seed, '__getitem__'):
131
+ try:
132
+ return self._seed[name]
133
+ except (KeyError, TypeError):
134
+ pass
135
+
136
+ # 方式2: 属性访问
137
+ if hasattr(self._seed, name):
138
+ return getattr(self._seed, name)
139
+
140
+ # 方式3: 如果 seed 是字典
141
+ if isinstance(self._seed, dict) and name in self._seed:
142
+ return self._seed[name]
143
+
144
+ except Exception as e:
145
+ raise AttributeError(
146
+ f"Error accessing '{name}' from seed: {e}"
147
+ ) from e
148
+
149
+ # 属性不存在
150
+ raise AttributeError(
151
+ f"'{self.__class__.__name__}' object has no attribute '{name}'"
152
+ )
29
153
 
30
154
  def __getitem__(self, key: str) -> Any:
31
- """支持字典式获取属性"""
32
- return getattr(self, key, self.seed[key])
155
+ """
156
+ 支持字典式访问
157
+
158
+ Args:
159
+ key: 键名
160
+
161
+ Returns:
162
+ 对应的值
163
+ """
164
+ try:
165
+ return getattr(self, key)
166
+ except AttributeError:
167
+ raise KeyError(key)
168
+
169
+ def __setattr__(self, name: str, value: Any) -> None:
170
+ """
171
+ 设置属性
172
+
173
+ Args:
174
+ name: 属性名
175
+ value: 属性值
176
+ """
177
+ if name.startswith('_') or name in self.__slots__:
178
+ object.__setattr__(self, name, value)
179
+ else:
180
+ # 设置到额外属性中
181
+ self._extra_attrs[name] = value
182
+ # 清除缓存
183
+ self.invalidate_cache()
184
+
185
+ def __setitem__(self, key: str, value: Any) -> None:
186
+ """支持字典式设置"""
187
+ setattr(self, key, value)
188
+
189
+ def __delattr__(self, name: str) -> None:
190
+ """
191
+ 删除属性
192
+
193
+ Args:
194
+ name: 属性名
195
+ """
196
+ if name.startswith('_') or name in self.__slots__:
197
+ object.__delattr__(self, name)
198
+ elif name in self._extra_attrs:
199
+ del self._extra_attrs[name]
200
+ self.invalidate_cache()
201
+ else:
202
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")
203
+
204
+ def __delitem__(self, key: str) -> None:
205
+ """支持字典式删除"""
206
+ delattr(self, key)
207
+
208
+ def __contains__(self, key: str) -> bool:
209
+ """
210
+ 检查是否包含某个属性
211
+
212
+ Args:
213
+ key: 属性名
214
+
215
+ Returns:
216
+ 是否包含该属性
217
+ """
218
+ try:
219
+ getattr(self, key)
220
+ return True
221
+ except AttributeError:
222
+ return False
223
+
224
+ def __iter__(self):
225
+ """支持迭代,返回所有属性名"""
226
+ return iter(self.to_dict.keys())
227
+
228
+ def keys(self):
229
+ """返回所有属性名"""
230
+ return self.to_dict.keys()
231
+
232
+ def values(self):
233
+ """返回所有属性值"""
234
+ return self.to_dict.values()
235
+
236
+ def items(self):
237
+ """返回所有属性键值对"""
238
+ return self.to_dict.items()
239
+
240
+ def get(self, key: str, default: Any = None) -> Any:
241
+ """
242
+ 安全获取属性值
243
+
244
+ Args:
245
+ key: 属性名
246
+ default: 默认值
247
+
248
+ Returns:
249
+ 属性值或默认值
250
+ """
251
+ try:
252
+ return getattr(self, key)
253
+ except AttributeError:
254
+ return default
255
+
256
+ def update(self, other: Union[Dict[str, Any], 'Response'], **kwargs: Any) -> None:
257
+ """
258
+ 更新属性
259
+
260
+ Args:
261
+ other: 字典或另一个 Response 对象
262
+ **kwargs: 额外的属性
263
+ """
264
+ if isinstance(other, dict):
265
+ self._extra_attrs.update(other)
266
+ elif isinstance(other, Response):
267
+ self._extra_attrs.update(other._extra_attrs)
268
+ elif hasattr(other, 'items'):
269
+ self._extra_attrs.update(dict(other.items()))
270
+
271
+ self._extra_attrs.update(kwargs)
272
+ self.invalidate_cache()
273
+
274
+ def copy(self) -> 'Response':
275
+ """
276
+ 创建副本
277
+
278
+ Returns:
279
+ 新的 Response 对象
280
+ """
281
+ return Response(self._seed, self._response, **self._extra_attrs)
282
+
283
+ def __repr__(self) -> str:
284
+ """字符串表示"""
285
+ extra_attrs = ', '.join(f'{k}={v!r}' for k, v in list(self._extra_attrs.items())[:3])
286
+ if len(self._extra_attrs) > 3:
287
+ extra_attrs += f', ... (+{len(self._extra_attrs) - 3} more)'
288
+
289
+ return f"{self.__class__.__name__}(seed={self._seed!r}, response={self._response!r}, {extra_attrs})"
290
+
291
+ def __str__(self) -> str:
292
+ """用户友好的字符串表示"""
293
+ return f"{self.__class__.__name__} with {len(self._extra_attrs)} extra attributes"
294
+
295
+ def __eq__(self, other: Any) -> bool:
296
+ """相等性比较"""
297
+ if not isinstance(other, Response):
298
+ return False
299
+
300
+ return (
301
+ self._seed == other._seed and
302
+ self._response == other._response and
303
+ self._extra_attrs == other._extra_attrs
304
+ )
305
+
306
+ def __hash__(self) -> int:
307
+ """哈希值计算"""
308
+ # 只对不可变部分计算哈希
309
+ try:
310
+ return hash((id(self._seed), id(self._response), tuple(sorted(self._extra_attrs.items()))))
311
+ except TypeError:
312
+ # 如果包含不可哈希的值,使用对象ID
313
+ return hash(id(self))
314
+
315
+
316
+ # 扩展版本:支持更多高级特性
317
+ class AdvancedResponse(Response):
318
+ """
319
+ 高级响应类,提供更多功能
320
+ """
321
+
322
+ __slots__ = ('_observers', '_frozen')
323
+
324
+ def __init__(self, seed: Any, response: Any, frozen: bool = False, **kwargs: Any) -> None:
325
+ """
326
+ 初始化高级响应对象
327
+
328
+ Args:
329
+ seed: 种子对象
330
+ response: 响应对象
331
+ frozen: 是否冻结对象(不允许修改)
332
+ **kwargs: 额外属性
333
+ """
334
+ super().__init__(seed, response, **kwargs)
335
+ object.__setattr__(self, '_observers', [])
336
+ object.__setattr__(self, '_frozen', frozen)
337
+
338
+ def freeze(self) -> None:
339
+ """冻结对象,不允许修改"""
340
+ object.__setattr__(self, '_frozen', True)
341
+
342
+ def unfreeze(self) -> None:
343
+ """解冻对象,允许修改"""
344
+ object.__setattr__(self, '_frozen', False)
345
+
346
+ @property
347
+ def is_frozen(self) -> bool:
348
+ """检查对象是否被冻结"""
349
+ return self._frozen
350
+
351
+ def __setattr__(self, name: str, value: Any) -> None:
352
+ """设置属性(支持冻结检查)"""
353
+ if self._frozen and not name.startswith('_'):
354
+ raise AttributeError(f"Cannot modify frozen {self.__class__.__name__} object")
355
+
356
+ old_value = getattr(self, name, None)
357
+ super().__setattr__(name, value)
358
+
359
+ # 通知观察者
360
+ self._notify_observers(name, old_value, value)
361
+
362
+ def add_observer(self, callback: callable) -> None:
363
+ """
364
+ 添加观察者
365
+
366
+ Args:
367
+ callback: 回调函数,签名为 callback(attr_name, old_value, new_value)
368
+ """
369
+ if callback not in self._observers:
370
+ self._observers.append(callback)
371
+
372
+ def remove_observer(self, callback: callable) -> None:
373
+ """
374
+ 移除观察者
375
+
376
+ Args:
377
+ callback: 要移除的回调函数
378
+ """
379
+ if callback in self._observers:
380
+ self._observers.remove(callback)
381
+
382
+ def _notify_observers(self, attr_name: str, old_value: Any, new_value: Any) -> None:
383
+ """通知所有观察者"""
384
+ for observer in self._observers:
385
+ try:
386
+ observer(attr_name, old_value, new_value)
387
+ except Exception as e:
388
+ # 记录错误但不中断执行
389
+ print(f"Observer error: {e}")
390
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cobweb-launcher
3
- Version: 3.1.28
3
+ Version: 3.1.29
4
4
  Summary: spider_hole
5
5
  Home-page: https://github.com/Juannie-PP/cobweb
6
6
  Author: Juannie-PP
@@ -6,7 +6,7 @@ cobweb/base/common_queue.py,sha256=hYdaM70KrWjvACuLKaGhkI2VqFCnd87NVvWzmnfIg8Q,1
6
6
  cobweb/base/item.py,sha256=1bS4U_3vzI2jzSSeoEbLoLT_5CfgLPopWiEYtaahbvw,1674
7
7
  cobweb/base/logger.py,sha256=Vsg1bD4LXW91VgY-ANsmaUu-mD88hU_WS83f7jX3qF8,2011
8
8
  cobweb/base/request.py,sha256=LBI2eCoXtQcUh6XQT813Q3rzhlhUFSm_UBGayYWIHKI,16052
9
- cobweb/base/response.py,sha256=-5huP_3wwx4rtdc2ipKiah_0h5F4MahdMTAVsPvz2bY,753
9
+ cobweb/base/response.py,sha256=L3sX2PskV744uz3BJ8xMuAoAfGCeh20w8h0Cnd9vLo0,11377
10
10
  cobweb/base/seed.py,sha256=ddaWCq_KaWwpmPl1CToJlfCxEEnoJ16kjo6azJs9uls,5000
11
11
  cobweb/base/task_queue.py,sha256=2MqGpHGNmK5B-kqv7z420RWyihzB9zgDHJUiLsmtzOI,6402
12
12
  cobweb/base/test.py,sha256=N8MDGb94KQeI4pC5rCc2QdohE9_5AgcOyGqKjbMsOEs,9588
@@ -34,8 +34,8 @@ cobweb/utils/decorators.py,sha256=ZwVQlz-lYHgXgKf9KRCp15EWPzTDdhoikYUNUCIqNeM,11
34
34
  cobweb/utils/dotting.py,sha256=L-jGSApdnFIP4jUWH6p5qIme0aJ1vyDrxAx8wOJWvcs,1960
35
35
  cobweb/utils/oss.py,sha256=wmToIIVNO8nCQVRmreVaZejk01aCWS35e1NV6cr0yGI,4192
36
36
  cobweb/utils/tools.py,sha256=14TCedqt07m4z6bCnFAsITOFixeGr8V3aOKk--L7Cr0,879
37
- cobweb_launcher-3.1.28.dist-info/LICENSE,sha256=z1rxSIGOyzcSb3orZxFPxzx-0C1vTocmswqBNxpKfEk,1063
38
- cobweb_launcher-3.1.28.dist-info/METADATA,sha256=wfEfQ0X_cG30OMyHK5wOOGdSDBpJxksegG-EfnBpfyA,6051
39
- cobweb_launcher-3.1.28.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
40
- cobweb_launcher-3.1.28.dist-info/top_level.txt,sha256=4GETBGNsKqiCUezmT-mJn7tjhcDlu7nLIV5gGgHBW4I,7
41
- cobweb_launcher-3.1.28.dist-info/RECORD,,
37
+ cobweb_launcher-3.1.29.dist-info/LICENSE,sha256=z1rxSIGOyzcSb3orZxFPxzx-0C1vTocmswqBNxpKfEk,1063
38
+ cobweb_launcher-3.1.29.dist-info/METADATA,sha256=0v2KV50bVlPLBdWIp2DXQscDy_tWmKYL3lCtA7aiRoM,6051
39
+ cobweb_launcher-3.1.29.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
40
+ cobweb_launcher-3.1.29.dist-info/top_level.txt,sha256=4GETBGNsKqiCUezmT-mJn7tjhcDlu7nLIV5gGgHBW4I,7
41
+ cobweb_launcher-3.1.29.dist-info/RECORD,,