Two modules for talking to the world your program runs in: os for the operating system and sys for the Python interpreter itself.

sys — the Python interpreter

Command-line arguments

We met this in Section 11:

import sys

print(sys.argv)
# ['main.py', 'arg1', 'arg2']

sys.argv[0] is the script name; the rest are the arguments passed on the command line.

Exiting

import sys

if not have_data():
    print("No data found")
    sys.exit(1)

sys.exit(n) ends the program. 0 means success, anything else is a failure code.

Standard streams

The three streams every program has:

import sys

print("normal output", file=sys.stdout)    # default
print("an error",     file=sys.stderr)
input_text = sys.stdin.read()              # read piped input

sys.stderr is where errors and warnings should go — it stays unbuffered and can be redirected separately from regular output.

Python info

print(sys.version)            # detailed Python version string
print(sys.version_info)       # tuple — easier to compare
print(sys.platform)           # 'darwin' / 'linux' / 'win32'
print(sys.executable)         # path to the Python binary running this code
print(sys.path)               # module search path

sys.version_info is useful for version checks:

if sys.version_info < (3, 10):
    sys.exit("This script requires Python 3.10 or newer")

os — the operating system

os is huge — we’ll cover the most useful parts.

Environment variables

import os

# read
home: str | None = os.environ.get("HOME")
print(home)

# write (for the current process and any children only)
os.environ["DEBUG"] = "1"

os.environ is a dictionary-like object. Use .get() to read safely — os.environ["MISSING"] raises KeyError.

For private configuration (API keys, passwords), reading from os.environ is the standard pattern. Don’t hardcode secrets in source.

Current working directory

print(os.getcwd())            # current dir
os.chdir("/tmp")              # change dir

Modern code prefers Path.cwd() from pathlib — same idea, more consistent API.

Process info

print(os.getpid())            # this process's PID
print(os.getppid())            # parent process's PID
print(os.cpu_count())          # how many CPU cores

os.cpu_count() is useful for sizing thread pools and worker processes.

Running other programs

For running external commands, use subprocess:

import subprocess

result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
print(result.stdout)
print(result.returncode)

subprocess.run:

  • Takes a list of arguments — each piece of the command separately
  • Returns a result object with stdout, stderr, returncode
  • text=True returns strings instead of bytes

Always pass the command as a list, not a single string. The list form avoids issues where user-supplied data could be misinterpreted as part of the command.

Path operations (legacy)

os.path has its own path-handling functions:

import os.path

os.path.join("data", "users.csv")
os.path.exists("data/users.csv")
os.path.basename("data/users.csv")
os.path.splitext("users.csv")

These work, but pathlib is the modern alternative — cleaner and more consistent. Use os.path only when you find it in older code.

A practical example — read config from env

A small pattern that comes up constantly:

import os
import sys


def get_required_env(name: str) -> str:
    value: str | None = os.environ.get(name)
    if value is None:
        print(f"Error: env var {name} is not set", file=sys.stderr)
        sys.exit(1)
    return value


def main() -> None:
    api_key: str = get_required_env("API_KEY")
    debug: bool = os.environ.get("DEBUG", "0") == "1"

    print(f"Starting (debug={debug})")
    # ... use api_key ...


if __name__ == "__main__":
    main()

Reads sensitive config from environment variables, fails loudly when something required is missing. The basis of every twelve-factor-style application.

Summary of Section 14

A whirlwind tour of the most useful standard library modules:

  • math, statistics — numeric utilities
  • random — random numbers, choices, shuffles
  • datetime — dates, times, durations, time zones
  • collectionsCounter, defaultdict, deque
  • pathlib — file paths (deeper)
  • os, sys — environment, args, interpreter, processes

There’s far more in the standard library than fits in one section. Browse the Python docs when you need something — chances are it’s already there.

What’s next

Section 15: Debugging and Code Quality — reading tracebacks, using the debugger, and writing clean code.

Toggle theme (T)