Sandbox & Code Execution
Plug-in code runs inside a RestrictedPython sandbox. The sandbox restricts access to dangerous builtins, rewrites attribute/item access at compile time, and enforces naming conventions. Understanding these restrictions is important for writing working plug-in code.
How It Works
The framework compiles all plug-in source files through RestrictedPython before execution. This process:
Parses the source code into an AST (Abstract Syntax Tree).
Walks the AST with a
RestrictionMutatorthat rewrites or rejects dangerous constructs.Compiles the mutated AST into bytecode.
Executes the bytecode in a curated environment dict that contains only approved globals (e.g.
HTTP,JSON,Log,Dict,ObjectContainer).
Files with the .pys extension (service code) and .pym extension (model
definitions) are compiled through the same pipeline. The standard .py
extension is used for the main Code/__init__.py entry point, which is
also restricted.
File Types
Extension |
Policy |
Description |
|---|---|---|
|
StandardPolicy / ElevatedPolicy |
Plug-in code ( |
|
ServicePolicy |
Service code ( |
|
ModelPolicy |
Model template files. Always compiled under ModelPolicy. |
Naming Restrictions
RestrictedPython enforces strict naming rules at compile time:
Variables and attributes starting with
_are forbidden. Any variable name, function name, or attribute access that starts with an underscore will cause a compile error — for example,_my_var,self._data,obj.__dict__are all illegal.The bare name
_is allowed (e.g._ = some_value).Names ending with
__roles__are forbidden.The name
printedis reserved by the sandbox for print capture.
Allowed dunder names (Standard / Cloud policy):
__init__, __call__, __str__, __repr__
Additional dunder names (Elevated policy only):
__getitem__, __getattr__, __getattribute__, __setitem__, __setattr__,
__delitem__, __delattr__, __iter__, __len__, __name__,
__eq__, __ne__, __enter__, __exit__
This means that under the Standard policy, you cannot define custom
__getitem__, __iter__, __len__, __eq__, etc. on your classes.
Elevated policy allows these.
Available Builtins
The sandbox provides a limited subset of Python 2.7 builtins. The following tables list what is available and what is blocked.
Builtins from RestrictedPython safe_builtins
These Python builtins are available in all policies:
False, None, True, abs, basestring, bool, callable, chr, cmp, complex,
divmod, float, hash, hex, id, int, isinstance, issubclass, len, long,
oct, ord, pow, range, repr, round, str, tuple, unichr, unicode,
xrange, zip
All standard exception classes (Exception, ValueError,
TypeError, etc.) are also available.
Additional builtins added by the sandbox
The sandbox extends safe_builtins with these names:
Name |
Description |
|---|---|
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
Python |
|
|
|
|
|
Framework base object class. |
|
Framework exception class. |
Blocked Builtins
These standard Python 2.7 builtins are not available in the sandbox:
Blocked Builtin |
Reason |
|---|---|
|
Not included in |
|
Not included in |
|
Not included in |
|
Not included in |
|
Not included in |
|
Not included in |
|
Direct I/O not permitted. |
|
Direct I/O not permitted. |
|
Direct I/O not permitted. |
|
Direct I/O not permitted. |
|
Direct I/O not permitted. |
|
Can produce new code objects. |
|
Code execution not permitted. |
|
Uncontrolled namespace access. |
|
Uncontrolled namespace access. |
|
Uncontrolled namespace access. |
|
Introspection not available (available in Elevated policy). |
|
Not available (available in Elevated policy). |
|
Not available (available in Elevated policy). |
|
Not available (available in Elevated policy). |
|
Not available (available in Elevated policy). |
|
Not available (available in Elevated policy). |
|
Low-level, not available. |
|
Module manipulation not permitted. |
|
Low-level, not available. |
|
Esoteric, not available. |
Note
Functions like any(), all(), sum(), iter(), and
frozenset are legitimately useful but are simply not included in the
sandbox. You can work around some of these — for example, use a for
loop with a flag instead of any(), or use set instead of
frozenset.
Statement Restrictions
RestrictedPython blocks or rewrites several Python statements at compile time:
Statement |
Restriction |
|---|---|
|
Blocked entirely — causes a compile error. |
|
Blocked entirely — generators cannot be used. |
|
Blocked — causes a compile error. |
|
Blocked — causes a compile error. |
|
Allowed, but only |
AST Rewrites
RestrictedPython rewrites several constructs at compile time to route them through guard functions. This is mostly transparent to plug-in code but explains some unusual error messages:
Source Code |
Rewritten To |
Guard |
|---|---|---|
|
|
Attribute access proxy |
|
|
Item access proxy |
|
|
Write guard |
|
|
Write guard |
|
|
Write guard |
|
|
Iterator guard |
List/generator comprehensions |
|
Iterator guard |
|
|
Print redirect |
|
|
Apply guard |
Note
In the Plex sandbox, the write guard (_write_) is a passthrough
(lambda x: x), so attribute/item assignment is not actually guarded
at runtime — the compile-time restrictions are the primary enforcement.
Note
On Windows, print output is silently discarded. On other platforms,
it is written to stdout. Use Log.Debug() etc. for reliable logging.
Import System
The sandbox provides a custom __import__ that works as follows:
Looks for a file matching
<name>.<policy_ext>(e.g.mymodule.py,mymodule.pys,mymodule.pym) in the plug-in’s code path and any custom paths.If found, compiles it through
compile_restricted()as aRestrictedModule— subject to the same sandbox restrictions.If not found, falls back to Python’s standard
__import__.
The default module whitelist (modules that can be imported from Python’s standard library):
re, string, datetime, time
Under the Elevated policy, plug-ins can extend this whitelist via the
PlexPluginModuleWhitelist key in Info.plist.
Under the Cloud policy, only RestrictedModule imports and the
whitelisted modules above are permitted — all other standard library imports
are blocked.
Policy System
Security policies control what APIs and operations are available in different
contexts. For plug-in code, the active policy is determined by the
PlexPluginCodePolicy key in Info.plist. Services and
model files always use their dedicated policies regardless of this setting.
Policy |
Description |
|---|---|
Standard |
Default for most plug-ins. General-purpose API access with full
RestrictedPython restrictions. Only |
Elevated |
Relaxed restrictions. Grants access to additional built-ins
( |
ServicePolicy |
Applied to service code ( |
ModelPolicy |
Applied to model template files ( |
CloudPolicy |
Most restrictive bundle policy. Blocks non-whitelisted imports, isolates cookies, disables HTTP caching and global HTTP auth. |
Elevated Policy Details
When PlexPluginCodePolicy = Elevated is set in Info.plist, the
following additional privileges are granted:
Privilege |
Description |
|---|---|
Additional builtins |
|
Extended dunder names |
|
Import whitelist extension |
The plug-in can specify additional allowed module names via the
|
Bundled native libraries |
The plug-in can ship native |
Summary Table
Restriction |
Standard |
Elevated |
Cloud |
|---|---|---|---|
|
Yes (4 safe names) |
Partial (18 safe names) |
Yes (4 safe names) |
|
Blocked |
Blocked |
Blocked |
|
Blocked |
Blocked |
Blocked |
|
Not available |
Available |
Not available |
|
Not available |
Not available |
Not available |
|
Not available |
Not available |
Not available |
|
Not available |
Not available |
Not available |
Only |
Yes |
Yes |
Yes |
Augmented assign on attrs/items |
Blocked |
Blocked |
Blocked |
Standard library imports |
Allowed |
Allowed + custom whitelist |
Blocked (whitelist only) |
Bundled native libraries |
No |
Yes |
No |