Add an FS.Entry class that can morph to File or Dir.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 15 Oct 2001 16:47:05 +0000 (16:47 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 15 Oct 2001 16:47:05 +0000 (16:47 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@100 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py

index f9f6392004a11203c490c887dceb4de0a7eb55c8..e0941ad6dbc0c4b69b3984eee10cf1746328f900 100644 (file)
@@ -158,8 +158,19 @@ class FS:
            # supplied top-level directory.
            assert directory, "Tried to lookup a node by relative path with no top-level directory supplied."
         ret = directory.entries.setdefault(tail, fsclass(tail, directory))
+       if fsclass.__name__ == 'Entry':
+            # If they were looking up a generic entry, then
+           # whatever came back is all right.
+           return ret
+       if ret.__class__.__name__ == 'Entry':
+           # They were looking up a File or Dir but found a
+           # generic entry.  Transform the node.
+           ret.__class__ = fsclass
+           ret._morph()
+           return ret
         if not isinstance(ret, fsclass):
-            raise TypeError, ret
+            raise TypeError, "Tried to lookup %s '%s' as a %s." % \
+                       (ret.__class__.__name__, str(ret), fsclass.__name__)
         return ret
 
     def __transformPath(self, name, directory):
@@ -178,6 +189,16 @@ class FS:
         elif not directory:
             directory = self.Top
         return (name, directory)
+
+    def Entry(self, name, directory = None):
+        """Lookup or create a generic Entry node with the specified name.
+        If the name is a relative path (begins with ./, ../, or a file
+        name), then it is looked up relative to the supplied directory
+        node, or to the top level directory of the FS (supplied at
+        construction time) if no directory is supplied.
+        """
+        name, directory = self.__transformPath(name, directory)
+        return self.__doLookup(Entry, name, directory)
     
     def File(self, name, directory = None):
         """Lookup or create a File node with the specified name.  If
@@ -205,6 +226,41 @@ class FS:
         name, directory = self.__transformPath(name, directory)
         return self.__doLookup(Dir, name, directory)
 
+
+
+class Entry(Node):
+    """A generic class for file system entries.  This class if for
+    when we don't know yet whether the entry being looked up is a file
+    or a directory.  Instances of this class can morph into either
+    Dir or File objects by a later, more precise lookup."""
+
+    def __init__(self, name, directory):
+       """Initialize a generic file system Entry.
+       
+       Call the superclass initialization, take care of setting up
+       our relative and absolute paths, identify our parent
+       directory, and indicate that this node should use
+       signatures."""
+        Node.__init__(self)
+
+        if directory:
+            self.abspath = os.path.join(directory.abspath, name)
+            if str(directory.path) == '.':
+                self.path = os.path.join(name)
+            else:
+                self.path = os.path.join(directory.path, name)
+        else:
+            self.abspath = self.path = name
+       self.parent = directory
+       self.uses_signature = 1
+
+    def __str__(self):
+       """A FS node's string representation is its path name."""
+       return self.path
+
+    def exists(self):
+        return os.path.exists(self.path)
+
     
 
 # XXX TODO?
@@ -216,29 +272,35 @@ class FS:
 # linked_targets
 # is_accessible
 
-class Dir(Node):
+class Dir(Entry):
     """A class for directories in a file system.
     """
 
     def __init__(self, name, directory = None):
-        Node.__init__(self)
+        Entry.__init__(self, name, directory)
+       self._morph()
+
+    def _morph(self):
+       """Turn a file system node (either a freshly initialized
+       directory object or a separate Entry object) into a
+       proper directory object.
+       
+       Modify our paths to add the trailing slash that indicates
+       a directory.  Set up this directory's entries and hook it
+       into the file system tree.  Specify that directories (this
+       node) don't use signatures for currency calculation."""
+
+        self.path = os.path.join(self.path, '')
+        self.abspath = os.path.join(self.abspath, '')
 
         self.entries = PathDict()
         self.entries['.'] = self
-
-        if directory:
-            self.entries['..'] = directory
-            self.abspath = os.path.join(directory.abspath, name, '')
-            if str(directory.path) == '.':
-                self.path = os.path.join(name, '')
-            else:
-                self.path = os.path.join(directory.path, name, '')
-        else:
-            self.abspath = self.path = name
-            self.entries['..'] = None
-
-    def __str__(self):
-       return self.path
+       if hasattr(self, 'parent'):
+            self.entries['..'] = self.parent
+           delattr(self, 'parent')
+       else:
+           self.entries['..'] = None
+        self.uses_signature = None
 
     def up(self):
         return self.entries['..']
@@ -275,22 +337,14 @@ class Dir(Node):
 # is_under
 # relpath
 
-class File(Node):
+class File(Entry):
     """A class for files in a file system.
     """
-
-    def __init__(self, name, directory):
-        Node.__init__(self)
-
-        self.abspath = os.path.join(directory.abspath, name)
-        if str(directory.path) == '.':
-            self.path = name
-        else:
-            self.path = os.path.join(directory.path, name)
-        self.parent = directory
-
-    def __str__(self):
-       return self.path
+    def _morph(self):
+       """Turn a file system node into a File object.  Nothing
+       to be done, actually, because all of the info we need
+       is handled by our base Entry class initialization."""
+        pass
 
     def root(self):
         return self.parent.root()
@@ -301,8 +355,5 @@ class File(Node):
     def get_timestamp(self):
         return os.path.getmtime(self.path)
 
-    def exists(self):
-        return os.path.exists(self.path)
-
 
 default_fs = FS()
index 9b3d8db0f6f75f984e2a3efe5cfa06594458be55..bf94e1f2e3dc3260aaba27e442f7f0fed2c6f213 100644 (file)
@@ -101,23 +101,24 @@ class FSTestCase(unittest.TestCase):
         try:
             f2 = fs.File('f1/f2', directory = d1)
         except TypeError, x:
-            node = x.args[0]
-            assert node.path == 'd1/f1', "node.path %s != d1/f1" % node.path
-            assert str(node) == 'd1/f1', "str(node) %s != d1/f1" % str(node)
-            assert node.__class__.__name__ == 'File'
+           assert str(x) == "Tried to lookup File 'd1/f1' as a Dir.", x
         except:
             raise
 
         try:
             dir = fs.Dir('d1/f1')
         except TypeError, x:
-            node = x.args[0]
-            assert node.path == 'd1/f1', "node.path %s != d1/f1" % node.path
-            assert str(node) == 'd1/f1', "str(node) %s != d1/f1" % str(node)
-            assert node.__class__.__name__ == 'File'
+           assert str(x) == "Tried to lookup File 'd1/f1' as a Dir.", x
         except:
             raise
 
+       try:
+           f2 = fs.File('d1')
+       except TypeError, x:
+           assert str(x) == "Tried to lookup Dir 'd1/' as a File.", x
+       except:
+           raise
+
        # Test Dir.children()
        dir = fs.Dir('ddd')
        fs.File('ddd/f1')
@@ -135,7 +136,6 @@ class FSTestCase(unittest.TestCase):
 
         built_it = None
         assert not built_it
-        d1.path = "d"           # XXX FAKE SUBCLASS ATTRIBUTE
         d1.add_source(["d"])    # XXX FAKE SUBCLASS ATTRIBUTE
         d1.builder_set(Builder())
         d1.env_set(Environment())
@@ -144,13 +144,45 @@ class FSTestCase(unittest.TestCase):
 
         built_it = None
         assert not built_it
-        f1.path = "f"           # XXX FAKE SUBCLASS ATTRIBUTE
         f1.add_source(["f"])    # XXX FAKE SUBCLASS ATTRIBUTE
         f1.builder_set(Builder())
         f1.env_set(Environment())
         f1.build()
         assert built_it
 
+       e1 = fs.Entry("d1")
+       assert e1.__class__.__name__ == 'Dir'
+       assert e1.path == "d1/", e1.path
+
+       e2 = fs.Entry("d1/f1")
+       assert e2.__class__.__name__ == 'File'
+       assert e2.path == "d1/f1", e2.path
+
+       e3 = fs.Entry("e3")
+       assert e3.__class__.__name__ == 'Entry'
+       assert e3.path == "e3", e3.path
+
+       e4 = fs.Entry("d1/e4")
+       assert e4.__class__.__name__ == 'Entry'
+       assert e4.path == "d1/e4", e4.path
+
+       e5 = fs.Entry("e3/e5")
+       assert e3.__class__.__name__ == 'Dir'
+       assert e3.path == "e3/", e3.path
+       assert e5.__class__.__name__ == 'Entry'
+       assert e5.path == "e3/e5", e5.path
+
+       e6 = fs.Dir("d1/e4")
+       assert e6 is e4
+       assert e4.__class__.__name__ == 'Dir'
+       assert e4.path == "d1/e4/", e4.path
+
+       e7 = fs.File("e3/e5")
+       assert e7 is e5
+       assert e5.__class__.__name__ == 'File'
+       assert e5.path == "e3/e5", e5.path
+
+
 
 if __name__ == "__main__":
     suite = unittest.TestSuite()