diff --git a/rope/base/builtins.py b/rope/base/builtins.py index 151c64742..94755dcda 100644 --- a/rope/base/builtins.py +++ b/rope/base/builtins.py @@ -4,11 +4,12 @@ import rope.base.evaluate from rope.base import arguments, ast, pynames, pyobjects, utils +from rope.base.pyobjects import get_base_type -class BuiltinModule(pyobjects.AbstractModule): +class BuiltinModule(pyobjects.PyObject, pyobjects.AbstractModule): def __init__(self, name, pycore=None, initial={}): - super().__init__() + super().__init__(get_base_type("Module")) self.name = name self.pycore = pycore self.initial = initial @@ -48,8 +49,9 @@ def module(self): return -class _BuiltinElement: - def __init__(self, builtin, parent=None): +class _BuiltinElement(pyobjects.PyObject): + def __init__(self, builtin, parent=None, type_=None): + super().__init__(type_) self.builtin = builtin self._parent = parent @@ -70,8 +72,7 @@ def parent(self): class BuiltinClass(_BuiltinElement, pyobjects.AbstractClass): def __init__(self, builtin, attributes, parent=None): - _BuiltinElement.__init__(self, builtin, parent) - pyobjects.AbstractClass.__init__(self) + super().__init__(builtin, parent, type_=get_base_type("Type")) self.initial = attributes @utils.saveit @@ -83,13 +84,15 @@ def get_attributes(self): def get_module(self): return builtins + def get_superclasses(self): + return [] + class BuiltinFunction(_BuiltinElement, pyobjects.AbstractFunction): def __init__( self, returned=None, function=None, builtin=None, argnames=[], parent=None ): - _BuiltinElement.__init__(self, builtin, parent) - pyobjects.AbstractFunction.__init__(self) + super().__init__(builtin, parent, type_=get_base_type("Function")) self.argnames = argnames self.returned = returned self.function = function @@ -580,9 +583,9 @@ def get_definition_location(self): return (None, None) -class Iterator(pyobjects.AbstractClass): +class Iterator(pyobjects.PyObject, pyobjects.AbstractClass): def __init__(self, holding=None): - super().__init__() + super().__init__(get_base_type("Type")) self.holding = holding self.attributes = { "next": BuiltinName(BuiltinFunction(self.holding)), @@ -599,9 +602,9 @@ def get_returned_object(self, args): get_iterator = _create_builtin_getter(Iterator) -class Generator(pyobjects.AbstractClass): +class Generator(pyobjects.PyObject, pyobjects.AbstractClass): def __init__(self, holding=None): - super().__init__() + super().__init__(get_base_type("Type")) self.holding = holding self.attributes = { "next": BuiltinName(BuiltinFunction(self.holding)), @@ -681,9 +684,9 @@ def _property_function(args): return pyobjects.PyObject(Property(parameters[0])) -class Lambda(pyobjects.AbstractFunction): +class Lambda(pyobjects.PyObject, pyobjects.AbstractFunction): def __init__(self, node, scope): - super().__init__() + super().__init__(get_base_type("Function")) self.node = node self.arguments = node.args self.scope = scope diff --git a/rope/base/oi/type_hinting/utils.py b/rope/base/oi/type_hinting/utils.py index b0a7aff97..99c2a80f1 100644 --- a/rope/base/oi/type_hinting/utils.py +++ b/rope/base/oi/type_hinting/utils.py @@ -1,12 +1,11 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Optional, Union import rope.base.utils as base_utils from rope.base import evaluate from rope.base.exceptions import AttributeNotFoundError -from rope.base.pyobjects import PyClass, PyDefinedObject, PyFunction, PyObject +from rope.base.pyobjects import PyClass, PyFunction, PyObject def get_super_func(pyfunc): @@ -74,10 +73,7 @@ def get_mro(pyclass): return class_list -def resolve_type( - type_name: str, - pyobject: Union[PyDefinedObject, PyObject], -) -> Optional[Union[PyDefinedObject, PyObject]]: +def resolve_type(type_name, pyobject: PyObject) -> PyObject: # No need for Union. """ Find proper type object from its name. """ diff --git a/rope/base/pyobjects.py b/rope/base/pyobjects.py index bb1226f92..148515b23 100644 --- a/rope/base/pyobjects.py +++ b/rope/base/pyobjects.py @@ -5,9 +5,7 @@ class PyObject: def __init__(self, type_): - if type_ is None: - type_ = self - self.type = type_ + self.type = self if type_ is None else type_ def get_attributes(self): if self.type is self: @@ -66,22 +64,11 @@ def __iter__(self): """The same as ``iter(self.get_attributes())``""" return iter(self.get_attributes()) - _types = None - _unknown = None - @staticmethod - def _get_base_type(name): - if PyObject._types is None: - PyObject._types = {} - base_type = PyObject(None) - PyObject._types["Type"] = base_type - PyObject._types["Module"] = PyObject(base_type) - PyObject._types["Function"] = PyObject(base_type) - PyObject._types["Unknown"] = PyObject(base_type) - return PyObject._types[name] +_anchor_types = None -def get_base_type(name): +def get_base_type(name: str) -> PyObject: """Return the base type with name `name`. The base types are 'Type', 'Function', 'Module' and 'Unknown'. It @@ -89,83 +76,75 @@ def get_base_type(name): is discouraged. Use classes defined in this module instead. For example instead of ``pyobject.get_type() == get_base_type('Function')`` use - ``isinstance(pyobject, AbstractFunction)``. + ``is_abstract_function(pyobject)``. - You can use `AbstractClass` for classes, `AbstractFunction` for - functions, and `AbstractModule` for modules. You can also use - `PyFunction` and `PyClass` for testing if an object is - defined somewhere and rope can access its source. These classes - provide more methods. + You can use `is_abstract_class` for classes, `is_abstract_functions` for + functions, and `is_abstract_module` for modules. """ - return PyObject._get_base_type(name) + global _anchor_types + if _anchor_types is None: + base_type = PyObject(None) + _anchor_types = { + "Function": PyObject(base_type), + "Module": PyObject(base_type), + "Type": base_type, # a Class. + "Unknown": PyObject(base_type), + } + return _anchor_types[name] -def get_unknown(): - """Return a pyobject whose type is unknown +def get_unknown() -> PyObject: + """Return a pyobject whose type is unknown. - Note that two unknown objects are equal. So for example you can - write:: + Note that two unknown objects are equal. + + For example you can write:: if pyname.get_object() == get_unknown(): print('cannot determine what this pyname holds') Rope could have used `None` for indicating unknown objects but - we had to check that in many places. So actually this method - returns a null object. - + we had to check that in many places. """ - if PyObject._unknown is None: - PyObject._unknown = PyObject(get_base_type("Unknown")) - return PyObject._unknown + return get_base_type("Unknown") -class AbstractClass(PyObject): - def __init__(self): - super().__init__(get_base_type("Type")) - - def get_name(self): - pass - - def get_doc(self): - pass - - def get_superclasses(self): - return [] +class AbstractClass: + """ + A mixin class that marks a class as the base of Rope's type hierarchy. + Rope contains many tests of the form `isinstance(obj, AbstractClass) + """ -class AbstractFunction(PyObject): - def __init__(self): - super().__init__(get_base_type("Function")) + pass - def get_name(self): - pass - def get_doc(self): - pass +class AbstractFunction: + """ + A mixin class that marks a function as the base of Rope's type hierarchy. - def get_param_names(self, special_args=True): - return [] + Rope contains many tests of the form `isinstance(obj, AbstractFunction) + """ - def get_returned_object(self, args): - return get_unknown() + pass -class AbstractModule(PyObject): - def __init__(self, doc=None): - super().__init__(get_base_type("Module")) +class AbstractModule: + """ + A mixin class that marks a module as the base of Rope's type hierarchy. - def get_doc(self): - pass + Rope contains many tests of the form `isinstance(obj, AbstractModule). + """ - def get_resource(self): - pass + pass -class PyDefinedObject: +class PyDefinedObject(PyObject): """Python defined names that rope can access their sources""" - def __init__(self, pycore, ast_node, parent): + def __init__(self, pycore, ast_node, parent, type_): + super().__init__(type_) self.pycore = pycore self.ast_node = ast_node self.scope = None @@ -265,10 +244,19 @@ def _create_scope(self): class PyFunction(PyDefinedObject, AbstractFunction): - pass + def __init__(self, pycore, ast_node, parent): + super().__init__(pycore, ast_node, parent, get_base_type("Function")) + + # From AbstractFunction. + def get_param_names(self, special_args=True): + return [] -class PyComprehension(PyDefinedObject, PyObject): + def get_returned_object(self, args): + return get_unknown() + + +class PyComprehension(PyDefinedObject): pass def get_name(self): @@ -276,7 +264,13 @@ def get_name(self): class PyClass(PyDefinedObject, AbstractClass): - pass + def __init__(self, pycore, ast_node, parent): + super().__init__(pycore, ast_node, parent, get_base_type("Type")) + + # From AbstractClass. + + def get_superclasses(self): + return [] class _ConcludedData: @@ -299,11 +293,12 @@ def __str__(self): class _PyModule(PyDefinedObject, AbstractModule): + """The base class for PyModule and PyPackage.""" + def __init__(self, pycore, ast_node, resource): self.resource = resource self.concluded_data = [] - AbstractModule.__init__(self) - PyDefinedObject.__init__(self, pycore, ast_node, None) + super().__init__(pycore, ast_node, None, get_base_type("Module")) @property def absolute_name(self) -> str: @@ -323,6 +318,10 @@ def get_resource(self): class PyModule(_PyModule): + """ + A "target" class that allows tests of the form `isinstance(obj, pyobjects.PyModule)`. + """ + pass diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index 95bd15691..d4c73f8da 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -18,8 +18,7 @@ class PyFunction(pyobjects.PyFunction): def __init__(self, pycore, ast_node, parent): - rope.base.pyobjects.AbstractFunction.__init__(self) - rope.base.pyobjects.PyDefinedObject.__init__(self, pycore, ast_node, parent) + super().__init__(pycore, ast_node, parent) self.arguments = self.ast_node.args self.parameter_pyobjects = pynamesdef._Inferred( self._infer_parameters, self.get_module()._get_concluded_data() @@ -114,8 +113,7 @@ def decorators(self): class PyComprehension(pyobjects.PyComprehension): def __init__(self, pycore, ast_node, parent): self.visitor_class = _ComprehensionVisitor - rope.base.pyobjects.PyObject.__init__(self, type_="Comp") - rope.base.pyobjects.PyDefinedObject.__init__(self, pycore, ast_node, parent) + super().__init__(pycore, ast_node, parent, type_="Comp") def _create_scope(self): return rope.base.pyscopes.ComprehensionScope( @@ -129,8 +127,7 @@ def get_kind(self): class PyClass(pyobjects.PyClass): def __init__(self, pycore, ast_node, parent): self.visitor_class = _ClassVisitor - rope.base.pyobjects.AbstractClass.__init__(self) - rope.base.pyobjects.PyDefinedObject.__init__(self, pycore, ast_node, parent) + super().__init__(pycore, ast_node, parent) self.parent = parent self._superclasses = self.get_module()._get_concluded_data() @@ -400,7 +397,7 @@ def _Slice(self, node): class _ScopeVisitor(_ExpressionVisitor): def __init__(self, pycore, owner_object): - _ExpressionVisitor.__init__(self, scope_visitor=self) + super().__init__(scope_visitor=self) self.pycore = pycore self.owner_object = owner_object self.names = {}