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

Skip to content

Commit 2b14fc9

Browse files
New descriptor implementation (#465)
1 parent bb75c58 commit 2b14fc9

17 files changed

Lines changed: 868 additions & 743 deletions

pycroscope/asynq_checker.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,13 @@ def get_pure_async_equivalent(value: Value) -> str:
248248
return f"{_stringify_obj(value.val)}.asynq"
249249
if isinstance(value, UnboundMethodValue):
250250
return _stringify_async_fn(
251-
UnboundMethodValue(value.attr_name, value.composite, "asynq")
251+
UnboundMethodValue(
252+
value.attr_name,
253+
value.composite,
254+
"asynq",
255+
typevars=value.typevars,
256+
owner=value.owner,
257+
)
252258
)
253259
raise AssertionError(f"cannot get pure async equivalent of {value}")
254260

pycroscope/attributes.py

Lines changed: 53 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,10 @@
5252
from .stacked_scopes import Composite
5353
from .type_object import (
5454
AttributePolicy,
55-
MroValue,
5655
TypeObject,
5756
TypeObjectAttribute,
5857
_class_key_from_value,
59-
_get_attribute_value_from_symbol,
6058
_get_cached_property_return_type,
61-
_is_property_marker_value,
62-
_specialize_symbol_for_owner,
63-
class_keys_match,
6459
normalize_synthetic_descriptor_attribute,
6560
)
6661
from .value import (
@@ -476,69 +471,26 @@ def _super_thisclass_key(value: Value) -> type | str | None:
476471
return None
477472

478473

479-
def _super_mro_values(
480-
receiver_value: TypedValue, ctx: CanAssignContext
481-
) -> Sequence[MroValue]:
482-
# TODO: switch to just using the MRO; that currently doesn't work because it gets set too late
483-
if isinstance(receiver_value.typ, type):
484-
return [TypedValue(base) for base in receiver_value.typ.__mro__]
485-
return [
486-
entry.get_mro_value() for entry in receiver_value.get_type_object(ctx).get_mro()
487-
]
488-
489-
490-
# TODO: in principle this should be doable with TypeObject.get_attribute if we add a flag
491-
# that says to skip MRO elements up to a certain point.
492474
def _get_attribute_from_super_value(super_value: SuperValue, ctx: AttrContext) -> Value:
493475
if super_value.selfobj is None:
494476
return AnyValue(AnySource.inference)
495477
receiver_value, is_class_access = _super_receiver_type_value(super_value.selfobj)
496-
policy = AttributePolicy(
497-
receiver=receiver_value or AnyValue(AnySource.inference),
498-
on_class=is_class_access,
499-
)
500478
thisclass_key = _super_thisclass_key(super_value.thisclass)
501-
can_assign_ctx = ctx.get_can_assign_context()
502479
if receiver_value is None or thisclass_key is None:
503480
return AnyValue(AnySource.inference)
504481

505-
receiver_tobj = receiver_value.get_type_object(can_assign_ctx)
506-
saw_thisclass = False
507-
for mro_value in _super_mro_values(receiver_value, can_assign_ctx):
508-
if isinstance(mro_value, AnyValue):
509-
continue
510-
owner_key = _class_key_from_value(mro_value)
511-
if owner_key is None:
512-
continue
513-
if not saw_thisclass:
514-
if class_keys_match(owner_key, thisclass_key):
515-
saw_thisclass = True
516-
continue
517-
owner_tobj = mro_value.get_type_object(can_assign_ctx)
518-
symbol = owner_tobj.get_declared_symbol(ctx.attr)
519-
if symbol is not None:
520-
symbol = _specialize_symbol_for_owner(
521-
receiver_tobj, owner_tobj, symbol, can_assign_ctx, policy
522-
)
523-
result = _get_attribute_value_from_symbol(
524-
symbol,
525-
can_assign_ctx,
526-
on_class=is_class_access and not symbol.is_method,
527-
receiver_value=receiver_value,
528-
)
529-
if (
530-
is_class_access
531-
and symbol.property_info is not None
532-
and (
533-
result is UNINITIALIZED_VALUE
534-
or not _is_property_marker_value(result)
535-
)
536-
):
537-
result = TypedValue(property)
538-
result = set_self(result, receiver_value, owner_tobj.typ)
539-
ctx.record_usage(super, result)
540-
return result
541-
return UNINITIALIZED_VALUE
482+
receiver_tobj = receiver_value.get_type_object(ctx.get_can_assign_context())
483+
policy = AttributePolicy(
484+
receiver=receiver_value,
485+
on_class=is_class_access,
486+
anchor=thisclass_key,
487+
# TODO: Maybe shouldn't be necessary, but without this some methods get inferred wrong.
488+
prefer_symbolic=True,
489+
)
490+
attr = receiver_tobj.get_attribute(ctx.attr, policy)
491+
if attr is None:
492+
return UNINITIALIZED_VALUE
493+
return attr.value
542494

543495

544496
def _get_attribute_from_type_alias(value: TypeAliasValue, ctx: AttrContext) -> Value:
@@ -736,13 +688,6 @@ def _get_typed_instance_lookup_receiver(ctx: AttrContext) -> Value | None:
736688
return None
737689

738690

739-
def _get_instance_lookup_receiver(ctx: AttrContext) -> Value | None:
740-
self_value = ctx.get_self_value()
741-
if _contains_self_typevar(self_value):
742-
return self_value
743-
return _get_typed_instance_lookup_receiver(ctx)
744-
745-
746691
def _get_attribute_from_synthetic_class(
747692
class_key: type | str, self_value: Value, ctx: AttrContext
748693
) -> Value:
@@ -1192,29 +1137,29 @@ def _get_attribute_from_typed(
11921137
types.BuiltinFunctionType,
11931138
}:
11941139
return GenericValue(dict, [TypedValue(str), AnyValue(AnySource.explicit)])
1195-
receiver_value = _get_instance_lookup_receiver(ctx)
1196-
if receiver_value is not None:
1197-
can_assign_ctx = ctx.get_can_assign_context()
1198-
attribute = _get_type_object_attribute(
1199-
can_assign_ctx.make_type_object(typ),
1200-
ctx.attr,
1201-
ctx,
1202-
on_class=False,
1203-
receiver_value=receiver_value,
1140+
can_assign_ctx = ctx.get_can_assign_context()
1141+
attribute = _get_type_object_attribute(
1142+
can_assign_ctx.make_type_object(typ),
1143+
ctx.attr,
1144+
ctx,
1145+
on_class=False,
1146+
receiver_value=ctx.root_composite.value,
1147+
)
1148+
# Adding "if attribute is None: return UNINITIALIZED_VALUE" here breaks two tests:
1149+
# pycroscope/test_attributes.py::TestAttributes::test_attrs
1150+
# (missing attrs support in type_object.py?)
1151+
# TestImportFailureHandling::test_explicit_type_alias_uses_runtime_attribute_semantics
1152+
# (some weirdness about how we represent type aliases?)
1153+
if attribute is not None:
1154+
resolved_instance = _maybe_use_resolved_typed_instance_attribute(
1155+
attribute, resolved_value=attribute.value, typ=typ, ctx=ctx
12041156
)
1205-
if attribute is not None:
1206-
resolved_value = _substitute_typevars(
1207-
typ, generic_args, attribute.value, typ, ctx
1208-
)
1209-
resolved_instance = _maybe_use_resolved_typed_instance_attribute(
1210-
attribute, resolved_value=resolved_value, typ=typ, ctx=ctx
1211-
)
1212-
if resolved_instance is not None:
1213-
return resolved_instance
1157+
if resolved_instance is not None:
1158+
return resolved_instance
12141159
synthetic_class = ctx.get_synthetic_class(typ)
12151160
if synthetic_class is not None and _contains_self_typevar(ctx.get_self_value()):
12161161
synthetic_result = _get_direct_attribute_from_synthetic_instance(
1217-
synthetic_class, ctx.attr, ctx, receiver_value=receiver_value
1162+
synthetic_class, ctx.attr, ctx, receiver_value=ctx.root_composite.value
12181163
)
12191164
if synthetic_result is not UNINITIALIZED_VALUE:
12201165
synthetic_result = (
@@ -1425,31 +1370,43 @@ def _unwrap_value_from_typed(result: Value, typ: type, ctx: AttrContext) -> Valu
14251370
if ctx.attr == "__new__":
14261371
# __new__ is implicitly a staticmethod
14271372
return result
1428-
return UnboundMethodValue(ctx.attr, ctx.root_composite, typevars=typevars)
1373+
return UnboundMethodValue(
1374+
ctx.attr, ctx.root_composite, typevars=typevars, owner=typ
1375+
)
14291376
if isinstance(descriptor, staticmethod) or ctx.attr == "__new__":
14301377
return result
14311378
else:
1432-
return UnboundMethodValue(ctx.attr, ctx.root_composite, typevars=typevars)
1379+
return UnboundMethodValue(
1380+
ctx.attr, ctx.root_composite, typevars=typevars, owner=typ
1381+
)
14331382
elif isinstance(cls_val, (types.MethodType, MethodDescriptorType, SlotWrapperType)):
14341383
# built-in method; e.g. scope_lib.tests.SimpleDatabox.get
1435-
return UnboundMethodValue(ctx.attr, ctx.root_composite, typevars=typevars)
1384+
return UnboundMethodValue(
1385+
ctx.attr, ctx.root_composite, typevars=typevars, owner=typ
1386+
)
14361387
elif _static_hasattr(cls_val, "binder_cls") and _static_hasattr(cls_val, "fn"):
14371388
# qcore/asynq-style decorators expose a binder type on the descriptor but
14381389
# still behave like methods when accessed through instances.
1439-
return UnboundMethodValue(ctx.attr, ctx.root_composite, typevars=typevars)
1390+
return UnboundMethodValue(
1391+
ctx.attr, ctx.root_composite, typevars=typevars, owner=typ
1392+
)
14401393
elif (
14411394
_static_hasattr(cls_val, "decorator")
14421395
and _static_hasattr(cls_val, "instance")
14431396
and not isinstance(cls_val.instance, type)
14441397
):
14451398
# non-static method
1446-
return UnboundMethodValue(ctx.attr, ctx.root_composite, typevars=typevars)
1399+
return UnboundMethodValue(
1400+
ctx.attr, ctx.root_composite, typevars=typevars, owner=typ
1401+
)
14471402
elif is_async_fn(cls_val):
14481403
# static or class method
14491404
return result
14501405
elif _static_hasattr(cls_val, "func_code"):
14511406
# Cython function probably
1452-
return UnboundMethodValue(ctx.attr, ctx.root_composite, typevars=typevars)
1407+
return UnboundMethodValue(
1408+
ctx.attr, ctx.root_composite, typevars=typevars, owner=typ
1409+
)
14531410
transformed = ClassAttributeTransformer.transform_attribute(cls_val, ctx.options)
14541411
if transformed is not None:
14551412
return transformed
@@ -1602,7 +1559,10 @@ def _get_attribute_from_unbound(
16021559
except AttributeError:
16031560
return UNINITIALIZED_VALUE
16041561
result = UnboundMethodValue(
1605-
root_value.attr_name, root_value.composite, secondary_attr_name=ctx.attr
1562+
root_value.attr_name,
1563+
root_value.composite,
1564+
secondary_attr_name=ctx.attr,
1565+
owner=root_value.owner,
16061566
)
16071567
ctx.record_usage(type(method), result)
16081568
return result

pycroscope/checker.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,8 @@ def _get_runtime_overloaded_method_signature(
936936
return OverloadedSignature(signatures)
937937

938938
def _get_unbound_method_owner(self, value: UnboundMethodValue) -> type | None:
939+
if isinstance(value.owner, type):
940+
return value.owner
939941
root = replace_fallback(value.composite.value)
940942
if isinstance(root, TypedValue) and isinstance(root.typ, type):
941943
return root.typ

pycroscope/functions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777

7878
FunctionDefNode = ast.FunctionDef | ast.AsyncFunctionDef
7979
FunctionNode = FunctionDefNode | ast.Lambda
80-
IMPLICIT_CLASSMETHODS = ("__init_subclass__", "__new__")
80+
IMPLICIT_CLASSMETHODS = ("__init_subclass__", "__new__", "__class_getitem__")
8181

8282
YieldT = TypeVar("YieldT")
8383
SendT = TypeVar("SendT")

pycroscope/name_check_visitor.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3556,7 +3556,10 @@ def _check_for_incompatible_overrides(
35563556
continue
35573557
base_tobj = self.checker.make_type_object(base_value.typ)
35583558
base_attr = base_tobj.get_attribute(
3559-
varname, AttributePolicy(receiver=base_value, visitor=self, node=node)
3559+
varname,
3560+
AttributePolicy(
3561+
receiver=base_value, visitor=self, node=node, prefer_symbolic=True
3562+
),
35603563
)
35613564
if base_attr is None:
35623565
continue
@@ -13610,6 +13613,7 @@ def _get_dunder(
1361013613
self, node: ast.AST | None, callee_val: Value, method_name: str
1361113614
) -> Value:
1361213615
synthetic_lookup_val = callee_val
13616+
resolved_self_value = callee_val
1361313617
if (
1361413618
isinstance(callee_val, PredicateValue)
1361513619
and isinstance(callee_val.predicate, HasAttr)
@@ -13623,7 +13627,15 @@ def _get_dunder(
1362313627
node,
1362413628
ignore_none=self.options.get_value_for(IgnoreNoneAttributes),
1362513629
)
13626-
if isinstance(callee_val, TypedValue):
13630+
if isinstance(callee_val, TypeVarValue):
13631+
fallback_value = callee_val.get_fallback_value()
13632+
fallback_lookup_val = fallback_value.get_type_value(self)
13633+
if (
13634+
callee_val.typevar_param.bound is None
13635+
and not callee_val.typevar_param.constraints
13636+
):
13637+
resolved_self_value = fallback_lookup_val
13638+
elif isinstance(callee_val, TypedValue):
1362713639
fallback_lookup_val = SubclassValue.make(callee_val)
1362813640
elif isinstance(callee_val, KnownValueWithTypeVars):
1362913641
fallback_lookup_val = callee_val
@@ -13658,7 +13670,7 @@ def _get_dunder(
1365813670
node,
1365913671
ignore_none=self.options.get_value_for(IgnoreNoneAttributes),
1366013672
record_reads=False,
13661-
self_value=callee_val,
13673+
self_value=resolved_self_value,
1366213674
)
1366313675
if method_object is UNINITIALIZED_VALUE:
1366413676
self.show_error(

0 commit comments

Comments
 (0)