Commits

Anonymous committed 2dd7bef

Basic out of project resources support

  • Participants
  • Parent commits 4ec5b1b

Comments (0)

Files changed (8)

 Stories
 =======
 
-* Organize imports @ 2
+* Organize imports
 
 
-* Add import @ 2
+* Add import
 
 
-* Variable indentation and tab size @ 1
+* Variable indentation and tab size @ 4h
 
 
-* Find occurances @ 4
+* Find occurances
 
 
-* Configuring keys @ 2
+* Configuring keys
 
 
-* Configuring fonts @ 1
+* Configuring fonts
 
 
-* Showing syntactical errors @ 3
+* Showing syntactical errors
 
 
-* Editor folding @ 4
+* Editor folding
 
 
-* Having multiple clipboards @ 2
+* Having multiple clipboards
 
 
-* reST support inside pydocs @ 1
+* reST support inside pydocs
 
 
-* reST highlighting @ 2
+* reST outlines
 
 
-* reST outlines @ 3
+* reST codeassists
 
 
-* reSt codeassists @ 3
+* Adding tool bar
 
 
-* Adding tool bar @ 1
+* Commanding buffer
 
 
-* Commanding buffer @ 4
+* Subversion support using pysvn
 
 
-* Subversion support using pysvn @ 3
+* Last edit location; C-q
 
 
-* Last edit location; C-q @ 2
-
-
-* Running unit tests @ 3
+* Running unit tests
   Add a graphical view for running tests.
 
 
-* Enhance open dialog @ 1
+* Replacement; M-% @ 12h
 
 
-* Replacement; M-% @ 1
+* Save as and save all; C-x C-w, C-x s @ 4h
 
 
-* Save as and save all; C-x C-w, C-x s @ 1
+* Remembering last open project
 
 
-* Remembering last open project @ 1
+* File Encodings
 
 
-* File Encodings @ 2
+* Static type inference
 
 
-* Static type inference @ 6
+* Commenting and uncommenting lines @ 2h
 
 
-* Commenting and uncommenting lines @ 1
-
-
-* Enhancing searching @ 1
+* Enhancing searching @ 16h
 
   * End the search when some other key sequence is typed
   * C-s C-s should start the last search
   * Showing failed searches
 
 
-* Go to matching parents @ 1
+* Go to matching parenthesis @ 12h
 
 
-* Enhancing module running @ 2
+* Enhancing module running @ 20h
 
   * Showing running status in the GUI
   * Printing output somewhere
   * Running last run
 
 
-* User specified source folders @ 2
+* User specified source folders
   You should save this project specific information somewhere. How should
   guessed source folders be used.
 
 
-* Enhancing syntax highlighting @ 1
+* Enhancing syntax highlighting @ 16h
 
   * Only highlighting the changed region
   * Extend highlighting region while highlighting if necessay
   * Use Modified event for updating highlighting only when necessary
 
 
-* Enhancing menu @ 1
+* Enhancing menu @ 16h
 
   * Disable inaccessable items
   * Showing keyboard short-cuts in front of menu items
 
 
-* Enhancing editor @ 1
+* Enhancing editor @ 8h
 
   * Kill line; C-k
   * Select all; C-x h
   * Fixed places for StatusTexts
 
 
-* Having different strategies for selecting next/prev words @ 1
+* Having different strategies for selecting next/prev words @ 8h
 
 
-* Enhancing auto indentation @ 1
+* Enhancing auto indentation @ 12h
 
   * Indenting a range of file
-  * Removing extra spaces
   * Separating entering and correcting indentation?
 
 
-* Dynamic type inference @ 3
+* Dynamic type inference
 
 
-* Better New ... Dialogs @ 2
+* Better New ... Dialogs
   Better folder and module selection. Completing or selecting from a list.
 
 
-* Refactoring Core @ 1
+* Refactoring Core
 
   * Refactor editor._goto_definition; not using Core.get_core()
   * Refactor Core; Only use EditorManager when working with the Editors
 
 
