Simplify how NodeLists expand callable methods by making the NodeList
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 4 Jan 2009 07:40:06 +0000 (07:40 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 4 Jan 2009 07:40:06 +0000 (07:40 +0000)
class itself callable, instead of relying on a subsidiary
CallableComposite class for that behavior.

git-svn-id: http://scons.tigris.org/svn/scons/trunk@3871 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/Util.py
src/engine/SCons/UtilTests.py

index b47e262db3fa784dfa62b5c1d7f15e37edc21dc1..cbec5dde7e8f42024deb67b0400990d372af097d 100644 (file)
@@ -107,18 +107,6 @@ def updrive(path):
         path = string.upper(drive) + rest
     return path
 
-class CallableComposite(UserList):
-    """A simple composite callable class that, when called, will invoke all
-    of its contained callables with the same arguments."""
-    def __call__(self, *args, **kwargs):
-        retvals = map(lambda x, args=args, kwargs=kwargs: apply(x,
-                                                                args,
-                                                                kwargs),
-                      self.data)
-        if self.data and (len(self.data) == len(filter(callable, retvals))):
-            return self.__class__(retvals)
-        return NodeList(retvals)
-
 class NodeList(UserList):
     """This class is almost exactly like a regular list of Nodes
     (actually it can hold any object), with one important difference.
@@ -135,18 +123,20 @@ class NodeList(UserList):
     def __str__(self):
         return string.join(map(str, self.data))
 
+    def __iter__(self):
+        return iter(self.data)
+
+    def __call__(self, *args, **kwargs):
+        result = map(lambda x, args=args, kwargs=kwargs: apply(x,
+                                                               args,
+                                                               kwargs),
+                     self.data)
+        return self.__class__(result)
+
     def __getattr__(self, name):
-        # Return a list of the attribute, gotten from every element
-        # in the list
-        attrList = map(lambda x, n=name: getattr(x, n), self.data)
-
-        # Special case.  If the attribute is callable, we do not want
-        # to return a list of callables.  Rather, we want to return a
-        # single callable that, when called, will invoke the function on
-        # all elements of this list.
-        if self.data and (len(self.data) == len(filter(callable, attrList))):
-            return CallableComposite(attrList)
-        return self.__class__(attrList)
+        result = map(lambda x, n=name: getattr(x, n), self.data)
+        return self.__class__(result)
+
 
 _get_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
 
index a27f8c33f7db6e660633e04d94a5b993c40422f6..483591f6df6711fca800ff4eaca3085ac515caa1 100644 (file)
@@ -494,30 +494,6 @@ class UtilTestCase(unittest.TestCase):
         p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
         assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
 
-    def test_NodeList(self):
-        """Test NodeList class"""
-        class TestClass:
-            def __init__(self, name, child=None):
-                self.child = child
-                self.bar = name
-            def foo(self):
-                return self.bar + "foo"
-            def getself(self):
-                return self
-
-        t1 = TestClass('t1', TestClass('t1child'))
-        t2 = TestClass('t2', TestClass('t2child'))
-        t3 = TestClass('t3')
-
-        nl = NodeList([t1, t2, t3])
-        assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
-        assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
-        assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
-        assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
-               nl[0:2].child.foo()
-        assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
-               nl[0:2].child.bar
-
     def test_CLVar(self):
         """Test the command-line construction variable class"""
         f = SCons.Util.CLVar('a b')
@@ -738,6 +714,55 @@ class MD5TestCase(unittest.TestCase):
         s = MD5signature('222')
         assert 'bcbe3365e6ac95ea2c0343a2395834dd' == s, s
 
+class NodeListTestCase(unittest.TestCase):
+    def test_simple_attributes(self):
+        """Test simple attributes of a NodeList class"""
+        class TestClass:
+            def __init__(self, name, child=None):
+                self.child = child
+                self.bar = name
+
+        t1 = TestClass('t1', TestClass('t1child'))
+        t2 = TestClass('t2', TestClass('t2child'))
+        t3 = TestClass('t3')
+
+        nl = NodeList([t1, t2, t3])
+        assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
+        assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
+               nl[0:2].child.bar
+
+    def test_callable_attributes(self):
+        """Test callable attributes of a NodeList class"""
+        class TestClass:
+            def __init__(self, name, child=None):
+                self.child = child
+                self.bar = name
+            def foo(self):
+                return self.bar + "foo"
+            def getself(self):
+                return self
+
+        t1 = TestClass('t1', TestClass('t1child'))
+        t2 = TestClass('t2', TestClass('t2child'))
+        t3 = TestClass('t3')
+
+        nl = NodeList([t1, t2, t3])
+        assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
+        assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
+        assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
+        assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
+               nl[0:2].child.foo()
+        assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
+               nl[0:2].child.bar
+
+    def test_null(self):
+        """Test a null NodeList"""
+        nl = NodeList([])
+        r = str(nl)
+        assert r == '', r
+        for node in nl:
+            raise Exception, "should not enter this loop"
+
 
 class flattenTestCase(unittest.TestCase):
 
@@ -751,6 +776,7 @@ if __name__ == "__main__":
     tclasses = [ dictifyTestCase,
                  flattenTestCase,
                  MD5TestCase,
+                 NodeListTestCase,
                  UtilTestCase,
                ]
     for tclass in tclasses: