Skip to main content

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

Metadata

Release files for neval 0.0.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for neval 0.0.6
File Size Uploaded
neval-0.0.6.tar.gz 11.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for neval 0.0.6
File Interpreter ABI Platform
neval-0.0.6-py3-none-any.whl Python 3 none any Details

Total release size: 19.3 kB

Release files / neval-0.0.6.tar.gz

Download URL neval-0.0.6.tar.gz
Size 11.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6a294104f0f577828d1e2e02a56ce2f781861eae48f700359516178a3a751af9
BLAKE2b-256 checksum
How to use checksums
4c7725bd298b33b5e7995b0b4382fca878cfea316ff6fbccf66a8b29039f844a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.9.19

Release files / neval-0.0.6-py3-none-any.whl

Download URL neval-0.0.6-py3-none-any.whl
Size 7.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
645fd3735b02b8546e57e71323503d036ae4d14439578febfa6d3c93bb227f5b
BLAKE2b-256 checksum
How to use checksums
f0bfb240e2ffa921554ae9cc89069b056a46f1418aa94320100cbe956dacab5b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.9.19

Release history Release notifications | RSS feed

This release

0.0.6 This release

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page