-* Enhancing editor @ 2
+* Enhancing editor @ 16h
 
   * Clearing selection if something happens
   * Unifying builtin and emacs-style selections; if selection
     is active do the builtin cut and paste
   * Edit menu: cut, paste, undo, redo, revert
   * Showing modified file status
-  * Handle MYClass style names in next/prev word
 
 
-* Better multi-sequence key event handling @ 2
+* Handle 'HTTPClient' style names in next/prev word @ 4h
 
 
-* Optimizing StatementRangeFinder @ 1
+* Better multi-sequence key event handling
+
+
+* Optimizing StatementRangeFinder
   Add CachedLines and not starting from the start of the lines
 
 
-* Auto-importing modules @ 3
+* Auto-importing modules
   Hold the list of project classes and import them automatically
   in codeassist
 
 
-* Better list and tree navigation @ 1
+* Better list and tree navigation @ 12h
   Add filter texts and selection texts to trees and lists.
 
 
-* Local history @ 4
+* Local history
 
 
-* Open Type; C-T @ 3
+* Open Type; C-T @ 32h
 
 
-* Compound statements and auto-completion @ 1
+* Compound statements and auto-completion @ 12h
   Completions for for-loop, except, lambda and with variables
 
 
-* Auto completion type @ 2
+* Auto completion type @ 16h
 
   * Inserting or overwriting
   * Inserting common prefixes
 
 
-* Enhancing auto-completion @ 1
+* Enhancing auto-completion @ 8h
 
   * What to do when the program has syntax errors
   * Sorting proposals
 
 
-* We know the type of list and keyword args @ 1
+* We know the type of list and keyword args
 
 
-* Handle circular from-imports @ 1
+* Handle circular from-imports
 
 
-* Introcude factory method @ 2
+* Introduce factory method
 
 
-* Showing function signature when calling @ 1
+* Showing function signature when calling
 
 
-* Introduce temporary variable @ 3
+* Introduce temporary variable
 
 
-* Inline function @ 3
+* Inline function
 
 
-* Extract function @ 3
+* Extract function
 
 
-* AssList and AssTuple assignments @ 1
+* AssList and AssTuple assignments
 
 
-* 'global' keyword issues for pycore @ 1
+* 'global' keyword issues for pycore
 
 
-* Read __init__.py of packages @ 2
+* Read __init__.py of packages
 
 
-* Auto completion contexts; strings, comments, imports, functions and ... @ 2
+* Auto completion contexts; strings, comments, imports, functions and ...
 
 
-* Show PyDoc @ 2
+* Show PyDoc
 
 
-* Rename function parameters @ 2
+* Rename function parameters
 
 
-* Rename module refactoring @ 2
+* Rename module refactoring
 
 
-* Move a class to another module @ 3
+* Move a class to another module
 
 
-* Auto-completing function parameter names when calling @ 2
+* Auto-completing function parameter names when calling
+
+
+* Rename function
+
+
+* Rename class
+
+
+* Reloading changed editors after refactorings @ 8h
+  `FileEditor` should be a resource visitor. What to do when buffer
+  has been changed.
+
+
+* reST highlighting
+
+
+* Not renaming names in strings and comments in refactorings
+
+
+* Formating Code
+  Writing ASTs
 
 
 > Public Release 0.2pre5 : July 16, 2006
 
 
-* Out of project modules @ 2d
+* Out of project modules @ 28h
   Modules that cannot be found in the project and builtins
 
 
-* Formating Code @ 4d
-  Writing ASTs
-
-
-* Reloading changed editors after refactorings @ 1d
-  `FileEditor` should be a resource visitor
-
-
-* Not renaming names in strings and comments in refactorings @ 2d
-
-
-* Rename function @ 2d
-
-
-* Rename class @ 3d
-
-
 Remaining Stories
 =================
 * Change program goals and description; principles.html

