Work with System Information using Python `sys`

Learn how to use Python's sys module to access command-line arguments, manage the Python path, control recursion limits, and interact with standard I/O streams.

7 min read

The sys module in the Python standard library gives you access to Python system information and the interpreter's own runtime state. Use it to read command-line arguments, control how Python exits, inspect the module search path, write to standard error, and check the Python version at runtime.

pythonpython
import sys
 
print(sys.platform)             # darwin
print(sys.version_info[:3])  # (3, 14, 6)
print(len(sys.argv))          # 1

sys.platform identifies the operating system. sys.version_info is a named tuple with the Python version (major, minor, micro, release level, serial).

sys.argv is the list of command-line arguments; its length is 1 when no arguments are passed, because the script name is always the first element.

Reading command-line arguments

sys.argv is a list where the first item is the script name and the rest are the arguments the user passed.

pythonpython
import sys
 
if len(sys.argv) < 2:
    print("Usage: python script.py <name>")
    sys.exit(1)
 
name = sys.argv[1]
count = int(sys.argv[2]) if len(sys.argv) > 2 else 1
print(f"Hello, {name}! " * count)

If you save this as greet.py and run python greet.py Maya 3 from the terminal, sys.argv captures the script name plus both arguments, and the greeting repeats three times:

texttext
Hello, Maya! Hello, Maya! Hello, Maya!

sys.argv[0] is "greet.py". sys.argv[1] is "Maya". sys.argv[2] is "3", which we convert to an integer.

Always validate the length of sys.argv before accessing indices.

For complex argument parsing with flags, types, and help text, use argparse instead of raw sys.argv:

pythonpython
import argparse
 
parser = argparse.ArgumentParser(description="Greet someone.")
parser.add_argument("name", help="person to greet")
parser.add_argument("-c", "--count", type=int, default=1, help="number of times")
args = parser.parse_args()
 
print(f"Hello, {args.name}! " * args.count)

argparse handles validation, type conversion, help text, and error messages automatically.

Exiting a program with sys.exit()

sys.exit() terminates the Python process and optionally returns an exit code to the operating system.

pythonpython
import sys
 
def divide(a, b):
    if b == 0:
        print("Error: division by zero", file=sys.stderr)
        sys.exit(1)
    return a / b
 
result = divide(10, 0)

sys.exit(0) means success. sys.exit(1) (or any non-zero value) means failure.

The exit code is visible to shell scripts and CI pipelines that check $? after running your program.

sys.exit() raises SystemExit, which you can catch in tests or cleanup code:

pythonpython
import sys
 
try:
    sys.exit(1)
except SystemExit as e:
    print(f"Exit prevented in test. Code: {e.code}")

Do not catch SystemExit in production code unless you have a specific reason, such as running cleanup before re-raising.

Standard I/O streams

The sys module exposes three file-like objects for input and output.

pythonpython
import sys
 
sys.stdout.write("Standard output\n")
sys.stderr.write("Standard error\n")

Both lines show up in the terminal because it displays both streams together, but only stdout would be captured if you redirected the program's output to a file:

texttext
Standard output
Standard error
StreamPurpose
sys.stdinStandard input (keyboard, piped data)
sys.stdoutStandard output (terminal, redirected to file)
sys.stderrStandard error (error messages, always visible)

Use sys.stderr for error and diagnostic messages. This keeps them separate from normal output, so users can redirect stdout to a file and still see errors in the terminal.

To read all piped input at once:

pythonpython
import sys
 
if not sys.stdin.isatty():
    data = sys.stdin.read()
    print(f"Received {len(data)} characters from pipe")

sys.stdin.isatty() returns True when input comes from a terminal and False when it comes from a pipe or file redirection.

Understanding sys.path

sys.path is a list of directories Python searches when you use import. It is initialized from the PYTHONPATH environment variable, the standard library location, and the directory containing the running script.

pythonpython
import sys
 
for p in sys.path[:5]:
    print(p)

The exact entries depend on how Python was installed and where the script lives, but the general shape is always the same, starting with the current directory and then the standard library locations:

texttext
 
/usr/lib/python314.zip
/usr/lib/python3.14
/usr/lib/python3.14/lib-dynload
/Users/maya/Dev/project

The first entry (empty string) represents the current directory, which is why a script can always import sibling modules sitting next to it without any extra setup. You can add more directories to the search path at runtime:

pythonpython
import sys
 
sys.path.append("/Users/maya/my-libs")

