From: stevenknight Date: Mon, 15 Oct 2001 16:47:05 +0000 (+0000) Subject: Add an FS.Entry class that can morph to File or Dir. X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=e02428121b8d60e21d7184eb5dbf409fe3646c21;p=scons.git Add an FS.Entry class that can morph to File or Dir. git-svn-id: http://scons.tigris.org/svn/scons/trunk@100 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index f9f63920..e0941ad6 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -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() diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 9b3d8db0..bf94e1f2 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -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()