diff --git a/pytype/abstract/_base.py b/pytype/abstract/_base.py index 5bf01e844..9c85d4009 100644 --- a/pytype/abstract/_base.py +++ b/pytype/abstract/_base.py @@ -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): @@ -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); @@ -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. @@ -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 @@ -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) @@ -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" ) @@ -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( @@ -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: diff --git a/pytype/abstract/_classes.py b/pytype/abstract/_classes.py index da31a75b4..4ba75fd97 100644 --- a/pytype/abstract/_classes.py +++ b/pytype/abstract/_classes.py @@ -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): @@ -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__" ) @@ -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( @@ -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. @@ -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 @@ -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)} @@ -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), ) @@ -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)) @@ -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) diff --git a/pytype/abstract/_function_base.py b/pytype/abstract/_function_base.py index 8b9b763d2..756c1ebb1 100644 --- a/pytype/abstract/_function_base.py +++ b/pytype/abstract/_function_base.py @@ -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 @@ -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): @@ -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 = ( diff --git a/pytype/abstract/_instance_base.py b/pytype/abstract/_instance_base.py index 6d2f6fbcd..f2f4a7eac 100644 --- a/pytype/abstract/_instance_base.py +++ b/pytype/abstract/_instance_base.py @@ -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. diff --git a/pytype/abstract/_instances.py b/pytype/abstract/_instances.py index 1b07f0642..754975157 100644 --- a/pytype/abstract/_instances.py +++ b/pytype/abstract/_instances.py @@ -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( @@ -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) @@ -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 diff --git a/pytype/abstract/_interpreter_function.py b/pytype/abstract/_interpreter_function.py index 1fa37769b..67f496aee 100644 --- a/pytype/abstract/_interpreter_function.py +++ b/pytype/abstract/_interpreter_function.py @@ -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 @@ -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( @@ -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: @@ -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: @@ -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 diff --git a/pytype/abstract/_pytd_function.py b/pytype/abstract/_pytd_function.py index 31f8e5278..fac89c3e7 100644 --- a/pytype/abstract/_pytd_function.py +++ b/pytype/abstract/_pytd_function.py @@ -26,13 +26,14 @@ from pytype.typegraph import cfg 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 matcher # 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 SignatureMutationError(Exception): @@ -291,7 +292,7 @@ def call( # - An annotation has a type param that is not ambiguous or empty # - The mutation adds a type that is not ambiguous or empty def should_check(value): - return not _isinstance(value, "AMBIGUOUS_OR_EMPTY") + return not isinstance(value, _abstract.AMBIGUOUS_OR_EMPTY) def compatible_with(new, existing, view): """Check whether a new type can be added to a container.""" @@ -376,7 +377,7 @@ def compatible_with(new, existing, view): name = list(names)[0] if len(names) == 1 else None # Find the container class for base in obj.cls.mro: - if _isinstance(base, "ParameterizedClass"): + if isinstance(base, _abstract.ParameterizedClass): cls = base break else: @@ -427,7 +428,7 @@ def _can_match_multiple(self, args): if len(self.signatures) <= 1: return False for var in args.get_variables(): - if any(_isinstance(v, "AMBIGUOUS_OR_EMPTY") for v in var.data): + if any(isinstance(v, _abstract.AMBIGUOUS_OR_EMPTY) for v in var.data): return True # An opaque *args or **kwargs behaves like an unknown. return args.has_opaque_starargs_or_starstarargs() @@ -812,16 +813,16 @@ def _handle_paramspec( # Make sure the type params from subst get applied to val. constant_to_value # does not reliably do the type substitution because it ignores `subst` when # caching results. - if _isinstance(val, "ParameterizedClass"): + if isinstance(val, _abstract.ParameterizedClass): inner_types = [] for k, v in val.formal_type_parameters.items(): - if _isinstance(v, "TypeParameter") and v.full_name in subst: + if isinstance(v, _abstract.TypeParameter) and v.full_name in subst: typ = self.ctx.convert.merge_classes(subst[v.full_name].data) inner_types.append((k, typ)) else: inner_types.append((k, v)) val = val.replace(inner_types) - elif _isinstance(val, "TypeParameter") and val.full_name in subst: + elif isinstance(val, _abstract.TypeParameter) and val.full_name in subst: val = self.ctx.convert.merge_classes(subst[val.full_name].data) ret = self._paramspec_signature(return_callable, val, subst) if ret: diff --git a/pytype/abstract/abstract_utils.py b/pytype/abstract/abstract_utils.py index a17c64f58..2aa6b0672 100644 --- a/pytype/abstract/abstract_utils.py +++ b/pytype/abstract/abstract_utils.py @@ -220,53 +220,42 @@ def get_type(self, node: cfg.CFGNode, name: str) -> Any | None: return None -# The _isinstance and _make methods should be used only in pytype.abstract -# submodules that are unable to reference abstract.py classes due to circular -# dependencies. To prevent accidental misuse, the methods are marked private. -# Callers are expected to alias them like so: -# _isinstance = abstract_utils._isinstance # pylint: disable=protected-access +class _Abstract: + """A helper that lazily loads the 'abstract' module to prevent circular deps. -_ISINSTANCE_CACHE: dict[str, Any] = {} + Always import it like this: + if TYPE_CHECKING: + from pytype.abstract import abstract as _abstract + else: + _abstract = abstract_utils._abstract # pylint: disable=protected-access + """ -# TODO : This function is disasterous, does not act as a type guard since it's -# not a real isinstance, and being passed a string. Also there's no way -# to type this function and make pytype understand this because pytype does not -# discriminate different string literals when it comes to overloads. So just the -# best way here is to remove this function. -def _isinstance(obj, name_or_names: str | tuple[str, ...]) -> bool: - """Do an isinstance() call for a class defined in pytype.abstract. + _loaded = False - Args: - obj: An instance. - name_or_names: A name or tuple of names of classes in pytype.abstract. + def __getattr__(self, name: str) -> object: + if not self._loaded: + from pytype.abstract import abstract # pylint: disable=g-import-not-at-top,g-bad-import-order - Returns: - Whether obj is an instance of name_or_names. - """ - # This function is heavily optimized because of how often it is called - over - # 13M times in one analysis of an 1,100-line file that we profiled. The cache - # improves performance by about 10% over calling getattr() on the abstract - # module every time, and checking the __class__ attribute is about 20% faster - # than calling isinstance. - if not _ISINSTANCE_CACHE: - from pytype.abstract import abstract # pylint: disable=g-import-not-at-top # pytype: disable=import-error - - for attr in dir(abstract): - if attr[0].isupper(): - _ISINSTANCE_CACHE[attr] = getattr(abstract, attr) - if name_or_names.__class__ == tuple: - class_or_classes = tuple(_ISINSTANCE_CACHE[name] for name in name_or_names) - else: - class_or_classes = _ISINSTANCE_CACHE[name_or_names] - return isinstance(obj, class_or_classes) + # Copy all the attributes from the module to this object. + # This is done so that subsequent attribute accesses will not even need to + # go through this __getattr__ method and will be resolved directly. + self.__dict__.update({ + attr: getattr(abstract, attr) + for attr in dir(abstract) + if attr[0].isupper() + }) + self._loaded = True + return object.__getattribute__(self, name) -def _make(cls_name, *args, **kwargs): - """Make an instance of cls_name with the given arguments.""" - from pytype.abstract import abstract # pylint: disable=g-import-not-at-top # pytype: disable=import-error - return getattr(abstract, cls_name)(*args, **kwargs) +if TYPE_CHECKING: + from pytype.abstract import abstract # pylint: disable=g-import-not-at-top,g-bad-import-order + + _abstract = abstract +else: + _abstract = _Abstract() # Sentinel for get_atomic_value @@ -400,16 +389,16 @@ def get_views( def equivalent_to(binding, cls): """Whether binding.data is equivalent to cls, modulo parameterization.""" return ( - _isinstance(binding.data, "Class") + isinstance(binding.data, _abstract.Class) and binding.data.full_name == cls.full_name ) def is_subclass(value, cls): """Whether value is a subclass of cls, modulo parameterization.""" - if _isinstance(value, "Union"): + if isinstance(value, _abstract.Union): return any(is_subclass(v, cls) for v in value.options) - return _isinstance(value, "Class") and any( + return isinstance(value, _abstract.Class) and any( value_cls.full_name == cls.full_name for value_cls in value.mro ) @@ -442,7 +431,7 @@ def get_mro_bases(bases): mro_bases.append(base) # check if it contains user-defined generic types if ( - _isinstance(base, "ParameterizedClass") + isinstance(base, _abstract.ParameterizedClass) and base.full_name != "typing.Generic" ): has_user_generic = True @@ -477,9 +466,9 @@ def _merge_type( Raises: GenericTypeError: if the types don't match. """ - if t0 is None or _isinstance(t0, "Unsolvable"): + if t0 is None or isinstance(t0, _abstract.Unsolvable): return t1 - if t1 is None or _isinstance(t1, "Unsolvable"): + if t1 is None or isinstance(t1, _abstract.Unsolvable): return t0 # t0 is a base of t1 if t0 in t1.mro: @@ -518,21 +507,23 @@ def merge( ) -> "_instance_base.SimpleValue": return _merge_type(t0, t1, name, base) - if _isinstance(base, "ParameterizedClass"): + if isinstance(base, _abstract.ParameterizedClass): if base.full_name == "typing.Generic": return - if _isinstance(base.base_cls, ("InterpreterClass", "PyTDClass")): # pytype: disable=attribute-error + if isinstance( + base.base_cls, (_abstract.InterpreterClass, _abstract.PyTDClass) + ): # merge the type parameters info from base class formal_type_parameters.merge_from( - base.base_cls.all_formal_type_parameters, merge # pytype: disable=attribute-error + base.base_cls.all_formal_type_parameters, merge ) - params = base.get_formal_type_parameters() # pytype: disable=attribute-error + params = base.get_formal_type_parameters() if hasattr(container, "cls"): container_template = container.cls.template else: container_template = () for name, param in params.items(): - if _isinstance(param, "TypeParameter"): + if isinstance(param, _abstract.TypeParameter): # We have type parameter renaming, e.g., # class List(Generic[T]): pass # class Foo(List[U]): pass @@ -553,15 +544,15 @@ def merge( # Two unrelated containers happen to use the same type # parameter but with different types. last_type = formal_type_parameters[name] - formal_type_parameters[name] = merge(last_type, param, name) + formal_type_parameters[name] = merge(last_type, param, name) # pytype: disable=wrong-arg-types else: - if _isinstance(base, ("InterpreterClass", "PyTDClass")): + if isinstance(base, (_abstract.InterpreterClass, _abstract.PyTDClass)): # merge the type parameters info from base class - formal_type_parameters.merge_from(base.all_formal_type_parameters, merge) # pytype: disable=attribute-error + formal_type_parameters.merge_from(base.all_formal_type_parameters, merge) if base.template: # handle unbound type parameters for item in base.template: - if _isinstance(item, "TypeParameter"): + if isinstance(item, _abstract.TypeParameter): # This type parameter will be set as `ANY`. name = full_type_name(base, item.name) if name not in formal_type_parameters: @@ -578,7 +569,7 @@ def full_type_name(val: "_instance_base.SimpleValue", name: str) -> str: Returns: The full type parameter name (e.g., List.T). """ - if _isinstance(val, "Instance"): + if isinstance(val, _abstract.Instance): return full_type_name(val.cls, name) # The type is in current `class` for t in val.template: @@ -597,7 +588,7 @@ def maybe_extract_tuple(t: cfg.Variable) -> tuple[cfg.Variable, ...]: if len(values) > 1: return (t,) (v,) = values - if not _isinstance(v, "Tuple"): + if not isinstance(v, _abstract.Tuple): return (t,) return v.pyval @@ -653,7 +644,7 @@ def match_type_container(typ, container_type_name: str | tuple[str, ...]): if isinstance(container_type_name, str): container_type_name = (container_type_name,) if not ( - _isinstance(typ, "ParameterizedClass") + isinstance(typ, _abstract.ParameterizedClass) and typ.full_name in container_type_name ): return None @@ -682,21 +673,21 @@ def get_annotations_dict( annots = get_atomic_value(annots_var) except ConversionError: return None - return annots if _isinstance(annots, "AnnotationsDict") else None + return annots if isinstance(annots, _abstract.AnnotationsDict) else None def is_concrete_dict(val: "_base.BaseValue") -> bool: - return val.is_concrete and _isinstance(val, "Dict") + return val.is_concrete and isinstance(val, _abstract.Dict) def is_concrete_list(val: "_base.BaseValue") -> bool: - return val.is_concrete and _isinstance(val, "List") + return val.is_concrete and isinstance(val, _abstract.List) def is_indefinite_iterable(val: "_base.BaseValue") -> bool: """True if val is a non-concrete instance of typing.Iterable.""" - instance = _isinstance(val, "Instance") - cls_instance = _isinstance(val.cls, "Class") + instance = isinstance(val, _abstract.Instance) + cls_instance = isinstance(val.cls, _abstract.Class) if not (instance and cls_instance and not val.is_concrete): return False for cls in val.cls.mro: @@ -705,7 +696,7 @@ def is_indefinite_iterable(val: "_base.BaseValue") -> bool: elif cls.full_name == "builtins.tuple": # A tuple's cls attribute may point to either PyTDClass(tuple) or # TupleClass; only the former is indefinite. - return _isinstance(cls, "PyTDClass") + return isinstance(cls, _abstract.PyTDClass) elif cls.full_name == "typing.Iterable": return True return False @@ -717,13 +708,16 @@ def is_var_indefinite_iterable(var: cfg.Variable) -> bool: def is_dataclass(val: "class_mixin.Class") -> bool: - # TODO: b/350643999 - _isinstance call possibly not needed. - return _isinstance(val, "Class") and "__dataclass_fields__" in val.metadata # pytype: disable=attribute-error + # TODO: b/350643999 - isinstance call possibly not needed. + return ( + isinstance(val, _abstract.Class) + and "__dataclass_fields__" in val.metadata + ) def is_attrs(val: "class_mixin.Class") -> bool: - # TODO: b/350643999 - _isinstance call possibly not needed. - return _isinstance(val, "Class") and "__attrs_attrs__" in val.metadata # pytype: disable=attribute-error + # TODO: b/350643999 - isinstance call possibly not needed. + return isinstance(val, _abstract.Class) and "__attrs_attrs__" in val.metadata def merged_type_parameter( @@ -741,7 +735,7 @@ def merged_type_parameter( # syntax to typeguard something which is on the property, we would need to # change the callsite to pass in var.data[0] instead. def is_var_splat(var: cfg.Variable) -> bool: - if var.data and _isinstance(var.data[0], "Splat"): + if var.data and isinstance(var.data[0], _abstract.Splat): # A splat should never have more than one binding, since we create and use # it immediately. assert len(var.bindings) == 1 @@ -755,11 +749,17 @@ def unwrap_splat(var: cfg.Variable) -> "cfg.Variable": def is_callable(value: "_base.BaseValue") -> bool: """Returns whether 'value' is a callable.""" - if _isinstance( - value, ("Function", "BoundFunction", "ClassMethod", "StaticMethod") + if isinstance( + value, + ( + _abstract.Function, + _abstract.BoundFunction, + _abstract.ClassMethod, + _abstract.StaticMethod, + ), ): return True - if not _isinstance(value.cls, "Class"): + if not isinstance(value.cls, _abstract.Class): return False _, attr = value.ctx.attribute_handler.get_attribute( value.ctx.root_node, value.cls, "__call__" @@ -775,7 +775,7 @@ def expand_type_parameter_instances( seen = set() while bindings: b = bindings.pop(0) - if _isinstance(b.data, "TypeParameterInstance"): + if isinstance(b.data, _abstract.TypeParameterInstance): if b.data in seen: continue seen.add(b.data) @@ -792,7 +792,7 @@ def get_type_parameter_substitutions( """Get values for type_params from val's type parameters.""" subst = {} for p in type_params: - if _isinstance(val, "Class"): + if isinstance(val, _abstract.Class): param_value = val.get_formal_type_parameter(p.name).instantiate( val.ctx.root_node ) @@ -806,7 +806,7 @@ def is_type_variable( val: "_base.BaseValue", ) -> "TypeGuard[pytd.TypeParameter|pytd.ParamSpec]": """Check if a value is a type variable (TypeVar or ParamSpec).""" - return _isinstance(val, ("TypeParameter", "ParamSpec")) + return isinstance(val, (_abstract.TypeParameter, _abstract.ParamSpec)) def build_generic_template( @@ -835,7 +835,7 @@ def build_generic_template( def is_generic_protocol(val: "_base.BaseValue") -> bool: return ( - _isinstance(val, "ParameterizedClass") + isinstance(val, _abstract.ParameterizedClass) and val.full_name == "typing.Protocol" ) @@ -874,26 +874,26 @@ def flatten( True iff a value was ignored during flattening. """ # Used by special_builtins.IsInstance and IsSubclass - if _isinstance(value, "AnnotationClass"): - value = value.base_cls # pytype: disable=attribute-error - if _isinstance(value, "Class"): + if isinstance(value, _abstract.AnnotationClass): + value = value.base_cls + if isinstance(value, _abstract.Class): # A single class, no ambiguity. - classes.append(value) # pytype: disable=container-type-mismatch + classes.append(value) return False - elif _isinstance(value, "Tuple"): + elif isinstance(value, _abstract.Tuple): # A tuple, need to process each element. ambiguous = False - for var in value.pyval: # pytype: disable=attribute-error + for var in value.pyval: if len(var.bindings) != 1 or flatten(var.bindings[0].data, classes): # There were either multiple bindings or ambiguity deeper in the # recursion. ambiguous = True return ambiguous - elif _isinstance(value, "Union"): + elif isinstance(value, _abstract.Union): # A Union cannot be used in an isinstance call before Python 3.10, but # there's no harm in processing it anyway. ambiguous = False - for val in value.options: # pytype: disable=attribute-error + for val in value.options: if flatten(val, classes): ambiguous = True return ambiguous @@ -945,10 +945,13 @@ def maybe_unwrap_decorated_function(func: "_function_base.Function"): def unwrap_final(val: "_base.BaseValue") -> "_base.BaseValue": """Unwrap Final[T] -> T.""" - if _isinstance(val, "FinalAnnotation"): + if isinstance(val, _abstract.FinalAnnotation): # Final type created via an annotation in the current module - return val.annotation # pytype: disable=attribute-error - elif _isinstance(val, "Instance") and val.cls.full_name == "typing.Final": + return val.annotation + elif ( + isinstance(val, _abstract.Instance) + and val.cls.full_name == "typing.Final" + ): # Final types loaded from a pyi file get converted to abstract.Instance # with cls=typing.Final and instance type parameter T return get_atomic_value(val.get_instance_type_parameter(T)) @@ -995,25 +998,25 @@ def get_generic_type( The type of the value, with concrete type parameters replaced by TypeVars. For example, the generic type of `[0]` is `List[T]`. """ - is_class = _isinstance(val, "Class") + is_class = isinstance(val, _abstract.Class) if is_class: cls = val - elif _isinstance(val.cls, "Class"): + elif isinstance(val.cls, _abstract.Class): cls = val.cls else: return None for parent_cls in cls.mro: - if _isinstance(parent_cls, "ParameterizedClass"): - base_cls = parent_cls.base_cls # pytype: disable=attribute-error + if isinstance(parent_cls, _abstract.ParameterizedClass): + base_cls = parent_cls.base_cls else: base_cls = parent_cls - if _isinstance(base_cls, "Class") and base_cls.template: + if isinstance(base_cls, _abstract.Class) and base_cls.template: ctx = base_cls.ctx params = {item.name: item for item in base_cls.template} - generic_cls = _make("ParameterizedClass", base_cls, params, ctx) + generic_cls = _abstract.ParameterizedClass(base_cls, params, ctx) if is_class: - return _make( - "ParameterizedClass", ctx.convert.type_type, {T: generic_cls}, ctx + return _abstract.ParameterizedClass( + ctx.convert.type_type, {T: generic_cls}, ctx ) else: return generic_cls diff --git a/pytype/abstract/class_mixin.py b/pytype/abstract/class_mixin.py index 8ac12f8b1..dbef413ca 100644 --- a/pytype/abstract/class_mixin.py +++ b/pytype/abstract/class_mixin.py @@ -3,7 +3,7 @@ from collections.abc import Mapping, Sequence import dataclasses import logging -from typing import Any +from typing import Any, TYPE_CHECKING from pytype import datatypes from pytype.abstract import abstract_utils @@ -13,10 +13,12 @@ from pytype.pytd import pytd from pytype.typegraph import cfg +if TYPE_CHECKING: + 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.getLogger(__name__) -_isinstance = abstract_utils._isinstance # pylint: disable=protected-access -_make = abstract_utils._make # pylint: disable=protected-access _InterpreterFunction = Any # can't import due to a circular dependency FunctionMapType = Mapping[str, Sequence[_InterpreterFunction]] @@ -238,13 +240,13 @@ def has_protocol_base(self): def _init_protocol_attributes(self): """Compute this class's protocol attributes.""" - if _isinstance(self, "ParameterizedClass"): + if isinstance(self, _abstract.ParameterizedClass): self.protocol_attributes = self.base_cls.protocol_attributes return if not self.has_protocol_base(): self.protocol_attributes = set() return - if _isinstance(self, "PyTDClass") and self.pytd_cls.name.startswith( + if isinstance(self, _abstract.PyTDClass) and self.pytd_cls.name.startswith( "typing." ): protocol_attributes = set() @@ -287,7 +289,7 @@ def _init_overrides_bool(self): """Compute and cache whether the class sets its own boolean value.""" # A class's instances can evaluate to False if it defines __bool__ or # __len__. - if _isinstance(self, "ParameterizedClass"): + if isinstance(self, _abstract.ParameterizedClass): self.overrides_bool = self.base_cls.overrides_bool return for cls in self.mro: @@ -328,7 +330,7 @@ def _has_implicit_abcmeta(self): """Whether the class should be considered implicitly abstract.""" # Protocols must be marked as abstract to get around the # [ignored-abstractmethod] check for interpreter classes. - if not _isinstance(self, "InterpreterClass"): + if not isinstance(self, _abstract.InterpreterClass): return False # We check self._bases (immediate bases) instead of self.mro because our # builtins and typing stubs are inconsistent about implementing abstract @@ -400,7 +402,9 @@ def call_metaclass_init(self, node): node, init = self.ctx.attribute_handler.get_attribute( node, self.cls, "__init__" ) - if not init or not any(_isinstance(f, "SignedFunction") for f in init.data): + if not init or not any( + isinstance(f, _abstract.SignedFunction) for f in init.data + ): # Only SignedFunctions (InterpreterFunction and SimpleFunction) have # interesting side effects. return node @@ -442,8 +446,8 @@ def get_own_new(self, node, value): return node, None if len(new.bindings) == 1: f = new.bindings[0].data - if _isinstance( - f, "AMBIGUOUS_OR_EMPTY" + if isinstance( + f, _abstract.AMBIGUOUS_OR_EMPTY ) or self.ctx.convert.object_type.is_object_new(f): # Instead of calling object.__new__, our abstract classes directly # create instances of themselves. @@ -490,7 +494,7 @@ def _new_instance(self, container, node, args): key = self.ctx.vm.current_opcode or node assert key if key not in self._instance_cache: - self._instance_cache[key] = _make("Instance", self, self.ctx, container) + self._instance_cache[key] = _abstract.Instance(self, self.ctx, container) # pytype: disable=wrong-arg-types return self._instance_cache[key] def _check_not_instantiable(self): @@ -506,8 +510,8 @@ def _check_not_instantiable(self): return if self.ctx.vm.frame and self.ctx.vm.frame.func: calling_func = self.ctx.vm.frame.func.data - if _isinstance( - calling_func, "InterpreterFunction" + if isinstance( + calling_func, _abstract.InterpreterFunction ) and calling_func.name.startswith(f"{self.name}."): return self.ctx.errorlog.not_instantiable(self.ctx.vm.frames, self) @@ -567,7 +571,7 @@ def compute_mro(self): for row in bases: baselist = [] for base in row: - if _isinstance(base, "ParameterizedClass"): + if isinstance(base, _abstract.ParameterizedClass): base2cls[base.base_cls] = base baselist.append(base.base_cls) else: @@ -638,7 +642,7 @@ def _get_attrs_from_mro(self, cls_attrs, metadata_key): # Any subclass of a Parameterized dataclass must inherit attributes from # its parent's init. # See https://github.com/google/pytype/issues/1104 - if _isinstance(base_cls, "ParameterizedClass"): + if isinstance(base_cls, _abstract.ParameterizedClass): type_params = base_cls.formal_type_parameters base_cls = base_cls.base_cls if metadata_key in base_cls.metadata: diff --git a/pytype/abstract/function.py b/pytype/abstract/function.py index a83f55ef1..70422ce07 100644 --- a/pytype/abstract/function.py +++ b/pytype/abstract/function.py @@ -30,11 +30,12 @@ from pytype.abstract import _instance_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.pyc import opcodes # 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 -_make = abstract_utils._make # pylint: disable=protected-access def argname(i: int) -> str: @@ -44,26 +45,22 @@ def argname(i: int) -> str: def get_signatures(func: "_function_base.Function") -> "list[Signature]": """Gets the given function's signatures.""" - if _isinstance(func, "PyTDFunction"): + if isinstance(func, _abstract.PyTDFunction): # TODO: b/350643999 - There's something going wrong here. This is # implemented as a non-property method and it's not being called but yet # this code somehow doesn't get complained in tests. This will either crash # or not run upon reaching this part. - return [sig.signature for sig in func.signatures] # pytype: disable=attribute-error - elif _isinstance(func, "InterpreterFunction"): - f: "_interpreter_function.InterpreterFunction" = func # pytype: disable=annotation-type-mismatch - return [f.signature for f in f.signature_functions()] - elif _isinstance(func, "BoundFunction"): - f: "_function_base.BoundFunction" = func # pytype: disable=annotation-type-mismatch - sigs = get_signatures(f.underlying) + return [sig.signature for sig in func.signatures] + elif isinstance(func, _abstract.InterpreterFunction): + return [f.signature for f in func.signature_functions()] + elif isinstance(func, _abstract.BoundFunction): + sigs = get_signatures(func.underlying) return [sig.drop_first_parameter() for sig in sigs] # drop "self" - elif _isinstance(func, ("ClassMethod", "StaticMethod")): - f: "_function_base.ClassMethod | _function_base.StaticMethod" = func # pytype: disable=annotation-type-mismatch - return get_signatures(f.method) - elif _isinstance(func, "SignedFunction"): - f: "_function_base.SignedFunction" = func # pytype: disable=annotation-type-mismatch - return [f.signature] - elif _isinstance(func, "AMBIGUOUS_OR_EMPTY"): + elif isinstance(func, (_abstract.ClassMethod, _abstract.StaticMethod)): + return get_signatures(func.method) + elif isinstance(func, _abstract.SignedFunction): + return [func.signature] + elif isinstance(func, _abstract.AMBIGUOUS_OR_EMPTY): return [Signature.from_any()] elif func.__class__.__name__ == "PropertyInstance": # NOTE: We typically do not want to treat a PropertyInstance as a callable. @@ -73,8 +70,8 @@ def get_signatures(func: "_function_base.Function") -> "list[Signature]": # @property # def f()... return [] - elif _isinstance(func.cls, "CallableClass"): - return [Signature.from_callable(func.cls)] # pytype: disable=wrong-arg-types + elif isinstance(func.cls, _abstract.CallableClass): + return [Signature.from_callable(func.cls)] else: unwrapped = abstract_utils.maybe_unwrap_decorated_function(func) if unwrapped: @@ -83,7 +80,7 @@ def get_signatures(func: "_function_base.Function") -> "list[Signature]": get_signatures(f) for f in unwrapped.data ) ) - if _isinstance(func, "Instance"): + if isinstance(func, _abstract.Instance): _, call_var = func.ctx.attribute_handler.get_attribute( func.ctx.root_node, func, @@ -184,18 +181,15 @@ def _postprocess_annotation( """Postprocess the given annotation.""" ctx = annotation.ctx if name == self.varargs_name: - return _make( - "ParameterizedClass", - ctx.convert.tuple_type, - {abstract_utils.T: annotation}, - ctx, + return _abstract.ParameterizedClass( + ctx.convert.tuple_type, {abstract_utils.T: annotation}, ctx ) elif name == self.kwargs_name: params = { abstract_utils.K: ctx.convert.str_type, abstract_utils.V: annotation, } - return _make("ParameterizedClass", ctx.convert.dict_type, params, ctx) + return _abstract.ParameterizedClass(ctx.convert.dict_type, params, ctx) else: return annotation @@ -293,15 +287,15 @@ def _make_concatenated_type( Returns: A new Concatenate object, or None if type2 cannot be concatenated to. """ - if _isinstance(type2, "ParamSpec"): + if isinstance(type2, _abstract.ParamSpec): new_args = [type1, type2] - elif _isinstance(type2, "Concatenate"): + elif isinstance(type2, _abstract.Concatenate): # Pytype can't understand this custom isinstance check. type2 = cast(Any, type2) new_args = [type1] + type2.args + [type2.paramspec] else: return None - return _make("Concatenate", new_args, type1.ctx) + return _abstract.Concatenate(new_args, type1.ctx) def prepend_parameter(self: _SigT, name: str, typ: _base.BaseValue) -> _SigT: """Returns a new signature with a `name: typ` param added to the front.""" @@ -676,7 +670,7 @@ def starstarargs_as_dict(self): if not self.starstarargs or len(self.starstarargs.data) != 1: return None (kwdict,) = self.starstarargs.data - if not _isinstance(kwdict, "Dict"): + if not isinstance(kwdict, _abstract.Dict): return None return kwdict.pyval @@ -807,9 +801,9 @@ def simplify( # into starstarargs, so set starstarargs to None. assert starstarargs is not None kwdict = starstarargs.data[0] - if _isinstance(kwdict, "Dict") and not kwdict.is_concrete: + if isinstance(kwdict, _abstract.Dict) and not kwdict.is_concrete: cls = kwdict.cls - if _isinstance(cls, "PyTDClass"): + if isinstance(cls, _abstract.PyTDClass): # If cls is not already parameterized with the key and value types, we # parameterize it now to preserve them. params = { @@ -818,7 +812,7 @@ def simplify( ) for name in (abstract_utils.K, abstract_utils.V) } - cls = _make("ParameterizedClass", ctx.convert.dict_type, params, ctx) + cls = _abstract.ParameterizedClass(ctx.convert.dict_type, params, ctx) starstarargs = cls.instantiate(node) else: starstarargs = None @@ -877,7 +871,7 @@ def replace(self, **kwargs) -> "Args": def has_opaque_starargs_or_starstarargs(self) -> bool: return any( - arg and not _isinstance(arg, "PythonConstant") + arg and not isinstance(arg, _abstract.PythonConstant) for arg in (self.starargs, self.starstarargs) ) @@ -900,8 +894,8 @@ def instantiate( return self.to_variable(node) def prefix(self) -> "tuple[_typing.ParamSpec, ...]": - if _isinstance(self.paramspec, "Concatenate"): - return self.paramspec.args # pytype: disable=attribute-error + if isinstance(self.paramspec, _abstract.Concatenate): + return self.paramspec.args else: return () @@ -1108,16 +1102,16 @@ def call_function( elif ctx.options.precise_return and len(func_var.bindings) == 1: (funcb,) = func_var.bindings func = funcb.data - if _isinstance(func, "BoundFunction"): + if isinstance(func, _abstract.BoundFunction): func = func.underlying - if _isinstance(func, "PyTDFunction"): + if isinstance(func, _abstract.PyTDFunction): node, result = PyTDReturnType( func.signatures[0].pytd_sig.return_type, datatypes.HashableDict(), [funcb], ctx, ).instantiate(node) - elif _isinstance(func, "InterpreterFunction"): + elif isinstance(func, _abstract.InterpreterFunction): sig = func.signature_functions()[0].signature ret = sig.annotations.get("return", ctx.convert.unsolvable) result = ctx.vm.init_class(node, ret) @@ -1311,13 +1305,13 @@ def build_paramspec_signature( for i, typ in enumerate(r_args): name = f"_{i}" ret_posargs.append(name) - if not _isinstance(typ, "BaseValue"): + if not isinstance(typ, _abstract.BaseValue): typ = ctx.convert.constant_to_value(typ) ann[name] = typ # We have done prefix type matching in the matcher, so we can safely strip # off the lhs args from the sig by count. lhs = pspec_match.paramspec - l_nargs = len(lhs.args) if _isinstance(lhs, "Concatenate") else 0 + l_nargs = len(lhs.args) if isinstance(lhs, _abstract.Concatenate) else 0 param_names = tuple(ret_posargs) + sig.param_names[l_nargs:] # All params need to be in the annotations dict or output.py crashes sig.populate_annotation_dict(ann, ctx, param_names) diff --git a/pytype/abstract/mixin.py b/pytype/abstract/mixin.py index 52611eaa8..f2fcbc9ff 100644 --- a/pytype/abstract/mixin.py +++ b/pytype/abstract/mixin.py @@ -1,7 +1,7 @@ """Mixins for abstract.py.""" import logging -from typing import Any +from typing import Any, TYPE_CHECKING from pytype.abstract import abstract_utils from pytype.abstract import function @@ -10,9 +10,12 @@ from pytype.typegraph import cfg from pytype.types import types +if TYPE_CHECKING: + 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.getLogger(__name__) -_isinstance = abstract_utils._isinstance # pylint: disable=protected-access -_make = abstract_utils._make # pylint: disable=protected-access class MixinMeta(type): @@ -128,7 +131,11 @@ def set_slot(self, name, slot): # For getting a slot value, we don't need a ParameterizedClass's type # parameters, and evaluating them in the middle of constructing the class # can trigger a recursion error, so use only the base class. - base = self.base_cls if _isinstance(self, "ParameterizedClass") else self + base = ( + self.base_cls + if isinstance(self, _abstract.ParameterizedClass) + else self + ) _, attr = self.ctx.attribute_handler.get_attribute( self.ctx.root_node, base, name, base.to_binding(self.ctx.root_node) ) @@ -137,7 +144,7 @@ def set_slot(self, name, slot): def set_native_slot(self, name, method): """Add a new NativeFunction slot to this value.""" - self.set_slot(name, _make("NativeFunction", name, method, self.ctx)) + self.set_slot(name, _abstract.NativeFunction(name, method, self.ctx)) def call_pytd(self, node, name, *args): """Call the (original) pytd version of a method we overwrote."""