Skip to content

Commit 14b3caa

Browse files
committed
Fix: Print as String
1 parent 3b413b2 commit 14b3caa

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

  • pybind11_stubgen/parser/mixins

pybind11_stubgen/parser/mixins/fix.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)