"""The one `.mam.env` parser (Issue #26). Rules: blank lines and `#` comments are skipped; `KEY=VALUE` with the key and the value stripped; one matching pair of surrounding quotes is removed; no shell interpretation (`$(...)`, `${...}` and backticks stay literal). A key must match ``^[A-Za-z_][A-Za-z0-9_]*$``; a line that does not, or that has no `=`, is skipped with a warning naming the line (never the value — the file holds secrets). Variables already in the environment win over the file. Stdlib only: the delegate-job wrapper runs this file by path (``envfile.py --shell ``) and evals the ``export`` lines it prints, so values travel over a pipe, never on a command line. """ import os import re import shlex import sys from typing import List, MutableMapping, Tuple _KEY = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") def parse(path: str) -> Tuple[List[Tuple[str, str]], List[str]]: """Return ([(key, value)], [warning]) for the file at ``path``.""" pairs: List[Tuple[str, str]] = [] warnings: List[str] = [] try: with open(path, "r", encoding="utf-8") as f: lines = f.read().splitlines() except (OSError, UnicodeDecodeError) as e: return pairs, [f"{path}: cannot read ({type(e).__name__})"] for n, raw in enumerate(lines, 1): line = raw.strip() if not line or line.startswith("#"): continue key, sep, val = line.partition("=") key = key.strip() if not sep: warnings.append(f"{path}:{n}: skipped (no '=')") continue if not _KEY.fullmatch(key): warnings.append(f"{path}:{n}: skipped (invalid key)") continue val = val.strip() if len(val) >= 2 and val[0] == val[-1] and val[0] in "\"'": val = val[1:-1] pairs.append((key, val)) return pairs, warnings def apply(path: str, environ: MutableMapping[str, str]) -> Tuple[List[Tuple[str, str]], List[str]]: """Set each pair not already in ``environ``; return (the pairs set, warnings).""" pairs, warnings = parse(path) applied = [] for key, val in pairs: if key not in environ: environ[key] = val applied.append((key, val)) return applied, warnings def main(argv: List[str]) -> int: if len(argv) != 2 or argv[0] != "--shell": print("usage: envfile.py --shell ", file=sys.stderr) return 2 path = argv[1] if not os.path.isfile(path): return 0 applied, warnings = apply(path, dict(os.environ)) for w in warnings: print(f"WARNING: {w}", file=sys.stderr) for key, val in applied: print(f"export {key}={shlex.quote(val)}") return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))