docs/workingon.txt

-Refactoring
-===========
+Out of project modules @ 28h
+============================
 
-- (a_statment + another_statment).an_attr
-- dictionaries
+- Using absolute paths
+- Adding OutOfProjectTest to suite()
+- Test get_child, has_child
+- Out of project resources should be reference objects
+* zip imports
 
-* Triples
-* " some string
-* Comments
+Out Of Project Resources
+------------------------
 
-* Names in strings or comments
-* Using ScopeNameFinder in _CodeCompletionCollector
-* Changing HoldingScopeFinder to get source_code instead of lines
-* Using SourceLinesAdapter in LineOrientedSourceTools
-* LineOrientedSourceTools should be more powerful; Elimination of source_code.split('\n')
-* Optimize SourceLinesAdapter methods
-* More work on WordRangeFinder._get_statment_start; merging with StatementRangeFinder?
+1. Special resource for out of project files
+2. Finding the wanted module
+3. Read only files and buffers
 
-* Only looking in the holding scope range for renaming
-* Having only one Refactoring instance for a project?
-* PyObject and PyName equality checks; Value objects?
-* Refactor pycore; So many kinds for Modules; eliminate checking module.is_package?
-* Consider using `StatementRangeFinder` in highlight module?
+There seems to be many obstacles for supporting out of project resources.
+Possible approaches:
+
+1. Reimplement `Resource`s so that they know nothing about whether the file
+   is in the project or not. They should only hold real address.
+
+2. Derive new subclasses from `Resource` class.
+
 
 
 
 
 Remaining Stories
 =================
+* Optimize SourceLinesAdapter methods
+* More work on WordRangeFinder._get_statment_start; merging with StatementRangeFinder?
+* Only looking in the holding scope range for renaming
+* Having only one Refactoring instance for a project?
+* PyObject and PyName equality checks; Value objects?
+* Refactor pycore; So many kinds for Modules; eliminate checking module.is_package?
+* Consider using `StatementRangeFinder` in highlight module?
 * Directories should contain __init__.py to be packages in codeassist?
 * Code assists on relative imports?
 * Python C extensions?
   * Dividing week time; 5/7 for core and 2/7 for UI
 
 * The same pattern for module names; ~ing or ~ or ~er
-* New package structure
-  ::
-    rope/
-      core/
-        ...
-      ui/
-        ...
 

rope/codeanalyze.py

         return self.source_code[self._find_word_start(offset - 1):offset]
     
     def get_word_at(self, offset):
-        return self.source_code[self._find_word_start(offset - 1):self._find_word_end(offset - 1) + 1]
+        return self.source_code[self._find_word_start(offset - 1):
+                                self._find_word_end(offset - 1) + 1]
     
     def _find_string_start(self, offset):
         kind = self.source_code[offset]
             lineno += 1
         return (lineno, offset - current_pos)
 
+    def _get_scope_indents(self, scope):
+        return self.get_indents(scope.get_lineno())
+    
     def get_holding_scope(self, module_scope, lineno, line_indents=None):
         line_indents = line_indents
         if line_indents is None:
             line_indents = self.get_indents(lineno)
-        scopes = [(module_scope, 0)]
+        scopes = [module_scope]
         current_scope = module_scope
         while current_scope is not None and \
               (current_scope.get_kind() == 'Module' or
-               self.get_indents(current_scope.get_lineno()) < line_indents):
+               self._get_scope_indents(current_scope) < line_indents):
             while len(scopes) > 1 and \
-                  scopes[-1][1] >= self.get_indents(current_scope.get_lineno()):
+                  self._get_scope_indents(scopes[-1]) >= self._get_scope_indents(current_scope):
                 scopes.pop()
-            scopes.append((current_scope, self.get_indents(current_scope.get_lineno())))
+            scopes.append(current_scope)
             new_scope = None
             for scope in current_scope.get_scopes():
                 if scope.get_lineno() <= lineno:
                     break
             current_scope = new_scope
         min_indents = line_indents
