Skip to content

Commit f858e05

Browse files
committed
chore: evaluate_all
1 parent 47b8869 commit f858e05

File tree

4 files changed

+58
-40
lines changed

4 files changed

+58
-40
lines changed

lagrange/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .utils.sign import sign_provider
1010
from .info import InfoManager
1111
from .info.app import app_list
12+
from .utils.binary.protobuf.models import evaluate_all
1213

1314

1415
class Lagrange:
@@ -66,3 +67,6 @@ def launch(self):
6667
log.root.info("Program exited by user")
6768
else:
6869
log.root.info("Program exited normally")
70+
71+
72+
evaluate_all()

lagrange/pb/service/group.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ class MemberInfoLevel(ProtoStruct):
321321

322322
class GetGrpMemberInfoRsp(ProtoStruct):
323323
grp_id: int = proto_field(1)
324-
body: list["GetGrpMemberInfoRspBody"] = proto_field(2)
324+
body: "list[GetGrpMemberInfoRspBody]" = proto_field(2)
325325
next_key: Optional[bytes] = proto_field(15, default=None) # base64(pb)
326326

327327

@@ -429,15 +429,15 @@ class GetInfoRspField(ProtoStruct, debug=True):
429429
str_t: list[GetInfoRspF2] = proto_field(2, default_factory=list)
430430

431431

432+
class GetInfoFromUidRsp(ProtoStruct):
433+
body: list["GetInfoRspBody"] = proto_field(1)
434+
435+
432436
class GetInfoRspBody(ProtoStruct):
433437
uid: str = proto_field(1)
434438
fields: GetInfoRspField = proto_field(2)
435439

436440

437-
class GetInfoFromUidRsp(ProtoStruct):
438-
body: list[GetInfoRspBody] = proto_field(1)
439-
440-
441441
class Oidb88D0Args(ProtoStruct):
442442
seq: Optional[int] = proto_field(22, default=None)
443443

lagrange/pb/status/group.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ class GroupSub16Head(ProtoStruct):
111111
f44: Optional[PBGroupReaction] = proto_field(44, default=None) # set reaction only
112112

113113

114+
class GroupSub20Head(ProtoStruct):
115+
f1: int = proto_field(1) # 20
116+
grp_id: int = proto_field(4)
117+
f13: int = proto_field(13) # 19
118+
body: "GroupSub20Body" = proto_field(26)
119+
120+
114121
class GroupSub20Body(ProtoStruct):
115122
type: int = proto_field(1) # 12: nudge, 14: group_sign
116123
# f2: int = proto_field(2) # 1061
@@ -121,13 +128,6 @@ class GroupSub20Body(ProtoStruct):
121128
f10: int = proto_field(10) # rand?
122129

123130

124-
class GroupSub20Head(ProtoStruct):
125-
f1: int = proto_field(1) # 20
126-
grp_id: int = proto_field(4)
127-
f13: int = proto_field(13) # 19
128-
body: GroupSub20Body = proto_field(26)
129-
130-
131131
class PBGroupAlbumUpdateBody(ProtoStruct):
132132
# f1: 6
133133
args: str = proto_field(2)

lagrange/utils/binary/protobuf/models.py

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import sys
12
from dataclasses import MISSING
23
from types import GenericAlias
3-
from typing import cast, TypeVar, Union, Any, Callable, overload, get_origin, get_args, get_type_hints
4+
from typing import cast, TypeVar, Union, Any, Callable, overload, get_origin, get_args, ForwardRef
45
from collections.abc import Mapping
56
from typing_extensions import Self, TypeAlias, dataclass_transform
67
from typing import Optional, ClassVar
@@ -31,8 +32,8 @@ def __init__(self, tag: int, default: Any, default_factory: Any):
3132
if tag <= 0:
3233
raise ValueError("Tag must be a positive integer")
3334
self.tag = tag
34-
self._default = default
35-
self._default_factory = default_factory
35+
self.default = default
36+
self.default_factory = default_factory
3637
self._unevaluated = False
3738

3839
def ensure_annotation(self, name: str, type_: Any) -> None:
@@ -44,10 +45,10 @@ def ensure_annotation(self, name: str, type_: Any) -> None:
4445
self._unevaluated = True
4546

