vim/sadness/ropevim/src/rope/rope/base/pyobjectsdef.py @ 48cacfdc2ca6

vim: add ropevim
author Steve Losh <steve@stevelosh.com>
date Wed, 06 Oct 2010 12:30:46 -0400
parents (none)
children (none)
import rope.base.codeanalyze
import rope.base.evaluate
import rope.base.builtins
import rope.base.oi.soi
import rope.base.pyscopes
from rope.base import (pynamesdef as pynames, exceptions, ast,
                       astutils, pyobjects, fscommands, arguments, utils)
from rope.base.pyobjects import *


class PyFunction(pyobjects.PyFunction):

    def __init__(self, pycore, ast_node, parent):
        AbstractFunction.__init__(self)
        PyDefinedObject.__init__(self, pycore, ast_node, parent)
        self.arguments = self.ast_node.args
        self.parameter_pyobjects = pynames._Inferred(
            self._infer_parameters, self.get_module()._get_concluded_data())
        self.returned = pynames._Inferred(self._infer_returned)
        self.parameter_pynames = None

    def _create_structural_attributes(self):
        return {}

    def _create_concluded_attributes(self):
        return {}

    def _create_scope(self):
        return rope.base.pyscopes.FunctionScope(self.pycore, self,
                                                _FunctionVisitor)

    def _infer_parameters(self):
        pyobjects = rope.base.oi.soi.infer_parameter_objects(self)
        self._handle_special_args(pyobjects)
        return pyobjects

    def _infer_returned(self, args=None):
        return rope.base.oi.soi.infer_returned_object(self, args)

    def _handle_special_args(self, pyobjects):
        if len(pyobjects) == len(self.arguments.args):
            if self.arguments.vararg:
                pyobjects.append(rope.base.builtins.get_list())
            if self.arguments.kwarg:
                pyobjects.append(rope.base.builtins.get_dict())

    def _set_parameter_pyobjects(self, pyobjects):
        if pyobjects is not None:
            self._handle_special_args(pyobjects)
        self.parameter_pyobjects.set(pyobjects)

    def get_parameters(self):
        if self.parameter_pynames is None:
            result = {}
            for index, name in enumerate(self.get_param_names()):
                # TODO: handle tuple parameters
                result[name] = pynames.ParameterName(self, index)
            self.parameter_pynames = result
        return self.parameter_pynames

    def get_parameter(self, index):
        if index < len(self.parameter_pyobjects.get()):
            return self.parameter_pyobjects.get()[index]

    def get_returned_object(self, args):
        return self.returned.get(args)

    def get_name(self):
        return self.get_ast().name

    def get_param_names(self, special_args=True):
        # TODO: handle tuple parameters
        result = [node.id for node in self.arguments.args
                  if isinstance(node, ast.Name)]
        if special_args:
            if self.arguments.vararg:
                result.append(self.arguments.vararg)
            if self.arguments.kwarg:
                result.append(self.arguments.kwarg)
        return result

    def get_kind(self):
        """Get function type

        It returns one of 'function', 'method', 'staticmethod' or
        'classmethod' strs.

        """
        scope = self.parent.get_scope()
        if isinstance(self.parent, PyClass):
            for decorator in self.decorators:
                pyname = rope.base.evaluate.eval_node(scope, decorator)
                if pyname == rope.base.builtins.builtins['staticmethod']:
                    return 'staticmethod'
                if pyname == rope.base.builtins.builtins['classmethod']:
                    return 'classmethod'
            return 'method'
        return 'function'

    @property
    def decorators(self):
        try:
            return getattr(self.ast_node, 'decorator_list')
        except AttributeError:
            return getattr(self.ast_node, 'decorators', None)