-        for l in range(scopes[-1][0].get_lineno() + 1, lineno):
+        for l in range(scopes[-1].get_lineno() + 1, lineno):
             if self.lines.get_line(l).strip() != '' and \
                not self.lines.get_line(l).strip().startswith('#'):
                 min_indents = min(min_indents, self.get_indents(l))
-        while len(scopes) > 1 and min_indents <= scopes[-1][1]:
+        while len(scopes) > 1 and min_indents <= self._get_scope_indents(scopes[-1]):
             scopes.pop()
-        return scopes[-1][0]
+        return scopes[-1]
 
 
 class _StatementEvaluator(object):
         self.pycore = rope.pycore.PyCore(self)
         self.resources = {}
         self.resources[''] = RootFolder(self)
+        self.out_of_project_resources = {}
 
     def get_root_folder(self):
         return self.get_resource('')
     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]
+
     @staticmethod
     def remove_recursively(file):
         for root, dirs, files in os.walk(file, topdown=False):
         Project.remove_recursively(self.project._get_resource_path(self.name))
 
     def is_folder(self):
-        """Returns true if the resouse is a folder"""
+        """Returns true if the resource is a folder"""
 
     def get_project(self):
         """Returns the project this resource belongs to"""
         return self.get_path() == resource.get_path()
 
 
-class File(Resource):
-    '''Represents a file in a project'''
+class _File(Resource):
+    """Represents a file in a project"""
 
     def __init__(self, project, name):
-        super(File, self).__init__(project, name)
+        super(_File, self).__init__(project, name)
         self.observers = []
     
     def read(self):
             self.observers.remove(observer)
 
     def remove(self):
-        super(File, self).remove()
+        super(_File, self).remove()
         for observer in 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()
