hanki

env

stdlib/extra/env.hk: environment-variable reads.

A pure-Hanki face over the sys native seam, like io/fs. The [env] effect bubbles up through each delegating call. Any program that can touch the environment therefore says so in its signatures and can be refused wholesale with --deny env; env reads are the leading real-world exfiltration target (cloud credentials, CI tokens).

What this module refuses, and why

Reading is here; mutating ambient process state is not, and that is a decision and no gap:

Each of those would be a one-line delegation to write. The reason is recorded here for that reason, and not left to read as an oversight.

The effectful functions have no hermetic test: each is a thin delegation to the sys native seam, and exercising it needs real, non-deterministic OS state that cannot be asserted in the pure stdlib test pack; coverage for those is end-to-end through the CLI integration tests and example programs. _unflatten is the exception and is tested at the bottom: it is ordinary pure logic, and the seam's pairing rule is worth pinning.

get!

def get!(name: string) -> Option<string> [env]

Read the environment variable name. Some(value) when set, None when unset (or, in v0, when the value is not valid UTF-8). @no-doctest: reads the process environment; host-dependent, and cannot assert in a doctest

vars!

def vars!() -> Map<string, string> [env]

Every environment variable, as a map from name to value.

The whole environment in one read, which lets a script inspect it or hand a modified copy to process.run_with!. An entry whose name or value is not valid UTF-8 is skipped, which is get!'s answer for the same case. @no-doctest: reads the process environment; host-dependent, and cannot assert in a doctest

_unflatten

def _unflatten(flat: List<string>, i: int, acc: Map<string, string>) -> Map<string, string>

Rebuild the map from the seam's flattened [name, value, ...] list: the inverse of process._flatten_env, on the same wire form, which lets the two compose. A trailing odd element cannot occur (the seam emits pairs) and is dropped and never guessed at.

cwd!

def cwd!() -> Result<string, sys.FsError> [fs_read]

The process's current working directory.

Err(NotUtf8) when the path is not valid UTF-8, Err(Other) when the OS refuses the read; a directory can be deleted out from under a running process. Charges [fs_read] and not [env]: the answer comes from the filesystem and names a location on it. @no-doctest: reads the live working directory; host-dependent, and cannot assert in a doctest

home!

def home!() -> Option<string> [env]

The current user's home directory, or None when HOME is unset.

No more than a named get!("HOME"), by design: there is no fallback to a password-database lookup. A script that cares about the difference should see None and say what it wants, and one that does not can write home!().unwrap_or("."). @no-doctest: reads the process environment; host-dependent, and cannot assert in a doctest

temp_dir!

def temp_dir!() -> string [env]

The directory for temporary files: TMPDIR when set, else /tmp.

The POSIX rule, written out here and not hidden behind a seam call: the rule is the whole answer on the platforms Hanki targets, and a script that needs to know which of the two it got can read TMPDIR itself. Returns a plain string: there is always an answer. @no-doctest: reads the process environment; host-dependent, and cannot assert in a doctest