class PyClass(pyobjects.PyClass):

    def __init__(self, pycore, ast_node, parent):
        self.visitor_class = _ClassVisitor
        AbstractClass.__init__(self)
        PyDefinedObject.__init__(self, pycore, ast_node, parent)
        self.parent = parent
        self._superclasses = self.get_module()._get_concluded_data()

    def get_superclasses(self):
        if self._superclasses.get() is None:
            self._superclasses.set(self._get_bases())
        return self._superclasses.get()

    def get_name(self):
        return self.get_ast().name

    def _create_concluded_attributes(self):
        result = {}
        for base in reversed(self.get_superclasses()):
            result.update(base.get_attributes())
        return result

    def _get_bases(self):
        result = []
        for base_name in self.ast_node.bases:
            base = rope.base.evaluate.eval_node(self.parent.get_scope(),
                                                base_name)
            if base is not None and \
               base.get_object().get_type() == get_base_type('Type'):
                result.append(base.get_object())
        return result

    def _create_scope(self):
        return rope.base.pyscopes.ClassScope(self.pycore, self)


class PyModule(pyobjects.PyModule):

    def __init__(self, pycore, source=None,
                 resource=None, force_errors=False):
        ignore = pycore.project.prefs.get('ignore_syntax_errors', False)
        syntax_errors = force_errors or not ignore
        self.has_errors = False
        try:
            source, node = self._init_source(pycore, source, resource)
        except exceptions.ModuleSyntaxError:
            self.has_errors = True
            if syntax_errors:
                raise
            else:
                source = '\n'
                node = ast.parse('\n')
        self.source_code = source
        self.star_imports = []
        self.visitor_class = _GlobalVisitor
        self.coding = fscommands.read_str_coding(self.source_code)
        super(PyModule, self).__init__(pycore, node, resource)

    def _init_source(self, pycore, source_code, resource):
        filename = 'string'
        if resource:
            filename = resource.path
        try:
            if source_code is None:
                source_bytes = resource.read_bytes()
                source_code = fscommands.file_data_to_unicode(source_bytes)
            else:
                if isinstance(source_code, unicode):
                    source_bytes = fscommands.unicode_to_file_data(source_code)
                else:
                    source_bytes = source_code
            ast_node = ast.parse(source_bytes, filename=filename)
        except SyntaxError, e:
            raise exceptions.ModuleSyntaxError(filename, e.lineno, e.msg)
        except UnicodeDecodeError, e:
            raise exceptions.ModuleSyntaxError(filename, 1, '%s' % (e.reason))
        return source_code, ast_node

    @utils.prevent_recursion(lambda: {})
    def _create_concluded_attributes(self):
        result = {}
        for star_import in self.star_imports:
            result.update(star_import.get_names())
        return result

    def _create_scope(self):
        return rope.base.pyscopes.GlobalScope(self.pycore, self)

    @property
    @utils.saveit
    def lines(self):
        """A `SourceLinesAdapter`"""
        return rope.base.codeanalyze.SourceLinesAdapter(self.source_code)

    @property
    @utils.saveit
    def logical_lines(self):
        """A `LogicalLinesFinder`"""
        return rope.base.codeanalyze.CachingLogicalLineFinder(self.lines)


class PyPackage(pyobjects.PyPackage):

    def __init__(self, pycore, resource=None, force_errors=False):
        self.resource = resource
        init_dot_py = self._get_init_dot_py()
        if init_dot_py is not None:
            ast_node = pycore.resource_to_pyobject(
                init_dot_py, force_errors=force_errors).get_ast()
        else:
            ast_node = ast.parse('\n')
        super(PyPackage, self).__init__(pycore, ast_node, resource)

    def _create_structural_attributes(self):
        result = {}
        modname = self.pycore.modname(self.resource)
        extension_submodules = self.pycore._builtin_submodules(modname)
        for name, module in extension_submodules.iteritems():
            result[name] = rope.base.builtins.BuiltinName(module)
        if self.resource is None:
            return result
        for name, resource in self._get_child_resources().items():
            result[name] = pynames.ImportedModule(self, resource=resource)
        return result

    def _create_concluded_attributes(self):
        result = {}
        init_dot_py = self._get_init_dot_py()
        if init_dot_py:
            init_object = self.pycore.resource_to_pyobject(init_dot_py)
            result.update(init_object.get_attributes())
        return result

    def _get_child_resources(self):
        result = {}
        for child in self.resource.get_children():
            if child.is_folder():
                result[child.name] = child
            elif child.name.endswith('.py') and \
                 child.name != '__init__.py':
                name = child.name[:-3]
                result[name] = child
        return result

    def _get_init_dot_py(self):
        if self.resource is not None and self.resource.has_child('__init__.py'):
            return self.resource.get_child('__init__.py')
        else:
            return None

    def _create_scope(self):
        return self.get_module().get_scope()

    def get_module(self):
        init_dot_py = self._get_init_dot_py()
        if init_dot_py:
            return self.pycore.resource_to_pyobject(init_dot_py)
        return self


