reproto 0.1.2__py3-none-any.whl → 0.1.4__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 (87) hide show
  1. .git/COMMIT_EDITMSG +1 -30
  2. .git/index +0 -0
  3. .git/logs/HEAD +4 -0
  4. .git/logs/refs/heads/iyue +4 -0
  5. .git/logs/refs/remotes/gitlab/iyue +4 -0
  6. .git/logs/refs/remotes/origin/iyue +4 -0
  7. .git/objects/00/81a7e1ec3251cc192ed2b73d5be897593fb872 +0 -0
  8. .git/objects/00/885cebf557ff261574deb93cb96449d06db01c +0 -0
  9. .git/objects/02/33f5b60263e5dc6d041c0b223a0724eb650faa +0 -0
  10. .git/objects/07/557fbd1d149ce51af8e98e8ca8590757a89dfa +0 -0
  11. .git/objects/08/760c38a9f9a4b5d4de0a1df0d197b27c1592d6 +0 -0
  12. .git/objects/12/f9cc4ad0b4f0af4f7bae379f281b2cebe7cc7f +0 -0
  13. .git/objects/22/316ec4a32601c236f997552e9600b7b40e3162 +0 -0
  14. .git/objects/24/d78e796570a8572a03bc1dd26608a7cfb506f8 +2 -0
  15. .git/objects/2a/fe93d8bcbeab9e136d8b6766604c32b3610314 +0 -0
  16. .git/objects/2d/8a0da260a710010ae62be134ac1cea6ceecfd1 +0 -0
  17. .git/objects/2d/e8ecbb5ab5de1a032bef3f4606ce5fa7c6c4e8 +0 -0
  18. .git/objects/30/9347e5681d80bd3c7949882e27090dd9070d16 +0 -0
  19. .git/objects/33/633c9df669ff8cf38638717937a54990814268 +0 -0
  20. .git/objects/39/993e3600bf4ab82aa361b738ee97a108787450 +0 -0
  21. .git/objects/3b/ab663710fd6b43d9372313fced9043c4cb07dd +0 -0
  22. .git/objects/3e/b3f1273caf6814dfa69325ccbd9fd1340cf20a +0 -0
  23. .git/objects/3f/ac4973433ec0116d2348fe7a748a0002702efb +0 -0
  24. .git/objects/40/c6c8816b3ebd8cac4fa36f20454c84aa5929d1 +0 -0
  25. .git/objects/45/fbc774dedb61c7c205ea732f59a8dca8d13555 +3 -0
  26. .git/objects/5d/3e5b3d75b03eba63deda0dcac7ca304d41e4da +0 -0
  27. .git/objects/60/5cb6fd6a9f8894ad4d43a9b8e4785c1b3b0e17 +1 -0
  28. .git/objects/60/f61a0ea50091eac8d344c86597375cbdfc2785 +0 -0
  29. .git/objects/63/ddda2a403efaab3f4c6597b3a73a7b1147adb5 +0 -0
  30. .git/objects/66/6c5c5fc30435228116fa08c9d821bebaaa8926 +0 -0
  31. .git/objects/7c/ef0adfb28fd774bc78061c6f088e1ef9b050f6 +0 -0
  32. .git/objects/7d/dc129188a10c68ab756ef2cacb292c76920403 +0 -0
  33. .git/objects/80/17038e0f7818a44a742f77c86f4f88ed768fcd +0 -0
  34. .git/objects/87/c7db6c91c17a2df84b56d30bd24a0f6b9dbdd9 +0 -0
  35. .git/objects/90/6b05fdb9211a7d32515bb2da31e58e4ca89916 +0 -0
  36. .git/objects/92/984cf67b2c25d435468a8218daa26ba0466054 +0 -0
  37. .git/objects/93/140b54b1fb9116ee214afee8abf2c72a232487 +0 -0
  38. .git/objects/93/68f2c32f83054ab072b7c9686d8baa0bad7f12 +4 -0
  39. .git/objects/9a/5ad062be9f6e001f4237a598a08981aba731e6 +0 -0
  40. .git/objects/a1/655e0cb323c300562f97dcc67d5a446908c8ec +0 -0
  41. .git/objects/a5/38cc82cef7c49500d3522220f0f60a9ebc1ae6 +0 -0
  42. .git/objects/a9/41063a7ce89c353fa24378ec7c3f12f08f9df8 +0 -0
  43. .git/objects/ac/5c983d949d8c928bb022badf801e45e75e785e +0 -0
  44. .git/objects/b0/82ca2c1b5a03edff25da3c2b2b573d049877e9 +0 -0
  45. .git/objects/b2/a941b669991d7e0f9bdc35f1014897a933f4e2 +1 -0
  46. .git/objects/bc/e98bdb71c8681acb460195fdcbbe5d36290976 +0 -0
  47. .git/objects/c1/87d5e047eca86cfd8d444be2987aaa3f62c4d6 +0 -0
  48. .git/objects/c4/c2da96b0bb8db2acb0e6615cf340c7e51af26b +0 -0
  49. .git/objects/c5/13a96e7584636b20b12280c029750d5bc3da1e +0 -0
  50. .git/objects/c7/c34283697bd3cce07db53953eda25ee7cc371e +0 -0
  51. .git/objects/c9/d60d922a04b87587cd67b0abf9fe5a7b7b76cd +0 -0
  52. .git/objects/d2/69b1676dbf32f76a7c405d0b4ea6a70ac3a626 +0 -0
  53. .git/objects/d3/5a918b1d9125ad35d60e08b181323df3246f1a +0 -0
  54. .git/objects/d8/eaf86669fbfd10497570c1784db1ed2696b588 +0 -0
  55. .git/objects/d9/90e6d553577d37ebce8b28b3015ecbde038b42 +0 -0
  56. .git/objects/da/13cc15bcd8ee39c81f36dee7f179a569ecab0b +0 -0
  57. .git/objects/e4/4c1d8a90207ac082d8ab7ff0db66708e2ebc31 +0 -0
  58. .git/objects/e6/5ff9bea681f0b8f409ec27761935883c1f0cc9 +0 -0
  59. .git/objects/f8/ed595d25bd9d500e765a792c513878f7ddb1f7 +0 -0
  60. .git/refs/heads/iyue +1 -1
  61. .git/refs/remotes/gitlab/iyue +1 -1
  62. .git/refs/remotes/origin/iyue +1 -1
  63. README.md +104 -190
  64. core/__init__.py +23 -0
  65. core/info_decoder.py +13 -4
  66. core/reconstructor.py +25 -16
  67. generation/__init__.py +17 -0
  68. generation/proto_generator.py +11 -5
  69. main.py +59 -56
  70. models/__init__.py +31 -24
  71. parsing/__init__.py +22 -0
  72. parsing/enum_parser.py +10 -2
  73. parsing/java_parser.py +7 -1
  74. pyproject.toml +2 -1
  75. reproto-0.1.4.dist-info/METADATA +210 -0
  76. {reproto-0.1.2.dist-info → reproto-0.1.4.dist-info}/RECORD +85 -31
  77. requirements.txt +3 -1
  78. utils/__init__.py +55 -0
  79. utils/builtin_proto.py +8 -1
  80. utils/file_cache.py +8 -1
  81. utils/report_utils.py +71 -0
  82. utils/type_index.py +8 -1
  83. utils/version_checker.py +311 -0
  84. core/bytecode_parser.py +0 -274
  85. reproto-0.1.2.dist-info/METADATA +0 -295
  86. {reproto-0.1.2.dist-info → reproto-0.1.4.dist-info}/WHEEL +0 -0
  87. {reproto-0.1.2.dist-info → reproto-0.1.4.dist-info}/entry_points.txt +0 -0
