Welcome, guest | Sign In | My Account | Store | Cart
#!/usr/bin/env python

"""
stackEnv -- a module providing a stack-based environment to automatically
            transfer some arguments (stackEnv) to called functions without
            passing them explicitly in the call, thus forming a kind of
            hierarchical environment as known from Unix processes
"""

import sys
import unittest

### internals
##############################

_PREFIX = '__STACKENV:'

def _internalName(name):
    "internally used name for the stackEnv variable of the given name"
    return _PREFIX + name

def _internalDelMarker(name):
    """
    internally used name to marker that the stackEnv variable of the
    given name is deleted below this frame
    """
    return _PREFIX + '!' + name

### low level
##############################

def setStackEnv(frame, name, value):
    """
    set the stackEnv variable of the given name to the given value
    in the given frame
    """
    frame.f_locals[_internalName(name)] = value
    delMarker = _internalDelMarker(name)
    if delMarker in frame.f_locals:
        del frame.f_locals[delMarker]

def delStackEnv(frame, name):
    """
    mark the stackEnv variable of the given name as deleted below the
    given frame
    """
    frame.f_locals[_internalDelMarker(name)] = True

class NoSuchStackEnv(Exception):
    """
    Exception stating that the a stackEnv variable for the given name
    does not exist
    """
    pass

def getStackEnv(frame, name):
    """
    return the stackEnv variable value of the given name
    """
    internalName = _internalName(name)
    delMarker = _internalDelMarker(name)
    walker = frame
    while walker:
        if delMarker in walker.f_locals:  # explicitly deleted in this frame?
            raise NoSuchStackEnv(name)
        try:
            return walker.f_locals[internalName]
        except KeyError:  # not in this frame
            walker = walker.f_back
    # not found anywhere in the frames above
    raise NoSuchStackEnv(name)

def yieldAllStackEnvItems(frame):
    "yield all stackEnv variable names and values in use above the given frame"
    found = set([])
    walker = frame
    while walker:
        for name, value in walker.f_locals.iteritems():
            if name.startswith(_PREFIX):
                name = name[len(_PREFIX):]  # strip off prefix
                if name.startswith('!'):  # del marker?
                    found.add(name[1:])  # just store it (w/o '!') as found
                elif name not in found:  # new name?
                    yield name, value
                    found.add(name)
        walker = walker.f_back

### high level
##############################

def setStackEnvs(**kwargs):
    "set a bunch of stackEnv variables in one step via kwargs"
    backFrame = sys._getframe().f_back
    for name, value in kwargs.iteritems():
        setStackEnv(backFrame, name, value)

class StackEnv(dict):
    """
    The singleton instance of this class is the main interface to the stackEnv.
    See the unit test below for example usage (which is as simple as possible).
    """

    def __str__(self):
        return '{ %s }' % ', '.join('%r: %r' % item for item in self.iteritems())

    def __repr__(self):
        return '{ %s }' % ', '.join('%r: %r' % item for item in self.iteritems())

    def iteritems(self):
        return yieldAllStackEnvItems(sys._getframe().f_back)

    def items(self):
        return [ item for item in self.iteritems() ]

    def __iter__(self):
        for name, value in yieldAllStackEnvItems(sys._getframe().f_back):
            yield name

    def __setattr__(self, name, value):
        setStackEnv(sys._getframe().f_back, name, value)

    # TODO: def __iadd__() etc.
    
    def __delattr__(self, name):
        delStackEnv(sys._getframe().f_back, name)
    
    def __getattr__(self, name):
        return getStackEnv(sys._getframe().f_back, name)
    
    def __contains__(self, name):
        try:
            getStackEnv(sys._getframe().f_back, name)
            return True
        except NoSuchStackEnv:
            return False

    def clear(self):
        # collect the names first to prevent changes during iteration:
        names = [ name
            for name, value in yieldAllStackEnvItems(sys._getframe().f_back) ]
        for name in names:
            delStackEnv(sys._getframe().f_back, name)
    
stackEnv = StackEnv()

### Unit tests
###########################################

