Skip to content

Commit

Permalink
Make _abstract a real module-like object
Browse files Browse the repository at this point in the history
Drop the string-based functions `_isinstance` and `_make`, instead use the real objects directly.

Very similarly to the prior situation, the actual `abstract` module will be lazily loaded only after the first access of such an attribute.

PiperOrigin-RevId: 692727204
  • Loading branch information
oprypin authored and copybara-github committed Nov 6, 2024
1 parent 60a5567 commit 68befc3
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 223 deletions.
49 changes: 25 additions & 24 deletions pytype/abstract/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
from pytype.abstract import _classes # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.abstract import function # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.typegraph import cfg # pylint: disable=g-bad-import-order,g-import-not-at-top

_isinstance = abstract_utils._isinstance # pylint: disable=protected-access
_make = abstract_utils._make # pylint: disable=protected-access
from pytype.abstract import abstract as _abstract # pylint: disable=g-import-not-at-top, g-bad-import-order
else:
_abstract = abstract_utils._abstract # pylint: disable=protected-access


class BaseValue(utils.ContextWeakrefMixin, types.BaseValue):
Expand Down Expand Up @@ -314,7 +314,10 @@ def instantiate(
raise NotImplementedError(self.__class__.__name__)

def to_annotation_container(self):
if _isinstance(self, "PyTDClass") and self.full_name == "builtins.tuple":
if (
isinstance(self, _abstract.PyTDClass)
and self.full_name == "builtins.tuple"
):
# If we are parameterizing builtins.tuple, replace it with typing.Tuple so
# that heterogeneous tuple annotations work. We need the isinstance()
# check to distinguish PyTDClass(tuple) from ParameterizedClass(tuple);
Expand All @@ -324,7 +327,7 @@ def to_annotation_container(self):
).get_module("Tuple")
typing.load_lazy_attribute("Tuple")
return abstract_utils.get_atomic_value(typing.members["Tuple"])
return _make("AnnotationContainer", self.name, self.ctx, self)
return _abstract.AnnotationContainer(self.name, self.ctx, self) # pytype: disable=wrong-arg-types

