diff --git a/util/namespace.py b/util/namespace.py new file mode 100644 index 0000000..07920d4 --- /dev/null +++ b/util/namespace.py @@ -0,0 +1,88 @@ +# +# Copyright (c) 2008 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Special namespaces for execution of Python code, e.g. for implementing +configuration or export/import formats, or providing a restricted Python +for scripts or an interactive interpreter. + +$Id$ +""" + +import traceback + +_not_found = object() + + +class BaseNamespace(dict): + + builtins = 'dir', 'output' + + def __init__(self, *args, **kw): + super(BaseNamespace, self).__init__(*args, **kw) + self['__builtins__'] = {} + for key in self.builtins: + self[key] = getattr(self, key) + + def output(self, value): + print value + + def dir(self, obj=None): + if obj is None: + return dir(self) + return dir(obj) + + def __getitem__(self, key): + result = self.get(key, _not_found) + if result is _not_found: + raise NameError(key) + return result + + def __getattr__(self, key): + result = self.get(key, _not_found) + if result is _not_found: + raise AttributeError(key) + return result + + +class Symbol(object): + + def __init__(self, namespace, name): + self.namespace = namespace + self.name = name + + def __str__(self): + return self.name + + def __repr__(self): + return "" % self.name + + +class AutoNamespace(BaseNamespace): + + symbolFactory = Symbol + + def __getitem__(self, key): + result = self.get(key, _not_found) + if result is _not_found: + result = getattr(self, key, _not_found) + if result is _not_found: + sym = Symbol(self, key) + self[key] = sym + return sym + return result diff --git a/util/namespace.txt b/util/namespace.txt new file mode 100644 index 0000000..9528a4b --- /dev/null +++ b/util/namespace.txt @@ -0,0 +1,63 @@ +============================================== +Python Execution in Special Dynamic Namespaces +============================================== + +$Id$ + + >>> from cybertools.util import namespace + + +A Very Restricted Basic Namespace +================================= + + >>> minimal = namespace.BaseNamespace() + +We + + >>> code = """ + ... print 'Hello' + ... # a comment + ... print dir('') + ... print '__builtins__:', __builtins__ + ... output('something') + ... a = 'a is a variable' + ... if a: + ... print a + ... """ + >>> exec code in minimal + Hello + ['__add__', ..., 'zfill'] + __builtins__: {} + something + a is a variable + +Assignments in code executed in a namespace may be accessed as attributes +of the namespace later. + + >>> minimal.a + 'a is a variable' + +By setting the ``__builtins__`` mapping to an empty dictionary the minimal +namespace provides a secure restricted execution environment. + + >>> exec "import os" in minimal + Traceback (most recent call last): + ... + ImportError: __import__ not found + + >>> exec "f = open('dummy.txt', 'w')" in minimal + Traceback (most recent call last): + ... + NameError: open + + +A Namespace Automatically Generating Symbols +============================================ + + >>> auto = namespace.AutoNamespace() + + >>> exec "print something" in auto + something + + >>> auto.something + diff --git a/util/tests.py b/util/tests.py index 449a190..37f7ff8 100755 --- a/util/tests.py +++ b/util/tests.py @@ -23,6 +23,7 @@ def test_suite(): doctest.DocFileSuite('config.txt', optionflags=flags), doctest.DocFileSuite('defer.txt', optionflags=flags), doctest.DocFileSuite('format.txt', optionflags=flags), + doctest.DocFileSuite('namespace.txt', optionflags=flags), doctest.DocFileSuite('property.txt', optionflags=flags), doctest.DocFileSuite('jeep.txt', optionflags=flags), doctest.DocFileSuite('randomname.txt', optionflags=flags),