🐍 Python Course · Stage 3 · Lesson 36 of 89 · sys — System Params
Course Overview →

🔵 Read Beginner and Intermediate tabs. The Expert tab has the internals — worth reading once you finish Stage 3.

← itertools — Iterators asyncio — Async I/O →
thecodex.expert · The Codex Family of Knowledge
Python stdlib

Python sys — System-specific Parameters and Functions module

argv, stdout, sys.path, sys.modules — direct access to the Python interpreter's own internals.

Python 3.13 Standard Library Last verified:
Canonical Definition

The Python sys module provides access to variables and functions used by the Python interpreter itself — command-line arguments, standard streams, the module import system, recursion limits, and interpreter version information.

🟩 Beginner

Accessing the Python interpreter from your code

What you'll learn: sys.argv for command-line arguments, sys.exit() for clean program termination, and sys.path (the module search path).

How to read this tab: Create a script that prints sys.argv, then run it with: python script.py hello world 42. See what sys.argv contains.

⏱ 20 min 📄 3 sections 🔶 Prerequisite: Modules & Packages
One sentence

The sys module is your window into the Python interpreter itself — it lets you read command-line arguments, control stdin/stdout/stderr, inspect the import system, and exit the program.

💡

sys.argv is always a list of strings. Even if you pass 42 as an argument, sys.argv contains "42" (a string). Convert explicitly: int(sys.argv[1]). For production CLIs with many arguments, use the argparse module instead of parsing sys.argv manually.

Command-line arguments

Pythonsys_argv.py
import sys

# sys.argv[0] is the script name
# sys.argv[1:] are the arguments passed
# python3 script.py hello world --verbose

print(sys.argv)        # ['script.py', 'hello', 'world', '--verbose']
print(sys.argv[0])     # script.py
print(sys.argv[1:])    # ['hello', 'world', '--verbose']

# Simple argument parsing
if len(sys.argv) < 2:
    print(f"Usage: {sys.argv[0]} <name>", file=sys.stderr)
    sys.exit(1)   # exit with error code

name = sys.argv[1]
print(f"Hello, {name}!")
# For complex argument parsing, use argparse (stdlib) instead
💡

Print to stderr for error messages. print("Error: something went wrong", file=sys.stderr). This keeps error output separate from normal output — important for scripts used in pipelines where stdout is redirected to a file.

stdin, stdout, stderr

Pythonsys_streams.py
import sys

# Write to stdout (same as print but explicit)
sys.stdout.write("Hello\n")

# Write to stderr — for error messages and diagnostics
sys.stderr.write("Error: something went wrong\n")
print("Error:", exc_info, file=sys.stderr)   # print to stderr

# Read from stdin
line = sys.stdin.readline()
for line in sys.stdin:    # iterate all lines
    process(line.strip())

# Redirect stdout
import io
buffer = io.StringIO()
sys.stdout = buffer
print("captured")
sys.stdout = sys.__stdout__   # restore
output = buffer.getvalue()    # "captured\n"
📎

Useful for cross-version compatibility. sys.version gives the full version string. sys.version_info gives a named tuple (major, minor, micro). Check: if sys.version_info < (3, 9): raise RuntimeError("Python 3.9+ required").

Python interpreter information

Pythonsys_info.py
import sys

print(sys.version)         # '3.13.0 (main, Oct 2024, ...)' — full version string
print(sys.version_info)    # sys.version_info(major=3, minor=13, micro=0, ...)
print(sys.platform)        # 'linux', 'darwin', 'win32'
print(sys.executable)      # '/usr/bin/python3' — path to interpreter
print(sys.prefix)          # installation directory

# sys.path — where Python looks for modules to import
print(sys.path)
sys.path.insert(0, "/my/custom/modules")   # add custom path

# Check Python version
if sys.version_info < (3, 10):
    raise RuntimeError("Python 3.10+ required")

✅ Beginner tab complete — check your understanding

  • I know sys.argv[0] is the script name; sys.argv[1:] are the arguments
  • I can use sys.exit(0) for success and sys.exit(1) for failure
  • I know sys.path is the list of directories Python searches for modules

Continue to asyncio →

🔵 Intermediate

stdin, stdout, stderr, and sys.modules

What you'll learn: The three standard streams (stdin, stdout, stderr) and how to redirect them. sys.modules as the module cache. Recursion limits.

How to read this tab: Try redirecting stdout to a file: sys.stdout = open("output.txt", "w") then print() goes to the file. Remember to restore it: sys.stdout = sys.__stdout__.

⏱ 25 min 📄 2 sections 🔶 Prerequisite: Beginner tab
📎

sys.modules is the import cache. When you import a module, Python checks sys.modules first. If it's there, it reuses the cached module object — doesn't re-execute the file. You can check if something is imported: "json" in sys.modules. You can even force a reimport by deleting from sys.modules (rare but sometimes useful in tests).

The import system and sys.modules