class _AssignVisitor(object):

    def __init__(self, scope_visitor):
        self.scope_visitor = scope_visitor
        self.assigned_ast = None

    def _Assign(self, node):
        self.assigned_ast = node.value
        for child_node in node.targets:
            ast.walk(child_node, self)

    def _assigned(self, name, assignment=None):
        self.scope_visitor._assigned(name, assignment)

    def _Name(self, node):
        assignment = None
        if self.assigned_ast is not None:
            assignment = pynames.AssignmentValue(self.assigned_ast)
        self._assigned(node.id, assignment)

    def _Tuple(self, node):
        names = astutils.get_name_levels(node)
        for name, levels in names:
            assignment = None
            if self.assigned_ast is not None:
                assignment = pynames.AssignmentValue(self.assigned_ast, levels)
            self._assigned(name, assignment)

    def _Attribute(self, node):
        pass

    def _Subscript(self, node):
        pass

    def _Slice(self, node):
        pass


class _ScopeVisitor(object):

    def __init__(self, pycore, owner_object):
        self.pycore = pycore
        self.owner_object = owner_object
        self.names = {}
        self.defineds = []

    def get_module(self):
        if self.owner_object is not None:
            return self.owner_object.get_module()
        else:
            return None

    def _ClassDef(self, node):
        pyclass = PyClass(self.pycore, node, self.owner_object)
        self.names[node.name] = pynames.DefinedName(pyclass)
        self.defineds.append(pyclass)

    def _FunctionDef(self, node):
        pyfunction = PyFunction(self.pycore, node, self.owner_object)
        for decorator in pyfunction.decorators:
            if isinstance(decorator, ast.Name) and decorator.id == 'property':
                if isinstance(self, _ClassVisitor):
                    type_ = rope.base.builtins.Property(pyfunction)
                    arg = pynames.UnboundName(PyObject(self.owner_object))
                    def _eval(type_=type_, arg=arg):
                        return type_.get_property_object(
                            arguments.ObjectArguments([arg]))
                    self.names[node.name] = pynames.EvaluatedName(
                        _eval, module=self.get_module(), lineno=node.lineno)
                    break
        else:
            self.names[node.name] = pynames.DefinedName(pyfunction)
        self.defineds.append(pyfunction)

    def _Assign(self, node):
        ast.walk(node, _AssignVisitor(self))

    def _AugAssign(self, node):
        pass

    def _For(self, node):
        names = self._update_evaluated(node.target, node.iter,
                                       '.__iter__().next()')
        for child in node.body + node.orelse:
            ast.walk(child, self)

    def _assigned(self, name, assignment):
        pyname = self.names.get(name, None)
        if pyname is None:
            pyname = pynames.AssignedName(module=self.get_module())
        if isinstance(pyname, pynames.AssignedName):
            if assignment is not None:
                pyname.assignments.append(assignment)
            self.names[name] = pyname

    def _update_evaluated(self, targets, assigned,
                          evaluation= '', eval_type=False):
        result = {}
        names = astutils.get_name_levels(targets)
        for name, levels in names:
            assignment = pynames.AssignmentValue(assigned, levels,
                                                 evaluation, eval_type)
            self._assigned(name, assignment)
        return result

    def _With(self, node):
        if node.optional_vars:
            self._update_evaluated(node.optional_vars,
                                   node.context_expr, '.__enter__()')
        for child in node.body:
            ast.walk(child, self)

    def _excepthandler(self, node):
        if node.name is not None and isinstance(node.name, ast.Name):
            type_node = node.type
            if isinstance(node.type, ast.Tuple) and type_node.elts:
                type_node = type_node.elts[0]
            self._update_evaluated(node.name, type_node, eval_type=True)
        for child in node.body:
            ast.walk(child, self)

    def _ExceptHandler(self, node):
        self._excepthandler(node)

    def _Import(self, node):
        for import_pair in node.names:
            module_name = import_pair.name
            alias = import_pair.asname
            first_package = module_name.split('.')[0]
            if alias is not None:
                imported = pynames.ImportedModule(self.get_module(),
                                                  module_name)
                if not self._is_ignored_import(imported):
                    self.names[alias] = imported
            else:
                imported = pynames.ImportedModule(self.get_module(),
                                                  first_package)
                if not self._is_ignored_import(imported):
                    self.names[first_package] = imported

    def _ImportFrom(self, node):
        level = 0
        if node.level:
            level = node.level
        imported_module = pynames.ImportedModule(self.get_module(),
                                                 node.module, level)
        if self._is_ignored_import(imported_module):
            return
        if len(node.names) == 1 and node.names[0].name == '*':
            if isinstance(self.owner_object, PyModule):
                self.owner_object.star_imports.append(
                    StarImport(imported_module))
        else:
            for imported_name in node.names:
                imported = imported_name.name
                alias = imported_name.asname
                if alias is not None:
                    imported = alias
                self.names[imported] = pynames.ImportedName(imported_module,
                                                            imported_name.name)

    def _is_ignored_import(self, imported_module):
        if not self.pycore.project.prefs.get('ignore_bad_imports', False):
            return False
        return not isinstance(imported_module.get_object(), AbstractModule)

    def _Global(self, node):
        module = self.get_module()
        for name in node.names:
            if module is not None:
                try:
                    pyname = module[name]
                except exceptions.AttributeNotFoundError:
                    pyname = pynames.AssignedName(node.lineno)
            self.names[name] = pyname


