1. zjes
  2. rope_py3k

Commits

Ali Gholami Rudi  committed 04bc7cf

Restructuring packages

  • Participants
  • Parent commits 2be74bd
  • Branches trunk

Comments (0)

Files changed (49)

File rope/base/__init__.py

  • Ignore whitespace
Empty file added.

File rope/base/codeanalyze.py

View file
  • Ignore whitespace
+import compiler
+import re
+import tokenize
+import token
+
+import rope.base.pyobjects
+import rope.base.pynames
+import rope.base.exceptions
+
+
+class WordRangeFinder(object):
+
+    def __init__(self, source_code):
+        self.source_code = source_code
+    
+    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):
+        current_offset = offset + 1
+        while current_offset < len(self.source_code) and \
+              self._is_id_char(current_offset):
+            current_offset += 1;
+        return current_offset - 1
+
+    def _find_last_non_space_char(self, offset):
+        if offset <= 0:
+            return 0
+        current_offset = offset
+        while current_offset >= 0 and self.source_code[current_offset] in ' \t\n':
+            while current_offset >= 0 and self.source_code[current_offset] in ' \t':
+                current_offset -= 1
+            if current_offset >= 0 and self.source_code[current_offset] == '\n':
+                current_offset -= 1
+                if current_offset >= 0 and self.source_code[current_offset] == '\\':
+                    current_offset -= 1
+        return current_offset
+    
+    def get_word_before(self, offset):
+        return self.source_code[self._find_word_start(offset - 1):offset]
+    
+
+    def get_word_at(self, offset):
+        offset = self._get_fixed_offset(offset)
+        return self.source_code[self._find_word_start(offset):
+                                self._find_word_end(offset) + 1]
+
+    def _get_fixed_offset(self, offset):
+        if offset >= len(self.source_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.source_code) - 1 and self._is_id_char(offset + 1):
+                return offset + 1
+        return offset
+    
+    def _is_id_char(self, offset):
+        return self.source_code[offset].isalnum() or self.source_code[offset] == '_'
+    
+    def _find_string_start(self, offset):
+        kind = self.source_code[offset]
+        current_offset = offset - 1
+        while self.source_code[current_offset] != kind:
+            current_offset -= 1
+        return current_offset
+    
+    def _find_parens_start(self, offset):
+        current_offset = self._find_last_non_space_char(offset - 1)
+        while current_offset >= 0 and self.source_code[current_offset] not in '[({':
+            if self.source_code[current_offset] in ':,':
+                pass
+            else:
+                current_offset = self._find_primary_start(current_offset)
+            current_offset = self._find_last_non_space_char(current_offset - 1)
+        return current_offset
+
+    def _find_atom_start(self, offset):
+        old_offset = offset
+        if self.source_code[offset] in '\n\t ':
+            offset = self._find_last_non_space_char(offset)
+        if self.source_code[offset] in '\'"':
+            return self._find_string_start(offset)
+        if self.source_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):
+        last_parens = offset
+        current_offset = self._find_last_non_space_char(offset)
+        while current_offset > 0 and self.source_code[current_offset] in ')]}':
+            last_parens = current_offset = self._find_parens_start(current_offset)
+            current_offset = self._find_last_non_space_char(current_offset - 1)
+
+        if current_offset > 0 and self.source_code[current_offset] in '\'"':
+            return self._find_string_start(current_offset)
+        elif current_offset > 0 and self._is_id_char(current_offset):
+            return self._find_word_start(current_offset)
+        return last_parens
+
+    def _find_primary_start(self, offset):
+        if offset >= len(self.source_code):
+            offset = len(self.source_code) - 1
+        current_offset = offset + 1
+        if self.source_code[offset] != '.':
+            current_offset = self._find_primary_without_dot_start(offset)
+        while current_offset > 0 and \
+              self.source_code[self._find_last_non_space_char(current_offset - 1)] == '.':
+            dot_position = self._find_last_non_space_char(current_offset - 1)
+            current_offset = self._find_primary_without_dot_start(dot_position - 1)
+            
+            if not self._is_id_char(current_offset):
+                break
+
+        return current_offset
+    
+    def get_primary_at(self, offset):
+        offset = self._get_fixed_offset(offset)
+        return self.source_code[self._find_primary_start(offset):
+                                self._find_word_end(offset) + 1].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)
+        word_start = self._find_atom_start(offset - 1)
+        real_start = self._find_primary_start(offset - 1)
+        if self.source_code[word_start:offset].strip() == '':
+            word_start = offset
+        if self.source_code[real_start:offset].strip() == '':
+            real_start = offset
+        if real_start == word_start:
+            return ('', self.source_code[word_start:offset], word_start)
+        else:
+            if self.source_code[offset - 1] == '.':
+                return (self.source_code[real_start:offset - 1], '', offset)
+            last_dot_position = word_start
+            if self.source_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)
+            return (self.source_code[real_start:last_char_position + 1],
+                    self.source_code[word_start:offset], word_start)
+    
+    def _get_line_start(self, offset):
+        while offset > 0 and self.source_code[offset] != '\n':
+            offset -= 1
+        return offset
+    
+    def _get_line_end(self, offset):
+        while offset < len(self.source_code) and self.source_code[offset] != '\n':
+            offset += 1
+        return offset
+    
+    def _is_followed_by_equals(self, offset):
+        while offset < len(self.source_code) and self.source_code[offset] in ' \\':
+            if self.source_code[offset] == '\\':
+                offset = self._get_line_end(offset)
+            offset += 1
+        if offset + 1 < len(self.source_code) and \
+           self.source_code[offset] == '=' and self.source_code[offset + 1] != '=' :
+            return True
+        return False
+    
+    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) + 1
+        if '.' in self.source_code[word_start:word_end]:
+            return False
+        line_start = self._get_line_start(word_start)
+        line = self.source_code[line_start:word_start].strip()
+        if line == '' and self._is_followed_by_equals(word_end):
+            return True
+        return False
+
+    def is_a_class_or_function_name_in_header(self, offset):
+        word_start = self._find_word_start(offset - 1)
+        word_end = self._find_word_end(offset - 1) + 1
+        line_start = self._get_line_start(word_start)
+        prev_word = self.source_code[line_start:word_start].strip()
+        return prev_word in ['def', 'class']
+    
+    def _find_first_non_space_char(self, offset):
+        if offset >= len(self.source_code):
+            return len(self.source_code)
+        current_offset = offset
+        while current_offset < len(self.source_code) and\
+              self.source_code[current_offset] in ' \t\n':
+            while current_offset < len(self.source_code) and \
+                  self.source_code[current_offset] in ' \t\n':
+                current_offset += 1
+            if current_offset + 1 < len(self.source_code) and self.source_code[current_offset] == '\\':
+                current_offset += 2
+        return current_offset
+    
+    def is_a_function_being_called(self, offset):
+        word_start = self._find_word_start(offset - 1)
+        word_end = self._find_word_end(offset - 1) + 1
+        next_char = self._find_first_non_space_char(word_end)
+        return not self.is_a_class_or_function_name_in_header(offset) and \
+               next_char < len(self.source_code) and self.source_code[next_char] == '('
+    
+    def _find_import_pair_end(self, start):
+        next_char = self._find_first_non_space_char(start)
+        if self.source_code[next_char] == '(':
+            try:
+                return self.source_code.index(')', next_char)
+            except ValueError:
+                return SyntaxError('Unmatched Parens')
+        else:
+            current_offset = next_char
+            while current_offset < len(self.source_code):
+                if self.source_code[current_offset] == '\n':
+                    break
+                if self.source_code[current_offset] == '\\':
+                    current_offset += 1
+                current_offset += 1
+            return current_offset
+    
+    def is_import_statement(self, offset):
+        try:
+            last_import = self.source_code.rindex('import ', 0, offset)
+            import_names = last_import + 8
+        except ValueError:
+            return False
+        return self._find_import_pair_end(import_names) >= offset
+
+    def is_from_statement(self, offset):
+        try:
+            last_from = self.source_code.rindex('from ', 0, offset)
+            from_import = self.source_code.index(' import ', last_from)
+            from_names = from_import + 8
+        except ValueError:
+            return False
+        return self._find_import_pair_end(from_names) >= offset
+
+    def is_from_statement_module(self, offset):
+        if offset >= len(self.source_code) - 1:
+            return False
+        stmt_start = self._find_primary_start(offset)
+        line_start = self._get_line_start(stmt_start)
+        prev_word = self.source_code[line_start:stmt_start].strip()
+        return prev_word == 'from'
+    
+    def is_a_name_after_from_import(self, offset):
+        try:
+            last_from = self.source_code.rindex('from ', 0, offset)
+            from_import = self.source_code.index(' import ', last_from)
+            from_names = from_import + 8
+        except ValueError:
+            return False
+        if from_names >= offset:
+            return False
+        return self._find_import_pair_end(from_names) >= offset
+    
+    def is_function_keyword_parameter(self, offset):
+        word_end = self._find_word_end(offset)
+        if word_end + 1 == len(self.source_code):
+            return False
+        next_char = self._find_first_non_space_char(word_end + 1)
+        if next_char + 2 >= len(self.source_code) or \
+           self.source_code[next_char] != '=' or \
+           self.source_code[next_char + 1] == '=':
+            return False
+        word_start = self._find_word_start(offset)
+        prev_char = self._find_last_non_space_char(word_start - 1)
+        if prev_char - 1 < 0 or self.source_code[prev_char] not in ',(':
+            return False
+        return True
+    
+    def find_parens_start_from_inside(self, offset):
+        current_offset = offset
+        opens = 1
+        while current_offset > 0:
+            if self.source_code[current_offset] == '(':
+                opens -= 1
+            if opens == 0:
+                break
+            if self.source_code[current_offset] == ')':
+                opens += 1
+            current_offset -= 1
+        return current_offset
+    
+    def is_assigned_here(self, offset):
+        operation = self.get_assignment_type(offset)
+        operations = ('=', '-=', '+=', '*=', '/=', '%=', '**=',
+                      '>>=', '<<=', '&=', '^=', '|=')
+        return operation in operations
+
+    def get_assignment_type(self, offset):
+        word_end = self._find_word_end(offset)
+        next_char = self._find_first_non_space_char(word_end + 1)
+        current_char = next_char
+        while current_char + 1 < len(self.source_code) and \
+              (self.source_code[current_char] != '=' or \
+               self.source_code[current_char + 1] == '=') and \
+              current_char < next_char + 3:
+            current_char += 1
+        operation = self.source_code[next_char:current_char + 1]
+        return operation
+
+
+class StatementEvaluator(object):
+
+    def __init__(self, scope):
+        self.scope = scope
+        self.result = None
+
+    def visitName(self, node):
+        self.result = self.scope.lookup(node.name)
+    
+    def visitGetattr(self, node):
+        pyname = StatementEvaluator.get_statement_result(self.scope, node.expr)
+        if pyname is not None:
+            try:
+                self.result = pyname.get_object().get_attribute(node.attrname)
+            except rope.base.exceptions.AttributeNotFoundException:
+                self.result = None
+
+    def visitCallFunc(self, node):
+        pyname = StatementEvaluator.get_statement_result(self.scope, node.node)
+        if pyname is None:
+            return
+        pyobject = pyname.get_object()
+        if pyobject.get_type() == rope.base.pyobjects.PyObject.get_base_type('Type'):
+            self.result = rope.base.pynames.AssignedName(pyobject=rope.base.pyobjects.PyObject(type_=pyobject))
+        elif pyobject.get_type() == rope.base.pyobjects.PyObject.get_base_type('Function'):
+            self.result = rope.base.pynames.AssignedName(pyobject=pyobject._get_returned_object())
+        elif '__call__' in pyobject.get_attributes():
+            call_function = pyobject.get_attribute('__call__')
+            self.result = rope.base.pynames.AssignedName(
+                pyobject=call_function.get_object()._get_returned_object())
+    
+    def visitAdd(self, node):
+        pass
+
+    def visitAnd(self, node):
+        pass
+
+    def visitBackquote(self, node):
+        pass
+
+    def visitBitand(self, node):
+        pass
+
+    def visitBitor(self, node):
+        pass
+
+    def visitXor(self, node):
+        pass
+
+    def visitCompare(self, node):
+        pass
+    
+    def visitDict(self, node):
+        pass
+    
+    def visitFloorDiv(self, node):
+        pass
+    
+    def visitList(self, node):
+        pass
+    
+    def visitListComp(self, node):
+        pass
+
+    def visitMul(self, node):
+        pass
+    
+    def visitNot(self, node):
+        pass
+    
+    def visitOr(self, node):
+        pass
+    
+    def visitPower(self, node):
+        pass
+    
+    def visitRightShift(self, node):
+        pass
+    
+    def visitLeftShift(self, node):
+        pass
+    
+    def visitSlice(self, node):
+        pass
+    
+    def visitSliceobj(self, node):
+        pass
+    
+    def visitTuple(self, node):
+        pass
+    
+    def visitSubscript(self, node):
+        pass
+
+    @staticmethod
+    def get_statement_result(scope, node):
+        evaluator = StatementEvaluator(scope)
+        compiler.walk(node, evaluator)
+        return evaluator.result
+    
+    @staticmethod
+    def get_string_result(scope, string):
+        evaluator = StatementEvaluator(scope)
+        node = compiler.parse(string)
+        compiler.walk(node, evaluator)
+        return evaluator.result
+
+
+class ScopeNameFinder(object):
+    
+    def __init__(self, pymodule):
+        self.source_code = pymodule.source_code
+        self.module_scope = pymodule.get_scope()
+        self.lines = SourceLinesAdapter(self.source_code)
+        self.word_finder = WordRangeFinder(self.source_code)
+
+    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.pyobject.get_type() == rope.base.pyobjects.PyObject.get_base_type('Type') and \
+           self.word_finder.is_a_class_or_function_name_in_header(offset):
+            return True
+        if lineno != holding_scope.get_start() and \
+           holding_scope.pyobject.get_type() == rope.base.pyobjects.PyObject.get_base_type('Type') and \
+           self.word_finder._is_name_assigned_in_class_body(offset):
+            return True
+        return False
+    
+    def _is_function_name_in_function_header(self, holding_scope, offset, lineno):
+        if lineno == holding_scope.get_start() and \
+           holding_scope.pyobject.get_type() == rope.base.pyobjects.PyObject.get_base_type('Function') and \
+           self.word_finder.is_a_class_or_function_name_in_header(offset):
+            return True
+        return False
+
+    def get_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.word_finder.is_function_keyword_parameter(offset):
+            keyword_name = self.word_finder.get_word_at(offset)
+            function_parens = self.word_finder.find_parens_start_from_inside(offset)
+            function_pyname = self.get_pyname_at(function_parens - 1)
+            if function_pyname is not None:
+                function_pyobject = function_pyname.get_object()
+                if function_pyobject.get_type() == \
+                   rope.base.pyobjects.PyObject.get_base_type('Type'):
+                    function_pyobject = function_pyobject.get_attribute('__init__').get_object()
+                return function_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.word_finder.get_primary_at(offset).strip()
+            try:
+                return class_scope.pyobject.get_attribute(name)
+            except rope.base.exceptions.AttributeNotFoundException:
+                return None
+        # function header
+        if self._is_function_name_in_function_header(holding_scope, offset, lineno):
+            name = self.word_finder.get_primary_at(offset).strip()
+            return holding_scope.parent.get_name(name)
+        # from statement module
+        if self.word_finder.is_from_statement_module(offset):
+            module = self.word_finder.get_primary_at(offset)
+            module_pyname = self._find_module(module)
+            return module_pyname
+        name = self.word_finder.get_primary_at(offset)
+        result = self.get_pyname_in_scope(holding_scope, name)
+        return result
+    
+    def _find_module(self, module_name):
+        current_folder = None
+        if self.module_scope.pyobject.get_resource():
+            current_folder = self.module_scope.pyobject.get_resource().get_parent()
+        dot_count = 0
+        if module_name.startswith('.'):
+            for c in module_name:
+                if c == '.':
+                    dot_count += 1
+                else:
+                    break
+        return rope.base.pynames.ImportedModule(self.module_scope.pyobject,
+                                           module_name[dot_count:], dot_count)
+
+    def get_pyname_in_scope(self, holding_scope, name):
+        ast = compiler.parse(name)
+        result = StatementEvaluator.get_statement_result(holding_scope, ast)
+        return result
+
+
+def get_pyname_at(pycore, resource, offset):
+    """Finds the pyname at the offset
+    
+    This function is unefficient for multiple calls because of the
+    recalculation of initialization data
+    """
+    pymodule = pycore.resource_to_pyobject(resource)
+    source_code = pymodule.source_code
+    pyname_finder = rope.base.codeanalyze.ScopeNameFinder(pymodule)
+    pyname = pyname_finder.get_pyname_at(offset)
+    return pyname
+
+def get_name_at(resource, offset):
+    source_code = resource.read()
+    word_finder = rope.base.codeanalyze.WordRangeFinder(source_code)
+    name = word_finder.get_primary_at(offset).split('.')[-1]
+    return name
+
+
+class Lines(object):
+
+    def get_line(self, line_number):
+        pass
+
+    def length(self):
+        pass
+
+
+class SourceLinesAdapter(Lines):
+    """Adapts source_code to Lines interface
+    
+    Note: The creation of this class is expensive.
+    """    
+    
+    def __init__(self, source_code):
+        self.source_code = source_code
+        self.line_starts = None
+        self._initialize_line_starts()
+    
+    def _initialize_line_starts(self):
+        self.line_starts = []
+        self.line_starts.append(0)
+        for i, c in enumerate(self.source_code):
+            if c == '\n':
+                self.line_starts.append(i + 1)
+        self.line_starts.append(len(self.source_code) + 1)
+    
+    def get_line(self, line_number):
+        return self.source_code[self.line_starts[line_number - 1]:
+                                self.line_starts[line_number] - 1]
+    
+    def length(self):
+        return len(self.line_starts) - 1
+
+    def get_line_number(self, offset):
+        down = 0
+        up = len(self.line_starts)
+        current = (down + up) / 2
+        while down <= current < up:
+            if self.line_starts[current] <= offset < self.line_starts[current + 1]:
+                return current + 1
+            if offset < self.line_starts[current]:
+                up = current - 1
+            else:
+                down = current + 1
+            current = (down + up) / 2
+        return current + 1
+
+    def get_line_start(self, line_number):
+        return self.line_starts[line_number - 1]
+
+    def get_line_end(self, line_number):
+        return self.line_starts[line_number] - 1
+
+
+class ArrayLinesAdapter(Lines):
+
+    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 LogicalLineFinder(object):
+    
+    def __init__(self, lines):
+        self.lines = lines
+    
+    def get_logical_line_in(self, line_number):
+        block_start = StatementRangeFinder.get_block_start(self.lines,
+                                                           line_number)
+        readline = LinesToReadline(self.lines, block_start)
+        last_line_start = block_start
+        for current in tokenize.generate_tokens(readline):
+            current_lineno = current[2][0] + block_start - 1
+            if current[0] == token.NEWLINE:
+                if current_lineno >= line_number:
+                    return (self._get_first_non_empty_line(last_line_start),
+                            current_lineno)
+                last_line_start = current_lineno + 1
+        return (last_line_start, self.lines.length())
+    
+    def _get_first_non_empty_line(self, line_number):
+        current = line_number
+        while current <= self.lines.length():
+            line = self.lines.get_line(current)
+            if line.strip() != '' and not line.startswith('#'):
+                return current
+            current += 1
+        return current
+
+
+class StatementRangeFinder(object):
+    """A method object for finding the range of a statement"""
+
+    def __init__(self, lines, lineno):
+        self.lines = lines
+        self.lineno = lineno
+        self.in_string = ''
+        self.open_count = 0
+        self.explicit_continuation = False
+        self.open_parens = []
+
+    def _analyze_line(self, current_line_number):
+        current_line = self.lines.get_line(current_line_number)
+        for i, char in enumerate(current_line):
+            if char in '\'"':
+                if self.in_string == '':
+                    self.in_string = char
+                    if char * 3 == current_line[i:i + 3]:
+                        self.in_string = char * 3
+                elif self.in_string == current_line[i:i + len(self.in_string)] and \
+                     not (i > 0 and current_line[i - 1] == '\\' and
+                          not (i > 1 and current_line[i - 2:i] == '\\\\')):
+                    self.in_string = ''
+            if self.in_string != '':
+                continue
+            if char == '#':
+                break
+            if char in '([{':
+                self.open_count += 1
+                self.open_parens.append((current_line_number, i))
+            if char in ')]}':
+                self.open_count -= 1
+                if self.open_parens:
+                    self.open_parens.pop()
+        if current_line.rstrip().endswith('\\'):
+            self.explicit_continuation = True
+        else:
+            self.explicit_continuation = False
+
+    def analyze(self):
+        last_statement = 1
+        for current_line_number in range(self.get_block_start(self.lines, self.lineno),
+                                         self.lineno + 1):
+            if not self.explicit_continuation and self.open_count == 0 and self.in_string == '':
+                last_statement = current_line_number
+            self._analyze_line(current_line_number)
+        last_indents = self.get_line_indents(last_statement)
+        end_line = self.lineno
+        for i in range(self.lineno + 1, self.lines.length() + 1):
+            if self.get_line_indents(i) >= last_indents:
+                end_line = i
+            else:
+                break
+        self.block_end = end_line
+        self.statement_start = last_statement
+
+    def get_statement_start(self):
+        return self.statement_start
+
+    def get_block_end(self):
+        return self.block_end
+
+    def last_open_parens(self):
+        if not self.open_parens:
+            return None
+        return self.open_parens[-1]
+
+    def is_line_continued(self):
+        return self.open_count != 0 or self.explicit_continuation
+
+    def get_line_indents(self, line_number):
+        indents = 0
+        for char in self.lines.get_line(line_number):
+            if char == ' ':
+                indents += 1
+            else:
+                break
+        return indents
+    
+    @staticmethod
+    def get_block_start(lines, lineno):
+        """Aproximating block start"""
+        pattern = StatementRangeFinder.get_block_start_patterns()
+        for i in reversed(range(1, lineno + 1)):
+            if pattern.search(lines.get_line(i)) is not None:
+                return i
+        return 1
+
+    @classmethod
+    def get_block_start_patterns(cls):
+        if not hasattr(cls, '__block_start_pattern'):
+            pattern = '^\\s*(((def|class|if|elif|except|for|while|with)\\s)|((try|else|finally|except)\\s*:))'
+            cls.__block_start_pattern = re.compile(pattern, re.M)
+        return cls.__block_start_pattern
+    
+
+# XXX: Should we use it
+class xxxStatementRangeFinder(object):
+    """A method object for finding the range of a statement"""
+
+    def __init__(self, lines, lineno):
+        self.lines = lines
+        self.lineno = lineno
+        self.block_start = StatementRangeFinder.get_block_start(lines, lineno)
+        self.open_parens = []
+        self.statement_start = self.block_start
+        self.block_end = lineno
+        self.continued = True
+
+    def analyze(self):
+        readline = LinesToReadline(self.lines, self.block_start)
+        try:
+            for current in tokenize.generate_tokens(readline):
+                current_lineno = current[2][0] + self.block_start - 1
+                if current_lineno < self.lineno:
+                    if current[0] == token.NEWLINE:
+                        self.statement_start = current_lineno + 1
+                if current_lineno <= self.lineno:
+                    if current[0] == token.OP and current[1] in '([{':
+                        self.open_parens.append((current_lineno, current[2][1]))
+                    if current[0] == token.OP and current[1] in ')]}':
+                        self.open_parens.pop()
+
+                if current_lineno == self.lineno:
+                    if current[0] in (tokenize.NEWLINE, tokenize.COMMENT):
+                        self.continued = False
+
+                if current_lineno > self.lineno:
+                    self.block_end = current_lineno - 1
+                    if current[0] == token.DEDENT:
+                        break
+        except tokenize.TokenError:
+            pass
+
+    def get_statement_start(self):
+        return self.statement_start
+
+    def get_block_end(self):
+        return self.block_end
+
+    def last_open_parens(self):
+        if not self.open_parens:
+            return None
+        return self.open_parens[-1]
+
+    def is_line_continued(self):
+        return self.continued
+
+    def get_line_indents(self, line_number):
+        indents = 0
+        for char in self.lines.get_line(line_number):
+            if char == ' ':
+                indents += 1
+            else:
+                break
+        return indents
+    