4647
def get_default(self) -> Any:
47-
if self._default is not MISSING:
48-
return self._default
49-
elif self._default_factory is not MISSING:
50-
return self._default_factory()
48+
if self.default is not MISSING:
49+
return self.default
50+
elif self.default_factory is not MISSING:
51+
return self.default_factory()
5152
return MISSING
5253

5354
@property
@@ -166,15 +167,16 @@ def check_type(value: Any, typ: Any) -> bool:
166167
return False # or True if value is None else False
167168

168169

170+
_unevaluated_classes: set[type["ProtoStruct"]] = set()
171+
172+
169173
@dataclass_transform(kw_only_default=True, field_specifiers=(proto_field,))
170174
class ProtoStruct:
171175
__proto_fields__: ClassVar[dict[str, ProtoField]]
172176
__proto_debug__: ClassVar[bool]
173-
__proto_evaluated__: ClassVar[bool] = False
174177

175178
def __init__(self, __from_raw: bool = False, /, **kwargs):
176179
undefined_params: list[ProtoField] = []
177-
self._evaluate()
178180
for name, field in self.__proto_fields__.items():
179181
if name in kwargs:
180182
value = kwargs.pop(name)
@@ -212,11 +214,13 @@ def _process_field(cls):
212214
if field is MISSING:
213215
raise TypeError(f'{name!r} should define its proto_field!')
214216
field.ensure_annotation(name, typ)
217+
if field._unevaluated:
218+
_unevaluated_classes.add(cls)
215219
cls_fields.append(field)
216220

217221
for f in cls_fields:
218222
fields[f.name] = f
219-
if f._default is MISSING:
223+
if f.default is MISSING:
220224
delattr(cls, f.name)
221225

222226
for name, value in cls.__dict__.items():
@@ -225,28 +229,20 @@ def _process_field(cls):
225229

226230
cls.__proto_fields__ = fields
227231

228-
@classmethod
229-
def _evaluate(cls):
230-
for base in reversed(cls.__mro__):
231-
if base in (ProtoStruct, object):
232-
continue
233-
if getattr(base, '__proto_evaluated__', False):
234-
continue
235-
try:
236-
annotations = get_type_hints(base)
237-
except NameError:
238-
annotations = {}
239-
for field in base.__proto_fields__.values():
240-
if field._unevaluated and field.name in annotations:
241-
field.type = annotations[field.name]
242-
base.__proto_evaluated__ = True
243-
244232
@classmethod
245233
def update_forwardref(cls, mapping: dict[str, "type[ProtoStruct]"]):
246234
"""更新 ForwardRef"""
247235
for field in cls.__proto_fields__.values():
248-
if field._unevaluated:
249-
field.type = typing._eval_type(field.type, mapping, mapping) # type: ignore
236+
if not field._unevaluated:
237+
continue
238+
try:
239+
typ = field.type
240+
if isinstance(typ, str):
241+
typ = ForwardRef(typ, is_argument=False, is_class=True)
242+
field.type = typing._eval_type(typ, mapping, mapping) # type: ignore
243+
field._unevaluated = False
244+
except NameError:
245+
pass
250246

251247
def __init_subclass__(cls, **kwargs):
252248
cls.__proto_debug__ = kwargs.pop("debug") if "debug" in kwargs else False
@@ -295,3 +291,21 @@ def decode(cls, data: bytes) -> Self:
295291
if pb_dict and cls.__proto_debug__: # unhandled tags
296292
pass
297293
return cls(True, **kwargs)
294+
295+
296+
def evaluate_all():
297+
modules = set()
298+
for cls in _unevaluated_classes:
299+
modules.add(cls.__module__)
300+
for base in cls.__mro__[-1:0:-1][2:]:
301+
modules.add(base.__module__)
302+
globalns = {}
303+
for module in modules:
304+
globalns.update(getattr(sys.modules.get(module, None), "__dict__", {}))
305+
for cls in _unevaluated_classes:
306+
cls.update_forwardref(globalns)
307+
_unevaluated_classes.clear()
308+
modules.clear()
309+
globalns.clear()
310+
del modules
311+
del globalns

0 commit comments

Comments
 (0)