Skip to content

Commit

Permalink
Merge pull request #1611 from google/google_sync
Browse files Browse the repository at this point in the history
Google sync
  • Loading branch information
rchen152 committed Apr 9, 2024
2 parents 52d8e44 + f423ec5 commit 256fbf6
Show file tree
Hide file tree
Showing 35 changed files with 1,010 additions and 454 deletions.
4 changes: 2 additions & 2 deletions pytype/rewrite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ py_library(
pytype.config
pytype.load_pytd
pytype.errors.errors
pytype.rewrite.abstract.abstract
)

py_library(
Expand Down Expand Up @@ -105,6 +104,7 @@ py_library(
load_abstract.py
DEPS
pytype.load_pytd
pytype.pytd.pytd
pytype.rewrite.abstract.abstract
pytype.rewrite.overlays.overlays
)
Expand All @@ -115,6 +115,7 @@ py_test(
SRCS
load_abstract_test.py
DEPS
pytype.rewrite.abstract.abstract
pytype.rewrite.tests.test_utils
)

Expand Down Expand Up @@ -169,7 +170,6 @@ py_test(
stack_test.py
DEPS
.stack
pytype.rewrite.abstract.abstract
pytype.rewrite.tests.test_utils
)

Expand Down
5 changes: 0 additions & 5 deletions pytype/rewrite/abstract/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ py_library(
instances.py
DEPS
.base
.classes
)

py_test(
Expand All @@ -100,7 +99,6 @@ py_test(
instances_test.py
DEPS
.base
.classes
.instances
pytype.rewrite.tests.test_utils
)
Expand All @@ -120,7 +118,6 @@ py_test(
SRCS
internal_test.py
DEPS
.classes
.internal
pytype.rewrite.tests.test_utils
)
Expand All @@ -132,7 +129,6 @@ py_library(
utils.py
DEPS
.base
.classes
)

py_test(
Expand All @@ -142,7 +138,6 @@ py_test(
utils_test.py
DEPS
.base
.classes
.utils
pytype.rewrite.tests.test_utils
)
4 changes: 2 additions & 2 deletions pytype/rewrite/abstract/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@

BaseValue = _base.BaseValue
ContextType = _base.ContextType
PythonConstant = _base.PythonConstant
Singleton = _base.Singleton
Singletons = _base.Singletons
Union = _base.Union

SimpleClass = _classes.SimpleClass
BaseInstance = _classes.BaseInstance
FrozenInstance = _classes.FrozenInstance
InterpreterClass = _classes.InterpreterClass
MutableInstance = _classes.MutableInstance
PythonConstant = _classes.PythonConstant

Args = _functions.Args
BaseFunction = _functions.BaseFunction
Expand All @@ -34,6 +33,7 @@
Dict = _instances.Dict
List = _instances.List
Set = _instances.Set
Tuple = _instances.Tuple

ConstKeyDict = _internal.ConstKeyDict
Splat = _internal.Splat
Expand Down
80 changes: 48 additions & 32 deletions pytype/rewrite/abstract/base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"""Base abstract representation of Python values."""

import abc
import dataclasses
from typing import Any, Dict, Optional, Protocol, Sequence, Tuple
from typing import Any, Generic, Optional, Protocol, Sequence, Tuple, TypeVar

from pytype import config
from pytype import load_pytd
Expand All @@ -12,39 +11,29 @@
from pytype.types import types
from typing_extensions import Self


@dataclasses.dataclass(init=False)
class Singletons:
"""Singleton abstract values."""

# For readability, we give these the same name as the value they represent.
# pylint: disable=invalid-name
Any: 'Singleton'
__build_class__: 'Singleton'
Never: 'Singleton'
NULL: 'Singleton'
# pylint: enable=invalid-name

def __init__(self, ctx: 'ContextType'):
for field in dataclasses.fields(self):
setattr(self, field.name, Singleton(ctx, field.name))
_T = TypeVar('_T')


class ContextType(Protocol):

options: config.Options
pytd_loader: load_pytd.Loader

singles: Singletons
errorlog: Any
abstract_converter: Any
abstract_loader: Any
pytd_converter: Any
consts: Any


class BaseValue(types.BaseValue, abc.ABC):
"""Base class for abstract values."""

# For convenience, we want the 'name' attribute to be available on all values.
# Setting it as a class attribute gives subclasses the most flexibility in how
# to define it.
name = ''

def __init__(self, ctx: ContextType):
self._ctx = ctx

Expand All @@ -62,14 +51,18 @@ def _attrs(self) -> Tuple[Any, ...]:
`self._ctx`.
"""

@property
def full_name(self):
return self.name

def __eq__(self, other):
return self.__class__ == other.__class__ and self._attrs == other._attrs

def __hash__(self):
return hash((self.__class__, self._ctx) + self._attrs)

def to_variable(self: Self) -> variables.Variable[Self]:
return variables.Variable.from_value(self)
def to_variable(self, name: Optional[str] = None) -> variables.Variable[Self]:
return variables.Variable.from_value(self, name=name)

def get_attribute(self, name: str) -> Optional['BaseValue']:
del name # unused
Expand All @@ -92,21 +85,44 @@ def to_pytd_type_of_instance(self) -> pytd.Type:
return self._ctx.pytd_converter.to_pytd_type_of_instance(self)


class PythonConstant(BaseValue, Generic[_T]):
"""Representation of a Python constant.
DO NOT INSTANTIATE THIS CLASS DIRECTLY! Doing so will create extra copies of
constants, potentially causing subtle bugs. Instead, fetch the canonical
instance of the constant using ctx.consts[constant].
"""

def __init__(
self, ctx: ContextType, constant: _T, allow_direct_instantiation=False):
if self.__class__ is PythonConstant and not allow_direct_instantiation:
raise ValueError('Do not instantiate PythonConstant directly. Use '
'ctx.consts[constant] instead.')
super().__init__(ctx)
self.constant = constant

def __repr__(self):
return f'PythonConstant({self.constant!r})'

@property
def _attrs(self):
return (self.constant,)


class Singleton(BaseValue):
"""Singleton value."""
"""Singleton value.
_INSTANCES: Dict[Tuple[ContextType, str], 'Singleton'] = {}
name: str
DO NOT INSTANTIATE THIS CLASS DIRECTLY! Doing so will create extra copies of
singletons, potentially causing subtle bugs. Instead, fetch the canonical
instance of the singleton using ctx.consts.singles[name].
"""

def __new__(cls, ctx: ContextType, name: str):
key = (ctx, name)
if key in cls._INSTANCES:
return cls._INSTANCES[key]
self = super().__new__(cls)
cls._INSTANCES[key] = self
return self
name: str

def __init__(self, ctx, name):
def __init__(self, ctx, name, allow_direct_instantiation=False):
if self.__class__ is Singleton and not allow_direct_instantiation:
raise ValueError('Do not instantiate Singleton directly. Use '
'ctx.consts.singles[name] instead.')
super().__init__(ctx)
self.name = name

Expand Down
91 changes: 62 additions & 29 deletions pytype/rewrite/abstract/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,91 @@
import unittest


class BaseValueTest(test_utils.ContextfulTestBase):
class FakeValue(base.BaseValue):

def __repr__(self):
return 'FakeValue'

@property
def _attrs(self):
return (id(self),)


class TestBase(test_utils.ContextfulTestBase):

def _const(self, const):
return base.PythonConstant(self.ctx, const, allow_direct_instantiation=True)


class BaseValueTest(TestBase):

def test_to_variable(self):
v = FakeValue(self.ctx)
var = v.to_variable()
assert_type(var, variables.Variable[FakeValue])
self.assertEqual(var.get_atomic_value(), v)
self.assertIsNone(var.name)

def test_name(self):
var = FakeValue(self.ctx).to_variable('NamedVariable')
self.assertEqual(var.name, 'NamedVariable')


class PythonConstantTest(TestBase):

def test_equal(self):
c1 = self._const('a')
c2 = self._const('a')
self.assertEqual(c1, c2)

class C(base.BaseValue):
def test_not_equal(self):
c1 = self._const('a')
c2 = self._const('b')
self.assertNotEqual(c1, c2)

def __repr__(self):
return 'C'
def test_constant_type(self):
c = self._const('a')
assert_type(c.constant, str)

@property
def _attrs(self):
return (id(self),)
def test_get_type_from_variable(self):
var = self._const(True).to_variable()
const = var.get_atomic_value(base.PythonConstant[int]).constant
assert_type(const, int)

c = C(self.ctx)
var = c.to_variable()
assert_type(var, variables.Variable[C])
self.assertEqual(var.get_atomic_value(), c)
def test_direct_instantiation(self):
with self.assertRaises(ValueError):
base.PythonConstant(self.ctx, None)


class SingletonTest(test_utils.ContextfulTestBase):
class SingletonTest(TestBase):

def test_duplicate(self):
s1 = base.Singleton(self.ctx, 'TEST_SINGLETON')
s2 = base.Singleton(self.ctx, 'TEST_SINGLETON')
self.assertIs(s1, s2)
def test_direct_instantiation(self):
with self.assertRaises(ValueError):
base.Singleton(self.ctx, 'TEST_SINGLETON')


class UnionTest(test_utils.ContextfulTestBase):
class UnionTest(TestBase):

def test_basic(self):
options = (classes.PythonConstant(self.ctx, True),
classes.PythonConstant(self.ctx, False))
options = (self._const(True), self._const(False))
union = base.Union(self.ctx, options)
self.assertEqual(union.options, options)

def test_flatten(self):
union1 = base.Union(self.ctx, (classes.PythonConstant(self.ctx, True),
classes.PythonConstant(self.ctx, False)))
union2 = base.Union(self.ctx, (union1, classes.PythonConstant(self.ctx, 5)))
self.assertEqual(union2.options, (classes.PythonConstant(self.ctx, True),
classes.PythonConstant(self.ctx, False),
classes.PythonConstant(self.ctx, 5)))
union1 = base.Union(self.ctx, (self._const(True), self._const(False)))
union2 = base.Union(self.ctx, (union1, self._const(5)))
self.assertEqual(union2.options,
(self._const(True), self._const(False), self._const(5)))

def test_deduplicate(self):
true = classes.PythonConstant(self.ctx, True)
false = classes.PythonConstant(self.ctx, False)
true = self._const(True)
false = self._const(False)
union = base.Union(self.ctx, (true, false, true))
self.assertEqual(union.options, (true, false))

def test_order(self):
true = classes.PythonConstant(self.ctx, True)
false = classes.PythonConstant(self.ctx, False)
true = self._const(True)
false = self._const(False)
self.assertEqual(base.Union(self.ctx, (true, false)),
base.Union(self.ctx, (false, true)))

Expand Down

0 comments on commit 256fbf6

Please sign in to comment.