#!/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))