def to_variable(self, node: "cfg.CFGNode") -> "cfg.Variable":
"""Build a variable out of this abstract value.
Expand Down Expand Up @@ -412,34 +415,32 @@ def should_set_self_annot(self) -> bool:
# To do argument matching for custom generic classes, the 'self' annotation
# needs to be replaced with a generic type.

# We need to disable attribute-error because pytype doesn't understand our
# special _isinstance function.
# pytype: disable=attribute-error
if (
not _isinstance(self, "SignedFunction")
or not self.signature.param_names
):
if not isinstance(self, _abstract.SignedFunction):
return False
self: _abstract.SignedFunction
if not self.signature.param_names:
# no 'self' to replace
return False
# SimpleFunctions are methods we construct internally for generated classes
# like namedtuples.
if not _isinstance(self, ("InterpreterFunction", "SimpleFunction")):
if not isinstance(
self, (_abstract.InterpreterFunction, _abstract.SimpleFunction)
):
return False
# We don't want to clobber our own generic annotations.
return (
self.signature.param_names[0] not in self.signature.annotations
or not self.signature.annotations[self.signature.param_names[0]].formal
)
# pytype: enable=attribute-error


def _get_template(val: BaseValue) -> set[str]:
"""Get the value's class template."""
if _isinstance(val, "Class"):
if isinstance(val, _abstract.Class):
res = {t.full_name for t in val.template}
if _isinstance(val, "ParameterizedClass"):
if isinstance(val, _abstract.ParameterizedClass):
res.update(_get_template(val.base_cls))
elif _isinstance(val, ("PyTDClass", "InterpreterClass")):
elif isinstance(val, (_abstract.PyTDClass, _abstract.InterpreterClass)):
for base in val.bases():
base = abstract_utils.get_atomic_value(
base, default=val.ctx.convert.unsolvable
Expand Down Expand Up @@ -473,12 +474,12 @@ def _compute_template(val: BaseValue) -> Sequence[BaseValue]:
Raises:
GenericTypeError: if the type annotation for generic type is incorrect
"""
if _isinstance(val, "PyTDClass"):
if isinstance(val, _abstract.PyTDClass):
return [
val.ctx.convert.constant_to_value(itm.type_param)
for itm in val.pytd_cls.template
]
elif not _isinstance(val, "InterpreterClass"):
elif not isinstance(val, _abstract.InterpreterClass):
return ()
bases = [
abstract_utils.get_atomic_value(base, default=val.ctx.convert.unsolvable)
Expand All @@ -489,7 +490,7 @@ def _compute_template(val: BaseValue) -> Sequence[BaseValue]:
# Compute the number of `typing.Generic` and collect the type parameters
for base in bases:
if base.full_name == "typing.Generic":
if _isinstance(base, "PyTDClass"):
if isinstance(base, _abstract.PyTDClass):
raise abstract_utils.GenericTypeError(
val, "Cannot inherit from plain Generic"
)
Expand All @@ -506,10 +507,10 @@ def _compute_template(val: BaseValue) -> Sequence[BaseValue]:
# `typing.Generic`
for base in bases:
if base.full_name != "typing.Generic":
if _isinstance(base, "ParameterizedClass"):
if isinstance(base, _abstract.ParameterizedClass):
for item in base.template:
param = base.formal_type_parameters.get(item.name)
if _isinstance(base, "TypeParameter"):
if isinstance(base, _abstract.TypeParameter):
t = param.with_scope(val.full_name)
if t not in template:
raise abstract_utils.GenericTypeError(
Expand All @@ -519,11 +520,11 @@ def _compute_template(val: BaseValue) -> Sequence[BaseValue]:
# Compute template parameters according to C3
seqs = []
for base in bases:
if _isinstance(base, "ParameterizedClass"):
if isinstance(base, _abstract.ParameterizedClass):
seq = []
for item in base.template:
param = base.formal_type_parameters.get(item.name)
if _isinstance(param, "TypeParameter"):
if isinstance(param, _abstract.TypeParameter):
seq.append(param.with_scope(val.full_name))
seqs.append(seq)
try:
Expand Down
26 changes: 16 additions & 10 deletions pytype/abstract/_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
from pytype.abstract import _function_base # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.abstract import _interpreter_function # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.abstract import _typing # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.abstract import abstract as _abstract # pylint: disable=g-import-not-at-top, g-bad-import-order
else:
_abstract = abstract_utils._abstract # pylint: disable=protected-access

log: logging.Logger = logging.getLogger(__name__)
_isinstance = abstract_utils._isinstance # pylint: disable=protected-access


class BuildClass(_base.BaseValue):
Expand Down Expand Up @@ -59,7 +61,7 @@ def call(
"Invalid ambiguous argument to __build_class__"
)
(func,) = funcvar.data
if not _isinstance(func, "InterpreterFunction"):
if not isinstance(func, _abstract.InterpreterFunction):
raise abstract_utils.ConversionError(
"Invalid argument to __build_class__"
)
Expand All @@ -78,7 +80,7 @@ def call(
subst.update({
v.name: any_var
for v in base.formal_type_parameters.values()
if _isinstance(v, "TypeParameter")
if isinstance(v, _abstract.TypeParameter)
})

node, _ = func.call(
Expand Down Expand Up @@ -166,7 +168,7 @@ def update_method_type_params(self) -> None:
skip = set()
for mbr in self.members.values():
for m in mbr.data:
if not _isinstance(m, "Function"):
if not isinstance(m, _abstract.Function):
continue
methods.append(m)
# We don't need to update the same method twice.
Expand Down Expand Up @@ -214,7 +216,9 @@ def _override_check(self) -> None:
member_data = [
m
for m in member.data
if _isinstance(m, ("InterpreterClass", "InterpreterFunction"))
if isinstance(
m, (_abstract.InterpreterClass, _abstract.InterpreterFunction)
)
]
if not member_data:
continue
Expand Down Expand Up @@ -294,7 +298,9 @@ def get_own_attributes(self) -> set[str]:

def get_own_abstract_methods(self) -> set[str]:
def _can_be_abstract(var):
return any(_isinstance(v, "Function") and v.is_abstract for v in var.data)
return any(
isinstance(v, _abstract.Function) and v.is_abstract for v in var.data
)

return {name for name, var in self.members.items() if _can_be_abstract(var)}

Expand Down Expand Up @@ -1069,9 +1075,9 @@ def get_args(self):
return [self.formal_type_parameters[i] for i in range(self.num_args)]

def has_paramspec(self) -> bool:
return _isinstance(
return isinstance(
self.formal_type_parameters[abstract_utils.ARGS],
("ParamSpec", "Concatenate"),
(_abstract.ParamSpec, _abstract.Concatenate),
)


Expand Down Expand Up @@ -1188,7 +1194,7 @@ def _new_instance(
p = self.formal_type_parameters[i]
if container is abstract_utils.DUMMY_CONTAINER or (
isinstance(container, _instance_base.SimpleValue)
and _isinstance(p, "TypeParameter")
and isinstance(p, _abstract.TypeParameter)
and p.full_name in container.all_template_names
):
content.append(p.instantiate(self.ctx.root_node, container))
Expand Down Expand Up @@ -1283,7 +1289,7 @@ def add_slot(
except abstract_utils.ConversionError:
pass
else:
if self._instance and _isinstance(other, "Tuple"):
if self._instance and isinstance(other, _abstract.Tuple):
pyval = self._instance.pyval + other.pyval
ret = _instances.Tuple(pyval, self.ctx)
return node, ret.to_variable(node)
Expand Down
16 changes: 9 additions & 7 deletions pytype/abstract/_function_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@
from pytype.errors import error_types
from pytype.types import types

log: logging.Logger = logging.getLogger(__name__)
_isinstance = abstract_utils._isinstance # pylint: disable=protected-access

if TYPE_CHECKING:
from pytype import context # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype import datatypes # pylint: disable=g-bad-import-order,g-import-not-at-top
Expand All @@ -29,6 +26,11 @@
from pytype.abstract import _pytd_function # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.pyc import opcodes # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.typegraph import cfg # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.abstract import abstract as _abstract # pylint: disable=g-import-not-at-top, g-bad-import-order
else:
_abstract = abstract_utils._abstract # pylint: disable=protected-access

log: logging.Logger = logging.getLogger(__name__)


class Function(_instance_base.SimpleValue, types.Function):
Expand Down Expand Up @@ -697,16 +699,16 @@ def _map_args(
def _check_paramspec_args(self, args: function.Args) -> None:
args_pspec, kwargs_pspec = None, None
for name, _, formal in self.signature.iter_args(args):
if not _isinstance(formal, "ParameterizedClass"):
if not isinstance(formal, _abstract.ParameterizedClass):
continue
params = formal.get_formal_type_parameters() # pytype: disable=attribute-error
params = formal.get_formal_type_parameters()
if name == self.signature.varargs_name:
for param in params.values():
if _isinstance(param, "ParamSpecArgs"):
if isinstance(param, _abstract.ParamSpecArgs):
args_pspec = param
elif name == self.signature.kwargs_name:
for param in params.values():
if _isinstance(param, "ParamSpecKwargs"):
if isinstance(param, _abstract.ParamSpecKwargs):
kwargs_pspec = param
if args_pspec or kwargs_pspec:
valid = (
Expand Down
5 changes: 2 additions & 3 deletions pytype/abstract/_instance_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@
from pytype.abstract import function
from pytype.errors import error_types

log: logging.Logger = logging.getLogger(__name__)
_isinstance = abstract_utils._isinstance # pylint: disable=protected-access

if TYPE_CHECKING:
from pytype import context # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.abstract import _typing # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.typegraph import cfg # pylint: disable=g-bad-import-order,g-import-not-at-top

log: logging.Logger = logging.getLogger(__name__)


class SimpleValue(_base.BaseValue):
"""A basic abstract value that represents instances.
Expand Down
12 changes: 7 additions & 5 deletions pytype/abstract/_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
from pytype.typegraph import cfg_utils
from pytype.types import types

log: logging.Logger = logging.getLogger(__name__)
_make = abstract_utils._make # pylint: disable=protected-access

if TYPE_CHECKING:
from pytype import context # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype import state # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.abstract import abstract as _abstract # pylint: disable=g-import-not-at-top, g-bad-import-order
else:
_abstract = abstract_utils._abstract # pylint: disable=protected-access

log: logging.Logger = logging.getLogger(__name__)


def _var_map(
Expand Down Expand Up @@ -295,7 +297,7 @@ def get_special_attribute(
self, node: cfg.CFGNode, name: str, valself: cfg.Variable
):
if name == "__iter__":
f = _make("NativeFunction", name, self.__iter__, self.ctx)
f = _abstract.NativeFunction(name, self.__iter__, self.ctx)
return f.to_variable(node)
elif name == "__next__":
return self.to_variable(node)
Expand Down Expand Up @@ -325,7 +327,7 @@ def __init__(
for name, instance_param in tuple(enumerate(content))
+ ((abstract_utils.T, combined_content),)
}
cls = _make("TupleClass", ctx.convert.tuple_type, class_params, ctx)
cls = _abstract.TupleClass(ctx.convert.tuple_type, class_params, ctx)
super().__init__(cls, ctx)
mixin.PythonConstant.init_mixin(self, content)
self._hash = None # memoized due to expensive computation
Expand Down
21 changes: 12 additions & 9 deletions pytype/abstract/_interpreter_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@
from pytype.pytd import pytd_utils
from pytype.typegraph import cfg_utils


log: logging.Logger = logging.getLogger(__name__)
_isinstance = abstract_utils._isinstance # pylint: disable=protected-access

if TYPE_CHECKING:
from pytype import context # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype import datatypes # pylint: disable=g-bad-import-order,g-import-not-at-top
Expand All @@ -34,6 +30,11 @@
from pytype.blocks import blocks # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.pyc import opcodes # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.typegraph import cfg # pylint: disable=g-bad-import-order,g-import-not-at-top
from pytype.abstract import abstract as _abstract # pylint: disable=g-import-not-at-top, g-bad-import-order
else:
_abstract = abstract_utils._abstract # pylint: disable=protected-access

log: logging.Logger = logging.getLogger(__name__)


def _matches_generator_helper(
Expand All @@ -52,7 +53,7 @@ def _matches_generator_helper(
return (
isinstance(base_cls, _classes.PyTDClass)
and base_cls.name in allowed_types
) or _isinstance(base_cls, "AMBIGUOUS_OR_EMPTY")
) or isinstance(base_cls, _abstract.AMBIGUOUS_OR_EMPTY)


def _matches_generator(type_obj: "_base.BaseValue") -> bool:
Expand Down Expand Up @@ -567,7 +568,7 @@ def _paramspec_signature(
) -> function.Signature | None:
# Unpack the paramspec substitution we have created in the matcher.
rhs = callable_type.formal_type_parameters[0]
if _isinstance(rhs, "Concatenate"):
if isinstance(rhs, _abstract.Concatenate):
r_pspec = rhs.paramspec
r_args = rhs.args
else:
Expand All @@ -593,15 +594,17 @@ def _handle_paramspec(
if not sig.has_return_annotation:
return
retval = sig.annotations["return"]
if not (_isinstance(retval, "CallableClass") and retval.has_paramspec()): # pytype: disable=attribute-error
if not (
isinstance(retval, _abstract.CallableClass) and retval.has_paramspec()
):
return
ret_sig = self._paramspec_signature(retval, substs)
if ret_sig:
ret_annot = self.ctx.pytd_convert.signature_to_callable(ret_sig)
annotations["return"] = ret_annot
for name, _, annot in sig.iter_args(callargs):
if _isinstance(annot, "CallableClass") and annot.has_paramspec(): # pytype: disable=attribute-error
param_sig = self._paramspec_signature(annot, substs) # pytype: disable=wrong-arg-types
if isinstance(annot, _abstract.CallableClass) and annot.has_paramspec():
param_sig = self._paramspec_signature(annot, substs)
if param_sig:
param_annot = self.ctx.pytd_convert.signature_to_callable(param_sig)
annotations[name] = param_annot
Expand Down
Loading

0 comments on commit 68befc3

Please sign in to comment.