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=Truereturns 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 utilitiesrandom— random numbers, choices, shufflesdatetime— dates, times, durations, time zonescollections—Counter,defaultdict,dequepathlib— 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.