runtime
113 / 250
lesson 113 CLI & Automation · intermediate

Settings from environment variables

Some settings belong to the machine, not to one run: where reports go, an API key, whether to send emails. Environment variables carry them, always as text, and a script reads them with a default.

You'll be able to
  • Read a variable with os.environ.get(name, default) and explain why os.environ[name] can fail
  • Convert the text to the type the program needs, and treat "0" and "no" as off
  • Combine sources in a clear order: command line, then environment, then a default
The idea

Every running program receives a set of named text values from whatever started it: the environment. A shell sets one for a single run with REPORT_LIMIT=50 python report.py, or for the whole session with export REPORT_LIMIT=50. Servers and schedulers set them in their configuration. They suit settings that differ per machine (a folder, a database address) and secrets that shouldn't be typed on a command line where they end up in the shell's history.

Python shows the environment as os.environ, which behaves like a dictionary from text to text. Assigning to it, as the first line below does, sets a variable for this program (and any program it starts), just as the shell would have.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · Reading with and without a default
import os

os.environ["EXPORT_FOLDER"] = "exports/daily"
print(os.environ["EXPORT_FOLDER"])
print(os.environ.get("EXPORT_BATCH_LIMIT"))
print(os.environ.get("EXPORT_BATCH_LIMIT", "25"))
limit = int(os.environ.get("EXPORT_BATCH_LIMIT", "25"))
print(limit + 5)
example-2.py · Square brackets on a missing variable
import os

folder = os.environ["EXPORT_ARCHIVE_FOLDER"]
print("archiving to", folder)

A function that reads settings is easier to test when it takes the environment as a parameter. The program passes os.environ; a test passes an ordinary dictionary, so it doesn't depend on what happens to be set on the machine running it.

On/off settings need care. Every non-empty text is true in an if, including "0" and "no", so compare against the words that mean *on* after strip() and lower().

example-3.py · All of a report's settings in one place
def load_settings(env):
    limit = int(env.get("REPORT_LIMIT", "25"))
    send = env.get("SEND_EMAIL", "no").strip().lower()
    return {
        "folder": env.get("REPORT_FOLDER", "reports"),
        "limit": limit,
        "send_email": send in ["1", "true", "yes", "on"],
    }

print(load_settings({}))
print(load_settings({"REPORT_LIMIT": "100", "SEND_EMAIL": " Yes ", "REPORT_FOLDER": "/srv/reports"}))
print(load_settings({"SEND_EMAIL": "0"}))

When to use it: for settings that belong to the machine or the deployment, and for secrets. A value someone chooses per run belongs on the command line instead. When both exist, the usual order is: an explicit command-line value wins, then the environment, then the default in the code.

Mistakes to avoid: os.environ[name] for an optional setting (KeyError when it's missing); using the text without converting it ("10" > "9" is False, because text compares letter by letter); and if env.get("DEBUG"):, which is true for "0".

Review
  • os.environ.get(name, default) reads a setting; brackets raise KeyError when it's missing.
  • Environment values are always text: convert numbers, and compare on/off words after strip and lower.
  • Take the environment as a parameter, and use a clear order: command line, environment, default.
  • Next: starting other programs from Python, and keeping that call replaceable.
Practise this

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Settings from environment variables — CLI & Automation · Runtime