RestrictedPython
Compiles a restricted subset of Python so you can define a trusted boundary for running untrusted code.
Repository Health
Technical Analysis
RestrictedPython parses Python source with the standard ast module, walks the tree with a RestrictingNodeTransformer that rejects or rewrites disallowed constructs, and recompiles the sanitized AST into a real CPython code object via compile_restricted. It does not sandbox by itself: the caller supplies the runtime globals (safe_builtins, safe_globals) and guard hooks (_getattr_, _getiter_, _write_) that decide what the compiled code can actually touch when it runs.
It is CPython-only by design and has shipped since 2002 as part of the Zope/Plone stack, where it still backs the ability to let non-developers author page templates and scripts without granting them arbitrary code execution. The project is explicit that it defines a trusted-execution boundary, not a full sandbox, and documents the security tradeoffs (e.g. of supplying your own __import__) rather than hiding them.
What You Get
compile_restricted/compile_restricted_exec/compile_restricted_eval/compile_restricted_single/compile_restricted_function— drop-in replacements forcompile()that apply the restriction policy firstRestrictingNodeTransformer— the pluggable AST policy that rejects forbidden syntax (e.g.import, direct attribute access, unguarded iteration) at compile timesafe_builtins/safe_globals— a pre-built minimal builtins dict safe to hand toexec()/eval()as the execution globalssafer_getattr/guarded_setattr/guarded_delattr/guarded_iter_unpack_sequence— guard functions that route attribute and sequence access through explicit security checksPrintCollector— a guarded stand-in forprint()that captures output instead of writing to stdout directly
Common Use Cases
- Letting end users write custom scripting/templating logic (page templates, formula fields, workflow rules) inside a SaaS or CMS product without giving them arbitrary code execution
- Building a plugin or extension system where third-party snippets run in-process but under a defined capability boundary
- Grading or evaluating user-submitted code (e.g. coding exercises, notebooks) where the harness needs to block filesystem/network/import access
- Embedding a restricted expression or rule-evaluation language inside a larger trusted Python application (its original use case in Zope/Plone)
Under The Hood
Architecture
The library is organized around an AST transformation pipeline: source code is parsed with Python’s ast module in compile.py’s _compile_restricted_mode, then walked by a RestrictingNodeTransformer (transformer.py, ~1300 lines) that rewrites or rejects nodes to enforce the restricted subset, and the resulting sanitized AST is recompiled into a genuine CPython code object. Execution then runs inside caller-supplied globals (Guards.py’s safe_builtins/safe_globals) that provide guarded wrappers (guarded_setattr/delattr, safer_getattr, guarded_iter_unpack_sequence) so attribute and item access go through explicit checks rather than trusting the object model directly. The compile-time policy and the runtime guard set are independently pluggable — swapping the policy argument to compile_restricted changes what compiles at all, while swapping the globals dict changes what compiled code can do — making the AST node-visitor contract in RestrictingNodeTransformer the core abstraction that everything else depends on.
Tech Stack
Pure Python 3.10-3.15, CPython-only by explicit design (the README and runtime warn that PyPy and other implementations cannot honor the restrictions). It has no runtime third-party dependencies; the only dependencies are dev/test extras declared in pyproject.toml (pytest, pytest-mock for testing; Sphinx + furo for docs; mypy for typechecking in strict mode). The project is built with setuptools via a PEP 517 backend and orchestrated with tox across per-version environments plus docs/coverage/release-check environments, run through a GitHub Actions matrix spanning Ubuntu and Windows that is itself generated from the shared zope.meta template.
Code Quality
Testing is extensive: a tests/ package plus a nested tests/transformer/ directory with over 30 files, each targeting one AST node category (test_lambda.py, test_call.py, test_import.py, test_fstring.py, and similar), run under pytest with pytest-mock. Coverage is enforced at fail_under = 100 in pyproject.toml, with a dedicated coverage tox environment that combines results across every supported Python version. Type annotations run throughout the source (from __future__ import annotations, typing.NamedTuple/TypeAlias), and mypy runs in strict mode per [tool.mypy], with narrow, explicit overrides where full strictness isn’t practical. Errors are surfaced explicitly — SyntaxError/TypeError raised with templated messages — rather than swallowed, and CI runs pre-commit plus the full tox matrix on every push, pull request, and a weekly schedule.
API Design
The public surface is deliberately small and import-only: five compile_restricted_* functions plus safe_globals/safe_builtins/utility_builtins/limited_builtins, all re-exported from the package root, so a caller typically needs only from RestrictedPython import compile_restricted, safe_globals plus a call to the builtin exec()/eval() to get started, with no scaffolding or config files required. Documentation is substantial for a security-focused library — a dedicated ReadTheDocs site, a growing “Security considerations” page, and README examples showing both an allowed and a blocked call — and the guard functions are named for their behavior (safer_getattr, guarded_setattr), which keeps the security model legible without reading the transformer internals. The main cost to developer experience is inherent to the problem domain: correct usage requires supplying your own _getattr_/_getiter_/import policy, and the project is upfront that it is not a sandbox on its own, which raises the learning curve for first-time users even as it prevents false confidence.