File rope/base/dynamicoi.py

View file
  • Ignore whitespace
+import os
+import re
+import subprocess
+import sys
+import socket
+import cPickle as pickle
+import marshal
+import tempfile
+import threading
+
+import rope.base.pyobjects
+
+
+class DynamicObjectInference(object):
+    
+    def __init__(self, pycore):
+        self.pycore = pycore
+        self.files = {}
+    
+    def run_module(self, resource, args=None, stdin=None, stdout=None):
+        """Return a PythonFileRunner for controlling the process"""
+        return PythonFileRunner(self.pycore, resource, args, stdin,
+                                stdout, self._data_received)
+    
+    def infer_returned_object(self, pyobject):
+        organizer = self._find_organizer(pyobject)
+        if organizer:
+            return organizer.returned.to_pyobject(self.pycore.project)
+
+    def infer_parameter_objects(self, pyobject):
+        organizer = self._find_organizer(pyobject)
+        if organizer:
+            pyobjects = [parameter.to_pyobject(self.pycore.project)
+                         for parameter in organizer.args]
+            return pyobjects
+    
+    def _find_organizer(self, pyobject):
+        resource = pyobject.get_module().get_resource()
+        if resource is None:
+            return
+        path = os.path.abspath(resource._get_real_path())
+        lineno = pyobject._get_ast().lineno
+        if path in self.files and lineno in self.files[path]:
+            organizer = self.files[path][lineno]
+            return organizer
+    
+    def _data_received(self, data):
+        path = data[0][1]
+        lineno = data[0][2]
+        if path not in self.files:
+            self.files[path] = {}
+        if lineno not in self.files[path]:
+            self.files[path][lineno] = _CallInformationOrganizer()
+        returned = _ObjectPersistedForm.create_persistent_object(data[2])
+        args = [_ObjectPersistedForm.create_persistent_object(arg) for arg in data[1]]
+        self.files[path][lineno].add_call_information(args, returned)
+
+
+class _CallInformationOrganizer(object):
+    
+    def __init__(self):
+        self.args = None
+        self.returned = None
+    
+    def add_call_information(self, args, returned):
+        if self.returned is None or \
+           not isinstance(returned, (_PersistedNone, _PersistedUnknown)):
+            self.returned = returned
+        if self.returned is None or args and \
+           not isinstance(args[0], (_PersistedNone, _PersistedUnknown)):
+            self.args = args
+
+
+class _ObjectPersistedForm(object):
+    
+    def _get_pymodule(self, project, path):
+        root = os.path.abspath(project.get_root_address())
+        if path.startswith(root):
+            relative_path = path[len(root):]
+            if relative_path.startswith('/'):
+                relative_path = relative_path[1:]
+            resource = project.get_resource(relative_path)
+        else:
+            resource = project.get_out_of_project_resource(path)
+        return project.get_pycore().resource_to_pyobject(resource)
+    
+    def _get_pyobject_at(self, project, path, lineno):
+        scope = self._get_pymodule(project, path).get_scope()
+        inner_scope = scope.get_inner_scope_for_line(lineno)
+        return inner_scope.pyobject
+
+    # TODO: Implement __eq__ for subclasses
+    def __eq__(self, object_):
+        if type(object) != type(self):
+            return False
+
+    @staticmethod
+    def create_persistent_object(data):
+        type_ = data[0]
+        if type_ == 'none':
+            return _PersistedNone()
+        if type_ == 'module':
+            return _PersistedModule(*data[1:])
+        if type_ == 'function':
+            return _PersistedFunction(*data[1:])
+        if type_ == 'class':
+            return _PersistedClass(*data[1:])
+        if type_ == 'instance':
+            return _PersistedClass(is_instance=True, *data[1:])
+        return _PersistedUnknown()
+
+
+class _PersistedNone(_ObjectPersistedForm):
+
+    def to_pyobject(self, project):
+        return None
+
+
+class _PersistedUnknown(_ObjectPersistedForm):
+
+    def to_pyobject(self, project):
+        return None
+
+
+class _PersistedModule(_ObjectPersistedForm):
+    
+    def __init__(self, path):
+        self.path = path
+    
+    def to_pyobject(self, project):
+        return self._get_pymodule(project, self.path)
+
+
+class _PersistedFunction(_ObjectPersistedForm):
+    
+    def __init__(self, path, lineno):
+        self.path = path
+        self.lineno = lineno
+    
+    def to_pyobject(self, project):
+        return self._get_pyobject_at(project, self.path, self.lineno)
+
+
+class _PersistedClass(_ObjectPersistedForm):
+    
+    def __init__(self, path, name, is_instance=False):
+        self.path = path
+        self.name = name
+        self.is_instance = is_instance
+    
+    def to_pyobject(self, project):
+        pymodule = self._get_pymodule(project, self.path)
+        module_scope = pymodule.get_scope()
+        suspected_pyobject = None
+        if self.name in module_scope.get_names():
+            suspected_pyobject = module_scope.get_name(self.name).get_object()
+        if suspected_pyobject is not None and \
+           suspected_pyobject.get_type() == rope.base.pyobjects.PyObject.get_base_type('Type'):
+            if self.is_instance:
+                return rope.base.pyobjects.PyObject(suspected_pyobject)
+            else:
+                return suspected_pyobject
+        else:
+            lineno = self._find_occurrence(pymodule.get_resource().read())
+            if lineno is not None:
+                inner_scope = module_scope.get_inner_scope_for_line(lineno)
+                return inner_scope.pyobject
+    
+    def _find_occurrence(self, source):
+        pattern = re.compile(r'^\s*class\s*' + self.name + r'\b')
+        lines = source.split('\n')
+        for i in range(len(lines)):
+            if pattern.match(lines[i]):
+                return i + 1
+
+
+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):
+        env = dict(os.environ)
+        source_folders = []
+        file_path = self.file._get_real_path()
+        for folder in self.file.get_project().get_pycore().get_source_folders():
+            source_folders.append(os.path.abspath(folder._get_real_path()))
+        env['PYTHONPATH'] = env.get('PYTHONPATH', '') + os.pathsep + \
+                            os.pathsep.join(source_folders)
+        runmod_path = self.pycore.find_module('rope.base.runmod')._get_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,
+                os.path.abspath(self.pycore.project.get_root_address()),
+                os.path.abspath(self.file._get_real_path())]
+        if self.args is not None:
+            args.extend(self.args)
+        self.process = subprocess.Popen(executable=sys.executable, args=args,
+                                        cwd=os.path.split(file_path)[0], stdin=self.stdin,
+                                        stdout=self.stdout, stderr=self.stdout, env=env)
+    
+    def _init_data_receiving(self):
+        if self.analyze_data is None:
+            return
+        # Disabling FIFO data transfer due to blocking for running
+        # unittests.
+        # 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):
+        for data in self.receiver.receive_data():
+            self.analyze_data(data)
+        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. This does *not* work on windows."""
+        os.kill(self.process.pid, 9)
+    
+    def add_finishing_observer(self, observer):
+        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, e:
+                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('r')
+        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 unsecure 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)

File rope/base/exceptions.py

View file
  • Ignore whitespace
+class RopeException(Exception):
+    """Base exception for rope"""
+
+
+class ModuleNotFoundException(RopeException):
+    """Module not found exception"""
+
+
+class AttributeNotFoundException(RopeException):
+    """Attribute not found exception"""
+
+
+class NameNotFoundException(RopeException):
+    """Attribute not found exception"""
+
+
+class RefactoringException(RopeException):
+    """Errors for performing a refactoring"""
+
+
+class RopeUIException(RopeException):
+    """Base exception for user interface parts of rope"""
+

File rope/base/fscommands.py

View file
  • Ignore whitespace
+import os
+import shutil
+
+try:
+    import pysvn
+except ImportError:
+    pass
+
+
+def create_fscommands(project):
+    root = project.get_root_folder()
+    if 'pysvn' in globals() and root.has_child('.svn'):
+        return SubversionCommands()
+    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)
+
+
+class SubversionCommands(object):
+    
+    def __init__(self):
+        self.normal_actions = FileSystemCommands()
+        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)

File rope/base/objectinfer.py

View file
  • Ignore whitespace
+from rope.base.pyobjects import *
+import rope.base.codeanalyze
+
+
+class ObjectInfer(object):
+
+    def __init__(self, pycore):
+        self.pycore = pycore
+    
+    def infer_object(self, pyname):
+        """Infers the `PyObject` this `PyName` references"""
+        if not pyname.assigned_asts:
+            return
+        for assign_node in reversed(pyname.assigned_asts):
+            try:
+                lineno = 1
+                if hasattr(assign_node, 'lineno') and assign_node.lineno is not None:
+                    lineno = assign_node.lineno
+                holding_scope = pyname.module.get_scope().\
+                                get_inner_scope_for_line(lineno)
+                resulting_pyname = rope.base.codeanalyze.StatementEvaluator.\
+                                   get_statement_result(holding_scope, assign_node)
+                if resulting_pyname is None:
+                    return None
+                return resulting_pyname.get_object()
+            except IsBeingInferredException:
+                pass
+    
+    def infer_returned_object(self, pyobject):
+        """Infers the `PyObject` this callable `PyObject` returns after calling"""
+        dynamically_inferred_object = self.pycore.dynamicoi.infer_returned_object(pyobject)
+        if dynamically_inferred_object is not None:
+            return dynamically_inferred_object
+        scope = pyobject.get_scope()
+        if not scope._get_returned_asts():
+            return
+        for returned_node in reversed(scope._get_returned_asts()):
+            try:
+                resulting_pyname = rope.base.codeanalyze.StatementEvaluator.\
+                                   get_statement_result(scope, returned_node)
+                if resulting_pyname is None:
+                    return None
+                return resulting_pyname.get_object()
+            except IsBeingInferredException:
+                pass
+    
+    def infer_parameter_objects(self, pyobject):
+        """Infers the `PyObject` of parameters of this callable `PyObject`"""
+        dynamically_inferred_object = self.pycore.dynamicoi.infer_parameter_objects(pyobject)
+        if dynamically_inferred_object is not None:
+            return dynamically_inferred_object
+        

File rope/base/project.py

View file
  • Ignore whitespace
+import os
+
+import rope.base.pycore
+import rope.base.fscommands
+from rope.base.exceptions import RopeException
+
+
+class Project(object):
+    """A Project containing files and folders"""
+
+    def __init__(self, projectRootAddress):
+        self.root = projectRootAddress
+        if not os.path.exists(self.root):
+            os.mkdir(self.root)
+        elif not os.path.isdir(self.root):
+            raise RopeException('Project root exists and is not a directory')
+        self.pycore = rope.base.pycore.PyCore(self)
+        self.resources = {}
+        self.resources[''] = RootFolder(self)
+        self.out_of_project_resources = {}
+        self.fscommands = rope.base.fscommands.create_fscommands(self)
+
+    def get_root_folder(self):
+        return self.get_resource('')
+
+    def get_root_address(self):
+        return self.root
+
+    def get_resource(self, resource_name):
+        if resource_name not in self.resources:
+            path = self._get_resource_path(resource_name)
+            if not os.path.exists(path):
+                raise RopeException('Resource %s does not exist' % resource_name)
+            elif os.path.isfile(path):
+                self.resources[resource_name] = File(self, resource_name)
+            elif os.path.isdir(path):
+                self.resources[resource_name] = Folder(self, resource_name)
+            else:
+                raise RopeException('Unknown resource ' + resource_name)
+        return self.resources[resource_name]
+
+    def get_files(self):
+        return self._get_files_recursively(self.get_root_folder())
+
+    def _create_file(self, file_name):
+        file_path = self._get_resource_path(file_name)
+        if os.path.exists(file_path):
+            if os.path.isfile(file_path):
+                raise RopeException('File already exists')
+            else:
+                raise RopeException('A folder with the same name'
+                                    ' as this file already exists')
+        try:
+            self.fscommands.create_file(file_path)
+        except IOError, e:
+            raise RopeException(e)
+
+    def _create_folder(self, folder_name):
+        folder_path = self._get_resource_path(folder_name)
+        if os.path.exists(folder_path):
+            if not os.path.isdir(folder_path):
+                raise RopeException('A file with the same name as'
+                                    ' this folder already exists')
+            else:
+                raise RopeException('Folder already exists')
+        self.fscommands.create_folder(folder_path)
+
+    def _get_resource_path(self, name):
+        return os.path.join(self.root, *name.split('/'))
+
+    def _get_files_recursively(self, folder):
+        result = []
+        for file in folder.get_files():
+            if not file.get_name().endswith('.pyc'):
+                result.append(file)
+        for folder in folder.get_folders():
+            if not folder.get_name().startswith('.'):
+                result.extend(self._get_files_recursively(folder))
+        return result
+
+    def get_pycore(self):
+        return self.pycore
+
+    def get_out_of_project_resource(self, path):
+        path = os.path.abspath(path)
+        if path not in self.out_of_project_resources:
+            if not os.path.exists(path):
+                raise RopeException('Resource %s does not exist' % path)
+            elif os.path.isfile(path):
+                self.out_of_project_resources[path] = OutOfProjectFile(self, path)
+            elif os.path.isdir(path):
+                self.out_of_project_resources[path] = OutOfProjectFolder(self, path)
+            else:
+                raise RopeException('Unknown resource ' + path)
+        return self.out_of_project_resources[path]
+    
+    def _update_resource_location(self, resource, new_location=None):
+        del self.resources[resource.get_path()]
+        if new_location is not None:
+            self.resources[new_location] = resource
+
+    def remove_recursively(self, path):
+        self.fscommands.remove(path)
+
+
+class Resource(object):
+    """Represents files and folders in a project"""
+
+    def __init__(self, project, name):
+        self.project = project
+        self.name = name
+        self.observers = []
+
+    def get_path(self):
+        """Returns 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.name
+
+    def get_name(self):
+        """Returns the name of this resource"""
+        return self.name.split('/')[-1]
+    
+    def remove(self):
+        """Removes resource from the project"""
+    
+    def move(self, new_location):
+        """Moves resource to new_lcation"""
+
+    def is_folder(self):
+        """Returns true if the resource is a folder"""
+
+    def get_project(self):
+        """Returns the project this resource belongs to"""
+        return self.project
+
+    def add_change_observer(self, observer):
+        self.observers.append(observer)
+
+    def remove_change_observer(self, observer):
+        if observer in self.observers:
+            self.observers.remove(observer)
+
+    def get_parent(self):
+        parent = '/'.join(self.name.split('/')[0:-1])
+        return self.project.get_resource(parent)
+
+    def _get_real_path(self):
+        """Returns the file system path of this resource"""
+        return self.project._get_resource_path(self.name)
+    
+    def _get_destination_for_move(self, destination):
+        dest_path = self.project._get_resource_path(destination)
+        if os.path.isdir(dest_path):
+            if destination != '':
+                return destination + '/' + self.get_name()
+            else:
+                return self.get_name()
+        return destination
+
+
+class _File(Resource):
+    """Represents a file in a project"""
+
+    def __init__(self, project, name):
+        super(_File, self).__init__(project, name)
+    
+    def read(self):
+        return open(self.project._get_resource_path(self.name)).read()
+
+    def write(self, contents):
+        file_ = open(self.project._get_resource_path(self.name), 'w')
+        file_.write(contents)
+        file_.close()
+        for observer in list(self.observers):
+            observer(self)
+        self.get_parent()._child_changed(self)
+
+    def is_folder(self):
+        return False
+
+    def remove(self):
+        self.project.remove_recursively(self.project._get_resource_path(self.name))
+        self.project._update_resource_location(self)
+        for observer in list(self.observers):
+            observer(self)
+        self.get_parent()._child_changed(self)
+
+    def move(self, new_location):
+        destination = self._get_destination_for_move(new_location)
+        self.project.fscommands.move(self.project._get_resource_path(self.name),
+                                     self.project._get_resource_path(destination))
+        self.project._update_resource_location(self, destination)
+        self.get_parent()._child_changed(self)
+        self.name = destination
+        self.get_parent()._child_changed(self)
+        for observer in list(self.observers):
+            observer(self)
+
+
+class File(_File):
+    """Represents a file in a project"""
+
+
+class OutOfProjectFile(_File):
+    """Represents a file outside a project"""
+
+    def __init__(self, project, path):
+        super(OutOfProjectFile, self).__init__(project, path)
+        self.path = path
+        
+    def read(self):
+        return open(self.path).read()
+
+    def _get_real_path(self):
+        """Returns the file system path of this resource"""
+        return self.path
+
+    def get_parent(self):
+        parent = '/'.join(self.path.split('/')[0:-1])
+        return self.project.get_out_of_project_resource(parent)
+
+
+class _Folder(Resource):
+    """Represents a folder in a project"""
+
+    def __init__(self, project, name):
+        super(_Folder, self).__init__(project, name)
+
+
+    def is_folder(self):
+        return True
+
+    def get_children(self):
+        """Returns the children resources of this folder"""
+        path = self._get_real_path()
+        result = []
+        content = os.listdir(path)
+        for name in content:
+            if self.get_path() != '':
+                resource_name = self.get_path() + '/' + name
+            else:
+                resource_name = name
+            result.append(self.project.get_resource(resource_name))
+        return result
+
+    def create_file(self, file_name):
+        if self.get_path():
+            file_path = self.get_path() + '/' + file_name
+        else:
+            file_path = file_name
+        self.project._create_file(file_path)
+        child = self.get_child(file_name)
+        self._child_changed(child)
+        return child
+
+    def create_folder(self, folder_name):
+        if self.get_path():
+            folder_path = self.get_path() + '/' + folder_name
+        else:
+            folder_path = folder_name
+        self.project._create_folder(folder_path)
+        child = self.get_child(folder_name)
+        self._child_changed(child)
+        return child
+
+    def get_child(self, name):
+        if self.get_path():
+            child_path = self.get_path() + '/' + name
+        else:
+            child_path = name
+        return self.project.get_resource(child_path)
+    
+    def has_child(self, name):
+        try:
+            self.get_child(name)
+            return True
+        except RopeException:
+            return False
+
+    def get_files(self):
+        result = []
+        for resource in self.get_children():
+            if not resource.is_folder():
+                result.append(resource)
+        return result
+
+    def get_folders(self):
+        result = []
+        for resource in self.get_children():
+            if resource.is_folder():
+                result.append(resource)
+        return result
+
+    def remove(self):
+        for child in self.get_children():
+            child.remove()
+        self.project.remove_recursively(self.project._get_resource_path(self.name))
+        self.project._update_resource_location(self)
+        self.get_parent()._child_changed(self)
+
+    def move(self, new_location):
+        destination = self._get_destination_for_move(new_location)
+        self.project.fscommands.create_folder(self.project._get_resource_path(destination))
+        for child in self.get_children():
+            if not (child.is_folder() and child.get_name() == '.svn'):
+                child.move(destination + '/' + child.get_name())
+        self.project.fscommands.remove(self.project._get_resource_path(self.get_path()))
+        self.project._update_resource_location(self, destination)
+        self.get_parent()._child_changed(self)
+        self.name = destination
+        self.get_parent()._child_changed(self)
+    
+    def _child_changed(self, child):
+        if child != self:
+            for observer in list(self.observers):
+                observer(self)
+
+
+class Folder(_Folder):
+    """Represents a non root folder in a project"""
+
+    def __init__(self, project, folderName):
+        super(Folder, self).__init__(project, folderName)
+
+
+class RootFolder(_Folder):
+    """Represents the root folder of a project"""
+
+    def __init__(self, project):
+        super(RootFolder, self).__init__(project, '')
+
+
+class OutOfProjectFolder(_Folder):
+    """Represents a folder outside the project"""
+
+    def __init__(self, project, path):
+        super(OutOfProjectFolder, self).__init__(project, path)
+        self.path = path
+    
+    def get_children(self):
+        result = []
+        content = os.listdir(self.path)
+        for name in content:
+            resource_path = os.path.join(self.path, name)
+            result.append(self.project.get_out_of_project_resource(resource_path))
+        return result
+
+    def get_child(self, name):
+        child_path = os.path.join(self.path, name)
+        return self.project.get_out_of_project_resource(child_path)
+    
+    def _get_real_path(self):
+        """Returns the file system path of this resource"""
+        return self.path
+
+    def get_parent(self):
+        parent = '/'.join(self.path.split('/')[0:-1])
+        return self.project.get_out_of_project_resource(parent)

