]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
new files
authorGuido van Rossum <guido@python.org>
Thu, 12 Jan 1995 12:29:47 +0000 (12:29 +0000)
committerGuido van Rossum <guido@python.org>
Thu, 12 Jan 1995 12:29:47 +0000 (12:29 +0000)
Lib/Complex.py [new file with mode: 0644]
Lib/cgi.py [new file with mode: 0755]
Lib/popen2.py [new file with mode: 0644]
Lib/rexec.py [new file with mode: 0644]

diff --git a/Lib/Complex.py b/Lib/Complex.py
new file mode 100644 (file)
index 0000000..f4892f3
--- /dev/null
@@ -0,0 +1,275 @@
+# Complex numbers
+# ---------------
+
+# This module represents complex numbers as instances of the class Complex.
+# A Complex instance z has two data attribues, z.re (the real part) and z.im
+# (the imaginary part).  In fact, z.re and z.im can have any value -- all
+# arithmetic operators work regardless of the type of z.re and z.im (as long
+# as they support numerical operations).
+#
+# The following functions exist (Complex is actually a class):
+# Complex([re [,im]) -> creates a complex number from a real and an imaginary part
+# IsComplex(z) -> true iff z is a complex number (== has .re and .im attributes)
+# Polar([r [,phi [,fullcircle]]]) ->
+#      the complex number z for which r == z.radius() and phi == z.angle(fullcircle)
+#      (r and phi default to 0)
+#
+# Complex numbers have the following methods:
+# z.abs() -> absolute value of z
+# z.radius() == z.abs()
+# z.angle([fullcircle]) -> angle from positive X axis; fullcircle gives units
+# z.phi([fullcircle]) == z.angle(fullcircle)
+#
+# These standard functions and unary operators accept complex arguments:
+# abs(z)
+# -z
+# +z
+# not z
+# repr(z) == `z`
+# str(z)
+# hash(z) -> a combination of hash(z.re) and hash(z.im) such that if z.im is zero
+#            the result equals hash(z.re)
+# Note that hex(z) and oct(z) are not defined.
+#
+# These conversions accept complex arguments only if their imaginary part is zero:
+# int(z)
+# long(z)
+# float(z)
+#
+# The following operators accept two complex numbers, or one complex number
+# and one real number (int, long or float):
+# z1 + z2
+# z1 - z2
+# z1 * z2
+# z1 / z2
+# pow(z1, z2)
+# cmp(z1, z2)
+# Note that z1 % z2 and divmod(z1, z2) are not defined,
+# nor are shift and mask operations.
+#
+# The standard module math does not support complex numbers.
+# (I suppose it would be easy to implement a cmath module.)
+#
+# Idea:
+# add a class Polar(r, phi) and mixed-mode arithmetic which
+# chooses the most appropriate type for the result:
+# Complex for +,-,cmp
+# Polar   for *,/,pow
+
+
+import types, math
+
+if not hasattr(math, 'hypot'):
+       def hypot(x, y):
+               # XXX I know there's a way to compute this without possibly causing
+               # overflow, but I can't remember what it is right now...
+               return math.sqrt(x*x + y*y)
+       math.hypot = hypot
+
+twopi = math.pi*2.0
+halfpi = math.pi/2.0
+
+def IsComplex(obj):
+       return hasattr(obj, 're') and hasattr(obj, 'im')
+
+def Polar(r = 0, phi = 0, fullcircle = twopi):
+       phi = phi * (twopi / fullcircle)
+       return Complex(math.cos(phi)*r, math.sin(phi)*r)
+
+class Complex:
+
+       def __init__(self, re=0, im=0):
+               if IsComplex(re):
+                       im = im + re.im
+                       re = re.re
+               if IsComplex(im):
+                       re = re - im.im
+                       im = im.re
+               self.re = re
+               self.im = im
+
+       def __setattr__(self, name, value):
+               if hasattr(self, name):
+                       raise TypeError, "Complex numbers have set-once attributes"
+               self.__dict__[name] = value
+
+       def __repr__(self):
+               if not self.im:
+                       return 'Complex(%s)' % `self.re`
+               else:
+                       return 'Complex(%s, %s)' % (`self.re`, `self.im`)
+
+       def __str__(self):
+               if not self.im:
+                       return `self.re`
+               else:
+                       return 'Complex(%s, %s)' % (`self.re`, `self.im`)
+
+       def __coerce__(self, other):
+               if IsComplex(other):
+                       return self, other
+               return self, Complex(other)     # May fail
+
+       def __cmp__(self, other):
+               return cmp(self.re, other.re) or cmp(self.im, other.im)
+
+       def __hash__(self):
+               if not self.im: return hash(self.re)
+               mod = sys.maxint + 1L
+               return int((hash(self.re) + 2L*hash(self.im) + mod) % (2L*mod) - mod)
+
+       def __neg__(self):
+               return Complex(-self.re, -self.im)
+
+       def __pos__(self):
+               return self
+
+       def __abs__(self):
+               return math.hypot(self.re, self.im)
+               ##return math.sqrt(self.re*self.re + self.im*self.im)
+
+
+       def __int__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to int"
+               return int(self.re)
+
+       def __long__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to long"
+               return long(self.re)
+
+       def __float__(self):
+               if self.im:
+                       raise ValueError, "can't convert Complex with nonzero im to float"
+               return float(self.re)
+
+       def __nonzero__(self):
+               return not (self.re == self.im == 0)
+
+       abs = radius = __abs__
+
+       def angle(self, fullcircle = twopi):
+               return (fullcircle/twopi) * ((halfpi - math.atan2(self.re, self.im)) % twopi)
+
+       phi = angle
+
+       def __add__(self, other):
+               return Complex(self.re + other.re, self.im + other.im)
+
+       __radd__ = __add__
+
+       def __sub__(self, other):
+               return Complex(self.re - other.re, self.im - other.im)
+
+       def __rsub__(self, other):
+               return Complex(other.re - self.re, other.im - self.im)
+
+       def __mul__(self, other):
+               return Complex(self.re*other.re - self.im*other.im,
+                              self.re*other.im + self.im*other.re)
+
+       __rmul__ = __mul__
+
+       def __div__(self, other):
+               # Deviating from the general principle of not forcing re or im
+               # to be floats, we cast to float here, otherwise division
+               # of Complex numbers with integer re and im parts would use
+               # the (truncating) integer division
+               d = float(other.re*other.re + other.im*other.im)
+               if not d: raise ZeroDivisionError, 'Complex division'
+               return Complex((self.re*other.re + self.im*other.im) / d,
+                              (self.im*other.re - self.re*other.im) / d)
+
+       def __rdiv__(self, other):
+               return other / self
+
+       def __pow__(self, n, z=None):
+               if z is not None:
+                       raise TypeError, 'Complex does not support ternary pow()'
+               if IsComplex(n):
+                       if n.im: raise TypeError, 'Complex to the Complex power'
+                       n = n.re
+               r = pow(self.abs(), n)
+               phi = n*self.angle()
+               return Complex(math.cos(phi)*r, math.sin(phi)*r)
+       
+       def __rpow__(self, base):
+               return pow(base, self)
+
+
+# Everything below this point is part of the test suite
+
+def checkop(expr, a, b, value, fuzz = 1e-6):
+       import sys
+       print '       ', a, 'and', b,
+       try:
+               result = eval(expr)
+       except:
+               result = sys.exc_type
+       print '->', result
+       if (type(result) == type('') or type(value) == type('')):
+               ok = result == value
+       else:
+               ok = abs(result - value) <= fuzz
+       if not ok:
+               print '!!\t!!\t!! should be', value, 'diff', abs(result - value)
+
+
+def test():
+       testsuite = {
+               'a+b': [
+                       (1, 10, 11),
+                       (1, Complex(0,10), Complex(1,10)),
+                       (Complex(0,10), 1, Complex(1,10)),
+                       (Complex(0,10), Complex(1), Complex(1,10)),
+                       (Complex(1), Complex(0,10), Complex(1,10)),
+               ],
+               'a-b': [
+                       (1, 10, -9),
+                       (1, Complex(0,10), Complex(1,-10)),
+                       (Complex(0,10), 1, Complex(-1,10)),
+                       (Complex(0,10), Complex(1), Complex(-1,10)),
+                       (Complex(1), Complex(0,10), Complex(1,-10)),
+               ],
+               'a*b': [
+                       (1, 10, 10),
+                       (1, Complex(0,10), Complex(0, 10)),
+                       (Complex(0,10), 1, Complex(0,10)),
+                       (Complex(0,10), Complex(1), Complex(0,10)),
+                       (Complex(1), Complex(0,10), Complex(0,10)),
+               ],
+               'a/b': [
+                       (1., 10, 0.1),
+                       (1, Complex(0,10), Complex(0, -0.1)),
+                       (Complex(0, 10), 1, Complex(0, 10)),
+                       (Complex(0, 10), Complex(1), Complex(0, 10)),
+                       (Complex(1), Complex(0,10), Complex(0, -0.1)),
+               ],
+               'pow(a,b)': [
+                       (1, 10, 1),
+                       (1, Complex(0,10), 'TypeError'),
+                       (Complex(0,10), 1, Complex(0,10)),
+                       (Complex(0,10), Complex(1), Complex(0,10)),
+                       (Complex(1), Complex(0,10), 'TypeError'),
+                       (2, Complex(4,0), 16),
+               ],
+               'cmp(a,b)': [
+                       (1, 10, -1),
+                       (1, Complex(0,10), 1),
+                       (Complex(0,10), 1, -1),
+                       (Complex(0,10), Complex(1), -1),
+                       (Complex(1), Complex(0,10), 1),
+               ],
+       }
+       exprs = testsuite.keys()
+       exprs.sort()
+       for expr in exprs:
+               print expr + ':'
+               t = (expr,)
+               for item in testsuite[expr]:
+                       apply(checkop, t+item)
+       
+
+if __name__ == '__main__':
+       test()
diff --git a/Lib/cgi.py b/Lib/cgi.py
new file mode 100755 (executable)
index 0000000..d412109
--- /dev/null
@@ -0,0 +1,278 @@
+#!/usr/local/bin/python
+#
+# A class for wrapping the WWW Forms Common Gateway Interface (CGI) 
+# Michael McLay, NIST  mclay@eeel.nist.gov  6/14/94
+# 
+# modified by Steve Majewski <sdm7g@Virginia.EDU> 12/5/94 
+#
+
+# Several classes to parse the name/value pairs that are passed to 
+# a server's CGI by GET, POST or PUT methods by a WWW FORM. This 
+# module is based on Mike McLay's original cgi.py after discussing
+# changes with him and others on the comp.lang.python newsgroup, and
+# at the NIST Python workshop. 
+#
+# The rationale for changes was:
+#    The original FormContent class was almost, but not quite like
+#    a dictionary object. Besides adding some extra access methods,
+#    it had a values() method with different arguments and semantics
+#    from the standard values() method of a mapping object. Also, 
+#    it provided several different access methods that may be necessary
+#    or useful, but made it a little more confusing to figure out how
+#    to use. Also, we wanted to make the most typical cases the simplest
+#    and most convenient access methods. ( Most form fields just return
+#    a single value, and in practice, a lot of code was just assuming
+#    a single value and ignoring all others. On the other hand, the 
+#    protocol allows multiple values to be returned. 
+#
+#  The new base class (FormContentDict) is just like a dictionary.
+#  In fact, if you just want a dictionary, all of the stuff that was
+#  in __init__ has been extracted into a cgi.parse() function that will
+#  return the "raw" dictionary, but having a class allows you to customize 
+#  it further. 
+#   Mike McLay's original FormContent class is reimplemented as a 
+#  subclass of FormContentDict.
+#   There are two additional sub-classes, but I'm not yet too sure 
+#  whether they are what I want. 
+# 
+
+import string,regsub,sys,os,urllib
+# since os.environ may often be used in cgi code, we name it in this module.
+from os import environ
+
+
+def parse():
+       if environ['REQUEST_METHOD'] == 'POST':
+               qs = sys.stdin.read(string.atoi(environ['CONTENT_LENGTH']))
+               environ['QUERY_STRING'] = qs
+       else:
+               qs = environ['QUERY_STRING']
+       name_value_pairs = string.splitfields(qs, '&')
+       dict = {}
+       for name_value in name_value_pairs:
+               nv = string.splitfields(name_value, '=')
+               if len(nv) != 2:
+                       continue
+               name = nv[0]
+               value = urllib.unquote(regsub.gsub('+',' ',nv[1]))
+               if len(value):
+                       if dict.has_key (name):
+                               dict[name].append(value)
+                       else:
+                               dict[name] = [value]
+       return dict
+
+
+
+# The FormContent constructor creates a dictionary from the name/value pairs
+# passed through the CGI interface.
+
+
+#
+#  form['key'] 
+#  form.__getitem__('key') 
+#  form.has_key('key')
+#  form.keys()
+#  form.values()
+#  form.items()
+#  form.dict
+
+class FormContentDict:
+       def __init__( self ):
+               self.dict = parse()
+               self.query_string = environ['QUERY_STRING']
+       def __getitem__(self,key):
+               return self.dict[key]
+       def keys(self):
+               return self.dict.keys()
+       def has_key(self, key):
+               return self.dict.has_key(key)
+       def values(self):
+               return self.dict.values()
+       def items(self):
+               return self.dict.items() 
+       def __len__( self ):
+               return len(self.dict)
+
+
+# This is the "strict" single-value expecting version. 
+# IF you only expect a single value for each field, then form[key]
+# will return that single value ( the [0]-th ), and raise an 
+# IndexError if that expectation is not true. 
+# IF you expect a field to have possible multiple values, than you
+# can use form.getlist( key ) to get all of the values. 
+# values() and items() are a compromise: they return single strings
+#  where there is a single value, and lists of strings otherwise. 
+
+class SvFormContentDict(FormContentDict):
+       def __getitem__( self, key ):
+               if len( self.dict[key] ) > 1 : 
+                       raise IndexError, 'expecting a single value' 
+               return self.dict[key][0]
+       def getlist( self, key ):
+               return self.dict[key]
+       def values( self ):
+               lis = []
+               for each in self.dict.values() : 
+                       if len( each ) == 1 : 
+                               lis.append( each[0] )
+                       else: lis.append( each )
+               return lis
+       def items( self ):
+               lis = []
+               for key,value in self.dict.items():
+                       if len(value) == 1 :
+                               lis.append( (key,value[0]) )
+                       else:   lis.append( (key,value) )
+               return lis
+
+
+# And this sub-class is similar to the above, but it will attempt to 
+# interpret numerical values. This is here as mostly as an example,
+# but I think the real way to handle typed-data from a form may be
+# to make an additional table driver parsing stage that has a table
+# of allowed input patterns and the output conversion types - it 
+# would signal type-errors on parse, not on access. 
+class InterpFormContentDict(SvFormContentDict):
+       def __getitem__( self, key ):
+               v = SvFormContentDict.__getitem__( self, key )
+               if v[0] in string.digits+'+-.' : 
+                       try:  return  string.atoi( v ) 
+                       except ValueError:
+                               try:    return string.atof( v )
+                               except ValueError: pass
+               return string.strip(v)
+       def values( self ):
+               lis = [] 
+               for key in self.keys():
+                       try:
+                               lis.append( self[key] )
+                       except IndexError:
+                               lis.append( self.dict[key] )
+               return lis
+       def items( self ):
+               lis = [] 
+               for key in self.keys():
+                       try:
+                               lis.append( (key, self[key]) )
+                       except IndexError:
+                               lis.append( (key, self.dict[key]) )
+               return lis
+
+
+# class FormContent parses the name/value pairs that are passed to a
+# server's CGI by GET, POST, or PUT methods by a WWW FORM. several 
+# specialized FormContent dictionary access methods have been added 
+# for convenience.
+
+# function                   return value
+#
+# form.keys()                     all keys in dictionary
+# form.has_key('key')             test keys existance
+# form[key]                       returns list associated with key
+# form.values('key')              key's list (same as form.[key])
+# form.indexed_value('key' index) nth element in key's value list
+# form.value(key)                 key's unstripped value 
+# form.length(key)                number of elements in key's list
+# form.stripped(key)              key's value with whitespace stripped
+# form.pars()                     full dictionary 
+
+
+
+class FormContent(FormContentDict):
+# This is the original FormContent semantics of values,
+# not the dictionary like semantics. 
+       def values(self,key):
+               if self.dict.has_key(key):return self.dict[key]
+               else: return None
+       def indexed_value(self,key, location):
+               if self.dict.has_key(key):
+                       if len (self.dict[key]) > location:
+                               return self.dict[key][location]
+                       else: return None
+               else: return None
+       def value(self,key):
+               if self.dict.has_key(key):return self.dict[key][0]
+               else: return None
+       def length(self,key):
+               return len (self.dict[key])
+       def stripped(self,key):
+               if self.dict.has_key(key):return string.strip(self.dict[key][0])
+               else: return None
+       def pars(self):
+               return self.dict
+
+
+
+
+
+
+def print_environ_usage():
+       print """
+<H3>These operating system environment variables could have been 
+set:</H3> <UL>
+<LI>AUTH_TYPE
+<LI>CONTENT_LENGTH
+<LI>CONTENT_TYPE
+<LI>DATE_GMT
+<LI>DATE_LOCAL
+<LI>DOCUMENT_NAME
+<LI>DOCUMENT_ROOT
+<LI>DOCUMENT_URI
+<LI>GATEWAY_INTERFACE
+<LI>LAST_MODIFIED
+<LI>PATH
+<LI>PATH_INFO
+<LI>PATH_TRANSLATED
+<LI>QUERY_STRING
+<LI>REMOTE_ADDR
+<LI>REMOTE_HOST
+<LI>REMOTE_IDENT
+<LI>REMOTE_USER
+<LI>REQUEST_METHOD
+<LI>SCRIPT_NAME
+<LI>SERVER_NAME
+<LI>SERVER_PORT
+<LI>SERVER_PROTOCOL
+<LI>SERVER_ROOT
+<LI>SERVER_SOFTWARE
+</UL>
+"""
+
+def print_environ():
+       skeys = environ.keys()
+       skeys.sort()
+       print '<h3> The following environment variables were set by the CGI script: </H3>'
+       print '<dl>'
+       for key in skeys:
+               print '<dt>',key, '<dd>', environ[key]
+       print '</dl>' 
+
+def print_form( form ):
+       print '<h3> The following name/value pairs were entered in the form:</h3>'
+       print '<dl>'
+       skeys = form.keys()
+       skeys.sort()
+       for key in skeys:
+               print '<dt>',key, ' : <i> ',escape(`type(form[key])`),' </i>','<dd>', form[key]
+       print '</dl>'
+
+def escape( s ):
+       return regsub.gsub( '<', '&lt;', regsub.gsub( '>' , '&gt;', s ))
+
+def test( what ):
+       label = escape(str(what))
+       print 'Content-type: text/html\n\n'
+       print '<HEADER>\n<TITLE>' + label + '</TITLE>\n</HEADER>\n'
+       print '<BODY>\n' 
+       print "<H1>" + label +"</H1>\n"
+       form = what()
+       print_form( form )
+       print_environ()
+       print_environ_usage() 
+       print '</body>'
+
+if __name__ == '__main__' : 
+       test_classes = ( FormContent, FormContentDict, SvFormContentDict, InterpFormContentDict )
+       test( test_classes[0] ) # by default, test compatibility with 
+                               # old version, change index to test others.
diff --git a/Lib/popen2.py b/Lib/popen2.py
new file mode 100644 (file)
index 0000000..d195fef
--- /dev/null
@@ -0,0 +1,35 @@
+import os
+import sys
+import string
+
+MAXFD = 100    # Max number of file descriptors (os.getdtablesize()???)
+
+def popen2(cmd):
+       cmd = string.split(cmd)
+       p2cread, p2cwrite = os.pipe()
+       c2pread, c2pwrite = os.pipe()
+       pid = os.fork()
+       if pid == 0:
+               # Child
+               os.close(0)
+               os.close(1)
+               if os.dup(p2cread) <> 0:
+                       sys.stderr.write('popen2: bad read dup\n')
+               if os.dup(c2pwrite) <> 1:
+                       sys.stderr.write('popen2: bad write dup\n')
+               for i in range(3, MAXFD):
+                       try:
+                               os.close(i)
+                       except:
+                               pass
+               try:
+                       os.execv(cmd[0], cmd)
+               finally:
+                       os._exit(1)
+               # Shouldn't come here, I guess
+               os._exit(1)
+       os.close(p2cread)
+       tochild = os.fdopen(p2cwrite, 'w')
+       os.close(c2pwrite)
+       fromchild = os.fdopen(c2pread, 'r')
+       return fromchild, tochild
diff --git a/Lib/rexec.py b/Lib/rexec.py
new file mode 100644 (file)
index 0000000..0e6ba20
--- /dev/null
@@ -0,0 +1,184 @@
+# Implement restricted execution of Python code
+
+import __builtin__
+import new
+import os
+import sys
+import types
+
+def trace(fmt, *args):
+       if 0:
+               sys.stderr.write(fmt % args + '\n')
+
+def copydict(src, dst, exceptions = [], only = None):
+       if only is None:
+               for key in src.keys():
+                       if key not in exceptions:
+                               dst[key] = src[key]
+       else:
+               for key in only:
+                       dst[key] = src[key]
+
+def copymodule(src, dst, exceptions = [], only = None):
+       copydict(src.__dict__, dst.__dict__, exceptions, only)
+
+safe_path = ['/ufs/guido/lib/python']
+safe_modules = ['array', 'math', 'regex', 'strop', 'time']
+unsafe_builtin_names = ['open', 'reload', '__import__',
+                       'eval', 'execfile', 'dir', 'vars',
+                       'raw_input', 'input']
+safe_posix_names = ['error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat',
+                   'times', 'uname', 'getpid', 'getppid', 'getcwd',
+                   'getuid', 'getgid', 'geteuid', 'getegid']
+
+safe_sys = new.module('sys')
+safe_sys.modules = {}
+safe_sys.modules['sys'] = safe_sys
+safe_sys.path = safe_path[:]
+safe_sys.argv = ['-']
+safe_sys.builtin_module_names = safe_modules[:] + ['posix']
+safe_sys.builtin_module_names.sort()
+safe_sys.copyright = sys.copyright
+safe_sys.version = sys.version + ' [restricted mode]'
+safe_sys.exit = sys.exit
+
+def new_module(name):
+       safe_sys.modules[name] = m = new.module(name)
+       return m
+
+safe_builtin = new_module('__builtin__')
+copymodule(__builtin__, safe_builtin, unsafe_builtin_names)
+
+safe_main = new_module('__main__')
+
+safe_posix = new_module('posix')
+import posix
+copymodule(posix, safe_posix, None, safe_posix_names)
+safe_posix.environ = {}
+copydict(posix.environ, safe_posix.environ)
+
+safe_types = new_module('types')
+copymodule(types, safe_types)
+
+def safe_import(name):
+       if safe_sys.modules.has_key(name):
+               return safe_sys.modules[name]
+       if name in safe_modules:
+               temp = {}
+               exec "import "+name in temp
+               m = new_module(name)
+               copymodule(temp[name], m)
+               return m
+       for dirname in safe_path:
+               filename = os.path.join(dirname, name + '.py')
+               try:
+                       f = open(filename, 'r')
+                       f.close()
+               except IOError:
+                       continue
+               m = new_module(name)
+               rexecfile(filename, m.__dict__)
+               return m
+       raise ImportError, name
+safe_builtin.__import__ = safe_import
+
+def safe_open(file, mode = 'r'):
+       if type(file) != types.StringType or type(mode) != types.StringType:
+               raise TypeError, 'open argument(s) must be string(s)'
+       if mode not in ('r', 'rb'):
+               raise IOError, 'open for writing not allowed'
+       if '/' in file:
+               raise IOError, 'open pathname not allowed'
+       return open(file, mode)
+safe_builtin.open = safe_open
+
+def safe_dir(object = safe_main):
+       keys = object.__dict__.keys()
+       keys.sort()
+       return keys
+safe_builtin.dir = safe_dir
+
+def safe_vars(object = safe_main):
+       keys = safe_dir(object)
+       dict = {}
+       copydict(object.__dict__, dict, None, keys)
+       return dict
+safe_builtin.vars = safe_vars
+
+
+def exterior():
+       """Return env of caller's caller, as triple: (name, locals, globals).
+
+       Name will be None if env is __main__, and locals will be None if same
+       as globals, ie local env is global env."""
+
+       import sys, __main__
+
+       bogus = 'bogus'                 # A locally usable exception
+       try: raise bogus                # Force an exception
+       except bogus:
+               at = sys.exc_traceback.tb_frame.f_back # The external frame.
+               if at.f_back: at = at.f_back # And further, if any.
+               where, globals, locals = at.f_code, at.f_globals, at.f_locals
+               if locals == globals:   # Exterior is global?
+                       locals = None
+               if where:
+                       where = where.co_name
+               return (where, locals, globals)
+
+
+def rexec(str, globals = None, locals = None):
+       trace('rexec(%s, ...)', `str`)
+       if globals is None:
+               globals = locals = exterior()[2]
+       elif locals is None:
+               locals = globals
+       globals['__builtins__'] = safe_builtin.__dict__
+       safe_sys.stdout = sys.stdout
+       safe_sys.stderr = sys.stderr
+       exec str in globals, locals
+
+def rexecfile(file, globals = None, locals = None):
+       trace('rexecfile(%s, ...)', `file`)
+       if globals is None:
+               globals = locals = exterior()[2]
+       elif locals is None:
+               locals = globals
+       globals['__builtins__'] = safe_builtin.__dict__
+       safe_sys.stdout = sys.stdout
+       safe_sys.stderr = sys.stderr
+       return execfile(file, globals, locals)
+
+def reval(str, globals = None, locals = None):
+       trace('reval(%s, ...)', `str`)
+       if globals is None:
+               globals = locals = exterior()[2]
+       elif locals is None:
+               locals = globals
+       globals['__builtins__'] = safe_builtin.__dict__
+       safe_sys.stdout = sys.stdout
+       safe_sys.stderr = sys.stderr
+       return eval(str, globals, locals)
+safe_builtin.eval = reval
+
+
+def test():
+       import traceback
+       g = {}
+       while 1:
+               try:
+                       s = raw_input('--> ')
+               except EOFError:
+                       break
+               try:
+                       try:
+                               c = compile(s, '', 'eval')
+                       except:
+                               rexec(s, g)
+                       else:
+                               print reval(c, g)
+               except:
+                       traceback.print_exc()
+
+if __name__ == '__main__':
+       test()