Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e95bedf

Browse files
[codex] Remove redundant constructor and generic fallback logic (#448)
## Summary This PR removes several stale fallback paths that were reconstructing generic and constructor metadata from runtime objects or annotations instead of using the canonical class/type information we already compute. Highlights: - remove constructor and method type-parameter fallback paths that inferred class generics from signatures - simplify partial-subscript constructor signature selection so it trusts the main constructor path - remove protocol-specific generic recovery branches that had become redundant after the class-registration cleanup - simplify enum fallback base recovery by using direct runtime base extraction instead of rebuilding arbitrary runtime annotations - share the concrete-signature helper and fix a Python 3.12 forward-reference regression introduced during that refactor ## Why These paths were layering recovery logic on top of richer abstractions we already have, especially around `TypeObject`-backed class metadata and `Value`-based generic analysis. That made the code harder to reason about and increased the chance of inconsistent behavior between runtime and synthetic paths. The result is less production code and a cleaner abstraction boundary: class type parameters come from class analysis, and helper code no longer tries to rediscover them from constructors, protocol runtime objects, or rebuilt annotations. ## User impact This is primarily an internal cleanup, but it also fixes a real Python 3.12 import-time regression in `signature.py` caused by a runtime-evaluated `MaybeSignature` annotation. ## Validation - `uv run --python 3.14 --extra tests pytest` - `uv run --python 3.12 python tools/conformance_ci.py --typing-repo ~/py/typing` - targeted regression checks during development around constructors, protocol/generic import-failure handling, `test_self.py`, and `test_signature.py` Conformance matches the current known-failure baseline after rebasing on latest `main`.
1 parent 82a9686 commit e95bedf

5 files changed

Lines changed: 73 additions & 283 deletions

File tree

pycroscope/attributes.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@
4343
safe_issubclass,
4444
)
4545
from .signature import (
46-
BoundMethodSignature,
4746
MaybeSignature,
4847
OverloadedSignature,
4948
ParameterKind,
5049
Signature,
50+
as_concrete_signature,
5151
)
5252
from .stacked_scopes import Composite
5353
from .type_object import (
@@ -1272,11 +1272,7 @@ def _signature_from_synthetic_attribute(
12721272
signature = ctx.signature_from_value(value)
12731273
if signature is None and isinstance(value, KnownValue):
12741274
signature = ctx.get_signature(value.val)
1275-
if isinstance(signature, BoundMethodSignature):
1276-
signature = signature.get_signature(ctx=ctx.get_can_assign_context())
1277-
if isinstance(signature, (Signature, OverloadedSignature)):
1278-
return signature
1279-
return None
1275+
return as_concrete_signature(signature, ctx.get_can_assign_context())
12801276

12811277

12821278
def _select_matching_synthetic_signature(

pycroscope/checker.py

Lines changed: 40 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import ast
88
import collections.abc
99
import enum
10-
import inspect
1110
import types
1211
from collections.abc import Callable, Generator, Iterable, Sequence
1312
from contextlib import contextmanager
@@ -54,6 +53,7 @@
5453
Signature,
5554
SigParameter,
5655
_promote_constructor_type_arg,
56+
as_concrete_signature,
5757
make_bound_method,
5858
)
5959
from .stacked_scopes import Composite
@@ -159,32 +159,6 @@ def _bound_method_self_value_from_typevars(typevars: TypeVarMap) -> Value | None
159159
return None
160160

161161

162-
def _infer_type_params_from_signature(signature: MaybeSignature) -> list[TypeParam]:
163-
seen: set[object] = set()
164-
inferred: list[TypeParam] = []
165-
166-
def _record(value: Value) -> None:
167-
for type_param in iter_type_params_in_value(value):
168-
if type_param.typevar in seen:
169-
continue
170-
seen.add(type_param.typevar)
171-
inferred.append(type_param)
172-
173-
def _walk(sig: MaybeSignature) -> None:
174-
if isinstance(sig, OverloadedSignature):
175-
for sub_sig in sig.signatures:
176-
_walk(sub_sig)
177-
return
178-
if not isinstance(sig, Signature):
179-
return
180-
for parameter in sig.parameters.values():
181-
_record(parameter.annotation)
182-
_record(sig.return_value)
183-
184-
_walk(signature)
185-
return inferred
186-
187-
188162
def _apply_type_parameter_defaults(
189163
type_params: Sequence[TypeParam], checker: "Checker"
190164
) -> list[Value]:
@@ -412,14 +386,7 @@ def _signature_from_synthetic_attribute(
412386
signature = ctx.signature_from_value(value)
413387
if signature is None and isinstance(value, KnownValue):
414388
signature = ctx.get_signature(value.val)
415-
can_assign_ctx = ctx.get_can_assign_context()
416-
if isinstance(signature, BoundMethodSignature):
417-
if can_assign_ctx is None:
418-
return None
419-
signature = signature.get_signature(ctx=can_assign_ctx)
420-
if isinstance(signature, (Signature, OverloadedSignature)):
421-
return signature
422-
return None
389+
return as_concrete_signature(signature, ctx.get_can_assign_context())
423390

424391

425392
def _synthetic_descriptor_set_type(descriptor: Value, ctx: AttrContext) -> Value | None:
@@ -959,15 +926,6 @@ def _get_unbound_method_owner(self, value: UnboundMethodValue) -> type | None:
959926
return root.val
960927
return None
961928

962-
def _as_concrete_signature(
963-
self, signature: MaybeSignature
964-
) -> ConcreteSignature | None:
965-
if isinstance(signature, BoundMethodSignature):
966-
return signature.get_signature(ctx=self)
967-
if isinstance(signature, (Signature, OverloadedSignature)):
968-
return signature
969-
return None
970-
971929
def _is_uninformative_constructor_signature(
972930
self, signature: ConcreteSignature
973931
) -> bool:
@@ -1134,7 +1092,7 @@ def _bind_constructor_like_signature(
11341092
ctx=self,
11351093
self_annotation_value=self_annotation_value,
11361094
)
1137-
concrete = self._as_concrete_signature(signature)
1095+
concrete = as_concrete_signature(signature, self)
11381096
if concrete is None:
11391097
return None
11401098
if self_annotation_value is None:
@@ -1469,8 +1427,8 @@ def _runtime_init_self_annotation_matches(
14691427
init_method = safe_getattr(origin, "__init__", None)
14701428
if init_method is None:
14711429
return True
1472-
init_sig = self._as_concrete_signature(
1473-
self.arg_spec_cache.get_argspec(init_method)
1430+
init_sig = as_concrete_signature(
1431+
self.arg_spec_cache.get_argspec(init_method), self
14741432
)
14751433
if init_sig is None:
14761434
return True
@@ -1504,40 +1462,12 @@ def _runtime_new_cls_annotation_matches(
15041462
new_method = safe_getattr(origin, "__new__", None)
15051463
if new_method is None:
15061464
return True
1507-
if isinstance(new_method, types.FunctionType):
1508-
runtime_annotations = safe_getattr(new_method, "__annotations__", None)
1509-
try:
1510-
runtime_sig = inspect.signature(new_method)
1511-
except (TypeError, ValueError):
1512-
runtime_sig = None
1513-
if (
1514-
isinstance(runtime_annotations, dict)
1515-
and runtime_sig is not None
1516-
and runtime_sig.parameters
1517-
):
1518-
first_parameter_name = next(iter(runtime_sig.parameters.values())).name
1519-
cls_runtime_annotation = runtime_annotations.get(first_parameter_name)
1520-
if cls_runtime_annotation is not None:
1521-
cls_annotation = type_from_runtime(
1522-
cls_runtime_annotation,
1523-
visitor=self,
1524-
globals=safe_getattr(new_method, "__globals__", None),
1525-
suppress_errors=True,
1526-
).substitute_typevars(typevar_map)
1527-
cls_annotation_root = replace_fallback(cls_annotation)
1528-
if not isinstance(cls_annotation_root, AnyValue):
1529-
return _matches_constructor_receiver_annotation(
1530-
cls_annotation,
1531-
class_type_value,
1532-
self,
1533-
enforce_nongeneric_match=True,
1534-
)
15351465
new_sig: MaybeSignature = self._get_runtime_overloaded_method_signature(
15361466
origin, "__new__"
15371467
)
15381468
if new_sig is None:
15391469
new_sig = self.arg_spec_cache.get_argspec(new_method)
1540-
concrete_new_sig = self._as_concrete_signature(new_sig)
1470+
concrete_new_sig = as_concrete_signature(new_sig, self)
15411471
if concrete_new_sig is None:
15421472
return True
15431473
signatures = (
@@ -1558,31 +1488,6 @@ def _runtime_new_cls_annotation_matches(
15581488
return True
15591489
return not checked
15601490

1561-
def _synthetic_init_self_annotation_matches(
1562-
self,
1563-
synthetic_class: SyntheticClassObjectValue,
1564-
*,
1565-
instance_type: Value,
1566-
get_return_override: Callable[[MaybeSignature], Value | None],
1567-
get_call_attribute: Callable[[Value], Value] | None,
1568-
) -> bool:
1569-
init_symbol = self.make_type_object(
1570-
synthetic_class.class_type.typ
1571-
).get_declared_symbol("__init__")
1572-
has_direct_init = init_symbol is not None and init_symbol.is_method
1573-
init_sig = self._get_synthetic_constructor_method_signature(
1574-
synthetic_class,
1575-
"__init__",
1576-
use_direct_method=has_direct_init,
1577-
bound_self_value=instance_type,
1578-
self_annotation_value=instance_type,
1579-
get_return_override=get_return_override,
1580-
get_call_attribute=get_call_attribute,
1581-
)
1582-
if init_sig is None:
1583-
return True
1584-
return not _is_incompatible_constructor_signature(init_sig)
1585-
15861491
def _synthetic_new_cls_annotation_matches(
15871492
self,
15881493
synthetic_class: SyntheticClassObjectValue,
@@ -1607,7 +1512,7 @@ def _synthetic_new_cls_annotation_matches(
16071512
get_return_override=get_return_override,
16081513
get_call_attribute=get_call_attribute,
16091514
)
1610-
concrete_sig = self._as_concrete_signature(method_sig)
1515+
concrete_sig = as_concrete_signature(method_sig, self)
16111516
if concrete_sig is None:
16121517
return True
16131518
signatures = (
@@ -1699,8 +1604,8 @@ def _get_synthetic_constructor_method_signature(
16991604
get_return_override=get_return_override,
17001605
get_call_attribute=get_call_attribute,
17011606
)
1702-
resolved_concrete = self._as_concrete_signature(resolved_sig)
1703-
direct_concrete = self._as_concrete_signature(method_sig)
1607+
resolved_concrete = as_concrete_signature(resolved_sig, self)
1608+
direct_concrete = as_concrete_signature(method_sig, self)
17041609
if isinstance(resolved_concrete, OverloadedSignature):
17051610
method_sig = resolved_sig
17061611
elif resolved_concrete is not None and (
@@ -1892,8 +1797,8 @@ def _get_synthetic_constructor_signature(
18921797
isinstance(value.class_type.typ, type)
18931798
and safe_issubclass(value.class_type.typ, enum.Enum)
18941799
and isinstance(
1895-
enum_argspec := self._as_concrete_signature(
1896-
self.arg_spec_cache.get_argspec(value.class_type.typ)
1800+
enum_argspec := as_concrete_signature(
1801+
self.arg_spec_cache.get_argspec(value.class_type.typ), self
18971802
),
18981803
(Signature, OverloadedSignature),
18991804
)
@@ -2008,7 +1913,7 @@ def _get_synthetic_constructor_signature(
20081913
get_return_override=get_return_override,
20091914
get_call_attribute=get_call_attribute,
20101915
)
2011-
concrete = self._as_concrete_signature(call_sig)
1916+
concrete = as_concrete_signature(call_sig, self)
20121917
if (
20131918
concrete is not None
20141919
and not self._is_uninformative_constructor_signature(concrete)
@@ -2344,7 +2249,7 @@ def signature_from_value(
23442249
)
23452250
)
23462251
concrete_argspec = (
2347-
self._as_concrete_signature(argspec)
2252+
as_concrete_signature(argspec, self)
23482253
if argspec is not None
23492254
else None
23502255
)
@@ -2668,21 +2573,24 @@ def _signature_from_partial_subscript(
26682573
)
26692574
if origin_argspec is None:
26702575
origin_argspec = self.arg_spec_cache.get_argspec(root.val)
2671-
elif self._runtime_has_explicit_new_return_annotation(root.val):
2672-
runtime_constructor_sig = self._get_runtime_constructor_signature(
2673-
root.val
2674-
)
2675-
if runtime_constructor_sig is not None:
2676-
origin_argspec = runtime_constructor_sig
2677-
preserve_exact_return = True
2576+
preserve_exact_return = self._runtime_has_explicit_new_return_annotation(
2577+
root.val
2578+
)
26782579
elif isinstance(root, SyntheticClassObjectValue):
26792580
synthetic_root = root
26802581
class_type = root.class_type.typ
2681-
origin_argspec = self.signature_from_value(
2582+
origin_argspec = self._get_synthetic_constructor_signature(
26822583
root,
26832584
get_return_override=get_return_override,
26842585
get_call_attribute=get_call_attribute,
2586+
apply_default_type_args=False,
26852587
)
2588+
if origin_argspec is None:
2589+
origin_argspec = self.signature_from_value(
2590+
root,
2591+
get_return_override=get_return_override,
2592+
get_call_attribute=get_call_attribute,
2593+
)
26862594
if self._synthetic_has_explicit_new_return_annotation(
26872595
root,
26882596
get_return_override=get_return_override,
@@ -2691,29 +2599,11 @@ def _signature_from_partial_subscript(
26912599
preserve_exact_return = True
26922600
else:
26932601
return None
2694-
if synthetic_root is not None:
2695-
synthetic_origin_argspec = self._get_synthetic_constructor_signature(
2696-
synthetic_root,
2697-
get_return_override=get_return_override,
2698-
get_call_attribute=get_call_attribute,
2699-
apply_default_type_args=False,
2700-
)
2701-
if synthetic_origin_argspec is not None and (
2702-
origin_argspec is None
2703-
or not isinstance(origin_argspec, (Signature, OverloadedSignature))
2704-
or self._is_uninformative_constructor_signature(origin_argspec)
2705-
or not self._is_uninformative_constructor_signature(
2706-
synthetic_origin_argspec
2707-
)
2708-
):
2709-
origin_argspec = synthetic_origin_argspec
27102602
if origin_argspec is None:
27112603
return None
27122604
if class_type is None:
27132605
return origin_argspec
27142606
type_params = self.get_type_parameters(class_type)
2715-
if not type_params:
2716-
type_params = list(_infer_type_params_from_signature(origin_argspec))
27172607
explicit_member_values = [
27182608
type_from_value(member, self, value.node, suppress_errors=True)
27192609
for member in value.members
@@ -2793,16 +2683,26 @@ def _signature_from_partial_subscript(
27932683
receiver_instance_type = GenericValue(class_type, receiver_member_values)
27942684
else:
27952685
receiver_instance_type = TypedValue(class_type)
2796-
if (
2797-
synthetic_root is not None
2798-
and not self._synthetic_init_self_annotation_matches(
2686+
if synthetic_root is not None:
2687+
init_symbol = self.make_type_object(
2688+
synthetic_root.class_type.typ
2689+
).get_declared_symbol("__init__")
2690+
has_direct_init = init_symbol is not None and init_symbol.is_method
2691+
init_sig = self._get_synthetic_constructor_method_signature(
27992692
synthetic_root,
2800-
instance_type=receiver_instance_type,
2693+
"__init__",
2694+
use_direct_method=has_direct_init,
2695+
bound_self_value=receiver_instance_type,
2696+
self_annotation_value=receiver_instance_type,
28012697
get_return_override=get_return_override,
28022698
get_call_attribute=get_call_attribute,
28032699
)
2804-
):
2805-
return _make_incompatible_constructor_signature(specialized_instance_type)
2700+
if init_sig is not None and _is_incompatible_constructor_signature(
2701+
init_sig
2702+
):
2703+
return _make_incompatible_constructor_signature(
2704+
specialized_instance_type
2705+
)
28062706
if (
28072707
synthetic_root is not None
28082708
and not self._synthetic_new_cls_annotation_matches(

0 commit comments

Comments
 (0)