class Test(unittest.TestCase):
    def runTest(self):

        def b():
            self.assertEqual(42, stackEnv.a)
            self.assertFalse('b' in stackEnv)
            stackEnv.a = 43
            stackEnv.b = 24
            self.assertEqual(43, stackEnv.a)
            self.assertTrue('b' in stackEnv)
            self.assertTrue(24, stackEnv.b)

        def a():
            self.assertTrue(41, stackEnv.a)
            self.assertTrue('b' in stackEnv)
            self.assertEqual(23, stackEnv.b)
            stackEnv.a = 42
            del stackEnv.b
            self.assertEqual(42, stackEnv.a)
            self.assertFalse('b' in stackEnv)
            b()
            self.assertEqual(42, stackEnv.a)
            self.assertFalse('b' in stackEnv)

        for version in ('short', 'long'):
            if version == 'long':
                stackEnv.a = 41
                stackEnv.b = 23
            elif version == 'short':
                setStackEnvs(a=41, b=23)
            self.assertEqual(41, stackEnv.a)
            self.assertTrue('b' in stackEnv)
            self.assertEqual(23, stackEnv.b)
            a()
            self.assertEqual(41, stackEnv.a)
            self.assertTrue('b' in stackEnv)
            self.assertEqual(23, stackEnv.b)
            stackEnv.clear()
            self.assertFalse('a' in stackEnv)
            self.assertFalse('b' in stackEnv)

def main(argv):
    unittest.main(argv=argv)
    return 0

if __name__ == '__main__':
    sys.exit(main(sys.argv))

Diff to Previous Revision

--- revision 2 2012-05-29 14:53:25
+++ revision 3 2017-06-22 10:24:05
@@ -1,195 +1,195 @@
-    #!/usr/bin/env python
+#!/usr/bin/env python
+
+"""
+stackEnv -- a module providing a stack-based environment to automatically
+            transfer some arguments (stackEnv) to called functions without
+            passing them explicitly in the call, thus forming a kind of
+            hierarchical environment as known from Unix processes
+"""
+
+import sys
+import unittest
+
+### internals
+##############################
+
+_PREFIX = '__STACKENV:'
+
+def _internalName(name):
+    "internally used name for the stackEnv variable of the given name"
+    return _PREFIX + name
+
+def _internalDelMarker(name):
+    """
+    internally used name to marker that the stackEnv variable of the
+    given name is deleted below this frame
+    """
+    return _PREFIX + '!' + name
+
+### low level
+##############################
+
+def setStackEnv(frame, name, value):
+    """
+    set the stackEnv variable of the given name to the given value
+    in the given frame
+    """
+    frame.f_locals[_internalName(name)] = value
+    delMarker = _internalDelMarker(name)
+    if delMarker in frame.f_locals:
+        del frame.f_locals[delMarker]
+
+def delStackEnv(frame, name):
+    """
+    mark the stackEnv variable of the given name as deleted below the
+    given frame
+    """
+    frame.f_locals[_internalDelMarker(name)] = True
+
+class NoSuchStackEnv(Exception):
+    """
+    Exception stating that the a stackEnv variable for the given name
+    does not exist
+    """
+    pass
+
+def getStackEnv(frame, name):
+    """
+    return the stackEnv variable value of the given name
+    """
+    internalName = _internalName(name)
+    delMarker = _internalDelMarker(name)
+    walker = frame
+    while walker:
+        if delMarker in walker.f_locals:  # explicitly deleted in this frame?
+            raise NoSuchStackEnv(name)
+        try:
+            return walker.f_locals[internalName]
+        except KeyError:  # not in this frame
+            walker = walker.f_back
+    # not found anywhere in the frames above
+    raise NoSuchStackEnv(name)
+
+def yieldAllStackEnvItems(frame):
+    "yield all stackEnv variable names and values in use above the given frame"
+    found = set([])
+    walker = frame
+    while walker:
+        for name, value in walker.f_locals.iteritems():
+            if name.startswith(_PREFIX):
+                name = name[len(_PREFIX):]  # strip off prefix
+                if name.startswith('!'):  # del marker?
+                    found.add(name[1:])  # just store it (w/o '!') as found
+                elif name not in found:  # new name?
+                    yield name, value
+                    found.add(name)
+        walker = walker.f_back
+
+### high level
+##############################
+
+def setStackEnvs(**kwargs):
+    "set a bunch of stackEnv variables in one step via kwargs"
+    backFrame = sys._getframe().f_back
+    for name, value in kwargs.iteritems():
+        setStackEnv(backFrame, name, value)
+
+class StackEnv(dict):
+    """
+    The singleton instance of this class is the main interface to the stackEnv.
+    See the unit test below for example usage (which is as simple as possible).
+    """
+
+    def __str__(self):
+        return '{ %s }' % ', '.join('%r: %r' % item for item in self.iteritems())
+
+    def __repr__(self):
+        return '{ %s }' % ', '.join('%r: %r' % item for item in self.iteritems())
+
+    def iteritems(self):
+        return yieldAllStackEnvItems(sys._getframe().f_back)
+
+    def items(self):
+        return [ item for item in self.iteritems() ]
+
+    def __iter__(self):
+        for name, value in yieldAllStackEnvItems(sys._getframe().f_back):
+            yield name
+
+    def __setattr__(self, name, value):
+        setStackEnv(sys._getframe().f_back, name, value)
+
+    # TODO: def __iadd__() etc.
     