File rope/base/pycore.py

View file
  • Ignore whitespace
+import re
+import sys
+
+import rope.base.objectinfer
+import rope.refactor
+import rope.base.dynamicoi
+from rope.base.exceptions import ModuleNotFoundException
+from rope.base.pyobjects import *
+
+
+class PyCore(object):
+
+    def __init__(self, project):
+        self.project = project
+        self.module_map = {}
+        self.object_infer = rope.base.objectinfer.ObjectInfer(self)
+        self.refactoring = rope.refactor.PythonRefactoring(self)
+        self.dynamicoi = rope.base.dynamicoi.DynamicObjectInference(self)
+        self.classes = None
+
+    def get_module(self, name, current_folder=None):
+        """Returns a `PyObject` if the module was found."""
+        module = self.find_module(name, current_folder)
+        if module is None:
+            raise ModuleNotFoundException('Module %s not found' % name)
+        return self.resource_to_pyobject(module)
+    
+    def get_relative_module(self, name, current_folder, level):
+        module = self.find_relative_module(name, current_folder, level)
+        if module is None:
+            raise ModuleNotFoundException('Module %s not found' % name)
+        return self.resource_to_pyobject(module)
+    
+    def get_string_module(self, module_content, resource=None):
+        """Returns a `PyObject` object for the given module_content"""
+        return PyModule(self, module_content, resource)
+
+    def get_string_scope(self, module_content, resource=None):
+        """Returns a `Scope` object for the given module_content"""
+        return self.get_string_module(module_content, resource).get_scope()
+
+    def _invalidate_resource_cache(self, resource):
+        self.classes = None
+        if resource in self.module_map:
+            local_module = self.module_map[resource]
+            del self.module_map[resource]
+            resource.remove_change_observer(self._invalidate_resource_cache)
+            local_module._invalidate_concluded_data()
+
+    def create_module(self, src_folder, new_module):
+        """Creates a module and returns a `rope.project.File`"""
+        packages = new_module.split('.')
+        parent = src_folder
+        for package in packages[:-1]:
+            parent = parent.get_child(package)
+        return parent.create_file(packages[-1] + '.py')
+
+    def create_package(self, src_folder, new_package):
+        """Creates a package and returns a `rope.project.Folder`"""
+        packages = new_package.split('.')
+        parent = src_folder
+        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
+
+    def _find_module_in_source_folder(self, source_folder, module_name):
+        result = []
+        module = source_folder
+        packages = module_name.split('.')
+        for pkg in packages[:-1]:
+            if  module.is_folder() and module.has_child(pkg):
+                module = module.get_child(pkg)
+                result.append(module)
+            else:
+                return None
+        if not module.is_folder():
+            return None
+
+        if module.has_child(packages[-1]) and \
+           module.get_child(packages[-1]).is_folder():
+            result.append(module.get_child(packages[-1]))
+            return result
+        elif module.has_child(packages[-1] + '.py') and \
+             not module.get_child(packages[-1] + '.py').is_folder():
+            result.append(module.get_child(packages[-1] + '.py'))
+            return result
+        return None
+    
+    def get_python_path_folders(self):
+        result = []
+        for src in sys.path:
+            try:
+                src_folder = self.project.get_out_of_project_resource(src)
+                result.append(src_folder)
+            except rope.base.exceptions.RopeException:
+                pass
+        return result
+    
+    def find_module(self, module_name, current_folder=None):
+        """Returns a resource pointing to the given module
+        
+        returns None if it can not be found
+        """
+        module_resource_list = self._find_module_resource_list(module_name,
+                                                               current_folder)
+        if module_resource_list is not None:
+            return module_resource_list[-1]
+    
+    def find_relative_module(self, module_name, current_folder, level):
+        for i in range(level - 1):
+            current_folder = current_folder.get_parent()
+        if module_name == '':
+            return current_folder
+        else:
+            module = self._find_module_in_source_folder(current_folder, module_name)
+            if module is not None:
+                return module[-1]
+    
+    def _find_module_resource_list(self, module_name, current_folder=None):
+        """Returns a list of lists of `Folder`s and `File`s for the given module"""
+        for src in self.get_source_folders():
+            module = self._find_module_in_source_folder(src, module_name)
+            if module is not None:
+                return module
+        for src in self.get_python_path_folders():
+            module = self._find_module_in_source_folder(src, module_name)
+            if module is not None:
+                return module
+        if current_folder is not None:
+            module = self._find_module_in_source_folder(current_folder, module_name)
+            if module is not None:
+                return module
+        return None
+
+    def get_source_folders(self):
+        """Returns project source folders"""
+        return self._find_source_folders(self.project.get_root_folder())
+    
+    def resource_to_pyobject(self, resource):
+        if resource in self.module_map:
+            return self.module_map[resource]
+        if resource.is_folder():
+            result = PyPackage(self, resource)
+        else:
+            result = PyModule(self, resource.read(), resource=resource)
+        self.module_map[resource] = result
+        resource.add_change_observer(self._invalidate_resource_cache)
+        return result
+    
+    def get_python_files(self):
+        """Returns all python files available in the project"""
+        return [resource for resource in self.project.get_files()
+                if resource.get_name().endswith('.py')]
+
+    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.get_name().endswith('.py'):
+                result.append(folder)
+                break
+        for resource in folder.get_folders():
+            result.extend(self._find_source_folders(resource))
+        return result
+    
+    def _get_object_infer(self):
+        return self.object_infer
+    
+    def get_refactoring(self):
+        return self.refactoring
+    
+    def _invalidate_all_concluded_data(self):
+        for module in self.module_map.values():
+            module._invalidate_concluded_data()
+
+    def run_module(self, resource, args=None, stdin=None, stdout=None):
+        runner = self.dynamicoi.run_module(resource, args, stdin, stdout)
+        runner.add_finishing_observer(self._invalidate_all_concluded_data)
+        runner.run()
+        return runner
+    
+    def get_subclasses(self, pyclass):
+        if self.classes is None:
+            classes = []
+            pattern = re.compile(r'^[ \t]*class[ \t]+\w', re.M)
+            for resource in self.get_python_files():
+                pyscope = self.resource_to_pyobject(resource).get_scope()
+                source = pyscope.pyobject.source_code
+                for match in pattern.finditer(source):
+                    holding_scope = pyscope.get_inner_scope_for_offset(match.start())
+                    classes.append(holding_scope.pyobject)
+            self.classes = classes
+        return [class_ for class_ in self.classes
+                if pyclass in class_.get_superclasses()]