sys.modules is a dictionary mapping module names to already-imported module objects. When you import foo, Python checks sys.modules first — if it's there, it returns the cached module. This is why importing the same module twice is cheap. You can inspect it, add mock modules, or even remove a module to force reimport.

Pythonsys_modules.py
import sys
import json

# Already imported
print("json" in sys.modules)   # True
print(sys.modules["json"] is json)   # True — same object

# Force reimport (rarely needed)
del sys.modules["json"]
import json   # fresh import

# Mock a module for testing
import types
mock_requests = types.ModuleType("requests")
mock_requests.get = lambda url: type("Response", (), {"status_code": 200})()
sys.modules["requests"] = mock_requests
import requests   # returns our mock

Hitting the recursion limit gives you RecursionError. The default is 1000 levels deep. If your recursive algorithm legitimately needs more (large trees, deep graphs), increase it with sys.setrecursionlimit(5000). But first ask: can I rewrite this iteratively? Deep recursion in Python is slower than iteration and risks stack overflow.

sys.getrecursionlimit and sys.setrecursionlimit

Pythonsys_recursion.py
import sys

# Default is 1000
print(sys.getrecursionlimit())   # 1000

# Increase if needed (with care)
sys.setrecursionlimit(10000)

# Check recursion depth
def depth_check():
    frame = sys._getframe()
    d = 0
    while frame:
        frame = frame.f_back
        d += 1
    return d

# sys.getsizeof — memory usage of an object
import sys
print(sys.getsizeof([]))           # 56 bytes (empty list)
print(sys.getsizeof([1,2,3]))      # 88 bytes (3 ints + list overhead)
print(sys.getsizeof("hello"))      # 54 bytes
# Note: getsizeof does NOT include objects referenced by the object
Commonly confused
sys.exit() raises SystemExit, it does not immediately terminate. sys.exit() raises a SystemExit exception. This means finally blocks and atexit handlers still run. If caught by a bare except:, the program will not exit. Use os._exit() only if you need immediate termination without cleanup.
sys.getsizeof() does not measure total memory of nested objects. It returns only the memory used by the object itself, not by any objects it references. A list of 1000 strings: sys.getsizeof(lst) gives the list's own memory (pointers). The strings' memory is not included. Use tracemalloc for full memory profiling.
sys.path manipulation is permanent for the current process. Modifying sys.path affects all subsequent imports in the current Python session. For repeatable setups, use virtual environments and PYTHONPATH instead.
How this connects

✅ Intermediate tab complete — check your understanding

  • I know the difference between stdout (normal output) and stderr (error output)
  • I can check if a module is already imported with "name" in sys.modules
  • I know sys.getrecursionlimit() and can increase it with sys.setrecursionlimit()

Continue to asyncio →

🔴 Expert

CPython frame objects and audit hooks

What you'll learn: sys._getframe() for inspecting the call stack. sys.audit hooks (Python 3.8+) for security monitoring. CPython implementation details.

How to read this tab: Read when building debugging tools, security auditing systems, or needing to inspect the call stack dynamically.

⏱ 15 min 📄 2 sections 🔶 Prerequisite: After Stage 3
📎

Frame objects represent function calls. Each function call creates a frame on the call stack. sys._getframe() gives you the current frame, frame.f_back gives the caller, and so on up the stack. This is how debuggers, profilers, and logging libraries like logging know where they were called from.

CPython frame objects and the frame stack

sys._getframe(depth) returns the call frame at the given depth. A frame object contains: f_code (the code object), f_locals (local variables dict), f_globals (global variables dict), f_back (the calling frame), f_lineno (current line). The leading underscore signals this is a CPython implementation detail — not guaranteed to exist in other Python implementations (PyPy, Jython). This is the mechanism behind tracebacks, debuggers (pdb), and profilers (cProfile).

sys.audit hooks (Python 3.8+)

PEP 578 added sys.audit(event, *args) and sys.addaudithook(hook) — a security mechanism for monitoring or restricting potentially dangerous operations. When sys.audit("open", file, mode) is called internally by CPython (on file open, exec, import, etc.), all registered audit hooks are called. This enables security frameworks to monitor or veto operations. Audit hooks cannot be removed once added — only new hooks can be added. This is used by the CPython restricted execution proposals.

✅ Expert tab complete

  • I know sys._getframe() returns the current execution frame object
  • I understand what sys.addaudithook() does and when it's used

Continue to asyncio →

Sources

1
Python Standard Library — sys. docs.python.org/3/library/sys.html.
2
PEP 578 — Python Runtime Audit Hooks. peps.python.org/pep-0578/.
3
CPython source — Python/ceval.c. github.com/python/cpython.
Source confidence: High Last verified: Primary source: docs.python.org/3/library/sys.html

Sources

1
Python Standard Library — sys. docs.python.org/3/library/sys.html.
2
PEP 578 — Runtime Audit Hooks. peps.python.org/pep-0578/.