Python’s sys module exposes the interpreter’s runtime state. import sys. Arguments: sys.argv → list[str] — argv[0] is the script name. Exit: sys.exit(code=0) — raises SystemExit(code); code=0 is success; non-zero is error; a string prints to stderr and exits 1. Streams: sys.stdin, sys.stdout, sys.stderr — file-like objects; reassign to redirect I/O. Path: sys.path — module search directories; sys.path.insert(0, dir) to prepend. Modules: sys.modules — cache dict {name: module}; del sys.modules["mod"] forces reimport. Version: sys.version_info — (major, minor, micro, releaselevel, serial) named tuple; check: sys.version_info >= (3, 11). Platform: sys.platform — "linux", "darwin", "win32". Executable: sys.executable — path to the running Python binary. Sizes: sys.getsizeof(obj) → bytes (shallow size); sys.maxsize — max int for a platform pointer. Byte order: sys.byteorder — "little" or "big". Recursion: sys.getrecursionlimit() / sys.setrecursionlimit(n). Exception: sys.exc_info() → (type, value, traceback) — only meaningful inside an except block; prefer traceback module for formatting. Trace hooks: sys.settrace(fn) / sys.setprofile(fn) — per-frame debugging and profiling. Claude Code generates CLI argument processors, stream redirectors, import hooks, version guards, and memory profilers.
CLAUDE.md for sys
## sys Stack
- Stdlib: import sys
- Args: sys.argv[1:] # command-line arguments
- Exit: sys.exit(0) # clean exit; sys.exit("error msg") → code 1
- Path: sys.path.insert(0, d) # prepend to module search path
- Mods: sys.modules["name"] # cached module; del to force reimport
- Version: sys.version_info >= (3, 11)
- Size: sys.getsizeof(obj) # shallow bytes
- IO: sys.stdout = open("log.txt", "w") # redirect output
sys Interpreter State Pipeline
# app/sysutil.py — argv, streams, path, modules, version, tracing
from __future__ import annotations
import io
import sys
import traceback
from contextlib import contextmanager
from dataclasses import dataclass
from typing import Generator, TextIO
# ─────────────────────────────────────────────────────────────────────────────
# 1. argv helpers
# ─────────────────────────────────────────────────────────────────────────────
def script_name() -> str:
"""
Return the name of the running script (argv[0] basename).
Example:
print(f"Usage: {script_name()} <input> <output>")
"""
import os
return os.path.basename(sys.argv[0]) if sys.argv else "<stdin>"
def argv_flags() -> dict[str, "str | bool"]:
"""
Parse --key=value and --flag arguments from sys.argv[1:].
Returns {key: value_str} for --key=value, {key: True} for --flag.
Example:
flags = argv_flags()
verbose = flags.get("verbose", False)
output = flags.get("output", "out.txt")
"""
result: dict[str, "str | bool"] = {}
for arg in sys.argv[1:]:
if arg.startswith("--"):
if "=" in arg:
key, val = arg[2:].split("=", 1)
result[key] = val
else:
result[arg[2:]] = True
return result
def require_args(n: int, usage: str = "") -> list[str]:
"""
Assert that at least n positional arguments were given, or exit with usage.
Example:
src, dest = require_args(2, "src dest")
# exits with usage if fewer than 2 args
"""
args = sys.argv[1:]
if len(args) < n:
prog = script_name()
sys.exit(f"Usage: {prog} {usage}")
return args
# ─────────────────────────────────────────────────────────────────────────────
# 2. Stream redirect context managers
# ─────────────────────────────────────────────────────────────────────────────
@contextmanager
def capture_stdout() -> Generator[io.StringIO, None, None]:
"""
Context manager: capture sys.stdout into a StringIO buffer.
Yields the buffer; restores stdout on exit.
Example:
with capture_stdout() as buf:
print("captured!")
assert "captured" in buf.getvalue()
"""
buf = io.StringIO()
old = sys.stdout
sys.stdout = buf
try:
yield buf
finally:
sys.stdout = old
@contextmanager
def capture_stderr() -> Generator[io.StringIO, None, None]:
"""Capture sys.stderr into a StringIO buffer."""
buf = io.StringIO()
old = sys.stderr
sys.stderr = buf
try:
yield buf
finally:
sys.stderr = old
@contextmanager
def redirect_stdout(stream: TextIO) -> Generator[None, None, None]:
"""
Redirect sys.stdout to stream for the duration of the block.
Example:
with open("log.txt", "w") as f, redirect_stdout(f):
print("goes to file")
"""
old = sys.stdout
sys.stdout = stream
try:
yield
finally:
sys.stdout = old
@contextmanager
def suppress_output() -> Generator[None, None, None]:
"""Discard all stdout and stderr output in the block."""
null = open(io.os.devnull, "w") if hasattr(io, "os") else io.StringIO()
dev_null = io.StringIO()
old_out, old_err = sys.stdout, sys.stderr
sys.stdout = dev_null
sys.stderr = dev_null
try:
yield
finally:
sys.stdout = old_out
sys.stderr = old_err
# ─────────────────────────────────────────────────────────────────────────────
# 3. Module cache utilities
# ─────────────────────────────────────────────────────────────────────────────
def force_reimport(module_name: str) -> object:
"""
Remove a module from sys.modules and reimport it.
Useful in long-running processes that hot-reload code.
Example:
force_reimport("myapp.config")
import myapp.config # gets the fresh version
"""
import importlib
sys.modules.pop(module_name, None)
return importlib.import_module(module_name)
def is_imported(module_name: str) -> bool:
"""Return True if a module has been imported (is in sys.modules)."""
return module_name in sys.modules
def list_submodules(package: str) -> list[str]:
"""
Return names of all loaded modules that belong to package.
Example:
list_submodules("xml") # ["xml", "xml.etree", "xml.etree.ElementTree", ...]
"""
prefix = package + "."
return [
name for name in sys.modules
if name == package or name.startswith(prefix)
]
# ─────────────────────────────────────────────────────────────────────────────
# 4. Version and platform guards
# ─────────────────────────────────────────────────────────────────────────────
def require_python(major: int, minor: int = 0) -> None:
"""
Exit with a message if the Python version is older than required.
Example:
require_python(3, 11)
"""
if sys.version_info < (major, minor):
sys.exit(
f"Python {major}.{minor}+ required; "
f"running {sys.version_info.major}.{sys.version_info.minor}"
)
def python_info() -> dict[str, object]:
"""
Return a dict with Python version, platform, executable, and byte order.
Example:
info = python_info()
print(info["version"])
"""
return {
"version": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
"platform": sys.platform,
"executable": sys.executable,
"byteorder": sys.byteorder,
"maxsize": sys.maxsize,
"encoding": sys.getdefaultencoding(),
"fs_encoding": sys.getfilesystemencoding(),
}
# ─────────────────────────────────────────────────────────────────────────────
# 5. Memory sizing helpers
# ─────────────────────────────────────────────────────────────────────────────
def shallow_size(obj: object) -> int:
"""Return sys.getsizeof(obj) in bytes (shallow — does not recurse)."""
return sys.getsizeof(obj)
def deep_size(obj: object, seen: "set | None" = None) -> int:
"""
Recursively estimate total memory of obj and its referents.
Handles cycles via a seen-set.
Example:
data = {"a": list(range(1000)), "b": "hello"}
print(f"{deep_size(data):,} bytes")
"""
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
seen.add(obj_id)
size = sys.getsizeof(obj)
if isinstance(obj, dict):
size += sum(deep_size(k, seen) + deep_size(v, seen) for k, v in obj.items())
elif isinstance(obj, (list, tuple, set, frozenset)):
size += sum(deep_size(item, seen) for item in obj)
elif hasattr(obj, "__dict__"):
size += deep_size(obj.__dict__, seen)
return size
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== sys demo ===")
# ── python_info ───────────────────────────────────────────────────────────
print("\n--- python_info ---")
for k, v in python_info().items():
print(f" {k}: {v}")
# ── capture_stdout ────────────────────────────────────────────────────────
print("\n--- capture_stdout ---")
with capture_stdout() as buf:
print("hello from captured stdout")
print("line two")
output = buf.getvalue()
print(f" captured {len(output)} chars: {output.strip()!r}")
# ── capture_stderr ────────────────────────────────────────────────────────
print("\n--- capture_stderr ---")
with capture_stderr() as ebuf:
import warnings
warnings.warn("test warning", UserWarning, stacklevel=1)
err = ebuf.getvalue()
print(f" stderr had content: {bool(err)}")
# ── is_imported / list_submodules ─────────────────────────────────────────
print("\n--- sys.modules helpers ---")
print(f" is_imported('json'): {is_imported('json')}")
import json
print(f" is_imported('json'): {is_imported('json')}")
print(f" xml submodules: {list_submodules('xml')[:4]}")
# ── shallow_size / deep_size ──────────────────────────────────────────────
print("\n--- memory sizing ---")
data = list(range(100))
print(f" shallow list[100]: {shallow_size(data)} bytes")
print(f" deep list[100]: {deep_size(data)} bytes")
d = {"key": [1, 2, 3], "msg": "hello world"}
print(f" deep dict: {deep_size(d)} bytes")
# ── argv_flags simulation ─────────────────────────────────────────────────
print("\n--- argv_flags ---")
orig = sys.argv[:]
sys.argv = ["demo.py", "--verbose", "--output=result.txt", "--count=5"]
flags = argv_flags()
print(f" flags: {flags}")
sys.argv = orig
# ── recursion limit ───────────────────────────────────────────────────────
print("\n--- recursion limit ---")
print(f" getrecursionlimit(): {sys.getrecursionlimit()}")
print("\n=== done ===")
For the os module alternative — os.environ, os.getpid(), os.getcwd(), os.path — handle OS-level process state, environment variables, and filesystem operations; sys handles Python interpreter state (argv, streams, path, modules, version); use os for process/filesystem concerns and sys for interpreter concerns — they are complementary, not alternatives. For the argparse / click alternatives — argparse.ArgumentParser and click turn sys.argv into structured, validated, documented options and subcommands — use argparse or click for any non-trivial CLI; use sys.argv directly only for the simplest single-argument scripts or when implementing your own argument parsing layer. The Claude Skills 360 bundle includes sys skill sets covering script_name()/argv_flags()/require_args() argv utilities, capture_stdout()/capture_stderr()/redirect_stdout()/suppress_output() stream redirectors, force_reimport()/is_imported()/list_submodules() module cache helpers, require_python()/python_info() version and platform guards, and shallow_size()/deep_size() memory sizing utilities. Start with the free tier to try interpreter state patterns and sys pipeline code generation.