from rope.base.fscommands import _decode_data from rope.base import ast, exceptions, utils class PyObject(object): def __init__(self, type_): if type_ is None: type_ = self self.type = type_ def get_attributes(self): if self.type is self: return {} return self.type.get_attributes() def get_attribute(self, name): if name not in self.get_attributes(): raise exceptions.AttributeNotFoundError("Attribute %s not found" % name) return self.get_attributes()[name] def get_type(self): return self.type def __getitem__(self, key): """The same as ``get_attribute(key)``""" return self.get_attribute(key) def __contains__(self, key): """The same as ``key in self.get_attributes()``""" return key in self.get_attributes() def __eq__(self, obj): """Check the equality of two `PyObject` Currently it is assumed that instances (the direct instances of `PyObject`, not the instances of its subclasses) are equal if their types are equal. For every other object like defineds or builtins rope assumes objects are reference objects and their identities should match. """ if self.__class__ != obj.__class__: return False if type(self) == PyObject: if self is not self.type: return self.type == obj.type else: return self.type is obj.type return self is obj def __ne__(self, obj): return not self.__eq__(obj) def __hash__(self): """See docs for `__eq__()` method""" if type(self) == PyObject and self != self.type: return hash(self.type) + 1 else: return super(PyObject, self).__hash__() 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] def get_base_type(name): """Return the base type with name `name`. The base types are 'Type', 'Function', 'Module' and 'Unknown'. It was used to check the type of a `PyObject` but currently its use is discouraged. Use classes defined in this module instead. For example instead of ``pyobject.get_type() == get_base_type('Function')`` use ``isinstance(pyobject, AbstractFunction)``. 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. """ return PyObject._get_base_type(name) def get_unknown(): """Return a pyobject whose type is unknown Note that two unknown objects are equal. So 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. """ if PyObject._unknown is None: PyObject._unknown = PyObject(get_base_type("Unknown")) return PyObject._unknown class AbstractClass(PyObject): def __init__(self): super(AbstractClass, self).__init__(get_base_type("Type")) def get_name(self): pass def get_doc(self): pass def get_superclasses(self): return [] class AbstractFunction(PyObject): def __init__(self): super(AbstractFunction, self).__init__(get_base_type("Function")) def get_name(self): pass def get_doc(self): pass def get_param_names(self, special_args=True): return [] def get_returned_object(self, args): return get_unknown() class AbstractModule(PyObject): def __init__(self, doc=None): super(AbstractModule, self).__init__(get_base_type("Module")) def get_doc(self): pass def get_resource(self): pass class PyDefinedObject(object): """Python defined names that rope can access their sources""" def __init__(self, pycore, ast_node, parent): self.pycore = pycore self.ast_node = ast_node self.scope = None self.parent = parent self.structural_attributes = None self.concluded_attributes = self.get_module()._get_concluded_data() self.attributes = self.get_module()._get_concluded_data() self.defineds = None visitor_class = None @utils.prevent_recursion(lambda: {}) def _get_structural_attributes(self): if self.structural_attributes is None: self.structural_attributes = self._create_structural_attributes() return self.structural_attributes @utils.prevent_recursion(lambda: {}) def _get_concluded_attributes(self): if self.concluded_attributes.get() is None: self._get_structural_attributes() self.concluded_attributes.set(self._create_concluded_attributes()) return self.concluded_attributes.get() def get_attributes(self): if self.attributes.get() is None: result = dict(self._get_concluded_attributes()) result.update(self._get_structural_attributes()) self.attributes.set(result) return self.attributes.get() def get_attribute(self, name): if name in self._get_structural_attributes(): return self._get_structural_attributes()[name] if name in self._get_concluded_attributes(): return self._get_concluded_attributes()[name] raise exceptions.AttributeNotFoundError("Attribute %s not found" % name) def get_scope(self): if self.scope is None: self.scope = self._create_scope() return self.scope def get_module(self): current_object = self while current_object.parent is not None: current_object = current_object.parent return current_object def get_doc(self): if len(self.get_ast().body) > 0: expr = self.get_ast().body[0] if isinstance(expr, ast.Expr) and isinstance(expr.value, ast.Str): docstring = expr.value.s coding = self.get_module().coding return _decode_data(docstring, coding) def _get_defined_objects(self): if self.defineds is None: self._get_structural_attributes() return self.defineds def _create_structural_attributes(self): if self.visitor_class is None: return {} new_visitor = self.visitor_class(self.pycore, self) for child in ast.get_child_nodes(self.ast_node): ast.walk(child, new_visitor) self.defineds = new_visitor.defineds return new_visitor.names def _create_concluded_attributes(self): return {} def get_ast(self): return self.ast_node def _create_scope(self): pass class PyFunction(PyDefinedObject, AbstractFunction): """Only a placeholder""" class PyComprehension(PyDefinedObject, PyObject): """Only a placeholder""" class PyClass(PyDefinedObject, AbstractClass): """Only a placeholder""" class _ConcludedData(object): def __init__(self): self.data_ = None def set(self, data): self.data_ = data def get(self): return self.data_ data = property(get, set) def _invalidate(self): self.data = None def __str__(self): return "<" + str(self.data) + ">" class _PyModule(PyDefinedObject, AbstractModule): def __init__(self, pycore, ast_node, resource): self.resource = resource self.concluded_data = [] AbstractModule.__init__(self) PyDefinedObject.__init__(self, pycore, ast_node, None) def _get_concluded_data(self): new_data = _ConcludedData() self.concluded_data.append(new_data) return new_data def _forget_concluded_data(self): for data in self.concluded_data: data._invalidate() def get_resource(self): return self.resource class PyModule(_PyModule): """Only a placeholder""" class PyPackage(_PyModule): """Only a placeholder""" class IsBeingInferredError(exceptions.RopeError): pass