Changelog
All notable changes to tn-venv are documented here. The format follows Keep a Changelog, and the project adheres to Semantic Versioning.
[Unreleased]
Added
Add
.vscode/launch.json.Add
__version__variable inscripts/fetch_shields_badges.py.Add
tn-venv-create-activateinto the Official Authorized field indocs/plugins/community.md.
Fixed
Add
docs*andscripts*to exclude list in[tool.setuptools.packages.find]exclude pattern.
[1.1.0] — 2026-10-16
Added
tn-venv --list-pluginsflag. Loads every plugin via the same discovery path asrun_session(built-ins + entry points +TN_VENV_PLUGINS) and prints a table of plugin name, registered hooks, and provenance. Hookless plugins (CLI shims, subcommand installers, …) appear as(no hooks)so they are not silently invisible.HookName.HELP_EPILOGhook. Plugins can now append text totn-venv --helpoutput via a public hook instead of monkey- patchingargparse. The listener takes no arguments and returns a string; tn-venv appends it after an auto-generatedPlugins:block. Plugins are loaded eagerly by :func:cli_runso a plugin that wrapstn_venv.cli.cli_run(e.g.tn-venv-guiinstalling itsguisubcommand) gets a chance to intercept argv liketn-venv gui --helpbefore argparse sees the help flag.PluginSourcedataclass exported fromtn_venv.plugins. Every loaded plugin instance now carries a_tn_venv_sourceattribute describing where it came from (built-in,entry-point: NAME=SPEC, orenv: TN_VENV_PLUGINS).--list-pluginsconsumes this; the attribute is private but the dataclass is part of the public API for callers who want to introspect plugins programmatically.
Changed
The plugin loader is now invoked eagerly at the top of
tn_venv.cli.cli_runrather than lazily on--help. Plugins that replacetn_venv.cli.cli_run(e.g. thetn-venv-guipackage installing itsguisubcommand) now intercept argv liketn-venv gui --helpon the very first invocation that triggers plugin loading. The lazy--help` trigger (viaformat_help) is still in place as a belt-and- braces fallback for hosts that print help without going throughcli_run``.main()looks upcli_runon its own module at call time instead of binding the name at definition time, so the console-script entry point picks up a plugin’s monkey-patch even whenmainwas imported before the patch was applied.
Fixed
tn-venv gui --helpprinted the full tn-venv help instead of the GUI subcommand’s help. The cli_run monkey-patch installed by the plugin only ran on the next invocation, so argparse saw--helpfirst and short-circuited before the wrapper could dispatch. Fixed by capturing the originalcli_runreference at the top ofcli_runand re-invoking through the (now-patched) module attribute when the plugin has replaced the function.Plugin loader dropped every plugin past the first one. A module that declared
PLUGINS = [A, B, C]only ever loadedA; the same bug affected comma-separated entries inTN_VENV_PLUGINSand entry points that pointed at modules rather than classes. The loader now returnslist[Plugin]from every code path (_from_module_attr,_from_entry_point,_resolve_target) and propagates them up toload_plugins.vars()scan picked the firstPluginsubclass it found, so a module that definedclass _Base(Plugin)followed byclass Concrete(_Base)loaded the empty-named base class instead of the concrete one — a silent no-op that hid user plugins. The scan now skips classes whosenameis empty (the convention for abstract / helper bases) and exposes a dedicated_scan_module_for_pluginshelper for the rule.Replaced
:class:~tn_venv.…`` cross-references indocs/guide/plugins.mdanddocs/development/architecture.mdwith plain inline code. The docs site does not configuresphinx.ext.autodoc, so the references previously rendered as literal<code>~tn_venv.…</code>(with the leading tilde) and provided no linkability.
Docs
Moved the full plugin reference from a single page into a new
docs/plugins/directory.docs/guide/plugins.mdis now a navigation pointer; the authoritative reference lives at- doc:
plugins/index.
New pages under
docs/plugins/:index(overview, public API table, when-to-use, versioning policy),quickstart(minimal end-to-end “write your first plugin” recipe, including thepyproject.tomlentry-point snippet),discovery(built-ins / entry points /TN_VENV_PLUGINS, deduplication rules, failure isolation, programmatic use, edge cases),hooks(full reference for all six hooks — the five lifecycle hooks plusHELP_EPILOG— withctx.datakeys populated per stage, listener signatures, priority, fault isolation, recipes),authoring(the fullPlugin/HookRegistry/HookContext/PluginSourceAPI, including introspected members used by--list-plugins),cli-integration(--list-plugins,HELP_EPILOG, adding a CLI subcommand, the cli-run monkey-patch contract),reference(one-screen API table for everything intn_venv.plugins).Slimmed plugin mentions in the rest of the docs to one-line references pointing at
docs/plugins/:docs/reference/python-api.md(Plugin system section now lists the public symbols and links to the reference),docs/reference/cli.md(--list-pluginsnow points at the new CLI-integration page),docs/reference/environment-variables.md(TN_VENV_PLUGINSrow now points atdocs/plugins/index),docs/development/extending.md(the existing redirect at the top now points at../plugins/...instead of../guide/plugins).New plugin-registry page at
docs/plugins/community.md. Plugin authors add their row by opening a PR; the four ratings (Maintenance / Stability / Documentation / Adoption, each 1-5 stars), the tier badge (Bronze / Silver / Gold / Platinum, derived from the average), the Official Authorized flag (granted only by the tn-venv team), and the last-reviewed date are filled in by the maintainers after merge. Shields.io badge URLs for every rating dimension are documented and the page also lays out the Plugin of the Month, Rising Plugin, and Most Discussed slots that will be filled in by the November 2026 monthly cycle. Thetn-venv-guirow ships at Tier 4 (Platinum) with Official Authorized status; all subsequent rows must be added by maintainers, not by the plugin authors themselves.docs/development/architecture.mdPlugin hooks section updated for the eager plugin load (see the [Changed] section above).docs/guide/plugins.mdupdated cross-reference ({ref}target) renamedhelp-epilog-hooktohelp-epilogto match the labelled section anchor.
[1.0.0] — 2026-10-05
Added
Plugin system under
tn_venv.plugins: aPluginbase class, a priority-orderedHookRegistry, and aload_pluginsloader that combines built-in plugins, third-party entry points in thetn_venv.pluginsgroup, and theTN_VENV_PLUGINSenvironment variable. Five lifecycle hooks are emitted byrun_session:session_start,pre_create,post_activators,post_seed,session_end.Built-in
VersionStampPlugin: appendstn-venv-version = <version>topyvenv.cfgafter the activation scripts have been generated. Doubles as the worked example in the newdocs/guide/plugins.md.docs/guide/plugins.md— user-facing tutorial covering discovery, hook semantics, and writing a plugin. The architecture and environment-variables references were updated to point at it.
Fixed
Fix the import for the
Pluginclass referenced in type annotations within the filetn_venv/plugins/registry.py. Change its import source from thetn_venv.plugins.builtinmodule back to the originaltn_venv.plugins.apimodule.
[0.2.0] — 2026-10-02
Added
Add alias
TNErrorback to public API.mypy static type checking, running alongside pyrefly:
[tool.mypy]configuration inpyproject.toml, amypy-typecheckGitHub Actions workflow, and.mypy_cache/added to.gitignore.pyrefly
project-excludescovering.venv,.github, build outputs,__pycache__, and tests.pyrefly static type checking:
[tool.pyrefly]configuration inpyproject.tomland apyrefly-typecheckGitHub Actions workflow.CONTRIBUTORSfile listing core contributors. Closes #2.Version stamp (
Version: 1.0) inLicense.txt.Full documentation site under
docs/(Sphinx + MyST): user guide, CLI / env-var / config-file references, architecture and extension manuals, published to GitHub Pages.Expanded test suite: per-module coverage for discovery providers, creators, activators, seeder, config, and utilities.
Changed
The cmd.exe activation script (
activate.bat) now carries arem generated by tn-venvheader comment.Rename
TNErrortoTnVenvError. The old name remains importable as a backwards-compatible alias.
Fixed
Fixes #6.
Fixes #7.
Fixes #4.
Fixes #5. For the time being, comment out this constant.
Options.from_mappingnormalises explicitNonevalues for list options (python,extra_search_dir,seed_packages,requirements,activators) to empty lists before filtering; previously the normalisation loop was dead code and could never run. Fixes #8.Fixes #9.
Type errors reported by pyrefly and mypy in the discovery providers:
winregattribute access inwindows_registry, and thesubprocess.runoverload, possibly-unbound variable, and regex match-group accesses inpy_launcher.Type errors reported by mypy in
cli,report, anddiscovery.python_info.Type errors reported by pyrefly in
cliandreport.Correct the
--dry-runexample output in the configuration guide. Fixes #1.Documentation build configuration (
docs/conf.py,docs/requirements.txt).Contact email in
SECURITY.md.
Removed
Unused import
dataclasses.fieldintn_venv/config/spec.py.
[0.1.0] — 2026-09-12
Initial public release.
Added
One-command creation: bare
tn-venvbuilds./.venvwith pip installed.Interpreter discovery from PATH, the Windows registry (PEP 514), the
pylauncher (PEP 397), uv-managed installs, and pyenv;-paccepts paths, versions (3.12), command names, and implementation specs (pypy3.10).Platform-native creators: Windows copies
venvlauncherredirectors with a real-binary + DLL fallback; POSIX symlinks with copy fallback.Seeding via
ensurepip, with pip pinning (--pip 24.0), index upgrades (--upgrade-pip),setuptools/wheel,--with PKGand-r FILEinstallation at creation time,--extra-search-dir, and--offline.Activation scripts for bash/zsh, cmd.exe, PowerShell, fish, csh/tcsh, Nushell, plus
activate_this.py.Layered configuration: CLI flags >
TN_VENV_*environment variables > config files (pyproject.toml,tn-venv.ini,.tn-venv.toml,setup.cfg) > defaults;--dry-runto inspect the result.--clear/--upgradelifecycle management with an inter-process file lock (stale-lock reclamation included).Python API:
tn_venv.create_venv()andtn_venv.cli_run().