[Unreleased]
0.4.1 - 2026-06-20
0.4.0
New ExMonty Features
Buffered
open()builtin and file handles. Pythonopen(path, mode)(with context-managerwithsupport,read/write, anda/w/rmodes) now works against:read_writeand:overlaymounts. File objects round-trip as a new{:file_handle, %{path, mode, position}}tagged value, and three new os calls —:open,:append_text,:append_bytes— are dispatchable throughSandboxos:handler maps.PseudoFSdoes not yet service:open(returnsNotImplementedError); use a mount for file I/O.ExMonty.Mount— host filesystem mounts. Map virtual paths in the sandbox to real host directories with:read_only,:read_write, or:overlayaccess modes. Path canonicalisation, boundary checks, and symlink-escape detection are always enforced.mounts = ExMonty.Mount.new!() |> ExMonty.Mount.add!("/data", "/var/lib/myapp/data", :read_only) |> ExMonty.Mount.add!("/scratch", "/tmp/sandbox-scratch", :overlay) ExMonty.Sandbox.run(code, mounts: mounts)Composes with the existing
:osoption — mounts handle FS calls; the:osmap handles non-FS fallbacks (:getenv,:datetime_now, etc.). Unmounted paths raisePermissionError(upstream'sOsFunction::on_no_handlersemantics) instead of ExMonty's previous generic:os_error.Mount state (overlay writes,
write_bytes_usedcounter) is cumulative on the mount object across runs. Construct a fresh mount to discard. Seeproposals/MOUNT_TABLE.mdfor the design rationale.datetimesupport.date,datetime,timedelta, andtimezonePython values round-trip via new tagged tuples:# Output direction (Python → Elixir) ExMonty.eval("from datetime import date\ndate(2026, 5, 1)") # {:ok, {:date, %{year: 2026, month: 5, day: 1}}, ""} # Input direction (Elixir → Python) ExMonty.eval("x.year", inputs: %{"x" => {:date, %{year: 2026, month: 5, day: 1}}}) # {:ok, 2026, ""} # date.today() / datetime.now() via host handlers ExMonty.Sandbox.run("from datetime import date\ndate.today()", os: %{date_today: fn _, _ -> {:ok, {:date, %{year: 2026, month: 5, day: 1}}} end})Encoded shapes:
{:date, %{year, month, day}}{:datetime, %{year, month, day, hour, minute, second, microsecond, offset_seconds, tz_name}}(offset_secondsandtz_namearenilfor naive datetimes){:timedelta, %{days, seconds, microseconds}}{:timezone, %{offset_seconds, name}}
New OS handler atoms
:date_todayand:datetime_nowsurfaced throughExMonty.Sandboxfor hosts to provide deterministic clocks.
Bug Fixes
- Sandbox handler allowlist.
ExMonty.Sandboxnow accepts:date_today/:datetime_nowkeys inos:handler maps (previously they were silently dropped).
Upstream Python Features (monty v0.0.8 → v0.0.18)
These come from upstream and don't change the ExMonty API — but they affect what Python code you can run:
open()builtin (v0.0.18).with open(...) as f:, buffered read/write, file objects. Thewithstatement works for built-in context managers likeopen(); user-defined__enter__/__exit__classes are still unsupported (no class definitions). See "Bufferedopen()" above.jsonmodule.import json; json.loads(...) / json.dumps(...).datetimemodule.date,datetime,timedelta,timezone(see above).- Multi-module imports.
import a, b, cin one statement. - Chain assignment.
a = b = c = 1. - Nested subscript assignment.
d[k][i] = v,matrix[i][j] = 0. hasattr/setattr. Work on host-provided objects (dataclasses, modules). Class definitions in Python source are still not supported.zip(..., strict=True). RaisesValueErroron length mismatch.str.expandtabs(tabsize).- Named single-kwarg calls.
f(x=1)(single-kwarg edge case fixed). INT_MAX_STR_DIGITSguard.int("1" * 5000)raisesValueError, matching CPython's quadratic-time DoS protection (default 4300 digits).- Bug fixes:
i64::MINnegation panic, source-line >65535 parse panic, partial future-resolution panic in mixed gathers, GC interval ignored in tracker, GC reference release in cycles, empty-tuple singleton counted against memory limit. - More hardening (v0.0.18): trial-deletion cycle GC, duplicate-parameter
and duplicate-coroutine panics, exception-stack corruption on
raise, further integer-op panics, comprehension generator/AST depth limits, and JSON BigInt limits — all converted from panics to proper exceptions or bounded errors.
Not Yet Exposed to Elixir
- Filesystem mounting (
MountTableAPI) — upstream supports overlay / read-only / layered filesystems. ExMonty'sPseudoFSdoes not yet wrap this. - Async in Rust — internal VM async support; no surface change for Elixir callers today.
MontyRepl/JsonMontyObject— Rust-only APIs.
Internal
- monty pinned to v0.0.18 (commit
45a3b2d5). Update procedure now tracks tagged releases by default; seeUPDATE_PROCEDURE.md. - Upstream
OsFunction→OsFunctionCallrefactor absorbed. The os-call surface now carries typed args inline; our NIF extracts the(positional, keyword)view viaOsFunctionCall::to_argsand routes mount calls through the newMountTable::handle_os_call(&call)signature. No Elixir-side API change beyond the new:open/:append_*os atoms. PrintWriter::Collectrenamed toPrintWriter::CollectStringin monty upstream — internal NIF change only, no Elixir-side impact.
0.3.0
Breaking Changes
- Removed
external_functionsoption fromcompile/2andSandbox.run/2. External functions are now auto-detected at runtime via name lookup. The:external_functionsoption is no longer accepted. ExMonty.Native.compile/4is nowcompile/3(removedexternal_fnsparameter).
New Features
- Name lookup progress tag. When Python code references an undefined name
(without calling it), execution pauses with
{:name_lookup, name, snapshot, output}. Resume with{:ok, {:function, name}}to provide a callable,{:ok, value}for a constant, or:undefinedto raiseNameError. MontyObject::Functiontype. A new tagged tuple{:function, name}(or{:function, name, docstring}) can be returned from name lookups to provide callable function objects to the Python VM.handle_name_lookup/1callback inExMonty.Sandboxbehaviour (optional). Called when the sandbox encounters an undefined name. The sandbox auto-resolves names found in the:functionsmap.
Upstream Improvements (monty v0.0.8)
remodule implementation (regex support).- Full Python
mathmodule (~50 functions). - PEP 448 generalised unpacking (
[*a, *b],{**a, **b}). - Tuple comparison operators (
<,>,<=,>=). - Dict view and set/frozenset operators.
strandbytescomparison operators.- Augmented assignment on subscript targets (
x[i] += 1). max()kwargs/default support.- Bug fixes: stack-depth tracking, loop iterator depth, variable shadowing panic,
os.environpanic in JS bindings.
0.2.1
- Fix dataclass round-trip:
ExMonty.Dataclassstructs returned from handlers are now decoded back toMontyObject::Dataclass, preserving field access (p.x), frozen semantics, and method calls. - Add
field_namesandtype_idfields toExMonty.Dataclassstruct for full fidelity with monty's dataclass representation. - Fix float inf/nan encoding:
float('inf'),float('-inf'), andfloat('nan')now encode as:infinity,:neg_infinity, and:nanatoms instead of crashing. - Float special atoms round-trip through inputs.
0.2.0
- Update monty to 47427c0 (v0.0.7).
- Support dataclass method calls (new
:method_calltag in interactive mode). - Upstream:
filter(),map(),getattr()builtins,dict(iterable), dataclass methods,asyncio.run(), user-defined function calling. - Upstream:
MontyExceptionandStackFramenow serializable. - Upstream: improved resource limit enforcement (time check rate-limiting, 4x pow safety multiplier, catchable RecursionError).
- Upstream: bug fixes for string comparison, async stack overflow, i64::MIN division overflow.
- PrintWriter refactored from trait to enum (internal change, no public API impact).
0.1.1
- Update monty to v0.0.4 (86712b6) — includes fuzz testing, small-tuple optimizations, AST depth overflow fix, and
id()memory leak fix.
0.1.0
- Initial release.
ExMonty.eval/2,compile/2,run/3for sandboxed Python execution.- Interactive pause/resume (
start/3,resume/2,resume_futures/2) for external function calls and OS calls. ExMonty.Sandboxhigh-level handler +ExMonty.PseudoFSin-memory filesystem.- Runner/snapshot serialization (
dump/1,load_runner/1,dump_snapshot/1,load_snapshot/1).