Adding new stuff
This commit is contained in:
parent
cb028acf05
commit
7b21428a6d
451 changed files with 6 additions and 1 deletions
|
|
@ -1,18 +0,0 @@
|
|||
"""rope, a python refactoring library"""
|
||||
|
||||
INFO = __doc__
|
||||
VERSION = '0.10.3'
|
||||
COPYRIGHT = """\
|
||||
Copyright (C) 2014-2015 Matej Cepl
|
||||
Copyright (C) 2006-2012 Ali Gholami Rudi
|
||||
Copyright (C) 2009-2012 Anton Gritsay
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2 of the license, or (at your
|
||||
opinion) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details."""
|
||||
Binary file not shown.
|
|
@ -1,8 +0,0 @@
|
|||
"""Base rope package
|
||||
|
||||
This package contains rope core modules that are used by other modules
|
||||
and packages.
|
||||
|
||||
"""
|
||||
|
||||
__all__ = ['project', 'libutils', 'exceptions']
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,111 +0,0 @@
|
|||
import rope.base.evaluate
|
||||
from rope.base import ast
|
||||
|
||||
|
||||
class Arguments(object):
|
||||
"""A class for evaluating parameters passed to a function
|
||||
|
||||
You can use the `create_arguments` factory. It handles implicit
|
||||
first arguments.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, args, scope):
|
||||
self.args = args
|
||||
self.scope = scope
|
||||
self.instance = None
|
||||
|
||||
def get_arguments(self, parameters):
|
||||
result = []
|
||||
for pyname in self.get_pynames(parameters):
|
||||
if pyname is None:
|
||||
result.append(None)
|
||||
else:
|
||||
result.append(pyname.get_object())
|
||||
return result
|
||||
|
||||
def get_pynames(self, parameters):
|
||||
result = [None] * max(len(parameters), len(self.args))
|
||||
for index, arg in enumerate(self.args):
|
||||
if isinstance(arg, ast.keyword) and arg.arg in parameters:
|
||||
result[parameters.index(arg.arg)] = self._evaluate(arg.value)
|
||||
else:
|
||||
result[index] = self._evaluate(arg)
|
||||
return result
|
||||
|
||||
def get_instance_pyname(self):
|
||||
if self.args:
|
||||
return self._evaluate(self.args[0])
|
||||
|
||||
def _evaluate(self, ast_node):
|
||||
return rope.base.evaluate.eval_node(self.scope, ast_node)
|
||||
|
||||
|
||||
def create_arguments(primary, pyfunction, call_node, scope):
|
||||
"""A factory for creating `Arguments`"""
|
||||
args = list(call_node.args)
|
||||
args.extend(call_node.keywords)
|
||||
called = call_node.func
|
||||
# XXX: Handle constructors
|
||||
if _is_method_call(primary, pyfunction) and \
|
||||
isinstance(called, ast.Attribute):
|
||||
args.insert(0, called.value)
|
||||
return Arguments(args, scope)
|
||||
|
||||
|
||||
class ObjectArguments(object):
|
||||
|
||||
def __init__(self, pynames):
|
||||
self.pynames = pynames
|
||||
|
||||
def get_arguments(self, parameters):
|
||||
result = []
|
||||
for pyname in self.pynames:
|
||||
if pyname is None:
|
||||
result.append(None)
|
||||
else:
|
||||
result.append(pyname.get_object())
|
||||
return result
|
||||
|
||||
def get_pynames(self, parameters):
|
||||
return self.pynames
|
||||
|
||||
def get_instance_pyname(self):
|
||||
return self.pynames[0]
|
||||
|
||||
|
||||
class MixedArguments(object):
|
||||
|
||||
def __init__(self, pyname, arguments, scope):
|
||||
"""`argumens` is an instance of `Arguments`"""
|
||||
self.pyname = pyname
|
||||
self.args = arguments
|
||||
|
||||
def get_pynames(self, parameters):
|
||||
return [self.pyname] + self.args.get_pynames(parameters[1:])
|
||||
|
||||
def get_arguments(self, parameters):
|
||||
result = []
|
||||
for pyname in self.get_pynames(parameters):
|
||||
if pyname is None:
|
||||
result.append(None)
|
||||
else:
|
||||
result.append(pyname.get_object())
|
||||
return result
|
||||
|
||||
def get_instance_pyname(self):
|
||||
return self.pyname
|
||||
|
||||
|
||||
def _is_method_call(primary, pyfunction):
|
||||
if primary is None:
|
||||
return False
|
||||
pyobject = primary.get_object()
|
||||
if isinstance(pyobject.get_type(), rope.base.pyobjects.PyClass) and \
|
||||
isinstance(pyfunction, rope.base.pyobjects.PyFunction) and \
|
||||
isinstance(pyfunction.parent, rope.base.pyobjects.PyClass):
|
||||
return True
|
||||
if isinstance(pyobject.get_type(), rope.base.pyobjects.AbstractClass) and \
|
||||
isinstance(pyfunction, rope.base.builtins.BuiltinFunction):
|
||||
return True
|
||||
return False
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
import _ast
|
||||
from _ast import *
|
||||
|
||||
from rope.base import fscommands
|
||||
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
unicode = str
|
||||
|
||||
|
||||
def parse(source, filename='<string>'):
|
||||
# NOTE: the raw string should be given to `compile` function
|
||||
if isinstance(source, unicode):
|
||||
source = fscommands.unicode_to_file_data(source)
|
||||
if b'\r' in source:
|
||||
source = source.replace(b'\r\n', b'\n').replace(b'\r', b'\n')
|
||||
if not source.endswith(b'\n'):
|
||||
source += b'\n'
|
||||
try:
|
||||
return compile(source, filename, 'exec', _ast.PyCF_ONLY_AST)
|
||||
except (TypeError, ValueError) as e:
|
||||
error = SyntaxError()
|
||||
error.lineno = 1
|
||||
error.filename = filename
|
||||
error.msg = str(e)
|
||||
raise error
|
||||
|
||||
|
||||
def walk(node, walker):
|
||||
"""Walk the syntax tree"""
|
||||
method_name = '_' + node.__class__.__name__
|
||||
method = getattr(walker, method_name, None)
|
||||
if method is not None:
|
||||
if isinstance(node, _ast.ImportFrom) and node.module is None:
|
||||
# In python < 2.7 ``node.module == ''`` for relative imports
|
||||
# but for python 2.7 it is None. Generalizing it to ''.
|
||||
node.module = ''
|
||||
return method(node)
|
||||
for child in get_child_nodes(node):
|
||||
walk(child, walker)
|
||||
|
||||
|
||||
def get_child_nodes(node):
|
||||
if isinstance(node, _ast.Module):
|
||||
return node.body
|
||||
result = []
|
||||
if node._fields is not None:
|
||||
for name in node._fields:
|
||||
child = getattr(node, name)
|
||||
if isinstance(child, list):
|
||||
for entry in child:
|
||||
if isinstance(entry, _ast.AST):
|
||||
result.append(entry)
|
||||
if isinstance(child, _ast.AST):
|
||||
result.append(child)
|
||||
return result
|
||||
|
||||
|
||||
def call_for_nodes(node, callback, recursive=False):
|
||||
"""If callback returns `True` the child nodes are skipped"""
|
||||
result = callback(node)
|
||||
if recursive and not result:
|
||||
for child in get_child_nodes(node):
|
||||
call_for_nodes(child, callback, recursive)
|
||||
|
||||
|
||||
def get_children(node):
|
||||
result = []
|
||||
if node._fields is not None:
|
||||
for name in node._fields:
|
||||
if name in ['lineno', 'col_offset']:
|
||||
continue
|
||||
child = getattr(node, name)
|
||||
result.append(child)
|
||||
return result
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
from rope.base import ast
|
||||
|
||||
|
||||
def get_name_levels(node):
|
||||
"""Return a list of ``(name, level)`` tuples for assigned names
|
||||
|
||||
The `level` is `None` for simple assignments and is a list of
|
||||
numbers for tuple assignments for example in::
|
||||
|
||||
a, (b, c) = x
|
||||
|
||||
The levels for for `a` is ``[0]``, for `b` is ``[1, 0]`` and for
|
||||
`c` is ``[1, 1]``.
|
||||
|
||||
"""
|
||||
visitor = _NodeNameCollector()
|
||||
ast.walk(node, visitor)
|
||||
return visitor.names
|
||||
|
||||
|
||||
class _NodeNameCollector(object):
|
||||
|
||||
def __init__(self, levels=None):
|
||||
self.names = []
|
||||
self.levels = levels
|
||||
self.index = 0
|
||||
|
||||
def _add_node(self, node):
|
||||
new_levels = []
|
||||
if self.levels is not None:
|
||||
new_levels = list(self.levels)
|
||||
new_levels.append(self.index)
|
||||
self.index += 1
|
||||
self._added(node, new_levels)
|
||||
|
||||
def _added(self, node, levels):
|
||||
if hasattr(node, 'id'):
|
||||
self.names.append((node.id, levels))
|
||||
|
||||
def _Name(self, node):
|
||||
self._add_node(node)
|
||||
|
||||
def _ExceptHandler(self, node):
|
||||
self.names.append((node.name, []))
|
||||
|
||||
def _Tuple(self, node):
|
||||
new_levels = []
|
||||
if self.levels is not None:
|
||||
new_levels = list(self.levels)
|
||||
new_levels.append(self.index)
|
||||
self.index += 1
|
||||
visitor = _NodeNameCollector(new_levels)
|
||||
for child in ast.get_child_nodes(node):
|
||||
ast.walk(child, visitor)
|
||||
self.names.extend(visitor.names)
|
||||
|
||||
def _Subscript(self, node):
|
||||
self._add_node(node)
|
||||
|
||||
def _Attribute(self, node):
|
||||
self._add_node(node)
|
||||
|
||||
def _Slice(self, node):
|
||||
self._add_node(node)
|
||||
|
|
@ -1,809 +0,0 @@
|
|||
"""This module trys to support builtin types and functions."""
|
||||
import inspect
|
||||
try:
|
||||
raw_input
|
||||
except NameError:
|
||||
raw_input = input
|
||||
|
||||
import rope.base.evaluate
|
||||
from rope.base.utils import pycompat
|
||||
from rope.base import pynames, pyobjects, arguments, utils
|
||||
|
||||
|
||||
class BuiltinModule(pyobjects.AbstractModule):
|
||||
|
||||
def __init__(self, name, pycore=None, initial={}):
|
||||
super(BuiltinModule, self).__init__()
|
||||
self.name = name
|
||||
self.pycore = pycore
|
||||
self.initial = initial
|
||||
|
||||
parent = None
|
||||
|
||||
def get_attributes(self):
|
||||
return self.attributes
|
||||
|
||||
def get_doc(self):
|
||||
if self.module:
|
||||
return self.module.__doc__
|
||||
|
||||
def get_name(self):
|
||||
return self.name.split('.')[-1]
|
||||
|
||||
@property
|
||||
@utils.saveit
|
||||
def attributes(self):
|
||||
result = _object_attributes(self.module, self)
|
||||
result.update(self.initial)
|
||||
if self.pycore is not None:
|
||||
submodules = self.pycore._builtin_submodules(self.name)
|
||||
for name, module in submodules.items():
|
||||
result[name] = rope.base.builtins.BuiltinName(module)
|
||||
return result
|
||||
|
||||
@property
|
||||
@utils.saveit
|
||||
def module(self):
|
||||
try:
|
||||
result = __import__(self.name)
|
||||
for token in self.name.split('.')[1:]:
|
||||
result = getattr(result, token, None)
|
||||
return result
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
|
||||
class _BuiltinElement(object):
|
||||
|
||||
def __init__(self, builtin, parent=None):
|
||||
self.builtin = builtin
|
||||
self._parent = parent
|
||||
|
||||
def get_doc(self):
|
||||
if self.builtin:
|
||||
return getattr(self.builtin, '__doc__', None)
|
||||
|
||||
def get_name(self):
|
||||
if self.builtin:
|
||||
return getattr(self.builtin, '__name__', None)
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
if self._parent is None:
|
||||
return builtins
|
||||
return self._parent
|
||||
|
||||
|
||||
class BuiltinClass(_BuiltinElement, pyobjects.AbstractClass):
|
||||
|
||||
def __init__(self, builtin, attributes, parent=None):
|
||||
_BuiltinElement.__init__(self, builtin, parent)
|
||||
pyobjects.AbstractClass.__init__(self)
|
||||
self.initial = attributes
|
||||
|
||||
@utils.saveit
|
||||
def get_attributes(self):
|
||||
result = _object_attributes(self.builtin, self)
|
||||
result.update(self.initial)
|
||||
return result
|
||||
|
||||
|
||||
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)
|
||||
self.argnames = argnames
|
||||
self.returned = returned
|
||||
self.function = function
|
||||
|
||||
def get_returned_object(self, args):
|
||||
if self.function is not None:
|
||||
return self.function(_CallContext(self.argnames, args))
|
||||
else:
|
||||
return self.returned
|
||||
|
||||
def get_param_names(self, special_args=True):
|
||||
return self.argnames
|
||||
|
||||
|
||||
class BuiltinUnknown(_BuiltinElement, pyobjects.PyObject):
|
||||
|
||||
def __init__(self, builtin):
|
||||
super(BuiltinUnknown, self).__init__(pyobjects.get_unknown())
|
||||
self.builtin = builtin
|
||||
self.type = pyobjects.get_unknown()
|
||||
|
||||
def get_name(self):
|
||||
return getattr(type(self.builtin), '__name__', None)
|
||||
|
||||
@utils.saveit
|
||||
def get_attributes(self):
|
||||
return _object_attributes(self.builtin, self)
|
||||
|
||||
|
||||
def _object_attributes(obj, parent):
|
||||
attributes = {}
|
||||
for name in dir(obj):
|
||||
if name == 'None':
|
||||
continue
|
||||
try:
|
||||
child = getattr(obj, name)
|
||||
except AttributeError:
|
||||
# descriptors are allowed to raise AttributeError
|
||||
# even if they are in dir()
|
||||
continue
|
||||
pyobject = None
|
||||
if inspect.isclass(child):
|
||||
pyobject = BuiltinClass(child, {}, parent=parent)
|
||||
elif inspect.isroutine(child):
|
||||
pyobject = BuiltinFunction(builtin=child, parent=parent)
|
||||
else:
|
||||
pyobject = BuiltinUnknown(builtin=child)
|
||||
attributes[name] = BuiltinName(pyobject)
|
||||
return attributes
|
||||
|
||||
|
||||
def _create_builtin_type_getter(cls):
|
||||
def _get_builtin(*args):
|
||||
if not hasattr(cls, '_generated'):
|
||||
cls._generated = {}
|
||||
if args not in cls._generated:
|
||||
cls._generated[args] = cls(*args)
|
||||
return cls._generated[args]
|
||||
return _get_builtin
|
||||
|
||||
|
||||
def _create_builtin_getter(cls):
|
||||
type_getter = _create_builtin_type_getter(cls)
|
||||
|
||||
def _get_builtin(*args):
|
||||
return pyobjects.PyObject(type_getter(*args))
|
||||
return _get_builtin
|
||||
|
||||
|
||||
class _CallContext(object):
|
||||
|
||||
def __init__(self, argnames, args):
|
||||
self.argnames = argnames
|
||||
self.args = args
|
||||
|
||||
def _get_scope_and_pyname(self, pyname):
|
||||
if pyname is not None and isinstance(pyname, pynames.AssignedName):
|
||||
pymodule, lineno = pyname.get_definition_location()
|
||||
if pymodule is None:
|
||||
return None, None
|
||||
if lineno is None:
|
||||
lineno = 1
|
||||
scope = pymodule.get_scope().get_inner_scope_for_line(lineno)
|
||||
name = None
|
||||
while name is None and scope is not None:
|
||||
for current in scope.get_names():
|
||||
if scope[current] is pyname:
|
||||
name = current
|
||||
break
|
||||
else:
|
||||
scope = scope.parent
|
||||
return scope, name
|
||||
return None, None
|
||||
|
||||
def get_argument(self, name):
|
||||
if self.args:
|
||||
args = self.args.get_arguments(self.argnames)
|
||||
return args[self.argnames.index(name)]
|
||||
|
||||
def get_pyname(self, name):
|
||||
if self.args:
|
||||
args = self.args.get_pynames(self.argnames)
|
||||
if name in self.argnames:
|
||||
return args[self.argnames.index(name)]
|
||||
|
||||
def get_arguments(self, argnames):
|
||||
if self.args:
|
||||
return self.args.get_arguments(argnames)
|
||||
|
||||
def get_pynames(self, argnames):
|
||||
if self.args:
|
||||
return self.args.get_pynames(argnames)
|
||||
|
||||
def get_per_name(self):
|
||||
if self.args is None:
|
||||
return None
|
||||
pyname = self.args.get_instance_pyname()
|
||||
scope, name = self._get_scope_and_pyname(pyname)
|
||||
if name is not None:
|
||||
pymodule = pyname.get_definition_location()[0]
|
||||
return pymodule.pycore.object_info.get_per_name(scope, name)
|
||||
return None
|
||||
|
||||
def save_per_name(self, value):
|
||||
if self.args is None:
|
||||
return None
|
||||
pyname = self.args.get_instance_pyname()
|
||||
scope, name = self._get_scope_and_pyname(pyname)
|
||||
if name is not None:
|
||||
pymodule = pyname.get_definition_location()[0]
|
||||
pymodule.pycore.object_info.save_per_name(scope, name, value)
|
||||
|
||||
|
||||
class _AttributeCollector(object):
|
||||
|
||||
def __init__(self, type):
|
||||
self.attributes = {}
|
||||
self.type = type
|
||||
|
||||
def __call__(self, name, returned=None, function=None,
|
||||
argnames=['self'], check_existence=True):
|
||||
try:
|
||||
builtin = getattr(self.type, name)
|
||||
except AttributeError:
|
||||
if check_existence:
|
||||
raise
|
||||
builtin = None
|
||||
self.attributes[name] = BuiltinName(
|
||||
BuiltinFunction(returned=returned, function=function,
|
||||
argnames=argnames, builtin=builtin))
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
self.attributes[name] = value
|
||||
|
||||
|
||||
class List(BuiltinClass):
|
||||
|
||||
def __init__(self, holding=None):
|
||||
self.holding = holding
|
||||
collector = _AttributeCollector(list)
|
||||
|
||||
collector('__iter__', function=self._iterator_get)
|
||||
collector('__new__', function=self._new_list)
|
||||
|
||||
# Adding methods
|
||||
collector('append', function=self._list_add,
|
||||
argnames=['self', 'value'])
|
||||
collector('__setitem__', function=self._list_add,
|
||||
argnames=['self', 'index', 'value'])
|
||||
collector('insert', function=self._list_add,
|
||||
argnames=['self', 'index', 'value'])
|
||||
collector('extend', function=self._self_set,
|
||||
argnames=['self', 'iterable'])
|
||||
|
||||
# Getting methods
|
||||
collector('__getitem__', function=self._list_get)
|
||||
collector('pop', function=self._list_get)
|
||||
try:
|
||||
collector('__getslice__', function=self._list_get)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
super(List, self).__init__(list, collector.attributes)
|
||||
|
||||
def _new_list(self, args):
|
||||
return _create_builtin(args, get_list)
|
||||
|
||||
def _list_add(self, context):
|
||||
if self.holding is not None:
|
||||
return
|
||||
holding = context.get_argument('value')
|
||||
if holding is not None and holding != pyobjects.get_unknown():
|
||||
context.save_per_name(holding)
|
||||
|
||||
def _self_set(self, context):
|
||||
if self.holding is not None:
|
||||
return
|
||||
iterable = context.get_pyname('iterable')
|
||||
holding = _infer_sequence_for_pyname(iterable)
|
||||
if holding is not None and holding != pyobjects.get_unknown():
|
||||
context.save_per_name(holding)
|
||||
|
||||
def _list_get(self, context):
|
||||
if self.holding is not None:
|
||||
args = context.get_arguments(['self', 'key'])
|
||||
if (len(args) > 1 and args[1] is not None and
|
||||
args[1].get_type() == builtins['slice'].get_object()):
|
||||
return get_list(self.holding)
|
||||
return self.holding
|
||||
return context.get_per_name()
|
||||
|
||||
def _iterator_get(self, context):
|
||||
return get_iterator(self._list_get(context))
|
||||
|
||||
def _self_get(self, context):
|
||||
return get_list(self._list_get(context))
|
||||
|
||||
|
||||
get_list = _create_builtin_getter(List)
|
||||
get_list_type = _create_builtin_type_getter(List)
|
||||
|
||||
|
||||
class Dict(BuiltinClass):
|
||||
|
||||
def __init__(self, keys=None, values=None):
|
||||
self.keys = keys
|
||||
self.values = values
|
||||
collector = _AttributeCollector(dict)
|
||||
collector('__new__', function=self._new_dict)
|
||||
collector('__setitem__', function=self._dict_add)
|
||||
collector('popitem', function=self._item_get)
|
||||
collector('pop', function=self._value_get)
|
||||
collector('get', function=self._key_get)
|
||||
collector('keys', function=self._key_list)
|
||||
collector('values', function=self._value_list)
|
||||
collector('items', function=self._item_list)
|
||||
collector('copy', function=self._self_get)
|
||||
collector('__getitem__', function=self._value_get)
|
||||
collector('__iter__', function=self._key_iter)
|
||||
collector('update', function=self._self_set)
|
||||
super(Dict, self).__init__(dict, collector.attributes)
|
||||
|
||||
def _new_dict(self, args):
|
||||
def do_create(holding=None):
|
||||
if holding is None:
|
||||
return get_dict()
|
||||
type = holding.get_type()
|
||||
if isinstance(type, Tuple) and \
|
||||
len(type.get_holding_objects()) == 2:
|
||||
return get_dict(*type.get_holding_objects())
|
||||
return _create_builtin(args, do_create)
|
||||
|
||||
def _dict_add(self, context):
|
||||
if self.keys is not None:
|
||||
return
|
||||
key, value = context.get_arguments(['self', 'key', 'value'])[1:]
|
||||
if key is not None and key != pyobjects.get_unknown():
|
||||
context.save_per_name(get_tuple(key, value))
|
||||
|
||||
def _item_get(self, context):
|
||||
if self.keys is not None:
|
||||
return get_tuple(self.keys, self.values)
|
||||
item = context.get_per_name()
|
||||
if item is None or not isinstance(item.get_type(), Tuple):
|
||||
return get_tuple(self.keys, self.values)
|
||||
return item
|
||||
|
||||
def _value_get(self, context):
|
||||
item = self._item_get(context).get_type()
|
||||
return item.get_holding_objects()[1]
|
||||
|
||||
def _key_get(self, context):
|
||||
item = self._item_get(context).get_type()
|
||||
return item.get_holding_objects()[0]
|
||||
|
||||
def _value_list(self, context):
|
||||
return get_list(self._value_get(context))
|
||||
|
||||
def _key_list(self, context):
|
||||
return get_list(self._key_get(context))
|
||||
|
||||
def _item_list(self, context):
|
||||
return get_list(self._item_get(context))
|
||||
|
||||
def _value_iter(self, context):
|
||||
return get_iterator(self._value_get(context))
|
||||
|
||||
def _key_iter(self, context):
|
||||
return get_iterator(self._key_get(context))
|
||||
|
||||
def _item_iter(self, context):
|
||||
return get_iterator(self._item_get(context))
|
||||
|
||||
def _self_get(self, context):
|
||||
item = self._item_get(context).get_type()
|
||||
key, value = item.get_holding_objects()[:2]
|
||||
return get_dict(key, value)
|
||||
|
||||
def _self_set(self, context):
|
||||
if self.keys is not None:
|
||||
return
|
||||
new_dict = context.get_pynames(['self', 'd'])[1]
|
||||
if new_dict and isinstance(new_dict.get_object().get_type(), Dict):
|
||||
args = arguments.ObjectArguments([new_dict])
|
||||
items = new_dict.get_object()['popitem'].\
|
||||
get_object().get_returned_object(args)
|
||||
context.save_per_name(items)
|
||||
else:
|
||||
holding = _infer_sequence_for_pyname(new_dict)
|
||||
if holding is not None and isinstance(holding.get_type(), Tuple):
|
||||
context.save_per_name(holding)
|
||||
|
||||
|
||||
get_dict = _create_builtin_getter(Dict)
|
||||
get_dict_type = _create_builtin_type_getter(Dict)
|
||||
|
||||
|
||||
class Tuple(BuiltinClass):
|
||||
|
||||
def __init__(self, *objects):
|
||||
self.objects = objects
|
||||
first = None
|
||||
if objects:
|
||||
first = objects[0]
|
||||
attributes = {
|
||||
'__getitem__': BuiltinName(BuiltinFunction(first)), # TODO: add slice support
|
||||
'__getslice__':
|
||||
BuiltinName(BuiltinFunction(pyobjects.PyObject(self))),
|
||||
'__new__': BuiltinName(BuiltinFunction(function=self._new_tuple)),
|
||||
'__iter__': BuiltinName(BuiltinFunction(get_iterator(first)))}
|
||||
super(Tuple, self).__init__(tuple, attributes)
|
||||
|
||||
def get_holding_objects(self):
|
||||
return self.objects
|
||||
|
||||
def _new_tuple(self, args):
|
||||
return _create_builtin(args, get_tuple)
|
||||
|
||||
|
||||
get_tuple = _create_builtin_getter(Tuple)
|
||||
get_tuple_type = _create_builtin_type_getter(Tuple)
|
||||
|
||||
|
||||
class Set(BuiltinClass):
|
||||
|
||||
def __init__(self, holding=None):
|
||||
self.holding = holding
|
||||
collector = _AttributeCollector(set)
|
||||
collector('__new__', function=self._new_set)
|
||||
|
||||
self_methods = ['copy', 'difference', 'intersection',
|
||||
'symmetric_difference', 'union']
|
||||
for method in self_methods:
|
||||
collector(method, function=self._self_get)
|
||||
collector('add', function=self._set_add)
|
||||
collector('update', function=self._self_set)
|
||||
collector('update', function=self._self_set)
|
||||
collector('symmetric_difference_update', function=self._self_set)
|
||||
collector('difference_update', function=self._self_set)
|
||||
|
||||
collector('pop', function=self._set_get)
|
||||
collector('__iter__', function=self._iterator_get)
|
||||
super(Set, self).__init__(set, collector.attributes)
|
||||
|
||||
def _new_set(self, args):
|
||||
return _create_builtin(args, get_set)
|
||||
|
||||
def _set_add(self, context):
|
||||
if self.holding is not None:
|
||||
return
|
||||
holding = context.get_arguments(['self', 'value'])[1]
|
||||
if holding is not None and holding != pyobjects.get_unknown():
|
||||
context.save_per_name(holding)
|
||||
|
||||
def _self_set(self, context):
|
||||
if self.holding is not None:
|
||||
return
|
||||
iterable = context.get_pyname('iterable')
|
||||
holding = _infer_sequence_for_pyname(iterable)
|
||||
if holding is not None and holding != pyobjects.get_unknown():
|
||||
context.save_per_name(holding)
|
||||
|
||||
def _set_get(self, context):
|
||||
if self.holding is not None:
|
||||
return self.holding
|
||||
return context.get_per_name()
|
||||
|
||||
def _iterator_get(self, context):
|
||||
return get_iterator(self._set_get(context))
|
||||
|
||||
def _self_get(self, context):
|
||||
return get_list(self._set_get(context))
|
||||
|
||||
|
||||
get_set = _create_builtin_getter(Set)
|
||||
get_set_type = _create_builtin_type_getter(Set)
|
||||
|
||||
|
||||
class Str(BuiltinClass):
|
||||
|
||||
def __init__(self):
|
||||
self_object = pyobjects.PyObject(self)
|
||||
collector = _AttributeCollector(str)
|
||||
collector('__iter__', get_iterator(self_object), check_existence=False)
|
||||
|
||||
self_methods = ['__getitem__', 'capitalize', 'center',
|
||||
'encode', 'expandtabs', 'join', 'ljust',
|
||||
'lower', 'lstrip', 'replace', 'rjust', 'rstrip',
|
||||
'strip', 'swapcase', 'title', 'translate', 'upper',
|
||||
'zfill']
|
||||
for method in self_methods:
|
||||
collector(method, self_object)
|
||||
|
||||
py2_self_methods = ["__getslice__", "decode"]
|
||||
for method in py2_self_methods:
|
||||
try:
|
||||
collector(method, self_object)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
for method in ['rsplit', 'split', 'splitlines']:
|
||||
collector(method, get_list(self_object))
|
||||
|
||||
super(Str, self).__init__(str, collector.attributes)
|
||||
|
||||
def get_doc(self):
|
||||
return str.__doc__
|
||||
|
||||
|
||||
get_str = _create_builtin_getter(Str)
|
||||
get_str_type = _create_builtin_type_getter(Str)
|
||||
|
||||
|
||||
class BuiltinName(pynames.PyName):
|
||||
|
||||
def __init__(self, pyobject):
|
||||
self.pyobject = pyobject
|
||||
|
||||
def get_object(self):
|
||||
return self.pyobject
|
||||
|
||||
def get_definition_location(self):
|
||||
return (None, None)
|
||||
|
||||
|
||||
class Iterator(pyobjects.AbstractClass):
|
||||
|
||||
def __init__(self, holding=None):
|
||||
super(Iterator, self).__init__()
|
||||
self.holding = holding
|
||||
self.attributes = {
|
||||
'next': BuiltinName(BuiltinFunction(self.holding)),
|
||||
'__iter__': BuiltinName(BuiltinFunction(self))}
|
||||
|
||||
def get_attributes(self):
|
||||
return self.attributes
|
||||
|
||||
def get_returned_object(self, args):
|
||||
return self.holding
|
||||
|
||||
get_iterator = _create_builtin_getter(Iterator)
|
||||
|
||||
|
||||
class Generator(pyobjects.AbstractClass):
|
||||
|
||||
def __init__(self, holding=None):
|
||||
super(Generator, self).__init__()
|
||||
self.holding = holding
|
||||
self.attributes = {
|
||||
'next': BuiltinName(BuiltinFunction(self.holding)),
|
||||
'__iter__': BuiltinName(BuiltinFunction(
|
||||
get_iterator(self.holding))),
|
||||
'close': BuiltinName(BuiltinFunction()),
|
||||
'send': BuiltinName(BuiltinFunction()),
|
||||
'throw': BuiltinName(BuiltinFunction())}
|
||||
|
||||
def get_attributes(self):
|
||||
return self.attributes
|
||||
|
||||
def get_returned_object(self, args):
|
||||
return self.holding
|
||||
|
||||
get_generator = _create_builtin_getter(Generator)
|
||||
|
||||
|
||||
class File(BuiltinClass):
|
||||
|
||||
def __init__(self):
|
||||
str_object = get_str()
|
||||
str_list = get_list(get_str())
|
||||
attributes = {}
|
||||
|
||||
def add(name, returned=None, function=None):
|
||||
builtin = getattr(open, name, None)
|
||||
attributes[name] = BuiltinName(
|
||||
BuiltinFunction(returned=returned, function=function,
|
||||
builtin=builtin))
|
||||
add('__iter__', get_iterator(str_object))
|
||||
for method in ['next', 'read', 'readline', 'readlines']:
|
||||
add(method, str_list)
|
||||
for method in ['close', 'flush', 'lineno', 'isatty', 'seek', 'tell',
|
||||
'truncate', 'write', 'writelines']:
|
||||
add(method)
|
||||
super(File, self).__init__(open, attributes)
|
||||
|
||||
|
||||
get_file = _create_builtin_getter(File)
|
||||
get_file_type = _create_builtin_type_getter(File)
|
||||
|
||||
|
||||
class Property(BuiltinClass):
|
||||
|
||||
def __init__(self, fget=None, fset=None, fdel=None, fdoc=None):
|
||||
self._fget = fget
|
||||
self._fdoc = fdoc
|
||||
attributes = {
|
||||
'fget': BuiltinName(BuiltinFunction()),
|
||||
'fset': BuiltinName(pynames.UnboundName()),
|
||||
'fdel': BuiltinName(pynames.UnboundName()),
|
||||
'__new__': BuiltinName(
|
||||
BuiltinFunction(function=_property_function))}
|
||||
super(Property, self).__init__(property, attributes)
|
||||
|
||||
def get_property_object(self, args):
|
||||
if isinstance(self._fget, pyobjects.AbstractFunction):
|
||||
return self._fget.get_returned_object(args)
|
||||
|
||||
|
||||
def _property_function(args):
|
||||
parameters = args.get_arguments(['fget', 'fset', 'fdel', 'fdoc'])
|
||||
return pyobjects.PyObject(Property(parameters[0]))
|
||||
|
||||
|
||||
class Lambda(pyobjects.AbstractFunction):
|
||||
|
||||
def __init__(self, node, scope):
|
||||
super(Lambda, self).__init__()
|
||||
self.node = node
|
||||
self.arguments = node.args
|
||||
self.scope = scope
|
||||
|
||||
def get_returned_object(self, args):
|
||||
result = rope.base.evaluate.eval_node(self.scope, self.node.body)
|
||||
if result is not None:
|
||||
return result.get_object()
|
||||
else:
|
||||
return pyobjects.get_unknown()
|
||||
|
||||
def get_module(self):
|
||||
return self.parent.get_module()
|
||||
|
||||
def get_scope(self):
|
||||
return self.scope
|
||||
|
||||
def get_kind(self):
|
||||
return 'lambda'
|
||||
|
||||
def get_ast(self):
|
||||
return self.node
|
||||
|
||||
def get_attributes(self):
|
||||
return {}
|
||||
|
||||
def get_name(self):
|
||||
return 'lambda'
|
||||
|
||||
def get_param_names(self, special_args=True):
|
||||
result = [pycompat.get_ast_arg_arg(node) for node in self.arguments.args
|
||||
if isinstance(node, pycompat.ast_arg_type)]
|
||||
if self.arguments.vararg:
|
||||
result.append('*' + pycompat.get_ast_arg_arg(self.arguments.vararg))
|
||||
if self.arguments.kwarg:
|
||||
result.append('**' + pycompat.get_ast_arg_arg(self.arguments.kwarg))
|
||||
return result
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
return self.scope.pyobject
|
||||
|
||||
|
||||
class BuiltinObject(BuiltinClass):
|
||||
|
||||
def __init__(self):
|
||||
super(BuiltinObject, self).__init__(object, {})
|
||||
|
||||
|
||||
class BuiltinType(BuiltinClass):
|
||||
|
||||
def __init__(self):
|
||||
super(BuiltinType, self).__init__(type, {})
|
||||
|
||||
|
||||
def _infer_sequence_for_pyname(pyname):
|
||||
if pyname is None:
|
||||
return None
|
||||
seq = pyname.get_object()
|
||||
args = arguments.ObjectArguments([pyname])
|
||||
if '__iter__' in seq:
|
||||
obj = seq['__iter__'].get_object()
|
||||
if not isinstance(obj, pyobjects.AbstractFunction):
|
||||
return None
|
||||
iter = obj.get_returned_object(args)
|
||||
if iter is not None and 'next' in iter:
|
||||
holding = iter['next'].get_object().\
|
||||
get_returned_object(args)
|
||||
return holding
|
||||
|
||||
|
||||
def _create_builtin(args, creator):
|
||||
passed = args.get_pynames(['sequence'])[0]
|
||||
if passed is None:
|
||||
holding = None
|
||||
else:
|
||||
holding = _infer_sequence_for_pyname(passed)
|
||||
if holding is not None:
|
||||
return creator(holding)
|
||||
else:
|
||||
return creator()
|
||||
|
||||
|
||||
def _range_function(args):
|
||||
return get_list()
|
||||
|
||||
|
||||
def _reversed_function(args):
|
||||
return _create_builtin(args, get_iterator)
|
||||
|
||||
|
||||
def _sorted_function(args):
|
||||
return _create_builtin(args, get_list)
|
||||
|
||||
|
||||
def _super_function(args):
|
||||
passed_class, passed_self = args.get_arguments(['type', 'self'])
|
||||
if passed_self is None:
|
||||
return passed_class
|
||||
else:
|
||||
#pyclass = passed_self.get_type()
|
||||
pyclass = passed_class
|
||||
if isinstance(pyclass, pyobjects.AbstractClass):
|
||||
supers = pyclass.get_superclasses()
|
||||
if supers:
|
||||
return pyobjects.PyObject(supers[0])
|
||||
return passed_self
|
||||
|
||||
|
||||
def _zip_function(args):
|
||||
args = args.get_pynames(['sequence'])
|
||||
objects = []
|
||||
for seq in args:
|
||||
if seq is None:
|
||||
holding = None
|
||||
else:
|
||||
holding = _infer_sequence_for_pyname(seq)
|
||||
objects.append(holding)
|
||||
tuple = get_tuple(*objects)
|
||||
return get_list(tuple)
|
||||
|
||||
|
||||
def _enumerate_function(args):
|
||||
passed = args.get_pynames(['sequence'])[0]
|
||||
if passed is None:
|
||||
holding = None
|
||||
else:
|
||||
holding = _infer_sequence_for_pyname(passed)
|
||||
tuple = get_tuple(None, holding)
|
||||
return get_iterator(tuple)
|
||||
|
||||
|
||||
def _iter_function(args):
|
||||
passed = args.get_pynames(['sequence'])[0]
|
||||
if passed is None:
|
||||
holding = None
|
||||
else:
|
||||
holding = _infer_sequence_for_pyname(passed)
|
||||
return get_iterator(holding)
|
||||
|
||||
|
||||
def _input_function(args):
|
||||
return get_str()
|
||||
|
||||
|
||||
_initial_builtins = {
|
||||
'list': BuiltinName(get_list_type()),
|
||||
'dict': BuiltinName(get_dict_type()),
|
||||
'tuple': BuiltinName(get_tuple_type()),
|
||||
'set': BuiltinName(get_set_type()),
|
||||
'str': BuiltinName(get_str_type()),
|
||||
'file': BuiltinName(get_file_type()),
|
||||
'open': BuiltinName(get_file_type()),
|
||||
'unicode': BuiltinName(get_str_type()),
|
||||
'range': BuiltinName(BuiltinFunction(function=_range_function,
|
||||
builtin=range)),
|
||||
'reversed': BuiltinName(BuiltinFunction(function=_reversed_function,
|
||||
builtin=reversed)),
|
||||
'sorted': BuiltinName(BuiltinFunction(function=_sorted_function,
|
||||
builtin=sorted)),
|
||||
'super': BuiltinName(BuiltinFunction(function=_super_function,
|
||||
builtin=super)),
|
||||
'property': BuiltinName(BuiltinFunction(function=_property_function,
|
||||
builtin=property)),
|
||||
'zip': BuiltinName(BuiltinFunction(function=_zip_function, builtin=zip)),
|
||||
'enumerate': BuiltinName(BuiltinFunction(function=_enumerate_function,
|
||||
builtin=enumerate)),
|
||||
'object': BuiltinName(BuiltinObject()),
|
||||
'type': BuiltinName(BuiltinType()),
|
||||
'iter': BuiltinName(BuiltinFunction(function=_iter_function,
|
||||
builtin=iter)),
|
||||
'raw_input': BuiltinName(BuiltinFunction(function=_input_function,
|
||||
builtin=raw_input)),
|
||||
}
|
||||
|
||||
builtins = BuiltinModule(pycompat.builtins.__name__, initial=_initial_builtins)
|
||||
|
|
@ -1,450 +0,0 @@
|
|||
import datetime
|
||||
import difflib
|
||||
import os
|
||||
import time
|
||||
|
||||
import rope.base.fscommands
|
||||
from rope.base import taskhandle, exceptions, utils
|
||||
|
||||
|
||||
class Change(object):
|
||||
"""The base class for changes
|
||||
|
||||
Rope refactorings return `Change` objects. They can be previewed,
|
||||
committed or undone.
|
||||
"""
|
||||
|
||||
def do(self, job_set=None):
|
||||
"""Perform the change
|
||||
|
||||
.. note:: Do use this directly. Use `Project.do()` instead.
|
||||
"""
|
||||
|
||||
def undo(self, job_set=None):
|
||||
"""Perform the change
|
||||
|
||||
.. note:: Do use this directly. Use `History.undo()` instead.
|
||||
"""
|
||||
|
||||
def get_description(self):
|
||||
"""Return the description of this change
|
||||
|
||||
This can be used for previewing the changes.
|
||||
"""
|
||||
return str(self)
|
||||
|
||||
def get_changed_resources(self):
|
||||
"""Return the list of resources that will be changed"""
|
||||
return []
|
||||
|
||||
@property
|
||||
@utils.saveit
|
||||
def _operations(self):
|
||||
return _ResourceOperations(self.resource.project)
|
||||
|
||||
|
||||
class ChangeSet(Change):
|
||||
"""A collection of `Change` objects
|
||||
|
||||
This class holds a collection of changes. This class provides
|
||||
these fields:
|
||||
|
||||
* `changes`: the list of changes
|
||||
* `description`: the goal of these changes
|
||||
"""
|
||||
|
||||
def __init__(self, description, timestamp=None):
|
||||
self.changes = []
|
||||
self.description = description
|
||||
self.time = timestamp
|
||||
|
||||
def do(self, job_set=taskhandle.NullJobSet()):
|
||||
try:
|
||||
done = []
|
||||
for change in self.changes:
|
||||
change.do(job_set)
|
||||
done.append(change)
|
||||
self.time = time.time()
|
||||
except Exception:
|
||||
for change in done:
|
||||
change.undo()
|
||||
raise
|
||||
|
||||
def undo(self, job_set=taskhandle.NullJobSet()):
|
||||
try:
|
||||
done = []
|
||||
for change in reversed(self.changes):
|
||||
change.undo(job_set)
|
||||
done.append(change)
|
||||
except Exception:
|
||||
for change in done:
|
||||
change.do()
|
||||
raise
|
||||
|
||||
def add_change(self, change):
|
||||
self.changes.append(change)
|
||||
|
||||
def get_description(self):
|
||||
result = [str(self) + ':\n\n\n']
|
||||
for change in self.changes:
|
||||
result.append(change.get_description())
|
||||
result.append('\n')
|
||||
return ''.join(result)
|
||||
|
||||
def __str__(self):
|
||||
if self.time is not None:
|
||||
date = datetime.datetime.fromtimestamp(self.time)
|
||||
if date.date() == datetime.date.today():
|
||||
string_date = 'today'
|
||||
elif date.date() == (datetime.date.today() -
|
||||
datetime.timedelta(1)):
|
||||
string_date = 'yesterday'
|
||||
elif date.year == datetime.date.today().year:
|
||||
string_date = date.strftime('%b %d')
|
||||
else:
|
||||
string_date = date.strftime('%d %b, %Y')
|
||||
string_time = date.strftime('%H:%M:%S')
|
||||
string_time = '%s %s ' % (string_date, string_time)
|
||||
return self.description + ' - ' + string_time
|
||||
return self.description
|
||||
|
||||
def get_changed_resources(self):
|
||||
result = set()
|
||||
for change in self.changes:
|
||||
result.update(change.get_changed_resources())
|
||||
return result
|
||||
|
||||
|
||||
def _handle_job_set(function):
|
||||
"""A decorator for handling `taskhandle.JobSet`\s
|
||||
|
||||
A decorator for handling `taskhandle.JobSet`\s for `do` and `undo`
|
||||
methods of `Change`\s.
|
||||
"""
|
||||
def call(self, job_set=taskhandle.NullJobSet()):
|
||||
job_set.started_job(str(self))
|
||||
function(self)
|
||||
job_set.finished_job()
|
||||
return call
|
||||
|
||||
|
||||
class ChangeContents(Change):
|
||||
"""A class to change the contents of a file
|
||||
|
||||
Fields:
|
||||
|
||||
* `resource`: The `rope.base.resources.File` to change
|
||||
* `new_contents`: What to write in the file
|
||||
"""
|
||||
|
||||
def __init__(self, resource, new_contents, old_contents=None):
|
||||
self.resource = resource
|
||||
# IDEA: Only saving diffs; possible problems when undo/redoing
|
||||
self.new_contents = new_contents
|
||||
self.old_contents = old_contents
|
||||
|
||||
@_handle_job_set
|
||||
def do(self):
|
||||
if self.old_contents is None:
|
||||
self.old_contents = self.resource.read()
|
||||
self._operations.write_file(self.resource, self.new_contents)
|
||||
|
||||
@_handle_job_set
|
||||
def undo(self):
|
||||
if self.old_contents is None:
|
||||
raise exceptions.HistoryError(
|
||||
'Undoing a change that is not performed yet!')
|
||||
self._operations.write_file(self.resource, self.old_contents)
|
||||
|
||||
def __str__(self):
|
||||
return 'Change <%s>' % self.resource.path
|
||||
|
||||
def get_description(self):
|
||||
new = self.new_contents
|
||||
old = self.old_contents
|
||||
if old is None:
|
||||
if self.resource.exists():
|
||||
old = self.resource.read()
|
||||
else:
|
||||
old = ''
|
||||
result = difflib.unified_diff(
|
||||
old.splitlines(True), new.splitlines(True),
|
||||
'a/' + self.resource.path, 'b/' + self.resource.path)
|
||||
return ''.join(list(result))
|
||||
|
||||
def get_changed_resources(self):
|
||||
return [self.resource]
|
||||
|
||||
|
||||
class MoveResource(Change):
|
||||
"""Move a resource to a new location
|
||||
|
||||
Fields:
|
||||
|
||||
* `resource`: The `rope.base.resources.Resource` to move
|
||||
* `new_resource`: The destination for move; It is the moved
|
||||
resource not the folder containing that resource.
|
||||
"""
|
||||
|
||||
def __init__(self, resource, new_location, exact=False):
|
||||
self.project = resource.project
|
||||
self.resource = resource
|
||||
if not exact:
|
||||
new_location = _get_destination_for_move(resource, new_location)
|
||||
if resource.is_folder():
|
||||
self.new_resource = self.project.get_folder(new_location)
|
||||
else:
|
||||
self.new_resource = self.project.get_file(new_location)
|
||||
|
||||
@_handle_job_set
|
||||
def do(self):
|
||||
self._operations.move(self.resource, self.new_resource)
|
||||
|
||||
@_handle_job_set
|
||||
def undo(self):
|
||||
self._operations.move(self.new_resource, self.resource)
|
||||
|
||||
def __str__(self):
|
||||
return 'Move <%s>' % self.resource.path
|
||||
|
||||
def get_description(self):
|
||||
return 'rename from %s\nrename to %s' % (self.resource.path,
|
||||
self.new_resource.path)
|
||||
|
||||
def get_changed_resources(self):
|
||||
return [self.resource, self.new_resource]
|
||||
|
||||
|
||||
class CreateResource(Change):
|
||||
"""A class to create a resource
|
||||
|
||||
Fields:
|
||||
|
||||
* `resource`: The resource to create
|
||||
"""
|
||||
|
||||
def __init__(self, resource):
|
||||
self.resource = resource
|
||||
|
||||
@_handle_job_set
|
||||
def do(self):
|
||||
self._operations.create(self.resource)
|
||||
|
||||
@_handle_job_set
|
||||
def undo(self):
|
||||
self._operations.remove(self.resource)
|
||||
|
||||
def __str__(self):
|
||||
return 'Create Resource <%s>' % (self.resource.path)
|
||||
|
||||
def get_description(self):
|
||||
return 'new file %s' % (self.resource.path)
|
||||
|
||||
def get_changed_resources(self):
|
||||
return [self.resource]
|
||||
|
||||
def _get_child_path(self, parent, name):
|
||||
if parent.path == '':
|
||||
return name
|
||||
else:
|
||||
return parent.path + '/' + name
|
||||
|
||||
|
||||
class CreateFolder(CreateResource):
|
||||
"""A class to create a folder
|
||||
|
||||
See docs for `CreateResource`.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, name):
|
||||
resource = parent.project.get_folder(
|
||||
self._get_child_path(parent, name))
|
||||
super(CreateFolder, self).__init__(resource)
|
||||
|
||||
|
||||
class CreateFile(CreateResource):
|
||||
"""A class to create a file
|
||||
|
||||
See docs for `CreateResource`.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, name):
|
||||
resource = parent.project.get_file(self._get_child_path(parent, name))
|
||||
super(CreateFile, self).__init__(resource)
|
||||
|
||||
|
||||
class RemoveResource(Change):
|
||||
"""A class to remove a resource
|
||||
|
||||
Fields:
|
||||
|
||||
* `resource`: The resource to be removed
|
||||
"""
|
||||
|
||||
def __init__(self, resource):
|
||||
self.resource = resource
|
||||
|
||||
@_handle_job_set
|
||||
def do(self):
|
||||
self._operations.remove(self.resource)
|
||||
|
||||
# TODO: Undoing remove operations
|
||||
@_handle_job_set
|
||||
def undo(self):
|
||||
raise NotImplementedError(
|
||||
'Undoing `RemoveResource` is not implemented yet.')
|
||||
|
||||
def __str__(self):
|
||||
return 'Remove <%s>' % (self.resource.path)
|
||||
|
||||
def get_changed_resources(self):
|
||||
return [self.resource]
|
||||
|
||||
|
||||
def count_changes(change):
|
||||
"""Counts the number of basic changes a `Change` will make"""
|
||||
if isinstance(change, ChangeSet):
|
||||
result = 0
|
||||
for child in change.changes:
|
||||
result += count_changes(child)
|
||||
return result
|
||||
return 1
|
||||
|
||||
|
||||
def create_job_set(task_handle, change):
|
||||
return task_handle.create_jobset(str(change), count_changes(change))
|
||||
|
||||
|
||||
class _ResourceOperations(object):
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
self.fscommands = project.fscommands
|
||||
self.direct_commands = rope.base.fscommands.FileSystemCommands()
|
||||
|
||||
def _get_fscommands(self, resource):
|
||||
if self.project.is_ignored(resource):
|
||||
return self.direct_commands
|
||||
return self.fscommands
|
||||
|
||||
def write_file(self, resource, contents):
|
||||
data = rope.base.fscommands.unicode_to_file_data(contents)
|
||||
fscommands = self._get_fscommands(resource)
|
||||
fscommands.write(resource.real_path, data)
|
||||
for observer in list(self.project.observers):
|
||||
observer.resource_changed(resource)
|
||||
|
||||
def move(self, resource, new_resource):
|
||||
fscommands = self._get_fscommands(resource)
|
||||
fscommands.move(resource.real_path, new_resource.real_path)
|
||||
for observer in list(self.project.observers):
|
||||
observer.resource_moved(resource, new_resource)
|
||||
|
||||
def create(self, resource):
|
||||
if resource.is_folder():
|
||||
self._create_resource(resource.path, kind='folder')
|
||||
else:
|
||||
self._create_resource(resource.path)
|
||||
for observer in list(self.project.observers):
|
||||
observer.resource_created(resource)
|
||||
|
||||
def remove(self, resource):
|
||||
fscommands = self._get_fscommands(resource)
|
||||
fscommands.remove(resource.real_path)
|
||||
for observer in list(self.project.observers):
|
||||
observer.resource_removed(resource)
|
||||
|
||||
def _create_resource(self, file_name, kind='file'):
|
||||
resource_path = self.project._get_resource_path(file_name)
|
||||
if os.path.exists(resource_path):
|
||||
raise exceptions.RopeError('Resource <%s> already exists'
|
||||
% resource_path)
|
||||
resource = self.project.get_file(file_name)
|
||||
if not resource.parent.exists():
|
||||
raise exceptions.ResourceNotFoundError(
|
||||
'Parent folder of <%s> does not exist' % resource.path)
|
||||
fscommands = self._get_fscommands(resource)
|
||||
try:
|
||||
if kind == 'file':
|
||||
fscommands.create_file(resource_path)
|
||||
else:
|
||||
fscommands.create_folder(resource_path)
|
||||
except IOError as e:
|
||||
raise exceptions.RopeError(e)
|
||||
|
||||
|
||||
def _get_destination_for_move(resource, destination):
|
||||
dest_path = resource.project._get_resource_path(destination)
|
||||
if os.path.isdir(dest_path):
|
||||
if destination != '':
|
||||
return destination + '/' + resource.name
|
||||
else:
|
||||
return resource.name
|
||||
return destination
|
||||
|
||||
|
||||
class ChangeToData(object):
|
||||
|
||||
def convertChangeSet(self, change):
|
||||
description = change.description
|
||||
changes = []
|
||||
for child in change.changes:
|
||||
changes.append(self(child))
|
||||
return (description, changes, change.time)
|
||||
|
||||
def convertChangeContents(self, change):
|
||||
return (change.resource.path, change.new_contents, change.old_contents)
|
||||
|
||||
def convertMoveResource(self, change):
|
||||
return (change.resource.path, change.new_resource.path)
|
||||
|
||||
def convertCreateResource(self, change):
|
||||
return (change.resource.path, change.resource.is_folder())
|
||||
|
||||
def convertRemoveResource(self, change):
|
||||
return (change.resource.path, change.resource.is_folder())
|
||||
|
||||
def __call__(self, change):
|
||||
change_type = type(change)
|
||||
if change_type in (CreateFolder, CreateFile):
|
||||
change_type = CreateResource
|
||||
method = getattr(self, 'convert' + change_type.__name__)
|
||||
return (change_type.__name__, method(change))
|
||||
|
||||
|
||||
class DataToChange(object):
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
|
||||
def makeChangeSet(self, description, changes, time=None):
|
||||
result = ChangeSet(description, time)
|
||||
for child in changes:
|
||||
result.add_change(self(child))
|
||||
return result
|
||||
|
||||
def makeChangeContents(self, path, new_contents, old_contents):
|
||||
resource = self.project.get_file(path)
|
||||
return ChangeContents(resource, new_contents, old_contents)
|
||||
|
||||
def makeMoveResource(self, old_path, new_path):
|
||||
resource = self.project.get_file(old_path)
|
||||
return MoveResource(resource, new_path, exact=True)
|
||||
|
||||
def makeCreateResource(self, path, is_folder):
|
||||
if is_folder:
|
||||
resource = self.project.get_folder(path)
|
||||
else:
|
||||
resource = self.project.get_file(path)
|
||||
return CreateResource(resource)
|
||||
|
||||
def makeRemoveResource(self, path, is_folder):
|
||||
if is_folder:
|
||||
resource = self.project.get_folder(path)
|
||||
else:
|
||||
resource = self.project.get_file(path)
|
||||
return RemoveResource(resource)
|
||||
|
||||
def __call__(self, data):
|
||||
method = getattr(self, 'make' + data[0])
|
||||
return method(*data[1])
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
import bisect
|
||||
import re
|
||||
import token
|
||||
import tokenize
|
||||
|
||||
|
||||
class ChangeCollector(object):
|
||||
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.changes = []
|
||||
|
||||
def add_change(self, start, end, new_text=None):
|
||||
if new_text is None:
|
||||
new_text = self.text[start:end]
|
||||
self.changes.append((start, end, new_text))
|
||||
|
||||
def get_changed(self):
|
||||
if not self.changes:
|
||||
return None
|
||||
|
||||
self.changes.sort(key=lambda x: x[:2])
|
||||
pieces = []
|
||||
last_changed = 0
|
||||
for change in self.changes:
|
||||
start, end, text = change
|
||||
pieces.append(self.text[last_changed:start] + text)
|
||||
last_changed = end
|
||||
if last_changed < len(self.text):
|
||||
pieces.append(self.text[last_changed:])
|
||||
result = ''.join(pieces)
|
||||
if result != self.text:
|
||||
return result
|
||||
|
||||
|
||||
class SourceLinesAdapter(object):
|
||||
"""Adapts source to Lines interface
|
||||
|
||||
Note: The creation of this class is expensive.
|
||||
"""
|
||||
|
||||
def __init__(self, source_code):
|
||||
self.code = source_code
|
||||
self.starts = None
|
||||
self._initialize_line_starts()
|
||||
|
||||
def _initialize_line_starts(self):
|
||||
self.starts = []
|
||||
self.starts.append(0)
|
||||
try:
|
||||
i = 0
|
||||
while True:
|
||||
i = self.code.index('\n', i) + 1
|
||||
self.starts.append(i)
|
||||
except ValueError:
|
||||
pass
|
||||
self.starts.append(len(self.code) + 1)
|
||||
|
||||
def get_line(self, lineno):
|
||||
return self.code[self.starts[lineno - 1]:
|
||||
self.starts[lineno] - 1]
|
||||
|
||||
def length(self):
|
||||
return len(self.starts) - 1
|
||||
|
||||
def get_line_number(self, offset):
|
||||
return bisect.bisect(self.starts, offset)
|
||||
|
||||
def get_line_start(self, lineno):
|
||||
return self.starts[lineno - 1]
|
||||
|
||||
def get_line_end(self, lineno):
|
||||
return self.starts[lineno] - 1
|
||||
|
||||
|
||||
class ArrayLinesAdapter(object):
|
||||
|
||||
def __init__(self, lines):
|
||||
self.lines = lines
|
||||
|
||||
def get_line(self, line_number):
|
||||
return self.lines[line_number - 1]
|
||||
|
||||
def length(self):
|
||||
return len(self.lines)
|
||||
|
||||
|
||||
class LinesToReadline(object):
|
||||
|
||||
def __init__(self, lines, start):
|
||||
self.lines = lines
|
||||
self.current = start
|
||||
|
||||
def readline(self):
|
||||
if self.current <= self.lines.length():
|
||||
self.current += 1
|
||||
return self.lines.get_line(self.current - 1) + '\n'
|
||||
return ''
|
||||
|
||||
def __call__(self):
|
||||
return self.readline()
|
||||
|
||||
|
||||
class _CustomGenerator(object):
|
||||
|
||||
def __init__(self, lines):
|
||||
self.lines = lines
|
||||
self.in_string = ''
|
||||
self.open_count = 0
|
||||
self.continuation = False
|
||||
|
||||
def __call__(self):
|
||||
size = self.lines.length()
|
||||
result = []
|
||||
i = 1
|
||||
while i <= size:
|
||||
while i <= size and not self.lines.get_line(i).strip():
|
||||
i += 1
|
||||
if i <= size:
|
||||
start = i
|
||||
while True:
|
||||
line = self.lines.get_line(i)
|
||||
self._analyze_line(line)
|
||||
if not (self.continuation or self.open_count or
|
||||
self.in_string) or i == size:
|
||||
break
|
||||
i += 1
|
||||
result.append((start, i))
|
||||
i += 1
|
||||
return result
|
||||
|
||||
# Matches all backslashes before the token, to detect escaped quotes
|
||||
_main_tokens = re.compile(r'(\\*)((\'\'\'|"""|\'|")|#|\[|\]|\{|\}|\(|\))')
|
||||
|
||||
def _analyze_line(self, line):
|
||||
token = None
|
||||
for match in self._main_tokens.finditer(line):
|
||||
prefix = match.group(1)
|
||||
token = match.group(2)
|
||||
# Skip any tokens which are escaped
|
||||
if len(prefix) % 2 == 1:
|
||||
continue
|
||||
if token in ["'''", '"""', "'", '"']:
|
||||
if not self.in_string:
|
||||
self.in_string = token
|
||||
elif self.in_string == token:
|
||||
self.in_string = ''
|
||||
if self.in_string:
|
||||
continue
|
||||
if token == '#':
|
||||
break
|
||||
if token in '([{':
|
||||
self.open_count += 1
|
||||
elif token in ')]}':
|
||||
self.open_count -= 1
|
||||
if line and token != '#' and line.endswith('\\'):
|
||||
self.continuation = True
|
||||
else:
|
||||
self.continuation = False
|
||||
|
||||
|
||||
def custom_generator(lines):
|
||||
return _CustomGenerator(lines)()
|
||||
|
||||
|
||||
class LogicalLineFinder(object):
|
||||
|
||||
def __init__(self, lines):
|
||||
self.lines = lines
|
||||
|
||||
def logical_line_in(self, line_number):
|
||||
indents = count_line_indents(self.lines.get_line(line_number))
|
||||
tries = 0
|
||||
while True:
|
||||
block_start = get_block_start(self.lines, line_number, indents)
|
||||
try:
|
||||
return self._block_logical_line(block_start, line_number)
|
||||
except IndentationError as e:
|
||||
tries += 1
|
||||
if tries == 5:
|
||||
raise e
|
||||
lineno = e.lineno + block_start - 1
|
||||
indents = count_line_indents(self.lines.get_line(lineno))
|
||||
|
||||
def generate_starts(self, start_line=1, end_line=None):
|
||||
for start, end in self.generate_regions(start_line, end_line):
|
||||
yield start
|
||||
|
||||
def generate_regions(self, start_line=1, end_line=None):
|
||||
# XXX: `block_start` should be at a better position!
|
||||
block_start = 1
|
||||
readline = LinesToReadline(self.lines, block_start)
|
||||
try:
|
||||
for start, end in self._logical_lines(readline):
|
||||
real_start = start + block_start - 1
|
||||
real_start = self._first_non_blank(real_start)
|
||||
if end_line is not None and real_start >= end_line:
|
||||
break
|
||||
real_end = end + block_start - 1
|
||||
if real_start >= start_line:
|
||||
yield (real_start, real_end)
|
||||
except tokenize.TokenError:
|
||||
pass
|
||||
|
||||
def _block_logical_line(self, block_start, line_number):
|
||||
readline = LinesToReadline(self.lines, block_start)
|
||||
shifted = line_number - block_start + 1
|
||||
region = self._calculate_logical(readline, shifted)
|
||||
start = self._first_non_blank(region[0] + block_start - 1)
|
||||
if region[1] is None:
|
||||
end = self.lines.length()
|
||||
else:
|
||||
end = region[1] + block_start - 1
|
||||
return start, end
|
||||
|
||||
def _calculate_logical(self, readline, line_number):
|
||||
last_end = 1
|
||||
try:
|
||||
for start, end in self._logical_lines(readline):
|
||||
if line_number <= end:
|
||||
return (start, end)
|
||||
last_end = end + 1
|
||||
except tokenize.TokenError as e:
|
||||
current = e.args[1][0]
|
||||
return (last_end, max(last_end, current - 1))
|
||||
return (last_end, None)
|
||||
|
||||
def _logical_lines(self, readline):
|
||||
last_end = 1
|
||||
for current_token in tokenize.generate_tokens(readline):
|
||||
current = current_token[2][0]
|
||||
if current_token[0] == token.NEWLINE:
|
||||
yield (last_end, current)
|
||||
last_end = current + 1
|
||||
|
||||
def _first_non_blank(self, line_number):
|
||||
current = line_number
|
||||
while current < self.lines.length():
|
||||
line = self.lines.get_line(current).strip()
|
||||
if line and not line.startswith('#'):
|
||||
return current
|
||||
current += 1
|
||||
return current
|
||||
|
||||
|
||||
def tokenizer_generator(lines):
|
||||
return LogicalLineFinder(lines).generate_regions()
|
||||
|
||||
|
||||
class CachingLogicalLineFinder(object):
|
||||
|
||||
def __init__(self, lines, generate=custom_generator):
|
||||
self.lines = lines
|
||||
self._generate = generate
|
||||
|
||||
_starts = None
|
||||
|
||||
@property
|
||||
def starts(self):
|
||||
if self._starts is None:
|
||||
self._init_logicals()
|
||||
return self._starts
|
||||
|
||||
_ends = None
|
||||
|
||||
@property
|
||||
def ends(self):
|
||||
if self._ends is None:
|
||||
self._init_logicals()
|
||||
return self._ends
|
||||
|
||||
def _init_logicals(self):
|
||||
"""Should initialize _starts and _ends attributes"""
|
||||
size = self.lines.length() + 1
|
||||
self._starts = [None] * size
|
||||
self._ends = [None] * size
|
||||
for start, end in self._generate(self.lines):
|
||||
self._starts[start] = True
|
||||
self._ends[end] = True
|
||||
|
||||
def logical_line_in(self, line_number):
|
||||
start = line_number
|
||||
while start > 0 and not self.starts[start]:
|
||||
start -= 1
|
||||
if start == 0:
|
||||
try:
|
||||
start = self.starts.index(True, line_number)
|
||||
except ValueError:
|
||||
return (line_number, line_number)
|
||||
return (start, self.ends.index(True, start))
|
||||
|
||||
def generate_starts(self, start_line=1, end_line=None):
|
||||
if end_line is None:
|
||||
end_line = self.lines.length()
|
||||
for index in range(start_line, end_line):
|
||||
if self.starts[index]:
|
||||
yield index
|
||||
|
||||
|
||||
def get_block_start(lines, lineno, maximum_indents=80):
|
||||
"""Approximate block start"""
|
||||
pattern = get_block_start_patterns()
|
||||
for i in range(lineno, 0, -1):
|
||||
match = pattern.search(lines.get_line(i))
|
||||
if match is not None and \
|
||||
count_line_indents(lines.get_line(i)) <= maximum_indents:
|
||||
striped = match.string.lstrip()
|
||||
# Maybe we're in a list comprehension or generator expression
|
||||
if i > 1 and striped.startswith('if') or striped.startswith('for'):
|
||||
bracs = 0
|
||||
for j in range(i, min(i + 5, lines.length() + 1)):
|
||||
for c in lines.get_line(j):
|
||||
if c == '#':
|
||||
break
|
||||
if c in '[(':
|
||||
bracs += 1
|
||||
if c in ')]':
|
||||
bracs -= 1
|
||||
if bracs < 0:
|
||||
break
|
||||
if bracs < 0:
|
||||
break
|
||||
if bracs < 0:
|
||||
continue
|
||||
return i
|
||||
return 1
|
||||
|
||||
|
||||
_block_start_pattern = None
|
||||
|
||||
|
||||
def get_block_start_patterns():
|
||||
global _block_start_pattern
|
||||
if not _block_start_pattern:
|
||||
pattern = '^\\s*(((def|class|if|elif|except|for|while|with)\\s)|'\
|
||||
'((try|else|finally|except)\\s*:))'
|
||||
_block_start_pattern = re.compile(pattern, re.M)
|
||||
return _block_start_pattern
|
||||
|
||||
|
||||
def count_line_indents(line):
|
||||
indents = 0
|
||||
for char in line:
|
||||
if char == ' ':
|
||||
indents += 1
|
||||
elif char == '\t':
|
||||
indents += 8
|
||||
else:
|
||||
return indents
|
||||
return 0
|
||||
|
||||
|
||||
def get_string_pattern():
|
||||
start = r'(\b[uU]?[rR]?)?'
|
||||
longstr = r'%s"""(\\.|"(?!"")|\\\n|[^"\\])*"""' % start
|
||||
shortstr = r'%s"(\\.|\\\n|[^"\\])*"' % start
|
||||
return '|'.join([longstr, longstr.replace('"', "'"),
|
||||
shortstr, shortstr.replace('"', "'")])
|
||||
|
||||
|
||||
def get_comment_pattern():
|
||||
return r'#[^\n]*'
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
# The default ``config.py``
|
||||
# flake8: noqa
|
||||
|
||||
|
||||
def set_prefs(prefs):
|
||||
"""This function is called before opening the project"""
|
||||
|
||||
# Specify which files and folders to ignore in the project.
|
||||
# Changes to ignored resources are not added to the history and
|
||||
# VCSs. Also they are not returned in `Project.get_files()`.
|
||||
# Note that ``?`` and ``*`` match all characters but slashes.
|
||||
# '*.pyc': matches 'test.pyc' and 'pkg/test.pyc'
|
||||
# 'mod*.pyc': matches 'test/mod1.pyc' but not 'mod/1.pyc'
|
||||
# '.svn': matches 'pkg/.svn' and all of its children
|
||||
# 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o'
|
||||
# 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o'
|
||||
prefs['ignored_resources'] = [
|
||||
'*.pyc', '*~', '.ropeproject', '.hg', '.svn', '_svn',
|
||||
'.git', '.tox', '.env', 'env', 'venv', 'node_modules',
|
||||
'bower_components'
|
||||
]
|
||||
|
||||
# Specifies which files should be considered python files. It is
|
||||
# useful when you have scripts inside your project. Only files
|
||||
# ending with ``.py`` are considered to be python files by
|
||||
# default.
|
||||
#prefs['python_files'] = ['*.py']
|
||||
|
||||
# Custom source folders: By default rope searches the project
|
||||
# for finding source folders (folders that should be searched
|
||||
# for finding modules). You can add paths to that list. Note
|
||||
# that rope guesses project source folders correctly most of the
|
||||
# time; use this if you have any problems.
|
||||
# The folders should be relative to project root and use '/' for
|
||||
# separating folders regardless of the platform rope is running on.
|
||||
# 'src/my_source_folder' for instance.
|
||||
#prefs.add('source_folders', 'src')
|
||||
|
||||
# You can extend python path for looking up modules
|
||||
#prefs.add('python_path', '~/python/')
|
||||
|
||||
# Should rope save object information or not.
|
||||
prefs['save_objectdb'] = True
|
||||
prefs['compress_objectdb'] = False
|
||||
|
||||
# If `True`, rope analyzes each module when it is being saved.
|
||||
prefs['automatic_soa'] = True
|
||||
# The depth of calls to follow in static object analysis
|
||||
prefs['soa_followed_calls'] = 0
|
||||
|
||||
# If `False` when running modules or unit tests "dynamic object
|
||||
# analysis" is turned off. This makes them much faster.
|
||||
prefs['perform_doa'] = True
|
||||
|
||||
# Rope can check the validity of its object DB when running.
|
||||
prefs['validate_objectdb'] = True
|
||||
|
||||
# How many undos to hold?
|
||||
prefs['max_history_items'] = 32
|
||||
|
||||
# Shows whether to save history across sessions.
|
||||
prefs['save_history'] = True
|
||||
prefs['compress_history'] = False
|
||||
|
||||
# Set the number spaces used for indenting. According to
|
||||
# :PEP:`8`, it is best to use 4 spaces. Since most of rope's
|
||||
# unit-tests use 4 spaces it is more reliable, too.
|
||||
prefs['indent_size'] = 4
|
||||
|
||||
# Builtin and c-extension modules that are allowed to be imported
|
||||
# and inspected by rope.
|
||||
prefs['extension_modules'] = []
|
||||
|
||||
# Add all standard c-extensions to extension_modules list.
|
||||
prefs['import_dynload_stdmods'] = True
|
||||
|
||||
# If `True` modules with syntax errors are considered to be empty.
|
||||
# The default value is `False`; When `False` syntax errors raise
|
||||
# `rope.base.exceptions.ModuleSyntaxError` exception.
|
||||
prefs['ignore_syntax_errors'] = False
|
||||
|
||||
# If `True`, rope ignores unresolvable imports. Otherwise, they
|
||||
# appear in the importing namespace.
|
||||
prefs['ignore_bad_imports'] = False
|
||||
|
||||
# If `True`, rope will insert new module imports as
|
||||
# `from <package> import <module>` by default.
|
||||
prefs['prefer_module_from_imports'] = False
|
||||
|
||||
# If `True`, rope will transform a comma list of imports into
|
||||
# multiple separate import statements when organizing
|
||||
# imports.
|
||||
prefs['split_imports'] = False
|
||||
|
||||
# If `True`, rope will sort imports alphabetically by module name
|
||||
# instead of alphabetically by import statement, with from imports
|
||||
# after normal imports.
|
||||
prefs['sort_imports_alphabetically'] = False
|
||||
|
||||
|
||||
def project_opened(project):
|
||||
"""This function is called after opening the project"""
|
||||
# Do whatever you like here!
|
||||
|
|
@ -1,332 +0,0 @@
|
|||
import rope.base.builtins
|
||||
import rope.base.pynames
|
||||
import rope.base.pyobjects
|
||||
from rope.base import ast, astutils, exceptions, pyobjects, arguments, worder
|
||||
from rope.base.utils import pycompat
|
||||
|
||||
|
||||
BadIdentifierError = exceptions.BadIdentifierError
|
||||
|
||||
|
||||
def eval_location(pymodule, offset):
|
||||
"""Find the pyname at the offset"""
|
||||
return eval_location2(pymodule, offset)[1]
|
||||
|
||||
|
||||
def eval_location2(pymodule, offset):
|
||||
"""Find the primary and pyname at offset"""
|
||||
pyname_finder = ScopeNameFinder(pymodule)
|
||||
return pyname_finder.get_primary_and_pyname_at(offset)
|
||||
|
||||
|
||||
def eval_node(scope, node):
|
||||
"""Evaluate a `ast.AST` node and return a PyName
|
||||
|
||||
Return `None` if the expression cannot be evaluated.
|
||||
"""
|
||||
return eval_node2(scope, node)[1]
|
||||
|
||||
|
||||
def eval_node2(scope, node):
|
||||
evaluator = StatementEvaluator(scope)
|
||||
ast.walk(node, evaluator)
|
||||
return evaluator.old_result, evaluator.result
|
||||
|
||||
|
||||
def eval_str(holding_scope, name):
|
||||
return eval_str2(holding_scope, name)[1]
|
||||
|
||||
|
||||
def eval_str2(holding_scope, name):
|
||||
try:
|
||||
# parenthesizing for handling cases like 'a_var.\nattr'
|
||||
node = ast.parse('(%s)' % name)
|
||||
except SyntaxError:
|
||||
raise BadIdentifierError(
|
||||
'Not a resolvable python identifier selected.')
|
||||
return eval_node2(holding_scope, node)
|
||||
|
||||
|
||||
class ScopeNameFinder(object):
|
||||
|
||||
def __init__(self, pymodule):
|
||||
self.module_scope = pymodule.get_scope()
|
||||
self.lines = pymodule.lines
|
||||
self.worder = worder.Worder(pymodule.source_code, True)
|
||||
|
||||
def _is_defined_in_class_body(self, holding_scope, offset, lineno):
|
||||
if lineno == holding_scope.get_start() and \
|
||||
holding_scope.parent is not None and \
|
||||
holding_scope.parent.get_kind() == 'Class' and \
|
||||
self.worder.is_a_class_or_function_name_in_header(offset):
|
||||
return True
|
||||
if lineno != holding_scope.get_start() and \
|
||||
holding_scope.get_kind() == 'Class' and \
|
||||
self.worder.is_name_assigned_in_class_body(offset):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_function_name_in_function_header(self, scope, offset, lineno):
|
||||
if scope.get_start() <= lineno <= scope.get_body_start() and \
|
||||
scope.get_kind() == 'Function' and \
|
||||
self.worder.is_a_class_or_function_name_in_header(offset):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_pyname_at(self, offset):
|
||||
return self.get_primary_and_pyname_at(offset)[1]
|
||||
|
||||
def get_primary_and_pyname_at(self, offset):
|
||||
lineno = self.lines.get_line_number(offset)
|
||||
holding_scope = self.module_scope.get_inner_scope_for_line(lineno)
|
||||
# function keyword parameter
|
||||
if self.worder.is_function_keyword_parameter(offset):
|
||||
keyword_name = self.worder.get_word_at(offset)
|
||||
pyobject = self.get_enclosing_function(offset)
|
||||
if isinstance(pyobject, pyobjects.PyFunction):
|
||||
return (None,
|
||||
pyobject.get_parameters().get(keyword_name, None))
|
||||
# class body
|
||||
if self._is_defined_in_class_body(holding_scope, offset, lineno):
|
||||
class_scope = holding_scope
|
||||
if lineno == holding_scope.get_start():
|
||||
class_scope = holding_scope.parent
|
||||
name = self.worder.get_primary_at(offset).strip()
|
||||
try:
|
||||
return (None, class_scope.pyobject[name])
|
||||
except rope.base.exceptions.AttributeNotFoundError:
|
||||
return (None, None)
|
||||
# function header
|
||||
if self._is_function_name_in_function_header(holding_scope,
|
||||
offset, lineno):
|
||||
name = self.worder.get_primary_at(offset).strip()
|
||||
return (None, holding_scope.parent[name])
|
||||
# from statement module
|
||||
if self.worder.is_from_statement_module(offset):
|
||||
module = self.worder.get_primary_at(offset)
|
||||
module_pyname = self._find_module(module)
|
||||
return (None, module_pyname)
|
||||
if self.worder.is_from_aliased(offset):
|
||||
name = self.worder.get_from_aliased(offset)
|
||||
else:
|
||||
name = self.worder.get_primary_at(offset)
|
||||
return eval_str2(holding_scope, name)
|
||||
|
||||
def get_enclosing_function(self, offset):
|
||||
function_parens = self.worder.find_parens_start_from_inside(offset)
|
||||
try:
|
||||
function_pyname = self.get_pyname_at(function_parens - 1)
|
||||
except BadIdentifierError:
|
||||
function_pyname = None
|
||||
if function_pyname is not None:
|
||||
pyobject = function_pyname.get_object()
|
||||
if isinstance(pyobject, pyobjects.AbstractFunction):
|
||||
return pyobject
|
||||
elif isinstance(pyobject, pyobjects.AbstractClass) and \
|
||||
'__init__' in pyobject:
|
||||
return pyobject['__init__'].get_object()
|
||||
elif '__call__' in pyobject:
|
||||
return pyobject['__call__'].get_object()
|
||||
return None
|
||||
|
||||
def _find_module(self, module_name):
|
||||
dots = 0
|
||||
while module_name[dots] == '.':
|
||||
dots += 1
|
||||
return rope.base.pynames.ImportedModule(
|
||||
self.module_scope.pyobject, module_name[dots:], dots)
|
||||
|
||||
|
||||
class StatementEvaluator(object):
|
||||
|
||||
def __init__(self, scope):
|
||||
self.scope = scope
|
||||
self.result = None
|
||||
self.old_result = None
|
||||
|
||||
def _Name(self, node):
|
||||
self.result = self.scope.lookup(node.id)
|
||||
|
||||
def _Attribute(self, node):
|
||||
pyname = eval_node(self.scope, node.value)
|
||||
if pyname is None:
|
||||
pyname = rope.base.pynames.UnboundName()
|
||||
self.old_result = pyname
|
||||
if pyname.get_object() != rope.base.pyobjects.get_unknown():
|
||||
try:
|
||||
self.result = pyname.get_object()[node.attr]
|
||||
except exceptions.AttributeNotFoundError:
|
||||
self.result = None
|
||||
|
||||
def _Call(self, node):
|
||||
primary, pyobject = self._get_primary_and_object_for_node(node.func)
|
||||
if pyobject is None:
|
||||
return
|
||||
|
||||
def _get_returned(pyobject):
|
||||
args = arguments.create_arguments(primary, pyobject,
|
||||
node, self.scope)
|
||||
return pyobject.get_returned_object(args)
|
||||
if isinstance(pyobject, rope.base.pyobjects.AbstractClass):
|
||||
result = None
|
||||
if '__new__' in pyobject:
|
||||
new_function = pyobject['__new__'].get_object()
|
||||
result = _get_returned(new_function)
|
||||
if result is None or \
|
||||
result == rope.base.pyobjects.get_unknown():
|
||||
result = rope.base.pyobjects.PyObject(pyobject)
|
||||
self.result = rope.base.pynames.UnboundName(pyobject=result)
|
||||
return
|
||||
|
||||
pyfunction = None
|
||||
if isinstance(pyobject, rope.base.pyobjects.AbstractFunction):
|
||||
pyfunction = pyobject
|
||||
elif '__call__' in pyobject:
|
||||
pyfunction = pyobject['__call__'].get_object()
|
||||
if pyfunction is not None:
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=_get_returned(pyfunction))
|
||||
|
||||
def _Str(self, node):
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=rope.base.builtins.get_str())
|
||||
|
||||
def _Num(self, node):
|
||||
type_name = type(node.n).__name__
|
||||
self.result = self._get_builtin_name(type_name)
|
||||
|
||||
def _get_builtin_name(self, type_name):
|
||||
pytype = rope.base.builtins.builtins[type_name].get_object()
|
||||
return rope.base.pynames.UnboundName(
|
||||
rope.base.pyobjects.PyObject(pytype))
|
||||
|
||||
def _BinOp(self, node):
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
self._get_object_for_node(node.left))
|
||||
|
||||
def _BoolOp(self, node):
|
||||
pyobject = self._get_object_for_node(node.values[0])
|
||||
if pyobject is None:
|
||||
pyobject = self._get_object_for_node(node.values[1])
|
||||
self.result = rope.base.pynames.UnboundName(pyobject)
|
||||
|
||||
def _Repr(self, node):
|
||||
self.result = self._get_builtin_name('str')
|
||||
|
||||
def _UnaryOp(self, node):
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
self._get_object_for_node(node.operand))
|
||||
|
||||
def _Compare(self, node):
|
||||
self.result = self._get_builtin_name('bool')
|
||||
|
||||
def _Dict(self, node):
|
||||
keys = None
|
||||
values = None
|
||||
if node.keys:
|
||||
keys = self._get_object_for_node(node.keys[0])
|
||||
values = self._get_object_for_node(node.values[0])
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=rope.base.builtins.get_dict(keys, values))
|
||||
|
||||
def _List(self, node):
|
||||
holding = None
|
||||
if node.elts:
|
||||
holding = self._get_object_for_node(node.elts[0])
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=rope.base.builtins.get_list(holding))
|
||||
|
||||
def _ListComp(self, node):
|
||||
pyobject = self._what_does_comprehension_hold(node)
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=rope.base.builtins.get_list(pyobject))
|
||||
|
||||
def _GeneratorExp(self, node):
|
||||
pyobject = self._what_does_comprehension_hold(node)
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=rope.base.builtins.get_iterator(pyobject))
|
||||
|
||||
def _what_does_comprehension_hold(self, node):
|
||||
scope = self._make_comprehension_scope(node)
|
||||
pyname = eval_node(scope, node.elt)
|
||||
return pyname.get_object() if pyname is not None else None
|
||||
|
||||
def _make_comprehension_scope(self, node):
|
||||
scope = self.scope
|
||||
module = scope.pyobject.get_module()
|
||||
names = {}
|
||||
for comp in node.generators:
|
||||
new_names = _get_evaluated_names(comp.target, comp.iter, module,
|
||||
'.__iter__().next()', node.lineno)
|
||||
names.update(new_names)
|
||||
return rope.base.pyscopes.TemporaryScope(scope.pycore, scope, names)
|
||||
|
||||
def _Tuple(self, node):
|
||||
objects = []
|
||||
if len(node.elts) < 4:
|
||||
for stmt in node.elts:
|
||||
pyobject = self._get_object_for_node(stmt)
|
||||
objects.append(pyobject)
|
||||
else:
|
||||
objects.append(self._get_object_for_node(node.elts[0]))
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=rope.base.builtins.get_tuple(*objects))
|
||||
|
||||
def _get_object_for_node(self, stmt):
|
||||
pyname = eval_node(self.scope, stmt)
|
||||
pyobject = None
|
||||
if pyname is not None:
|
||||
pyobject = pyname.get_object()
|
||||
return pyobject
|
||||
|
||||
def _get_primary_and_object_for_node(self, stmt):
|
||||
primary, pyname = eval_node2(self.scope, stmt)
|
||||
pyobject = None
|
||||
if pyname is not None:
|
||||
pyobject = pyname.get_object()
|
||||
return primary, pyobject
|
||||
|
||||
def _Subscript(self, node):
|
||||
if isinstance(node.slice, ast.Index):
|
||||
self._call_function(node.value, '__getitem__',
|
||||
[node.slice.value])
|
||||
elif isinstance(node.slice, ast.Slice):
|
||||
self._call_function(node.value, '__getitem__',
|
||||
[node.slice])
|
||||
|
||||
def _Slice(self, node):
|
||||
self.result = self._get_builtin_name('slice')
|
||||
|
||||
def _call_function(self, node, function_name, other_args=None):
|
||||
pyname = eval_node(self.scope, node)
|
||||
if pyname is not None:
|
||||
pyobject = pyname.get_object()
|
||||
else:
|
||||
return
|
||||
if function_name in pyobject:
|
||||
called = pyobject[function_name].get_object()
|
||||
if not called or \
|
||||
not isinstance(called, pyobjects.AbstractFunction):
|
||||
return
|
||||
args = [node]
|
||||
if other_args:
|
||||
args += other_args
|
||||
arguments_ = arguments.Arguments(args, self.scope)
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=called.get_returned_object(arguments_))
|
||||
|
||||
def _Lambda(self, node):
|
||||
self.result = rope.base.pynames.UnboundName(
|
||||
pyobject=rope.base.builtins.Lambda(node, self.scope))
|
||||
|
||||
|
||||
def _get_evaluated_names(targets, assigned, module, evaluation, lineno):
|
||||
result = {}
|
||||
for name, levels in astutils.get_name_levels(targets):
|
||||
assignment = rope.base.pynames.AssignmentValue(assigned, levels,
|
||||
evaluation)
|
||||
# XXX: this module should not access `rope.base.pynamesdef`!
|
||||
pyname = rope.base.pynamesdef.AssignedName(lineno, module)
|
||||
pyname.assignments.append(assignment)
|
||||
result[name] = pyname
|
||||
return result
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
class RopeError(Exception):
|
||||
"""Base exception for rope"""
|
||||
|
||||
|
||||
class ResourceNotFoundError(RopeError):
|
||||
"""Resource not found exception"""
|
||||
|
||||
|
||||
class RefactoringError(RopeError):
|
||||
"""Errors for performing a refactoring"""
|
||||
|
||||
|
||||
class InterruptedTaskError(RopeError):
|
||||
"""The task has been interrupted"""
|
||||
|
||||
|
||||
class HistoryError(RopeError):
|
||||
"""Errors for history undo/redo operations"""
|
||||
|
||||
|
||||
class ModuleNotFoundError(RopeError):
|
||||
"""Module not found exception"""
|
||||
|
||||
|
||||
class AttributeNotFoundError(RopeError):
|
||||
"""Attribute not found exception"""
|
||||
|
||||
|
||||
class NameNotFoundError(RopeError):
|
||||
"""Name not found exception"""
|
||||
|
||||
|
||||
class BadIdentifierError(RopeError):
|
||||
"""The name cannot be resolved"""
|
||||
|
||||
|
||||
class ModuleSyntaxError(RopeError):
|
||||
"""Module has syntax errors
|
||||
|
||||
The `filename` and `lineno` fields indicate where the error has
|
||||
occurred.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, filename, lineno, message):
|
||||
self.filename = filename
|
||||
self.lineno = lineno
|
||||
self.message_ = message
|
||||
super(ModuleSyntaxError, self).__init__(
|
||||
'Syntax error in file <%s> line <%s>: %s' %
|
||||
(filename, lineno, message))
|
||||
|
||||
|
||||
class ModuleDecodeError(RopeError):
|
||||
"""Cannot decode module"""
|
||||
|
||||
def __init__(self, filename, message):
|
||||
self.filename = filename
|
||||
self.message_ = message
|
||||
super(ModuleDecodeError, self).__init__(
|
||||
'Cannot decode file <%s>: %s' % (filename, message))
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
"""Project file system commands.
|
||||
|
||||
This modules implements file system operations used by rope. Different
|
||||
version control systems can be supported by implementing the interface
|
||||
provided by `FileSystemCommands` class. See `SubversionCommands` and
|
||||
`MercurialCommands` for example.
|
||||
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
import rope.base.utils.pycompat as pycompat
|
||||
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
unicode = str
|
||||
|
||||
def create_fscommands(root):
|
||||
dirlist = os.listdir(root)
|
||||
commands = {'.hg': MercurialCommands,
|
||||
'.svn': SubversionCommands,
|
||||
'.git': GITCommands,
|
||||
'_svn': SubversionCommands,
|
||||
'_darcs': DarcsCommands}
|
||||
for key in commands:
|
||||
if key in dirlist:
|
||||
try:
|
||||
return commands[key](root)
|
||||
except (ImportError, OSError):
|
||||
pass
|
||||
return FileSystemCommands()
|
||||
|
||||
|
||||
class FileSystemCommands(object):
|
||||
|
||||
def create_file(self, path):
|
||||
open(path, 'w').close()
|
||||
|
||||
def create_folder(self, path):
|
||||
os.mkdir(path)
|
||||
|
||||
def move(self, path, new_location):
|
||||
shutil.move(path, new_location)
|
||||
|
||||
def remove(self, path):
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
else:
|
||||
shutil.rmtree(path)
|
||||
|
||||
def write(self, path, data):
|
||||
file_ = open(path, 'wb')
|
||||
try:
|
||||
file_.write(data)
|
||||
finally:
|
||||
file_.close()
|
||||
|
||||
|
||||
class SubversionCommands(object):
|
||||
|
||||
def __init__(self, *args):
|
||||
self.normal_actions = FileSystemCommands()
|
||||
import pysvn
|
||||
self.client = pysvn.Client()
|
||||
|
||||
def create_file(self, path):
|
||||
self.normal_actions.create_file(path)
|
||||
self.client.add(path, force=True)
|
||||
|
||||
def create_folder(self, path):
|
||||
self.normal_actions.create_folder(path)
|
||||
self.client.add(path, force=True)
|
||||
|
||||
def move(self, path, new_location):
|
||||
self.client.move(path, new_location, force=True)
|
||||
|
||||
def remove(self, path):
|
||||
self.client.remove(path, force=True)
|
||||
|
||||
def write(self, path, data):
|
||||
self.normal_actions.write(path, data)
|
||||
|
||||
|
||||
class MercurialCommands(object):
|
||||
|
||||
def __init__(self, root):
|
||||
self.hg = self._import_mercurial()
|
||||
self.normal_actions = FileSystemCommands()
|
||||
try:
|
||||
self.ui = self.hg.ui.ui(
|
||||
verbose=False, debug=False, quiet=True,
|
||||
interactive=False, traceback=False, report_untrusted=False)
|
||||
except:
|
||||
self.ui = self.hg.ui.ui()
|
||||
self.ui.setconfig('ui', 'interactive', 'no')
|
||||
self.ui.setconfig('ui', 'debug', 'no')
|
||||
self.ui.setconfig('ui', 'traceback', 'no')
|
||||
self.ui.setconfig('ui', 'verbose', 'no')
|
||||
self.ui.setconfig('ui', 'report_untrusted', 'no')
|
||||
self.ui.setconfig('ui', 'quiet', 'yes')
|
||||
|
||||
self.repo = self.hg.hg.repository(self.ui, root)
|
||||
|
||||
def _import_mercurial(self):
|
||||
import mercurial.commands
|
||||
import mercurial.hg
|
||||
import mercurial.ui
|
||||
return mercurial
|
||||
|
||||
def create_file(self, path):
|
||||
self.normal_actions.create_file(path)
|
||||
self.hg.commands.add(self.ui, self.repo, path)
|
||||
|
||||
def create_folder(self, path):
|
||||
self.normal_actions.create_folder(path)
|
||||
|
||||
def move(self, path, new_location):
|
||||
self.hg.commands.rename(self.ui, self.repo, path,
|
||||
new_location, after=False)
|
||||
|
||||
def remove(self, path):
|
||||
self.hg.commands.remove(self.ui, self.repo, path)
|
||||
|
||||
def write(self, path, data):
|
||||
self.normal_actions.write(path, data)
|
||||
|
||||
|
||||
class GITCommands(object):
|
||||
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self._do(['version'])
|
||||
self.normal_actions = FileSystemCommands()
|
||||
|
||||
def create_file(self, path):
|
||||
self.normal_actions.create_file(path)
|
||||
self._do(['add', self._in_dir(path)])
|
||||
|
||||
def create_folder(self, path):
|
||||
self.normal_actions.create_folder(path)
|
||||
|
||||
def move(self, path, new_location):
|
||||
self._do(['mv', self._in_dir(path), self._in_dir(new_location)])
|
||||
|
||||
def remove(self, path):
|
||||
self._do(['rm', self._in_dir(path)])
|
||||
|
||||
def write(self, path, data):
|
||||
# XXX: should we use ``git add``?
|
||||
self.normal_actions.write(path, data)
|
||||
|
||||
def _do(self, args):
|
||||
_execute(['git'] + args, cwd=self.root)
|
||||
|
||||
def _in_dir(self, path):
|
||||
if path.startswith(self.root):
|
||||
return path[len(self.root) + 1:]
|
||||
return self.root
|
||||
|
||||
|
||||
class DarcsCommands(object):
|
||||
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.normal_actions = FileSystemCommands()
|
||||
|
||||
def create_file(self, path):
|
||||
self.normal_actions.create_file(path)
|
||||
self._do(['add', path])
|
||||
|
||||
def create_folder(self, path):
|
||||
self.normal_actions.create_folder(path)
|
||||
self._do(['add', path])
|
||||
|
||||
def move(self, path, new_location):
|
||||
self._do(['mv', path, new_location])
|
||||
|
||||
def remove(self, path):
|
||||
self.normal_actions.remove(path)
|
||||
|
||||
def write(self, path, data):
|
||||
self.normal_actions.write(path, data)
|
||||
|
||||
def _do(self, args):
|
||||
_execute(['darcs'] + args, cwd=self.root)
|
||||
|
||||
|
||||
def _execute(args, cwd=None):
|
||||
process = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE)
|
||||
process.wait()
|
||||
return process.returncode
|
||||
|
||||
|
||||
def unicode_to_file_data(contents, encoding=None):
|
||||
if not isinstance(contents, unicode):
|
||||
return contents
|
||||
if encoding is None:
|
||||
encoding = read_str_coding(contents)
|
||||
if encoding is not None:
|
||||
return contents.encode(encoding)
|
||||
try:
|
||||
return contents.encode()
|
||||
except UnicodeEncodeError:
|
||||
return contents.encode('utf-8')
|
||||
|
||||
|
||||
def file_data_to_unicode(data, encoding=None):
|
||||
result = _decode_data(data, encoding)
|
||||
if '\r' in result:
|
||||
result = result.replace('\r\n', '\n').replace('\r', '\n')
|
||||
return result
|
||||
|
||||
|
||||
def _decode_data(data, encoding):
|
||||
if isinstance(data, unicode):
|
||||
return data
|
||||
if encoding is None:
|
||||
encoding = read_str_coding(data)
|
||||
if encoding is None:
|
||||
# there is no encoding tip, we need to guess.
|
||||
# PEP263 says that "encoding not explicitly defined" means it is ascii,
|
||||
# but we will use utf8 instead since utf8 fully covers ascii and btw is
|
||||
# the only non-latin sane encoding.
|
||||
encoding = 'utf-8'
|
||||
try:
|
||||
return data.decode(encoding)
|
||||
except (UnicodeError, LookupError):
|
||||
# fallback to latin1: it should never fail
|
||||
return data.decode('latin1')
|
||||
|
||||
|
||||
def read_file_coding(path):
|
||||
file = open(path, 'b')
|
||||
count = 0
|
||||
result = []
|
||||
while True:
|
||||
current = file.read(10)
|
||||
if not current:
|
||||
break
|
||||
count += current.count('\n')
|
||||
result.append(current)
|
||||
file.close()
|
||||
return _find_coding(''.join(result))
|
||||
|
||||
|
||||
def read_str_coding(source):
|
||||
if type(source) == bytes:
|
||||
newline = b'\n'
|
||||
else:
|
||||
newline = '\n'
|
||||
#try:
|
||||
# source = source.decode("utf-8")
|
||||
#except AttributeError:
|
||||
# pass
|
||||
try:
|
||||
first = source.index(newline) + 1
|
||||
second = source.index(newline, first) + 1
|
||||
except ValueError:
|
||||
second = len(source)
|
||||
return _find_coding(source[:second])
|
||||
|
||||
|
||||
def _find_coding(text):
|
||||
if isinstance(text, pycompat.str):
|
||||
text = text.encode('utf-8')
|
||||
coding = b'coding'
|
||||
to_chr = chr if pycompat.PY3 else lambda x: x
|
||||
try:
|
||||
start = text.index(coding) + len(coding)
|
||||
if text[start] not in b'=:':
|
||||
return
|
||||
start += 1
|
||||
while start < len(text) and to_chr(text[start]).isspace():
|
||||
start += 1
|
||||
end = start
|
||||
while end < len(text):
|
||||
c = text[end]
|
||||
if not to_chr(c).isalnum() and c not in b'-_':
|
||||
break
|
||||
end += 1
|
||||
result = text[start:end]
|
||||
if isinstance(result, bytes):
|
||||
result = result.decode('utf-8')
|
||||
return result
|
||||
except ValueError:
|
||||
pass
|
||||
|
|
@ -1,235 +0,0 @@
|
|||
from rope.base import exceptions, change, taskhandle
|
||||
|
||||
|
||||
class History(object):
|
||||
"""A class that holds project history"""
|
||||
|
||||
def __init__(self, project, maxundos=None):
|
||||
self.project = project
|
||||
self._undo_list = []
|
||||
self._redo_list = []
|
||||
self._maxundos = maxundos
|
||||
self._load_history()
|
||||
self.project.data_files.add_write_hook(self.write)
|
||||
self.current_change = None
|
||||
|
||||
def _load_history(self):
|
||||
if self.save:
|
||||
result = self.project.data_files.read_data(
|
||||
'history', compress=self.compress, import_=True)
|
||||
if result is not None:
|
||||
to_change = change.DataToChange(self.project)
|
||||
for data in result[0]:
|
||||
self._undo_list.append(to_change(data))
|
||||
for data in result[1]:
|
||||
self._redo_list.append(to_change(data))
|
||||
|
||||
def do(self, changes, task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Perform the change and add it to the `self.undo_list`
|
||||
|
||||
Note that uninteresting changes (changes to ignored files)
|
||||
will not be appended to `self.undo_list`.
|
||||
|
||||
"""
|
||||
try:
|
||||
self.current_change = changes
|
||||
changes.do(change.create_job_set(task_handle, changes))
|
||||
finally:
|
||||
self.current_change = None
|
||||
if self._is_change_interesting(changes):
|
||||
self.undo_list.append(changes)
|
||||
self._remove_extra_items()
|
||||
del self.redo_list[:]
|
||||
|
||||
def _remove_extra_items(self):
|
||||
if len(self.undo_list) > self.max_undos:
|
||||
del self.undo_list[0:len(self.undo_list) - self.max_undos]
|
||||
|
||||
def _is_change_interesting(self, changes):
|
||||
for resource in changes.get_changed_resources():
|
||||
if not self.project.is_ignored(resource):
|
||||
return True
|
||||
return False
|
||||
|
||||
def undo(self, change=None, drop=False,
|
||||
task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Redo done changes from the history
|
||||
|
||||
When `change` is `None`, the last done change will be undone.
|
||||
If change is not `None` it should be an item from
|
||||
`self.undo_list`; this change and all changes that depend on
|
||||
it will be undone. In both cases the list of undone changes
|
||||
will be returned.
|
||||
|
||||
If `drop` is `True`, the undone change will not be appended to
|
||||
the redo list.
|
||||
|
||||
"""
|
||||
if not self._undo_list:
|
||||
raise exceptions.HistoryError('Undo list is empty')
|
||||
if change is None:
|
||||
change = self.undo_list[-1]
|
||||
dependencies = self._find_dependencies(self.undo_list, change)
|
||||
self._move_front(self.undo_list, dependencies)
|
||||
self._perform_undos(len(dependencies), task_handle)
|
||||
result = self.redo_list[-len(dependencies):]
|
||||
if drop:
|
||||
del self.redo_list[-len(dependencies):]
|
||||
return result
|
||||
|
||||
def redo(self, change=None, task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Redo undone changes from the history
|
||||
|
||||
When `change` is `None`, the last undone change will be
|
||||
redone. If change is not `None` it should be an item from
|
||||
`self.redo_list`; this change and all changes that depend on
|
||||
it will be redone. In both cases the list of redone changes
|
||||
will be returned.
|
||||
|
||||
"""
|
||||
if not self.redo_list:
|
||||
raise exceptions.HistoryError('Redo list is empty')
|
||||
if change is None:
|
||||
change = self.redo_list[-1]
|
||||
dependencies = self._find_dependencies(self.redo_list, change)
|
||||
self._move_front(self.redo_list, dependencies)
|
||||
self._perform_redos(len(dependencies), task_handle)
|
||||
return self.undo_list[-len(dependencies):]
|
||||
|
||||
def _move_front(self, change_list, changes):
|
||||
for change in changes:
|
||||
change_list.remove(change)
|
||||
change_list.append(change)
|
||||
|
||||
def _find_dependencies(self, change_list, change):
|
||||
index = change_list.index(change)
|
||||
return _FindChangeDependencies(change_list[index:])()
|
||||
|
||||
def _perform_undos(self, count, task_handle):
|
||||
for i in range(count):
|
||||
self.current_change = self.undo_list[-1]
|
||||
try:
|
||||
job_set = change.create_job_set(task_handle,
|
||||
self.current_change)
|
||||
self.current_change.undo(job_set)
|
||||
finally:
|
||||
self.current_change = None
|
||||
self.redo_list.append(self.undo_list.pop())
|
||||
|
||||
def _perform_redos(self, count, task_handle):
|
||||
for i in range(count):
|
||||
self.current_change = self.redo_list[-1]
|
||||
try:
|
||||
job_set = change.create_job_set(task_handle,
|
||||
self.current_change)
|
||||
self.current_change.do(job_set)
|
||||
finally:
|
||||
self.current_change = None
|
||||
self.undo_list.append(self.redo_list.pop())
|
||||
|
||||
def contents_before_current_change(self, file):
|
||||
if self.current_change is None:
|
||||
return None
|
||||
result = self._search_for_change_contents([self.current_change], file)
|
||||
if result is not None:
|
||||
return result
|
||||
if file.exists() and not file.is_folder():
|
||||
return file.read()
|
||||
else:
|
||||
return None
|
||||
|
||||
def _search_for_change_contents(self, change_list, file):
|
||||
for change_ in reversed(change_list):
|
||||
if isinstance(change_, change.ChangeSet):
|
||||
result = self._search_for_change_contents(change_.changes,
|
||||
file)
|
||||
if result is not None:
|
||||
return result
|
||||
if isinstance(change_, change.ChangeContents) and \
|
||||
change_.resource == file:
|
||||
return change_.old_contents
|
||||
|
||||
def write(self):
|
||||
if self.save:
|
||||
data = []
|
||||
to_data = change.ChangeToData()
|
||||
self._remove_extra_items()
|
||||
data.append([to_data(change_) for change_ in self.undo_list])
|
||||
data.append([to_data(change_) for change_ in self.redo_list])
|
||||
self.project.data_files.write_data('history', data,
|
||||
compress=self.compress)
|
||||
|
||||
def get_file_undo_list(self, resource):
|
||||
result = []
|
||||
for change in self.undo_list:
|
||||
if resource in change.get_changed_resources():
|
||||
result.append(change)
|
||||
return result
|
||||
|
||||
def __str__(self):
|
||||
return 'History holds %s changes in memory' % \
|
||||
(len(self.undo_list) + len(self.redo_list))
|
||||
|
||||
undo_list = property(lambda self: self._undo_list)
|
||||
redo_list = property(lambda self: self._redo_list)
|
||||
|
||||
@property
|
||||
def tobe_undone(self):
|
||||
"""The last done change if available, `None` otherwise"""
|
||||
if self.undo_list:
|
||||
return self.undo_list[-1]
|
||||
|
||||
@property
|
||||
def tobe_redone(self):
|
||||
"""The last undone change if available, `None` otherwise"""
|
||||
if self.redo_list:
|
||||
return self.redo_list[-1]
|
||||
|
||||
@property
|
||||
def max_undos(self):
|
||||
if self._maxundos is None:
|
||||
return self.project.prefs.get('max_history_items', 100)
|
||||
else:
|
||||
return self._maxundos
|
||||
|
||||
@property
|
||||
def save(self):
|
||||
return self.project.prefs.get('save_history', False)
|
||||
|
||||
@property
|
||||
def compress(self):
|
||||
return self.project.prefs.get('compress_history', False)
|
||||
|
||||
def clear(self):
|
||||
"""Forget all undo and redo information"""
|
||||
del self.undo_list[:]
|
||||
del self.redo_list[:]
|
||||
|
||||
|
||||
class _FindChangeDependencies(object):
|
||||
|
||||
def __init__(self, change_list):
|
||||
self.change = change_list[0]
|
||||
self.change_list = change_list
|
||||
self.changed_resources = set(self.change.get_changed_resources())
|
||||
|
||||
def __call__(self):
|
||||
result = [self.change]
|
||||
for change in self.change_list[1:]:
|
||||
if self._depends_on(change, result):
|
||||
result.append(change)
|
||||
self.changed_resources.update(change.get_changed_resources())
|
||||
return result
|
||||
|
||||
def _depends_on(self, changes, result):
|
||||
for resource in changes.get_changed_resources():
|
||||
if resource is None:
|
||||
continue
|
||||
if resource in self.changed_resources:
|
||||
return True
|
||||
for changed in self.changed_resources:
|
||||
if resource.is_folder() and resource.contains(changed):
|
||||
return True
|
||||
if changed.is_folder() and changed.contains(resource):
|
||||
return True
|
||||
return False
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
"""A few useful functions for using rope as a library"""
|
||||
import os.path
|
||||
|
||||
import rope.base.project
|
||||
import rope.base.pycore
|
||||
from rope.base import pyobjectsdef
|
||||
from rope.base import utils
|
||||
from rope.base import taskhandle
|
||||
|
||||
|
||||
def path_to_resource(project, path, type=None):
|
||||
"""Get the resource at path
|
||||
|
||||
You only need to specify `type` if `path` does not exist. It can
|
||||
be either 'file' or 'folder'. If the type is `None` it is assumed
|
||||
that the resource already exists.
|
||||
|
||||
Note that this function uses `Project.get_resource()`,
|
||||
`Project.get_file()`, and `Project.get_folder()` methods.
|
||||
|
||||
"""
|
||||
project_path = path_relative_to_project_root(project, path)
|
||||
if project_path is None:
|
||||
project_path = rope.base.project._realpath(path)
|
||||
project = rope.base.project.get_no_project()
|
||||
if type is None:
|
||||
return project.get_resource(project_path)
|
||||
if type == 'file':
|
||||
return project.get_file(project_path)
|
||||
if type == 'folder':
|
||||
return project.get_folder(project_path)
|
||||
return None
|
||||
|
||||
|
||||
def path_relative_to_project_root(project, path):
|
||||
return relative(project.address, path)
|
||||
|
||||
@utils.deprecated()
|
||||
def relative(root, path):
|
||||
root = rope.base.project._realpath(root).replace(os.path.sep, '/')
|
||||
path = rope.base.project._realpath(path).replace(os.path.sep, '/')
|
||||
if path == root:
|
||||
return ''
|
||||
if path.startswith(root + '/'):
|
||||
return path[len(root) + 1:]
|
||||
|
||||
|
||||
def report_change(project, path, old_content):
|
||||
"""Report that the contents of file at `path` was changed
|
||||
|
||||
The new contents of file is retrieved by reading the file.
|
||||
|
||||
"""
|
||||
resource = path_to_resource(project, path)
|
||||
if resource is None:
|
||||
return
|
||||
for observer in list(project.observers):
|
||||
observer.resource_changed(resource)
|
||||
if project.pycore.automatic_soa:
|
||||
rope.base.pycore.perform_soa_on_changed_scopes(project, resource,
|
||||
old_content)
|
||||
|
||||
|
||||
def analyze_module(project, resource):
|
||||
"""Perform static object analysis on a python file in the project
|
||||
|
||||
Note that this might be really time consuming.
|
||||
"""
|
||||
project.pycore.analyze_module(resource)
|
||||
|
||||
|
||||
def analyze_modules(project, task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Perform static object analysis on all python files in the project
|
||||
|
||||
Note that this might be really time consuming.
|
||||
"""
|
||||
resources = project.get_python_files()
|
||||
job_set = task_handle.create_jobset('Analyzing Modules', len(resources))
|
||||
for resource in resources:
|
||||
job_set.started_job(resource.path)
|
||||
analyze_module(project, resource)
|
||||
job_set.finished_job()
|
||||
|
||||
|
||||
def get_string_module(project, code, resource=None, force_errors=False):
|
||||
"""Returns a `PyObject` object for the given code
|
||||
|
||||
If `force_errors` is `True`, `exceptions.ModuleSyntaxError` is
|
||||
raised if module has syntax errors. This overrides
|
||||
``ignore_syntax_errors`` project config.
|
||||
|
||||
"""
|
||||
return pyobjectsdef.PyModule(project.pycore, code, resource,
|
||||
force_errors=force_errors)
|
||||
|
||||
|
||||
def get_string_scope(project, code, resource=None):
|
||||
"""Returns a `Scope` object for the given code"""
|
||||
return get_string_module(project, code, resource).get_scope()
|
||||
|
||||
|
||||
def is_python_file(project, resource):
|
||||
return project.pycore.is_python_file(resource)
|
||||
|
||||
|
||||
def modname(resource):
|
||||
if resource.is_folder():
|
||||
module_name = resource.name
|
||||
source_folder = resource.parent
|
||||
elif resource.name == '__init__.py':
|
||||
module_name = resource.parent.name
|
||||
source_folder = resource.parent.parent
|
||||
else:
|
||||
module_name = resource.name[:-3]
|
||||
source_folder = resource.parent
|
||||
|
||||
while source_folder != source_folder.parent and \
|
||||
source_folder.has_child('__init__.py'):
|
||||
module_name = source_folder.name + '.' + module_name
|
||||
source_folder = source_folder.parent
|
||||
|
||||
return module_name
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
"""Rope object analysis and inference package
|
||||
|
||||
Rope makes some simplifying assumptions about a python program. It
|
||||
assumes that a program only performs assignments and function calls.
|
||||
Tracking assignments is simple and `PyName` objects handle that. The
|
||||
main problem is function calls. Rope uses these two approaches for
|
||||
obtaining call information:
|
||||
|
||||
* Static object analysis: `rope.base.pycore.PyCore.analyze_module()`
|
||||
|
||||
It can analyze modules to obtain information about functions. This
|
||||
is done by analyzing function calls in a module or scope. Currently
|
||||
SOA analyzes the scopes that are changed while saving or when the
|
||||
user asks to analyze a module. That is mainly because static
|
||||
analysis is time-consuming.
|
||||
|
||||
* Dynamic object analysis: `rope.base.pycore.PyCore.run_module()`
|
||||
|
||||
When you run a module or your testsuite, when DOA is enabled, it
|
||||
collects information about parameters passed to and objects returned
|
||||
from functions. The main problem with this approach is that it is
|
||||
quite slow; Not when looking up the information but when collecting
|
||||
them.
|
||||
|
||||
An instance of `rope.base.oi.objectinfo.ObjectInfoManager` can be used
|
||||
for accessing these information. It saves the data in a
|
||||
`rope.base.oi.objectdb.ObjectDB` internally.
|
||||
|
||||
Now if our objectdb does not know anything about a function and we
|
||||
need the value returned by it, static object inference, SOI, comes
|
||||
into play. It analyzes function body and tries to infer the object
|
||||
that is returned from it (we usually need the returned value for the
|
||||
given parameter objects).
|
||||
|
||||
Rope might collect and store information for other `PyName`\s, too.
|
||||
For instance rope stores the object builtin containers hold.
|
||||
|
||||
"""
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,166 +0,0 @@
|
|||
try:
|
||||
import pickle
|
||||
except ImportError:
|
||||
import cPickle as pickle
|
||||
import marshal
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
|
||||
|
||||
class PythonFileRunner(object):
|
||||
"""A class for running python project files"""
|
||||
|
||||
def __init__(self, pycore, file_, args=None, stdin=None,
|
||||
stdout=None, analyze_data=None):
|
||||
self.pycore = pycore
|
||||
self.file = file_
|
||||
self.analyze_data = analyze_data
|
||||
self.observers = []
|
||||
self.args = args
|
||||
self.stdin = stdin
|
||||
self.stdout = stdout
|
||||
|
||||
def run(self):
|
||||
"""Execute the process"""
|
||||
env = dict(os.environ)
|
||||
file_path = self.file.real_path
|
||||
path_folders = self.pycore.project.get_source_folders() + \
|
||||
self.pycore.project.get_python_path_folders()
|
||||
env['PYTHONPATH'] = os.pathsep.join(folder.real_path
|
||||
for folder in path_folders)
|
||||
runmod_path = self.pycore.project.find_module('rope.base.oi.runmod').real_path
|
||||
self.receiver = None
|
||||
self._init_data_receiving()
|
||||
send_info = '-'
|
||||
if self.receiver:
|
||||
send_info = self.receiver.get_send_info()
|
||||
args = [sys.executable, runmod_path, send_info,
|
||||
self.pycore.project.address, self.file.real_path]
|
||||
if self.analyze_data is None:
|
||||
del args[1:4]
|
||||
if self.args is not None:
|
||||
args.extend(self.args)
|
||||
self.process = subprocess.Popen(
|
||||
executable=sys.executable, args=args, env=env,
|
||||
cwd=os.path.split(file_path)[0], stdin=self.stdin,
|
||||
stdout=self.stdout, stderr=self.stdout, close_fds=os.name != 'nt')
|
||||
|
||||
def _init_data_receiving(self):
|
||||
if self.analyze_data is None:
|
||||
return
|
||||
# Disabling FIFO data transfer due to blocking when running
|
||||
# unittests in the GUI.
|
||||
# XXX: Handle FIFO data transfer for `rope.ui.testview`
|
||||
if True or os.name == 'nt':
|
||||
self.receiver = _SocketReceiver()
|
||||
else:
|
||||
self.receiver = _FIFOReceiver()
|
||||
self.receiving_thread = threading.Thread(
|
||||
target=self._receive_information)
|
||||
self.receiving_thread.setDaemon(True)
|
||||
self.receiving_thread.start()
|
||||
|
||||
def _receive_information(self):
|
||||
#temp = open('/dev/shm/info', 'wb')
|
||||
for data in self.receiver.receive_data():
|
||||
self.analyze_data(data)
|
||||
#temp.write(str(data) + '\n')
|
||||
#temp.close()
|
||||
for observer in self.observers:
|
||||
observer()
|
||||
|
||||
def wait_process(self):
|
||||
"""Wait for the process to finish"""
|
||||
self.process.wait()
|
||||
if self.analyze_data:
|
||||
self.receiving_thread.join()
|
||||
|
||||
def kill_process(self):
|
||||
"""Stop the process"""
|
||||
if self.process.poll() is not None:
|
||||
return
|
||||
try:
|
||||
if hasattr(self.process, 'terminate'):
|
||||
self.process.terminate()
|
||||
elif os.name != 'nt':
|
||||
os.kill(self.process.pid, 9)
|
||||
else:
|
||||
import ctypes
|
||||
handle = int(self.process._handle)
|
||||
ctypes.windll.kernel32.TerminateProcess(handle, -1)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def add_finishing_observer(self, observer):
|
||||
"""Notify this observer when execution finishes"""
|
||||
self.observers.append(observer)
|
||||
|
||||
|
||||
class _MessageReceiver(object):
|
||||
|
||||
def receive_data(self):
|
||||
pass
|
||||
|
||||
def get_send_info(self):
|
||||
pass
|
||||
|
||||
|
||||
class _SocketReceiver(_MessageReceiver):
|
||||
|
||||
def __init__(self):
|
||||
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.data_port = 3037
|
||||
while self.data_port < 4000:
|
||||
try:
|
||||
self.server_socket.bind(('', self.data_port))
|
||||
break
|
||||
except socket.error:
|
||||
self.data_port += 1
|
||||
self.server_socket.listen(1)
|
||||
|
||||
def get_send_info(self):
|
||||
return str(self.data_port)
|
||||
|
||||
def receive_data(self):
|
||||
conn, addr = self.server_socket.accept()
|
||||
self.server_socket.close()
|
||||
my_file = conn.makefile('rb')
|
||||
while True:
|
||||
try:
|
||||
yield pickle.load(my_file)
|
||||
except EOFError:
|
||||
break
|
||||
my_file.close()
|
||||
conn.close()
|
||||
|
||||
|
||||
class _FIFOReceiver(_MessageReceiver):
|
||||
|
||||
def __init__(self):
|
||||
# XXX: this is insecure and might cause race conditions
|
||||
self.file_name = self._get_file_name()
|
||||
os.mkfifo(self.file_name)
|
||||
|
||||
def _get_file_name(self):
|
||||
prefix = tempfile.gettempdir() + '/__rope_'
|
||||
i = 0
|
||||
while os.path.exists(prefix + str(i).rjust(4, '0')):
|
||||
i += 1
|
||||
return prefix + str(i).rjust(4, '0')
|
||||
|
||||
def get_send_info(self):
|
||||
return self.file_name
|
||||
|
||||
def receive_data(self):
|
||||
my_file = open(self.file_name, 'rb')
|
||||
while True:
|
||||
try:
|
||||
yield marshal.load(my_file)
|
||||
except EOFError:
|
||||
break
|
||||
my_file.close()
|
||||
os.remove(self.file_name)
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
"""
|
||||
Hinting the type using docstring of class/function.
|
||||
|
||||
It's an irreplaceable thing if you are using Dependency Injection with passive class:
|
||||
http://www.martinfowler.com/articles/injection.html
|
||||
|
||||
Some code extracted (or based on code) from:
|
||||
https://github.com/davidhalter/jedi/blob/b489019f5bd5750051122b94cc767df47751ecb7/jedi/evaluate/docstrings.py
|
||||
Thanks to @davidhalter for this utils under MIT License.
|
||||
|
||||
Similar solutions:
|
||||
|
||||
- https://www.jetbrains.com/pycharm/help/type-hinting-in-pycharm.html
|
||||
- https://www.python.org/dev/peps/pep-0484/#type-comments
|
||||
- http://www.pydev.org/manual_adv_type_hints.html
|
||||
- https://jedi.readthedocs.org/en/latest/docs/features.html#type-hinting
|
||||
|
||||
Discussions:
|
||||
|
||||
- https://groups.google.com/d/topic/rope-dev/JlAzmZ83K1M/discussion
|
||||
- https://groups.google.com/d/topic/rope-dev/LCFNN98vckI/discussion
|
||||
|
||||
"""
|
||||
import re
|
||||
from ast import literal_eval
|
||||
|
||||
from rope.base.exceptions import AttributeNotFoundError
|
||||
from rope.base.evaluate import ScopeNameFinder
|
||||
from rope.base.pyobjects import PyClass, PyFunction
|
||||
|
||||
PEP0484_PATTERNS = [
|
||||
re.compile(r'type:\s*([^\n, ]+)'),
|
||||
]
|
||||
|
||||
DOCSTRING_PARAM_PATTERNS = [
|
||||
r'\s*:type\s+%s:\s*([^\n, ]+)', # Sphinx
|
||||
r'\s*:param\s+(\w+)\s+%s:[^\n]+', # Sphinx param with type
|
||||
r'\s*@type\s+%s:\s*([^\n, ]+)', # Epydoc
|
||||
]
|
||||
|
||||
DOCSTRING_RETURN_PATTERNS = [
|
||||
re.compile(r'\s*:rtype:\s*([^\n, ]+)', re.M), # Sphinx
|
||||
re.compile(r'\s*@rtype:\s*([^\n, ]+)', re.M), # Epydoc
|
||||
]
|
||||
|
||||
REST_ROLE_PATTERN = re.compile(r':[^`]+:`([^`]+)`')
|
||||
|
||||
try:
|
||||
from numpydoc.docscrape import NumpyDocString
|
||||
except ImportError:
|
||||
def _search_param_in_numpydocstr(docstr, param_str):
|
||||
return []
|
||||
else:
|
||||
def _search_param_in_numpydocstr(docstr, param_str):
|
||||
"""Search `docstr` (in numpydoc format) for type(-s) of `param_str`."""
|
||||
params = NumpyDocString(docstr)._parsed_data['Parameters']
|
||||
for p_name, p_type, p_descr in params:
|
||||
if p_name == param_str:
|
||||
m = re.match('([^,]+(,[^,]+)*?)(,[ ]*optional)?$', p_type)
|
||||
if m:
|
||||
p_type = m.group(1)
|
||||
|
||||
if p_type.startswith('{'):
|
||||
types = set(type(x).__name__ for x in literal_eval(p_type))
|
||||
return list(types)
|
||||
else:
|
||||
return [p_type]
|
||||
return []
|
||||
|
||||
|
||||
def hint_pep0484(pyname):
|
||||
from rope.base.oi.soi import _get_lineno_for_node
|
||||
lineno = _get_lineno_for_node(pyname.assignments[0].ast_node)
|
||||
holding_scope = pyname.module.get_scope().get_inner_scope_for_line(lineno)
|
||||
line = holding_scope._get_global_scope()._scope_finder.lines.get_line(lineno)
|
||||
if '#' in line:
|
||||
type_strs = _search_type_in_pep0484(line.split('#', 1)[1])
|
||||
if type_strs:
|
||||
return _resolve_type(type_strs[0], holding_scope.pyobject)
|
||||
|
||||
|
||||
def _search_type_in_pep0484(code):
|
||||
""" For more info see:
|
||||
https://www.python.org/dev/peps/pep-0484/#type-comments
|
||||
|
||||
>>> _search_type_in_pep0484('type: int')
|
||||
['int']
|
||||
"""
|
||||
for p in PEP0484_PATTERNS:
|
||||
match = p.search(code)
|
||||
if match:
|
||||
return [match.group(1)]
|
||||
|
||||
|
||||
def hint_param(pyfunc, param_name):
|
||||
type_strs = None
|
||||
func = pyfunc
|
||||
while not type_strs and func:
|
||||
if func.get_doc():
|
||||
type_strs = _search_param_in_docstr(func.get_doc(), param_name)
|
||||
func = _get_superfunc(func)
|
||||
|
||||
if type_strs:
|
||||
return _resolve_type(type_strs[0], pyfunc)
|
||||
|
||||
|
||||
def _get_superfunc(pyfunc):
|
||||
|
||||
if not isinstance(pyfunc.parent, PyClass):
|
||||
return
|
||||
|
||||
for cls in _get_mro(pyfunc.parent)[1:]:
|
||||
try:
|
||||
superfunc = cls.get_attribute(pyfunc.get_name()).get_object()
|
||||
except AttributeNotFoundError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(superfunc, PyFunction):
|
||||
return superfunc
|
||||
|
||||
|
||||
def _get_mro(pyclass):
|
||||
# FIXME: to use real mro() result
|
||||
l = [pyclass]
|
||||
for cls in l:
|
||||
for super_cls in cls.get_superclasses():
|
||||
if isinstance(super_cls, PyClass) and super_cls not in l:
|
||||
l.append(super_cls)
|
||||
return l
|
||||
|
||||
|
||||
def _resolve_type(type_name, pyobj):
|
||||
type_ = None
|
||||
if '.' not in type_name:
|
||||
try:
|
||||
type_ = pyobj.get_module().get_scope().get_name(type_name).get_object()
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
mod_name, attr_name = type_name.rsplit('.', 1)
|
||||
try:
|
||||
mod_finder = ScopeNameFinder(pyobj.get_module())
|
||||
mod = mod_finder._find_module(mod_name).get_object()
|
||||
type_ = mod.get_attribute(attr_name).get_object()
|
||||
except Exception:
|
||||
pass
|
||||
return type_
|
||||
|
||||
|
||||
def _search_param_in_docstr(docstr, param_str):
|
||||
"""
|
||||
Search `docstr` for type(-s) of `param_str`.
|
||||
|
||||
>>> _search_param_in_docstr(':type param: int', 'param')
|
||||
['int']
|
||||
>>> _search_param_in_docstr('@type param: int', 'param')
|
||||
['int']
|
||||
>>> _search_param_in_docstr(
|
||||
... ':type param: :class:`threading.Thread`', 'param')
|
||||
['threading.Thread']
|
||||
>>> bool(_search_param_in_docstr('no document', 'param'))
|
||||
False
|
||||
>>> _search_param_in_docstr(':param int param: some description', 'param')
|
||||
['int']
|
||||
|
||||
"""
|
||||
patterns = [re.compile(p % re.escape(param_str))
|
||||
for p in DOCSTRING_PARAM_PATTERNS]
|
||||
for pattern in patterns:
|
||||
match = pattern.search(docstr)
|
||||
if match:
|
||||
return [_strip_rst_role(match.group(1))]
|
||||
|
||||
return (_search_param_in_numpydocstr(docstr, param_str) or
|
||||
[])
|
||||
|
||||
|
||||
def _strip_rst_role(type_str):
|
||||
"""
|
||||
Strip off the part looks like a ReST role in `type_str`.
|
||||
|
||||
>>> _strip_rst_role(':class:`ClassName`') # strip off :class:
|
||||
'ClassName'
|
||||
>>> _strip_rst_role(':py:obj:`module.Object`') # works with domain
|
||||
'module.Object'
|
||||
>>> _strip_rst_role('ClassName') # do nothing when not ReST role
|
||||
'ClassName'
|
||||
|
||||
See also:
|
||||
http://sphinx-doc.org/domains.html#cross-referencing-python-objects
|
||||
|
||||
"""
|
||||
match = REST_ROLE_PATTERN.match(type_str)
|
||||
if match:
|
||||
return match.group(1)
|
||||
else:
|
||||
return type_str
|
||||
|
||||
|
||||
def hint_return(pyfunc):
|
||||
type_str = None
|
||||
func = pyfunc
|
||||
while not type_str and func:
|
||||
if func.get_doc():
|
||||
type_str = _search_return_in_docstr(func.get_doc())
|
||||
func = _get_superfunc(func)
|
||||
if type_str:
|
||||
return _resolve_type(type_str, pyfunc)
|
||||
|
||||
|
||||
def _search_return_in_docstr(code):
|
||||
for p in DOCSTRING_RETURN_PATTERNS:
|
||||
match = p.search(code)
|
||||
if match:
|
||||
return _strip_rst_role(match.group(1))
|
||||
|
||||
|
||||
def hint_attr(pyclass, attr_name):
|
||||
type_strs = None
|
||||
for cls in _get_mro(pyclass):
|
||||
if cls.get_doc():
|
||||
type_strs = _search_param_in_docstr(cls.get_doc(), attr_name)
|
||||
if type_strs:
|
||||
break
|
||||
if type_strs:
|
||||
return _resolve_type(type_strs[0], pyclass)
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
from rope.base.oi import objectdb
|
||||
|
||||
|
||||
class MemoryDB(objectdb.FileDict):
|
||||
|
||||
def __init__(self, project, persist=None):
|
||||
self.project = project
|
||||
self._persist = persist
|
||||
self.files = self
|
||||
self._load_files()
|
||||
self.project.data_files.add_write_hook(self.write)
|
||||
|
||||
def _load_files(self):
|
||||
self._files = {}
|
||||
if self.persist:
|
||||
result = self.project.data_files.read_data(
|
||||
'objectdb', compress=self.compress, import_=True)
|
||||
if result is not None:
|
||||
self._files = result
|
||||
|
||||
def keys(self):
|
||||
return self._files.keys()
|
||||
|
||||
def __iter__(self):
|
||||
for f in self._files:
|
||||
yield f
|
||||
|
||||
def __len__(self):
|
||||
return len(self._files)
|
||||
|
||||
def __setitem__(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._files
|
||||
|
||||
def __getitem__(self, key):
|
||||
return FileInfo(self._files[key])
|
||||
|
||||
def create(self, path):
|
||||
self._files[path] = {}
|
||||
|
||||
def rename(self, file, newfile):
|
||||
if file not in self._files:
|
||||
return
|
||||
self._files[newfile] = self._files[file]
|
||||
del self[file]
|
||||
|
||||
def __delitem__(self, file):
|
||||
del self._files[file]
|
||||
|
||||
def write(self):
|
||||
if self.persist:
|
||||
self.project.data_files.write_data('objectdb', self._files,
|
||||
self.compress)
|
||||
|
||||
@property
|
||||
def compress(self):
|
||||
return self.project.prefs.get('compress_objectdb', False)
|
||||
|
||||
@property
|
||||
def persist(self):
|
||||
if self._persist is not None:
|
||||
return self._persist
|
||||
else:
|
||||
return self.project.prefs.get('save_objectdb', False)
|
||||
|
||||
|
||||
class FileInfo(objectdb.FileInfo):
|
||||
|
||||
def __init__(self, scopes):
|
||||
self.scopes = scopes
|
||||
|
||||
def create_scope(self, key):
|
||||
self.scopes[key] = ScopeInfo()
|
||||
|
||||
def keys(self):
|
||||
return self.scopes.keys()
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.scopes
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.scopes[key]
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self.scopes[key]
|
||||
|
||||
def __iter__(self):
|
||||
for s in self.scopes:
|
||||
yield s
|
||||
|
||||
def __len__(self):
|
||||
return len(self.scopes)
|
||||
|
||||
def __setitem__(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
|
||||
class ScopeInfo(objectdb.ScopeInfo):
|
||||
|
||||
def __init__(self):
|
||||
self.call_info = {}
|
||||
self.per_name = {}
|
||||
|
||||
def get_per_name(self, name):
|
||||
return self.per_name.get(name, None)
|
||||
|
||||
def save_per_name(self, name, value):
|
||||
self.per_name[name] = value
|
||||
|
||||
def get_returned(self, parameters):
|
||||
return self.call_info.get(parameters, None)
|
||||
|
||||
def get_call_infos(self):
|
||||
for args, returned in self.call_info.items():
|
||||
yield objectdb.CallInfo(args, returned)
|
||||
|
||||
def add_call(self, parameters, returned):
|
||||
self.call_info[parameters] = returned
|
||||
|
||||
def __getstate__(self):
|
||||
return (self.call_info, self.per_name)
|
||||
|
||||
def __setstate__(self, data):
|
||||
self.call_info, self.per_name = data
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
from __future__ import print_function
|
||||
try:
|
||||
from collections import MutableMapping
|
||||
except ImportError:
|
||||
from UserDict import DictMixin as MutableMapping
|
||||
|
||||
|
||||
class ObjectDB(object):
|
||||
|
||||
def __init__(self, db, validation):
|
||||
self.db = db
|
||||
self.validation = validation
|
||||
self.observers = []
|
||||
self.files = db.files
|
||||
|
||||
def validate_files(self):
|
||||
for file in list(self.files):
|
||||
if not self.validation.is_file_valid(file):
|
||||
del self.files[file]
|
||||
self._file_removed(file)
|
||||
|
||||
def validate_file(self, file):
|
||||
if file not in self.files:
|
||||
return
|
||||
for key in list(self.files[file]):
|
||||
if not self.validation.is_scope_valid(file, key):
|
||||
del self.files[file][key]
|
||||
|
||||
def file_moved(self, file, newfile):
|
||||
if file not in self.files:
|
||||
return
|
||||
self.files.rename(file, newfile)
|
||||
self._file_removed(file)
|
||||
self._file_added(newfile)
|
||||
|
||||
def get_files(self):
|
||||
return self.files.keys()
|
||||
|
||||
def get_returned(self, path, key, args):
|
||||
scope_info = self._get_scope_info(path, key, readonly=True)
|
||||
result = scope_info.get_returned(args)
|
||||
if self.validation.is_value_valid(result):
|
||||
return result
|
||||
|
||||
def get_pername(self, path, key, name):
|
||||
scope_info = self._get_scope_info(path, key, readonly=True)
|
||||
result = scope_info.get_per_name(name)
|
||||
if self.validation.is_value_valid(result):
|
||||
return result
|
||||
|
||||
def get_callinfos(self, path, key):
|
||||
scope_info = self._get_scope_info(path, key, readonly=True)
|
||||
return scope_info.get_call_infos()
|
||||
|
||||
def add_callinfo(self, path, key, args, returned):
|
||||
scope_info = self._get_scope_info(path, key, readonly=False)
|
||||
old_returned = scope_info.get_returned(args)
|
||||
if self.validation.is_more_valid(returned, old_returned):
|
||||
scope_info.add_call(args, returned)
|
||||
|
||||
def add_pername(self, path, key, name, value):
|
||||
scope_info = self._get_scope_info(path, key, readonly=False)
|
||||
old_value = scope_info.get_per_name(name)
|
||||
if self.validation.is_more_valid(value, old_value):
|
||||
scope_info.save_per_name(name, value)
|
||||
|
||||
def add_file_list_observer(self, observer):
|
||||
self.observers.append(observer)
|
||||
|
||||
def write(self):
|
||||
self.db.write()
|
||||
|
||||
def _get_scope_info(self, path, key, readonly=True):
|
||||
if path not in self.files:
|
||||
if readonly:
|
||||
return _NullScopeInfo()
|
||||
self.files.create(path)
|
||||
self._file_added(path)
|
||||
if key not in self.files[path]:
|
||||
if readonly:
|
||||
return _NullScopeInfo()
|
||||
self.files[path].create_scope(key)
|
||||
result = self.files[path][key]
|
||||
if isinstance(result, dict):
|
||||
print(self.files, self.files[path], self.files[path][key])
|
||||
return result
|
||||
|
||||
def _file_removed(self, path):
|
||||
for observer in self.observers:
|
||||
observer.removed(path)
|
||||
|
||||
def _file_added(self, path):
|
||||
for observer in self.observers:
|
||||
observer.added(path)
|
||||
|
||||
def __str__(self):
|
||||
scope_count = 0
|
||||
for file_dict in self.files.values():
|
||||
scope_count += len(file_dict)
|
||||
return 'ObjectDB holds %s file and %s scope infos' % \
|
||||
(len(self.files), scope_count)
|
||||
|
||||
|
||||
class _NullScopeInfo(object):
|
||||
|
||||
def __init__(self, error_on_write=True):
|
||||
self.error_on_write = error_on_write
|
||||
|
||||
def get_per_name(self, name):
|
||||
pass
|
||||
|
||||
def save_per_name(self, name, value):
|
||||
if self.error_on_write:
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_returned(self, parameters):
|
||||
pass
|
||||
|
||||
def get_call_infos(self):
|
||||
return []
|
||||
|
||||
def add_call(self, parameters, returned):
|
||||
if self.error_on_write:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class FileInfo(MutableMapping):
|
||||
|
||||
def create_scope(self, key):
|
||||
pass
|
||||
|
||||
|
||||
class FileDict(MutableMapping):
|
||||
|
||||
def create(self, key):
|
||||
pass
|
||||
|
||||
def rename(self, key, new_key):
|
||||
pass
|
||||
|
||||
|
||||
class ScopeInfo(object):
|
||||
|
||||
def get_per_name(self, name):
|
||||
pass
|
||||
|
||||
def save_per_name(self, name, value):
|
||||
pass
|
||||
|
||||
def get_returned(self, parameters):
|
||||
pass
|
||||
|
||||
def get_call_infos(self):
|
||||
pass
|
||||
|
||||
def add_call(self, parameters, returned):
|
||||
pass
|
||||
|
||||
|
||||
class CallInfo(object):
|
||||
|
||||
def __init__(self, args, returned):
|
||||
self.args = args
|
||||
self.returned = returned
|
||||
|
||||
def get_parameters(self):
|
||||
return self.args
|
||||
|
||||
def get_returned(self):
|
||||
return self.returned
|
||||
|
||||
|
||||
class FileListObserver(object):
|
||||
|
||||
def added(self, path):
|
||||
pass
|
||||
|
||||
def removed(self, path):
|
||||
pass
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
import warnings
|
||||
|
||||
from rope.base import exceptions, resourceobserver
|
||||
from rope.base.oi import objectdb, memorydb, transform
|
||||
|
||||
|
||||
class ObjectInfoManager(object):
|
||||
"""Stores object information
|
||||
|
||||
It uses an instance of `objectdb.ObjectDB` for storing
|
||||
information.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
self.to_textual = transform.PyObjectToTextual(project)
|
||||
self.to_pyobject = transform.TextualToPyObject(project)
|
||||
self.doi_to_pyobject = transform.DOITextualToPyObject(project)
|
||||
self._init_objectdb()
|
||||
if project.prefs.get('validate_objectdb', False):
|
||||
self._init_validation()
|
||||
|
||||
def _init_objectdb(self):
|
||||
dbtype = self.project.get_prefs().get('objectdb_type', None)
|
||||
persist = None
|
||||
if dbtype is not None:
|
||||
warnings.warn(
|
||||
'"objectdb_type" project config is deprecated;\n'
|
||||
'Use "save_objectdb" instead in your project '
|
||||
'config file.\n(".ropeproject/config.py" by default)\n',
|
||||
DeprecationWarning)
|
||||
if dbtype != 'memory' and self.project.ropefolder is not None:
|
||||
persist = True
|
||||
self.validation = TextualValidation(self.to_pyobject)
|
||||
db = memorydb.MemoryDB(self.project, persist=persist)
|
||||
self.objectdb = objectdb.ObjectDB(db, self.validation)
|
||||
|
||||
def _init_validation(self):
|
||||
self.objectdb.validate_files()
|
||||
observer = resourceobserver.ResourceObserver(
|
||||
changed=self._resource_changed, moved=self._resource_moved,
|
||||
removed=self._resource_moved)
|
||||
files = []
|
||||
for path in self.objectdb.get_files():
|
||||
resource = self.to_pyobject.path_to_resource(path)
|
||||
if resource is not None and resource.project == self.project:
|
||||
files.append(resource)
|
||||
self.observer = resourceobserver.FilteredResourceObserver(observer,
|
||||
files)
|
||||
self.objectdb.add_file_list_observer(_FileListObserver(self))
|
||||
self.project.add_observer(self.observer)
|
||||
|
||||
def _resource_changed(self, resource):
|
||||
try:
|
||||
self.objectdb.validate_file(
|
||||
self.to_textual.resource_to_path(resource))
|
||||
except exceptions.ModuleSyntaxError:
|
||||
pass
|
||||
|
||||
def _resource_moved(self, resource, new_resource=None):
|
||||
self.observer.remove_resource(resource)
|
||||
if new_resource is not None:
|
||||
old = self.to_textual.resource_to_path(resource)
|
||||
new = self.to_textual.resource_to_path(new_resource)
|
||||
self.objectdb.file_moved(old, new)
|
||||
self.observer.add_resource(new_resource)
|
||||
|
||||
def get_returned(self, pyobject, args):
|
||||
result = self.get_exact_returned(pyobject, args)
|
||||
if result is not None:
|
||||
return result
|
||||
path, key = self._get_scope(pyobject)
|
||||
if path is None:
|
||||
return None
|
||||
for call_info in self.objectdb.get_callinfos(path, key):
|
||||
returned = call_info.get_returned()
|
||||
if returned and returned[0] not in ('unknown', 'none'):
|
||||
result = returned
|
||||
break
|
||||
if result is None:
|
||||
result = returned
|
||||
if result is not None:
|
||||
return self.to_pyobject(result)
|
||||
|
||||
def get_exact_returned(self, pyobject, args):
|
||||
path, key = self._get_scope(pyobject)
|
||||
if path is not None:
|
||||
returned = self.objectdb.get_returned(
|
||||
path, key, self._args_to_textual(pyobject, args))
|
||||
if returned is not None:
|
||||
return self.to_pyobject(returned)
|
||||
|
||||
def _args_to_textual(self, pyfunction, args):
|
||||
parameters = list(pyfunction.get_param_names(special_args=False))
|
||||
arguments = args.get_arguments(parameters)[:len(parameters)]
|
||||
textual_args = tuple([self.to_textual(arg)
|
||||
for arg in arguments])
|
||||
return textual_args
|
||||
|
||||
def get_parameter_objects(self, pyobject):
|
||||
path, key = self._get_scope(pyobject)
|
||||
if path is None:
|
||||
return None
|
||||
arg_count = len(pyobject.get_param_names(special_args=False))
|
||||
unknowns = arg_count
|
||||
parameters = [None] * arg_count
|
||||
for call_info in self.objectdb.get_callinfos(path, key):
|
||||
args = call_info.get_parameters()
|
||||
for index, arg in enumerate(args[:arg_count]):
|
||||
old = parameters[index]
|
||||
if self.validation.is_more_valid(arg, old):
|
||||
parameters[index] = arg
|
||||
if self.validation.is_value_valid(arg):
|
||||
unknowns -= 1
|
||||
if unknowns == 0:
|
||||
break
|
||||
if unknowns < arg_count:
|
||||
return [self.to_pyobject(parameter)
|
||||
for parameter in parameters]
|
||||
|
||||
def get_passed_objects(self, pyfunction, parameter_index):
|
||||
path, key = self._get_scope(pyfunction)
|
||||
if path is None:
|
||||
return []
|
||||
result = []
|
||||
for call_info in self.objectdb.get_callinfos(path, key):
|
||||
args = call_info.get_parameters()
|
||||
if len(args) > parameter_index:
|
||||
parameter = self.to_pyobject(args[parameter_index])
|
||||
if parameter is not None:
|
||||
result.append(parameter)
|
||||
return result
|
||||
|
||||
def doa_data_received(self, data):
|
||||
def doi_to_normal(textual):
|
||||
pyobject = self.doi_to_pyobject(textual)
|
||||
return self.to_textual(pyobject)
|
||||
function = doi_to_normal(data[0])
|
||||
args = tuple([doi_to_normal(textual) for textual in data[1]])
|
||||
returned = doi_to_normal(data[2])
|
||||
if function[0] == 'defined' and len(function) == 3:
|
||||
self._save_data(function, args, returned)
|
||||
|
||||
def function_called(self, pyfunction, params, returned=None):
|
||||
function_text = self.to_textual(pyfunction)
|
||||
params_text = tuple([self.to_textual(param)
|
||||
for param in params])
|
||||
returned_text = ('unknown',)
|
||||
if returned is not None:
|
||||
returned_text = self.to_textual(returned)
|
||||
self._save_data(function_text, params_text, returned_text)
|
||||
|
||||
def save_per_name(self, scope, name, data):
|
||||
path, key = self._get_scope(scope.pyobject)
|
||||
if path is not None:
|
||||
self.objectdb.add_pername(path, key, name, self.to_textual(data))
|
||||
|
||||
def get_per_name(self, scope, name):
|
||||
path, key = self._get_scope(scope.pyobject)
|
||||
if path is not None:
|
||||
result = self.objectdb.get_pername(path, key, name)
|
||||
if result is not None:
|
||||
return self.to_pyobject(result)
|
||||
|
||||
def _save_data(self, function, args, returned=('unknown',)):
|
||||
self.objectdb.add_callinfo(function[1], function[2], args, returned)
|
||||
|
||||
def _get_scope(self, pyobject):
|
||||
resource = pyobject.get_module().get_resource()
|
||||
if resource is None:
|
||||
return None, None
|
||||
textual = self.to_textual(pyobject)
|
||||
if textual[0] == 'defined':
|
||||
path = textual[1]
|
||||
if len(textual) == 3:
|
||||
key = textual[2]
|
||||
else:
|
||||
key = ''
|
||||
return path, key
|
||||
return None, None
|
||||
|
||||
def sync(self):
|
||||
self.objectdb.sync()
|
||||
|
||||
def __str__(self):
|
||||
return str(self.objectdb)
|
||||
|
||||
|
||||
class TextualValidation(object):
|
||||
|
||||
def __init__(self, to_pyobject):
|
||||
self.to_pyobject = to_pyobject
|
||||
|
||||
def is_value_valid(self, value):
|
||||
# ???: Should none and unknown be considered valid?
|
||||
if value is None or value[0] in ('none', 'unknown'):
|
||||
return False
|
||||
return self.to_pyobject(value) is not None
|
||||
|
||||
def is_more_valid(self, new, old):
|
||||
if old is None:
|
||||
return True
|
||||
return new[0] not in ('unknown', 'none')
|
||||
|
||||
def is_file_valid(self, path):
|
||||
return self.to_pyobject.path_to_resource(path) is not None
|
||||
|
||||
def is_scope_valid(self, path, key):
|
||||
if key == '':
|
||||
textual = ('defined', path)
|
||||
else:
|
||||
textual = ('defined', path, key)
|
||||
return self.to_pyobject(textual) is not None
|
||||
|
||||
|
||||
class _FileListObserver(object):
|
||||
|
||||
def __init__(self, object_info):
|
||||
self.object_info = object_info
|
||||
self.observer = self.object_info.observer
|
||||
self.to_pyobject = self.object_info.to_pyobject
|
||||
|
||||
def removed(self, path):
|
||||
resource = self.to_pyobject.path_to_resource(path)
|
||||
if resource is not None:
|
||||
self.observer.remove_resource(resource)
|
||||
|
||||
def added(self, path):
|
||||
resource = self.to_pyobject.path_to_resource(path)
|
||||
if resource is not None:
|
||||
self.observer.add_resource(resource)
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
def __rope_start_everything():
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
try:
|
||||
import pickle
|
||||
except ImportError:
|
||||
import cPickle as pickle
|
||||
import marshal
|
||||
import inspect
|
||||
import types
|
||||
import threading
|
||||
import rope.base.utils.pycompat as pycompat
|
||||
|
||||
class _MessageSender(object):
|
||||
|
||||
def send_data(self, data):
|
||||
pass
|
||||
|
||||
class _SocketSender(_MessageSender):
|
||||
|
||||
def __init__(self, port):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(('127.0.0.1', port))
|
||||
self.my_file = s.makefile('wb')
|
||||
|
||||
def send_data(self, data):
|
||||
if not self.my_file.closed:
|
||||
pickle.dump(data, self.my_file)
|
||||
|
||||
def close(self):
|
||||
self.my_file.close()
|
||||
|
||||
class _FileSender(_MessageSender):
|
||||
|
||||
def __init__(self, file_name):
|
||||
self.my_file = open(file_name, 'wb')
|
||||
|
||||
def send_data(self, data):
|
||||
if not self.my_file.closed:
|
||||
marshal.dump(data, self.my_file)
|
||||
|
||||
def close(self):
|
||||
self.my_file.close()
|
||||
|
||||
def _cached(func):
|
||||
cache = {}
|
||||
|
||||
def newfunc(self, arg):
|
||||
if arg in cache:
|
||||
return cache[arg]
|
||||
result = func(self, arg)
|
||||
cache[arg] = result
|
||||
return result
|
||||
return newfunc
|
||||
|
||||
class _FunctionCallDataSender(object):
|
||||
|
||||
def __init__(self, send_info, project_root):
|
||||
self.project_root = project_root
|
||||
if send_info.isdigit():
|
||||
self.sender = _SocketSender(int(send_info))
|
||||
else:
|
||||
self.sender = _FileSender(send_info)
|
||||
|
||||
def global_trace(frame, event, arg):
|
||||
# HACK: Ignoring out->in calls
|
||||
# This might lose some information
|
||||
if self._is_an_interesting_call(frame):
|
||||
return self.on_function_call
|
||||
sys.settrace(global_trace)
|
||||
threading.settrace(global_trace)
|
||||
|
||||
def on_function_call(self, frame, event, arg):
|
||||
if event != 'return':
|
||||
return
|
||||
args = []
|
||||
returned = ('unknown',)
|
||||
code = frame.f_code
|
||||
for argname in code.co_varnames[:code.co_argcount]:
|
||||
try:
|
||||
argvalue = self._object_to_persisted_form(
|
||||
frame.f_locals[argname])
|
||||
args.append(argvalue)
|
||||
except (TypeError, AttributeError):
|
||||
args.append(('unknown',))
|
||||
try:
|
||||
returned = self._object_to_persisted_form(arg)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
try:
|
||||
data = (self._object_to_persisted_form(frame.f_code),
|
||||
tuple(args), returned)
|
||||
self.sender.send_data(data)
|
||||
except (TypeError):
|
||||
pass
|
||||
return self.on_function_call
|
||||
|
||||
def _is_an_interesting_call(self, frame):
|
||||
#if frame.f_code.co_name in ['?', '<module>']:
|
||||
# return False
|
||||
#return not frame.f_back or
|
||||
# not self._is_code_inside_project(frame.f_back.f_code)
|
||||
if not self._is_code_inside_project(frame.f_code) and \
|
||||
(not frame.f_back or
|
||||
not self._is_code_inside_project(frame.f_back.f_code)):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _is_code_inside_project(self, code):
|
||||
source = self._path(code.co_filename)
|
||||
return source is not None and os.path.exists(source) and \
|
||||
_realpath(source).startswith(self.project_root)
|
||||
|
||||
@_cached
|
||||
def _get_persisted_code(self, object_):
|
||||
source = self._path(object_.co_filename)
|
||||
if not os.path.exists(source):
|
||||
raise TypeError('no source')
|
||||
return ('defined', _realpath(source), str(object_.co_firstlineno))
|
||||
|
||||
@_cached
|
||||
def _get_persisted_class(self, object_):
|
||||
try:
|
||||
return ('defined', _realpath(inspect.getsourcefile(object_)),
|
||||
object_.__name__)
|
||||
except (TypeError, AttributeError):
|
||||
return ('unknown',)
|
||||
|
||||
def _get_persisted_builtin(self, object_):
|
||||
if isinstance(object_, pycompat.string_types):
|
||||
return ('builtin', 'str')
|
||||
if isinstance(object_, list):
|
||||
holding = None
|
||||
if len(object_) > 0:
|
||||
holding = object_[0]
|
||||
return ('builtin', 'list',
|
||||
self._object_to_persisted_form(holding))
|
||||
if isinstance(object_, dict):
|
||||
keys = None
|
||||
values = None
|
||||
if len(object_) > 0:
|
||||
# @todo - fix it properly, why is __locals__ being
|
||||
# duplicated ?
|
||||
keys = [key for key in object_.keys() if key != '__locals__'][0]
|
||||
values = object_[keys]
|
||||
return ('builtin', 'dict',
|
||||
self._object_to_persisted_form(keys),
|
||||
self._object_to_persisted_form(values))
|
||||
if isinstance(object_, tuple):
|
||||
objects = []
|
||||
if len(object_) < 3:
|
||||
for holding in object_:
|
||||
objects.append(self._object_to_persisted_form(holding))
|
||||
else:
|
||||
objects.append(self._object_to_persisted_form(object_[0]))
|
||||
return tuple(['builtin', 'tuple'] + objects)
|
||||
if isinstance(object_, set):
|
||||
holding = None
|
||||
if len(object_) > 0:
|
||||
for o in object_:
|
||||
holding = o
|
||||
break
|
||||
return ('builtin', 'set',
|
||||
self._object_to_persisted_form(holding))
|
||||
return ('unknown',)
|
||||
|
||||
def _object_to_persisted_form(self, object_):
|
||||
if object_ is None:
|
||||
return ('none',)
|
||||
if isinstance(object_, types.CodeType):
|
||||
return self._get_persisted_code(object_)
|
||||
if isinstance(object_, types.FunctionType):
|
||||
return self._get_persisted_code(object_.__code__)
|
||||
if isinstance(object_, types.MethodType):
|
||||
return self._get_persisted_code(object_.__func__.__code__)
|
||||
if isinstance(object_, types.ModuleType):
|
||||
return self._get_persisted_module(object_)
|
||||
if isinstance(object_, pycompat.string_types + (list, dict, tuple, set)):
|
||||
return self._get_persisted_builtin(object_)
|
||||
if isinstance(object_, type):
|
||||
return self._get_persisted_class(object_)
|
||||
return ('instance', self._get_persisted_class(type(object_)))
|
||||
|
||||
@_cached
|
||||
def _get_persisted_module(self, object_):
|
||||
path = self._path(object_.__file__)
|
||||
if path and os.path.exists(path):
|
||||
return ('defined', _realpath(path))
|
||||
return ('unknown',)
|
||||
|
||||
def _path(self, path):
|
||||
if path.endswith('.pyc'):
|
||||
path = path[:-1]
|
||||
if path.endswith('.py'):
|
||||
return path
|
||||
|
||||
def close(self):
|
||||
self.sender.close()
|
||||
sys.settrace(None)
|
||||
|
||||
def _realpath(path):
|
||||
return os.path.realpath(os.path.abspath(os.path.expanduser(path)))
|
||||
|
||||
send_info = sys.argv[1]
|
||||
project_root = sys.argv[2]
|
||||
file_to_run = sys.argv[3]
|
||||
run_globals = globals()
|
||||
run_globals.update({'__name__': '__main__',
|
||||
'__builtins__': __builtins__,
|
||||
'__file__': file_to_run})
|
||||
|
||||
if send_info != '-':
|
||||
data_sender = _FunctionCallDataSender(send_info, project_root)
|
||||
del sys.argv[1:4]
|
||||
pycompat.execfile(file_to_run, run_globals)
|
||||
if send_info != '-':
|
||||
data_sender.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
__rope_start_everything()
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
import rope.base.ast
|
||||
import rope.base.oi.soi
|
||||
import rope.base.pynames
|
||||
from rope.base import pyobjects, evaluate, astutils, arguments
|
||||
|
||||
|
||||
def analyze_module(pycore, pymodule, should_analyze,
|
||||
search_subscopes, followed_calls):
|
||||
"""Analyze `pymodule` for static object inference
|
||||
|
||||
Analyzes scopes for collecting object information. The analysis
|
||||
starts from inner scopes.
|
||||
|
||||
"""
|
||||
_analyze_node(pycore, pymodule, should_analyze,
|
||||
search_subscopes, followed_calls)
|
||||
|
||||
|
||||
def _analyze_node(pycore, pydefined, should_analyze,
|
||||
search_subscopes, followed_calls):
|
||||
if search_subscopes(pydefined):
|
||||
for scope in pydefined.get_scope().get_scopes():
|
||||
_analyze_node(pycore, scope.pyobject, should_analyze,
|
||||
search_subscopes, followed_calls)
|
||||
if should_analyze(pydefined):
|
||||
new_followed_calls = max(0, followed_calls - 1)
|
||||
return_true = lambda pydefined: True
|
||||
return_false = lambda pydefined: False
|
||||
|
||||
def _follow(pyfunction):
|
||||
_analyze_node(pycore, pyfunction, return_true,
|
||||
return_false, new_followed_calls)
|
||||
|
||||
if not followed_calls:
|
||||
_follow = None
|
||||
visitor = SOAVisitor(pycore, pydefined, _follow)
|
||||
for child in rope.base.ast.get_child_nodes(pydefined.get_ast()):
|
||||
rope.base.ast.walk(child, visitor)
|
||||
|
||||
|
||||
class SOAVisitor(object):
|
||||
|
||||
def __init__(self, pycore, pydefined, follow_callback=None):
|
||||
self.pycore = pycore
|
||||
self.pymodule = pydefined.get_module()
|
||||
self.scope = pydefined.get_scope()
|
||||
self.follow = follow_callback
|
||||
|
||||
def _FunctionDef(self, node):
|
||||
pass
|
||||
|
||||
def _ClassDef(self, node):
|
||||
pass
|
||||
|
||||
def _Call(self, node):
|
||||
for child in rope.base.ast.get_child_nodes(node):
|
||||
rope.base.ast.walk(child, self)
|
||||
primary, pyname = evaluate.eval_node2(self.scope, node.func)
|
||||
if pyname is None:
|
||||
return
|
||||
pyfunction = pyname.get_object()
|
||||
if isinstance(pyfunction, pyobjects.AbstractFunction):
|
||||
args = arguments.create_arguments(primary, pyfunction,
|
||||
node, self.scope)
|
||||
elif isinstance(pyfunction, pyobjects.PyClass):
|
||||
pyclass = pyfunction
|
||||
if '__init__' in pyfunction:
|
||||
pyfunction = pyfunction['__init__'].get_object()
|
||||
pyname = rope.base.pynames.UnboundName(pyobjects.PyObject(pyclass))
|
||||
args = self._args_with_self(primary, pyname, pyfunction, node)
|
||||
elif '__call__' in pyfunction:
|
||||
pyfunction = pyfunction['__call__'].get_object()
|
||||
args = self._args_with_self(primary, pyname, pyfunction, node)
|
||||
else:
|
||||
return
|
||||
self._call(pyfunction, args)
|
||||
|
||||
def _args_with_self(self, primary, self_pyname, pyfunction, node):
|
||||
base_args = arguments.create_arguments(primary, pyfunction,
|
||||
node, self.scope)
|
||||
return arguments.MixedArguments(self_pyname, base_args, self.scope)
|
||||
|
||||
def _call(self, pyfunction, args):
|
||||
if isinstance(pyfunction, pyobjects.PyFunction):
|
||||
if self.follow is not None:
|
||||
before = self._parameter_objects(pyfunction)
|
||||
self.pycore.object_info.function_called(
|
||||
pyfunction, args.get_arguments(pyfunction.get_param_names()))
|
||||
pyfunction._set_parameter_pyobjects(None)
|
||||
if self.follow is not None:
|
||||
after = self._parameter_objects(pyfunction)
|
||||
if after != before:
|
||||
self.follow(pyfunction)
|
||||
# XXX: Maybe we should not call every builtin function
|
||||
if isinstance(pyfunction, rope.base.builtins.BuiltinFunction):
|
||||
pyfunction.get_returned_object(args)
|
||||
|
||||
def _parameter_objects(self, pyfunction):
|
||||
result = []
|
||||
for i in range(len(pyfunction.get_param_names(False))):
|
||||
result.append(pyfunction.get_parameter(i))
|
||||
return result
|
||||
|
||||
def _Assign(self, node):
|
||||
for child in rope.base.ast.get_child_nodes(node):
|
||||
rope.base.ast.walk(child, self)
|
||||
visitor = _SOAAssignVisitor()
|
||||
nodes = []
|
||||
for child in node.targets:
|
||||
rope.base.ast.walk(child, visitor)
|
||||
nodes.extend(visitor.nodes)
|
||||
for subscript, levels in nodes:
|
||||
instance = evaluate.eval_node(self.scope, subscript.value)
|
||||
args_pynames = []
|
||||
args_pynames.append(evaluate.eval_node(self.scope,
|
||||
subscript.slice.value))
|
||||
value = rope.base.oi.soi._infer_assignment(
|
||||
rope.base.pynames.AssignmentValue(node.value, levels),
|
||||
self.pymodule)
|
||||
args_pynames.append(rope.base.pynames.UnboundName(value))
|
||||
if instance is not None and value is not None:
|
||||
pyobject = instance.get_object()
|
||||
if '__setitem__' in pyobject:
|
||||
pyfunction = pyobject['__setitem__'].get_object()
|
||||
args = arguments.ObjectArguments([instance] + args_pynames)
|
||||
self._call(pyfunction, args)
|
||||
# IDEA: handle `__setslice__`, too
|
||||
|
||||
|
||||
class _SOAAssignVisitor(astutils._NodeNameCollector):
|
||||
|
||||
def __init__(self):
|
||||
super(_SOAAssignVisitor, self).__init__()
|
||||
self.nodes = []
|
||||
|
||||
def _added(self, node, levels):
|
||||
if isinstance(node, rope.base.ast.Subscript) and \
|
||||
isinstance(node.slice, rope.base.ast.Index):
|
||||
self.nodes.append((node, levels))
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
"""A module for inferring objects
|
||||
|
||||
For more information see the documentation in `rope.base.oi`
|
||||
package.
|
||||
|
||||
"""
|
||||
import rope.base.builtins
|
||||
import rope.base.pynames
|
||||
import rope.base.pyobjects
|
||||
from rope.base import evaluate, utils, arguments
|
||||
from rope.base.oi.docstrings import hint_return, hint_param, hint_attr, hint_pep0484
|
||||
|
||||
|
||||
_ignore_inferred = utils.ignore_exception(
|
||||
rope.base.pyobjects.IsBeingInferredError)
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def infer_returned_object(pyfunction, args):
|
||||
"""Infer the `PyObject` this `PyFunction` returns after calling"""
|
||||
object_info = pyfunction.pycore.object_info
|
||||
result = object_info.get_exact_returned(pyfunction, args)
|
||||
if result is not None:
|
||||
return result
|
||||
result = _infer_returned(pyfunction, args)
|
||||
if result is not None:
|
||||
if args and pyfunction.get_module().get_resource() is not None:
|
||||
params = args.get_arguments(
|
||||
pyfunction.get_param_names(special_args=False))
|
||||
object_info.function_called(pyfunction, params, result)
|
||||
return result
|
||||
result = object_info.get_returned(pyfunction, args)
|
||||
if result is not None:
|
||||
return result
|
||||
type_ = hint_return(pyfunction)
|
||||
if type_ is not None:
|
||||
return rope.base.pyobjects.PyObject(type_)
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def infer_parameter_objects(pyfunction):
|
||||
"""Infer the `PyObject`\s of parameters of this `PyFunction`"""
|
||||
object_info = pyfunction.pycore.object_info
|
||||
result = object_info.get_parameter_objects(pyfunction)
|
||||
if result is None:
|
||||
result = _parameter_objects(pyfunction)
|
||||
_handle_first_parameter(pyfunction, result)
|
||||
return result
|
||||
|
||||
|
||||
def _handle_first_parameter(pyobject, parameters):
|
||||
kind = pyobject.get_kind()
|
||||
if parameters is None or kind not in ['method', 'classmethod']:
|
||||
pass
|
||||
if not parameters:
|
||||
if not pyobject.get_param_names(special_args=False):
|
||||
return
|
||||
parameters.append(rope.base.pyobjects.get_unknown())
|
||||
if kind == 'method':
|
||||
parameters[0] = rope.base.pyobjects.PyObject(pyobject.parent)
|
||||
if kind == 'classmethod':
|
||||
parameters[0] = pyobject.parent
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def infer_assigned_object(pyname):
|
||||
if not pyname.assignments:
|
||||
return
|
||||
for assignment in reversed(pyname.assignments):
|
||||
result = _infer_assignment(assignment, pyname.module)
|
||||
if isinstance(result, rope.base.builtins.BuiltinUnknown) and result.get_name() == 'NotImplementedType':
|
||||
break
|
||||
elif result == rope.base.pyobjects.get_unknown():
|
||||
break
|
||||
elif result is not None:
|
||||
return result
|
||||
|
||||
hinting_result = hint_pep0484(pyname)
|
||||
if hinting_result is not None:
|
||||
return hinting_result
|
||||
|
||||
hinting_result = _infer_assigned_object_by_hint(pyname)
|
||||
if hinting_result is not None:
|
||||
return hinting_result
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _infer_assigned_object_by_hint(pyname):
|
||||
lineno = _get_lineno_for_node(pyname.assignments[0].ast_node)
|
||||
holding_scope = pyname.module.get_scope().get_inner_scope_for_line(lineno)
|
||||
pyobject = holding_scope.pyobject
|
||||
if isinstance(pyobject, rope.base.pyobjects.PyClass):
|
||||
pyclass = pyobject
|
||||
elif (isinstance(pyobject, rope.base.pyobjectsdef.PyFunction) and
|
||||
isinstance(pyobject.parent, rope.base.pyobjects.PyClass)):
|
||||
pyclass = pyobject.parent
|
||||
else:
|
||||
return
|
||||
for name, attr in pyclass.get_attributes().items():
|
||||
if attr is pyname:
|
||||
type_ = hint_attr(pyclass, name)
|
||||
if type_ is not None:
|
||||
return rope.base.pyobjects.PyObject(type_)
|
||||
break
|
||||
|
||||
|
||||
def get_passed_objects(pyfunction, parameter_index):
|
||||
object_info = pyfunction.pycore.object_info
|
||||
result = object_info.get_passed_objects(pyfunction,
|
||||
parameter_index)
|
||||
if not result:
|
||||
statically_inferred = _parameter_objects(pyfunction)
|
||||
if len(statically_inferred) > parameter_index:
|
||||
result.append(statically_inferred[parameter_index])
|
||||
return result
|
||||
|
||||
|
||||
def _infer_returned(pyobject, args):
|
||||
if args:
|
||||
# HACK: Setting parameter objects manually
|
||||
# This is not thread safe and might cause problems if `args`
|
||||
# does not come from a good call site
|
||||
pyobject.get_scope().invalidate_data()
|
||||
pyobject._set_parameter_pyobjects(
|
||||
args.get_arguments(pyobject.get_param_names(special_args=False)))
|
||||
scope = pyobject.get_scope()
|
||||
if not scope._get_returned_asts():
|
||||
return
|
||||
maxtries = 3
|
||||
for returned_node in reversed(scope._get_returned_asts()[-maxtries:]):
|
||||
try:
|
||||
resulting_pyname = evaluate.eval_node(scope, returned_node)
|
||||
if resulting_pyname is None:
|
||||
continue
|
||||
pyobject = resulting_pyname.get_object()
|
||||
if pyobject == rope.base.pyobjects.get_unknown():
|
||||
continue
|
||||
if not scope._is_generator():
|
||||
return pyobject
|
||||
else:
|
||||
return rope.base.builtins.get_generator(pyobject)
|
||||
except rope.base.pyobjects.IsBeingInferredError:
|
||||
pass
|
||||
|
||||
|
||||
def _parameter_objects(pyobject):
|
||||
result = []
|
||||
params = pyobject.get_param_names(special_args=False)
|
||||
for name in params:
|
||||
type_ = hint_param(pyobject, name)
|
||||
if type_ is not None:
|
||||
result.append(rope.base.pyobjects.PyObject(type_))
|
||||
else:
|
||||
result.append(rope.base.pyobjects.get_unknown())
|
||||
return result
|
||||
|
||||
# handling `rope.base.pynames.AssignmentValue`
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def _infer_assignment(assignment, pymodule):
|
||||
result = _follow_pyname(assignment, pymodule)
|
||||
if result is None:
|
||||
return None
|
||||
pyname, pyobject = result
|
||||
pyobject = _follow_evaluations(assignment, pyname, pyobject)
|
||||
if pyobject is None:
|
||||
return None
|
||||
return _follow_levels(assignment, pyobject)
|
||||
|
||||
|
||||
def _follow_levels(assignment, pyobject):
|
||||
for index in assignment.levels:
|
||||
if isinstance(pyobject.get_type(), rope.base.builtins.Tuple):
|
||||
holdings = pyobject.get_type().get_holding_objects()
|
||||
if holdings:
|
||||
pyobject = holdings[min(len(holdings) - 1, index)]
|
||||
else:
|
||||
pyobject = None
|
||||
elif isinstance(pyobject.get_type(), rope.base.builtins.List):
|
||||
pyobject = pyobject.get_type().holding
|
||||
else:
|
||||
pyobject = None
|
||||
if pyobject is None:
|
||||
break
|
||||
return pyobject
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def _follow_pyname(assignment, pymodule, lineno=None):
|
||||
assign_node = assignment.ast_node
|
||||
if lineno is None:
|
||||
lineno = _get_lineno_for_node(assign_node)
|
||||
holding_scope = pymodule.get_scope().get_inner_scope_for_line(lineno)
|
||||
pyname = evaluate.eval_node(holding_scope, assign_node)
|
||||
if pyname is not None:
|
||||
result = pyname.get_object()
|
||||
if isinstance(result.get_type(), rope.base.builtins.Property) and \
|
||||
holding_scope.get_kind() == 'Class':
|
||||
arg = rope.base.pynames.UnboundName(
|
||||
rope.base.pyobjects.PyObject(holding_scope.pyobject))
|
||||
return pyname, result.get_type().get_property_object(
|
||||
arguments.ObjectArguments([arg]))
|
||||
return pyname, result
|
||||
|
||||
|
||||
@_ignore_inferred
|
||||
def _follow_evaluations(assignment, pyname, pyobject):
|
||||
new_pyname = pyname
|
||||
tokens = assignment.evaluation.split('.')
|
||||
for token in tokens:
|
||||
call = token.endswith('()')
|
||||
if call:
|
||||
token = token[:-2]
|
||||
if token:
|
||||
pyname = new_pyname
|
||||
new_pyname = _get_attribute(pyobject, token)
|
||||
if new_pyname is not None:
|
||||
pyobject = new_pyname.get_object()
|
||||
if pyobject is not None and call:
|
||||
if isinstance(pyobject, rope.base.pyobjects.AbstractFunction):
|
||||
args = arguments.ObjectArguments([pyname])
|
||||
pyobject = pyobject.get_returned_object(args)
|
||||
else:
|
||||
pyobject = None
|
||||
if pyobject is None:
|
||||
break
|
||||
if pyobject is not None and assignment.assign_type:
|
||||
return rope.base.pyobjects.PyObject(pyobject)
|
||||
return pyobject
|
||||
|
||||
|
||||
def _get_lineno_for_node(assign_node):
|
||||
if hasattr(assign_node, 'lineno') and \
|
||||
assign_node.lineno is not None:
|
||||
return assign_node.lineno
|
||||
return 1
|
||||
|
||||
|
||||
def _get_attribute(pyobject, name):
|
||||
if pyobject is not None and name in pyobject:
|
||||
return pyobject[name]
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
"""Provides classes for persisting `PyObject`\s"""
|
||||
import os
|
||||
import re
|
||||
|
||||
import rope.base.builtins
|
||||
from rope.base import exceptions
|
||||
|
||||
|
||||
class PyObjectToTextual(object):
|
||||
"""For transforming `PyObject` to textual form
|
||||
|
||||
This can be used for storing `PyObjects` in files. Use
|
||||
`TextualToPyObject` for converting back.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
|
||||
def transform(self, pyobject):
|
||||
"""Transform a `PyObject` to textual form"""
|
||||
if pyobject is None:
|
||||
return ('none',)
|
||||
object_type = type(pyobject)
|
||||
try:
|
||||
method = getattr(self, object_type.__name__ + '_to_textual')
|
||||
return method(pyobject)
|
||||
except AttributeError:
|
||||
return ('unknown',)
|
||||
|
||||
def __call__(self, pyobject):
|
||||
return self.transform(pyobject)
|
||||
|
||||
def PyObject_to_textual(self, pyobject):
|
||||
if isinstance(pyobject.get_type(), rope.base.pyobjects.AbstractClass):
|
||||
result = self.transform(pyobject.get_type())
|
||||
if result[0] == 'defined':
|
||||
return ('instance', result)
|
||||
return result
|
||||
return ('unknown',)
|
||||
|
||||
def PyFunction_to_textual(self, pyobject):
|
||||
return self._defined_to_textual(pyobject)
|
||||
|
||||
def PyClass_to_textual(self, pyobject):
|
||||
return self._defined_to_textual(pyobject)
|
||||
|
||||
def _defined_to_textual(self, pyobject):
|
||||
address = []
|
||||
while pyobject.parent is not None:
|
||||
address.insert(0, pyobject.get_name())
|
||||
pyobject = pyobject.parent
|
||||
return ('defined', self._get_pymodule_path(pyobject.get_module()),
|
||||
'.'.join(address))
|
||||
|
||||
def PyModule_to_textual(self, pyobject):
|
||||
return ('defined', self._get_pymodule_path(pyobject))
|
||||
|
||||
def PyPackage_to_textual(self, pyobject):
|
||||
return ('defined', self._get_pymodule_path(pyobject))
|
||||
|
||||
def List_to_textual(self, pyobject):
|
||||
return ('builtin', 'list', self.transform(pyobject.holding))
|
||||
|
||||
def Dict_to_textual(self, pyobject):
|
||||
return ('builtin', 'dict', self.transform(pyobject.keys),
|
||||
self.transform(pyobject.values))
|
||||
|
||||
def Tuple_to_textual(self, pyobject):
|
||||
objects = [self.transform(holding)
|
||||
for holding in pyobject.get_holding_objects()]
|
||||
return tuple(['builtin', 'tuple'] + objects)
|
||||
|
||||
def Set_to_textual(self, pyobject):
|
||||
return ('builtin', 'set', self.transform(pyobject.holding))
|
||||
|
||||
def Iterator_to_textual(self, pyobject):
|
||||
return ('builtin', 'iter', self.transform(pyobject.holding))
|
||||
|
||||
def Generator_to_textual(self, pyobject):
|
||||
return ('builtin', 'generator', self.transform(pyobject.holding))
|
||||
|
||||
def Str_to_textual(self, pyobject):
|
||||
return ('builtin', 'str')
|
||||
|
||||
def File_to_textual(self, pyobject):
|
||||
return ('builtin', 'file')
|
||||
|
||||
def BuiltinFunction_to_textual(self, pyobject):
|
||||
return ('builtin', 'function', pyobject.get_name())
|
||||
|
||||
def _get_pymodule_path(self, pymodule):
|
||||
return self.resource_to_path(pymodule.get_resource())
|
||||
|
||||
def resource_to_path(self, resource):
|
||||
if resource.project == self.project:
|
||||
return resource.path
|
||||
else:
|
||||
return resource.real_path
|
||||
|
||||
|
||||
class TextualToPyObject(object):
|
||||
"""For transforming textual form to `PyObject`"""
|
||||
|
||||
def __init__(self, project, allow_in_project_absolutes=False):
|
||||
self.project = project
|
||||
|
||||
def __call__(self, textual):
|
||||
return self.transform(textual)
|
||||
|
||||
def transform(self, textual):
|
||||
"""Transform an object from textual form to `PyObject`"""
|
||||
if textual is None:
|
||||
return None
|
||||
type = textual[0]
|
||||
try:
|
||||
method = getattr(self, type + '_to_pyobject')
|
||||
return method(textual)
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def builtin_to_pyobject(self, textual):
|
||||
method = getattr(self, 'builtin_%s_to_pyobject' % textual[1], None)
|
||||
if method is not None:
|
||||
return method(textual)
|
||||
|
||||
def builtin_str_to_pyobject(self, textual):
|
||||
return rope.base.builtins.get_str()
|
||||
|
||||
def builtin_list_to_pyobject(self, textual):
|
||||
holding = self.transform(textual[2])
|
||||
return rope.base.builtins.get_list(holding)
|
||||
|
||||
def builtin_dict_to_pyobject(self, textual):
|
||||
keys = self.transform(textual[2])
|
||||
values = self.transform(textual[3])
|
||||
return rope.base.builtins.get_dict(keys, values)
|
||||
|
||||
def builtin_tuple_to_pyobject(self, textual):
|
||||
objects = []
|
||||
for holding in textual[2:]:
|
||||
objects.append(self.transform(holding))
|
||||
return rope.base.builtins.get_tuple(*objects)
|
||||
|
||||
def builtin_set_to_pyobject(self, textual):
|
||||
holding = self.transform(textual[2])
|
||||
return rope.base.builtins.get_set(holding)
|
||||
|
||||
def builtin_iter_to_pyobject(self, textual):
|
||||
holding = self.transform(textual[2])
|
||||
return rope.base.builtins.get_iterator(holding)
|
||||
|
||||
def builtin_generator_to_pyobject(self, textual):
|
||||
holding = self.transform(textual[2])
|
||||
return rope.base.builtins.get_generator(holding)
|
||||
|
||||
def builtin_file_to_pyobject(self, textual):
|
||||
return rope.base.builtins.get_file()
|
||||
|
||||
def builtin_function_to_pyobject(self, textual):
|
||||
if textual[2] in rope.base.builtins.builtins:
|
||||
return rope.base.builtins.builtins[textual[2]].get_object()
|
||||
|
||||
def unknown_to_pyobject(self, textual):
|
||||
return None
|
||||
|
||||
def none_to_pyobject(self, textual):
|
||||
return None
|
||||
|
||||
def _module_to_pyobject(self, textual):
|
||||
path = textual[1]
|
||||
return self._get_pymodule(path)
|
||||
|
||||
def _hierarchical_defined_to_pyobject(self, textual):
|
||||
path = textual[1]
|
||||
names = textual[2].split('.')
|
||||
pymodule = self._get_pymodule(path)
|
||||
pyobject = pymodule
|
||||
for name in names:
|
||||
if pyobject is None:
|
||||
return None
|
||||
if isinstance(pyobject, rope.base.pyobjects.PyDefinedObject):
|
||||
try:
|
||||
pyobject = pyobject.get_scope()[name].get_object()
|
||||
except exceptions.NameNotFoundError:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
return pyobject
|
||||
|
||||
def defined_to_pyobject(self, textual):
|
||||
if len(textual) == 2 or textual[2] == '':
|
||||
return self._module_to_pyobject(textual)
|
||||
else:
|
||||
return self._hierarchical_defined_to_pyobject(textual)
|
||||
|
||||
def instance_to_pyobject(self, textual):
|
||||
type = self.transform(textual[1])
|
||||
if type is not None:
|
||||
return rope.base.pyobjects.PyObject(type)
|
||||
|
||||
def _get_pymodule(self, path):
|
||||
resource = self.path_to_resource(path)
|
||||
if resource is not None:
|
||||
return self.project.get_pymodule(resource)
|
||||
|
||||
def path_to_resource(self, path):
|
||||
try:
|
||||
root = self.project.address
|
||||
if not os.path.isabs(path):
|
||||
return self.project.get_resource(path)
|
||||
if path == root or path.startswith(root + os.sep):
|
||||
# INFO: This is a project file; should not be absolute
|
||||
return None
|
||||
import rope.base.project
|
||||
return rope.base.project.get_no_project().get_resource(path)
|
||||
except exceptions.ResourceNotFoundError:
|
||||
return None
|
||||
|
||||
|
||||
class DOITextualToPyObject(TextualToPyObject):
|
||||
"""For transforming textual form to `PyObject`
|
||||
|
||||
The textual form DOI uses is different from rope's standard
|
||||
textual form. The reason is that we cannot find the needed
|
||||
information by analyzing live objects. This class can be
|
||||
used to transform DOI textual form to `PyObject` and later
|
||||
we can convert it to standard textual form using
|
||||
`TextualToPyObject` class.
|
||||
|
||||
"""
|
||||
|
||||
def _function_to_pyobject(self, textual):
|
||||
path = textual[1]
|
||||
lineno = int(textual[2])
|
||||
pymodule = self._get_pymodule(path)
|
||||
if pymodule is not None:
|
||||
scope = pymodule.get_scope()
|
||||
inner_scope = scope.get_inner_scope_for_line(lineno)
|
||||
return inner_scope.pyobject
|
||||
|
||||
def _class_to_pyobject(self, textual):
|
||||
path, name = textual[1:]
|
||||
pymodule = self._get_pymodule(path)
|
||||
if pymodule is None:
|
||||
return None
|
||||
module_scope = pymodule.get_scope()
|
||||
suspected = None
|
||||
if name in module_scope.get_names():
|
||||
suspected = module_scope[name].get_object()
|
||||
if suspected is not None and \
|
||||
isinstance(suspected, rope.base.pyobjects.PyClass):
|
||||
return suspected
|
||||
else:
|
||||
lineno = self._find_occurrence(name,
|
||||
pymodule.get_resource().read())
|
||||
if lineno is not None:
|
||||
inner_scope = module_scope.get_inner_scope_for_line(lineno)
|
||||
return inner_scope.pyobject
|
||||
|
||||
def defined_to_pyobject(self, textual):
|
||||
if len(textual) == 2:
|
||||
return self._module_to_pyobject(textual)
|
||||
else:
|
||||
if textual[2].isdigit():
|
||||
result = self._function_to_pyobject(textual)
|
||||
else:
|
||||
result = self._class_to_pyobject(textual)
|
||||
if not isinstance(result, rope.base.pyobjects.PyModule):
|
||||
return result
|
||||
|
||||
def _find_occurrence(self, name, source):
|
||||
pattern = re.compile(r'^\s*class\s*' + name + r'\b')
|
||||
lines = source.split('\n')
|
||||
for i in range(len(lines)):
|
||||
if pattern.match(lines[i]):
|
||||
return i + 1
|
||||
|
||||
def path_to_resource(self, path):
|
||||
import rope.base.libutils
|
||||
relpath = rope.base.libutils.path_relative_to_project_root(
|
||||
self.project, path)
|
||||
if relpath is not None:
|
||||
path = relpath
|
||||
return super(DOITextualToPyObject, self).path_to_resource(path)
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
class Prefs(object):
|
||||
|
||||
def __init__(self):
|
||||
self.prefs = {}
|
||||
self.callbacks = {}
|
||||
|
||||
def set(self, key, value):
|
||||
"""Set the value of `key` preference to `value`."""
|
||||
if key in self.callbacks:
|
||||
self.callbacks[key](value)
|
||||
else:
|
||||
self.prefs[key] = value
|
||||
|
||||
def add(self, key, value):
|
||||
"""Add an entry to a list preference
|
||||
|
||||
Add `value` to the list of entries for the `key` preference.
|
||||
|
||||
"""
|
||||
if not key in self.prefs:
|
||||
self.prefs[key] = []
|
||||
self.prefs[key].append(value)
|
||||
|
||||
def get(self, key, default=None):
|
||||
"""Get the value of the key preference"""
|
||||
return self.prefs.get(key, default)
|
||||
|
||||
def add_callback(self, key, callback):
|
||||
"""Add `key` preference with `callback` function
|
||||
|
||||
Whenever `key` is set the callback is called with the
|
||||
given `value` as parameter.
|
||||
|
||||
"""
|
||||
self.callbacks[key] = callback
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.set(key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.get(key)
|
||||
|
|
@ -1,491 +0,0 @@
|
|||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import rope.base.fscommands
|
||||
import rope.base.resourceobserver as resourceobserver
|
||||
import rope.base.utils.pycompat as pycompat
|
||||
from rope.base import exceptions, taskhandle, prefs, history, pycore, utils
|
||||
from rope.base.exceptions import ModuleNotFoundError
|
||||
from rope.base.resources import File, Folder, _ResourceMatcher
|
||||
|
||||
try:
|
||||
import pickle
|
||||
except ImportError:
|
||||
import cPickle as pickle
|
||||
|
||||
|
||||
class _Project(object):
|
||||
|
||||
def __init__(self, fscommands):
|
||||
self.observers = []
|
||||
self.fscommands = fscommands
|
||||
self.prefs = prefs.Prefs()
|
||||
self.data_files = _DataFiles(self)
|
||||
self._custom_source_folders = []
|
||||
|
||||
def get_resource(self, resource_name):
|
||||
"""Get a resource in a project.
|
||||
|
||||
`resource_name` is the path of a resource in a project. It is
|
||||
the path of a resource relative to project root. Project root
|
||||
folder address is an empty string. If the resource does not
|
||||
exist a `exceptions.ResourceNotFound` exception would be
|
||||
raised. Use `get_file()` and `get_folder()` when you need to
|
||||
get nonexistent `Resource`\s.
|
||||
|
||||
"""
|
||||
path = self._get_resource_path(resource_name)
|
||||
if not os.path.exists(path):
|
||||
raise exceptions.ResourceNotFoundError(
|
||||
'Resource <%s> does not exist' % resource_name)
|
||||
elif os.path.isfile(path):
|
||||
return File(self, resource_name)
|
||||
elif os.path.isdir(path):
|
||||
return Folder(self, resource_name)
|
||||
else:
|
||||
raise exceptions.ResourceNotFoundError('Unknown resource '
|
||||
+ resource_name)
|
||||
|
||||
def get_module(self, name, folder=None):
|
||||
"""Returns a `PyObject` if the module was found."""
|
||||
# check if this is a builtin module
|
||||
pymod = self.pycore.builtin_module(name)
|
||||
if pymod is not None:
|
||||
return pymod
|
||||
module = self.find_module(name, folder)
|
||||
if module is None:
|
||||
raise ModuleNotFoundError('Module %s not found' % name)
|
||||
return self.pycore.resource_to_pyobject(module)
|
||||
|
||||
def get_python_path_folders(self):
|
||||
result = []
|
||||
for src in self.prefs.get('python_path', []) + sys.path:
|
||||
try:
|
||||
src_folder = get_no_project().get_resource(src)
|
||||
result.append(src_folder)
|
||||
except exceptions.ResourceNotFoundError:
|
||||
pass
|
||||
return result
|
||||
|
||||
# INFO: It was decided not to cache source folders, since:
|
||||
# - Does not take much time when the root folder contains
|
||||
# packages, that is most of the time
|
||||
# - We need a separate resource observer; `self.observer`
|
||||
# does not get notified about module and folder creations
|
||||
def get_source_folders(self):
|
||||
"""Returns project source folders"""
|
||||
if self.root is None:
|
||||
return []
|
||||
result = list(self._custom_source_folders)
|
||||
result.extend(self.pycore._find_source_folders(self.root))
|
||||
return result
|
||||
|
||||
def validate(self, folder):
|
||||
"""Validate files and folders contained in this folder
|
||||
|
||||
It validates all of the files and folders contained in this
|
||||
folder if some observers are interested in them.
|
||||
|
||||
"""
|
||||
for observer in list(self.observers):
|
||||
observer.validate(folder)
|
||||
|
||||
def add_observer(self, observer):
|
||||
"""Register a `ResourceObserver`
|
||||
|
||||
See `FilteredResourceObserver`.
|
||||
"""
|
||||
self.observers.append(observer)
|
||||
|
||||
def remove_observer(self, observer):
|
||||
"""Remove a registered `ResourceObserver`"""
|
||||
if observer in self.observers:
|
||||
self.observers.remove(observer)
|
||||
|
||||
def do(self, changes, task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Apply the changes in a `ChangeSet`
|
||||
|
||||
Most of the time you call this function for committing the
|
||||
changes for a refactoring.
|
||||
"""
|
||||
self.history.do(changes, task_handle=task_handle)
|
||||
|
||||
def get_pymodule(self, resource, force_errors=False):
|
||||
return self.pycore.resource_to_pyobject(resource, force_errors)
|
||||
|
||||
def get_pycore(self):
|
||||
return self.pycore
|
||||
|
||||
def get_file(self, path):
|
||||
"""Get the file with `path` (it may not exist)"""
|
||||
return File(self, path)
|
||||
|
||||
def get_folder(self, path):
|
||||
"""Get the folder with `path` (it may not exist)"""
|
||||
return Folder(self, path)
|
||||
|
||||
def get_prefs(self):
|
||||
return self.prefs
|
||||
|
||||
def get_relative_module(self, name, folder, level):
|
||||
module = self.find_relative_module(name, folder, level)
|
||||
if module is None:
|
||||
raise ModuleNotFoundError('Module %s not found' % name)
|
||||
return self.pycore.resource_to_pyobject(module)
|
||||
|
||||
def find_module(self, modname, folder=None):
|
||||
"""Returns a resource corresponding to the given module
|
||||
|
||||
returns None if it can not be found
|
||||
"""
|
||||
for src in self.get_source_folders():
|
||||
module = _find_module_in_folder(src, modname)
|
||||
if module is not None:
|
||||
return module
|
||||
for src in self.get_python_path_folders():
|
||||
module = _find_module_in_folder(src, modname)
|
||||
if module is not None:
|
||||
return module
|
||||
if folder is not None:
|
||||
module = _find_module_in_folder(folder, modname)
|
||||
if module is not None:
|
||||
return module
|
||||
return None
|
||||
|
||||
def find_relative_module(self, modname, folder, level):
|
||||
for i in range(level - 1):
|
||||
folder = folder.parent
|
||||
if modname == '':
|
||||
return folder
|
||||
else:
|
||||
return _find_module_in_folder(folder, modname)
|
||||
|
||||
def is_ignored(self, resource):
|
||||
return False
|
||||
|
||||
def _get_resource_path(self, name):
|
||||
pass
|
||||
|
||||
@property
|
||||
@utils.saveit
|
||||
def history(self):
|
||||
return history.History(self)
|
||||
|
||||
@property
|
||||
@utils.saveit
|
||||
def pycore(self):
|
||||
return pycore.PyCore(self)
|
||||
|
||||
def close(self):
|
||||
warnings.warn('Cannot close a NoProject',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
||||
ropefolder = None
|
||||
|
||||
|
||||
class Project(_Project):
|
||||
"""A Project containing files and folders"""
|
||||
|
||||
def __init__(self, projectroot, fscommands=None,
|
||||
ropefolder='.ropeproject', **prefs):
|
||||
"""A rope project
|
||||
|
||||
:parameters:
|
||||
- `projectroot`: The address of the root folder of the project
|
||||
- `fscommands`: Implements the file system operations used
|
||||
by rope; have a look at `rope.base.fscommands`
|
||||
- `ropefolder`: The name of the folder in which rope stores
|
||||
project configurations and data. Pass `None` for not using
|
||||
such a folder at all.
|
||||
- `prefs`: Specify project preferences. These values
|
||||
overwrite config file preferences.
|
||||
|
||||
"""
|
||||
if projectroot != '/':
|
||||
projectroot = _realpath(projectroot).rstrip('/\\')
|
||||
self._address = projectroot
|
||||
self._ropefolder_name = ropefolder
|
||||
if not os.path.exists(self._address):
|
||||
os.mkdir(self._address)
|
||||
elif not os.path.isdir(self._address):
|
||||
raise exceptions.RopeError('Project root exists and'
|
||||
' is not a directory')
|
||||
if fscommands is None:
|
||||
fscommands = rope.base.fscommands.create_fscommands(self._address)
|
||||
super(Project, self).__init__(fscommands)
|
||||
self.ignored = _ResourceMatcher()
|
||||
self.file_list = _FileListCacher(self)
|
||||
self.prefs.add_callback('ignored_resources', self.ignored.set_patterns)
|
||||
if ropefolder is not None:
|
||||
self.prefs['ignored_resources'] = [ropefolder]
|
||||
self._init_prefs(prefs)
|
||||
self._init_source_folders()
|
||||
|
||||
@utils.deprecated('Delete once deprecated functions are gone')
|
||||
def _init_source_folders(self):
|
||||
for path in self.prefs.get('source_folders', []):
|
||||
folder = self.get_resource(path)
|
||||
self._custom_source_folders.append(folder)
|
||||
|
||||
def get_files(self):
|
||||
return self.file_list.get_files()
|
||||
|
||||
def get_python_files(self):
|
||||
"""Returns all python files available in the project"""
|
||||
return [resource for resource in self.get_files()
|
||||
if self.pycore.is_python_file(resource)]
|
||||
|
||||
def _get_resource_path(self, name):
|
||||
return os.path.join(self._address, *name.split('/'))
|
||||
|
||||
def _init_ropefolder(self):
|
||||
if self.ropefolder is not None:
|
||||
if not self.ropefolder.exists():
|
||||
self._create_recursively(self.ropefolder)
|
||||
if not self.ropefolder.has_child('config.py'):
|
||||
config = self.ropefolder.create_file('config.py')
|
||||
config.write(self._default_config())
|
||||
|
||||
def _create_recursively(self, folder):
|
||||
if folder.parent != self.root and not folder.parent.exists():
|
||||
self._create_recursively(folder.parent)
|
||||
folder.create()
|
||||
|
||||
def _init_prefs(self, prefs):
|
||||
run_globals = {}
|
||||
if self.ropefolder is not None:
|
||||
config = self.get_file(self.ropefolder.path + '/config.py')
|
||||
run_globals.update({'__name__': '__main__',
|
||||
'__builtins__': __builtins__,
|
||||
'__file__': config.real_path})
|
||||
if config.exists():
|
||||
config = self.ropefolder.get_child('config.py')
|
||||
pycompat.execfile(config.real_path, run_globals)
|
||||
else:
|
||||
exec(self._default_config(), run_globals)
|
||||
if 'set_prefs' in run_globals:
|
||||
run_globals['set_prefs'](self.prefs)
|
||||
for key, value in prefs.items():
|
||||
self.prefs[key] = value
|
||||
self._init_other_parts()
|
||||
self._init_ropefolder()
|
||||
if 'project_opened' in run_globals:
|
||||
run_globals['project_opened'](self)
|
||||
|
||||
def _default_config(self):
|
||||
import rope.base.default_config
|
||||
import inspect
|
||||
return inspect.getsource(rope.base.default_config)
|
||||
|
||||
def _init_other_parts(self):
|
||||
# Forcing the creation of `self.pycore` to register observers
|
||||
self.pycore
|
||||
|
||||
def is_ignored(self, resource):
|
||||
return self.ignored.does_match(resource)
|
||||
|
||||
def sync(self):
|
||||
"""Closes project open resources"""
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
"""Closes project open resources"""
|
||||
self.data_files.write()
|
||||
|
||||
def set(self, key, value):
|
||||
"""Set the `key` preference to `value`"""
|
||||
self.prefs.set(key, value)
|
||||
|
||||
@property
|
||||
def ropefolder(self):
|
||||
if self._ropefolder_name is not None:
|
||||
return self.get_folder(self._ropefolder_name)
|
||||
|
||||
def validate(self, folder=None):
|
||||
if folder is None:
|
||||
folder = self.root
|
||||
super(Project, self).validate(folder)
|
||||
|
||||
root = property(lambda self: self.get_resource(''))
|
||||
address = property(lambda self: self._address)
|
||||
|
||||
|
||||
class NoProject(_Project):
|
||||
"""A null object for holding out of project files.
|
||||
|
||||
This class is singleton use `get_no_project` global function
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
fscommands = rope.base.fscommands.FileSystemCommands()
|
||||
super(NoProject, self).__init__(fscommands)
|
||||
|
||||
def _get_resource_path(self, name):
|
||||
real_name = name.replace('/', os.path.sep)
|
||||
return _realpath(real_name)
|
||||
|
||||
def get_resource(self, name):
|
||||
universal_name = _realpath(name).replace(os.path.sep, '/')
|
||||
return super(NoProject, self).get_resource(universal_name)
|
||||
|
||||
def get_files(self):
|
||||
return []
|
||||
|
||||
def get_python_files(self):
|
||||
return []
|
||||
|
||||
_no_project = None
|
||||
|
||||
|
||||
def get_no_project():
|
||||
if NoProject._no_project is None:
|
||||
NoProject._no_project = NoProject()
|
||||
return NoProject._no_project
|
||||
|
||||
|
||||
class _FileListCacher(object):
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
self.files = None
|
||||
rawobserver = resourceobserver.ResourceObserver(
|
||||
self._changed, self._invalid, self._invalid,
|
||||
self._invalid, self._invalid)
|
||||
self.project.add_observer(rawobserver)
|
||||
|
||||
def get_files(self):
|
||||
if self.files is None:
|
||||
self.files = set()
|
||||
self._add_files(self.project.root)
|
||||
return self.files
|
||||
|
||||
def _add_files(self, folder):
|
||||
for child in folder.get_children():
|
||||
if child.is_folder():
|
||||
self._add_files(child)
|
||||
elif not self.project.is_ignored(child):
|
||||
self.files.add(child)
|
||||
|
||||
def _changed(self, resource):
|
||||
if resource.is_folder():
|
||||
self.files = None
|
||||
|
||||
def _invalid(self, resource, new_resource=None):
|
||||
self.files = None
|
||||
|
||||
|
||||
class _DataFiles(object):
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
self.hooks = []
|
||||
|
||||
def read_data(self, name, compress=False, import_=False):
|
||||
if self.project.ropefolder is None:
|
||||
return None
|
||||
compress = compress and self._can_compress()
|
||||
opener = self._get_opener(compress)
|
||||
file = self._get_file(name, compress)
|
||||
if not compress and import_:
|
||||
self._import_old_files(name)
|
||||
if file.exists():
|
||||
input = opener(file.real_path, 'rb')
|
||||
try:
|
||||
result = []
|
||||
try:
|
||||
while True:
|
||||
result.append(pickle.load(input))
|
||||
except EOFError:
|
||||
pass
|
||||
if len(result) == 1:
|
||||
return result[0]
|
||||
if len(result) > 1:
|
||||
return result
|
||||
finally:
|
||||
input.close()
|
||||
|
||||
def write_data(self, name, data, compress=False):
|
||||
if self.project.ropefolder is not None:
|
||||
compress = compress and self._can_compress()
|
||||
file = self._get_file(name, compress)
|
||||
opener = self._get_opener(compress)
|
||||
output = opener(file.real_path, 'wb')
|
||||
try:
|
||||
pickle.dump(data, output, 2)
|
||||
finally:
|
||||
output.close()
|
||||
|
||||
def add_write_hook(self, hook):
|
||||
self.hooks.append(hook)
|
||||
|
||||
def write(self):
|
||||
for hook in self.hooks:
|
||||
hook()
|
||||
|
||||
def _can_compress(self):
|
||||
try:
|
||||
import gzip # noqa
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
def _import_old_files(self, name):
|
||||
old = self._get_file(name + '.pickle', False)
|
||||
new = self._get_file(name, False)
|
||||
if old.exists() and not new.exists():
|
||||
shutil.move(old.real_path, new.real_path)
|
||||
|
||||
def _get_opener(self, compress):
|
||||
if compress:
|
||||
try:
|
||||
import gzip
|
||||
return gzip.open
|
||||
except ImportError:
|
||||
pass
|
||||
return open
|
||||
|
||||
def _get_file(self, name, compress):
|
||||
path = self.project.ropefolder.path + '/' + name
|
||||
if compress:
|
||||
path += '.gz'
|
||||
return self.project.get_file(path)
|
||||
|
||||
|
||||
def _realpath(path):
|
||||
"""Return the real path of `path`
|
||||
|
||||
Is equivalent to ``realpath(abspath(expanduser(path)))``.
|
||||
|
||||
Of the particular notice is the hack dealing with the unfortunate
|
||||
sitaution of running native-Windows python (os.name == 'nt') inside
|
||||
of Cygwin (abspath starts with '/'), which apparently normal
|
||||
os.path.realpath completely messes up.
|
||||
|
||||
"""
|
||||
# there is a bug in cygwin for os.path.abspath() for abs paths
|
||||
if sys.platform == 'cygwin':
|
||||
if path[1:3] == ':\\':
|
||||
return path
|
||||
elif path[1:3] == ':/':
|
||||
path = "/cygdrive/" + path[0] + path[2:]
|
||||
return os.path.abspath(os.path.expanduser(path))
|
||||
return os.path.realpath(os.path.abspath(os.path.expanduser(path)))
|
||||
|
||||
|
||||
def _find_module_in_folder(folder, modname):
|
||||
module = folder
|
||||
packages = modname.split('.')
|
||||
for pkg in packages[:-1]:
|
||||
if module.is_folder() and module.has_child(pkg):
|
||||
module = module.get_child(pkg)
|
||||
else:
|
||||
return None
|
||||
if module.is_folder():
|
||||
if module.has_child(packages[-1]) and \
|
||||
module.get_child(packages[-1]).is_folder():
|
||||
return module.get_child(packages[-1])
|
||||
elif module.has_child(packages[-1] + '.py') and \
|
||||
not module.get_child(packages[-1] + '.py').is_folder():
|
||||
return module.get_child(packages[-1] + '.py')
|
||||
|
|
@ -1,346 +0,0 @@
|
|||
import bisect
|
||||
import difflib
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import rope.base.libutils
|
||||
import rope.base.resourceobserver
|
||||
import rope.base.resources
|
||||
import rope.base.oi.doa
|
||||
import rope.base.oi.objectinfo
|
||||
import rope.base.oi.soa
|
||||
from rope.base import builtins
|
||||
from rope.base import exceptions
|
||||
from rope.base import stdmods
|
||||
from rope.base import taskhandle
|
||||
from rope.base import utils
|
||||
from rope.base.exceptions import ModuleNotFoundError
|
||||
from rope.base.pyobjectsdef import PyModule, PyPackage
|
||||
|
||||
|
||||
class PyCore(object):
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
self._init_resource_observer()
|
||||
self.cache_observers = []
|
||||
self.module_cache = _ModuleCache(self)
|
||||
self.extension_cache = _ExtensionCache(self)
|
||||
self.object_info = rope.base.oi.objectinfo.ObjectInfoManager(project)
|
||||
self._init_python_files()
|
||||
self._init_automatic_soa()
|
||||
|
||||
def _init_python_files(self):
|
||||
self.python_matcher = None
|
||||
patterns = self.project.prefs.get('python_files', None)
|
||||
if patterns is not None:
|
||||
self.python_matcher = rope.base.resources._ResourceMatcher()
|
||||
self.python_matcher.set_patterns(patterns)
|
||||
|
||||
def _init_resource_observer(self):
|
||||
callback = self._invalidate_resource_cache
|
||||
observer = rope.base.resourceobserver.ResourceObserver(
|
||||
changed=callback, moved=callback, removed=callback)
|
||||
self.observer = \
|
||||
rope.base.resourceobserver.FilteredResourceObserver(observer)
|
||||
self.project.add_observer(self.observer)
|
||||
|
||||
def _init_automatic_soa(self):
|
||||
if not self.automatic_soa:
|
||||
return
|
||||
callback = self._file_changed_for_soa
|
||||
observer = rope.base.resourceobserver.ResourceObserver(
|
||||
changed=callback, moved=callback, removed=callback)
|
||||
self.project.add_observer(observer)
|
||||
|
||||
@property
|
||||
def automatic_soa(self):
|
||||
auto_soa = self.project.prefs.get('automatic_soi', None)
|
||||
return self.project.prefs.get('automatic_soa', auto_soa)
|
||||
|
||||
def _file_changed_for_soa(self, resource, new_resource=None):
|
||||
old_contents = self.project.history.\
|
||||
contents_before_current_change(resource)
|
||||
if old_contents is not None:
|
||||
perform_soa_on_changed_scopes(self.project, resource, old_contents)
|
||||
|
||||
def is_python_file(self, resource):
|
||||
if resource.is_folder():
|
||||
return False
|
||||
if self.python_matcher is None:
|
||||
return resource.name.endswith('.py')
|
||||
return self.python_matcher.does_match(resource)
|
||||
|
||||
@utils.deprecated('Use `project.get_module` instead')
|
||||
def get_module(self, name, folder=None):
|
||||
"""Returns a `PyObject` if the module was found."""
|
||||
return self.project.get_module(name, folder)
|
||||
|
||||
def _builtin_submodules(self, modname):
|
||||
result = {}
|
||||
for extension in self.extension_modules:
|
||||
if extension.startswith(modname + '.'):
|
||||
name = extension[len(modname) + 1:]
|
||||
if '.' not in name:
|
||||
result[name] = self.builtin_module(extension)
|
||||
return result
|
||||
|
||||
def builtin_module(self, name):
|
||||
return self.extension_cache.get_pymodule(name)
|
||||
|
||||
@utils.deprecated('Use `project.get_relative_module` instead')
|
||||
def get_relative_module(self, name, folder, level):
|
||||
return self.project.get_relative_module(name, folder, level)
|
||||
|
||||
@utils.deprecated('Use `libutils.get_string_module` instead')
|
||||
def get_string_module(self, code, resource=None, force_errors=False):
|
||||
"""Returns a `PyObject` object for the given code
|
||||
|
||||
If `force_errors` is `True`, `exceptions.ModuleSyntaxError` is
|
||||
raised if module has syntax errors. This overrides
|
||||
``ignore_syntax_errors`` project config.
|
||||
|
||||
"""
|
||||
return PyModule(self, code, resource, force_errors=force_errors)
|
||||
|
||||
@utils.deprecated('Use `libutils.get_string_scope` instead')
|
||||
def get_string_scope(self, code, resource=None):
|
||||
"""Returns a `Scope` object for the given code"""
|
||||
return rope.base.libutils.get_string_scope(code, resource)
|
||||
|
||||
def _invalidate_resource_cache(self, resource, new_resource=None):
|
||||
for observer in self.cache_observers:
|
||||
observer(resource)
|
||||
|
||||
@utils.deprecated('Use `project.get_python_path_folders` instead')
|
||||
def get_python_path_folders(self):
|
||||
return self.project.get_python_path_folders()
|
||||
|
||||
@utils.deprecated('Use `project.find_module` instead')
|
||||
def find_module(self, modname, folder=None):
|
||||
"""Returns a resource corresponding to the given module
|
||||
|
||||
returns None if it can not be found
|
||||
"""
|
||||
return self.project.find_module(modname, folder)
|
||||
|
||||
@utils.deprecated('Use `project.find_relative_module` instead')
|
||||
def find_relative_module(self, modname, folder, level):
|
||||
return self.project.find_relative_module(modname, folder, level)
|
||||
|
||||
# INFO: It was decided not to cache source folders, since:
|
||||
# - Does not take much time when the root folder contains
|
||||
# packages, that is most of the time
|
||||
# - We need a separate resource observer; `self.observer`
|
||||
# does not get notified about module and folder creations
|
||||
@utils.deprecated('Use `project.get_source_folders` instead')
|
||||
def get_source_folders(self):
|
||||
"""Returns project source folders"""
|
||||
return self.project.get_source_folders()
|
||||
|
||||
def resource_to_pyobject(self, resource, force_errors=False):
|
||||
return self.module_cache.get_pymodule(resource, force_errors)
|
||||
|
||||
@utils.deprecated('Use `project.get_python_files` instead')
|
||||
def get_python_files(self):
|
||||
"""Returns all python files available in the project"""
|
||||
return self.project.get_python_files()
|
||||
|
||||
def _is_package(self, folder):
|
||||
if folder.has_child('__init__.py') and \
|
||||
not folder.get_child('__init__.py').is_folder():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _find_source_folders(self, folder):
|
||||
for resource in folder.get_folders():
|
||||
if self._is_package(resource):
|
||||
return [folder]
|
||||
result = []
|
||||
for resource in folder.get_files():
|
||||
if resource.name.endswith('.py'):
|
||||
result.append(folder)
|
||||
break
|
||||
for resource in folder.get_folders():
|
||||
result.extend(self._find_source_folders(resource))
|
||||
return result
|
||||
|
||||
def run_module(self, resource, args=None, stdin=None, stdout=None):
|
||||
"""Run `resource` module
|
||||
|
||||
Returns a `rope.base.oi.doa.PythonFileRunner` object for
|
||||
controlling the process.
|
||||
|
||||
"""
|
||||
perform_doa = self.project.prefs.get('perform_doi', True)
|
||||
perform_doa = self.project.prefs.get('perform_doa', perform_doa)
|
||||
receiver = self.object_info.doa_data_received
|
||||
if not perform_doa:
|
||||
receiver = None
|
||||
runner = rope.base.oi.doa.PythonFileRunner(
|
||||
self, resource, args, stdin, stdout, receiver)
|
||||
runner.add_finishing_observer(self.module_cache.forget_all_data)
|
||||
runner.run()
|
||||
return runner
|
||||
|
||||
def analyze_module(self, resource, should_analyze=lambda py: True,
|
||||
search_subscopes=lambda py: True, followed_calls=None):
|
||||
"""Analyze `resource` module for static object inference
|
||||
|
||||
This function forces rope to analyze this module to collect
|
||||
information about function calls. `should_analyze` is a
|
||||
function that is called with a `PyDefinedObject` argument. If
|
||||
it returns `True` the element is analyzed. If it is `None` or
|
||||
returns `False` the element is not analyzed.
|
||||
|
||||
`search_subscopes` is like `should_analyze`; The difference is
|
||||
that if it returns `False` the sub-scopes are all ignored.
|
||||
That is it is assumed that `should_analyze` returns `False`
|
||||
for all of its subscopes.
|
||||
|
||||
`followed_calls` override the value of ``soa_followed_calls``
|
||||
project config.
|
||||
"""
|
||||
if followed_calls is None:
|
||||
followed_calls = self.project.prefs.get('soa_followed_calls', 0)
|
||||
pymodule = self.resource_to_pyobject(resource)
|
||||
self.module_cache.forget_all_data()
|
||||
rope.base.oi.soa.analyze_module(
|
||||
self, pymodule, should_analyze, search_subscopes, followed_calls)
|
||||
|
||||
def get_classes(self, task_handle=taskhandle.NullTaskHandle()):
|
||||
warnings.warn('`PyCore.get_classes()` is deprecated',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return []
|
||||
|
||||
def __str__(self):
|
||||
return str(self.module_cache) + str(self.object_info)
|
||||
|
||||
@utils.deprecated('Use `libutils.modname` instead')
|
||||
def modname(self, resource):
|
||||
return rope.base.libutils.modname(resource)
|
||||
|
||||
@property
|
||||
@utils.cacheit
|
||||
def extension_modules(self):
|
||||
result = set(self.project.prefs.get('extension_modules', []))
|
||||
if self.project.prefs.get('import_dynload_stdmods', False):
|
||||
result.update(stdmods.dynload_modules())
|
||||
return result
|
||||
|
||||
|
||||
class _ModuleCache(object):
|
||||
|
||||
def __init__(self, pycore):
|
||||
self.pycore = pycore
|
||||
self.module_map = {}
|
||||
self.pycore.cache_observers.append(self._invalidate_resource)
|
||||
self.observer = self.pycore.observer
|
||||
|
||||
def _invalidate_resource(self, resource):
|
||||
if resource in self.module_map:
|
||||
self.forget_all_data()
|
||||
self.observer.remove_resource(resource)
|
||||
del self.module_map[resource]
|
||||
|
||||
def get_pymodule(self, resource, force_errors=False):
|
||||
if resource in self.module_map:
|
||||
return self.module_map[resource]
|
||||
if resource.is_folder():
|
||||
result = PyPackage(self.pycore, resource,
|
||||
force_errors=force_errors)
|
||||
else:
|
||||
result = PyModule(self.pycore, resource=resource,
|
||||
force_errors=force_errors)
|
||||
if result.has_errors:
|
||||
return result
|
||||
self.module_map[resource] = result
|
||||
self.observer.add_resource(resource)
|
||||
return result
|
||||
|
||||
def forget_all_data(self):
|
||||
for pymodule in self.module_map.values():
|
||||
pymodule._forget_concluded_data()
|
||||
|
||||
def __str__(self):
|
||||
return 'PyCore caches %d PyModules\n' % len(self.module_map)
|
||||
|
||||
|
||||
class _ExtensionCache(object):
|
||||
|
||||
def __init__(self, pycore):
|
||||
self.pycore = pycore
|
||||
self.extensions = {}
|
||||
|
||||
def get_pymodule(self, name):
|
||||
if name == '__builtin__':
|
||||
return builtins.builtins
|
||||
allowed = self.pycore.extension_modules
|
||||
if name not in self.extensions and name in allowed:
|
||||
self.extensions[name] = builtins.BuiltinModule(name, self.pycore)
|
||||
return self.extensions.get(name)
|
||||
|
||||
|
||||
def perform_soa_on_changed_scopes(project, resource, old_contents):
|
||||
pycore = project.pycore
|
||||
if resource.exists() and pycore.is_python_file(resource):
|
||||
try:
|
||||
new_contents = resource.read()
|
||||
# detecting changes in new_contents relative to old_contents
|
||||
detector = _TextChangeDetector(new_contents, old_contents)
|
||||
|
||||
def search_subscopes(pydefined):
|
||||
scope = pydefined.get_scope()
|
||||
return detector.is_changed(scope.get_start(), scope.get_end())
|
||||
|
||||
def should_analyze(pydefined):
|
||||
scope = pydefined.get_scope()
|
||||
start = scope.get_start()
|
||||
end = scope.get_end()
|
||||
return detector.consume_changes(start, end)
|
||||
pycore.analyze_module(resource, should_analyze, search_subscopes)
|
||||
except exceptions.ModuleSyntaxError:
|
||||
pass
|
||||
|
||||
|
||||
class _TextChangeDetector(object):
|
||||
|
||||
def __init__(self, old, new):
|
||||
self.old = old
|
||||
self.new = new
|
||||
self._set_diffs()
|
||||
|
||||
def _set_diffs(self):
|
||||
differ = difflib.Differ()
|
||||
self.lines = []
|
||||
lineno = 0
|
||||
for line in differ.compare(self.old.splitlines(True),
|
||||
self.new.splitlines(True)):
|
||||
if line.startswith(' '):
|
||||
lineno += 1
|
||||
elif line.startswith('-'):
|
||||
lineno += 1
|
||||
self.lines.append(lineno)
|
||||
|
||||
def is_changed(self, start, end):
|
||||
"""Tell whether any of start till end lines have changed
|
||||
|
||||
The end points are inclusive and indices start from 1.
|
||||
"""
|
||||
left, right = self._get_changed(start, end)
|
||||
if left < right:
|
||||
return True
|
||||
return False
|
||||
|
||||
def consume_changes(self, start, end):
|
||||
"""Clear the changed status of lines from start till end"""
|
||||
left, right = self._get_changed(start, end)
|
||||
if left < right:
|
||||
del self.lines[left:right]
|
||||
return left < right
|
||||
|
||||
def _get_changed(self, start, end):
|
||||
left = bisect.bisect_left(self.lines, start)
|
||||
right = bisect.bisect_right(self.lines, end)
|
||||
return left, right
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
import rope.base.pyobjects
|
||||
from rope.base import exceptions, utils
|
||||
|
||||
|
||||
class PyName(object):
|
||||
"""References to `PyObject`\s inside python programs"""
|
||||
|
||||
def get_object(self):
|
||||
"""Return the `PyObject` object referenced by this `PyName`"""
|
||||
|
||||
def get_definition_location(self):
|
||||
"""Return a (module, lineno) tuple"""
|
||||
|
||||
|
||||
class DefinedName(PyName):
|
||||
|
||||
def __init__(self, pyobject):
|
||||
self.pyobject = pyobject
|
||||
|
||||
def get_object(self):
|
||||
return self.pyobject
|
||||
|
||||
def get_definition_location(self):
|
||||
return (self.pyobject.get_module(), self.pyobject.get_ast().lineno)
|
||||
|
||||
|
||||
class AssignedName(PyName):
|
||||
"""Only a placeholder"""
|
||||
|
||||
|
||||
class UnboundName(PyName):
|
||||
|
||||
def __init__(self, pyobject=None):
|
||||
self.pyobject = pyobject
|
||||
if self.pyobject is None:
|
||||
self.pyobject = rope.base.pyobjects.get_unknown()
|
||||
|
||||
def get_object(self):
|
||||
return self.pyobject
|
||||
|
||||
def get_definition_location(self):
|
||||
return (None, None)
|
||||
|
||||
|
||||
class AssignmentValue(object):
|
||||
"""An assigned expression"""
|
||||
|
||||
def __init__(self, ast_node, levels=None, evaluation='',
|
||||
assign_type=False):
|
||||
"""The `level` is `None` for simple assignments and is
|
||||
a list of numbers for tuple assignments for example in::
|
||||
|
||||
a, (b, c) = x
|
||||
|
||||
The levels for for `a` is ``[0]``, for `b` is ``[1, 0]`` and for
|
||||
`c` is ``[1, 1]``.
|
||||
|
||||
"""
|
||||
self.ast_node = ast_node
|
||||
if levels is None:
|
||||
self.levels = []
|
||||
else:
|
||||
self.levels = levels
|
||||
self.evaluation = evaluation
|
||||
self.assign_type = assign_type
|
||||
|
||||
def get_lineno(self):
|
||||
return self.ast_node.lineno
|
||||
|
||||
|
||||
class EvaluatedName(PyName):
|
||||
"""A name whose object will be evaluated later"""
|
||||
|
||||
def __init__(self, callback, module=None, lineno=None):
|
||||
self.module = module
|
||||
self.lineno = lineno
|
||||
self.callback = callback
|
||||
self.pyobject = _Inferred(callback, _get_concluded_data(module))
|
||||
|
||||
def get_object(self):
|
||||
return self.pyobject.get()
|
||||
|
||||
def get_definition_location(self):
|
||||
return (self.module, self.lineno)
|
||||
|
||||
def invalidate(self):
|
||||
"""Forget the `PyObject` this `PyName` holds"""
|
||||
self.pyobject.set(None)
|
||||
|
||||
|
||||
class ParameterName(PyName):
|
||||
"""Only a placeholder"""
|
||||
|
||||
|
||||
class ImportedModule(PyName):
|
||||
|
||||
def __init__(self, importing_module, module_name=None,
|
||||
level=0, resource=None):
|
||||
self.importing_module = importing_module
|
||||
self.module_name = module_name
|
||||
self.level = level
|
||||
self.resource = resource
|
||||
self.pymodule = _get_concluded_data(self.importing_module)
|
||||
|
||||
def _current_folder(self):
|
||||
resource = self.importing_module.get_module().get_resource()
|
||||
if resource is None:
|
||||
return None
|
||||
return resource.parent
|
||||
|
||||
def _get_pymodule(self):
|
||||
if self.pymodule.get() is None:
|
||||
pycore = self.importing_module.pycore
|
||||
if self.resource is not None:
|
||||
self.pymodule.set(pycore.project.get_pymodule(self.resource))
|
||||
elif self.module_name is not None:
|
||||
try:
|
||||
if self.level == 0:
|
||||
pymodule = pycore.project.get_module(
|
||||
self.module_name, self._current_folder())
|
||||
else:
|
||||
pymodule = pycore.project.get_relative_module(
|
||||
self.module_name, self._current_folder(),
|
||||
self.level)
|
||||
self.pymodule.set(pymodule)
|
||||
except exceptions.ModuleNotFoundError:
|
||||
pass
|
||||
return self.pymodule.get()
|
||||
|
||||
def get_object(self):
|
||||
if self._get_pymodule() is None:
|
||||
return rope.base.pyobjects.get_unknown()
|
||||
return self._get_pymodule()
|
||||
|
||||
def get_definition_location(self):
|
||||
pymodule = self._get_pymodule()
|
||||
if not isinstance(pymodule, rope.base.pyobjects.PyDefinedObject):
|
||||
return (None, None)
|
||||
return (pymodule.get_module(), 1)
|
||||
|
||||
|
||||
class ImportedName(PyName):
|
||||
|
||||
def __init__(self, imported_module, imported_name):
|
||||
self.imported_module = imported_module
|
||||
self.imported_name = imported_name
|
||||
|
||||
def _get_imported_pyname(self):
|
||||
try:
|
||||
result = self.imported_module.get_object()[self.imported_name]
|
||||
if result != self:
|
||||
return result
|
||||
except exceptions.AttributeNotFoundError:
|
||||
pass
|
||||
return UnboundName()
|
||||
|
||||
@utils.prevent_recursion(rope.base.pyobjects.get_unknown)
|
||||
def get_object(self):
|
||||
return self._get_imported_pyname().get_object()
|
||||
|
||||
@utils.prevent_recursion(lambda: (None, None))
|
||||
def get_definition_location(self):
|
||||
return self._get_imported_pyname().get_definition_location()
|
||||
|
||||
|
||||
def _get_concluded_data(module):
|
||||
if module is None:
|
||||
return rope.base.pyobjects._ConcludedData()
|
||||
return module._get_concluded_data()
|
||||
|
||||
|
||||
def _circular_inference():
|
||||
raise rope.base.pyobjects.IsBeingInferredError(
|
||||
'Circular Object Inference')
|
||||
|
||||
|
||||
class _Inferred(object):
|
||||
|
||||
def __init__(self, get_inferred, concluded=None):
|
||||
self.get_inferred = get_inferred
|
||||
self.concluded = concluded
|
||||
if self.concluded is None:
|
||||
self.temp = None
|
||||
|
||||
@utils.prevent_recursion(_circular_inference)
|
||||
def get(self, *args, **kwds):
|
||||
if self.concluded is None or self.concluded.get() is None:
|
||||
self.set(self.get_inferred(*args, **kwds))
|
||||
if self._get() is None:
|
||||
self.set(rope.base.pyobjects.get_unknown())
|
||||
return self._get()
|
||||
|
||||
def set(self, pyobject):
|
||||
if self.concluded is not None:
|
||||
self.concluded.set(pyobject)
|
||||
self.temp = pyobject
|
||||
|
||||
def _get(self):
|
||||
if self.concluded is not None:
|
||||
return self.concluded.get()
|
||||
return self.temp
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import rope.base.oi.soi
|
||||
from rope.base import pynames
|
||||
from rope.base.pynames import *
|
||||
|
||||
|
||||
class AssignedName(pynames.AssignedName):
|
||||
|
||||
def __init__(self, lineno=None, module=None, pyobject=None):
|
||||
self.lineno = lineno
|
||||
self.module = module
|
||||
self.assignments = []
|
||||
self.pyobject = _Inferred(self._get_inferred,
|
||||
pynames._get_concluded_data(module))
|
||||
self.pyobject.set(pyobject)
|
||||
|
||||
@utils.prevent_recursion(lambda: None)
|
||||
def _get_inferred(self):
|
||||
if self.module is not None:
|
||||
return rope.base.oi.soi.infer_assigned_object(self)
|
||||
|
||||
def get_object(self):
|
||||
return self.pyobject.get()
|
||||
|
||||
def get_definition_location(self):
|
||||
"""Returns a (module, lineno) tuple"""
|
||||
if self.lineno is None and self.assignments:
|
||||
self.lineno = self.assignments[0].get_lineno()
|
||||
return (self.module, self.lineno)
|
||||
|
||||
def invalidate(self):
|
||||
"""Forget the `PyObject` this `PyName` holds"""
|
||||
self.pyobject.set(None)
|
||||
|
||||
|
||||
class ParameterName(pynames.ParameterName):
|
||||
|
||||
def __init__(self, pyfunction, index):
|
||||
self.pyfunction = pyfunction
|
||||
self.index = index
|
||||
|
||||
def get_object(self):
|
||||
result = self.pyfunction.get_parameter(self.index)
|
||||
if result is None:
|
||||
result = rope.base.pyobjects.get_unknown()
|
||||
return result
|
||||
|
||||
def get_objects(self):
|
||||
"""Returns the list of objects passed as this parameter"""
|
||||
return rope.base.oi.soi.get_passed_objects(
|
||||
self.pyfunction, self.index)
|
||||
|
||||
def get_definition_location(self):
|
||||
return (self.pyfunction.get_module(), self.pyfunction.get_ast().lineno)
|
||||
|
||||
_Inferred = pynames._Inferred
|
||||
|
|
@ -1,311 +0,0 @@
|
|||
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`\s
|
||||
|
||||
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 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
|
||||
|
|
@ -1,560 +0,0 @@
|
|||
import rope.base.builtins
|
||||
import rope.base.codeanalyze
|
||||
import rope.base.evaluate
|
||||
import rope.base.libutils
|
||||
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.utils import pycompat
|
||||
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
unicode = str
|
||||
|
||||
|
||||
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)
|
||||
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 = [pycompat.get_ast_arg_arg(node) for node in self.arguments.args
|
||||
if isinstance(node, pycompat.ast_arg_type)]
|
||||
if special_args:
|
||||
if self.arguments.vararg:
|
||||
result.append(pycompat.get_ast_arg_arg(self.arguments.vararg))
|
||||
if self.arguments.kwarg:
|
||||
result.append(pycompat.get_ast_arg_arg(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
|
||||
rope.base.pyobjects.AbstractClass.__init__(self)
|
||||
rope.base.pyobjects.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() == \
|
||||
rope.base.pyobjects.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 as e:
|
||||
raise exceptions.ModuleSyntaxError(filename, e.lineno, e.msg)
|
||||
except UnicodeDecodeError as 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.project.get_pymodule(
|
||||
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 = rope.base.libutils.modname(self.resource)
|
||||
extension_submodules = self.pycore._builtin_submodules(modname)
|
||||
for name, module in extension_submodules.items():
|
||||
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.project.get_pymodule(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.project.get_pymodule(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(
|
||||
rope.base.pyobjects.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, # noqa
|
||||
'.__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 = {}
|
||||
if isinstance(targets, str):
|
||||
assignment = pynames.AssignmentValue(assigned, [],
|
||||
evaluation, eval_type)
|
||||
self._assigned(targets, assignment)
|
||||
else:
|
||||
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):
|
||||
for item in pycompat.get_ast_with_items(node):
|
||||
if item.optional_vars:
|
||||
self._update_evaluated(item.optional_vars,
|
||||
item.context_expr, '.__enter__()')
|
||||
for child in node.body:
|
||||
ast.walk(child, self)
|
||||
|
||||
def _excepthandler(self, node):
|
||||
node_name_type = str if pycompat.PY3 else ast.Name
|
||||
if node.name is not None and isinstance(node.name, node_name_type):
|
||||
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(),
|
||||
rope.base.pyobjects.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]
|
||||
new_visitor = None
|
||||
if isinstance(first, pycompat.ast_arg_type):
|
||||
new_visitor = _ClassInitVisitor(self, pycompat.get_ast_arg_arg(first))
|
||||
if new_visitor is not None:
|
||||
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
|
||||
|
|
@ -1,314 +0,0 @@
|
|||
import rope.base.builtins
|
||||
import rope.base.codeanalyze
|
||||
import rope.base.pynames
|
||||
from rope.base import ast, exceptions, utils
|
||||
|
||||
|
||||
class Scope(object):
|
||||
|
||||
def __init__(self, pycore, pyobject, parent_scope):
|
||||
self.pycore = pycore
|
||||
self.pyobject = pyobject
|
||||
self.parent = parent_scope
|
||||
|
||||
def get_names(self):
|
||||
"""Return the names defined or imported in this scope"""
|
||||
return self.pyobject.get_attributes()
|
||||
|
||||
def get_defined_names(self):
|
||||
"""Return the names defined in this scope"""
|
||||
return self.pyobject._get_structural_attributes()
|
||||
|
||||
def get_name(self, name):
|
||||
"""Return name `PyName` defined in this scope"""
|
||||
if name not in self.get_names():
|
||||
raise exceptions.NameNotFoundError('name %s not found' % name)
|
||||
return self.get_names()[name]
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""The same as ``get_name(key)``"""
|
||||
return self.get_name(key)
|
||||
|
||||
def __contains__(self, key):
|
||||
"""The same as ``key in self.get_names()``"""
|
||||
return key in self.get_names()
|
||||
|
||||
@utils.saveit
|
||||
def get_scopes(self):
|
||||
"""Return the subscopes of this scope
|
||||
|
||||
The returned scopes should be sorted by the order they appear.
|
||||
"""
|
||||
return self._create_scopes()
|
||||
|
||||
def lookup(self, name):
|
||||
if name in self.get_names():
|
||||
return self.get_names()[name]
|
||||
if self.parent is not None:
|
||||
return self.parent._propagated_lookup(name)
|
||||
return None
|
||||
|
||||
def get_propagated_names(self):
|
||||
"""Return the visible names of this scope
|
||||
|
||||
Return the names defined in this scope that are visible from
|
||||
scopes containing this scope. This method returns the same
|
||||
dictionary returned by `get_names()` except for `ClassScope`
|
||||
which returns an empty dict.
|
||||
"""
|
||||
return self.get_names()
|
||||
|
||||
def _propagated_lookup(self, name):
|
||||
if name in self.get_propagated_names():
|
||||
return self.get_propagated_names()[name]
|
||||
if self.parent is not None:
|
||||
return self.parent._propagated_lookup(name)
|
||||
return None
|
||||
|
||||
def _create_scopes(self):
|
||||
return [pydefined.get_scope()
|
||||
for pydefined in self.pyobject._get_defined_objects()]
|
||||
|
||||
def _get_global_scope(self):
|
||||
current = self
|
||||
while current.parent is not None:
|
||||
current = current.parent
|
||||
return current
|
||||
|
||||
def get_start(self):
|
||||
return self.pyobject.get_ast().lineno
|
||||
|
||||
def get_body_start(self):
|
||||
body = self.pyobject.get_ast().body
|
||||
if body:
|
||||
return body[0].lineno
|
||||
return self.get_start()
|
||||
|
||||
def get_end(self):
|
||||
pymodule = self._get_global_scope().pyobject
|
||||
return pymodule.logical_lines.logical_line_in(self.logical_end)[1]
|
||||
|
||||
@utils.saveit
|
||||
def get_logical_end(self):
|
||||
global_scope = self._get_global_scope()
|
||||
return global_scope._scope_finder.find_scope_end(self)
|
||||
|
||||
start = property(get_start)
|
||||
end = property(get_end)
|
||||
logical_end = property(get_logical_end)
|
||||
|
||||
def get_kind(self):
|
||||
pass
|
||||
|
||||
|
||||
class GlobalScope(Scope):
|
||||
|
||||
def __init__(self, pycore, module):
|
||||
super(GlobalScope, self).__init__(pycore, module, None)
|
||||
self.names = module._get_concluded_data()
|
||||
|
||||
def get_start(self):
|
||||
return 1
|
||||
|
||||
def get_kind(self):
|
||||
return 'Module'
|
||||
|
||||
def get_name(self, name):
|
||||
try:
|
||||
return self.pyobject[name]
|
||||
except exceptions.AttributeNotFoundError:
|
||||
if name in self.builtin_names:
|
||||
return self.builtin_names[name]
|
||||
raise exceptions.NameNotFoundError('name %s not found' % name)
|
||||
|
||||
def get_names(self):
|
||||
if self.names.get() is None:
|
||||
result = dict(self.builtin_names)
|
||||
result.update(super(GlobalScope, self).get_names())
|
||||
self.names.set(result)
|
||||
return self.names.get()
|
||||
|
||||
def get_inner_scope_for_line(self, lineno, indents=None):
|
||||
return self._scope_finder.get_holding_scope(self, lineno, indents)
|
||||
|
||||
def get_inner_scope_for_offset(self, offset):
|
||||
return self._scope_finder.get_holding_scope_for_offset(self, offset)
|
||||
|
||||
@property
|
||||
@utils.saveit
|
||||
def _scope_finder(self):
|
||||
return _HoldingScopeFinder(self.pyobject)
|
||||
|
||||
@property
|
||||
def builtin_names(self):
|
||||
return rope.base.builtins.builtins.get_attributes()
|
||||
|
||||
|
||||
class FunctionScope(Scope):
|
||||
|
||||
def __init__(self, pycore, pyobject, visitor):
|
||||
super(FunctionScope, self).__init__(pycore, pyobject,
|
||||
pyobject.parent.get_scope())
|
||||
self.names = None
|
||||
self.returned_asts = None
|
||||
self.is_generator = None
|
||||
self.defineds = None
|
||||
self.visitor = visitor
|
||||
|
||||
def _get_names(self):
|
||||
if self.names is None:
|
||||
self._visit_function()
|
||||
return self.names
|
||||
|
||||
def _visit_function(self):
|
||||
if self.names is None:
|
||||
new_visitor = self.visitor(self.pycore, self.pyobject)
|
||||
for n in ast.get_child_nodes(self.pyobject.get_ast()):
|
||||
ast.walk(n, new_visitor)
|
||||
self.names = new_visitor.names
|
||||
self.names.update(self.pyobject.get_parameters())
|
||||
self.returned_asts = new_visitor.returned_asts
|
||||
self.is_generator = new_visitor.generator
|
||||
self.defineds = new_visitor.defineds
|
||||
|
||||
def _get_returned_asts(self):
|
||||
if self.names is None:
|
||||
self._visit_function()
|
||||
return self.returned_asts
|
||||
|
||||
def _is_generator(self):
|
||||
if self.is_generator is None:
|
||||
self._get_returned_asts()
|
||||
return self.is_generator
|
||||
|
||||
def get_names(self):
|
||||
return self._get_names()
|
||||
|
||||
def _create_scopes(self):
|
||||
if self.defineds is None:
|
||||
self._visit_function()
|
||||
return [pydefined.get_scope() for pydefined in self.defineds]
|
||||
|
||||
def get_kind(self):
|
||||
return 'Function'
|
||||
|
||||
def invalidate_data(self):
|
||||
for pyname in self.get_names().values():
|
||||
if isinstance(pyname, (rope.base.pynames.AssignedName,
|
||||
rope.base.pynames.EvaluatedName)):
|
||||
pyname.invalidate()
|
||||
|
||||
|
||||
class ClassScope(Scope):
|
||||
|
||||
def __init__(self, pycore, pyobject):
|
||||
super(ClassScope, self).__init__(pycore, pyobject,
|
||||
pyobject.parent.get_scope())
|
||||
|
||||
def get_kind(self):
|
||||
return 'Class'
|
||||
|
||||
def get_propagated_names(self):
|
||||
return {}
|
||||
|
||||
|
||||
class _HoldingScopeFinder(object):
|
||||
|
||||
def __init__(self, pymodule):
|
||||
self.pymodule = pymodule
|
||||
|
||||
def get_indents(self, lineno):
|
||||
return rope.base.codeanalyze.count_line_indents(
|
||||
self.lines.get_line(lineno))
|
||||
|
||||
def _get_scope_indents(self, scope):
|
||||
return self.get_indents(scope.get_start())
|
||||
|
||||
def get_holding_scope(self, module_scope, lineno, line_indents=None):
|
||||
if line_indents is None:
|
||||
line_indents = self.get_indents(lineno)
|
||||
current_scope = module_scope
|
||||
new_scope = current_scope
|
||||
while new_scope is not None and \
|
||||
(new_scope.get_kind() == 'Module' or
|
||||
self._get_scope_indents(new_scope) <= line_indents):
|
||||
current_scope = new_scope
|
||||
if current_scope.get_start() == lineno and \
|
||||
current_scope.get_kind() != 'Module':
|
||||
return current_scope
|
||||
new_scope = None
|
||||
for scope in current_scope.get_scopes():
|
||||
if scope.get_start() <= lineno:
|
||||
if lineno <= scope.get_end():
|
||||
new_scope = scope
|
||||
break
|
||||
else:
|
||||
break
|
||||
return current_scope
|
||||
|
||||
def _is_empty_line(self, lineno):
|
||||
line = self.lines.get_line(lineno)
|
||||
return line.strip() == '' or line.lstrip().startswith('#')
|
||||
|
||||
def _get_body_indents(self, scope):
|
||||
return self.get_indents(scope.get_body_start())
|
||||
|
||||
def get_holding_scope_for_offset(self, scope, offset):
|
||||
return self.get_holding_scope(
|
||||
scope, self.lines.get_line_number(offset))
|
||||
|
||||
def find_scope_end(self, scope):
|
||||
if not scope.parent:
|
||||
return self.lines.length()
|
||||
end = scope.pyobject.get_ast().body[-1].lineno
|
||||
scope_start = self.pymodule.logical_lines.logical_line_in(scope.start)
|
||||
if scope_start[1] >= end:
|
||||
# handling one-liners
|
||||
body_indents = self._get_scope_indents(scope) + 4
|
||||
else:
|
||||
body_indents = self._get_body_indents(scope)
|
||||
for l in self.logical_lines.generate_starts(
|
||||
min(end + 1, self.lines.length()), self.lines.length() + 1):
|
||||
if not self._is_empty_line(l):
|
||||
if self.get_indents(l) < body_indents:
|
||||
return end
|
||||
else:
|
||||
end = l
|
||||
return end
|
||||
|
||||
@property
|
||||
def lines(self):
|
||||
return self.pymodule.lines
|
||||
|
||||
@property
|
||||
def code(self):
|
||||
return self.pymodule.source_code
|
||||
|
||||
@property
|
||||
def logical_lines(self):
|
||||
return self.pymodule.logical_lines
|
||||
|
||||
|
||||
class TemporaryScope(Scope):
|
||||
"""Currently used for list comprehensions and generator expressions
|
||||
|
||||
These scopes do not appear in the `get_scopes()` method of their
|
||||
parent scopes.
|
||||
"""
|
||||
|
||||
def __init__(self, pycore, parent_scope, names):
|
||||
super(TemporaryScope, self).__init__(
|
||||
pycore, parent_scope.pyobject, parent_scope)
|
||||
self.names = names
|
||||
|
||||
def get_names(self):
|
||||
return self.names
|
||||
|
||||
def get_defined_names(self):
|
||||
return self.names
|
||||
|
||||
def _create_scopes(self):
|
||||
return []
|
||||
|
||||
def get_kind(self):
|
||||
return 'Temporary'
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
import os
|
||||
|
||||
|
||||
class ResourceObserver(object):
|
||||
"""Provides the interface for observing resources
|
||||
|
||||
`ResourceObserver`\s can be registered using `Project.
|
||||
add_observer()`. But most of the time `FilteredResourceObserver`
|
||||
should be used. `ResourceObserver`\s report all changes passed
|
||||
to them and they don't report changes to all resources. For
|
||||
example if a folder is removed, it only calls `removed()` for that
|
||||
folder and not its contents. You can use
|
||||
`FilteredResourceObserver` if you are interested in changes only
|
||||
to a list of resources. And you want changes to be reported on
|
||||
individual resources.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, changed=None, moved=None, created=None,
|
||||
removed=None, validate=None):
|
||||
self.changed = changed
|
||||
self.moved = moved
|
||||
self.created = created
|
||||
self.removed = removed
|
||||
self._validate = validate
|
||||
|
||||
def resource_changed(self, resource):
|
||||
"""It is called when the resource changes"""
|
||||
if self.changed is not None:
|
||||
self.changed(resource)
|
||||
|
||||
def resource_moved(self, resource, new_resource):
|
||||
"""It is called when a resource is moved"""
|
||||
if self.moved is not None:
|
||||
self.moved(resource, new_resource)
|
||||
|
||||
def resource_created(self, resource):
|
||||
"""Is called when a new resource is created"""
|
||||
if self.created is not None:
|
||||
self.created(resource)
|
||||
|
||||
def resource_removed(self, resource):
|
||||
"""Is called when a new resource is removed"""
|
||||
if self.removed is not None:
|
||||
self.removed(resource)
|
||||
|
||||
def validate(self, resource):
|
||||
"""Validate the existence of this resource and its children.
|
||||
|
||||
This function is called when rope need to update its resource
|
||||
cache about the files that might have been changed or removed
|
||||
by other processes.
|
||||
|
||||
"""
|
||||
if self._validate is not None:
|
||||
self._validate(resource)
|
||||
|
||||
|
||||
class FilteredResourceObserver(object):
|
||||
"""A useful decorator for `ResourceObserver`
|
||||
|
||||
Most resource observers have a list of resources and are
|
||||
interested only in changes to those files. This class satisfies
|
||||
this need. It dispatches resource changed and removed messages.
|
||||
It performs these tasks:
|
||||
|
||||
* Changes to files and folders are analyzed to check whether any
|
||||
of the interesting resources are changed or not. If they are,
|
||||
it reports these changes to `resource_observer` passed to the
|
||||
constructor.
|
||||
* When a resource is removed it checks whether any of the
|
||||
interesting resources are contained in that folder and reports
|
||||
them to `resource_observer`.
|
||||
* When validating a folder it validates all of the interesting
|
||||
files in that folder.
|
||||
|
||||
Since most resource observers are interested in a list of
|
||||
resources that change over time, `add_resource` and
|
||||
`remove_resource` might be useful.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, resource_observer, initial_resources=None,
|
||||
timekeeper=None):
|
||||
self.observer = resource_observer
|
||||
self.resources = {}
|
||||
if timekeeper is not None:
|
||||
self.timekeeper = timekeeper
|
||||
else:
|
||||
self.timekeeper = ChangeIndicator()
|
||||
if initial_resources is not None:
|
||||
for resource in initial_resources:
|
||||
self.add_resource(resource)
|
||||
|
||||
def add_resource(self, resource):
|
||||
"""Add a resource to the list of interesting resources"""
|
||||
if resource.exists():
|
||||
self.resources[resource] = self.timekeeper.get_indicator(resource)
|
||||
else:
|
||||
self.resources[resource] = None
|
||||
|
||||
def remove_resource(self, resource):
|
||||
"""Add a resource to the list of interesting resources"""
|
||||
if resource in self.resources:
|
||||
del self.resources[resource]
|
||||
|
||||
def clear_resources(self):
|
||||
"""Removes all registered resources"""
|
||||
self.resources.clear()
|
||||
|
||||
def resource_changed(self, resource):
|
||||
changes = _Changes()
|
||||
self._update_changes_caused_by_changed(changes, resource)
|
||||
self._perform_changes(changes)
|
||||
|
||||
def _update_changes_caused_by_changed(self, changes, changed):
|
||||
if changed in self.resources:
|
||||
changes.add_changed(changed)
|
||||
if self._is_parent_changed(changed):
|
||||
changes.add_changed(changed.parent)
|
||||
|
||||
def _update_changes_caused_by_moved(self, changes, resource,
|
||||
new_resource=None):
|
||||
if resource in self.resources:
|
||||
changes.add_removed(resource, new_resource)
|
||||
if new_resource in self.resources:
|
||||
changes.add_created(new_resource)
|
||||
if resource.is_folder():
|
||||
for file in list(self.resources):
|
||||
if resource.contains(file):
|
||||
new_file = self._calculate_new_resource(
|
||||
resource, new_resource, file)
|
||||
changes.add_removed(file, new_file)
|
||||
if self._is_parent_changed(resource):
|
||||
changes.add_changed(resource.parent)
|
||||
if new_resource is not None:
|
||||
if self._is_parent_changed(new_resource):
|
||||
changes.add_changed(new_resource.parent)
|
||||
|
||||
def _is_parent_changed(self, child):
|
||||
return child.parent in self.resources
|
||||
|
||||
def resource_moved(self, resource, new_resource):
|
||||
changes = _Changes()
|
||||
self._update_changes_caused_by_moved(changes, resource, new_resource)
|
||||
self._perform_changes(changes)
|
||||
|
||||
def resource_created(self, resource):
|
||||
changes = _Changes()
|
||||
self._update_changes_caused_by_created(changes, resource)
|
||||
self._perform_changes(changes)
|
||||
|
||||
def _update_changes_caused_by_created(self, changes, resource):
|
||||
if resource in self.resources:
|
||||
changes.add_created(resource)
|
||||
if self._is_parent_changed(resource):
|
||||
changes.add_changed(resource.parent)
|
||||
|
||||
def resource_removed(self, resource):
|
||||
changes = _Changes()
|
||||
self._update_changes_caused_by_moved(changes, resource)
|
||||
self._perform_changes(changes)
|
||||
|
||||
def _perform_changes(self, changes):
|
||||
for resource in changes.changes:
|
||||
self.observer.resource_changed(resource)
|
||||
self.resources[resource] = self.timekeeper.get_indicator(resource)
|
||||
for resource, new_resource in changes.moves.items():
|
||||
self.resources[resource] = None
|
||||
if new_resource is not None:
|
||||
self.observer.resource_moved(resource, new_resource)
|
||||
else:
|
||||
self.observer.resource_removed(resource)
|
||||
for resource in changes.creations:
|
||||
self.observer.resource_created(resource)
|
||||
self.resources[resource] = self.timekeeper.get_indicator(resource)
|
||||
|
||||
def validate(self, resource):
|
||||
changes = _Changes()
|
||||
for file in self._search_resource_moves(resource):
|
||||
if file in self.resources:
|
||||
self._update_changes_caused_by_moved(changes, file)
|
||||
for file in self._search_resource_changes(resource):
|
||||
if file in self.resources:
|
||||
self._update_changes_caused_by_changed(changes, file)
|
||||
for file in self._search_resource_creations(resource):
|
||||
if file in self.resources:
|
||||
changes.add_created(file)
|
||||
self._perform_changes(changes)
|
||||
|
||||
def _search_resource_creations(self, resource):
|
||||
creations = set()
|
||||
if resource in self.resources and resource.exists() and \
|
||||
self.resources[resource] is None:
|
||||
creations.add(resource)
|
||||
if resource.is_folder():
|
||||
for file in self.resources:
|
||||
if file.exists() and resource.contains(file) and \
|
||||
self.resources[file] is None:
|
||||
creations.add(file)
|
||||
return creations
|
||||
|
||||
def _search_resource_moves(self, resource):
|
||||
all_moved = set()
|
||||
if resource in self.resources and not resource.exists():
|
||||
all_moved.add(resource)
|
||||
if resource.is_folder():
|
||||
for file in self.resources:
|
||||
if resource.contains(file):
|
||||
if not file.exists():
|
||||
all_moved.add(file)
|
||||
moved = set(all_moved)
|
||||
for folder in [file for file in all_moved if file.is_folder()]:
|
||||
if folder in moved:
|
||||
for file in list(moved):
|
||||
if folder.contains(file):
|
||||
moved.remove(file)
|
||||
return moved
|
||||
|
||||
def _search_resource_changes(self, resource):
|
||||
changed = set()
|
||||
if resource in self.resources and self._is_changed(resource):
|
||||
changed.add(resource)
|
||||
if resource.is_folder():
|
||||
for file in self.resources:
|
||||
if file.exists() and resource.contains(file):
|
||||
if self._is_changed(file):
|
||||
changed.add(file)
|
||||
return changed
|
||||
|
||||
def _is_changed(self, resource):
|
||||
if self.resources[resource] is None:
|
||||
return False
|
||||
return self.resources[resource] != \
|
||||
self.timekeeper.get_indicator(resource)
|
||||
|
||||
def _calculate_new_resource(self, main, new_main, resource):
|
||||
if new_main is None:
|
||||
return None
|
||||
diff = resource.path[len(main.path):]
|
||||
return resource.project.get_resource(new_main.path + diff)
|
||||
|
||||
|
||||
class ChangeIndicator(object):
|
||||
|
||||
def get_indicator(self, resource):
|
||||
"""Return the modification time and size of a `Resource`."""
|
||||
path = resource.real_path
|
||||
# on dos, mtime does not change for a folder when files are added
|
||||
if os.name != 'posix' and os.path.isdir(path):
|
||||
return (os.path.getmtime(path),
|
||||
len(os.listdir(path)),
|
||||
os.path.getsize(path))
|
||||
return (os.path.getmtime(path),
|
||||
os.path.getsize(path))
|
||||
|
||||
|
||||
class _Changes(object):
|
||||
|
||||
def __init__(self):
|
||||
self.changes = set()
|
||||
self.creations = set()
|
||||
self.moves = {}
|
||||
|
||||
def add_changed(self, resource):
|
||||
self.changes.add(resource)
|
||||
|
||||
def add_removed(self, resource, new_resource=None):
|
||||
self.moves[resource] = new_resource
|
||||
|
||||
def add_created(self, resource):
|
||||
self.creations.add(resource)
|
||||
|
|
@ -1,243 +0,0 @@
|
|||
"""Files and folders in a project are represented as resource objects.
|
||||
|
||||
Files and folders are access through `Resource` objects. `Resource` has
|
||||
two subclasses: `File` and `Folder`. What we care about is that
|
||||
refactorings and `rope.base.change.Change`s use resources.
|
||||
|
||||
There are two options to create a `Resource` for a path in a project.
|
||||
Note that in these examples `path` is the path to a file or folder
|
||||
relative to the project's root. A project's root folder is represented
|
||||
by an empty string.
|
||||
|
||||
1) Use the `rope.base.Project.get_resource()` method. E.g.:
|
||||
|
||||
myresource = myproject.get_resource(path)
|
||||
|
||||
|
||||
2) Use the `rope.base.libutils` module. `libutils` has a function
|
||||
named `path_to_resource()`. It takes a project and a path:
|
||||
|
||||
from rope.base import libutils
|
||||
|
||||
myresource = libutils.path_to_resource(myproject, path)
|
||||
|
||||
Once we have a `Resource`, we can retrieve information from it, like
|
||||
getting the path relative to the project's root (via `path`), reading
|
||||
from and writing to the resource, moving the resource, etc.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from rope.base import change
|
||||
from rope.base import exceptions
|
||||
from rope.base import fscommands
|
||||
|
||||
|
||||
class Resource(object):
|
||||
"""Represents files and folders in a project"""
|
||||
|
||||
def __init__(self, project, path):
|
||||
self.project = project
|
||||
self._path = path
|
||||
|
||||
def move(self, new_location):
|
||||
"""Move resource to `new_location`"""
|
||||
self._perform_change(change.MoveResource(self, new_location),
|
||||
'Moving <%s> to <%s>' % (self.path, new_location))
|
||||
|
||||
def remove(self):
|
||||
"""Remove resource from the project"""
|
||||
self._perform_change(change.RemoveResource(self),
|
||||
'Removing <%s>' % self.path)
|
||||
|
||||
def is_folder(self):
|
||||
"""Return true if the resource is a folder"""
|
||||
|
||||
def create(self):
|
||||
"""Create this resource"""
|
||||
|
||||
def exists(self):
|
||||
return os.path.exists(self.real_path)
|
||||
|
||||
@property
|
||||
def parent(self):
|
||||
parent = '/'.join(self.path.split('/')[0:-1])
|
||||
return self.project.get_folder(parent)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""Return the path of this resource relative to the project root
|
||||
|
||||
The path is the list of parent directories separated by '/' followed
|
||||
by the resource name.
|
||||
"""
|
||||
return self._path
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of this resource"""
|
||||
return self.path.split('/')[-1]
|
||||
|
||||
@property
|
||||
def real_path(self):
|
||||
"""Return the file system path of this resource"""
|
||||
return self.project._get_resource_path(self.path)
|
||||
|
||||
def __eq__(self, obj):
|
||||
return self.__class__ == obj.__class__ and self.path == obj.path
|
||||
|
||||
def __ne__(self, obj):
|
||||
return not self.__eq__(obj)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.path)
|
||||
|
||||
def _perform_change(self, change_, description):
|
||||
changes = change.ChangeSet(description)
|
||||
changes.add_change(change_)
|
||||
self.project.do(changes)
|
||||
|
||||
|
||||
class File(Resource):
|
||||
"""Represents a file"""
|
||||
|
||||
def __init__(self, project, name):
|
||||
super(File, self).__init__(project, name)
|
||||
|
||||
def read(self):
|
||||
data = self.read_bytes()
|
||||
try:
|
||||
return fscommands.file_data_to_unicode(data)
|
||||
except UnicodeDecodeError as e:
|
||||
raise exceptions.ModuleDecodeError(self.path, e.reason)
|
||||
|
||||
def read_bytes(self):
|
||||
return open(self.real_path, 'rb').read()
|
||||
|
||||
def write(self, contents):
|
||||
try:
|
||||
if contents == self.read():
|
||||
return
|
||||
except IOError:
|
||||
pass
|
||||
self._perform_change(change.ChangeContents(self, contents),
|
||||
'Writing file <%s>' % self.path)
|
||||
|
||||
def is_folder(self):
|
||||
return False
|
||||
|
||||
def create(self):
|
||||
self.parent.create_file(self.name)
|
||||
|
||||
|
||||
class Folder(Resource):
|
||||
"""Represents a folder"""
|
||||
|
||||
def __init__(self, project, name):
|
||||
super(Folder, self).__init__(project, name)
|
||||
|
||||
def is_folder(self):
|
||||
return True
|
||||
|
||||
def get_children(self):
|
||||
"""Return the children of this folder"""
|
||||
try:
|
||||
children = os.listdir(self.real_path)
|
||||
except OSError:
|
||||
return []
|
||||
result = []
|
||||
for name in children:
|
||||
try:
|
||||
child = self.get_child(name)
|
||||
except exceptions.ResourceNotFoundError:
|
||||
continue
|
||||
if not self.project.is_ignored(child):
|
||||
result.append(self.get_child(name))
|
||||
return result
|
||||
|
||||
def create_file(self, file_name):
|
||||
self._perform_change(
|
||||
change.CreateFile(self, file_name),
|
||||
'Creating file <%s>' % self._get_child_path(file_name))
|
||||
return self.get_child(file_name)
|
||||
|
||||
def create_folder(self, folder_name):
|
||||
self._perform_change(
|
||||
change.CreateFolder(self, folder_name),
|
||||
'Creating folder <%s>' % self._get_child_path(folder_name))
|
||||
return self.get_child(folder_name)
|
||||
|
||||
def _get_child_path(self, name):
|
||||
if self.path:
|
||||
return self.path + '/' + name
|
||||
else:
|
||||
return name
|
||||
|
||||
def get_child(self, name):
|
||||
return self.project.get_resource(self._get_child_path(name))
|
||||
|
||||
def has_child(self, name):
|
||||
try:
|
||||
self.get_child(name)
|
||||
return True
|
||||
except exceptions.ResourceNotFoundError:
|
||||
return False
|
||||
|
||||
def get_files(self):
|
||||
return [resource for resource in self.get_children()
|
||||
if not resource.is_folder()]
|
||||
|
||||
def get_folders(self):
|
||||
return [resource for resource in self.get_children()
|
||||
if resource.is_folder()]
|
||||
|
||||
def contains(self, resource):
|
||||
if self == resource:
|
||||
return False
|
||||
return self.path == '' or resource.path.startswith(self.path + '/')
|
||||
|
||||
def create(self):
|
||||
self.parent.create_folder(self.name)
|
||||
|
||||
|
||||
class _ResourceMatcher(object):
|
||||
|
||||
def __init__(self):
|
||||
self.patterns = []
|
||||
self._compiled_patterns = []
|
||||
|
||||
def set_patterns(self, patterns):
|
||||
"""Specify which resources to match
|
||||
|
||||
`patterns` is a `list` of `str`\s that can contain ``*`` and
|
||||
``?`` signs for matching resource names.
|
||||
|
||||
"""
|
||||
self._compiled_patterns = None
|
||||
self.patterns = patterns
|
||||
|
||||
def _add_pattern(self, pattern):
|
||||
re_pattern = pattern.replace('.', '\\.').\
|
||||
replace('*', '[^/]*').replace('?', '[^/]').\
|
||||
replace('//', '/(.*/)?')
|
||||
re_pattern = '^(.*/)?' + re_pattern + '(/.*)?$'
|
||||
self.compiled_patterns.append(re.compile(re_pattern))
|
||||
|
||||
def does_match(self, resource):
|
||||
for pattern in self.compiled_patterns:
|
||||
if pattern.match(resource.path):
|
||||
return True
|
||||
path = os.path.join(resource.project.address,
|
||||
*resource.path.split('/'))
|
||||
if os.path.islink(path):
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def compiled_patterns(self):
|
||||
if self._compiled_patterns is None:
|
||||
self._compiled_patterns = []
|
||||
for pattern in self.patterns:
|
||||
self._add_pattern(pattern)
|
||||
return self._compiled_patterns
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
"""A module to ease code analysis
|
||||
|
||||
This module is here to help source code analysis.
|
||||
"""
|
||||
import re
|
||||
|
||||
from rope.base import codeanalyze, utils
|
||||
|
||||
|
||||
@utils.cached(7)
|
||||
def real_code(source):
|
||||
"""Simplify `source` for analysis
|
||||
|
||||
It replaces:
|
||||
|
||||
* comments with spaces
|
||||
* strs with a new str filled with spaces
|
||||
* implicit and explicit continuations with spaces
|
||||
* tabs and semicolons with spaces
|
||||
|
||||
The resulting code is a lot easier to analyze if we are interested
|
||||
only in offsets.
|
||||
"""
|
||||
collector = codeanalyze.ChangeCollector(source)
|
||||
for start, end in ignored_regions(source):
|
||||
if source[start] == '#':
|
||||
replacement = ' ' * (end - start)
|
||||
else:
|
||||
replacement = '"%s"' % (' ' * (end - start - 2))
|
||||
collector.add_change(start, end, replacement)
|
||||
source = collector.get_changed() or source
|
||||
collector = codeanalyze.ChangeCollector(source)
|
||||
parens = 0
|
||||
for match in _parens.finditer(source):
|
||||
i = match.start()
|
||||
c = match.group()
|
||||
if c in '({[':
|
||||
parens += 1
|
||||
if c in ')}]':
|
||||
parens -= 1
|
||||
if c == '\n' and parens > 0:
|
||||
collector.add_change(i, i + 1, ' ')
|
||||
source = collector.get_changed() or source
|
||||
return source.replace('\\\n', ' ').replace('\t', ' ').replace(';', '\n')
|
||||
|
||||
|
||||
@utils.cached(7)
|
||||
def ignored_regions(source):
|
||||
"""Return ignored regions like strings and comments in `source` """
|
||||
return [(match.start(), match.end()) for match in _str.finditer(source)]
|
||||
|
||||
|
||||
_str = re.compile('%s|%s' % (codeanalyze.get_comment_pattern(),
|
||||
codeanalyze.get_string_pattern()))
|
||||
_parens = re.compile(r'[\({\[\]}\)\n]')
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from rope.base import utils
|
||||
from rope.base.utils import pycompat
|
||||
|
||||
|
||||
def _stdlib_path():
|
||||
if pycompat.PY2:
|
||||
from distutils import sysconfig
|
||||
return sysconfig.get_python_lib(standard_lib=True,
|
||||
plat_specific=True)
|
||||
elif pycompat.PY3:
|
||||
import inspect
|
||||
return os.path.dirname(inspect.getsourcefile(inspect))
|
||||
|
||||
|
||||
@utils.cached(1)
|
||||
def standard_modules():
|
||||
return python_modules() | dynload_modules()
|
||||
|
||||
|
||||
@utils.cached(1)
|
||||
def python_modules():
|
||||
result = set()
|
||||
lib_path = _stdlib_path()
|
||||
if os.path.exists(lib_path):
|
||||
for name in os.listdir(lib_path):
|
||||
path = os.path.join(lib_path, name)
|
||||
if os.path.isdir(path):
|
||||
if '-' not in name:
|
||||
result.add(name)
|
||||
else:
|
||||
if name.endswith('.py'):
|
||||
result.add(name[:-3])
|
||||
return result
|
||||
|
||||
|
||||
def normalize_so_name(name):
|
||||
"""
|
||||
Handle different types of python installations
|
||||
"""
|
||||
if "cpython" in name:
|
||||
return os.path.splitext(os.path.splitext(name)[0])[0]
|
||||
return os.path.splitext(name)[0]
|
||||
|
||||
|
||||
@utils.cached(1)
|
||||
def dynload_modules():
|
||||
result = set(sys.builtin_module_names)
|
||||
dynload_path = os.path.join(_stdlib_path(), 'lib-dynload')
|
||||
if os.path.exists(dynload_path):
|
||||
for name in os.listdir(dynload_path):
|
||||
path = os.path.join(dynload_path, name)
|
||||
if os.path.isfile(path):
|
||||
if name.endswith('.dll'):
|
||||
result.add(normalize_so_name(name))
|
||||
if name.endswith('.so'):
|
||||
result.add(normalize_so_name(name))
|
||||
return result
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
from rope.base import exceptions
|
||||
|
||||
|
||||
class TaskHandle(object):
|
||||
|
||||
def __init__(self, name='Task', interrupts=True):
|
||||
"""Construct a TaskHandle
|
||||
|
||||
If `interrupts` is `False` the task won't be interrupted by
|
||||
calling `TaskHandle.stop()`.
|
||||
|
||||
"""
|
||||
self.name = name
|
||||
self.interrupts = interrupts
|
||||
self.stopped = False
|
||||
self.job_sets = []
|
||||
self.observers = []
|
||||
|
||||
def stop(self):
|
||||
"""Interrupts the refactoring"""
|
||||
if self.interrupts:
|
||||
self.stopped = True
|
||||
self._inform_observers()
|
||||
|
||||
def current_jobset(self):
|
||||
"""Return the current `JobSet`"""
|
||||
if self.job_sets:
|
||||
return self.job_sets[-1]
|
||||
|
||||
def add_observer(self, observer):
|
||||
"""Register an observer for this task handle
|
||||
|
||||
The observer is notified whenever the task is stopped or
|
||||
a job gets finished.
|
||||
|
||||
"""
|
||||
self.observers.append(observer)
|
||||
|
||||
def is_stopped(self):
|
||||
return self.stopped
|
||||
|
||||
def get_jobsets(self):
|
||||
return self.job_sets
|
||||
|
||||
def create_jobset(self, name='JobSet', count=None):
|
||||
result = JobSet(self, name=name, count=count)
|
||||
self.job_sets.append(result)
|
||||
self._inform_observers()
|
||||
return result
|
||||
|
||||
def _inform_observers(self):
|
||||
for observer in list(self.observers):
|
||||
observer()
|
||||
|
||||
|
||||
class JobSet(object):
|
||||
|
||||
def __init__(self, handle, name, count):
|
||||
self.handle = handle
|
||||
self.name = name
|
||||
self.count = count
|
||||
self.done = 0
|
||||
self.job_name = None
|
||||
|
||||
def started_job(self, name):
|
||||
self.check_status()
|
||||
self.job_name = name
|
||||
self.handle._inform_observers()
|
||||
|
||||
def finished_job(self):
|
||||
self.check_status()
|
||||
self.done += 1
|
||||
self.handle._inform_observers()
|
||||
self.job_name = None
|
||||
|
||||
def check_status(self):
|
||||
if self.handle.is_stopped():
|
||||
raise exceptions.InterruptedTaskError()
|
||||
|
||||
def get_active_job_name(self):
|
||||
return self.job_name
|
||||
|
||||
def get_percent_done(self):
|
||||
if self.count is not None and self.count > 0:
|
||||
percent = self.done * 100 // self.count
|
||||
return min(percent, 100)
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class NullTaskHandle(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def is_stopped(self):
|
||||
return False
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def create_jobset(self, *args, **kwds):
|
||||
return NullJobSet()
|
||||
|
||||
def get_jobsets(self):
|
||||
return []
|
||||
|
||||
def add_observer(self, observer):
|
||||
pass
|
||||
|
||||
|
||||
class NullJobSet(object):
|
||||
|
||||
def started_job(self, name):
|
||||
pass
|
||||
|
||||
def finished_job(self):
|
||||
pass
|
||||
|
||||
def check_status(self):
|
||||
pass
|
||||
|
||||
def get_active_job_name(self):
|
||||
pass
|
||||
|
||||
def get_percent_done(self):
|
||||
pass
|
||||
|
||||
def get_name(self):
|
||||
pass
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
import warnings
|
||||
|
||||
|
||||
def saveit(func):
|
||||
"""A decorator that caches the return value of a function"""
|
||||
|
||||
name = '_' + func.__name__
|
||||
|
||||
def _wrapper(self, *args, **kwds):
|
||||
if not hasattr(self, name):
|
||||
setattr(self, name, func(self, *args, **kwds))
|
||||
return getattr(self, name)
|
||||
return _wrapper
|
||||
|
||||
cacheit = saveit
|
||||
|
||||
|
||||
def prevent_recursion(default):
|
||||
"""A decorator that returns the return value of `default` in recursions"""
|
||||
def decorator(func):
|
||||
name = '_calling_%s_' % func.__name__
|
||||
|
||||
def newfunc(self, *args, **kwds):
|
||||
if getattr(self, name, False):
|
||||
return default()
|
||||
setattr(self, name, True)
|
||||
try:
|
||||
return func(self, *args, **kwds)
|
||||
finally:
|
||||
setattr(self, name, False)
|
||||
return newfunc
|
||||
return decorator
|
||||
|
||||
|
||||
def ignore_exception(exception_class):
|
||||
"""A decorator that ignores `exception_class` exceptions"""
|
||||
def _decorator(func):
|
||||
def newfunc(*args, **kwds):
|
||||
try:
|
||||
return func(*args, **kwds)
|
||||
except exception_class:
|
||||
pass
|
||||
return newfunc
|
||||
return _decorator
|
||||
|
||||
|
||||
def deprecated(message=None):
|
||||
"""A decorator for deprecated functions"""
|
||||
def _decorator(func, message=message):
|
||||
if message is None:
|
||||
message = '%s is deprecated' % func.__name__
|
||||
|
||||
def newfunc(*args, **kwds):
|
||||
warnings.warn(message, DeprecationWarning, stacklevel=2)
|
||||
return func(*args, **kwds)
|
||||
return newfunc
|
||||
return _decorator
|
||||
|
||||
|
||||
def cached(count):
|
||||
"""A caching decorator based on parameter objects"""
|
||||
def decorator(func):
|
||||
return _Cached(func, count)
|
||||
return decorator
|
||||
|
||||
|
||||
class _Cached(object):
|
||||
|
||||
def __init__(self, func, count):
|
||||
self.func = func
|
||||
self.cache = []
|
||||
self.count = count
|
||||
|
||||
def __call__(self, *args, **kwds):
|
||||
key = (args, kwds)
|
||||
for cached_key, cached_result in self.cache:
|
||||
if cached_key == key:
|
||||
return cached_result
|
||||
result = self.func(*args, **kwds)
|
||||
self.cache.append((key, result))
|
||||
if len(self.cache) > self.count:
|
||||
del self.cache[0]
|
||||
return result
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,67 +0,0 @@
|
|||
# this snippet was taken from this link
|
||||
# http://code.activestate.com/recipes/576694/
|
||||
|
||||
import collections
|
||||
|
||||
|
||||
class OrderedSet(collections.MutableSet):
|
||||
|
||||
def __init__(self, iterable=None):
|
||||
self.end = end = []
|
||||
end += [None, end, end] # sentinel
|
||||
# node for doubly linked list
|
||||
self.map = {} # key --> [key, prev, next]
|
||||
if iterable is not None:
|
||||
self |= iterable
|
||||
|
||||
def __len__(self):
|
||||
return len(self.map)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.map
|
||||
|
||||
def add(self, key):
|
||||
if key not in self.map:
|
||||
end = self.end
|
||||
curr = end[1]
|
||||
curr[2] = end[1] = self.map[key] = [key, curr, end]
|
||||
|
||||
def intersection(self, set_b):
|
||||
return OrderedSet([item for item in self if item in set_b])
|
||||
|
||||
def discard(self, key):
|
||||
if key in self.map:
|
||||
key, prev, next = self.map.pop(key)
|
||||
prev[2] = next
|
||||
next[1] = prev
|
||||
|
||||
def __iter__(self):
|
||||
end = self.end
|
||||
curr = end[2]
|
||||
while curr is not end:
|
||||
yield curr[0]
|
||||
curr = curr[2]
|
||||
|
||||
def __reversed__(self):
|
||||
end = self.end
|
||||
curr = end[1]
|
||||
while curr is not end:
|
||||
yield curr[0]
|
||||
curr = curr[1]
|
||||
|
||||
def pop(self, last=True):
|
||||
if not self:
|
||||
raise KeyError('set is empty')
|
||||
key = self.end[1][0] if last else self.end[2][0]
|
||||
self.discard(key)
|
||||
return key
|
||||
|
||||
def __repr__(self):
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, list(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, OrderedSet):
|
||||
return len(self) == len(other) and list(self) == list(other)
|
||||
return set(self) == set(other)
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
import sys
|
||||
import _ast
|
||||
# from rope.base import ast
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY27 = sys.version_info[0:2] >= (2, 7)
|
||||
PY3 = sys.version_info[0] == 3
|
||||
PY34 = sys.version_info[0:2] >= (3, 4)
|
||||
|
||||
try:
|
||||
str = unicode
|
||||
except NameError: # PY3
|
||||
|
||||
str = str
|
||||
string_types = (str,)
|
||||
import builtins
|
||||
ast_arg_type = _ast.arg
|
||||
|
||||
def execfile(fn, global_vars=None, local_vars=None):
|
||||
with open(fn) as f:
|
||||
code = compile(f.read(), fn, 'exec')
|
||||
exec(code, global_vars or {}, local_vars)
|
||||
|
||||
def get_ast_arg_arg(node):
|
||||
if isinstance(node, string_types): # TODO: G21: Understand the Algorithm (Where it's used?)
|
||||
return node
|
||||
return node.arg
|
||||
|
||||
def get_ast_with_items(node):
|
||||
return node.items
|
||||
|
||||
else: # PY2
|
||||
|
||||
string_types = (basestring,)
|
||||
builtins = __import__('__builtin__')
|
||||
ast_arg_type = _ast.Name
|
||||
execfile = execfile
|
||||
|
||||
def get_ast_arg_arg(node):
|
||||
if isinstance(node, string_types): # Python2 arguments.vararg, arguments.kwarg
|
||||
return node
|
||||
return node.id
|
||||
|
||||
def get_ast_with_items(node):
|
||||
return [node]
|
||||
|
|
@ -1,525 +0,0 @@
|
|||
import bisect
|
||||
import keyword
|
||||
|
||||
import rope.base.simplify
|
||||
|
||||
|
||||
def get_name_at(resource, offset):
|
||||
source_code = resource.read()
|
||||
word_finder = Worder(source_code)
|
||||
return word_finder.get_word_at(offset)
|
||||
|
||||
|
||||
class Worder(object):
|
||||
"""A class for finding boundaries of words and expressions
|
||||
|
||||
Note that in these methods, offset should be the index of the
|
||||
character not the index of the character after it.
|
||||
"""
|
||||
|
||||
def __init__(self, code, handle_ignores=False):
|
||||
simplified = rope.base.simplify.real_code(code)
|
||||
self.code_finder = _RealFinder(simplified, code)
|
||||
self.handle_ignores = handle_ignores
|
||||
self.code = code
|
||||
|
||||
def _init_ignores(self):
|
||||
ignores = rope.base.simplify.ignored_regions(self.code)
|
||||
self.dumb_finder = _RealFinder(self.code, self.code)
|
||||
self.starts = [ignored[0] for ignored in ignores]
|
||||
self.ends = [ignored[1] for ignored in ignores]
|
||||
|
||||
def _context_call(self, name, offset):
|
||||
if self.handle_ignores:
|
||||
if not hasattr(self, 'starts'):
|
||||
self._init_ignores()
|
||||
start = bisect.bisect(self.starts, offset)
|
||||
if start > 0 and offset < self.ends[start - 1]:
|
||||
return getattr(self.dumb_finder, name)(offset)
|
||||
return getattr(self.code_finder, name)(offset)
|
||||
|
||||
def get_primary_at(self, offset):
|
||||
return self._context_call('get_primary_at', offset)
|
||||
|
||||
def get_word_at(self, offset):
|
||||
return self._context_call('get_word_at', offset)
|
||||
|
||||
def get_primary_range(self, offset):
|
||||
return self._context_call('get_primary_range', offset)
|
||||
|
||||
def get_splitted_primary_before(self, offset):
|
||||
return self._context_call('get_splitted_primary_before', offset)
|
||||
|
||||
def get_word_range(self, offset):
|
||||
return self._context_call('get_word_range', offset)
|
||||
|
||||
def is_function_keyword_parameter(self, offset):
|
||||
return self.code_finder.is_function_keyword_parameter(offset)
|
||||
|
||||
def is_a_class_or_function_name_in_header(self, offset):
|
||||
return self.code_finder.is_a_class_or_function_name_in_header(offset)
|
||||
|
||||
def is_from_statement_module(self, offset):
|
||||
return self.code_finder.is_from_statement_module(offset)
|
||||
|
||||
def is_from_aliased(self, offset):
|
||||
return self.code_finder.is_from_aliased(offset)
|
||||
|
||||
def find_parens_start_from_inside(self, offset):
|
||||
return self.code_finder.find_parens_start_from_inside(offset)
|
||||
|
||||
def is_a_name_after_from_import(self, offset):
|
||||
return self.code_finder.is_a_name_after_from_import(offset)
|
||||
|
||||
def is_from_statement(self, offset):
|
||||
return self.code_finder.is_from_statement(offset)
|
||||
|
||||
def get_from_aliased(self, offset):
|
||||
return self.code_finder.get_from_aliased(offset)
|
||||
|
||||
def is_import_statement(self, offset):
|
||||
return self.code_finder.is_import_statement(offset)
|
||||
|
||||
def is_assigned_here(self, offset):
|
||||
return self.code_finder.is_assigned_here(offset)
|
||||
|
||||
def is_a_function_being_called(self, offset):
|
||||
return self.code_finder.is_a_function_being_called(offset)
|
||||
|
||||
def get_word_parens_range(self, offset):
|
||||
return self.code_finder.get_word_parens_range(offset)
|
||||
|
||||
def is_name_assigned_in_class_body(self, offset):
|
||||
return self.code_finder.is_name_assigned_in_class_body(offset)
|
||||
|
||||
def is_on_function_call_keyword(self, offset):
|
||||
return self.code_finder.is_on_function_call_keyword(offset)
|
||||
|
||||
def _find_parens_start(self, offset):
|
||||
return self.code_finder._find_parens_start(offset)
|
||||
|
||||
def get_parameters(self, first, last):
|
||||
return self.code_finder.get_parameters(first, last)
|
||||
|
||||
def get_from_module(self, offset):
|
||||
return self.code_finder.get_from_module(offset)
|
||||
|
||||
def is_assigned_in_a_tuple_assignment(self, offset):
|
||||
return self.code_finder.is_assigned_in_a_tuple_assignment(offset)
|
||||
|
||||
def get_assignment_type(self, offset):
|
||||
return self.code_finder.get_assignment_type(offset)
|
||||
|
||||
def get_function_and_args_in_header(self, offset):
|
||||
return self.code_finder.get_function_and_args_in_header(offset)
|
||||
|
||||
def get_lambda_and_args(self, offset):
|
||||
return self.code_finder.get_lambda_and_args(offset)
|
||||
|
||||
def find_function_offset(self, offset):
|
||||
return self.code_finder.find_function_offset(offset)
|
||||
|
||||
|
||||
class _RealFinder(object):
|
||||
|
||||
def __init__(self, code, raw):
|
||||
self.code = code
|
||||
self.raw = raw
|
||||
|
||||
def _find_word_start(self, offset):
|
||||
current_offset = offset
|
||||
while current_offset >= 0 and self._is_id_char(current_offset):
|
||||
current_offset -= 1
|
||||
return current_offset + 1
|
||||
|
||||
def _find_word_end(self, offset):
|
||||
while offset + 1 < len(self.code) and self._is_id_char(offset + 1):
|
||||
offset += 1
|
||||
return offset
|
||||
|
||||
def _find_last_non_space_char(self, offset):
|
||||
while offset >= 0 and self.code[offset].isspace():
|
||||
if self.code[offset] == '\n':
|
||||
return offset
|
||||
offset -= 1
|
||||
return max(-1, offset)
|
||||
|
||||
def get_word_at(self, offset):
|
||||
offset = self._get_fixed_offset(offset)
|
||||
return self.raw[self._find_word_start(offset):
|
||||
self._find_word_end(offset) + 1]
|
||||
|
||||
def _get_fixed_offset(self, offset):
|
||||
if offset >= len(self.code):
|
||||
return offset - 1
|
||||
if not self._is_id_char(offset):
|
||||
if offset > 0 and self._is_id_char(offset - 1):
|
||||
return offset - 1
|
||||
if offset < len(self.code) - 1 and self._is_id_char(offset + 1):
|
||||
return offset + 1
|
||||
return offset
|
||||
|
||||
def _is_id_char(self, offset):
|
||||
return self.code[offset].isalnum() or self.code[offset] == '_'
|
||||
|
||||
def _find_string_start(self, offset):
|
||||
kind = self.code[offset]
|
||||
try:
|
||||
return self.code.rindex(kind, 0, offset)
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
def _find_parens_start(self, offset):
|
||||
offset = self._find_last_non_space_char(offset - 1)
|
||||
while offset >= 0 and self.code[offset] not in '[({':
|
||||
if self.code[offset] not in ':,':
|
||||
offset = self._find_primary_start(offset)
|
||||
offset = self._find_last_non_space_char(offset - 1)
|
||||
return offset
|
||||
|
||||
def _find_atom_start(self, offset):
|
||||
old_offset = offset
|
||||
if self.code[offset] == '\n':
|
||||
return offset + 1
|
||||
if self.code[offset].isspace():
|
||||
offset = self._find_last_non_space_char(offset)
|
||||
if self.code[offset] in '\'"':
|
||||
return self._find_string_start(offset)
|
||||
if self.code[offset] in ')]}':
|
||||
return self._find_parens_start(offset)
|
||||
if self._is_id_char(offset):
|
||||
return self._find_word_start(offset)
|
||||
return old_offset
|
||||
|
||||
def _find_primary_without_dot_start(self, offset):
|
||||
"""It tries to find the undotted primary start
|
||||
|
||||
It is different from `self._get_atom_start()` in that it
|
||||
follows function calls, too; such as in ``f(x)``.
|
||||
|
||||
"""
|
||||
last_atom = offset
|
||||
offset = self._find_last_non_space_char(last_atom)
|
||||
while offset > 0 and self.code[offset] in ')]':
|
||||
last_atom = self._find_parens_start(offset)
|
||||
offset = self._find_last_non_space_char(last_atom - 1)
|
||||
if offset >= 0 and (self.code[offset] in '"\'})]' or
|
||||
self._is_id_char(offset)):
|
||||
atom_start = self._find_atom_start(offset)
|
||||
if not keyword.iskeyword(self.code[atom_start:offset + 1]):
|
||||
return atom_start
|
||||
return last_atom
|
||||
|
||||
def _find_primary_start(self, offset):
|
||||
if offset >= len(self.code):
|
||||
offset = len(self.code) - 1
|
||||
if self.code[offset] != '.':
|
||||
offset = self._find_primary_without_dot_start(offset)
|
||||
else:
|
||||
offset = offset + 1
|
||||
while offset > 0:
|
||||
prev = self._find_last_non_space_char(offset - 1)
|
||||
if offset <= 0 or self.code[prev] != '.':
|
||||
break
|
||||
offset = self._find_primary_without_dot_start(prev - 1)
|
||||
if not self._is_id_char(offset):
|
||||
break
|
||||
|
||||
return offset
|
||||
|
||||
def get_primary_at(self, offset):
|
||||
offset = self._get_fixed_offset(offset)
|
||||
start, end = self.get_primary_range(offset)
|
||||
return self.raw[start:end].strip()
|
||||
|
||||
def get_splitted_primary_before(self, offset):
|
||||
"""returns expression, starting, starting_offset
|
||||
|
||||
This function is used in `rope.codeassist.assist` function.
|
||||
"""
|
||||
if offset == 0:
|
||||
return ('', '', 0)
|
||||
end = offset - 1
|
||||
word_start = self._find_atom_start(end)
|
||||
real_start = self._find_primary_start(end)
|
||||
if self.code[word_start:offset].strip() == '':
|
||||
word_start = end
|
||||
if self.code[end].isspace():
|
||||
word_start = end
|
||||
if self.code[real_start:word_start].strip() == '':
|
||||
real_start = word_start
|
||||
if real_start == word_start == end and not self._is_id_char(end):
|
||||
return ('', '', offset)
|
||||
if real_start == word_start:
|
||||
return ('', self.raw[word_start:offset], word_start)
|
||||
else:
|
||||
if self.code[end] == '.':
|
||||
return (self.raw[real_start:end], '', offset)
|
||||
last_dot_position = word_start
|
||||
if self.code[word_start] != '.':
|
||||
last_dot_position = \
|
||||
self._find_last_non_space_char(word_start - 1)
|
||||
last_char_position = \
|
||||
self._find_last_non_space_char(last_dot_position - 1)
|
||||
if self.code[word_start].isspace():
|
||||
word_start = offset
|
||||
return (self.raw[real_start:last_char_position + 1],
|
||||
self.raw[word_start:offset], word_start)
|
||||
|
||||
def _get_line_start(self, offset):
|
||||
try:
|
||||
return self.code.rindex('\n', 0, offset + 1)
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
def _get_line_end(self, offset):
|
||||
try:
|
||||
return self.code.index('\n', offset)
|
||||
except ValueError:
|
||||
return len(self.code)
|
||||
|
||||
def is_name_assigned_in_class_body(self, offset):
|
||||
word_start = self._find_word_start(offset - 1)
|
||||
word_end = self._find_word_end(offset) + 1
|
||||
if '.' in self.code[word_start:word_end]:
|
||||
return False
|
||||
line_start = self._get_line_start(word_start)
|
||||
line = self.code[line_start:word_start].strip()
|
||||
return not line and self.get_assignment_type(offset) == '='
|
||||
|
||||
def is_a_class_or_function_name_in_header(self, offset):
|
||||
word_start = self._find_word_start(offset - 1)
|
||||
line_start = self._get_line_start(word_start)
|
||||
prev_word = self.code[line_start:word_start].strip()
|
||||
return prev_word in ['def', 'class']
|
||||
|
||||
def _find_first_non_space_char(self, offset):
|
||||
if offset >= len(self.code):
|
||||
return len(self.code)
|
||||
while offset < len(self.code) and self.code[offset].isspace():
|
||||
if self.code[offset] == '\n':
|
||||
return offset
|
||||
offset += 1
|
||||
return offset
|
||||
|
||||
def is_a_function_being_called(self, offset):
|
||||
word_end = self._find_word_end(offset) + 1
|
||||
next_char = self._find_first_non_space_char(word_end)
|
||||
return next_char < len(self.code) and \
|
||||
self.code[next_char] == '(' and \
|
||||
not self.is_a_class_or_function_name_in_header(offset)
|
||||
|
||||
def _find_import_end(self, start):
|
||||
return self._get_line_end(start)
|
||||
|
||||
def is_import_statement(self, offset):
|
||||
try:
|
||||
last_import = self.code.rindex('import ', 0, offset)
|
||||
except ValueError:
|
||||
return False
|
||||
return self._find_import_end(last_import + 7) >= offset
|
||||
|
||||
def is_from_statement(self, offset):
|
||||
try:
|
||||
last_from = self.code.rindex('from ', 0, offset)
|
||||
from_import = self.code.index(' import ', last_from)
|
||||
from_names = from_import + 8
|
||||
except ValueError:
|
||||
return False
|
||||
from_names = self._find_first_non_space_char(from_names)
|
||||
return self._find_import_end(from_names) >= offset
|
||||
|
||||
def is_from_statement_module(self, offset):
|
||||
if offset >= len(self.code) - 1:
|
||||
return False
|
||||
stmt_start = self._find_primary_start(offset)
|
||||
line_start = self._get_line_start(stmt_start)
|
||||
prev_word = self.code[line_start:stmt_start].strip()
|
||||
return prev_word == 'from'
|
||||
|
||||
def is_a_name_after_from_import(self, offset):
|
||||
try:
|
||||
if len(self.code) > offset and self.code[offset] == '\n':
|
||||
line_start = self._get_line_start(offset - 1)
|
||||
else:
|
||||
line_start = self._get_line_start(offset)
|
||||
last_from = self.code.rindex('from ', line_start, offset)
|
||||
from_import = self.code.index(' import ', last_from)
|
||||
from_names = from_import + 8
|
||||
except ValueError:
|
||||
return False
|
||||
if from_names - 1 > offset:
|
||||
return False
|
||||
return self._find_import_end(from_names) >= offset
|
||||
|
||||
def get_from_module(self, offset):
|
||||
try:
|
||||
last_from = self.code.rindex('from ', 0, offset)
|
||||
import_offset = self.code.index(' import ', last_from)
|
||||
end = self._find_last_non_space_char(import_offset)
|
||||
return self.get_primary_at(end)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def is_from_aliased(self, offset):
|
||||
if not self.is_a_name_after_from_import(offset):
|
||||
return False
|
||||
try:
|
||||
end = self._find_word_end(offset)
|
||||
as_end = min(self._find_word_end(end + 1), len(self.code))
|
||||
as_start = self._find_word_start(as_end)
|
||||
if self.code[as_start:as_end + 1] == 'as':
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def get_from_aliased(self, offset):
|
||||
try:
|
||||
end = self._find_word_end(offset)
|
||||
as_ = self._find_word_end(end + 1)
|
||||
alias = self._find_word_end(as_ + 1)
|
||||
start = self._find_word_start(alias)
|
||||
return self.raw[start:alias + 1]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def is_function_keyword_parameter(self, offset):
|
||||
word_end = self._find_word_end(offset)
|
||||
if word_end + 1 == len(self.code):
|
||||
return False
|
||||
next_char = self._find_first_non_space_char(word_end + 1)
|
||||
equals = self.code[next_char:next_char + 2]
|
||||
if equals == '==' or not equals.startswith('='):
|
||||
return False
|
||||
word_start = self._find_word_start(offset)
|
||||
prev_char = self._find_last_non_space_char(word_start - 1)
|
||||
return prev_char - 1 >= 0 and self.code[prev_char] in ',('
|
||||
|
||||
def is_on_function_call_keyword(self, offset):
|
||||
stop = self._get_line_start(offset)
|
||||
if self._is_id_char(offset):
|
||||
offset = self._find_word_start(offset) - 1
|
||||
offset = self._find_last_non_space_char(offset)
|
||||
if offset <= stop or self.code[offset] not in '(,':
|
||||
return False
|
||||
parens_start = self.find_parens_start_from_inside(offset)
|
||||
return stop < parens_start
|
||||
|
||||
def find_parens_start_from_inside(self, offset):
|
||||
stop = self._get_line_start(offset)
|
||||
while offset > stop:
|
||||
if self.code[offset] == '(':
|
||||
break
|
||||
if self.code[offset] != ',':
|
||||
offset = self._find_primary_start(offset)
|
||||
offset -= 1
|
||||
return max(stop, offset)
|
||||
|
||||
def is_assigned_here(self, offset):
|
||||
return self.get_assignment_type(offset) is not None
|
||||
|
||||
def get_assignment_type(self, offset):
|
||||
# XXX: does not handle tuple assignments
|
||||
word_end = self._find_word_end(offset)
|
||||
next_char = self._find_first_non_space_char(word_end + 1)
|
||||
single = self.code[next_char:next_char + 1]
|
||||
double = self.code[next_char:next_char + 2]
|
||||
triple = self.code[next_char:next_char + 3]
|
||||
if double not in ('==', '<=', '>=', '!='):
|
||||
for op in [single, double, triple]:
|
||||
if op.endswith('='):
|
||||
return op
|
||||
|
||||
def get_primary_range(self, offset):
|
||||
start = self._find_primary_start(offset)
|
||||
end = self._find_word_end(offset) + 1
|
||||
return (start, end)
|
||||
|
||||
def get_word_range(self, offset):
|
||||
offset = max(0, offset)
|
||||
start = self._find_word_start(offset)
|
||||
end = self._find_word_end(offset) + 1
|
||||
return (start, end)
|
||||
|
||||
def get_word_parens_range(self, offset, opening='(', closing=')'):
|
||||
end = self._find_word_end(offset)
|
||||
start_parens = self.code.index(opening, end)
|
||||
index = start_parens
|
||||
open_count = 0
|
||||
while index < len(self.code):
|
||||
if self.code[index] == opening:
|
||||
open_count += 1
|
||||
if self.code[index] == closing:
|
||||
open_count -= 1
|
||||
if open_count == 0:
|
||||
return (start_parens, index + 1)
|
||||
index += 1
|
||||
return (start_parens, index)
|
||||
|
||||
def get_parameters(self, first, last):
|
||||
keywords = []
|
||||
args = []
|
||||
current = self._find_last_non_space_char(last - 1)
|
||||
while current > first:
|
||||
primary_start = current
|
||||
current = self._find_primary_start(current)
|
||||
while current != first and self.code[current] not in '=,':
|
||||
current = self._find_last_non_space_char(current - 1)
|
||||
primary = self.raw[current + 1:primary_start + 1].strip()
|
||||
if self.code[current] == '=':
|
||||
primary_start = current - 1
|
||||
current -= 1
|
||||
while current != first and self.code[current] not in ',':
|
||||
current = self._find_last_non_space_char(current - 1)
|
||||
param_name = self.raw[current + 1:primary_start + 1].strip()
|
||||
keywords.append((param_name, primary))
|
||||
else:
|
||||
args.append(primary)
|
||||
current = self._find_last_non_space_char(current - 1)
|
||||
args.reverse()
|
||||
keywords.reverse()
|
||||
return args, keywords
|
||||
|
||||
def is_assigned_in_a_tuple_assignment(self, offset):
|
||||
start = self._get_line_start(offset)
|
||||
end = self._get_line_end(offset)
|
||||
primary_start = self._find_primary_start(offset)
|
||||
primary_end = self._find_word_end(offset)
|
||||
|
||||
prev_char_offset = self._find_last_non_space_char(primary_start - 1)
|
||||
next_char_offset = self._find_first_non_space_char(primary_end + 1)
|
||||
next_char = prev_char = ''
|
||||
if prev_char_offset >= start:
|
||||
prev_char = self.code[prev_char_offset]
|
||||
if next_char_offset < end:
|
||||
next_char = self.code[next_char_offset]
|
||||
try:
|
||||
equals_offset = self.code.index('=', start, end)
|
||||
except ValueError:
|
||||
return False
|
||||
if prev_char not in '(,' and next_char not in ',)':
|
||||
return False
|
||||
parens_start = self.find_parens_start_from_inside(offset)
|
||||
# XXX: only handling (x, y) = value
|
||||
return offset < equals_offset and \
|
||||
self.code[start:parens_start].strip() == ''
|
||||
|
||||
def get_function_and_args_in_header(self, offset):
|
||||
offset = self.find_function_offset(offset)
|
||||
lparens, rparens = self.get_word_parens_range(offset)
|
||||
return self.raw[offset:rparens + 1]
|
||||
|
||||
def find_function_offset(self, offset, definition='def '):
|
||||
while True:
|
||||
offset = self.code.index(definition, offset)
|
||||
if offset == 0 or not self._is_id_char(offset - 1):
|
||||
break
|
||||
offset += 1
|
||||
def_ = offset + 4
|
||||
return self._find_first_non_space_char(def_)
|
||||
|
||||
def get_lambda_and_args(self, offset):
|
||||
offset = self.find_function_offset(offset, definition='lambda ')
|
||||
lparens, rparens = self.get_word_parens_range(offset, opening=' ',
|
||||
closing=':')
|
||||
return self.raw[offset:rparens + 1]
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
"""rope IDE tools package
|
||||
|
||||
This package contains modules that can be used in IDEs
|
||||
but do not depend on the UI. So these modules will be used
|
||||
by `rope.ui` modules.
|
||||
|
||||
"""
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,222 +0,0 @@
|
|||
import re
|
||||
|
||||
from rope.base import builtins
|
||||
from rope.base import exceptions
|
||||
from rope.base import libutils
|
||||
from rope.base import pynames
|
||||
from rope.base import pyobjects
|
||||
from rope.base import resources
|
||||
from rope.base import resourceobserver
|
||||
from rope.base import taskhandle
|
||||
from rope.refactor import importutils
|
||||
|
||||
|
||||
class AutoImport(object):
|
||||
"""A class for finding the module that provides a name
|
||||
|
||||
This class maintains a cache of global names in python modules.
|
||||
Note that this cache is not accurate and might be out of date.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, project, observe=True, underlined=False):
|
||||
"""Construct an AutoImport object
|
||||
|
||||
If `observe` is `True`, listen for project changes and update
|
||||
the cache.
|
||||
|
||||
If `underlined` is `True`, underlined names are cached, too.
|
||||
"""
|
||||
self.project = project
|
||||
self.underlined = underlined
|
||||
self.names = project.data_files.read_data('globalnames')
|
||||
if self.names is None:
|
||||
self.names = {}
|
||||
project.data_files.add_write_hook(self._write)
|
||||
# XXX: using a filtered observer
|
||||
observer = resourceobserver.ResourceObserver(
|
||||
changed=self._changed, moved=self._moved, removed=self._removed)
|
||||
if observe:
|
||||
project.add_observer(observer)
|
||||
|
||||
def import_assist(self, starting):
|
||||
"""Return a list of ``(name, module)`` tuples
|
||||
|
||||
This function tries to find modules that have a global name
|
||||
that starts with `starting`.
|
||||
"""
|
||||
# XXX: breaking if gave up! use generators
|
||||
result = []
|
||||
for module in self.names:
|
||||
for global_name in self.names[module]:
|
||||
if global_name.startswith(starting):
|
||||
result.append((global_name, module))
|
||||
return result
|
||||
|
||||
def get_modules(self, name):
|
||||
"""Return the list of modules that have global `name`"""
|
||||
result = []
|
||||
for module in self.names:
|
||||
if name in self.names[module]:
|
||||
result.append(module)
|
||||
return result
|
||||
|
||||
def get_all_names(self):
|
||||
"""Return the list of all cached global names"""
|
||||
result = set()
|
||||
for module in self.names:
|
||||
result.update(set(self.names[module]))
|
||||
return result
|
||||
|
||||
def get_name_locations(self, name):
|
||||
"""Return a list of ``(resource, lineno)`` tuples"""
|
||||
result = []
|
||||
for module in self.names:
|
||||
if name in self.names[module]:
|
||||
try:
|
||||
pymodule = self.project.get_module(module)
|
||||
if name in pymodule:
|
||||
pyname = pymodule[name]
|
||||
module, lineno = pyname.get_definition_location()
|
||||
if module is not None:
|
||||
resource = module.get_module().get_resource()
|
||||
if resource is not None and lineno is not None:
|
||||
result.append((resource, lineno))
|
||||
except exceptions.ModuleNotFoundError:
|
||||
pass
|
||||
return result
|
||||
|
||||
def generate_cache(self, resources=None, underlined=None,
|
||||
task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Generate global name cache for project files
|
||||
|
||||
If `resources` is a list of `rope.base.resource.File`\s, only
|
||||
those files are searched; otherwise all python modules in the
|
||||
project are cached.
|
||||
|
||||
"""
|
||||
if resources is None:
|
||||
resources = self.project.get_python_files()
|
||||
job_set = task_handle.create_jobset(
|
||||
'Generatig autoimport cache', len(resources))
|
||||
for file in resources:
|
||||
job_set.started_job('Working on <%s>' % file.path)
|
||||
self.update_resource(file, underlined)
|
||||
job_set.finished_job()
|
||||
|
||||
def generate_modules_cache(self, modules, underlined=None,
|
||||
task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Generate global name cache for modules listed in `modules`"""
|
||||
job_set = task_handle.create_jobset(
|
||||
'Generatig autoimport cache for modules', len(modules))
|
||||
for modname in modules:
|
||||
job_set.started_job('Working on <%s>' % modname)
|
||||
if modname.endswith('.*'):
|
||||
mod = self.project.find_module(modname[:-2])
|
||||
if mod:
|
||||
for sub in submodules(mod):
|
||||
self.update_resource(sub, underlined)
|
||||
else:
|
||||
self.update_module(modname, underlined)
|
||||
job_set.finished_job()
|
||||
|
||||
def clear_cache(self):
|
||||
"""Clear all entries in global-name cache
|
||||
|
||||
It might be a good idea to use this function before
|
||||
regenerating global names.
|
||||
|
||||
"""
|
||||
self.names.clear()
|
||||
|
||||
def find_insertion_line(self, code):
|
||||
"""Guess at what line the new import should be inserted"""
|
||||
match = re.search(r'^(def|class)\s+', code)
|
||||
if match is not None:
|
||||
code = code[:match.start()]
|
||||
try:
|
||||
pymodule = libutils.get_string_module(self.project, code)
|
||||
except exceptions.ModuleSyntaxError:
|
||||
return 1
|
||||
testmodname = '__rope_testmodule_rope'
|
||||
importinfo = importutils.NormalImport(((testmodname, None),))
|
||||
module_imports = importutils.get_module_imports(self.project,
|
||||
pymodule)
|
||||
module_imports.add_import(importinfo)
|
||||
code = module_imports.get_changed_source()
|
||||
offset = code.index(testmodname)
|
||||
lineno = code.count('\n', 0, offset) + 1
|
||||
return lineno
|
||||
|
||||
def update_resource(self, resource, underlined=None):
|
||||
"""Update the cache for global names in `resource`"""
|
||||
try:
|
||||
pymodule = self.project.get_pymodule(resource)
|
||||
modname = self._module_name(resource)
|
||||
self._add_names(pymodule, modname, underlined)
|
||||
except exceptions.ModuleSyntaxError:
|
||||
pass
|
||||
|
||||
def update_module(self, modname, underlined=None):
|
||||
"""Update the cache for global names in `modname` module
|
||||
|
||||
`modname` is the name of a module.
|
||||
"""
|
||||
try:
|
||||
pymodule = self.project.get_module(modname)
|
||||
self._add_names(pymodule, modname, underlined)
|
||||
except exceptions.ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
def _module_name(self, resource):
|
||||
return libutils.modname(resource)
|
||||
|
||||
def _add_names(self, pymodule, modname, underlined):
|
||||
if underlined is None:
|
||||
underlined = self.underlined
|
||||
globals = []
|
||||
if isinstance(pymodule, pyobjects.PyDefinedObject):
|
||||
attributes = pymodule._get_structural_attributes()
|
||||
else:
|
||||
attributes = pymodule.get_attributes()
|
||||
for name, pyname in attributes.items():
|
||||
if not underlined and name.startswith('_'):
|
||||
continue
|
||||
if isinstance(pyname, (pynames.AssignedName, pynames.DefinedName)):
|
||||
globals.append(name)
|
||||
if isinstance(pymodule, builtins.BuiltinModule):
|
||||
globals.append(name)
|
||||
self.names[modname] = globals
|
||||
|
||||
def _write(self):
|
||||
self.project.data_files.write_data('globalnames', self.names)
|
||||
|
||||
def _changed(self, resource):
|
||||
if not resource.is_folder():
|
||||
self.update_resource(resource)
|
||||
|
||||
def _moved(self, resource, newresource):
|
||||
if not resource.is_folder():
|
||||
modname = self._module_name(resource)
|
||||
if modname in self.names:
|
||||
del self.names[modname]
|
||||
self.update_resource(newresource)
|
||||
|
||||
def _removed(self, resource):
|
||||
if not resource.is_folder():
|
||||
modname = self._module_name(resource)
|
||||
if modname in self.names:
|
||||
del self.names[modname]
|
||||
|
||||
|
||||
def submodules(mod):
|
||||
if isinstance(mod, resources.File):
|
||||
if mod.name.endswith('.py') and mod.name != '__init__.py':
|
||||
return set([mod])
|
||||
return set()
|
||||
if not mod.has_child('__init__.py'):
|
||||
return set()
|
||||
result = set([mod])
|
||||
for child in mod.get_children():
|
||||
result |= submodules(child)
|
||||
return result
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
"""For performing many refactorings as a single command
|
||||
|
||||
`changestack` module can be used to perform many refactorings on top
|
||||
of each other as one bigger command. It can be used like::
|
||||
|
||||
stack = ChangeStack(project, 'my big command')
|
||||
|
||||
#..
|
||||
stack.push(refactoring1.get_changes())
|
||||
#..
|
||||
stack.push(refactoring2.get_changes())
|
||||
#..
|
||||
stack.push(refactoringX.get_changes())
|
||||
|
||||
stack.pop_all()
|
||||
changes = stack.merged()
|
||||
|
||||
Now `changes` can be previewed or performed as before.
|
||||
"""
|
||||
|
||||
from rope.base import change
|
||||
|
||||
|
||||
class ChangeStack(object):
|
||||
|
||||
def __init__(self, project, description='merged changes'):
|
||||
self.project = project
|
||||
self.description = description
|
||||
self.stack = []
|
||||
|
||||
def push(self, changes):
|
||||
self.stack.append(changes)
|
||||
self.project.do(changes)
|
||||
|
||||
def pop_all(self):
|
||||
for i in range(len(self.stack)):
|
||||
self.project.history.undo(drop=True)
|
||||
|
||||
def merged(self):
|
||||
result = change.ChangeSet(self.description)
|
||||
for changes in self.stack:
|
||||
for c in self._basic_changes(changes):
|
||||
result.add_change(c)
|
||||
return result
|
||||
|
||||
def _basic_changes(self, changes):
|
||||
if isinstance(changes, change.ChangeSet):
|
||||
for child in changes.changes:
|
||||
for atom in self._basic_changes(child):
|
||||
yield atom
|
||||
else:
|
||||
yield changes
|
||||
|
|
@ -1,695 +0,0 @@
|
|||
import keyword
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import rope.base.codeanalyze
|
||||
import rope.base.evaluate
|
||||
from rope.base import builtins
|
||||
from rope.base import exceptions
|
||||
from rope.base import libutils
|
||||
from rope.base import pynames
|
||||
from rope.base import pynamesdef
|
||||
from rope.base import pyobjects
|
||||
from rope.base import pyobjectsdef
|
||||
from rope.base import pyscopes
|
||||
from rope.base import worder
|
||||
from rope.contrib import fixsyntax
|
||||
from rope.refactor import functionutils
|
||||
|
||||
|
||||
def code_assist(project, source_code, offset, resource=None,
|
||||
templates=None, maxfixes=1, later_locals=True):
|
||||
"""Return python code completions as a list of `CodeAssistProposal`\s
|
||||
|
||||
`resource` is a `rope.base.resources.Resource` object. If
|
||||
provided, relative imports are handled.
|
||||
|
||||
`maxfixes` is the maximum number of errors to fix if the code has
|
||||
errors in it.
|
||||
|
||||
If `later_locals` is `False` names defined in this scope and after
|
||||
this line is ignored.
|
||||
|
||||
"""
|
||||
if templates is not None:
|
||||
warnings.warn('Codeassist no longer supports templates',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
assist = _PythonCodeAssist(
|
||||
project, source_code, offset, resource=resource,
|
||||
maxfixes=maxfixes, later_locals=later_locals)
|
||||
return assist()
|
||||
|
||||
|
||||
def starting_offset(source_code, offset):
|
||||
"""Return the offset in which the completion should be inserted
|
||||
|
||||
Usually code assist proposals should be inserted like::
|
||||
|
||||
completion = proposal.name
|
||||
result = (source_code[:starting_offset] +
|
||||
completion + source_code[offset:])
|
||||
|
||||
Where starting_offset is the offset returned by this function.
|
||||
|
||||
"""
|
||||
word_finder = worder.Worder(source_code, True)
|
||||
expression, starting, starting_offset = \
|
||||
word_finder.get_splitted_primary_before(offset)
|
||||
return starting_offset
|
||||
|
||||
|
||||
def get_doc(project, source_code, offset, resource=None, maxfixes=1):
|
||||
"""Get the pydoc"""
|
||||
fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes)
|
||||
pyname = fixer.pyname_at(offset)
|
||||
if pyname is None:
|
||||
return None
|
||||
pyobject = pyname.get_object()
|
||||
return PyDocExtractor().get_doc(pyobject)
|
||||
|
||||
|
||||
def get_calltip(project, source_code, offset, resource=None,
|
||||
maxfixes=1, ignore_unknown=False, remove_self=False):
|
||||
"""Get the calltip of a function
|
||||
|
||||
The format of the returned string is
|
||||
``module_name.holding_scope_names.function_name(arguments)``. For
|
||||
classes `__init__()` and for normal objects `__call__()` function
|
||||
is used.
|
||||
|
||||
Note that the offset is on the function itself *not* after the its
|
||||
open parenthesis. (Actually it used to be the other way but it
|
||||
was easily confused when string literals were involved. So I
|
||||
decided it is better for it not to try to be too clever when it
|
||||
cannot be clever enough). You can use a simple search like::
|
||||
|
||||
offset = source_code.rindex('(', 0, offset) - 1
|
||||
|
||||
to handle simple situations.
|
||||
|
||||
If `ignore_unknown` is `True`, `None` is returned for functions
|
||||
without source-code like builtins and extensions.
|
||||
|
||||
If `remove_self` is `True`, the first parameter whose name is self
|
||||
will be removed for methods.
|
||||
"""
|
||||
fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes)
|
||||
pyname = fixer.pyname_at(offset)
|
||||
if pyname is None:
|
||||
return None
|
||||
pyobject = pyname.get_object()
|
||||
return PyDocExtractor().get_calltip(pyobject, ignore_unknown, remove_self)
|
||||
|
||||
|
||||
def get_definition_location(project, source_code, offset,
|
||||
resource=None, maxfixes=1):
|
||||
"""Return the definition location of the python name at `offset`
|
||||
|
||||
Return a (`rope.base.resources.Resource`, lineno) tuple. If no
|
||||
`resource` is given and the definition is inside the same module,
|
||||
the first element of the returned tuple would be `None`. If the
|
||||
location cannot be determined ``(None, None)`` is returned.
|
||||
|
||||
"""
|
||||
fixer = fixsyntax.FixSyntax(project, source_code, resource, maxfixes)
|
||||
pyname = fixer.pyname_at(offset)
|
||||
if pyname is not None:
|
||||
module, lineno = pyname.get_definition_location()
|
||||
if module is not None:
|
||||
return module.get_module().get_resource(), lineno
|
||||
return (None, None)
|
||||
|
||||
|
||||
def find_occurrences(*args, **kwds):
|
||||
import rope.contrib.findit
|
||||
warnings.warn('Use `rope.contrib.findit.find_occurrences()` instead',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return rope.contrib.findit.find_occurrences(*args, **kwds)
|
||||
|
||||
|
||||
def get_canonical_path(project, resource, offset):
|
||||
"""Get the canonical path to an object.
|
||||
|
||||
Given the offset of the object, this returns a list of
|
||||
(name, name_type) tuples representing the canonical path to the
|
||||
object. For example, the 'x' in the following code:
|
||||
|
||||
class Foo(object):
|
||||
def bar(self):
|
||||
class Qux(object):
|
||||
def mux(self, x):
|
||||
pass
|
||||
|
||||
we will return:
|
||||
|
||||
[('Foo', 'CLASS'), ('bar', 'FUNCTION'), ('Qux', 'CLASS'),
|
||||
('mux', 'FUNCTION'), ('x', 'PARAMETER')]
|
||||
|
||||
`resource` is a `rope.base.resources.Resource` object.
|
||||
|
||||
`offset` is the offset of the pyname you want the path to.
|
||||
|
||||
"""
|
||||
# Retrieve the PyName.
|
||||
pymod = project.get_pymodule(resource)
|
||||
pyname = rope.base.evaluate.eval_location(pymod, offset)
|
||||
|
||||
# Now get the location of the definition and its containing scope.
|
||||
defmod, lineno = pyname.get_definition_location()
|
||||
if not defmod:
|
||||
return None
|
||||
scope = defmod.get_scope().get_inner_scope_for_line(lineno)
|
||||
|
||||
# Start with the name of the object we're interested in.
|
||||
names = []
|
||||
if isinstance(pyname, pynamesdef.ParameterName):
|
||||
names = [(worder.get_name_at(pymod.get_resource(), offset),
|
||||
'PARAMETER') ]
|
||||
elif isinstance(pyname, pynamesdef.AssignedName):
|
||||
names = [(worder.get_name_at(pymod.get_resource(), offset),
|
||||
'VARIABLE')]
|
||||
|
||||
# Collect scope names.
|
||||
while scope.parent:
|
||||
if isinstance(scope, pyscopes.FunctionScope):
|
||||
scope_type = 'FUNCTION'
|
||||
elif isinstance(scope, pyscopes.ClassScope):
|
||||
scope_type = 'CLASS'
|
||||
else:
|
||||
scope_type = None
|
||||
names.append((scope.pyobject.get_name(), scope_type))
|
||||
scope = scope.parent
|
||||
|
||||
names.append((defmod.get_resource().real_path, 'MODULE'))
|
||||
names.reverse()
|
||||
return names
|
||||
|
||||
|
||||
class CompletionProposal(object):
|
||||
"""A completion proposal
|
||||
|
||||
The `scope` instance variable shows where proposed name came from
|
||||
and can be 'global', 'local', 'builtin', 'attribute', 'keyword',
|
||||
'imported', 'parameter_keyword'.
|
||||
|
||||
The `type` instance variable shows the approximate type of the
|
||||
proposed object and can be 'instance', 'class', 'function', 'module',
|
||||
and `None`.
|
||||
|
||||
All possible relations between proposal's `scope` and `type` are shown
|
||||
in the table below (different scopes in rows and types in columns):
|
||||
|
||||
| instance | class | function | module | None
|
||||
local | + | + | + | + |
|
||||
global | + | + | + | + |
|
||||
builtin | + | + | + | |
|
||||
attribute | + | + | + | + |
|
||||
imported | + | + | + | + |
|
||||
keyword | | | | | +
|
||||
parameter_keyword | | | | | +
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, scope, pyname=None):
|
||||
self.name = name
|
||||
self.pyname = pyname
|
||||
self.scope = self._get_scope(scope)
|
||||
|
||||
def __str__(self):
|
||||
return '%s (%s, %s)' % (self.name, self.scope, self.type)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
@property
|
||||
def parameters(self):
|
||||
"""The names of the parameters the function takes.
|
||||
|
||||
Returns None if this completion is not a function.
|
||||
"""
|
||||
pyname = self.pyname
|
||||
if isinstance(pyname, pynames.ImportedName):
|
||||
pyname = pyname._get_imported_pyname()
|
||||
if isinstance(pyname, pynames.DefinedName):
|
||||
pyobject = pyname.get_object()
|
||||
if isinstance(pyobject, pyobjects.AbstractFunction):
|
||||
return pyobject.get_param_names()
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
pyname = self.pyname
|
||||
if isinstance(pyname, builtins.BuiltinName):
|
||||
pyobject = pyname.get_object()
|
||||
if isinstance(pyobject, builtins.BuiltinFunction):
|
||||
return 'function'
|
||||
elif isinstance(pyobject, builtins.BuiltinClass):
|
||||
return 'class'
|
||||
elif isinstance(pyobject, builtins.BuiltinObject) or \
|
||||
isinstance(pyobject, builtins.BuiltinName):
|
||||
return 'instance'
|
||||
elif isinstance(pyname, pynames.ImportedModule):
|
||||
return 'module'
|
||||
elif isinstance(pyname, pynames.ImportedName) or \
|
||||
isinstance(pyname, pynames.DefinedName):
|
||||
pyobject = pyname.get_object()
|
||||
if isinstance(pyobject, pyobjects.AbstractFunction):
|
||||
return 'function'
|
||||
if isinstance(pyobject, pyobjects.AbstractClass):
|
||||
return 'class'
|
||||
return 'instance'
|
||||
|
||||
def _get_scope(self, scope):
|
||||
if isinstance(self.pyname, builtins.BuiltinName):
|
||||
return 'builtin'
|
||||
if isinstance(self.pyname, pynames.ImportedModule) or \
|
||||
isinstance(self.pyname, pynames.ImportedName):
|
||||
return 'imported'
|
||||
return scope
|
||||
|
||||
def get_doc(self):
|
||||
"""Get the proposed object's docstring.
|
||||
|
||||
Returns None if it can not be get.
|
||||
"""
|
||||
if not self.pyname:
|
||||
return None
|
||||
pyobject = self.pyname.get_object()
|
||||
if not hasattr(pyobject, 'get_doc'):
|
||||
return None
|
||||
return self.pyname.get_object().get_doc()
|
||||
|
||||
@property
|
||||
def kind(self):
|
||||
warnings.warn("the proposal's `kind` property is deprecated, "
|
||||
"use `scope` instead")
|
||||
return self.scope
|
||||
|
||||
|
||||
# leaved for backward compatibility
|
||||
CodeAssistProposal = CompletionProposal
|
||||
|
||||
|
||||
class NamedParamProposal(CompletionProposal):
|
||||
"""A parameter keyword completion proposal
|
||||
|
||||
Holds reference to ``_function`` -- the function which
|
||||
parameter ``name`` belongs to. This allows to determine
|
||||
default value for this parameter.
|
||||
"""
|
||||
def __init__(self, name, function):
|
||||
self.argname = name
|
||||
name = '%s=' % name
|
||||
super(NamedParamProposal, self).__init__(name, 'parameter_keyword')
|
||||
self._function = function
|
||||
|
||||
def get_default(self):
|
||||
"""Get a string representation of a param's default value.
|
||||
|
||||
Returns None if there is no default value for this param.
|
||||
"""
|
||||
definfo = functionutils.DefinitionInfo.read(self._function)
|
||||
for arg, default in definfo.args_with_defaults:
|
||||
if self.argname == arg:
|
||||
return default
|
||||
return None
|
||||
|
||||
|
||||
def sorted_proposals(proposals, scopepref=None, typepref=None):
|
||||
"""Sort a list of proposals
|
||||
|
||||
Return a sorted list of the given `CodeAssistProposal`\s.
|
||||
|
||||
`scopepref` can be a list of proposal scopes. Defaults to
|
||||
``['parameter_keyword', 'local', 'global', 'imported',
|
||||
'attribute', 'builtin', 'keyword']``.
|
||||
|
||||
`typepref` can be a list of proposal types. Defaults to
|
||||
``['class', 'function', 'instance', 'module', None]``.
|
||||
(`None` stands for completions with no type like keywords.)
|
||||
"""
|
||||
sorter = _ProposalSorter(proposals, scopepref, typepref)
|
||||
return sorter.get_sorted_proposal_list()
|
||||
|
||||
|
||||
def starting_expression(source_code, offset):
|
||||
"""Return the expression to complete"""
|
||||
word_finder = worder.Worder(source_code, True)
|
||||
expression, starting, starting_offset = \
|
||||
word_finder.get_splitted_primary_before(offset)
|
||||
if expression:
|
||||
return expression + '.' + starting
|
||||
return starting
|
||||
|
||||
|
||||
def default_templates():
|
||||
warnings.warn('default_templates() is deprecated.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return {}
|
||||
|
||||
|
||||
class _PythonCodeAssist(object):
|
||||
|
||||
def __init__(self, project, source_code, offset, resource=None,
|
||||
maxfixes=1, later_locals=True):
|
||||
self.project = project
|
||||
self.code = source_code
|
||||
self.resource = resource
|
||||
self.maxfixes = maxfixes
|
||||
self.later_locals = later_locals
|
||||
self.word_finder = worder.Worder(source_code, True)
|
||||
self.expression, self.starting, self.offset = \
|
||||
self.word_finder.get_splitted_primary_before(offset)
|
||||
|
||||
keywords = keyword.kwlist
|
||||
|
||||
def _find_starting_offset(self, source_code, offset):
|
||||
current_offset = offset - 1
|
||||
while current_offset >= 0 and (source_code[current_offset].isalnum() or
|
||||
source_code[current_offset] in '_'):
|
||||
current_offset -= 1
|
||||
return current_offset + 1
|
||||
|
||||
def _matching_keywords(self, starting):
|
||||
result = []
|
||||
for kw in self.keywords:
|
||||
if kw.startswith(starting):
|
||||
result.append(CompletionProposal(kw, 'keyword'))
|
||||
return result
|
||||
|
||||
def __call__(self):
|
||||
if self.offset > len(self.code):
|
||||
return []
|
||||
completions = list(self._code_completions().values())
|
||||
if self.expression.strip() == '' and self.starting.strip() != '':
|
||||
completions.extend(self._matching_keywords(self.starting))
|
||||
return completions
|
||||
|
||||
def _dotted_completions(self, module_scope, holding_scope):
|
||||
result = {}
|
||||
found_pyname = rope.base.evaluate.eval_str(holding_scope,
|
||||
self.expression)
|
||||
if found_pyname is not None:
|
||||
element = found_pyname.get_object()
|
||||
compl_scope = 'attribute'
|
||||
if isinstance(element, (pyobjectsdef.PyModule,
|
||||
pyobjectsdef.PyPackage)):
|
||||
compl_scope = 'imported'
|
||||
for name, pyname in element.get_attributes().items():
|
||||
if name.startswith(self.starting):
|
||||
result[name] = CompletionProposal(name, compl_scope,
|
||||
pyname)
|
||||
return result
|
||||
|
||||
def _undotted_completions(self, scope, result, lineno=None):
|
||||
if scope.parent is not None:
|
||||
self._undotted_completions(scope.parent, result)
|
||||
if lineno is None:
|
||||
names = scope.get_propagated_names()
|
||||
else:
|
||||
names = scope.get_names()
|
||||
for name, pyname in names.items():
|
||||
if name.startswith(self.starting):
|
||||
compl_scope = 'local'
|
||||
if scope.get_kind() == 'Module':
|
||||
compl_scope = 'global'
|
||||
if lineno is None or self.later_locals or \
|
||||
not self._is_defined_after(scope, pyname, lineno):
|
||||
result[name] = CompletionProposal(name, compl_scope,
|
||||
pyname)
|
||||
|
||||
def _from_import_completions(self, pymodule):
|
||||
module_name = self.word_finder.get_from_module(self.offset)
|
||||
if module_name is None:
|
||||
return {}
|
||||
pymodule = self._find_module(pymodule, module_name)
|
||||
result = {}
|
||||
for name in pymodule:
|
||||
if name.startswith(self.starting):
|
||||
result[name] = CompletionProposal(name, scope='global',
|
||||
pyname=pymodule[name])
|
||||
return result
|
||||
|
||||
def _find_module(self, pymodule, module_name):
|
||||
dots = 0
|
||||
while module_name[dots] == '.':
|
||||
dots += 1
|
||||
pyname = pynames.ImportedModule(pymodule,
|
||||
module_name[dots:], dots)
|
||||
return pyname.get_object()
|
||||
|
||||
def _is_defined_after(self, scope, pyname, lineno):
|
||||
location = pyname.get_definition_location()
|
||||
if location is not None and location[1] is not None:
|
||||
if location[0] == scope.pyobject.get_module() and \
|
||||
lineno <= location[1] <= scope.get_end():
|
||||
return True
|
||||
|
||||
def _code_completions(self):
|
||||
lineno = self.code.count('\n', 0, self.offset) + 1
|
||||
fixer = fixsyntax.FixSyntax(self.project, self.code,
|
||||
self.resource, self.maxfixes)
|
||||
pymodule = fixer.get_pymodule()
|
||||
module_scope = pymodule.get_scope()
|
||||
code = pymodule.source_code
|
||||
lines = code.split('\n')
|
||||
result = {}
|
||||
start = fixsyntax._logical_start(lines, lineno)
|
||||
indents = fixsyntax._get_line_indents(lines[start - 1])
|
||||
inner_scope = module_scope.get_inner_scope_for_line(start, indents)
|
||||
if self.word_finder.is_a_name_after_from_import(self.offset):
|
||||
return self._from_import_completions(pymodule)
|
||||
if self.expression.strip() != '':
|
||||
result.update(self._dotted_completions(module_scope, inner_scope))
|
||||
else:
|
||||
result.update(self._keyword_parameters(module_scope.pyobject,
|
||||
inner_scope))
|
||||
self._undotted_completions(inner_scope, result, lineno=lineno)
|
||||
return result
|
||||
|
||||
def _keyword_parameters(self, pymodule, scope):
|
||||
offset = self.offset
|
||||
if offset == 0:
|
||||
return {}
|
||||
word_finder = worder.Worder(self.code, True)
|
||||
if word_finder.is_on_function_call_keyword(offset - 1):
|
||||
function_parens = word_finder.\
|
||||
find_parens_start_from_inside(offset - 1)
|
||||
primary = word_finder.get_primary_at(function_parens - 1)
|
||||
try:
|
||||
function_pyname = rope.base.evaluate.\
|
||||
eval_str(scope, primary)
|
||||
except exceptions.BadIdentifierError:
|
||||
return {}
|
||||
if function_pyname is not None:
|
||||
pyobject = function_pyname.get_object()
|
||||
if isinstance(pyobject, pyobjects.AbstractFunction):
|
||||
pass
|
||||
elif isinstance(pyobject, pyobjects.AbstractClass) and \
|
||||
'__init__' in pyobject:
|
||||
pyobject = pyobject['__init__'].get_object()
|
||||
elif '__call__' in pyobject:
|
||||
pyobject = pyobject['__call__'].get_object()
|
||||
if isinstance(pyobject, pyobjects.AbstractFunction):
|
||||
param_names = []
|
||||
param_names.extend(
|
||||
pyobject.get_param_names(special_args=False))
|
||||
result = {}
|
||||
for name in param_names:
|
||||
if name.startswith(self.starting):
|
||||
result[name + '='] = NamedParamProposal(
|
||||
name, pyobject
|
||||
)
|
||||
return result
|
||||
return {}
|
||||
|
||||
|
||||
class _ProposalSorter(object):
|
||||
"""Sort a list of code assist proposals"""
|
||||
|
||||
def __init__(self, code_assist_proposals, scopepref=None, typepref=None):
|
||||
self.proposals = code_assist_proposals
|
||||
if scopepref is None:
|
||||
scopepref = ['parameter_keyword', 'local', 'global', 'imported',
|
||||
'attribute', 'builtin', 'keyword']
|
||||
self.scopepref = scopepref
|
||||
if typepref is None:
|
||||
typepref = ['class', 'function', 'instance', 'module', None]
|
||||
self.typerank = dict((type, index)
|
||||
for index, type in enumerate(typepref))
|
||||
|
||||
def get_sorted_proposal_list(self):
|
||||
"""Return a list of `CodeAssistProposal`"""
|
||||
proposals = {}
|
||||
for proposal in self.proposals:
|
||||
proposals.setdefault(proposal.scope, []).append(proposal)
|
||||
result = []
|
||||
for scope in self.scopepref:
|
||||
scope_proposals = proposals.get(scope, [])
|
||||
scope_proposals = [proposal for proposal in scope_proposals
|
||||
if proposal.type in self.typerank]
|
||||
scope_proposals.sort(key=self._proposal_key)
|
||||
result.extend(scope_proposals)
|
||||
return result
|
||||
|
||||
def _proposal_key(self, proposal1):
|
||||
def _underline_count(name):
|
||||
return sum(1 for c in name if c == "_")
|
||||
return (self.typerank.get(proposal1.type, 100),
|
||||
_underline_count(proposal1.name),
|
||||
proposal1.name)
|
||||
#if proposal1.type != proposal2.type:
|
||||
# return cmp(self.typerank.get(proposal1.type, 100),
|
||||
# self.typerank.get(proposal2.type, 100))
|
||||
#return self._compare_underlined_names(proposal1.name,
|
||||
# proposal2.name)
|
||||
|
||||
|
||||
class PyDocExtractor(object):
|
||||
|
||||
def get_doc(self, pyobject):
|
||||
if isinstance(pyobject, pyobjects.AbstractFunction):
|
||||
return self._get_function_docstring(pyobject)
|
||||
elif isinstance(pyobject, pyobjects.AbstractClass):
|
||||
return self._get_class_docstring(pyobject)
|
||||
elif isinstance(pyobject, pyobjects.AbstractModule):
|
||||
return self._trim_docstring(pyobject.get_doc())
|
||||
return None
|
||||
|
||||
def get_calltip(self, pyobject, ignore_unknown=False, remove_self=False):
|
||||
try:
|
||||
if isinstance(pyobject, pyobjects.AbstractClass):
|
||||
pyobject = pyobject['__init__'].get_object()
|
||||
if not isinstance(pyobject, pyobjects.AbstractFunction):
|
||||
pyobject = pyobject['__call__'].get_object()
|
||||
except exceptions.AttributeNotFoundError:
|
||||
return None
|
||||
if ignore_unknown and not isinstance(pyobject, pyobjects.PyFunction):
|
||||
return
|
||||
if isinstance(pyobject, pyobjects.AbstractFunction):
|
||||
result = self._get_function_signature(pyobject, add_module=True)
|
||||
if remove_self and self._is_method(pyobject):
|
||||
return result.replace('(self)', '()').replace('(self, ', '(')
|
||||
return result
|
||||
|
||||
def _get_class_docstring(self, pyclass):
|
||||
contents = self._trim_docstring(pyclass.get_doc(), 2)
|
||||
supers = [super.get_name() for super in pyclass.get_superclasses()]
|
||||
doc = 'class %s(%s):\n\n' % (pyclass.get_name(), ', '.join(supers)) \
|
||||
+ contents
|
||||
|
||||
if '__init__' in pyclass:
|
||||
init = pyclass['__init__'].get_object()
|
||||
if isinstance(init, pyobjects.AbstractFunction):
|
||||
doc += '\n\n' + self._get_single_function_docstring(init)
|
||||
return doc
|
||||
|
||||
def _get_function_docstring(self, pyfunction):
|
||||
functions = [pyfunction]
|
||||
if self._is_method(pyfunction):
|
||||
functions.extend(self._get_super_methods(pyfunction.parent,
|
||||
pyfunction.get_name()))
|
||||
return '\n\n'.join([self._get_single_function_docstring(function)
|
||||
for function in functions])
|
||||
|
||||
def _is_method(self, pyfunction):
|
||||
return isinstance(pyfunction, pyobjects.PyFunction) and \
|
||||
isinstance(pyfunction.parent, pyobjects.PyClass)
|
||||
|
||||
def _get_single_function_docstring(self, pyfunction):
|
||||
signature = self._get_function_signature(pyfunction)
|
||||
docs = self._trim_docstring(pyfunction.get_doc(), indents=2)
|
||||
return signature + ':\n\n' + docs
|
||||
|
||||
def _get_super_methods(self, pyclass, name):
|
||||
result = []
|
||||
for super_class in pyclass.get_superclasses():
|
||||
if name in super_class:
|
||||
function = super_class[name].get_object()
|
||||
if isinstance(function, pyobjects.AbstractFunction):
|
||||
result.append(function)
|
||||
result.extend(self._get_super_methods(super_class, name))
|
||||
return result
|
||||
|
||||
def _get_function_signature(self, pyfunction, add_module=False):
|
||||
location = self._location(pyfunction, add_module)
|
||||
if isinstance(pyfunction, pyobjects.PyFunction):
|
||||
info = functionutils.DefinitionInfo.read(pyfunction)
|
||||
return location + info.to_string()
|
||||
else:
|
||||
return '%s(%s)' % (location + pyfunction.get_name(),
|
||||
', '.join(pyfunction.get_param_names()))
|
||||
|
||||
def _location(self, pyobject, add_module=False):
|
||||
location = []
|
||||
parent = pyobject.parent
|
||||
while parent and not isinstance(parent, pyobjects.AbstractModule):
|
||||
location.append(parent.get_name())
|
||||
location.append('.')
|
||||
parent = parent.parent
|
||||
if add_module:
|
||||
if isinstance(pyobject, pyobjects.PyFunction):
|
||||
location.insert(0, self._get_module(pyobject))
|
||||
if isinstance(parent, builtins.BuiltinModule):
|
||||
location.insert(0, parent.get_name() + '.')
|
||||
return ''.join(location)
|
||||
|
||||
def _get_module(self, pyfunction):
|
||||
module = pyfunction.get_module()
|
||||
if module is not None:
|
||||
resource = module.get_resource()
|
||||
if resource is not None:
|
||||
return libutils.modname(resource) + '.'
|
||||
return ''
|
||||
|
||||
def _trim_docstring(self, docstring, indents=0):
|
||||
"""The sample code from :PEP:`257`"""
|
||||
if not docstring:
|
||||
return ''
|
||||
# Convert tabs to spaces (following normal Python rules)
|
||||
# and split into a list of lines:
|
||||
lines = docstring.expandtabs().splitlines()
|
||||
# Determine minimum indentation (first line doesn't count):
|
||||
indent = sys.maxsize
|
||||
for line in lines[1:]:
|
||||
stripped = line.lstrip()
|
||||
if stripped:
|
||||
indent = min(indent, len(line) - len(stripped))
|
||||
# Remove indentation (first line is special):
|
||||
trimmed = [lines[0].strip()]
|
||||
if indent < sys.maxsize:
|
||||
for line in lines[1:]:
|
||||
trimmed.append(line[indent:].rstrip())
|
||||
# Strip off trailing and leading blank lines:
|
||||
while trimmed and not trimmed[-1]:
|
||||
trimmed.pop()
|
||||
while trimmed and not trimmed[0]:
|
||||
trimmed.pop(0)
|
||||
# Return a single string:
|
||||
return '\n'.join((' ' * indents + line for line in trimmed))
|
||||
|
||||
|
||||
# Deprecated classes
|
||||
|
||||
class TemplateProposal(CodeAssistProposal):
|
||||
def __init__(self, name, template):
|
||||
warnings.warn('TemplateProposal is deprecated.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
super(TemplateProposal, self).__init__(name, 'template')
|
||||
self.template = template
|
||||
|
||||
|
||||
class Template(object):
|
||||
|
||||
def __init__(self, template):
|
||||
self.template = template
|
||||
warnings.warn('Template is deprecated.',
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
||||
def variables(self):
|
||||
return []
|
||||
|
||||
def substitute(self, mapping):
|
||||
return self.template
|
||||
|
||||
def get_cursor_location(self, mapping):
|
||||
return len(self.template)
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
"""Finding bad name and attribute accesses
|
||||
|
||||
`find_errors` function can be used to find possible bad name and
|
||||
attribute accesses. As an example::
|
||||
|
||||
errors = find_errors(project, project.get_resource('mod.py'))
|
||||
for error in errors:
|
||||
print('%s: %s' % (error.lineno, error.error))
|
||||
|
||||
prints possible errors for ``mod.py`` file.
|
||||
|
||||
TODO:
|
||||
|
||||
* use task handles
|
||||
* reporting names at most once
|
||||
* attributes of extension modules that don't appear in
|
||||
extension_modules project config can be ignored
|
||||
* not calling `PyScope.get_inner_scope_for_line()` if it is a
|
||||
bottleneck; needs profiling
|
||||
* not reporting occurrences where rope cannot infer the object
|
||||
* rope saves multiple objects for some of the names in its objectdb
|
||||
use all of them not to give false positives
|
||||
* ... ;-)
|
||||
|
||||
"""
|
||||
from rope.base import ast, evaluate, pyobjects
|
||||
|
||||
|
||||
def find_errors(project, resource):
|
||||
"""Find possible bad name and attribute accesses
|
||||
|
||||
It returns a list of `Error`\s.
|
||||
"""
|
||||
pymodule = project.get_pymodule(resource)
|
||||
finder = _BadAccessFinder(pymodule)
|
||||
ast.walk(pymodule.get_ast(), finder)
|
||||
return finder.errors
|
||||
|
||||
|
||||
class _BadAccessFinder(object):
|
||||
|
||||
def __init__(self, pymodule):
|
||||
self.pymodule = pymodule
|
||||
self.scope = pymodule.get_scope()
|
||||
self.errors = []
|
||||
|
||||
def _Name(self, node):
|
||||
if isinstance(node.ctx, (ast.Store, ast.Param)):
|
||||
return
|
||||
scope = self.scope.get_inner_scope_for_line(node.lineno)
|
||||
pyname = scope.lookup(node.id)
|
||||
if pyname is None:
|
||||
self._add_error(node, 'Unresolved variable')
|
||||
elif self._is_defined_after(scope, pyname, node.lineno):
|
||||
self._add_error(node, 'Defined later')
|
||||
|
||||
def _Attribute(self, node):
|
||||
if not isinstance(node.ctx, ast.Store):
|
||||
scope = self.scope.get_inner_scope_for_line(node.lineno)
|
||||
pyname = evaluate.eval_node(scope, node.value)
|
||||
if pyname is not None and \
|
||||
pyname.get_object() != pyobjects.get_unknown():
|
||||
if node.attr not in pyname.get_object():
|
||||
self._add_error(node, 'Unresolved attribute')
|
||||
ast.walk(node.value, self)
|
||||
|
||||
def _add_error(self, node, msg):
|
||||
if isinstance(node, ast.Attribute):
|
||||
name = node.attr
|
||||
else:
|
||||
name = node.id
|
||||
if name != 'None':
|
||||
error = Error(node.lineno, msg + ' ' + name)
|
||||
self.errors.append(error)
|
||||
|
||||
def _is_defined_after(self, scope, pyname, lineno):
|
||||
location = pyname.get_definition_location()
|
||||
if location is not None and location[1] is not None:
|
||||
if location[0] == self.pymodule and \
|
||||
lineno <= location[1] <= scope.get_end():
|
||||
return True
|
||||
|
||||
|
||||
class Error(object):
|
||||
|
||||
def __init__(self, lineno, error):
|
||||
self.lineno = lineno
|
||||
self.error = error
|
||||
|
||||
def __str__(self):
|
||||
return '%s: %s' % (self.lineno, self.error)
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
import rope.base.codeanalyze
|
||||
import rope.base.evaluate
|
||||
import rope.base.pyobjects
|
||||
from rope.base import taskhandle, exceptions, worder
|
||||
from rope.contrib import fixsyntax
|
||||
from rope.refactor import occurrences
|
||||
|
||||
|
||||
def find_occurrences(project, resource, offset, unsure=False, resources=None,
|
||||
in_hierarchy=False,
|
||||
task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Return a list of `Location`\s
|
||||
|
||||
If `unsure` is `True`, possible matches are returned, too. You
|
||||
can use `Location.unsure` to see which are unsure occurrences.
|
||||
`resources` can be a list of `rope.base.resource.File`\s that
|
||||
should be searched for occurrences; if `None` all python files
|
||||
in the project are searched.
|
||||
|
||||
"""
|
||||
name = worder.get_name_at(resource, offset)
|
||||
this_pymodule = project.get_pymodule(resource)
|
||||
primary, pyname = rope.base.evaluate.eval_location2(
|
||||
this_pymodule, offset)
|
||||
|
||||
def is_match(occurrence):
|
||||
return unsure
|
||||
finder = occurrences.create_finder(
|
||||
project, name, pyname, unsure=is_match,
|
||||
in_hierarchy=in_hierarchy, instance=primary)
|
||||
if resources is None:
|
||||
resources = project.get_python_files()
|
||||
job_set = task_handle.create_jobset('Finding Occurrences',
|
||||
count=len(resources))
|
||||
return _find_locations(finder, resources, job_set)
|
||||
|
||||
|
||||
def find_implementations(project, resource, offset, resources=None,
|
||||
task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Find the places a given method is overridden.
|
||||
|
||||
Finds the places a method is implemented. Returns a list of
|
||||
`Location`\s.
|
||||
"""
|
||||
name = worder.get_name_at(resource, offset)
|
||||
this_pymodule = project.get_pymodule(resource)
|
||||
pyname = rope.base.evaluate.eval_location(this_pymodule, offset)
|
||||
if pyname is not None:
|
||||
pyobject = pyname.get_object()
|
||||
if not isinstance(pyobject, rope.base.pyobjects.PyFunction) or \
|
||||
pyobject.get_kind() != 'method':
|
||||
raise exceptions.BadIdentifierError('Not a method!')
|
||||
else:
|
||||
raise exceptions.BadIdentifierError('Cannot resolve the identifier!')
|
||||
|
||||
def is_defined(occurrence):
|
||||
if not occurrence.is_defined():
|
||||
return False
|
||||
|
||||
def not_self(occurrence):
|
||||
if occurrence.get_pyname().get_object() == pyname.get_object():
|
||||
return False
|
||||
filters = [is_defined, not_self,
|
||||
occurrences.InHierarchyFilter(pyname, True)]
|
||||
finder = occurrences.Finder(project, name, filters=filters)
|
||||
if resources is None:
|
||||
resources = project.get_python_files()
|
||||
job_set = task_handle.create_jobset('Finding Implementations',
|
||||
count=len(resources))
|
||||
return _find_locations(finder, resources, job_set)
|
||||
|
||||
|
||||
def find_definition(project, code, offset, resource=None, maxfixes=1):
|
||||
"""Return the definition location of the python name at `offset`
|
||||
|
||||
A `Location` object is returned if the definition location can be
|
||||
determined, otherwise ``None`` is returned.
|
||||
"""
|
||||
fixer = fixsyntax.FixSyntax(project, code, resource, maxfixes)
|
||||
pyname = fixer.pyname_at(offset)
|
||||
if pyname is not None:
|
||||
module, lineno = pyname.get_definition_location()
|
||||
name = rope.base.worder.Worder(code).get_word_at(offset)
|
||||
if lineno is not None:
|
||||
start = module.lines.get_line_start(lineno)
|
||||
|
||||
def check_offset(occurrence):
|
||||
if occurrence.offset < start:
|
||||
return False
|
||||
pyname_filter = occurrences.PyNameFilter(pyname)
|
||||
finder = occurrences.Finder(project, name,
|
||||
[check_offset, pyname_filter])
|
||||
for occurrence in finder.find_occurrences(pymodule=module):
|
||||
return Location(occurrence)
|
||||
|
||||
|
||||
class Location(object):
|
||||
|
||||
def __init__(self, occurrence):
|
||||
self.resource = occurrence.resource
|
||||
self.region = occurrence.get_word_range()
|
||||
self.offset = self.region[0]
|
||||
self.unsure = occurrence.is_unsure()
|
||||
self.lineno = occurrence.lineno
|
||||
|
||||
|
||||
def _find_locations(finder, resources, job_set):
|
||||
result = []
|
||||
for resource in resources:
|
||||
job_set.started_job(resource.path)
|
||||
for occurrence in finder.find_occurrences(resource):
|
||||
result.append(Location(occurrence))
|
||||
job_set.finished_job()
|
||||
return result
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
"""Fix the name of modules
|
||||
|
||||
This module is useful when you want to rename many of the modules in
|
||||
your project. That can happen specially when you want to change their
|
||||
naming style.
|
||||
|
||||
For instance::
|
||||
|
||||
fixer = FixModuleNames(project)
|
||||
changes = fixer.get_changes(fixer=str.lower)
|
||||
project.do(changes)
|
||||
|
||||
Here it renames all modules and packages to use lower-cased chars.
|
||||
You can tell it to use any other style by using the ``fixer``
|
||||
argument.
|
||||
|
||||
"""
|
||||
from rope.base import taskhandle
|
||||
from rope.contrib import changestack
|
||||
from rope.refactor import rename
|
||||
|
||||
|
||||
class FixModuleNames(object):
|
||||
|
||||
def __init__(self, project):
|
||||
self.project = project
|
||||
|
||||
def get_changes(self, fixer=str.lower,
|
||||
task_handle=taskhandle.NullTaskHandle()):
|
||||
"""Fix module names
|
||||
|
||||
`fixer` is a function that takes and returns a `str`. Given
|
||||
the name of a module, it should return the fixed name.
|
||||
|
||||
"""
|
||||
stack = changestack.ChangeStack(self.project, 'Fixing module names')
|
||||
jobset = task_handle.create_jobset('Fixing module names',
|
||||
self._count_fixes(fixer) + 1)
|
||||
try:
|
||||
while True:
|
||||
for resource in self._tobe_fixed(fixer):
|
||||
jobset.started_job(resource.path)
|
||||
renamer = rename.Rename(self.project, resource)
|
||||
changes = renamer.get_changes(fixer(self._name(resource)))
|
||||
stack.push(changes)
|
||||
jobset.finished_job()
|
||||
break
|
||||
else:
|
||||
break
|
||||
finally:
|
||||
jobset.started_job('Reverting to original state')
|
||||
stack.pop_all()
|
||||
jobset.finished_job()
|
||||
return stack.merged()
|
||||
|
||||
def _count_fixes(self, fixer):
|
||||
return len(list(self._tobe_fixed(fixer)))
|
||||
|
||||
def _tobe_fixed(self, fixer):
|
||||
for resource in self.project.get_python_files():
|
||||
modname = self._name(resource)
|
||||
if modname != fixer(modname):
|
||||
yield resource
|
||||
|
||||
def _name(self, resource):
|
||||
modname = resource.name.rsplit('.', 1)[0]
|
||||
if modname == '__init__':
|
||||
modname = resource.parent.name
|
||||
return modname
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
import rope.base.codeanalyze
|
||||
import rope.base.evaluate
|
||||
from rope.base import exceptions
|
||||
from rope.base import libutils
|
||||
from rope.base import utils
|
||||
from rope.base import worder
|
||||
from rope.base.codeanalyze import ArrayLinesAdapter, LogicalLineFinder
|
||||
|
||||
|
||||
class FixSyntax(object):
|
||||
|
||||
def __init__(self, project, code, resource, maxfixes=1):
|
||||
self.project = project
|
||||
self.code = code
|
||||
self.resource = resource
|
||||
self.maxfixes = maxfixes
|
||||
|
||||
@utils.saveit
|
||||
def get_pymodule(self):
|
||||
"""Get a `PyModule`"""
|
||||
msg = None
|
||||
code = self.code
|
||||
tries = 0
|
||||
while True:
|
||||
try:
|
||||
if tries == 0 and self.resource is not None and \
|
||||
self.resource.read() == code:
|
||||
return self.project.get_pymodule(self.resource,
|
||||
force_errors=True)
|
||||
return libutils.get_string_module(
|
||||
self.project, code, resource=self.resource,
|
||||
force_errors=True)
|
||||
except exceptions.ModuleSyntaxError as e:
|
||||
if msg is None:
|
||||
msg = '%s:%s %s' % (e.filename, e.lineno, e.message_)
|
||||
if tries < self.maxfixes:
|
||||
tries += 1
|
||||
self.commenter.comment(e.lineno)
|
||||
code = '\n'.join(self.commenter.lines)
|
||||
else:
|
||||
raise exceptions.ModuleSyntaxError(
|
||||
e.filename, e.lineno,
|
||||
'Failed to fix error: {0}'.format(msg))
|
||||
|
||||
@property
|
||||
@utils.saveit
|
||||
def commenter(self):
|
||||
return _Commenter(self.code)
|
||||
|
||||
def pyname_at(self, offset):
|
||||
pymodule = self.get_pymodule()
|
||||
|
||||
def old_pyname():
|
||||
word_finder = worder.Worder(self.code, True)
|
||||
expression = word_finder.get_primary_at(offset)
|
||||
expression = expression.replace('\\\n', ' ').replace('\n', ' ')
|
||||
lineno = self.code.count('\n', 0, offset)
|
||||
scope = pymodule.get_scope().get_inner_scope_for_line(lineno)
|
||||
return rope.base.evaluate.eval_str(scope, expression)
|
||||
new_code = pymodule.source_code
|
||||
|
||||
def new_pyname():
|
||||
newoffset = self.commenter.transfered_offset(offset)
|
||||
return rope.base.evaluate.eval_location(pymodule, newoffset)
|
||||
if new_code.startswith(self.code[:offset + 1]):
|
||||
return new_pyname()
|
||||
result = old_pyname()
|
||||
if result is None:
|
||||
return new_pyname()
|
||||
return result
|
||||
|
||||
|
||||
class _Commenter(object):
|
||||
|
||||
def __init__(self, code):
|
||||
self.code = code
|
||||
self.lines = self.code.split('\n')
|
||||
self.lines.append('\n')
|
||||
self.origs = list(range(len(self.lines) + 1))
|
||||
self.diffs = [0] * (len(self.lines) + 1)
|
||||
|
||||
def comment(self, lineno):
|
||||
start = _logical_start(self.lines, lineno, check_prev=True) - 1
|
||||
# using self._get_stmt_end() instead of self._get_block_end()
|
||||
# to lower commented lines
|
||||
end = self._get_stmt_end(start)
|
||||
indents = _get_line_indents(self.lines[start])
|
||||
if 0 < start:
|
||||
last_lineno = self._last_non_blank(start - 1)
|
||||
last_line = self.lines[last_lineno]
|
||||
if last_line.rstrip().endswith(':'):
|
||||
indents = _get_line_indents(last_line) + 4
|
||||
self._set(start, ' ' * indents + 'pass')
|
||||
for line in range(start + 1, end + 1):
|
||||
self._set(line, self.lines[start])
|
||||
self._fix_incomplete_try_blocks(lineno, indents)
|
||||
|
||||
def transfered_offset(self, offset):
|
||||
lineno = self.code.count('\n', 0, offset)
|
||||
diff = sum(self.diffs[:lineno])
|
||||
return offset + diff
|
||||
|
||||
def _last_non_blank(self, start):
|
||||
while start > 0 and self.lines[start].strip() == '':
|
||||
start -= 1
|
||||
return start
|
||||
|
||||
def _get_block_end(self, lineno):
|
||||
end_line = lineno
|
||||
base_indents = _get_line_indents(self.lines[lineno])
|
||||
for i in range(lineno + 1, len(self.lines)):
|
||||
if _get_line_indents(self.lines[i]) >= base_indents:
|
||||
end_line = i
|
||||
else:
|
||||
break
|
||||
return end_line
|
||||
|
||||
def _get_stmt_end(self, lineno):
|
||||
base_indents = _get_line_indents(self.lines[lineno])
|
||||
for i in range(lineno + 1, len(self.lines)):
|
||||
if _get_line_indents(self.lines[i]) <= base_indents:
|
||||
return i - 1
|
||||
return lineno
|
||||
|
||||
def _fix_incomplete_try_blocks(self, lineno, indents):
|
||||
block_start = lineno
|
||||
last_indents = indents
|
||||
while block_start > 0:
|
||||
block_start = rope.base.codeanalyze.get_block_start(
|
||||
ArrayLinesAdapter(self.lines), block_start) - 1
|
||||
if self.lines[block_start].strip().startswith('try:'):
|
||||
indents = _get_line_indents(self.lines[block_start])
|
||||
if indents > last_indents:
|
||||
continue
|
||||
last_indents = indents
|
||||
block_end = self._find_matching_deindent(block_start)
|
||||
line = self.lines[block_end].strip()
|
||||
if not (line.startswith('finally:') or
|
||||
line.startswith('except ') or
|
||||
line.startswith('except:')):
|
||||
self._insert(block_end, ' ' * indents + 'finally:')
|
||||
self._insert(block_end + 1, ' ' * indents + ' pass')
|
||||
|
||||
def _find_matching_deindent(self, line_number):
|
||||
indents = _get_line_indents(self.lines[line_number])
|
||||
current_line = line_number + 1
|
||||
while current_line < len(self.lines):
|
||||
line = self.lines[current_line]
|
||||
if not line.strip().startswith('#') and not line.strip() == '':
|
||||
# HACK: We should have used logical lines here
|
||||
if _get_line_indents(self.lines[current_line]) <= indents:
|
||||
return current_line
|
||||
current_line += 1
|
||||
return len(self.lines) - 1
|
||||
|
||||
def _set(self, lineno, line):
|
||||
self.diffs[self.origs[lineno]] += len(line) - len(self.lines[lineno])
|
||||
self.lines[lineno] = line
|
||||
|
||||
def _insert(self, lineno, line):
|
||||
self.diffs[self.origs[lineno]] += len(line) + 1
|
||||
self.origs.insert(lineno, self.origs[lineno])
|
||||
self.lines.insert(lineno, line)
|
||||
|
||||
|
||||
def _logical_start(lines, lineno, check_prev=False):
|
||||
logical_finder = LogicalLineFinder(ArrayLinesAdapter(lines))
|
||||
if check_prev:
|
||||
prev = lineno - 1
|
||||
while prev > 0:
|
||||
start, end = logical_finder.logical_line_in(prev)
|
||||
if end is None or start <= lineno < end:
|
||||
return start
|
||||
if start <= prev:
|
||||
break
|
||||
prev -= 1
|
||||
return logical_finder.logical_line_in(lineno)[0]
|
||||
|
||||
|
||||
def _get_line_indents(line):
|
||||
return rope.base.codeanalyze.count_line_indents(line)
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
import rope.base.evaluate
|
||||
from rope.base import libutils
|
||||
from rope.base import (change, pyobjects, exceptions, pynames, worder,
|
||||
codeanalyze)
|
||||
from rope.refactor import sourceutils, importutils, functionutils, suites
|
||||
|
||||
|
||||
def create_generate(kind, project, resource, offset):
|
||||
"""A factory for creating `Generate` objects
|
||||
|
||||
`kind` can be 'variable', 'function', 'class', 'module' or
|
||||
'package'.
|
||||
|
||||
"""
|
||||
generate = eval('Generate' + kind.title())
|
||||
return generate(project, resource, offset)
|
||||
|
||||
|
||||
def create_module(project, name, sourcefolder=None):
|
||||
"""Creates a module and returns a `rope.base.resources.File`"""
|
||||
if sourcefolder is None:
|
||||
sourcefolder = project.root
|
||||
packages = name.split('.')
|
||||
parent = sourcefolder
|
||||
for package in packages[:-1]:
|
||||
parent = parent.get_child(package)
|
||||
return parent.create_file(packages[-1] + '.py')
|
||||
|
||||
|
||||
def create_package(project, name, sourcefolder=None):
|
||||
"""Creates a package and returns a `rope.base.resources.Folder`"""
|
||||
if sourcefolder is None:
|
||||
sourcefolder = project.root
|
||||
packages = name.split('.')
|
||||
parent = sourcefolder
|
||||
for package in packages[:-1]:
|
||||
parent = parent.get_child(package)
|
||||
made_packages = parent.create_folder(packages[-1])
|
||||
made_packages.create_file('__init__.py')
|
||||
return made_packages
|
||||
|
||||
|
||||
class _Generate(object):
|
||||
|
||||
def __init__(self, project, resource, offset):
|
||||
self.project = project
|
||||
self.resource = resource
|
||||
self.info = self._generate_info(project, resource, offset)
|
||||
self.name = self.info.get_name()
|
||||
self._check_exceptional_conditions()
|
||||
|
||||
def _generate_info(self, project, resource, offset):
|
||||
return _GenerationInfo(project.pycore, resource, offset)
|
||||
|
||||
def _check_exceptional_conditions(self):
|
||||
if self.info.element_already_exists():
|
||||
raise exceptions.RefactoringError(
|
||||
'Element <%s> already exists.' % self.name)
|
||||
if not self.info.primary_is_found():
|
||||
raise exceptions.RefactoringError(
|
||||
'Cannot determine the scope <%s> should be defined in.' %
|
||||
self.name)
|
||||
|
||||
def get_changes(self):
|
||||
changes = change.ChangeSet('Generate %s <%s>' %
|
||||
(self._get_element_kind(), self.name))
|
||||
indents = self.info.get_scope_indents()
|
||||
blanks = self.info.get_blank_lines()
|
||||
base_definition = sourceutils.fix_indentation(self._get_element(),
|
||||
indents)
|
||||
definition = '\n' * blanks[0] + base_definition + '\n' * blanks[1]
|
||||
|
||||
resource = self.info.get_insertion_resource()
|
||||
start, end = self.info.get_insertion_offsets()
|
||||
|
||||
collector = codeanalyze.ChangeCollector(resource.read())
|
||||
collector.add_change(start, end, definition)
|
||||
changes.add_change(change.ChangeContents(
|
||||
resource, collector.get_changed()))
|
||||
return changes
|
||||
|
||||
def get_location(self):
|
||||
return (self.info.get_insertion_resource(),
|
||||
self.info.get_insertion_lineno())
|
||||
|
||||
def _get_element_kind(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_element(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class GenerateFunction(_Generate):
|
||||
|
||||
def _generate_info(self, project, resource, offset):
|
||||
return _FunctionGenerationInfo(project.pycore, resource, offset)
|
||||
|
||||
def _get_element(self):
|
||||
decorator = ''
|
||||
args = []
|
||||
if self.info.is_static_method():
|
||||
decorator = '@staticmethod\n'
|
||||
if self.info.is_method() or self.info.is_constructor() or \
|
||||
self.info.is_instance():
|
||||
args.append('self')
|
||||
args.extend(self.info.get_passed_args())
|
||||
definition = '%sdef %s(%s):\n pass\n' % (decorator, self.name,
|
||||
', '.join(args))
|
||||
return definition
|
||||
|
||||
def _get_element_kind(self):
|
||||
return 'Function'
|
||||
|
||||
|
||||
class GenerateVariable(_Generate):
|
||||
|
||||
def _get_element(self):
|
||||
return '%s = None\n' % self.name
|
||||
|
||||
def _get_element_kind(self):
|
||||
return 'Variable'
|
||||
|
||||
|
||||
class GenerateClass(_Generate):
|
||||
|
||||
def _get_element(self):
|
||||
return 'class %s(object):\n pass\n' % self.name
|
||||
|
||||
def _get_element_kind(self):
|
||||
return 'Class'
|
||||
|
||||
|
||||
class GenerateModule(_Generate):
|
||||
|
||||
def get_changes(self):
|
||||
package = self.info.get_package()
|
||||
changes = change.ChangeSet('Generate Module <%s>' % self.name)
|
||||
new_resource = self.project.get_file('%s/%s.py' %
|
||||
(package.path, self.name))
|
||||
if new_resource.exists():
|
||||
raise exceptions.RefactoringError(
|
||||
'Module <%s> already exists' % new_resource.path)
|
||||
changes.add_change(change.CreateResource(new_resource))
|
||||
changes.add_change(_add_import_to_module(
|
||||
self.project, self.resource, new_resource))
|
||||
return changes
|
||||
|
||||
def get_location(self):
|
||||
package = self.info.get_package()
|
||||
return (package.get_child('%s.py' % self.name), 1)
|
||||
|
||||
|
||||
class GeneratePackage(_Generate):
|
||||
|
||||
def get_changes(self):
|
||||
package = self.info.get_package()
|
||||
changes = change.ChangeSet('Generate Package <%s>' % self.name)
|
||||
new_resource = self.project.get_folder('%s/%s' %
|
||||
(package.path, self.name))
|
||||
if new_resource.exists():
|
||||
raise exceptions.RefactoringError(
|
||||
'Package <%s> already exists' % new_resource.path)
|
||||
changes.add_change(change.CreateResource(new_resource))
|
||||
changes.add_change(_add_import_to_module(
|
||||
self.project, self.resource, new_resource))
|
||||
child = self.project.get_folder(package.path + '/' + self.name)
|
||||
changes.add_change(change.CreateFile(child, '__init__.py'))
|
||||
return changes
|
||||
|
||||
def get_location(self):
|
||||
package = self.info.get_package()
|
||||
child = package.get_child(self.name)
|
||||
return (child.get_child('__init__.py'), 1)
|
||||
|
||||
|
||||
def _add_import_to_module(project, resource, imported):
|
||||
pymodule = project.get_pymodule(resource)
|
||||
import_tools = importutils.ImportTools(project)
|
||||
module_imports = import_tools.module_imports(pymodule)
|
||||
module_name = libutils.modname(imported)
|
||||
new_import = importutils.NormalImport(((module_name, None), ))
|
||||
module_imports.add_import(new_import)
|
||||
return change.ChangeContents(resource, module_imports.get_changed_source())
|
||||
|
||||
|
||||
class _GenerationInfo(object):
|
||||
|
||||
def __init__(self, pycore, resource, offset):
|
||||
self.pycore = pycore
|
||||
self.resource = resource
|
||||
self.offset = offset
|
||||
self.source_pymodule = self.pycore.project.get_pymodule(resource)
|
||||
finder = rope.base.evaluate.ScopeNameFinder(self.source_pymodule)
|
||||
self.primary, self.pyname = finder.get_primary_and_pyname_at(offset)
|
||||
self._init_fields()
|
||||
|
||||
def _init_fields(self):
|
||||
self.source_scope = self._get_source_scope()
|
||||
self.goal_scope = self._get_goal_scope()
|
||||
self.goal_pymodule = self._get_goal_module(self.goal_scope)
|
||||
|
||||
def _get_goal_scope(self):
|
||||
if self.primary is None:
|
||||
return self._get_source_scope()
|
||||
pyobject = self.primary.get_object()
|
||||
if isinstance(pyobject, pyobjects.PyDefinedObject):
|
||||
return pyobject.get_scope()
|
||||
elif isinstance(pyobject.get_type(), pyobjects.PyClass):
|
||||
return pyobject.get_type().get_scope()
|
||||
|
||||
def _get_goal_module(self, scope):
|
||||
if scope is None:
|
||||
return
|
||||
while scope.parent is not None:
|
||||
scope = scope.parent
|
||||
return scope.pyobject
|
||||
|
||||
def _get_source_scope(self):
|
||||
module_scope = self.source_pymodule.get_scope()
|
||||
lineno = self.source_pymodule.lines.get_line_number(self.offset)
|
||||
return module_scope.get_inner_scope_for_line(lineno)
|
||||
|
||||
def get_insertion_lineno(self):
|
||||
lines = self.goal_pymodule.lines
|
||||
if self.goal_scope == self.source_scope:
|
||||
line_finder = self.goal_pymodule.logical_lines
|
||||
lineno = lines.get_line_number(self.offset)
|
||||
lineno = line_finder.logical_line_in(lineno)[0]
|
||||
root = suites.ast_suite_tree(self.goal_scope.pyobject.get_ast())
|
||||
suite = root.find_suite(lineno)
|
||||
indents = sourceutils.get_indents(lines, lineno)
|
||||
while self.get_scope_indents() < indents:
|
||||
lineno = suite.get_start()
|
||||
indents = sourceutils.get_indents(lines, lineno)
|
||||
suite = suite.parent
|
||||
return lineno
|
||||
else:
|
||||
return min(self.goal_scope.get_end() + 1, lines.length())
|
||||
|
||||
def get_insertion_resource(self):
|
||||
return self.goal_pymodule.get_resource()
|
||||
|
||||
def get_insertion_offsets(self):
|
||||
if self.goal_scope.get_kind() == 'Class':
|
||||
start, end = sourceutils.get_body_region(self.goal_scope.pyobject)
|
||||
if self.goal_pymodule.source_code[start:end].strip() == 'pass':
|
||||
return start, end
|
||||
lines = self.goal_pymodule.lines
|
||||
start = lines.get_line_start(self.get_insertion_lineno())
|
||||
return (start, start)
|
||||
|
||||
def get_scope_indents(self):
|
||||
if self.goal_scope.get_kind() == 'Module':
|
||||
return 0
|
||||
return sourceutils.get_indents(self.goal_pymodule.lines,
|
||||
self.goal_scope.get_start()) + 4
|
||||
|
||||
def get_blank_lines(self):
|
||||
if self.goal_scope.get_kind() == 'Module':
|
||||
base_blanks = 2
|
||||
if self.goal_pymodule.source_code.strip() == '':
|
||||
base_blanks = 0
|
||||
if self.goal_scope.get_kind() == 'Class':
|
||||
base_blanks = 1
|
||||
if self.goal_scope.get_kind() == 'Function':
|
||||
base_blanks = 0
|
||||
if self.goal_scope == self.source_scope:
|
||||
return (0, base_blanks)
|
||||
return (base_blanks, 0)
|
||||
|
||||
def get_package(self):
|
||||
primary = self.primary
|
||||
if self.primary is None:
|
||||
return self.pycore.project.get_source_folders()[0]
|
||||
if isinstance(primary.get_object(), pyobjects.PyPackage):
|
||||
return primary.get_object().get_resource()
|
||||
raise exceptions.RefactoringError(
|
||||
'A module/package can be only created in a package.')
|
||||
|
||||
def primary_is_found(self):
|
||||
return self.goal_scope is not None
|
||||
|
||||
def element_already_exists(self):
|
||||
if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):
|
||||
return False
|
||||
return self.get_name() in self.goal_scope.get_defined_names()
|
||||
|
||||
def get_name(self):
|
||||
return worder.get_name_at(self.resource, self.offset)
|
||||
|
||||
|
||||
class _FunctionGenerationInfo(_GenerationInfo):
|
||||
|
||||
def _get_goal_scope(self):
|
||||
if self.is_constructor():
|
||||
return self.pyname.get_object().get_scope()
|
||||
if self.is_instance():
|
||||
return self.pyname.get_object().get_type().get_scope()
|
||||
if self.primary is None:
|
||||
return self._get_source_scope()
|
||||
pyobject = self.primary.get_object()
|
||||
if isinstance(pyobject, pyobjects.PyDefinedObject):
|
||||
return pyobject.get_scope()
|
||||
elif isinstance(pyobject.get_type(), pyobjects.PyClass):
|
||||
return pyobject.get_type().get_scope()
|
||||
|
||||
def element_already_exists(self):
|
||||
if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):
|
||||
return False
|
||||
return self.get_name() in self.goal_scope.get_defined_names()
|
||||
|
||||
def is_static_method(self):
|
||||
return self.primary is not None and \
|
||||
isinstance(self.primary.get_object(), pyobjects.PyClass)
|
||||
|
||||
def is_method(self):
|
||||
return self.primary is not None and \
|
||||
isinstance(self.primary.get_object().get_type(), pyobjects.PyClass)
|
||||
|
||||
def is_constructor(self):
|
||||
return self.pyname is not None and \
|
||||
isinstance(self.pyname.get_object(), pyobjects.PyClass)
|
||||
|
||||
def is_instance(self):
|
||||
if self.pyname is None:
|
||||
return False
|
||||
pyobject = self.pyname.get_object()
|
||||
return isinstance(pyobject.get_type(), pyobjects.PyClass)
|
||||
|
||||
def get_name(self):
|
||||
if self.is_constructor():
|
||||
return '__init__'
|
||||
if self.is_instance():
|
||||
return '__call__'
|
||||
return worder.get_name_at(self.resource, self.offset)
|
||||
|
||||
def get_passed_args(self):
|
||||
result = []
|
||||
source = self.source_pymodule.source_code
|
||||
finder = worder.Worder(source)
|
||||
if finder.is_a_function_being_called(self.offset):
|
||||
start, end = finder.get_primary_range(self.offset)
|
||||
parens_start, parens_end = finder.get_word_parens_range(end - 1)
|
||||
call = source[start:parens_end]
|
||||
parser = functionutils._FunctionParser(call, False)
|
||||
args, keywords = parser.get_parameters()
|
||||
for arg in args:
|
||||
if self._is_id(arg):
|
||||
result.append(arg)
|
||||
else:
|
||||
result.append('arg%d' % len(result))
|
||||
for name, value in keywords:
|
||||
result.append(name)
|
||||
return result
|
||||
|
||||
def _is_id(self, arg):
|
||||
def id_or_underline(c):
|
||||
return c.isalpha() or c == '_'
|
||||
for c in arg:
|
||||
if not id_or_underline(c) and not c.isdigit():
|
||||
return False
|
||||
return id_or_underline(arg[0])
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
"""rope refactor package
|
||||
|
||||
This package contains modules that perform python refactorings.
|
||||
Refactoring classes perform refactorings in 4 steps:
|
||||
|
||||
1. Collect some data for performing the refactoring and use them
|
||||
to construct a refactoring class. Like::
|
||||
|
||||
renamer = Rename(project, resource, offset)
|
||||
|
||||
2. Some refactorings give you useful information about the
|
||||
refactoring after their construction. Like::
|
||||
|
||||
print(renamer.get_old_name())
|
||||
|
||||
3. Give the refactoring class more information about how to
|
||||
perform the refactoring and get the changes this refactoring is
|
||||
going to make. This is done by calling `get_changes` method of the
|
||||
refactoring class. Like::
|
||||
|
||||
changes = renamer.get_changes(new_name)
|
||||
|
||||
4. You can commit the changes. Like::
|
||||
|
||||
project.do(changes)
|
||||
|
||||
These steps are like the steps IDEs usually do for performing a
|
||||
refactoring. These are the things an IDE does in each step:
|
||||
|
||||
1. Construct a refactoring object by giving it information like
|
||||
resource, offset and ... . Some of the refactoring problems (like
|
||||
performing rename refactoring on language keywords) can be reported
|
||||
here.
|
||||
2. Print some information about the refactoring and ask the user
|
||||
about the information that are necessary for completing the
|
||||
refactoring (like new name).
|
||||
3. Call the `get_changes` by passing it information asked from
|
||||
the user (if necessary) and get and preview the changes returned by
|
||||
it.
|
||||
4. perform the refactoring.
|
||||
|
||||
From ``0.5m5`` release the `get_changes()` method of some time-
|
||||
consuming refactorings take an optional `rope.base.taskhandle.
|
||||
TaskHandle` parameter. You can use this object for stopping or
|
||||
monitoring the progress of refactorings.
|
||||
|
||||
"""
|
||||
from rope.refactor.importutils import ImportOrganizer # noqa
|
||||
from rope.refactor.topackage import ModuleToPackage # noqa
|
||||
|
||||
|
||||
__all__ = ['rename', 'move', 'inline', 'extract', 'restructure', 'topackage',
|
||||
'importutils', 'usefunction', 'change_signature',
|
||||
'encapsulate_field', 'introduce_factory', 'introduce_parameter',
|
||||
'localtofield', 'method_object', 'multiproject']
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue