Architecture
tn-venv is a four-stage pipeline wrapped in a configuration system. Each
stage is a package with a narrow contract, and the stages never see each
other’s internals — they exchange two data structures: PythonInfo (the
probe of the base interpreter) and CreatorContext (the paths of the
environment being built).
┌────────────────────────── tn_venv.cli ──────────────────────────┐
│ argparse (driven by config/spec.py) → exit codes │
└──────────────────────────────┬──────────────────────────────────┘
│
tn_venv.config: CLI ◄ env vars ◄ config file ◄ defaults
│
tn_venv.session
(Options → run_session)
│
┌──────────────┬────────────────────────┼───────────────────────┬──────────────┐
▼ ▼ ▼ ▼ ▼
tn_venv. tn_venv.create tn_venv.create. tn_venv.seed tn_venv.report
discovery (Creator per platform) activators (Seeder) (leveled output)
│ │ │ │
PythonInfo ──▶ CreatorContext ──▶ activation scripts ──▶ pip/setuptools/…
Package map
Module |
Responsibility |
|---|---|
|
argument parsing, config layering, exit codes, |
|
the single source of truth for every option ( |
|
TOML/INI/env-var loading and coercion |
|
|
|
spec parsing, providers, probing, matching |
|
|
|
|
|
one module per shell, template-based |
|
|
|
hook registry, lifecycle hooks, plugin discovery (entry points + |
|
inter-process file lock with stale detection |
|
subprocess wrapper with captured output |
|
leveled, colored console output |
|
the exception hierarchy |
Design rules
No runtime dependencies. Everything is stdlib. Optional behavior degrades instead of failing (e.g. a missing
pylauncher is a skipped provider, not an error).One source of truth per concern. Options exist once, in
OPTION_SPECS; platform branching exists once, in the creator factory; quoting rules exist once, in the activator base class.Probe, don’t guess. Interpreter facts (version, bitness, free-threading, launcher locations) come from executing the interpreter itself, never from parsing paths or filenames.
Mirror
venvsemantics. Where stdlibvenvhas an opinion — directory layout,pyvenv.cfgkeys, Windows launcher binaries,lib64symlinks — tn-venv adopts it, so environments stay interchangeable.Failures are typed. Every anticipated failure raises a subclass of
TnVenvError; the CLI maps them to documented exit codes and the API re-raises them unmodified.
Data flow in detail
CLI parses flags with
default=Noneeverywhere, so “not supplied” is distinguishable from “supplied as false”.Config merges layers into a plain dict: defaults ← file ← env ← CLI.
Session turns the dict into a validated
Optionsdataclass and acquires the destination lock.Discovery resolves
--pythonspecs to a probedPythonInfo(subprocess JSON probe, cached).Creator builds the skeleton (
ensure_directories), installs the interpreter binaries (setup_python), writespyvenv.cfg(create_configuration) and the SCM ignore file.Activators render their templates against
CreatorContextwith shell-appropriate quoting.Seeder invokes the new interpreter with a scrubbed environment (
PYTHONHOME/PYTHONPATHremoved,VIRTUAL_ENVset) for ensurepip and all pip steps.The lock is released and a
SessionResultis returned; the CLI prints the activation hint.
Plugin hooks
The session pipeline emits five events at well-defined points:
session_start, pre_create, post_activators, post_seed, and
session_end. Each callback receives a
HookContext and may mutate ctx.data (to
share scratch state with later hooks of the same run) or ctx.result
(during session_end only). A sixth hook, HELP_EPILOG, fires only
when tn-venv --help is rendered; its listener returns a string
that tn-venv appends to the help output.
Plugins are loaded by load_plugins, which consults the built-in
registry, the tn_venv.plugins entry-point group, and the
TN_VENV_PLUGINS environment variable in that order; duplicates are
deduplicated by class. The loader runs eagerly at the top of
cli_run — so a plugin that wraps cli_run (e.g. the
tn-venv-gui package installing its gui subcommand) gets a
chance to intercept argv like tn-venv gui --help before argparse
sees the help flag — and lazily inside format_help as a
belt-and-braces fallback for hosts that print help without going
through cli_run. See Plugins for the user-facing
tutorial.
Concurrency model
Creation is serialised per destination directory by
the file lock. Parallel
creations of different destinations are fully independent — tn-venv
holds no global mutable state beyond the per-process probe cache.