# The .env File Some settings belong to the computer a program runs on, not to the program: where an instrument is plugged in, which network port a server uses, a password. They differ from lab to lab, and some of them are secret, so they must not be written into code or into a file that is shared with others. plesty-lib reads such settings from **environment variables**. An environment variable is a named value that the program finds in its surroundings instead of in its code. There are two places to set one: - in the terminal, before starting the program: ```bash export PM_ADDRESS=tcp://192.168.1.20:5555 # macOS, Linux $env:PM_ADDRESS = "tcp://192.168.1.20:5555" # Windows PowerShell ``` - in a file called `.env`, one `NAME=value` per line. This is the usual place, because the file stays where it is and you do not have to set anything again in each new terminal: ```bash # .env — lines starting with # are comments PM_ADDRESS=tcp://192.168.1.20:5555 PM100D_VISA=USB0::0x1313::0x8078::P0000001::INSTR ``` ## Where the file goes plesty-lib looks for `.env` in the folder you start the program from, not in the folder of the script. Start your programs from the module's top folder and keep `.env` there. ## Never share the real file `.env` holds what is specific, and sometimes secret, to your lab. Keep it out of git or any other version control. A module created with `plesty init`, the command from plesty-sdk that creates a new module's files (see [Tools for publishing a module](../install.md#tools-for-publishing-a-module)), already lists `.env` in its `.gitignore`, the file that tells git what to leave out. What you share instead is **`.env.example`**: the same variable names with empty or harmless values, and a comment on each saying what it is for. Someone setting the module up copies it to `.env` and fills in their own values. ```bash cp .env.example .env ``` ## Which value wins When a name is set in more than one place, the first of these wins: 1. a value given to the program directly, such as a command-line option (see `override` below); 2. the terminal's environment; 3. the `.env` file; 4. the default in the code. ## Variables plesty-lib reads by itself You do not have to read these yourself: the parts of plesty-lib that need them do. | Variable | Read by | Holds | |---|---|---| | `_ADDRESS` | `CompositeDevice`, for each device you name without an address (see [Building a composite](../device/composite_device.md#building-a-composite)) | the network address of that device's server; the name in capitals, so a device called `pm` reads `PM_ADDRESS` | | `PLESTY_DATA` | experiments, `Run`, and the viewer made with `Viz` | the folder where this computer sees the lab's shared data disk (see [Data on a Shared Disk](../experiment/shared_disk.md)); a leading `~` is this computer's home folder | ## Reading your own variables: `EnvSettings` For settings of your own, use `EnvSettings` from `plesty.lib.utils`. It reads `.env` and the terminal's environment together, and converts a value to the type you ask for: ```python from plesty.lib.service import build_server, run_server from plesty.lib.utils import EnvSettings from powermeter_device import PowermeterDevice env = EnvSettings.load() address = env.require("PM100D_VISA") # must be set tcp_port = env.get("PM100D_TCP_PORT", default=5555, cast=int) # may be left out device = PowermeterDevice(address, sensor_type="S155C") run_server(build_server(device, address=f"tcp://*:{tcp_port}").run()) ``` | Call | Does | |---|---| | `EnvSettings.load()` | reads `.env` from the current folder, then the environment. A missing `.env` is not an error. | | `env.get(name, default=…, cast=…)` | the value, converted with `cast` (`int`, `float`, `bool`, …), or `default` when it is unset or empty. With `cast=bool`, `1`, `true`, `yes` and `on` mean true. | | `env.require(name, cast=…)` | the same, but stops the program with an error naming the variable when it is unset. Use it for anything the program cannot run without. | | `name in env` | whether the variable is set and not empty | Both `get` and `require` take `override=`: a value that wins over everything else when it is not `None`. Pass a parsed command-line option there, and the option replaces the environment for that one start: ```python address = env.require("PM100D_VISA", override=args.address) # --address, if given ``` `load()` also copies the values it read from `.env` into the program's environment, without replacing any that are already set, so programs it starts see them too. Pass `export=False` to only read them.