11from __future__ import annotations
22
33from abc import ABCMeta , abstractmethod
4- from typing import Any , ClassVar , Literal , TypeVar , cast , final , overload
4+ from typing import Any , ClassVar , Generic , Literal , TypeVar , cast , final , overload
55
6+ from attrs import define
67from typing_extensions import dataclass_transform , override
78
89from mcproto .buffer import Buffer
910from mcproto .protocol import StructFormat
1011from mcproto .types .abc import MCType
1112
13+ T = TypeVar ("T" )
14+ T_2 = TypeVar ("T_2" )
1215
13- class EntityMetadataEntry (MCType ):
16+
17+ class EntityMetadataEntry (MCType , Generic [T ]):
1418 """Represents an entry in an entity metadata list.
1519
1620 :param index: The index of the entry.
@@ -24,26 +28,31 @@ class EntityMetadataEntry(MCType):
2428 ENTRY_TYPE : ClassVar [int ] = None # type: ignore
2529
2630 index : int
27- value : Any
31+ value : T
2832
2933 __slots__ = ("index" , "value" , "hidden" , "default" , "name" )
3034
3135 def __init__ (
32- self , index : int , value : Any = None , default : Any = None , hidden : bool = False , name : str | None = None
36+ self ,
37+ index : int ,
38+ value : T | None = None ,
39+ default : T | None = None ,
40+ hidden : bool = False ,
41+ name : str | None = None ,
3342 ):
3443 self .index = index
35- self .value = default if value is None else value
44+ self .value = value if value is not None else default # type: ignore
3645 self .hidden = hidden
3746 self .default = default
3847 self .name = name # for debugging purposes
3948
4049 self .validate ()
4150
42- def setter (self , value : Any ) -> None :
51+ def setter (self , value : T ) -> None :
4352 """Set the value of the entry."""
4453 self .value = value
4554
46- def getter (self ) -> Any :
55+ def getter (self ) -> T :
4756 """Get the value of the entry."""
4857 return self .value
4958
@@ -117,7 +126,7 @@ def read_value(cls, buf: Buffer) -> Any:
117126 @override
118127 @classmethod
119128 @final
120- def deserialize (cls , buf : Buffer ) -> EntityMetadataEntry :
129+ def deserialize (cls , buf : Buffer ) -> EntityMetadataEntry [ T ] :
121130 """Deserialize the entity metadata entry.
122131
123132 :param buf: The buffer to read from.
@@ -128,19 +137,19 @@ def deserialize(cls, buf: Buffer) -> EntityMetadataEntry:
128137 return cls (index = index , value = value )
129138
130139
131- class ProxyEntityMetadataEntry (MCType ):
140+ class ProxyEntityMetadataEntry (MCType , Generic [ T , T_2 ] ):
132141 """A proxy entity metadata entry which is used to designate a part of a metadata entry in a human-readable format.
133142
134143 For example, this can be used to represent a certain mask for a ByteEME entry.
135144 """
136145
137146 ENTRY_TYPE : ClassVar [int ] = None # type: ignore
138147
139- bound_entry : EntityMetadataEntry
148+ bound_entry : EntityMetadataEntry [ T_2 ]
140149
141150 __slots__ = ("bound_entry" ,)
142151
143- def __init__ (self , bound_entry : EntityMetadataEntry , * args : Any , ** kwargs : Any ):
152+ def __init__ (self , bound_entry : EntityMetadataEntry [ T_2 ] , * args : Any , ** kwargs : Any ):
144153 self .bound_entry = bound_entry
145154 self .validate ()
146155
@@ -150,55 +159,43 @@ def serialize_to(self, buf: Buffer) -> None:
150159
151160 @override
152161 @classmethod
153- def deserialize (cls , buf : Buffer ) -> ProxyEntityMetadataEntry :
162+ def deserialize (cls , buf : Buffer ) -> ProxyEntityMetadataEntry [ T , T_2 ] :
154163 raise NotImplementedError ("Proxy entity metadata entries cannot be deserialized." )
155164
156165 @abstractmethod
157- def setter (self , value : Any ) -> None :
166+ def setter (self , value : T ) -> None :
158167 """Set the value of the entry by modifying the bound entry."""
159168
160169 @abstractmethod
161- def getter (self ) -> Any :
170+ def getter (self ) -> T :
162171 """Get the value of the entry by reading the bound entry."""
163172
164173 def validate (self ) -> None :
165174 """Validate that the proxy metadata entry has valid values."""
166175
167176
168- EntityDefault = TypeVar ("EntityDefault" )
169-
177+ @define
178+ class DefaultEntityMetadataEntryDeclaration (Generic [T ]):
179+ """Class used to pass the default metadata to the entity metadata."""
170180
171- class _DefaultEntityMetadataEntry :
172181 m_default : Any
173- m_type : type [EntityMetadataEntry ]
182+ m_type : type [EntityMetadataEntry [ T ] ]
174183 m_index : int
175184
176- __slots__ = ("m_default" , "m_type" )
177185
178-
179- def entry (entry_type : type [EntityMetadataEntry ], value : EntityDefault ) -> EntityDefault :
186+ def entry (entry_type : type [EntityMetadataEntry [T ]], value : T ) -> T :
180187 """Create a entity metadata entry with the given value.
181188
182189 :param entry_type: The type of the entry.
183190 :param default: The default value of the entry.
184191 :return: The default entity metadata entry.
185192 """
186-
187- class DefaultEntityMetadataEntry (_DefaultEntityMetadataEntry ):
188- m_default = value
189- m_type = entry_type
190- m_index = - 1
191-
192- __slots__ = ()
193-
194193 # This will be taken care of by EntityMetadata
195- return DefaultEntityMetadataEntry # type: ignore
196-
194+ return DefaultEntityMetadataEntryDeclaration (m_default = value , m_type = entry_type , m_index = - 1 ) # type: ignore
197195
198- ProxyInitializer = TypeVar ("ProxyInitializer" )
199196
200-
201- class _ProxyEntityMetadataEntry :
197+ @ define
198+ class ProxyEntityMetadataEntryDeclaration ( Generic [ T , T_2 ]) :
202199 """Class used to pass the bound entry and additional arguments to the proxy entity metadata entry.
203200
204201 Explanation:
@@ -212,21 +209,19 @@ class _ProxyEntityMetadataEntry:
212209 This is set by the EntityMetadataCreator.
213210 """
214211
215- m_bound_entry : EntityMetadataEntry
212+ m_bound_entry : EntityMetadataEntry [ T_2 ]
216213 m_args : tuple [Any ]
217214 m_kwargs : dict [str , Any ]
218- m_type : type [ProxyEntityMetadataEntry ]
215+ m_type : type [ProxyEntityMetadataEntry [ T , T_2 ] ]
219216 m_bound_index : int
220217
221- __slots__ = ("m_bound_entry" , "m_args" , "m_kwargs" , "m_type" , "m_bound_index" )
222-
223218
224219def proxy (
225- bound_entry : EntityDefault , # type: ignore # Used only once but I prefer to keep the type hint
226- proxy : type [ProxyEntityMetadataEntry ],
220+ bound_entry : T_2 , # This will in fact be an EntityMetadataEntry, but treated as a T_2 during type checking
221+ proxy : type [ProxyEntityMetadataEntry [ T , T_2 ] ],
227222 * args : Any ,
228223 ** kwargs : Any ,
229- ) -> ProxyInitializer : # type: ignore
224+ ) -> T :
230225 """Initialize the proxy entity metadata entry with the given bound entry and additional arguments.
231226
232227 :param bound_entry: The bound entry.
@@ -236,22 +231,16 @@ def proxy(
236231
237232 :return: The proxy entity metadata entry initializer.
238233 """
239- if not isinstance (bound_entry , type ):
234+ if not isinstance (bound_entry , DefaultEntityMetadataEntryDeclaration ):
240235 raise TypeError ("The bound entry must be an entity metadata entry type." )
241- if not issubclass (bound_entry , _DefaultEntityMetadataEntry ):
242- raise TypeError ("The bound entry must be an entity metadata entry." )
243-
244- class ProxyEntityMetadataEntry (_ProxyEntityMetadataEntry ):
245- m_bound_entry = bound_entry # type: ignore # This will be taken care of by EntityMetadata
246- m_args = args
247- m_kwargs = kwargs
248- m_type = proxy
249-
250- m_bound_index = - 1
251236
252- __slots__ = ()
253-
254- return ProxyEntityMetadataEntry # type: ignore
237+ return ProxyEntityMetadataEntryDeclaration ( # type: ignore
238+ m_bound_entry = bound_entry , # type: ignore
239+ m_args = args ,
240+ m_kwargs = kwargs ,
241+ m_type = proxy ,
242+ m_bound_index = - 1 ,
243+ )
255244
256245
257246@dataclass_transform (kw_only_default = True ) # field_specifiers=(entry, proxy))
@@ -265,10 +254,16 @@ class EntityMetadataCreator(ABCMeta):
265254 ```
266255 """
267256
268- m_defaults : ClassVar [dict [str , type [_DefaultEntityMetadataEntry | _ProxyEntityMetadataEntry ]]]
257+ m_defaults : ClassVar [
258+ dict [
259+ str ,
260+ DefaultEntityMetadataEntryDeclaration [Any ]
261+ | ProxyEntityMetadataEntryDeclaration [Any , EntityMetadataEntry [Any ]],
262+ ]
263+ ]
269264 m_index : ClassVar [dict [int , str ]]
270265 m_metadata : ClassVar [
271- dict [str , EntityMetadataEntry | ProxyEntityMetadataEntry ]
266+ dict [str , EntityMetadataEntry [ Any ] | ProxyEntityMetadataEntry [ Any , EntityMetadataEntry [ Any ]] ]
272267 ] # This is not an actual classvar, but I
273268 # Do not want it to appear in the __init__ signature
274269
@@ -309,7 +304,8 @@ def setup_class(cls: type[EntityMetadata]) -> None:
309304 if default is None :
310305 raise ValueError (f"Default value for { name } is not set. Use the entry() or proxy() functions." )
311306 # Check if we have a default entry
312- if isinstance (default , type ) and issubclass (default , _DefaultEntityMetadataEntry ):
307+ if isinstance (default , DefaultEntityMetadataEntryDeclaration ):
308+ default = cast (DefaultEntityMetadataEntryDeclaration [Any ], default )
313309 # Set the index of the entry
314310 default .m_index = current_index
315311
@@ -321,7 +317,8 @@ def setup_class(cls: type[EntityMetadata]) -> None:
321317
322318 # Increment the index
323319 current_index += 1
324- elif isinstance (default , type ) and issubclass (default , _ProxyEntityMetadataEntry ):
320+ elif isinstance (default , ProxyEntityMetadataEntryDeclaration ):
321+ default = cast (ProxyEntityMetadataEntryDeclaration [Any , EntityMetadataEntry [Any ]], default )
325322 # Find the bound entry
326323 if id (default .m_bound_entry ) not in bound_index :
327324 raise ValueError (f"Bound entry for { name } is not set." )
@@ -364,14 +361,16 @@ def __init__(self, *args: None, **kwargs: Any) -> None:
364361 raise ValueError (
365362 "EntityMetadata does not accept positional arguments. Specify all metadata entries by name."
366363 )
367- self .m_metadata : dict [str , EntityMetadataEntry | ProxyEntityMetadataEntry ] = {}
364+ self .m_metadata : dict [
365+ str , EntityMetadataEntry [Any ] | ProxyEntityMetadataEntry [Any , EntityMetadataEntry [Any ]]
366+ ] = {}
368367 for name , default in self .m_defaults .items ():
369- if issubclass (default , _DefaultEntityMetadataEntry ):
368+ if isinstance (default , DefaultEntityMetadataEntryDeclaration ):
370369 self .m_metadata [name ] = default .m_type (index = default .m_index , default = default .m_default , name = name )
371- elif issubclass (default , _ProxyEntityMetadataEntry ): # type: ignore # We want to check anyways
370+ elif isinstance (default , ProxyEntityMetadataEntryDeclaration ): # type: ignore # Check anyway
372371 # Bound entry
373372 bound_name = self .m_index [default .m_bound_index ]
374- bound_entry = cast (EntityMetadataEntry , self .m_metadata [bound_name ])
373+ bound_entry = cast (EntityMetadataEntry [ Any ] , self .m_metadata [bound_name ])
375374 self .m_metadata [name ] = default .m_type (bound_entry , * default .m_args , ** default .m_kwargs )
376375 else : # pragma: no cover
377376 raise ValueError (f"Invalid default value for { name } . Use the entry() or proxy() functions." ) # noqa: TRY004
@@ -383,13 +382,18 @@ def __init__(self, *args: None, **kwargs: Any) -> None:
383382
384383 @override
385384 def __setattr__ (self , name : str , value : Any ) -> None :
385+ """Any is used here because the type will be discovered statically by other means (dataclass_transform)."""
386386 if name != "m_metadata" and hasattr (self , "m_metadata" ) and name in self .m_metadata :
387387 self .m_metadata [name ].setter (value )
388388 else :
389389 super ().__setattr__ (name , value )
390390
391391 @override
392392 def __getattribute__ (self , name : str ) -> Any :
393+ """Get the value of the metadata entry.
394+
395+ .. seealso:: :meth:`__setattr__`
396+ """
393397 if name != "m_metadata" and hasattr (self , "m_metadata" ) and name in self .m_metadata :
394398 return self .m_metadata [name ].getter ()
395399 return super ().__getattribute__ (name )
0 commit comments