CertGrid CertGrid

Python automation cheat sheet

The whole path on one page, grouped by its five domains - scripting, files and data, OS and process, APIs and cloud, network and CI. Every output was printed by Python 3.14.4 on the lab's control node, and the rows are the ones that fail a script at 3 a.m. rather than the ones that appear most often.

Domain 1: Scripting foundations (19%)

  • sys.executable · sys.version_info

    Which interpreter is actually running this. The first thing to print when a script works for you and not for cron.

    Full guide
  • ArgumentParser · required=True · action="count"

    A parsed Namespace. `-vv` counts to 2, which is how verbosity levels are done.

    Full guide
  • a missing required argument

    argparse prints the error and exits **2** by itself - you write no validation and no exit code.

    Full guide
  • sys.exit(3)

    Your own exit code, for a caller to branch on. 0 success, 1 general failure, and your own meanings above that.

    Full guide
  • raise SystemExit('message')

    Prints to stderr and exits 1, with no traceback. The right shape for a configuration error.

    Full guide
  • basicConfig(format=, datefmt=, stream=stderr) · exc_info=True

    Timestamped lines on stderr, `%d%%` formatted by logging rather than by you, and a traceback attached to the ERROR.

    Full guide
  • os.environ.get(name, default)

    A setting from the environment with a fallback, and no KeyError for the one that is absent.

    Full guide
  • PATH · HOME · LANG

    The three that differ under a scheduler. Print them on the first failing run rather than guessing.

    Full guide

Domain 2: Files, data formats and text (21%)

  • Path.write_text · read_text(encoding=) · stat().st_size · resolve()

    Always pass `encoding="utf-8"`. Without it the result depends on the machine.

    Full guide
  • Path.glob · .parent · .stem · .suffix

    Splitting a path without string surgery - `/var/log/app`, `run`, `.log`.

    Full guide
  • csv.DictReader · newline=""

    Rows as dicts, every value a **string** - the comparison needs int(). `newline=""` is required or quoted fields with newlines break.

    Full guide
  • json.dumps · loads · default=str

    True becomes `true` and None becomes `null`. A datetime needs `default=`, or it raises.

    Full guide
  • yaml.safe_load · and two traps

    **`version: 1.10` parses as 1.1** and **`country: NO` as False.** Quote any value whose exact text matters.

    Full guide
  • named groups · groupdict() · findall

    One compiled pattern with named groups gives you a dict, which is far easier to read than numbered groups.

    Full guide
  • mkstemp + os.replace

    An atomic write: a reader sees the old file or the new one, never half of either. `os.replace` is the atomic step, and only on the same filesystem.

    Full guide
  • make_archive · which · disk_usage

    A tarball in one call, a dependency check, and the free space to decide whether to try.

    Full guide

Domain 3: OS, process and task automation (20%)

  • run(argv, capture_output=True, text=True, timeout=, check=True)

    The one call worth memorising. A list so no shell parses it, a timeout so it cannot hang, and check so a failure is loud.

    Full guide
  • returncode · stdout · stderr

    The three things a finished command gives you. Read stderr before deciding what went wrong.

    Full guide
  • CalledProcessError · TimeoutExpired

    `check=True` raises with the return code attached, and `timeout=` kills the child before raising.

    Full guide
  • env= replaces · dict(os.environ, X=y) adds

    **`env={"TOKEN":"abc"}` leaves the child with no HOME.** Copy the environment and add to it.

    Full guide
  • cwd= per child · os.umask(0) to read it

    `cwd=` affects one child; `os.chdir` affects the whole process. Reading the umask means setting it and putting it back.

    Full guide
  • os.open(path, O_CREAT | O_EXCL, 0o600)

    A private file that was never anything else, and a second attempt that refuses. `write_text` then `chmod` has a window.

    Full guide
  • fcntl.flock(f, LOCK_EX | LOCK_NB)

    A single-instance guard the kernel releases when the process dies - however it dies. An O_EXCL lock file goes stale; this cannot.

    Full guide
  • Popen.returncode after a signal

    Negative in Python, `128 + n` in a shell. **137 is SIGKILL** - the OOM killer or a grace period expiring.

    Full guide

Domain 4: APIs, web and cloud (20%)

  • requests.get(url, timeout=) · status_code · ok · .json()

    `ok` is `< 400`, not `== 200`. **There is no default timeout** - omit it and a hung server hangs the script.

    Full guide
  • a 502 that is text/html

    `JSONDecodeError: Expecting value` is what a `<` looks like to a JSON parser. Check the status before you parse.

    Full guide
  • params= encoding

    Spaces and ampersands encoded, a list repeated, and `None` dropped entirely. Never build a query string by hand.

    Full guide
  • headers={'Authorization': 'Bearer ...'}

    401 without it, 200 with it. The token comes from the environment, never from the source, and never from `params=`.

    Full guide
  • json= sets the body and the Content-Type

    201 and a Location header. `data=` with a dict would send a **form** and get a 400 from a JSON API.

    Full guide
  • follow next_page, on one Session

    Five hosts over three pages and **one TCP connection**. Loop on the server's pointer, not on arithmetic of your own.

    Full guide
  • ReadTimeout · Retry-After

    A timeout on every call, and when a 503 tells you how long to wait, wait that long rather than guessing.

    Full guide
  • NoRegionError · and S3 saying nothing

    **ec2 raises; s3 silently uses us-east-1.** The silent one is why your bucket "does not exist". Set the region explicitly.

    Full guide

Domain 5: Network, testing and CI/CD (20%)

  • connect · exec_command · recv_exit_status

    paramiko raises **nothing** for a failing command. Read the exit status or report success for a failure.

    Full guide
  • RejectPolicy, which is the default

    "Server not found in known_hosts" is the library protecting you. `AutoAddPolicy` is `StrictHostKeyChecking=no`.

    Full guide
  • put to .part, then posix_rename

    A reader never sees a half-written upload. `rename` fails over an existing target; `posix_rename` overwrites.

    Full guide
  • ThreadPoolExecutor over an inventory

    One dark host costs the whole run one timeout instead of holding up every other host. Catch per host and keep a result for each.

    Full guide
  • pytest -q · @pytest.mark.parametrize

    Three cases, three tests, three separate results. A loop inside one test stops at the first failure.

    Full guide
  • a failing assert, and --tb=line

    `assert 6 == 7` - pytest rewrites the assertion so both values are in the message. Write no message.

    Full guide
  • pytest exit 5

    **No tests collected exits 5**, not 0. A CI step that only treats 1 as failure goes green having run nothing.

    Full guide
  • patch("thismodule.subprocess.run", return_value=CompletedProcess(...))

    Patch where the name is **looked up**, not where it is defined - and assert on the argv that was passed, not just the result.

    Full guide
  • ruff check --select S,PLW,E722

    Five findings in one second: shell=True, no check, no timeout, a bare except. The cheapest gate that exists.

    Full guide