Skip to content

Commit

Permalink
Improve representation of KnownValue of type, function, module (#788)
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra authored Jul 11, 2024
1 parent 70dcfca commit 668ee5a
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 3 deletions.
2 changes: 2 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Improve representation of known module, function, and type objects
in error messages (#788)
- Add a mechanism to allow overriding the global variables in an
analyzed module. Use this mechanism to set the type of
`qcore.testing.Anything` to `Any`. (#786)
Expand Down
12 changes: 10 additions & 2 deletions pyanalyze/test_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,16 @@ def test_any_value() -> None:
def test_known_value() -> None:
val = KnownValue(3)
assert 3 == val.val
assert "Literal[3]" == str(val)
assert "Literal['']" == str(KnownValue(""))
assert str(val) == "Literal[3]"
assert str(KnownValue("")) == "Literal['']"
assert str(KnownValue(None)) == "None"
assert str(KnownValue(str)) == "type 'str'"
assert str(KnownValue(KnownValue)) == "type 'pyanalyze.value.KnownValue'"
assert (
str(KnownValue(test_known_value))
== "function 'pyanalyze.test_value.test_known_value'"
)
assert str(KnownValue(ast)) == "module 'ast'"
assert val.is_type(int)
assert not val.is_type(str)

Expand Down
20 changes: 19 additions & 1 deletion pyanalyze/value.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def function(x: int, y: list[int], z: Any):
from collections import deque
from dataclasses import InitVar, dataclass, field
from itertools import chain
from types import FunctionType
from types import FunctionType, ModuleType
from typing import (
Any,
Callable,
Expand Down Expand Up @@ -583,6 +583,12 @@ def __hash__(self) -> int:
def __str__(self) -> str:
if self.val is None:
return "None"
elif isinstance(self.val, ModuleType):
return f"module {self.val.__name__!r}"
elif isinstance(self.val, FunctionType):
return f"function {get_fully_qualified_name(self.val)!r}"
elif isinstance(self.val, type):
return f"type {get_fully_qualified_name(self.val)!r}"
else:
return f"Literal[{self.val!r}]"

Expand All @@ -601,6 +607,18 @@ def simplify(self) -> Value:
return val.simplify()


def get_fully_qualified_name(obj: Union[FunctionType, type]) -> str:
mod = getattr(obj, "__module__", None)
if mod == "builtins":
mod = None
name = getattr(obj, "__qualname__", None)
if name is None:
return repr(obj)
if mod:
return f"{mod}.{name}"
return name


@dataclass(frozen=True)
class KnownValueWithTypeVars(KnownValue):
"""Subclass of KnownValue that records a TypeVar substitution."""
Expand Down

0 comments on commit 668ee5a

Please sign in to comment.