@@ -1105,10 +1105,20 @@ class RewritePybind11EnumValueRepr(IParser):
11051105 def __init__ (self ):
11061106 super ().__init__ ()
11071107 self ._pybind11_enum_locations : dict [re .Pattern , str ] = {}
1108+ self ._rewrite_enum_current_module : QualifiedName = QualifiedName ()
11081109
11091110 def set_pybind11_enum_locations (self , locations : dict [re .Pattern , str ]):
11101111 self ._pybind11_enum_locations = locations
11111112
1113+ def handle_module (
1114+ self , path : QualifiedName , module : types .ModuleType
1115+ ) -> Module | None :
1116+ old = self ._rewrite_enum_current_module
1117+ self ._rewrite_enum_current_module = path
1118+ result = super ().handle_module (path , module )
1119+ self ._rewrite_enum_current_module = old
1120+ return result
1121+
11121122 def parse_value_str (self , value : str ) -> Value | InvalidExpression :
11131123 value = value .strip ()
11141124 match = self ._pybind11_enum_pattern .match (value )
@@ -1120,9 +1130,39 @@ def parse_value_str(self, value: str) -> Value | InvalidExpression:
11201130 continue
11211131 enum_class = self .parse_annotation_str (f"{ prefix } .{ enum_class_str } " )
11221132 if isinstance (enum_class , ResolvedType ):
1133+ if self ._is_ancestor_attr_path (enum_class .name ):
1134+ # Issue #304: a default expression rooted at a
1135+ # proper ancestor package of the current module
1136+ # (e.g. ``demo.NativeColor.Red`` inside the
1137+ # ``demo._bindings.enum`` stub) is evaluated at
1138+ # stub-import time. The ancestor's namespace is
1139+ # populated mid-``__init__`` by wildcard re-
1140+ # exports, so if the stub is imported during
1141+ # that init the lookup fails with a circular-
1142+ # import ``AttributeError``. The argument's
1143+ # type annotation is already rendered in the
1144+ # short form, so the enum class is in scope.
1145+ return Value (
1146+ repr = f"{ enum_class_str } .{ entry } " ,
1147+ is_print_safe = True ,
1148+ )
11231149 return Value (repr = f"{ enum_class .name } .{ entry } " , is_print_safe = True )
11241150 return super ().parse_value_str (value )
11251151
1152+ def _is_ancestor_attr_path (self , name : QualifiedName ) -> bool :
1153+ """Would evaluating ``name`` cross an ancestor package attribute?
1154+
1155+ The enum class itself is ``name[-1]``; the segments preceding
1156+ it form the containing path. Return True when that containing
1157+ path is a *proper* ancestor (strict prefix) of the module
1158+ currently being processed, and False otherwise.
1159+ """
1160+ container = name [:- 1 ]
1161+ if len (container ) == 0 :
1162+ return False
1163+ current = self ._rewrite_enum_current_module
1164+ return len (current ) > len (container ) and current [: len (container )] == container
1165+
11261166 def report_error (self , error : ParserError ) -> None :
11271167 if isinstance (error , InvalidExpressionError ):
11281168 match = self ._pybind11_enum_pattern .match (error .expression )
0 commit comments