==============================================
Python Execution in Special Dynamic Namespaces
==============================================

  >>> from cybertools.meta 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, 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", minimal)
  Traceback (most recent call last):
  ...
  ImportError: __import__ not found

  >>> exec("f = open('dummy.txt', 'w')", minimal)
  Traceback (most recent call last):
  ...
  NameError: open

Elements and sub-elements (children)
------------------------------------

  >>> code = """
  ... topic('zope3', title='Zope 3')[
  ...     annotation(author='jim')]
  ... topic('python')[
  ...     annotation(author='guido'),
  ...     child('zope3')]
  ... """

  >>> from cybertools.util.jeep import Jeep
  >>> from cybertools.meta.element import ElementFactory
  >>> symbols = namespace.BaseNamespace()
  >>> symbols['topic'] = ElementFactory(symbols, 'topic')
  >>> symbols['annotation'] = ElementFactory(symbols, 'annotation')
  >>> symbols['child'] = ElementFactory(symbols, 'child')

  >>> exec(code, symbols)

  >>> symbols.topic.instances
  [<Element 'zope3'>, <Element 'python'>]
  >>> zope3 = symbols.topic.instances[0]
  >>> dict(zope3.annotation)
  {'author': 'jim'}
  >>> zope3.title
  'Zope 3'


A Namespace Automatically Generating Elements
=============================================

  >>> auto = namespace.AutoNamespace()

  >>> exec("print(something)", auto)
  something

  >>> auto.something
  <AutoElement 'something'>


Execution of Python Code in a Namespace
=======================================

To simplify some standard use cases for working with namespaces the
module provides two classes for execution of Python code with appropriate
simple error handling.

Evaluation of Python expressions
--------------------------------

When evaluating an expression we always get a pair with the resulting
value and an - hopefully empty - error string.

  >>> ev = namespace.Evaluator(auto)
  >>> ev.evaluate('25 * 25')
  (625, '')

  >>> ev.evaluate('30/0')
  (None, ['Traceback...ZeroDivisionError...'])

Trying to execute a statement leads to a syntax error.
... in Python3 print is a function ...

ev.evaluate('print(something_else)')
(None, 'Traceback...SyntaxError...')

But we can explicitly allow the execution of statements. The result
of executing a statement is None.

  >>> ev = namespace.Evaluator(auto, allowExec=True)
  >>> ev.evaluate('print(something_else)')
  something_else
  (None, '')
  >>> ev.evaluate('25 * 25')
  (625, '')

Execution of Statements
-----------------------

  >>> ex = namespace.Executor(auto)
  >>> ex.execute('number = 25')
  ''

  >>> ex.execute('30/0')
  ['Traceback...ZeroDivisionError...']
