Ali Gholami Rudi  committed 3a74e2a

Making Resources reference objects
Invalidating cached modules in PyCore

  • Participants
  • Parent commits 7eba90e

Comments (0)

Files changed (5)

File docs/workingon.txt

-- Change from-import code assist to use PyCore
-- Project.pycore
-- Scope.lookup(name) it is recursive
-- Should Scope.get_names return all of the visible names in a scope?
-- Auto completion after dots
-- Resource.__hash__, Resource.__eq__
-- Factory methods for creating Functions, Classes, Modules
-- Holding ASTs in PyObjects and creating attributes only when needed
-- Handle circular imports
-- multi dot imports
+- Resources need to be reference objects; make factory methods
+- Resource.add_change_observer
+- Getting children only when necessary, invalidating, Resource.add_change_observer
+? Removing an observer from Resource.change_observers
+? Folders and change_observers
+? Refactor Resource hierarchy
 * Not overwriting PyNames; updating them
 ? Refactor pycore; So many different kinds for Modules
 * Inlining pycore._create_*
 * Handle circular from-imports
 * From-import non existance
 ? Refactor FunctionScope.get_scopes and get_names
-* Getting children only when necessary, invalidating, Resource.add_change_observer; File and Folder
 * Better Completion proposals kind to ease transformation; Names inside classes
 ? Not accessing pyname.object
 * Refactor pycore and codeassist
 ? Move Project.create_module and create_package to PyCore
 * Correcting indentation on '\n ${cursor}   def f():\n    pass\n'; cursor changes its line
 * 'super' codeassist super(${class}, self)
+? pyrope project for IDE part and rope for core part
 Before 0.2 Release

File rope/

 import rope.pycore
 from rope.exceptions import RopeException
 class Project(object):
-    '''A Project containing files and folders'''
+    """A Project containing files and folders"""
     def __init__(self, projectRootAddress):
         self.root = projectRootAddress
         if not os.path.exists(self.root):
             raise RopeException('Project root exists and is not a directory')
         self.code_assist = rope.codeassist.CodeAssist(self)
         self.pycore = rope.pycore.PyCore(self)
+        self.resources = {}
+        self.resources[''] = RootFolder(self)
     def get_root_folder(self):
-        return RootFolder(self)
+        return self.get_resource('')
     def get_root_address(self):
         return self.root
     def get_resource(self, resourceName):
-        path = self._get_resource_path(resourceName)
-        if not os.path.exists(path):
-            raise RopeException('resource %s does not exist' % resourceName)
-        if os.path.isfile(path):
-            return File(self, resourceName)
-        if os.path.isdir(path):
-            return Folder(self, resourceName)
-        raise RopeException('Unknown resource ' + resourceName)
+        if resourceName not in self.resources:
+            path = self._get_resource_path(resourceName)
+            if not os.path.exists(path):
+                raise RopeException('Resource %s does not exist' % resourceName)
+            elif os.path.isfile(path):
+                self.resources[resourceName] = File(self, resourceName)
+            elif os.path.isdir(path):
+                self.resources[resourceName] = Folder(self, resourceName)
+            else:
+                raise RopeException('Unknown resource ' + resourceName)
+        return self.resources[resourceName]
     def get_files(self):
         return self._get_files_recursively(self.get_root_folder())
 class Resource(object):
-    '''Represents files and folders in a project'''
+    """Represents files and folders in a project"""
     def remove(self):
-        '''Removes resource from the project'''
+        """Removes resource from the project"""
     def get_name(self):
-        '''Returns the name of this resource'''
+        """Returns the name of this resource"""
     def get_path(self):