File rope/base/pynames.py

View file
  • Ignore whitespace
+import rope.base.pyobjects
+from rope.base.exceptions import (ModuleNotFoundException, AttributeNotFoundException)
+
+
+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):
+    
+    def __init__(self, lineno=None, module=None, pyobject=None):
+        self.lineno = lineno
+        self.module = module
+        self.is_being_inferred = False
+        self.assigned_asts = []
+        self.pyobject = _get_concluded_data(module)
+        self.pyobject.set(pyobject)
+    
+    def get_object(self):
+        if self.is_being_inferred:
+            raise rope.base.pyobjects.IsBeingInferredException('Circular assignments')
+        if self.pyobject.get() is None and self.module is not None:
+            self.is_being_inferred = True
+            try:
+                object_infer = self.module.pycore._get_object_infer()
+                inferred_object = object_infer.infer_object(self)
+                self.pyobject.set(inferred_object)
+            finally:
+                self.is_being_inferred = False
+        if self.pyobject.get() is None:
+            self.pyobject.set(rope.base.pyobjects.PyObject(rope.base.pyobjects.
+                                                      PyObject.get_base_type('Unknown')))
+        return self.pyobject.get()
+    
+    def get_definition_location(self):
+        """Returns a (module, lineno) tuple"""
+        if self.lineno is None and self.assigned_asts:
+            self.lineno = self.assigned_asts[0].lineno
+        return (self.module, self.lineno)
+
+
+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 _get_current_folder(self):
+        resource = self.importing_module.get_module().get_resource()
+        if resource is None:
+            return None
+        return resource.get_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.resource_to_pyobject(self.resource))
+            elif self.module_name is not None:
+                try:
+                    if self.level == 0:
+                        self.pymodule.set(pycore.get_module(self.module_name,
+                                                            self._get_current_folder()))
+                    else:
+                        self.pymodule.set(pycore.get_relative_module(self.module_name,
+                                                                     self._get_current_folder(),
+                                                                     self.level))
+                    self.pymodule.get()._add_dependant(self.importing_module)
+                except ModuleNotFoundException:
+                    pass
+        return self.pymodule.get()
+    
+    def get_object(self):
+        if self._get_pymodule() is None:
+            return rope.base.pyobjects.PyObject(rope.base.pyobjects.PyObject.get_base_type('Unknown'))
+        return self._get_pymodule()
+    
+    def get_definition_location(self):
+        if self._get_pymodule() is None:
+            return (None, None)
+        return (self._get_pymodule().get_module(), 1)
+
+
+class ImportedName(PyName):
+    
+    def __init__(self, imported_module, imported_name):
+        self.imported_module = imported_module
+        self.imported_name = imported_name
+        self.imported_pyname = _get_concluded_data(imported_module.importing_module)
+    
+    def _get_imported_pyname(self):
+        if self.imported_pyname.get() is None:
+            try:
+                self.imported_pyname.set(self.imported_module.get_object()\
+                                         .get_attribute(self.imported_name))
+            except AttributeNotFoundException:
+                pass
+        if self.imported_pyname.get() is None:
+            self.imported_pyname.set(AssignedName())
+        return self.imported_pyname.get()
+    
+    def get_object(self):
+        return self._get_imported_pyname().get_object()
+    
+    def get_definition_location(self):
+        return self._get_imported_pyname().get_definition_location()
+
+
+class ParameterName(PyName):
+    
+    def __init__(self, pyfunction, index):
+        self.pyfunction = pyfunction
+        self.index = index
+        self.pyobject = _get_concluded_data(self.pyfunction.get_module())
+    
+    def get_object(self):
+        if self.pyobject.get() is None:
+            self.pyobject.set(self.pyfunction._get_parameter(self.index))
+        return self.pyobject.get()
+    
+    def get_definition_location(self):
+        return (self.pyfunction.get_module(), self.pyfunction._get_ast().lineno)
+
+
+class StarImport(object):
+    
+    def __init__(self, imported_module):
+        self.imported_module = imported_module
+        self.names = _get_concluded_data(imported_module.importing_module)
+    
+    def get_names(self):
+        if self.names.get() is None:
+            if isinstance(self.imported_module.get_object(), rope.base.pyobjects.PyPackage):
+                return {}
+            result = {}
+            for name, pyname in self.imported_module.get_object().get_attributes().iteritems():
+                if not name.startswith('_'):
+                    result[name] = ImportedName(self.imported_module, name)
+            self.names.set(result)
+        return self.names.get()
+
+
+def _get_concluded_data(module):
+    if module is None:
+        return rope.base.pyobjects._ConcludedData()
+    return module._get_concluded_data()

