Skip to main content

Execute raw Python code in an isolated namespace

Project description

NEval — Namespaced Code Evaluator

This package provides an alternative scoping mechanism to the standard exec and eval functions for running raw Python code. The goal is to overcome the following behavior of exec, as described in the docs:

If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.

For instance, when you want to use locals as the mutable scope and globals as additional lookup, the default exec and eval procedures result in unexpected behavior. For example:

# ✓ [1]
exec("a=[1]; print([i for i in a])", {}, {})

# ✗ NameError: name 'a' is not defined
exec("a=[1]; print([i for i in a if a])", {}, {})

# ✓ os
exec("import os; print(os.__name__)", {}, {})
 
# ✗ NameError: name 'os' is not defined
exec("import os; (lambda: print(os.__name__))()", {}, {})

NEval's alternative evaluation

The main difference is that neval has two arguments called namespace and namespace_readonly instead of locals and globals. These are similar to locals and globals but with a slightly different implementation under the hood to allow for deviations from eval and exec.

Key features of neval include:

  • namespace is a dict or an object with a __dict__ attribute. This object is
    read/write in order to reflect all scope changes happening during execution. This is helpful in keeping track of pipelines with many side effects.
  • namespace_readonly is a dict or an object with a __dict__ attribute. Note that the dictionary is treated as read-only in the sense that no objects will be added, no objects will be removed, and no objects will be replaced, although objects can be accessed and mutated. A common use case to set this parameter as globals() to make use of global variables and imported modules without changing the global state.
  • neval returns the value of the last section in your code. If the last section is an expression, it will return the executed value, if its a statement (such as assignments of function declarations) it will return None.
  • If an error occurs, a full traceback is generated along with a temporary code file to facilitate the whole process.
  • For Python >= 3.11, the traceback also includes a full printout of the code by making use of the new Exception.add_note feature. The aim is to mimic the helpful error feedback of ipython.
  File "/temp/neval-057d58343544b6d102cac201bdc11527a0224e87", line 2, in <module>
    c = 4/0
        ~^~
ZeroDivisionError: division by zero
Error in neval-057d58343544b6d102cac201bdc11527a0224e87:
      1 a = 1
----> 2 b = 4/0
      3 c = 5

Examples

from neval import neval
from types import SimpleNamespace
import numpy

neval("a = 1; b = 2; c = 3; d = 4; a + b + c + d")
# ✓ 10

ns = {}
neval("a = 1; b = 2; c = 3; d = 4; a + b + c + d", ns)
# ✓ 10

ns
# ✓ {'a': 1, 'b': 2, 'c': 3, 'd': 4}

ns = SimpleNamespace()
neval("a = argmax(array([1,2,3,4,3,2,1])**2)", ns, numpy)

ns.a
# ✓ 3

ns = SimpleNamespace()
a, b, c = 1, 2, 3
neval("d = a + b*2 + c*3", ns, globals())

ns.d
# ✓ 14

d
# ✗ NameError: name 'd' is not defined

neval("d = a + b*2 + c*3", globals())

d
# ✓ 14

params = SimpleNamespace(a=1, b=2, c=3)

neval("d = a + b*2 + c*3", params)

params
# ✓ namespace(a=1, b=2, c=3, d=14)

neval("""\
a = 1 + 2
this is incorrect
b = a + 3
""")
# ✗ NameError: name 'this' is not defined
#   Error in neval-8efcb70d4b63817f9fd92f3b61eb5a7c0c45cfe9:
#         1 a = 1 + 2
#   ----> 2 this is incorrect
#         3 b = a + 3

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

neval-0.0.6.tar.gz (11.6 kB view details)

Uploaded Source

Built Distribution

neval-0.0.6-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file neval-0.0.6.tar.gz.

File metadata

  • Download URL: neval-0.0.6.tar.gz
  • Upload date:
  • Size: 11.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.9.19

File hashes

Hashes for neval-0.0.6.tar.gz
Algorithm Hash digest
SHA256 6a294104f0f577828d1e2e02a56ce2f781861eae48f700359516178a3a751af9
MD5 562877f48c645229c90f6e180e83efc2
BLAKE2b-256 4c7725bd298b33b5e7995b0b4382fca878cfea316ff6fbccf66a8b29039f844a

See more details on using hashes here.

File details

Details for the file neval-0.0.6-py3-none-any.whl.

File metadata

  • Download URL: neval-0.0.6-py3-none-any.whl
  • Upload date:
  • Size: 7.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.9.19

File hashes

Hashes for neval-0.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 645fd3735b02b8546e57e71323503d036ae4d14439578febfa6d3c93bb227f5b
MD5 758d2592ec80b063e0978588a7a91cf3
BLAKE2b-256 f0bfb240e2ffa921554ae9cc89069b056a46f1418aa94320100cbe956dacab5b

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page