Source

rope_py3k / rope / base / pyobjects.py

Full commit
from rope.base import exceptions, ast


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`\s

        Currently it is assumed that two instances(The direct instances
        of `PyObject` are equal if their types are equal.  For every
        other object like defineds or builtins we assume objects are
        reference objects.

        """
        if self.__class__ != obj.__class__:
            return False
        if type(self) == PyObject:
            if self is not self.type:
                self.type == obj.type
            else:
                return self.type is obj.type
        return self is 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__()

    _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 'Object of pyname is not known'

    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()

    def _get_structural_attributes(self):
        if self.structural_attributes is None:
            self.structural_attributes = self._create_structural_attributes()
        return self.structural_attributes

    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):
                return expr.value.s

    def _create_structural_attributes(self):
        return {}

    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 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 = []
        self._is_valid = True
        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

    def is_valid(self):
        return self._is_valid

    def invalidate(self):
        self._is_valid = False


class PyModule(_PyModule):
    """Only a placeholder"""


class PyPackage(_PyModule):
    """Only a placeholder"""


class IsBeingInferredError(exceptions.RopeError):
    pass