utils/type_index.py CHANGED
@@ -10,7 +10,14 @@ Author: AI Assistant
10
10
  from pathlib import Path
11
11
  from typing import Dict, List, Optional, Set
12
12
  import threading
13
- from utils.logger import get_logger
13
+
14
+ # 智能导入:同时支持相对导入(包环境)和绝对导入(开发环境)
15
+ try:
16
+ # 相对导入(包环境)
17
+ from .logger import get_logger
18
+ except ImportError:
19
+ # 绝对导入(开发环境)
20
+ from utils.logger import get_logger
14
21
 
15
22
 
16
23
  class TypeMatchingIndex:
@@ -0,0 +1,311 @@
1
+ """
2
+ 通用版本检测模块
3
+
4
+ 支持检测Python包的新版本并提供用户友好的提示信息
5
+ 设计为通用模块,可以被其他Python库复用
6
+
7
+ 支持的版本源:
8
+ - PyPI (Python Package Index)
9
+
10
+ Author: AI Assistant
11
+ """
12
+
13
+ import json
14
+ import time
15
+ import threading
16
+ from typing import Optional, Callable
17
+ from pathlib import Path
18
+ from urllib.request import urlopen, Request
19
+ from urllib.error import URLError, HTTPError
20
+ from packaging import version
21
+ import tempfile
22
+ import importlib.metadata
23
+
24
+ # 智能导入:同时支持相对导入(包环境)和绝对导入(开发环境)
25
+ try:
26
+ # 相对导入(包环境)
27
+ from .logger import get_logger
28
+ except ImportError:
29
+ # 绝对导入(开发环境)
30
+ from utils.logger import get_logger
31
+
32
+
33
+ def get_package_version(package_name: str) -> Optional[str]:
34
+ """
35
+ 动态获取已安装包的版本号
36
+
37
+ Args:
38
+ package_name: 包名
39
+
40
+ Returns:
41
+ 包版本号,如果获取失败则返回None
42
+ """
43
+ try:
44
+ return importlib.metadata.version(package_name)
45
+ except importlib.metadata.PackageNotFoundError:
46
+ return None
47
+
48
+
49
+ class VersionChecker:
50
+ """
51
+ 通用版本检测器
52
+
53
+ 功能:
54
+ - 支持PyPI版本源
55
+ - 异步检测,不阻塞主程序
56
+ - 智能缓存,避免频繁请求
57
+ - 可配置的检测间隔和提示样式
58
+ - 动态获取当前版本
59
+ """
60
+
61
+ def __init__(self,
62
+ package_name: str,
63
+ current_version: Optional[str] = None,
64
+ cache_duration: int = 3600, # 缓存1小时
65
+ timeout: int = 5, # 请求超时5秒
66
+ silent_mode: bool = False): # 静默模式
67
+ """
68
+ 初始化版本检测器
69
+
70
+ Args:
71
+ package_name: 包名
72
+ current_version: 当前版本,如果为None则自动获取
73
+ cache_duration: 缓存持续时间(秒)
74
+ timeout: 网络请求超时时间(秒)
75
+ silent_mode: 是否启用静默模式(不打印日志)
76
+ """
77
+ self.package_name = package_name
78
+ self.current_version = current_version or get_package_version(package_name) or "0.0.0"
79
+ self.cache_duration = cache_duration
80
+ self.timeout = timeout
81
+ self.silent_mode = silent_mode
82
+
83
+ # 缓存配置
84
+ self.cache_dir = Path(tempfile.gettempdir()) / f".{package_name}_version_cache"
85
+ self.cache_file = self.cache_dir / "latest_version.json"
86
+
87
+ # 日志配置
88
+ self.logger = get_logger("version_checker") if not silent_mode else None
89
+
90
+ def check_version_async(self,
91
+ callback: Optional[Callable] = None,
92
+ show_notification: bool = True):
93
+ """
94
+ 异步检测版本更新
95
+
96
+ Args:
97
+ callback: 检测完成后的回调函数
98
+ show_notification: 是否显示更新提示
99
+ """
100
+ def _check():
101
+ try:
102
+ latest_version = self.check_version_sync()
103
+ if latest_version and self._is_newer_version(latest_version):
104
+ if show_notification:
105
+ self._show_update_notification(latest_version)
106
+ if callback:
107
+ callback(True, latest_version)
108
+ else:
109
+ if callback:
110
+ callback(False, latest_version)
111
+ except Exception as e:
112
+ if not self.silent_mode and self.logger:
113
+ self.logger.debug(f"版本检测失败: {e}")
114
+ if callback:
115
+ callback(False, None)
116
+
117
+ thread = threading.Thread(target=_check, daemon=True)
118
+ thread.start()
119
+
120
+ def check_version_sync(self) -> Optional[str]:
121
+ """
122
+ 同步检测版本更新
123
+
124
+ Returns:
125
+ 最新版本号,如果检测失败则返回None
126
+ """
127
+ # 检查缓存
128
+ cached_version = self._get_cached_version()
129
+ if cached_version:
130
+ return cached_version
131
+
132
+ # 从PyPI获取最新版本
133
+ try:
134
+ latest_version = self._check_pypi_version()
135
+ if latest_version:
136
+ self._cache_version(latest_version)
137
+ return latest_version
138
+ except Exception as e:
139
+ if not self.silent_mode and self.logger:
140
+ self.logger.debug(f"从PyPI检测版本失败: {e}")
141
+
142
+ return None
143
+
144
+ def _check_pypi_version(self) -> Optional[str]:
145
+ """从PyPI检测最新版本"""
146
+ url = f"https://pypi.org/pypi/{self.package_name}/json"
147
+
148
+ try:
149
+ request = Request(url, headers={'User-Agent': f'{self.package_name}-version-checker'})
150
+ with urlopen(request, timeout=self.timeout) as response:
151
+ data = json.loads(response.read().decode())
152
+ return data['info']['version']
153
+ except (URLError, HTTPError, KeyError, json.JSONDecodeError):
154
+ return None
155
+
156
+ def _is_newer_version(self, latest_version: str) -> bool:
157
+ """检查是否有新版本"""
158
+ try:
159
+ return version.parse(latest_version) > version.parse(self.current_version)
160
+ except Exception:
161
+ # 如果版本解析失败,使用字符串比较
162
+ return latest_version != self.current_version
163
+
164
+ def _get_cached_version(self) -> Optional[str]:
165
+ """获取缓存的版本信息"""
166
+ try:
167
+ if not self.cache_file.exists():
168
+ return None
169
+
170
+ with open(self.cache_file, 'r', encoding='utf-8') as f:
171
+ cache_data = json.load(f)
172
+
173
+ # 检查缓存是否过期
174
+ cache_time = cache_data.get('timestamp', 0)
175
+ if time.time() - cache_time > self.cache_duration:
176
+ return None
177
+
178
+ return cache_data.get('version')
179
+ except (FileNotFoundError, json.JSONDecodeError, KeyError):
180
+ return None
181
+
182
+ def _cache_version(self, latest_version: str):
183
+ """缓存版本信息"""
184
+ try:
185
+ self.cache_dir.mkdir(exist_ok=True)
186
+
187
+ cache_data = {
188
+ 'version': latest_version,
189
+ 'timestamp': time.time()
190
+ }
191
+
192
+ with open(self.cache_file, 'w', encoding='utf-8') as f:
193
+ json.dump(cache_data, f)
194
+ except Exception:
195
+ pass # 缓存失败不影响主功能
196
+
197
+ def _show_update_notification(self, latest_version: str):
198
+ """显示更新通知"""
199
+ if self.silent_mode:
200
+ return
201
+
202
+ print(f"\n🎉 {self.package_name} 有新版本可用!")
203
+ print(f" 当前版本: {self.current_version}")
204
+ print(f" 最新版本: {latest_version}")
205
+ print(f" 更新命令: pip install --upgrade {self.package_name}")
206
+ print()
207
+
208
+ def clear_cache(self):
209
+ """清除版本缓存"""
210
+ try:
211
+ if self.cache_file.exists():
212
+ self.cache_file.unlink()
213
+ except Exception:
214
+ pass
215
+
216
+
217
+ def create_version_checker(package_name: str,
218
+ current_version: Optional[str] = None,
219
+ **kwargs) -> VersionChecker:
220
+ """
221
+ 创建版本检测器的便捷函数
222
+
223
+ Args:
224
+ package_name: 包名
225
+ current_version: 当前版本,如果为None则自动获取
226
+ **kwargs: 其他配置参数
227
+
228
+ Returns:
229
+ VersionChecker实例
230
+ """
231
+ return VersionChecker(package_name, current_version, **kwargs)
232
+
233
+
234
+ def check_pypi_version(package_name: str,
235
+ current_version: Optional[str] = None,
236
+ show_notification: bool = True,
237
+ silent_mode: bool = False) -> Optional[str]:
238
+ """
239
+ 快速检测PyPI版本的便捷函数
240
+
241
+ Args:
242
+ package_name: 包名
243
+ current_version: 当前版本,如果为None则自动获取
244
+ show_notification: 是否显示通知
245
+ silent_mode: 是否静默模式
246
+
247
+ Returns:
248
+ 最新版本号,如果检测失败则返回None
249
+ """
250
+ checker = VersionChecker(package_name, current_version, silent_mode=silent_mode)
251
+ latest_version = checker.check_version_sync()
252
+
253
+ if latest_version and checker._is_newer_version(latest_version) and show_notification:
254
+ checker._show_update_notification(latest_version)
255
+
256
+ return latest_version
257
+
258
+
259
+ def check_reproto_version(current_version: Optional[str] = None,
260
+ show_notification: bool = True,
261
+ silent_mode: bool = False) -> Optional[str]:
262
+ """
263
+ 检测reproto包的PyPI版本更新
264
+
265
+ 这是专门为reproto项目设计的便捷函数,使用PyPI作为版本源
266
+
267
+ Args:
268
+ current_version: 当前版本,如果为None则自动获取
269
+ show_notification: 是否显示更新通知,默认True
270
+ silent_mode: 是否静默模式,默认False
271
+
272
+ Returns:
273
+ 最新版本号,如果检测失败则返回None
274
+ """
275
+ return check_pypi_version(
276
+ package_name="reproto",
277
+ current_version=current_version,
278
+ show_notification=show_notification,
279
+ silent_mode=silent_mode
280
+ )
281
+
282
+
283
+ def check_version_on_startup(current_version: Optional[str] = None):
284
+ """
285
+ 在程序启动时异步检测版本更新
286
+
287
+ 这个函数专门用于在reproto启动时进行版本检测,
288
+ 采用异步方式,不会阻塞主程序启动
289
+
290
+ Args:
291
+ current_version: 当前版本号,如果为None则自动获取
292
+ """
293
+ # 获取当前版本
294
+ actual_version = current_version or get_package_version("reproto") or "0.0.0"
295
+
296
+ def version_callback(has_update: bool, latest_version: Optional[str]):
297
+ if has_update and latest_version:
298
+ print(f"\n💡 提示:reproto有新版本 {latest_version} 可用")
299
+ print(f" 当前版本:{actual_version}")
300
+ print(f" 更新命令:pip install --upgrade reproto\n")
301
+
302
+ checker = create_version_checker(
303
+ package_name="reproto",
304
+ current_version=actual_version,
305
+ silent_mode=True # 静默模式,只在有更新时通过回调显示
306
+ )
307
+
308
+ checker.check_version_async(
309
+ callback=version_callback,
310
+ show_notification=False # 使用自定义回调,不使用默认通知
311
+ )
core/bytecode_parser.py DELETED
@@ -1,274 +0,0 @@
1
- from typing import List, Dict, Tuple
2
- import re
3
- from ..models import FieldDefinition, OneofDefinition
4
- from utils.type_utils import naming_converter
5
-
6
- class BytecodeParser:
7
- """
8
- 重新设计的 Protobuf info_string 解析器,基于对 Google Protobuf Lite 格式的深入理解。
9
-
10
- info_string 格式分析:
11
- - 前面是头部信息(版本、字段数量等)
12
- - 后面是字段描述符,每个字段用特定格式编码
13
- - '<' 字符表示 oneof 字段
14
- - 数字表示字段标签
15
- """
16
-
17
- def __init__(self, info_bytes: bytes, objects: List[str]):
18
- """
19
- 初始化解析器。
20
-
21
- 参数:
22
- info_bytes (bytes): 从 info_string 编码而来的字节数组。
23
- objects (List[str]): 作为符号表的 objects 数组。
24
- """
25
- self.info_string = info_bytes.decode('utf-8')
26
- self.objects = objects
27
- # print(f" > 原始 info_string: {repr(self.info_string)}")
28
- # print(f" > 对象数组: {self.objects}")
29
-
30
- def parse(self) -> Tuple[List[FieldDefinition], Dict[str, OneofDefinition]]:
31
- """
32
- 解析 info_string 并返回字段定义。
33
-
34
- 基于观察到的模式:
35
- - SearchResult: "\\u0000\\u0002\\u0001\\u0000\\u0001\\u0002\\u0002\\u0000\\u0000\\u0000\\u0001<\\u0000\\u0002<\\u0000"
36
- - ContactPhone: "\\u0000\\u0003\\u0001\\u0000\\u0001\\u0003\\u0003\\u0000\\u0000\\u0000\\u0001<\\u0000\\u0002<\\u0000\\u0003<\\u0000"
37
- """
38
- fields: List[FieldDefinition] = []
39
- oneofs: Dict[str, OneofDefinition] = {}
40
-
41
- # 尝试解析字段描述符部分
42
- # 寻找 '<' 字符,它们标识 oneof 字段
43
- field_descriptors = self._extract_field_descriptors()
44
-
45
- if not field_descriptors:
46
- return fields, oneofs
47
-
48
- # 确定 oneof 名称(从 objects 数组中)
49
- oneof_name = self._determine_oneof_name()
50
-
51
- if oneof_name:
52
- # 这是一个 oneof 结构
53
- oneof_def = OneofDefinition(name=oneof_name)
54
-
55
- for i, desc in enumerate(field_descriptors):
56
- if desc.get('is_oneof'):
57
- field_tag = desc['tag']
58
- field_type = self._determine_field_type(i + 2) # objects[2], objects[3], etc.
59
- field_name = self._generate_field_name(field_type, field_tag)
60
-
61
- field = FieldDefinition(
62
- name=field_name,
63
- type=field_type,
64
- tag=field_tag,
65
- rule="optional"
66
- )
67
- oneof_def.fields.append(field)
68
-
69
- oneofs[oneof_name] = oneof_def
70
- else:
71
- # 常规字段结构
72
- for i, desc in enumerate(field_descriptors):
73
- field_tag = desc['tag']
74
- # 对于常规字段,从 objects 数组中获取字段名
75
- if field_tag - 1 < len(self.objects):
76
- raw_field_name = self.objects[field_tag - 1] # tag 从 1 开始,数组从 0 开始
77
- field_name = self._clean_field_name(raw_field_name)
78
- else:
79
- field_name = f"field_{field_tag}"
80
-
81
- # 尝试推断字段类型
82
- field_type = self._infer_field_type(desc, field_tag)
83
-
84
- # 检查是否是 repeated 字段
85
- if field_type.startswith("repeated "):
86
- rule = "repeated"
87
- field_type = field_type[9:] # 移除 "repeated " 前缀
88
- else:
89
- rule = "optional"
90
-
91
- field = FieldDefinition(
92
- name=field_name,
93
- type=field_type,
94
- tag=field_tag,
95
- rule=rule
96
- )
97
- fields.append(field)
98
-
99
- return fields, oneofs
100
-
101
- def _extract_field_descriptors(self) -> List[Dict]:
102
- """
103
- 从 info_string 中提取字段描述符。
104
-
105
- 基于观察:
106
- - 字段描述符通常在字符串的后半部分
107
- - '<' 字符标识 oneof 字段
108
- - 数字字符表示字段标签
109
- """
110
- descriptors = []
111
-
112
- # 先打印原始字符串的字符分析
113
- # print(f" > 字符串分析:")
114
- # for i, char in enumerate(self.info_string):
115
- # if ord(char) > 32: # 可打印字符
116
- # print(f" 位置 {i}: '{char}' (ord={ord(char)})")
117
- # else:
118
- # print(f" 位置 {i}: \\x{ord(char):02x}")
119
-
120
- # 寻找 '<' 字符的位置
121
- oneof_positions = []
122
- for i, char in enumerate(self.info_string):
123
- if char == '<':
124
- oneof_positions.append(i)
125
-
126
- # print(f" > 找到 oneof 标记位置: {oneof_positions}")
127
-
128
- # 对于每个 '<' 字符,查找前面的数字作为字段标签
129
- for pos in oneof_positions:
130
- # 查找 '<' 前面的字符
131
- if pos > 0:
132
- prev_char = self.info_string[pos - 1]
133
- prev_byte = ord(prev_char)
134
- # print(f" '<' 前面的字节: \\x{prev_byte:02x} (值={prev_byte})")
135
-
136
- # 检查是否是有效的字段标签(1-15 是常见范围)
137
- if 1 <= prev_byte <= 15:
138
- tag = prev_byte
139
- descriptors.append({
140
- 'tag': tag,
141
- 'is_oneof': True,
142
- 'position': pos
143
- })
144
- # print(f" 发现 oneof 字段: tag={tag}, 位置={pos}")
145
-
146
- # 如果没有找到 oneof 字段,尝试寻找普通字段
147
- if not descriptors:
148
- # 寻找可能的字段标签模式
149
- # 对于非 oneof 字段,我们需要查找其他模式
150
- # print(f" > 没有找到 oneof 字段,尝试寻找常规字段...")
151
-
152
- # 查找特殊字符 'Ȉ' (ord=520) 这似乎是字段标记
153
- for i, char in enumerate(self.info_string):
154
- if ord(char) == 520: # 'Ȉ' 字符
155
- # 查找前面的字节作为字段标签
156
- if i > 0:
157
- prev_byte = ord(self.info_string[i - 1])
158
- if 1 <= prev_byte <= 50: # 扩大范围以捕获更多字段
159
- descriptors.append({
160
- 'tag': prev_byte,
161
- 'is_oneof': False,
162
- 'position': i
163
- })
164
- # print(f" 发现常规字段: tag={prev_byte}, 位置={i}")
165
-
166
- # 如果还是没有找到,尝试其他数字模式
167
- if not descriptors:
168
- for i, char in enumerate(self.info_string):
169
- byte_val = ord(char)
170
- if 1 <= byte_val <= 20 and byte_val <= len(self.objects): # 可能的字段标签
171
- # 检查前后上下文以确认这是字段标签
172
- if i < len(self.info_string) - 1:
173
- next_char = self.info_string[i + 1]
174
- # 如果下一个字符是特殊标记,这可能是字段标签
175
- if ord(next_char) in [50, 60, 520, 27]: # 各种字段类型标记
176
- descriptors.append({
177
- 'tag': byte_val,
178
- 'is_oneof': False,
179
- 'position': i
180
- })
181
- # print(f" 发现可能的字段: tag={byte_val}, 位置={i}")
182
-
183
- return descriptors
184
-
185
- def _determine_oneof_name(self) -> str:
186
- """
187
- 从 objects 数组确定 oneof 的名称。
188
-
189
- 观察:
190
- - objects[0] 通常是 oneof 字段名(如 "result_", "sealedValue_")
191
- - objects[1] 通常是 case 字段名(如 "resultCase_", "sealedValueCase_")
192
- """
193
- if len(self.objects) >= 2:
194
- field_name = self.objects[0]
195
- case_name = self.objects[1]
196
-
197
- # 检查是否符合 oneof 模式
198
- if (field_name.endswith('_') and
199
- case_name.endswith('Case_') and
200
- case_name.startswith(field_name[:-1])):
201
-
202
- # 从字段名生成 oneof 名称
203
- base_name = field_name[:-1] # 移除末尾的 '_'
204
- return self._to_snake_case(base_name)
205
-
206
- return ""
207
-
208
- def _determine_field_type(self, object_index: int) -> str:
209
- """
210
- 从 objects 数组确定字段类型。
211
- """
212
- if object_index < len(self.objects):
213
- obj = self.objects[object_index]
214
- if obj.endswith('.class'):
215
- # 移除 '.class' 后缀,提取类名
216
- class_name = obj[:-6]
217
- # 提取最后一部分作为类型名
218
- return class_name.split('.')[-1]
219
- else:
220
- return obj
221
- return "unknown"
222
-
223
- def _generate_field_name(self, field_type: str, field_tag: int) -> str:
224
- """
225
- 根据字段类型和标签生成字段名称。
226
- """
227
- # 将类型名转换为 snake_case
228
- name = self._to_snake_case(field_type)
229
- return name
230
-
231
- def _to_snake_case(self, name: str) -> str:
232
- """
233
- 将 CamelCase 转换为 snake_case。
234
- """
235
- return naming_converter.to_snake_case(name)
236
-
237
- def _clean_field_name(self, name: str) -> str:
238
- """
239
- 清理字段名称,去除不必要的后缀或前缀。
240
- """
241
- # 去除末尾的下划线
242
- if name.endswith('_'):
243
- name = name[:-1]
244
- # 转换为 snake_case
245
- return self._to_snake_case(name)
246
-
247
- def _infer_field_type(self, desc: Dict, field_tag: int) -> str:
248
- """
249
- 根据字段描述符推断字段类型。
250
- """
251
- # 检查 info_string 中字段标签后面的字节来推断类型
252
- position = desc.get('position', -1)
253
- if position > 0 and position < len(self.info_string) - 1:
254
- # 查看字段标签后面的字节
255
- next_byte = ord(self.info_string[position + 1])
256
-
257
- # 根据字节值推断类型
258
- if next_byte == 27: # 0x1B - repeated message
259
- # 查找对应的类类型
260
- if field_tag < len(self.objects):
261
- class_obj = self.objects[field_tag] # objects[1] 对应 tag=1
262
- if class_obj.endswith('.class'):
263
- class_name = class_obj[:-6].split('.')[-1]
264
- return f"repeated {class_name}"
265
- return "repeated message"
266
- elif next_byte == 520: # 'Ȉ' - string field
267
- return "string"
268
- elif next_byte == 12: # 0x0C - enum
269
- return "int32" # 枚举通常映射为 int32
270
- elif next_byte == 50: # '2' - 可能是某种数字类型
271
- return "int32"
272
-
273
- # 默认返回 string
274
- return "string"