+
 
 class _Folder(Resource):
     """Represents a folder in 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)
+    
 
 class FileFinder(object):
 
         created_package.create_file('__init__.py')
         return created_package
 
+    def _find_module_in_source_folder(self, source_folder, module_name):
+        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)
+            else:
+                return None
+        if not module.is_folder():
+            return None
+
+        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')
+        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.exceptions.RopeException:
+                pass
+        return result
+    
     def find_module(self, module_name):
-        source_folders = self.get_source_folders()
-        packages = module_name.split('.')
         result = []
-        for src in source_folders:
-            module = src
-            found = True
-            for pkg in packages[:-1]:
-                if  module.is_folder() and module.has_child(pkg):
-                    module = module.get_child(pkg)
-                else:
-                    found = False
-                    break
-
-            if module.is_folder() and module.has_child(packages[-1]) and\
-               module.get_child(packages[-1]).is_folder():
-                module = module.get_child(packages[-1])
-            elif module.is_folder() and \
-                 module.has_child(packages[-1] + '.py') and \
-                 not module.get_child(packages[-1] + '.py').is_folder():
-                module = module.get_child(packages[-1] + '.py')
-            else:
-                found = False
-            if found:
+        for src in self.get_source_folders():
+            module = self._find_module_in_source_folder(src, module_name)
+            if module is not None:
+                result.append(module)
+        if result:
+            return result
+        for src in self._get_python_path_folders():
+            module = self._find_module_in_source_folder(src, module_name)
+            if module is not None:
                 result.append(module)
         return result
 

ropetest/codeanalyzetest.py

         self.assertEquals('print {1: "one", 2, "two"}.keys',
                           word_finder.get_statement_at(29))
 
+    # TODO: eliminating comments
+    def xxx_test_comments_for_finding_statements(self):
+        word_finder = WordRangeFinder('var1 . # var2 . \n  var3')
+        self.assertEquals('var1 . # var2 . \n  var3',
+                          word_finder.get_statement_at(21))
+
+    def test_comments_for_finding_statements2(self):
+        word_finder = WordRangeFinder('var1 + "# var2".\n  var3')
+        self.assertEquals('"# var2".\n  var3',
+                          word_finder.get_statement_at(21))
+
 
 def suite():
     result = unittest.TestSuite()

ropetest/projecttest.py

         self.change_count += 1
 
 
+class OutOfProjectTest(unittest.TestCase):
+
+    def setUp(self):
+        super(OutOfProjectTest, self).setUp()
+        self.project_root = 'sample_project'
+        self.test_directory = 'temp_test_directory'
+        testutils.remove_recursively(self.project_root)
+        testutils.remove_recursively(self.test_directory)
+        os.mkdir(self.test_directory)
+        self.project = Project(self.project_root)
+
+    def tearDown(self):
+        testutils.remove_recursively(self.project_root)
+        testutils.remove_recursively(self.test_directory)
+        super(OutOfProjectTest, self).tearDown()
+
+    def test_simple_out_of_project_file(self):
+        sample_file_path = os.path.join(self.test_directory, 'sample.txt')
+        sample_file = file(sample_file_path, 'w')
+        sample_file.write('sample content\n')
+        sample_file.close()
+        sample_resource = self.project.get_out_of_project_resource(sample_file_path)
+        self.assertEquals('sample content\n', sample_resource.read())
+
+    def test_simple_out_of_project_folder(self):
+        sample_folder_path = os.path.join(self.test_directory, 'sample_folder')
+        os.mkdir(sample_folder_path)
+        sample_folder = self.project.get_out_of_project_resource(sample_folder_path)
+        self.assertEquals([], sample_folder.get_children())
+        
+        sample_file_path = os.path.join(sample_folder_path, 'sample.txt')
+        file(sample_file_path, 'w').close()
+        sample_resource = self.project.get_out_of_project_resource(sample_file_path)
+        self.assertEquals(sample_resource, sample_folder.get_children()[0])
+
+    def test_using_absolute_path(self):
+        sample_file_path = os.path.join(self.test_directory, 'sample.txt')
+        file(sample_file_path, 'w').close()
+        normal_sample_resource = self.project.get_out_of_project_resource(sample_file_path)
+        absolute_sample_resource = self.project.get_out_of_project_resource(os.path.abspath(sample_file_path))
+        self.assertEquals(normal_sample_resource, absolute_sample_resource)
+
+    def test_folder_get_child(self):
+        sample_folder_path = os.path.join(self.test_directory, 'sample_folder')
+        os.mkdir(sample_folder_path)
+        sample_folder = self.project.get_out_of_project_resource(sample_folder_path)
+        self.assertEquals([], sample_folder.get_children())
+        
+        sample_file_path = os.path.join(sample_folder_path, 'sample.txt')
+        file(sample_file_path, 'w').close()
+        sample_resource = self.project.get_out_of_project_resource(sample_file_path)
+        self.assertTrue(sample_folder.has_child('sample.txt'))
+        self.assertFalse(sample_folder.has_child('doesnothave.txt'))
+        self.assertEquals(sample_resource, sample_folder.get_child('sample.txt'))
+
+
 class FileFinderTest(unittest.TestCase):
     def setUp(self):
         unittest.TestCase.setUp(self)
 def suite():
     result = unittest.TestSuite()
     result.addTests(unittest.makeSuite(ProjectTest))
+    result.addTests(unittest.makeSuite(OutOfProjectTest))
     result.addTests(unittest.makeSuite(FileFinderTest))
     return result
 

ropetest/pycoretest.py

         an_attr = another_class.get_attributes()['an_attr']
         self.assertEquals(sample_class, an_attr.get_type())
 
+    def test_out_of_project_modules(self):
+        scope = self.pycore.get_string_scope('import re\n')
+        re_module = scope.get_names()['re']
+        self.assertTrue('match' in re_module.get_attributes())
+
 
 class PyCoreInProjectsTest(unittest.TestCase):