os module
The sandbox exposes a small, host-mediated subset of os. Filesystem
functions route through the same OS-call mechanism as pathlib and
open() (see pathlib.md, open.md): the host’s mount table (or os callback) decides
whether each call is permitted.
os.getenv(key, default=None)— yields to the host; the host decides which environment variables are visible (typically a curated subset, not the full host environment).os.environ— property that yields to the host and returns adictof the same curated environment. It is a plain dict, not anos._Environobject: mutating it does not propagate back to the host.os.listdir(path=None)— returns a list of entry names.os.stat(path)— returns the same 10-field stat result asPath.stat().os.mkdir(path, mode=0o777),os.makedirs(name, mode=0o777, exist_ok=False)os.remove(path),os.unlink(path),os.rmdir(path)os.rename(src, dst),os.replace(src, dst)os.urandom(size)— yields to the host, which must return exactlysizebytes; any other length, or a non-bytesvalue, raisesRuntimeError. Sandboxed code choosessize, so a host handler that allocates must cap it. Python’sAbstractOS.urandom()raisesMemoryErrorbefore allocating whensizeexceedsmax_urandom_bytes, 1 MiB by default;OSAccess(max_urandom_bytes=...)sets it, and zero rejects every nonempty request. Therandommodule makes this call only underrandom_start='call_host'; by default an unseeded generator seeds itself from OS entropy inside the sandbox (see random.md).os.fspath(path)— pure, no host involvement.os.getcwd()— pure: the sandbox’s virtual working directory.os.getcwdb()— pure: the same directory as UTF-8 bytes (virtual paths are always UTF-8, so no filesystem encoding is involved).os.chdir(path)— validated through aPath.stathost call (see below).- Constants (fixed POSIX values on every host OS, matching the sandbox’s
POSIX-only path model):
os.sep == '/',os.altsep is None,os.extsep == '.',os.curdir == '.',os.pardir == '..',os.linesep == '\n',os.name == 'posix',os.devnull == '/dev/null'.
- No file descriptors, no
bytespaths. Paths must bestrorpathlib.Path.bytespaths and integer fds (bools included, which CPython fd-converts with only aRuntimeWarning) raise the path-converterTypeErrorwith the accepted-types phrase narrowed to what Monty takes, e.g.stat: path should be string or os.PathLike, not bytes. For every other rejected type the phrase is CPython’s verbatim, soos.stat(1.5)still saysshould be string, bytes, os.PathLike or integer. Noteopen()does acceptbytespaths, decoding them as UTF-8; theosfunctions do not. The verbatimos.listdirphrase is POSIX CPython’s, which includesintegereven though Windows CPython omits it (no fd-based listdir there); the narrowed phrase forbytes,intandboolisstring, os.PathLike or None. - No
__fspath__protocol.os.fspath(and every path-taking function) accepts onlystr,bytes(fspath only), andpathlib.Path: a user-defined class implementing__fspath__raisesTypeErrorinstead of having its method called. dir_fdkeywords (dir_fd,src_dir_fd,dst_dir_fd) are parsed for signature parity, but any non-Nonevalue raises theNotImplementedErrorCPython uses on platforms without them (dir_fd unavailable on this platform). Non-int values raise the converterTypeError(argument should be integer or None, not str).os.stat(..., follow_symlinks=...)raisesNotImplementedError: stat: follow_symlinks unavailable on this platformfor any falsy value. CPython truth-tests the argument, soFalse,Noneand0all mean “lstat”, which Monty has no behavior for.os.lstatitself is not implemented.- All-keyword calls that overflow the signature are not always reported
the way CPython reports them.
os.fspath(path='a', foo=1)andos.listdir(path='.', foo=1)match (takes at most 1 keyword argument (2 given)), but functions with keyword-only slots (os.stat,os.mkdir,os.remove,os.rmdir,os.rename) report the first unknown keyword (stat() got an unexpected keyword argument 'foo') where CPython reports the arity (stat() takes at most 3 keyword arguments (4 given)). - The working directory is virtual and belongs to the session. A
session’s first feed sets it (an explicit
cwd, else that feed’s first mount’s virtual path, else/); it then persists across feeds, including anyos.chdir(), until a feed passescwdagain.os.getcwd()reports it and relative paths are resolved against it inside the interpreter, so a mount oroscallback only ever sees absolute paths. Host errors therefore name the resolved path (open('missing')raises[Errno 2] No such file or directory: '/data/missing') where CPython names the argument as written. Absolute paths reach mounts as written. Joining preserves.and..so mounts can validate NUL bytes and path limits before normalization. Python and JavaScriptoscallbacks receive lexically normalized paths, including both rename arguments. The interpreter rejects NUL bytes before dispatch, even in components cancelled by... Existence predicates returnFalsefor these paths; other operations raiseValueError. Length and depth limits are mount policy: a feed with any mount applies them to every path before the callback sees it, including paths no mount covers, while a feed with no mounts passes paths of any length to the callback. os.chdir(path)suspends asPath.staton the resolved target: hosts cannot observe a directory change, and without a mount oroshandler it raisesPermissionError. The interpreter raisesNotADirectoryErrorwhen the reply is not a directory, naming the argument as written like CPython;FileNotFoundErrorcomes from the host and names the resolved path.os.chdir('')raisesFileNotFoundErrorwithout consulting the host. Only after the host accepts the target is the stored directory lexically normalized (..collapses without consulting symlinks). Integer file descriptors are refused with thepath_tTypeError; CPython wouldfchdir. A Rust host that answers the stat with a future getsRuntimeErrorinstead of a silently unchanged directory.modearguments are type-checked ('str' object cannot be interpreted as an integer) but otherwise ignored: Monty’s filesystem backends do not model POSIX permission bits.os.replaceis an alias ofos.renameat the host boundary: both suspend with the same rename OS call, so overwrite semantics are whatever the host backend does (POSIX rename overwrites; a Windows host may refuse). CPython’sos.replaceguarantees overwrite on all platforms.- Hosts see pathlib-style call names.
os.listdirsuspends asPath.iterdir(the interpreter reduces the returned paths to names),os.statandos.chdirasPath.stat,os.remove/os.unlinkasPath.unlink,os.mkdir/os.makedirsasPath.mkdir,os.rename/os.replaceasPath.rename. A customoscallback cannot distinguish e.g.os.listdirfromPath.iterdir. os.statresults print asStatResult(...), notos.stat_result(...), and carry only the 10 core fields, same asPath.stat()(see filesystem.md).- Error side-effects differ slightly for
os.makedirs: Monty validatesmodeup front, while CPython only fails when it reaches the finalmkdir, after creating parent directories.
Everything else, including but not limited to: os.path.* (use
pathlib.Path instead), os.fchdir, os.walk, os.scandir,
os.removedirs, os.renames, os.lstat, os.access, os.symlink,
os.readlink, os.link, os.chmod, os.chown, os.umask, os.truncate,
os.utime, os.system, os.popen, os.fork, os.exec*, os.spawn*,
os.kill, os.pipe, os.read, os.write, os.open, os.close,
os.dup, os.fsync, os.cpu_count, os.getpid,
os.getuid, os.getgid, os.uname, os.terminal_size, os.get_terminal_size.
subprocess, signal, socket, threading, multiprocessing are not
importable either (see modules.md).