Python Atlas
Standard library & async

os and sys

Work with operating-system services and the running Python interpreter.

os exposes operating-system facilities; sys exposes the current Python process. Prefer high-level modules such as pathlib, subprocess, and shutil when they express the operation directly, then use os for lower-level details.

Environment and process state

import os
import sys

debug = os.environ.get("APP_DEBUG", "").casefold() in {"1", "true", "yes"}
data_dir = os.environ.get("APP_DATA_DIR")

if data_dir is None:
    print("APP_DATA_DIR is required", file=sys.stderr)
    raise SystemExit(2)

print(f"pid={os.getpid()} debug={debug}")

os.environ is a mutable mapping of strings. A change affects the current process and future child processes, not the parent shell. Do not log the entire mapping: it commonly contains credentials. Use os.getenv() for a single optional value and explicit validation for required configuration.

The current working directory is process-global:

from pathlib import Path

print(Path.cwd())

Avoid os.chdir() in libraries and servers because unrelated code observes the change. Resolve paths from a known base directory instead.

Command-line arguments and exit status

sys.argv[0] names the script; later entries are raw strings. argparse is usually better than manual parsing.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("source")
parser.add_argument("--limit", type=int, default=100)
args = parser.parse_args()

Return status 0 for success and a nonzero status for failure:

def main() -> int:
    return 0

if __name__ == "__main__":
    raise SystemExit(main())

This pattern is testable and lets Python flush streams and run cleanup handlers. os._exit() terminates immediately and is only appropriate in specialized post-fork situations.

Standard streams

sys.stdin, sys.stdout, and sys.stderr are text streams. Diagnostics belong on stderr so stdout can remain machine-readable.

import json
import sys

json.dump({"status": "ok"}, sys.stdout)
sys.stdout.write("\n")
print("processed one item", file=sys.stderr)

For binary data, use sys.stdin.buffer and sys.stdout.buffer. Never mix arbitrary text and binary writes without controlling buffering and encoding.

Filesystem-level operations

Useful os APIs include:

  • os.scandir() for efficient directory entries and metadata;
  • os.walk() for recursive traversal;
  • os.stat() for metadata;
  • os.replace() for atomic replacement on the same filesystem;
  • os.fspath() to accept both path objects and strings;
  • os.cpu_count() as a hint, not a workload-tuning guarantee.
import os
from pathlib import Path

def regular_files(directory: Path):
    with os.scandir(directory) as entries:
        for entry in entries:
            if entry.is_file(follow_symlinks=False):
                yield Path(entry.path)

Whether to follow symbolic links is a security decision. Code that scans untrusted trees should usually avoid following them and must handle files disappearing between inspection and use.

Platform portability

Use os.name, sys.platform, and sys.version_info only when behavior genuinely differs. Prefer capability checks over broad platform branches.

import os

if hasattr(os, "getuid"):
    print(os.getuid())

Do not split paths on / or construct shell commands with string concatenation. Use pathlib for paths and subprocess.run([...], shell=False) for external programs.

Common mistakes

  • Treating environment variables as trusted or typed values.
  • Assuming the working directory is the script directory.
  • Catching OSError broadly and discarding useful details such as filename and errno.
  • Mutating process-global state in reusable functions.
  • Using sys.exit() deep inside library code instead of raising a domain exception.

Practice

Write a CLI that requires REPORT_DIR, accepts --verbose, prints JSON to stdout, sends progress to stderr, and returns status 2 for configuration errors. Test it with a missing environment variable and a path containing spaces.

LEARNING RECORD

Finish this lesson

Mark it complete when you can explain the main decision without looking.

KNOWLEDGE CHECK

os and sys

1A reusable library needs a data directory. Which approach avoids unsafe assumptions about process-global state?
2Which CLI structure best preserves machine-readable output, diagnostics, cleanup, and a failure status?