This is occasionally useful for development, but for production code, install your modules and packages properly with pip install -e . or by setting PYTHONPATH.

Checking the Python version

Use sys.version_info for programmatic version checks and sys.version for human-readable output.

pythonpython
import sys
 
print(sys.version)
 
if sys.version_info >= (3, 11):
    print("Exception groups are available")
else:
    print("Upgrade to Python 3.11+ for exception groups")

sys.version prints the full interpreter build string, and since this example runs on Python 3.14, the version check also confirms exception groups are supported:

texttext
3.14.6 (main, Jul  4 2026, 10:00:00) [Clang 17.0.0]
Exception groups are available

sys.version_info is a named tuple with major, minor, micro, releaselevel, and serial. Compare it with tuples to check for minimum versions. Use this sparingly; most features are easier to check with try/except or hasattr.

Setting recursion limit

Python limits the depth of the call stack to prevent infinite recursion from crashing the interpreter. You can read and adjust this limit with sys.getrecursionlimit() and sys.setrecursionlimit().

pythonpython
import sys
 
print(sys.getrecursionlimit())  # 1000
 
sys.setrecursionlimit(2000)
print(sys.getrecursionlimit())  # 2000

The default is typically 1000. Increasing it lets deeply recursive functions run longer, but setting it too high can cause a segmentation fault instead of a clean RecursionError. Only adjust this when you have a specific deep-recursion use case and have confirmed the algorithm cannot be rewritten iteratively.

Practical example: a simple CLI tool

Combine sys.argv, sys.exit, and sys.stderr into a small command-line utility.

pythonpython
import sys
 
def read_file(filename):
    try:
        with open(filename) as f:
            return f.read()
    except FileNotFoundError:
        print(f"Error: '{filename}' not found", file=sys.stderr)
        sys.exit(1)
    except PermissionError:
        print(f"Error: permission denied for '{filename}'", file=sys.stderr)
        sys.exit(1)

With the error-handling function defined, the rest of the script validates the arguments the user passed on the command line before calling it, so a missing filename never reaches the function at all:

pythonpython
if len(sys.argv) != 2:
    print("Usage: python read.py <filename>", file=sys.stderr)
    sys.exit(1)
 
content = read_file(sys.argv[1])
print(content)

Error messages go to sys.stderr so they appear even when the user redirects output. The script exits with code 1 on any failure, which tells shell scripts and CI that something went wrong.

Common mistakes

Using exit() or quit() instead of sys.exit(). The built-in exit() and quit() exist for interactive use in the REPL. They may not work in all environments, especially frozen or embedded Python. Always use sys.exit() in scripts.

Accessing sys.argv without checking length. sys.argv[1] raises IndexError when no arguments are passed. Always check len(sys.argv) before accessing indices.

Modifying sys.path as a permanent solution. Adding directories to sys.path at runtime is a temporary fix. For reusable code, structure your project as a proper package or set PYTHONPATH in your shell profile.

Rune AI

Rune AI

Key Insights

  • Use sys.argv to read command-line arguments; the first element is always the script name.
  • Use sys.exit(code) to terminate a program with an exit status.
  • Use sys.stderr.write() for error messages so they are not mixed with standard output.
  • sys.path is the list of directories Python searches for module imports.
  • sys.version and sys.version_info provide the running Python version.
  • Use sys.stdin.read() for piped input and sys.stdout.write() for programmatic output.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between sys.exit() and exit()?

`sys.exit()` is the proper way to exit a Python program. It works everywhere and raises `SystemExit`, which you can catch in tests. The built-in `exit()` and `quit()` are meant for interactive use in the REPL and should not be used in production scripts.

How do I add a directory to the Python import path at runtime?

Use `sys.path.append('/path/to/directory')`. This adds the directory to the end of the search path for the current session. For permanent changes, use the `PYTHONPATH` environment variable or install your code as a package instead.

What is sys.platform and when should I use it?

`sys.platform` returns a string identifying the operating system: `'darwin'` for macOS, `'linux'` for Linux, `'win32'` for Windows. Use it for platform-specific code paths, but prefer higher-level abstractions like `pathlib` or `os.name` when they cover your need.

Conclusion

The sys module gives you access to the Python interpreter's runtime state. Use sys.argv for command-line arguments, sys.exit() to terminate with a status code, sys.stdin/sys.stdout/sys.stderr for I/O streams, and sys.path to understand or modify the module search path. For parsing complex command-line arguments, prefer argparse over raw sys.argv.