@@ -5313,16 +5313,82 @@ def _nominal_class_name_suffixes(class_name: str) -> tuple[str, ...]:
53135313 return tuple(("".join(tokens[index:]) for index in range(len(tokens) - 1)))
53145314
53155315
5316+ _REGISTERED_NOMINAL_AUTHORITY_ASSIGNMENTS = frozenset(
5317+ {
5318+ "__registry_key__",
5319+ "__enum_member_attr__",
5320+ "__enum_label_attr__",
5321+ }
5322+ )
5323+
5324+
5325+ def _module_class_nodes(module: ParsedModule) -> dict[str, ast.ClassDef]:
5326+ return {
5327+ node.name: node
5328+ for node in _walk_nodes(module.module)
5329+ if isinstance(node, ast.ClassDef)
5330+ }
5331+
5332+
5333+ def _class_family_nodes_in_module(
5334+ class_nodes: Mapping[str, ast.ClassDef],
5335+ node: ast.ClassDef,
5336+ ) -> tuple[ast.ClassDef, ...]:
5337+ """Return the class and same-module ancestors known to static analysis."""
5338+ family_nodes: list[ast.ClassDef] = []
5339+ seen: set[str] = set()
5340+ stack = [node]
5341+ while stack:
5342+ current = stack.pop()
5343+ if current.name in seen:
5344+ continue
5345+ seen.add(current.name)
5346+ family_nodes.append(current)
5347+ stack.extend(
5348+ (
5349+ class_nodes[base_name]
5350+ for base_name in CLASS_NODE_AUTHORITY.declared_base_names(current)
5351+ if base_name in class_nodes and base_name not in seen
5352+ )
5353+ )
5354+ return tuple(family_nodes)
5355+
5356+
5357+ def _is_registered_nominal_authority_node(node: ast.ClassDef) -> bool:
5358+ assignments = CLASS_NODE_AUTHORITY.direct_assignments(node)
5359+ return (
5360+ HELPER_SUPPORT_PROJECTION_AUTHORITY.declares_autoregister_meta(node)
5361+ or bool(_REGISTERED_NOMINAL_AUTHORITY_ASSIGNMENTS & set(assignments))
5362+ )
5363+
5364+
5365+ def _has_registered_nominal_authority_ancestor(
5366+ class_nodes: Mapping[str, ast.ClassDef],
5367+ node: ast.ClassDef,
5368+ ) -> bool:
5369+ """Return True when classvar leaves are backed by a real registry family."""
5370+ return any(
5371+ (
5372+ family_node is not node
5373+ and _is_registered_nominal_authority_node(family_node)
5374+ )
5375+ for family_node in _class_family_nodes_in_module(class_nodes, node)
5376+ )
5377+
5378+
53165379def _metadata_only_class_family_candidates(
53175380 module: ParsedModule,
53185381) -> tuple[MetadataOnlyClassFamilyCandidate, ...]:
5382+ class_nodes = _module_class_nodes(module)
53195383 grouped: dict[
53205384 str,
53215385 list[tuple[ast.ClassDef, tuple[str, ...], tuple[str, ...], int]],
53225386 ] = defaultdict(list)
53235387 for node in _walk_nodes(module.module):
53245388 if not isinstance(node, ast.ClassDef) or node.decorator_list:
53255389 continue
5390+ if _has_registered_nominal_authority_ancestor(class_nodes, node):
5391+ continue
53265392 assigned_names = (
53275393 HELPER_SYNTAX_PROJECTION_AUTHORITY.metadata_only_class_assignment_names(
53285394 node
@@ -5602,11 +5668,14 @@ def _classvar_only_sibling_leaf_candidates_for_class(
56025668def _classvar_only_sibling_leaf_candidates(
56035669 module: ParsedModule,
56045670) -> tuple[DeclarativeFamilyLeafCandidate, ...]:
5671+ class_nodes = _module_class_nodes(module)
56055672 return CANDIDATE_COLLECTION_AUTHORITY.ast_node_candidates(
56065673 module,
56075674 module.module,
56085675 ast.ClassDef,
5609- _classvar_only_sibling_leaf_candidates_for_class,
5676+ lambda parsed_module, node: ()
5677+ if _has_registered_nominal_authority_ancestor(class_nodes, node)
5678+ else _classvar_only_sibling_leaf_candidates_for_class(parsed_module, node),
56105679 )
56115680
56125681
0 commit comments