class _GlobalVisitor(_ScopeVisitor):

    def __init__(self, pycore, owner_object):
        super(_GlobalVisitor, self).__init__(pycore, owner_object)


class _ClassVisitor(_ScopeVisitor):

    def __init__(self, pycore, owner_object):
        super(_ClassVisitor, self).__init__(pycore, owner_object)

    def _FunctionDef(self, node):
        _ScopeVisitor._FunctionDef(self, node)
        if len(node.args.args) > 0:
            first = node.args.args[0]
            if isinstance(first, ast.Name):
                new_visitor = _ClassInitVisitor(self, first.id)
                for child in ast.get_child_nodes(node):
                    ast.walk(child, new_visitor)


class _FunctionVisitor(_ScopeVisitor):

    def __init__(self, pycore, owner_object):
        super(_FunctionVisitor, self).__init__(pycore, owner_object)
        self.returned_asts = []
        self.generator = False

    def _Return(self, node):
        if node.value is not None:
            self.returned_asts.append(node.value)

    def _Yield(self, node):
        if node.value is not None:
            self.returned_asts.append(node.value)
        self.generator = True


class _ClassInitVisitor(_AssignVisitor):

    def __init__(self, scope_visitor, self_name):
        super(_ClassInitVisitor, self).__init__(scope_visitor)
        self.self_name = self_name

    def _Attribute(self, node):
        if not isinstance(node.ctx, ast.Store):
            return
        if isinstance(node.value, ast.Name) and \
           node.value.id == self.self_name:
            if node.attr not in self.scope_visitor.names:
                self.scope_visitor.names[node.attr] = pynames.AssignedName(
                    lineno=node.lineno, module=self.scope_visitor.get_module())
            if self.assigned_ast is not None:
                pyname = self.scope_visitor.names[node.attr]
                if isinstance(pyname, pynames.AssignedName):
                    pyname.assignments.append(
                        pynames.AssignmentValue(self.assigned_ast))

    def _Tuple(self, node):
        if not isinstance(node.ctx, ast.Store):
            return
        for child in ast.get_child_nodes(node):
            ast.walk(child, self)

    def _Name(self, node):
        pass

    def _FunctionDef(self, node):
        pass

    def _ClassDef(self, node):
        pass

    def _For(self, node):
        pass

    def _With(self, node):
        pass


class StarImport(object):

    def __init__(self, imported_module):
        self.imported_module = imported_module

    def get_names(self):
        result = {}
        imported = self.imported_module.get_object()
        for name in imported:
            if not name.startswith('_'):
                result[name] = pynames.ImportedName(self.imported_module, name)
        return result