Settings and Secrets Outside the Code
Some values do not belong inside a script. The currency one person wants their reports in, the location of a file that is different on every computer, and above all passwords and API keys. Keeping them outside the code means the script can be shared without editing it, and a secret cannot leak just because someone saw the code.
Think of a house key. You keep it on your keyring, not taped to the front door. The door, like the code, can be shown to anyone; the key stays with the person it belongs to.
What Belongs Outside
Two kinds of value. Anything that differs between people or machines: a home currency, a folder, a preferred number of decimal places. And anything secret: passwords, API keys, tokens. The code describes what the program does; these values describe the situation it runs in.
Environment Variables
An environment variable is a named value that the terminal passes to every program it starts. Python reads them through os.environ, which behaves like a dictionary. Use its get method with a default, exactly as in Chapter 6, so the program still works when the variable is not set.
import os home = os.environ.get("HOME_CURRENCY", "USD") print(f"Reports will be in {home}")
Reports will be in USD
To set a variable for the current terminal session, macOS uses export and PowerShell uses a special $env: name. Run the script again afterwards and it picks the new value up. The setting lasts until that terminal window closes.
you@mac python-course % export HOME_CURRENCY=EUR you@mac python-course % python3 currency.py Reports will be in EUR
PS C:\Users\you\python-course> $env:HOME_CURRENCY = "EUR" PS C:\Users\you\python-course> python currency.py Reports will be in EUR
A Settings File
For several preferences, a small JSON file is friendlier than a handful of variables. The function below starts from a dictionary of defaults and, if a settings.json file exists, lays its values over the top with the dictionary method update. A missing file simply means the defaults. Notice dict(DEFAULTS): it copies the defaults first, so that updating the settings never changes the defaults themselves, which is Chapter 6's two-names-one-dictionary lesson in practice.
{"currency": "EUR"}
import json from pathlib import Path DEFAULTS = {"currency": "USD", "statement": "statement.csv"} def load_settings(): """Return the defaults, overridden by settings.json if it exists.""" settings = dict(DEFAULTS) path = Path("settings.json") if path.exists(): settings.update(json.loads(path.read_text(encoding="utf-8"))) return settings print(load_settings())
{'currency': 'EUR', 'statement': 'statement.csv'}
Secrets Never Go in the Code
An API key written into a script ends up everywhere the script goes: a shared folder, a screenshot, an email to a colleague, a code-sharing website. Once it is out, it has to be cancelled and replaced. The rule is simple and absolute: read secrets from an environment variable, and never print them. The exchange-rate service in this chapter needs no key, so the pattern below uses a placeholder name. If the variable is not set, the program stops with a message saying what to do, rather than failing later with something cryptic.
import os import sys api_key = os.environ.get("EXAMPLE_API_KEY") if api_key is None: sys.exit("Set EXAMPLE_API_KEY before running this script.") print("Key found; it is used, never printed.")
Set EXAMPLE_API_KEY before running this script.
Many projects keep their local secrets in a file called .env, loaded by a small package when the program starts. If you use one, it must never be shared or uploaded anywhere, for exactly the reason above.
- "A key in the code is fine if the code is private." Private things get shared, pasted and uploaded. Keep keys out of the code from the first line.
- "Environment variables are permanent." Set with export or $env: in a terminal, they last for that session. To keep one, add the export line to the file ~/.zshrc on a Mac, or set it in Windows' environment-variable settings.
- "Settings need a special library." The standard library covers both approaches: os.environ and a JSON file.
- A script with its settings outside can be handed to someone else and run without editing a single line.
- Keeping secrets out of code is a habit that prevents real, expensive leaks, and it is expected in every professional project.
Knowledge Check
What does os.environ.get("HOME_CURRENCY", "USD") give when the variable is not set?
"USD", the default that was passed- An error, because the variable does not exist
None, whatever default is passed to get- An empty string, because variables start empty
Why must an API key never be written into a script?
- Keys stop working when they are stored in a .py file
- Python cannot read long strings like keys from a file
- The script gets shared and copied, and the key leaks with it
- Environment variables make the program run noticeably faster than keys
Why does load_settings start with dict(DEFAULTS)?
- Because update only works on a dictionary that starts empty
- To sort the defaults into order before the file is read
- Because DEFAULTS cannot be read from inside a function without a copy
- To copy the defaults, so updating never changes DEFAULTS
How long does export HOME_CURRENCY=EUR last on macOS?
- Until the computer is next restarted
- For every terminal window opened from then on
- Until that terminal window is closed
- Only for the very next command that is run
You got correct