-        '''Returns the path of this resource relative to the project root
+        """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.
-        '''
+        """
     def is_folder(self):
-        '''Returns true if the resouse is a folder'''
+        """Returns true if the resouse is a folder"""
     def get_project(self):
-        '''Returns the project this resource belongs to'''
+        """Returns the project this resource belongs to"""
+    def add_change_observer(self, observer):
+        pass
     def _get_real_path(self):
-        '''Returns the file system path of this resource'''
+        """Returns the file system path of this resource"""
     def __hash__(self):
         return hash(self.get_path())
 class File(Resource):
     '''Represents a file in a project'''
     def __init__(self, project, fileName):
         self.project = project
         self.fileName = fileName
+        self.observers = []
     def read(self):
         return open(self.project._get_resource_path(self.fileName)).read()
         file = open(self.project._get_resource_path(self.fileName), 'w')
+        for observer in self.observers:
+            observer(self)
     def remove(self):
     def is_folder(self):
         return False
+    def add_change_observer(self, observer):
+        self.observers.append(observer)
     def _get_real_path(self):
         return self.project._get_resource_path(self.fileName)
 class _Folder(Resource):
-    '''Represents a folder in a project'''
+    """Represents a folder in a project"""
     def __init__(self, project, folderName):
         self.project = project
 class Folder(_Folder):
-    '''Represents a folder in a project'''
+    """Represents a non root folder in a project"""
     def __init__(self, project, folderName):
         super(Folder, self).__init__(project, folderName)
 class RootFolder(_Folder):
-    '''Represents a folder in a project'''
+    """Represents the root folder of a project"""
     def __init__(self, project):
         super(RootFolder, self).__init__(project, '')
 class FileFinder(object):
     def __init__(self, project):
         self.project = project
         self.last_keyword = None
         self.last_result = None
     def find_files_starting_with(self, starting):
-        '''Returns the Files in the project whose names starts with starting'''
+        """Returns the Files in the project whose names starts with starting"""
         files = []
         if self.last_keyword is not None and starting.startswith(self.last_keyword):
             files = self.last_result
 class PythonFileRunner(object):
-    '''A class for running python project files'''
+    """A class for running python project files"""
     def __init__(self, file, stdin=None, stdout=None):
         self.file = file
         file_path = self.file._get_real_path()
                                         stdout=stdout, stderr=stdout, env=env)
     def wait_process(self):
-        '''Wait for the process to finish'''
+        """Wait for the process to finish"""
     def kill_process(self):
-        '''Stop the process. This does not work on windows.'''
+        """Stop the process. This does not work on windows."""
         os.kill(, 9)

File rope/

         module = self.get_string_module(module_content)
         return GlobalScope(self, module)
+    def _invalidate_resource_cache(self, resource):
+        if resource in self.module_map:
+            del self.module_map[resource]
     def _create(self, resource):
         if resource in self.module_map:
             return self.module_map[resource]
             result =  self.get_string_module(
         self.module_map[resource] = result
+        resource.add_change_observer(self._invalidate_resource_cache)
         return result
 class PyDefinedObject(PyObject):
-    def __init__(self, type_, ast_node=None, pycore=None):
+    def __init__(self, type_, ast_node, pycore):
         self.ast_node = ast_node
         self.pycore = pycore
         self.attributes = None
     def _get_ast(self):
         return self.ast_node
 class PyFunction(PyDefinedObject):
-    def __init__(self, ast_node=None, pycore=None):
+    def __init__(self, ast_node, pycore):
         super(PyFunction, self).__init__(PyObject.get_base_type('Function'), ast_node, pycore)
         self.parameters = self.ast_node.argnames
 class PyClass(PyDefinedObject):
-    def __init__(self, ast_node=None, pycore=None):
+    def __init__(self, ast_node, pycore):
         super(PyClass, self).__init__(PyObject.get_base_type('Type'), ast_node, pycore)
     def _get_attributes_from_ast(self):
 class PyModule(PyDefinedObject):
-    def __init__(self, ast_node=None, pycore=None):
+    def __init__(self, ast_node, pycore):
         super(PyModule, self).__init__(PyObject.get_base_type('Module'), ast_node, pycore)
         self.is_package = False
 class PyPackage(PyObject):
-    def __init__(self, resource=None, pycore=None):
+    def __init__(self, resource, pycore):
         super(PyPackage, self).__init__(PyObject.get_base_type('Module'))
         self.is_package = True
         self.resource = resource
             self.attributes = attributes
         return self.attributes
-class PyConstantPackage(PyObject):
+class PyFilteredPackage(PyObject):
     def __init__(self):
-        super(PyConstantPackage, self).__init__(PyObject.get_base_type('Module'))
+        super(PyFilteredPackage, self).__init__(PyObject.get_base_type('Module'))
         self.is_package = True
         self.attributes = {}
             if alias is None and '.' in imported:
                 tokens = imported.split('.')
                 if tokens[0] in self.names and \
-                   isinstance(self.names[tokens[0]].object, PyConstantPackage):
+                   isinstance(self.names[tokens[0]].object, PyFilteredPackage):
                     pypkg = self.names[tokens[0]].object
-                    pypkg = PyConstantPackage()
+                    pypkg = PyFilteredPackage()
                     self.names[tokens[0]] = PyName(pypkg)
                 for token in tokens[1:-1]:
                     if token in pypkg.get_attributes() and \
-                       isinstance(pypkg.get_attributes()[token].object, PyConstantPackage):
+                       isinstance(pypkg.get_attributes()[token].object, PyFilteredPackage):
                         newpkg = pypkg.get_attributes()[token].object
-                        newpkg = PyConstantPackage()
+                        newpkg = PyFilteredPackage()
                         pypkg._add_attribute(token, PyName(newpkg))
                     pypkg = newpkg
                 pypkg._add_attribute(tokens[-1], PyName(module))

File ropetest/

     def test_project_root_is_root_folder(self):
         self.assertTrue(isinstance(self.project.get_root_folder(), RootFolder))
+    def test_resource_change_observer(self):
+        sample_file = self.project.get_root_folder().create_file('my_file.txt')
+        sample_file.write('a sample file version 1')
+        self.changed = False
+        def sample_change_observer(resource):
+            self.changed = True
+            self.assertEquals(sample_file, resource)
+        sample_file.add_change_observer(sample_change_observer)
+        sample_file.write('a sample file version 2')
+        self.assertTrue(self.changed)
 class FileFinderTest(unittest.TestCase):
     def setUp(self):

File ropetest/

         self.assertTrue('mod1' in package2.get_attributes() and
                         'mod2' in package2.get_attributes())
+    def test_invalidating_cache_after_resource_change(self):
+        module = self.project.create_module(self.project.get_root_folder(), 'mod')
+        module.write('import sys\n')
+        mod1 = self.pycore.get_module('mod')
+        self.assertTrue('var' not in mod1.get_attributes())
+        module.write('var = 10\n')
+        mod2 = self.pycore.get_module('mod')
+        self.assertTrue('var' in mod2.get_attributes())
 class PyCoreScopesTest(unittest.TestCase):