File rope/base/pyobjects.py

View file
  • Ignore whitespace
+import compiler
+
+import rope.base.pyscopes
+from rope.base.exceptions import (RopeException, AttributeNotFoundException)
+from rope.base.pynames import *
+
+
+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 AttributeNotFoundException('Attribute %s not found' % name)
+        return self.get_attributes()[name]
+
+    def get_type(self):
+        return self.type
+
+    @staticmethod
+    def get_base_type(name):
+        if not hasattr(PyObject, 'types'):
+            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]
+
+
+class PyDefinedObject(PyObject):
+
+    def __init__(self, type_, pycore, ast_node, parent):
+        super(PyDefinedObject, self).__init__(type_)
+        self.pycore = pycore
+        self.ast_node = ast_node
+        self.scope = None
+        self.parent = parent
+        self.structural_attributes = None
+        self.concluded_attributes = self.get_module()._get_concluded_data()
+        self.attributes = self.get_module()._get_concluded_data()
+    
+    def _get_structural_attributes(self):
+        if self.structural_attributes is None:
+            self.structural_attributes = self._create_structural_attributes()
+        return self.structural_attributes
+
+    def _get_concluded_attributes(self):
+        if self.concluded_attributes.get() is None:
+            self._get_structural_attributes()
+            self.concluded_attributes.set(self._create_concluded_attributes())
+        return self.concluded_attributes.get()
+
+    def get_attributes(self):
+        if self.attributes.get() is None:
+            result = dict(self._get_concluded_attributes())
+            result.update(self._get_structural_attributes())
+            self.attributes.set(result)