-    """
-    stackEnv -- a module providing a stack-based environment to automatically
-                transfer some arguments (stackEnv) to called functions without
-                passing them explicitly in the call, thus forming a kind of
-                hierarchical environment as known from Unix processes
-    """
+    def __delattr__(self, name):
+        delStackEnv(sys._getframe().f_back, name)
     
-    import sys
-    import unittest
+    def __getattr__(self, name):
+        return getStackEnv(sys._getframe().f_back, name)
     
-    ### internals
-    ##############################
+    def __contains__(self, name):
+        try:
+            getStackEnv(sys._getframe().f_back, name)
+            return True
+        except NoSuchStackEnv:
+            return False
+
+    def clear(self):
+        # collect the names first to prevent changes during iteration:
+        names = [ name
+            for name, value in yieldAllStackEnvItems(sys._getframe().f_back) ]
+        for name in names:
+            delStackEnv(sys._getframe().f_back, name)
     
-    _PREFIX = '__STACKENV:'
-    
-    def _internalName(name):
-        "internally used name for the stackEnv variable of the given name"
-        return _PREFIX + name
-    
-    def _internalDelMarker(name):
-        """
-        internally used name to marker that the stackEnv variable of the
-        given name is deleted below this frame
-        """
-        return _PREFIX + '!' + name
-    
-    ### low level
-    ##############################
-    
-    def setStackEnv(frame, name, value):
-        """
-        set the stackEnv variable of the given name to the given value
-        in the given frame
-        """
-        frame.f_locals[_internalName(name)] = value
-        delMarker = _internalDelMarker(name)
-        if delMarker in frame.f_locals:
-            del frame.f_locals[delMarker]
-    
-    def delStackEnv(frame, name):
-        """
-        mark the stackEnv variable of the given name as deleted below the
-        given frame
-        """
-        frame.f_locals[_internalDelMarker(name)] = True
-    
-    class NoSuchStackEnv(Exception):
-        """
-        Exception stating that the a stackEnv variable for the given name
-        does not exist
-        """
-        pass
-    
-    def getStackEnv(frame, name):
-        """
-        return the stackEnv variable value of the given name
-        """
-        internalName = _internalName(name)
-        delMarker = _internalDelMarker(name)
-        walker = frame
-        while walker:
-            if delMarker in walker.f_locals:  # explicitly deleted in this frame?
-                raise NoSuchStackEnv(name)
-            try:
-                return walker.f_locals[internalName]
-            except KeyError:  # not in this frame
-                walker = walker.f_back
-        # not found anywhere in the frames above
-        raise NoSuchStackEnv(name)
-    
-    def yieldAllStackEnvItems(frame):
-        "yield all stackEnv variable names and values in use above the given frame"
-        found = set([])
-        walker = frame
-        while walker:
-            for name, value in walker.f_locals.iteritems():
-                if name.startswith(_PREFIX):
-                    name = name[len(_PREFIX):]  # strip off prefix
-                    if name.startswith('!'):  # del marker?
-                        found.add(name[1:])  # just store it (w/o '!') as found
-                    elif name not in found:  # new name?
-                        yield name, value
-                        found.add(name)
-            walker = walker.f_back
-    
-    ### high level
-    ##############################
-    
-    def setStackEnvs(**kwargs):
-        "set a bunch of stackEnv variables in one step via kwargs"
-        backFrame = sys._getframe().f_back
-        for name, value in kwargs.iteritems():
-            setStackEnv(backFrame, name, value)
-    
-    class StackEnv(dict):
-        """
-        The singleton instance of this class is the main interface to the stackEnv.
-        See the unit test below for example usage (which is as simple as possible).
-        """
-    
-        def __str__(self):
-            return '{ %s }' % ', '.join('%r: %r' % item for item in self.iteritems())
-    
-        def __repr__(self):
-            return '{ %s }' % ', '.join('%r: %r' % item for item in self.iteritems())
-    
-        def iteritems(self):
-            return yieldAllStackEnvItems(sys._getframe().f_back)
-    
-        def items(self):
-            return [ item for item in self.iteritems() ]
-    
-        def __iter__(self):
-            for name, value in yieldAllStackEnvItems(sys._getframe().f_back):
-                yield name
-    
-        def __setattr__(self, name, value):
-            setStackEnv(sys._getframe().f_back, name, value)
-    
-        # TODO: def __iadd__() etc.
-        
-        def __delattr__(self, name):
-            delStackEnv(sys._getframe().f_back, name)
-        
-        def __getattr__(self, name):
-            return getStackEnv(sys._getframe().f_back, name)
-        
-        def __contains__(self, name):
-            try:
-                getStackEnv(sys._getframe().f_back, name)
-                return True
-            except NoSuchStackEnv:
-                return False
-    
-        def clear(self):
-            # collect the names first to prevent changes during iteration:
-            names = [ name
-                for name, value in yieldAllStackEnvItems(sys._getframe().f_back) ]
-            for name in names:
-                delStackEnv(sys._getframe().f_back, name)
-        
-    stackEnv = StackEnv()
-    
-    ### Unit tests
-    ###########################################
-    
-    class Test(unittest.TestCase):
-        def runTest(self):
-    
-            def b():
-                self.assertEqual(42, stackEnv.a)
-                self.assertFalse('b' in stackEnv)
-                stackEnv.a = 43
-                stackEnv.b = 24
-                self.assertEqual(43, stackEnv.a)
-                self.assertTrue('b' in stackEnv)
-                self.assertTrue(24, stackEnv.b)
-    
-            def a():
-                self.assertTrue(41, stackEnv.a)
-                self.assertTrue('b' in stackEnv)
-                self.assertEqual(23, stackEnv.b)
-                stackEnv.a = 42
-                del stackEnv.b
-                self.assertEqual(42, stackEnv.a)
-                self.assertFalse('b' in stackEnv)
-                b()
-                self.assertEqual(42, stackEnv.a)
-                self.assertFalse('b' in stackEnv)
-    
-            for version in ('short', 'long'):
-                if version == 'long':
-                    stackEnv.a = 41
-                    stackEnv.b = 23
-                elif version == 'short':
-                    setStackEnvs(a=41, b=23)
-                self.assertEqual(41, stackEnv.a)
-                self.assertTrue('b' in stackEnv)
-                self.assertEqual(23, stackEnv.b)
-                a()
-                self.assertEqual(41, stackEnv.a)
-                self.assertTrue('b' in stackEnv)
-                self.assertEqual(23, stackEnv.b)
-                stackEnv.clear()
-                self.assertFalse('a' in stackEnv)
-                self.assertFalse('b' in stackEnv)
-    
-    def main(argv):
-        unittest.main(argv=argv)
-        return 0
-    
-    if __name__ == '__main__':
-        sys.exit(main(sys.argv))
+stackEnv = StackEnv()
+
+### Unit tests
+###########################################
+
+class Test(unittest.TestCase):
+    def runTest(self):
+
+        def b():
+            self.assertEqual(42, stackEnv.a)
+            self.assertFalse('b' in stackEnv)
+            stackEnv.a = 43
+            stackEnv.b = 24
+            self.assertEqual(43, stackEnv.a)
+            self.assertTrue('b' in stackEnv)
+            self.assertTrue(24, stackEnv.b)
+
+        def a():
+            self.assertTrue(41, stackEnv.a)
+            self.assertTrue('b' in stackEnv)
+            self.assertEqual(23, stackEnv.b)
+            stackEnv.a = 42
+            del stackEnv.b
+            self.assertEqual(42, stackEnv.a)
+            self.assertFalse('b' in stackEnv)
+            b()
+            self.assertEqual(42, stackEnv.a)
+            self.assertFalse('b' in stackEnv)
+
+        for version in ('short', 'long'):
+            if version == 'long':
+                stackEnv.a = 41
+                stackEnv.b = 23
+            elif version == 'short':
+                setStackEnvs(a=41, b=23)
+            self.assertEqual(41, stackEnv.a)
+            self.assertTrue('b' in stackEnv)
+            self.assertEqual(23, stackEnv.b)
+            a()
+            self.assertEqual(41, stackEnv.a)
+            self.assertTrue('b' in stackEnv)
+            self.assertEqual(23, stackEnv.b)
+            stackEnv.clear()
+            self.assertFalse('a' in stackEnv)
+            self.assertFalse('b' in stackEnv)
+
+def main(argv):
+    unittest.main(argv=argv)
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))

History