@@ -191,7 +191,7 @@ def __init__(
191191 self .indent = ""
192192
193193 # Set of {x}, where {x} corresponds to to `import {x}`
194- self .imports : Set [str ] = set ()
194+ self .imports : Dict [str , bool ] = {}
195195 # dictionary of x->(y,z) for `from {x} import {y} as {z}`
196196 # if {z} is None, then it shortens to `from {x} import {y}`
197197 self .from_imports : Dict [str , Set [Tuple [str , str | None ]]] = defaultdict (set )
@@ -201,29 +201,46 @@ def __init__(
201201 # Comments
202202 self .source_code_info_by_scl = {tuple (location .path ): location for location in fd .source_code_info .location }
203203
204- def _import (self , path : str , name : str ) -> str :
204+ @property
205+ def _deprecated_name (self ) -> str :
206+ return "_deprecated"
207+
208+ @property
209+ def _typing_extensions_name (self ) -> str :
210+ return "_typing_extensions"
211+
212+ def _import_alias (self , path : str ) -> str :
213+ """import as prefixed with underscore to avoid conflicts with message/enum names"""
214+ return f"_{ path .replace ('.' , '_' )} "
215+
216+ def _import (self , path : str , name : str , * , alias : bool = True ) -> str :
205217 """Imports a stdlib path and returns a handle to it
206218 eg. self._import("typing", "Literal") -> "Literal"
219+
220+ If alias is true, then it will prefix the import with an underscore to prevent conflicts with builtin names
207221 """
208222 if path == "typing_extensions" :
209- stabilization = {"TypeAlias" : (3 , 10 ), "TypeVar" : (3 , 13 ), "type_check_only" : (3 , 12 )}
223+ stabilization = {"TypeAlias" : (3 , 10 ), "TypeVar" : (3 , 13 ), "type_check_only" : (3 , 12 ), "Self" : ( 3 , 11 ) }
210224 assert name in stabilization
211225 if not self .typing_extensions_min or self .typing_extensions_min < stabilization [name ]:
212226 self .typing_extensions_min = stabilization [name ]
213- return "typing_extensions ." + name
227+ return self . _typing_extensions_name + " ." + name
214228
215229 if path == "warnings" and name == "deprecated" :
216230 if not self .deprecated_min or self .deprecated_min < (3 , 11 ):
217231 self .deprecated_min = (3 , 13 )
218- return name
232+ return self . _deprecated_name
219233
220234 imp = path .replace ("/" , "." )
221235 if self .readable_stubs :
222236 self .from_imports [imp ].add ((name , None ))
223237 return name
224238 else :
225- self .imports .add (imp )
226- return imp + "." + name
239+ self .imports [imp ] = alias
240+ return (self ._import_alias (imp ) if alias else imp ) + "." + name
241+
242+ def _property (self ) -> str :
243+ return f"@{ self ._import ('builtins' , 'property' )} "
227244
228245 def _import_message (self , name : str ) -> str :
229246 """Import a referenced message and return a handle"""
@@ -252,7 +269,7 @@ def _import_message(self, name: str) -> str:
252269 # Not in file. Must import
253270 # Python generated code ignores proto packages, so the only relevant factor is
254271 # whether it is in the file or not.
255- import_name = self ._import (message_fd .name [:- 6 ].replace ("-" , "_" ) + "_pb2" , split [0 ])
272+ import_name = self ._import (message_fd .name [:- 6 ].replace ("-" , "_" ) + "_pb2" , split [0 ], alias = False )
256273
257274 remains = "." .join (split [1 :])
258275 if not remains :
@@ -427,7 +444,7 @@ def write_enums(
427444 self ._builtin ("int" ),
428445 )
429446 # Alias to the classic shorter definition "V"
430- wl ("V: {} = ValueType" , self ._import ("typing_extensions" , "TypeAlias" ))
447+ wl ("V: {} = ValueType # noqa: Y015 " , self ._import ("typing_extensions" , "TypeAlias" ))
431448 wl ("" )
432449 wl (
433450 "class {}({}[{}], {}):" ,
@@ -467,7 +484,7 @@ def write_enums(
467484 scl + [d .EnumDescriptorProto .VALUE_FIELD_NUMBER ],
468485 )
469486 if prefix == "" and not self .readable_stubs :
470- wl (f"{ _mangle_global_identifier (class_name )} : { self ._import ('typing_extensions' , 'TypeAlias' )} = { class_name } " )
487+ wl (f"{ _mangle_global_identifier (class_name )} : { self ._import ('typing_extensions' , 'TypeAlias' )} = { class_name } # noqa: Y015 " )
471488 wl ("" )
472489
473490 def write_messages (
@@ -544,7 +561,7 @@ def write_messages(
544561 if not (is_scalar (field ) and field .label != d .FieldDescriptorProto .LABEL_REPEATED ):
545562 # r/o Getters for non-scalar fields and scalar-repeated fields
546563 scl_field = scl + [d .DescriptorProto .FIELD_FIELD_NUMBER , idx ]
547- wl ("@property" )
564+ wl (self . _property () )
548565 body = " ..." if not self ._has_comments (scl_field ) else ""
549566 wl (f"def { field .name } (self) -> { field_type } :{ body } " )
550567 if self ._has_comments (scl_field ):
@@ -580,7 +597,7 @@ def write_messages(
580597
581598 if prefix == "" and not self .readable_stubs :
582599 wl ("" )
583- wl (f"{ _mangle_global_identifier (class_name )} : { self ._import ('typing_extensions' , 'TypeAlias' )} = { class_name } " )
600+ wl (f"{ _mangle_global_identifier (class_name )} : { self ._import ('typing_extensions' , 'TypeAlias' )} = { class_name } # noqa: Y015 " )
584601 wl ("" )
585602
586603 def write_stringly_typed_fields (self , desc : d .DescriptorProto ) -> None :
@@ -604,29 +621,29 @@ def write_stringly_typed_fields(self, desc: d.DescriptorProto) -> None:
604621 return
605622
606623 if hf_fields :
607- wl ("_HasFieldArgType: {} = {}[{}]" , self ._import ("typing_extensions" , "TypeAlias" ), self ._import ("typing" , "Literal" ), hf_fields_text )
624+ wl ("_HasFieldArgType: {} = {}[{}] # noqa: Y015 " , self ._import ("typing_extensions" , "TypeAlias" ), self ._import ("typing" , "Literal" ), hf_fields_text )
608625 wl (
609626 "def HasField(self, field_name: _HasFieldArgType) -> {}: ..." ,
610627 self ._builtin ("bool" ),
611628 )
612629 if cf_fields :
613- wl ("_ClearFieldArgType: {} = {}[{}]" , self ._import ("typing_extensions" , "TypeAlias" ), self ._import ("typing" , "Literal" ), cf_fields_text )
630+ wl ("_ClearFieldArgType: {} = {}[{}] # noqa: Y015 " , self ._import ("typing_extensions" , "TypeAlias" ), self ._import ("typing" , "Literal" ), cf_fields_text )
614631 wl (
615632 "def ClearField(self, field_name: _ClearFieldArgType) -> None: ..." ,
616633 )
617634
618635 # Write type aliases first so overloads are not interrupted
619636 for wo_field , members in sorted (wo_fields .items ()):
620637 wl (
621- "_WhichOneofReturnType_{}: {} = {}[{}]" ,
638+ "_WhichOneofReturnType_{}: {} = {}[{}] # noqa: Y015 " ,
622639 wo_field ,
623640 self ._import ("typing_extensions" , "TypeAlias" ),
624641 self ._import ("typing" , "Literal" ),
625642 # Returns `str`
626643 ", " .join (f'"{ m } "' for m in members ),
627644 )
628645 wl (
629- "_WhichOneofArgType_{}: {} = {}[{}]" ,
646+ "_WhichOneofArgType_{}: {} = {}[{}] # noqa: Y015 " ,
630647 wo_field ,
631648 self ._import ("typing_extensions" , "TypeAlias" ),
632649 self ._import ("typing" , "Literal" ),
@@ -751,7 +768,7 @@ def _import_casttype(self, casttype: str) -> str:
751768 split = casttype .split ("." )
752769 assert len (split ) == 2 , "mypy_protobuf.[casttype,keytype,valuetype] is expected to be of format path/to/file.TypeInFile"
753770 pkg = split [0 ].replace ("/" , "." )
754- return self ._import (pkg , split [1 ])
771+ return self ._import (pkg , split [1 ], alias = False )
755772
756773 def _map_key_value_types (
757774 self ,
@@ -983,7 +1000,7 @@ def write_grpc_services(
9831000 wl (
9841001 "def __new__(cls, channel: {}) -> {}: ..." ,
9851002 self ._import ("grpc" , "Channel" ),
986- class_name ,
1003+ self . _import ( "typing_extensions" , "Self" ) ,
9871004 )
9881005
9891006 # Write async overload
@@ -1168,21 +1185,25 @@ def write(self) -> str:
11681185 self .from_imports [reexport_imp ].update ((n , n ) for n in names )
11691186
11701187 if self .typing_extensions_min or self .deprecated_min :
1171- self .imports .add ("sys" )
1172- for pkg in sorted (self .imports ):
1173- self ._write_line (f"import { pkg } " )
1188+ # Special case for `sys` as it is needed for version checks
1189+ self .imports ["sys" ] = False
1190+ for pkg , dedot in sorted (self .imports .items ()):
1191+ if dedot :
1192+ self ._write_line (f"import { pkg } as { self ._import_alias (pkg )} " )
1193+ else :
1194+ self ._write_line (f"import { pkg } " )
11741195 if self .typing_extensions_min :
11751196 self ._write_line ("" )
11761197 self ._write_line (f"if sys.version_info >= { self .typing_extensions_min } :" )
1177- self ._write_line (" import typing as typing_extensions " )
1198+ self ._write_line (f " import typing as { self . _typing_extensions_name } " )
11781199 self ._write_line ("else:" )
1179- self ._write_line (" import typing_extensions" )
1200+ self ._write_line (f " import typing_extensions as { self . _typing_extensions_name } " )
11801201 if self .deprecated_min :
11811202 self ._write_line ("" )
11821203 self ._write_line (f"if sys.version_info >= { self .deprecated_min } :" )
1183- self ._write_line (" from warnings import deprecated" )
1204+ self ._write_line (f " from warnings import deprecated as { self . _deprecated_name } " )
11841205 self ._write_line ("else:" )
1185- self ._write_line (" from typing_extensions import deprecated" )
1206+ self ._write_line (f " from typing_extensions import deprecated as { self . _deprecated_name } " )
11861207
11871208 for pkg , items in sorted (self .from_imports .items ()):
11881209 self ._write_line (f"from { pkg } import (" )
0 commit comments