Skip to content

Commit

Permalink
implement support for Self (#423)
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra authored Jan 15, 2022
1 parent 2530daa commit 4d0d681
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Support PEP 673 (`typing_extensions.Self`) (#423)
- Updates for compatibility with recent changes in typeshed (#421):
- Fix override compatibility check for unknown callables
- Fix usage of removed type `_typeshed.SupportsLessThan`
Expand Down
3 changes: 3 additions & 0 deletions pyanalyze/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
ParamSpecArgsValue,
ParamSpecKwargsValue,
ParameterTypeGuardExtension,
SelfTVV,
TypeGuardExtension,
TypedValue,
SequenceIncompleteValue,
Expand Down Expand Up @@ -427,6 +428,8 @@ def _type_from_runtime(val: Any, ctx: Context, is_typeddict: bool = False) -> Va
return KnownValue(None)
elif is_typing_name(val, "NoReturn"):
return NO_RETURN_VALUE
elif is_typing_name(val, "Self"):
return SelfTVV
elif val is typing.Any:
return AnyValue(AnySource.explicit)
elif hasattr(val, "__supertype__"):
Expand Down
17 changes: 14 additions & 3 deletions pyanalyze/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
SubclassValue,
TypedValue,
TypeVarValue,
set_self,
)

# these don't appear to be in the standard types module
Expand Down Expand Up @@ -115,7 +116,9 @@ def get_attribute(ctx: AttrContext) -> Value:
if isinstance(root_value.typ.typ, str):
# TODO handle synthetic types
return AnyValue(AnySource.inference)
attribute_value = _get_attribute_from_subclass(root_value.typ.typ, ctx)
attribute_value = _get_attribute_from_subclass(
root_value.typ.typ, root_value.typ, ctx
)
elif isinstance(root_value.typ, AnyValue):
attribute_value = AnyValue(AnySource.from_another)
else:
Expand Down Expand Up @@ -144,7 +147,9 @@ def may_have_dynamic_attributes(typ: type) -> bool:
return False


def _get_attribute_from_subclass(typ: type, ctx: AttrContext) -> Value:
def _get_attribute_from_subclass(
typ: type, self_value: Value, ctx: AttrContext
) -> Value:
ctx.record_attr_read(typ)

# First check values that are special in Python
Expand All @@ -157,6 +162,7 @@ def _get_attribute_from_subclass(typ: type, ctx: AttrContext) -> Value:
result, _, should_unwrap = _get_attribute_from_mro(typ, ctx, on_class=True)
if should_unwrap:
result = _unwrap_value_from_subclass(result, ctx)
result = set_self(result, self_value)
ctx.record_usage(typ, result)
return result

Expand Down Expand Up @@ -223,7 +229,9 @@ def _get_attribute_from_synthetic_type(
result, provider = ctx.get_attribute_from_typeshed_recursively(
fq_name, on_class=False
)
return _substitute_typevars(fq_name, generic_args, result, provider, ctx)
result = _substitute_typevars(fq_name, generic_args, result, provider, ctx)
result = set_self(result, ctx.root_value)
return result


def _get_attribute_from_typed(
Expand All @@ -241,6 +249,7 @@ def _get_attribute_from_typed(
if should_unwrap:
result = _unwrap_value_from_typed(result, typ, ctx)
ctx.record_usage(typ, result)
result = set_self(result, ctx.root_value)
return result


Expand Down Expand Up @@ -337,6 +346,8 @@ def _get_attribute_from_known(obj: object, ctx: AttrContext) -> Value:
return GenericValue(dict, [TypedValue(str), TypedValue(types.ModuleType)])

result, _, _ = _get_attribute_from_mro(obj, ctx, on_class=True)
if safe_isinstance(obj, type):
result = set_self(result, TypedValue(obj))
if isinstance(obj, (types.ModuleType, type)):
ctx.record_usage(obj, result)
else:
Expand Down
11 changes: 11 additions & 0 deletions pyanalyze/value.py
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,10 @@ def __str__(self) -> str:
return f"<reference to {self.name}>"


# Special TypeVar used to implement PEP 673 Self.
SelfT = TypeVar("SelfT")


@dataclass(frozen=True)
class TypeVarValue(Value):
"""Value representing a ``typing.TypeVar`` or ``typing.ParamSpec``.
Expand Down Expand Up @@ -1563,6 +1567,13 @@ def __str__(self) -> str:
return str(self.typevar)


SelfTVV = TypeVarValue(SelfT)


def set_self(value: Value, self_value: Value) -> Value:
return value.substitute_typevars({SelfT: self_value})


@dataclass
class ParamSpecArgsValue(Value):
param_spec: ParamSpec
Expand Down

0 comments on commit 4d0d681

Please sign in to comment.