Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

env.loading

import env.loading

env lifts part of this module out to its own top level; each name below is shown with the path that reaches it. Anything still spelled env.loading.* needs import env.loading.

The loader: which sources to read, in what order, and what to do with the names that come out of them.

A load is three steps that stay separate. The sources are scanned into names and values, the $ references among them are resolved, and then the result is written into the process environment. Calling read() instead of load() stops after the second, which is what makes a .env file inspectable without anything being set.

Classes

Result

class env.Result

What a load did.

values is everything the sources defined, after expansion, in the order the names were first seen. The three lists beside it account for each name and each file, so a program never has to guess whether its configuration actually arrived.

var result = env.load()

if result.files.is_empty() {
  log.warn('no .env file was found; using the environment as it stands')
}

for name in result.skipped {
  log.debug('${name} was already set, so the file did not change it')
}
  • printable — has a @to_string(), so echo and print() show something useful

Constructor

env.Result()

Result.to_string()

env.Result.to_string()

Loader

class env.Loader

Builds a load: the sources it reads and the four decisions it makes about them.

Every setting returns the loader, so a whole configuration is one expression:

import env

var result = env.loader()
  .path('.env')
  .path('.env.local')
  .override()
  .load()

Sources are read in the order they were added and layered on top of one another, so the last source to define a name is the one that defines it. That is what makes the pair above work: .env holds what the team shares and .env.local holds what one machine does differently.

A loader is reusable. Nothing about it changes when load() runs, so the same one can be kept and run again.

Constructor

env.Loader()

Loader.path()

env.Loader.path(path: string) -> Loader

Adds a file to read.

A leading ~ is expanded to the home directory, and a relative path is resolved against the current working directory. A file that is not there is not an error unless required() says so.

Parameters

  • path (string)

Returns Loader

Loader.paths()

env.Loader.paths(paths: list) -> Loader

Adds several files to read, in the order given.

Parameters

  • paths (list)

Returns Loader

Loader.source()

env.Loader.source(text: string) -> Loader

Adds text to read, as though it were the contents of a file.

This is how configuration that arrived over the network, out of a secret store, or from a test fixture goes through exactly the same parsing, expansion and precedence as a file on disk.

Parameters

  • text (string)

Returns Loader

Loader.override()

env.Loader.override(enabled: ?bool) -> Loader

Sets whether a name already present in the process environment is replaced by the one the sources define.

Off by default, which is what makes a .env file a set of defaults: whatever the shell, the orchestrator or the CI runner already set survives, and the file fills in the rest. Turn it on and the file wins instead.

A variable set to the empty string counts as unset either way.

Parameters

  • enabled (?bool) — Defaults to true when the argument is left out.

Returns Loader

Loader.expand()

env.Loader.expand(enabled: ?bool) -> Loader

Sets whether $ references inside values are resolved.

On by default. Turn it off and every value is used exactly as it was written, which is what a file of opaque secrets wants.

Parameters

  • enabled (?bool) — Defaults to true when the argument is left out.

Returns Loader

Loader.required()

env.Loader.required(enabled: ?bool) -> Loader

Sets whether a file that is not there is an error.

Off by default, because the same program usually runs both on a laptop with a .env file and on a server where the orchestrator supplies the environment directly. Turn it on where the file is genuinely part of the deployment and its absence should stop the program rather than surface later as a missing setting.

Parameters

  • enabled (?bool) — Defaults to true when the argument is left out.

Returns Loader

Raises MissingFile from load() and read() when a file is absent.

Loader.read()

env.Loader.read() -> Result

Reads the sources and returns what they hold, without touching the process environment.

$ references still resolve, and they resolve the same way they would during a real load, so what comes back is what load() would have set.

The applied and skipped lists on the result are empty, because nothing was applied and nothing was skipped.

Returns Result

Raises ParseError when a source is not a valid environment file.

Raises MissingFile when required() is on and a file is absent.

Loader.load()

env.Loader.load() -> Result

Reads the sources and writes what they hold into the process environment.

A name already set to a non-empty value is left alone and recorded in Result.skipped, unless override() is on. Everything else is set and recorded in Result.applied.

import env

env.loader().path('.env').required().load()

Returns Result

Raises ParseError when a source is not a valid environment file.

Raises MissingFile when required() is on and a file is absent.

Raises MissingVariable when a ${NAME:?reason} reference finds nothing.


2026, Richard Ore and Zuri contributors