python-poetry/poetry
github.com/python-poetry/poetry ↗ · 962 files · 5.1 MB
Poetry: Python packaging and dependency management made easy [ ][PyPI Releases] [ ][PyPI Releases] [ ][PyPI] [ ][Discord] Poetry helps you declare, manage and install dependencies of Python projects, ensuring you have the right stack everywhere.
Overview
Poetry is a Python CLI for developers managing project dependencies, virtual environments, package builds, and publication. At commit d4fd21e4711ae948f04e18a1736d9ee85b590e87, it combines poetry-core project models with its own constraint solver, source repositories, lockfile persistence, and installation executor. Its distinctive capabilities include portable dependency locking with groups and markers, direct Git/path/URL dependencies, and plugins that extend commands or mutate project state.
System map
The executable frontend is a Cleo application with lazy command loading and event-driven environment and installer setup. Factory connects poetry-core project parsing to Poetry-owned configuration, repositories, plugins, and lock storage. Resolution, operation planning, artifact preparation, and environment mutation occupy separate modules. Distribution is through Python packages and a console script; the inspected CI includes lock validation, build/install smoke checks, typing, and tests.
- Both the installed poetry console script and python -m poetry reach poetry.console.application.main, which constructs Application and calls run().
- Application uses Cleo, registers COMMAND listeners for logging, environment setup, and installer setup, and supplies lazy factories for the COMMANDS table.
- The package requires Python >=3.10,<4.0, pins poetry-core 2.5.0, and declares poetry.core.masonry.api as its PEP 517 build backend.
- Factory.create_poetry validates requires-poetry, delegates project creation to BaseFactory, then attaches Locker, Config, a repository pool, and optional generic plugins.
- Solver owns Provider and invokes mixology.resolve_version; VersionSolver maintains incompatibilities and a PartialSolution while propagating constraints and choosing package versions.
- Installer reads installed packages through InstalledRepository.load and delegates mutations to Executor, while Transaction supplies environment-specific operations.
- Persistent state includes project poetry.lock, global config.toml/auth.toml, optional project poetry.toml, artifact caches, and virtual environments under a configurable cache path.
- CI declares poetry check --lock, pipx-based build/Twine/install smoke checks, poetry run mypy, and poetry run pytest --integration -v.
Feature catalog
The inspected command table spans project creation, dependency changes, installation, packaging, diagnostics, environments, sources, and Poetry's own runtime. Dependency operations expose groups, extras, dry runs, lock-only execution, and synchronization. Build and publish integrate with Python packaging backends and package indexes. Plugin-provided commands and documented compatibility limits extend or qualify the built-in surface.
- new creates a project with a default src layout; init interactively creates pyproject.toml; version displays or changes the project version using PEP 440 values or bump rules.
- add accepts names, constraints, Git references/subdirectories, files, directories, and URLs; remove is registered alongside update, whose documented behavior preserves pyproject constraints while refreshing compatible locked versions.
- install supports group selection, extras, no-root/no-directory, bytecode compilation, and dry runs; sync additionally removes packages outside the desired lock state; lock preserves existing versions unless --regenerate is used.
- build selects direct poetry-core builders or isolated backend builds; publish targets PyPI by default or a configured repository and accepts certificates, dry-run, and skip-existing behavior.
- env exposes activate/info/list/remove/use, with activate printing shell instructions; run executes environment commands or configured script callables; python install/list/remove manages or discovers interpreters, including standalone builds.
- source add/show/remove manages project indexes with primary/supplemental/explicit priorities; config lists, sets, unsets, migrates, or applies local settings; cache clear/list manages cached repository entries.
- about reports Poetry/core versions; check validates project and lock consistency; debug info/resolve/tags diagnoses environments and resolution; show lists packages; search queries remote indexes with a documented PyPI browser requirement.
- self add/install/lock/remove/show/show plugins/sync/update manages Poetry's runtime and addons; project-required plugins can install under .poetry/plugins, while export and shell are documented plugin commands.
Data flow
CLI inputs become project models, configuration, and installer state before resolution or environment mutation begins. Package candidates arrive through prioritized indexes or direct origins and become solved packages with transitive metadata. Transactions filter that result against the selected environment and installed distributions. Artifact caches, build environments, lockfiles, project TOML, and upload requests form distinct output boundaries.
- --directory selects the working directory, while relative --project values resolve against it; Application passes the resulting project_directory into lazy Factory.create_poetry.
- Config.create loads global configuration and auth configuration, Factory merges project poetry.toml, and Config.get applies normalized POETRY_* environment overrides at lookup time.
- RepositoryPool sends source-qualified dependencies directly to their named repository; general lookup excludes explicit repositories and stops before supplemental sources once earlier sources produce candidates.
- Provider resolves Git/file/directory/URL dependencies through type-specific loaders, pins their dependency constraints to discovered versions, and caches them; index candidates are ordered using yanked, prerelease, and version properties.
- Solver aggregates the solved dependency graph into TransitivePackageInfo and simplifies group markers; Transaction evaluates selected groups, environment markers, and extras before comparing versions and source identity with installed packages.
- Chooser filters repository links by binary policy and environment wheel support; Executor retrieves cached/downloaded archives, builds non-wheels through Chef, validates available locked hashes, and sends wheels to WheelInstaller.
- Locker serializes package files, extras, Python requirements, lock format, and a content hash; AddCommand writes changed project TOML only after installer success and outside dry-run mode.
- Publisher resolves a target and authentication, configures Uploader, and passes publication options; Uploader converts each archive's metadata into legacy upload fields and streams file content through a multipart encoder.
Behavior traces
The source walks cover startup/project initialization, dependency installation, and add/lock persistence from command input to output. Startup establishes options and plugins before command-event setup injects the environment and installer. Installation branches on lock existence, freshness, and re-resolution before executing operations and optionally installing the root. Persistence follows explicit success and dry-run conditions, with separate refresh and regeneration paths.
- Startup: main → Application().run → _run → _configure_global_options → working-directory context → _load_plugins → Cleo dispatch; project plugin paths are added before application entry points load and activate.
- Project initialization: Application.poetry → Factory.create_poetry → _ensure_valid_poetry_version → BaseFactory.create_poetry → Locker/Config construction → create_pool → optional generic plugin activation.
- Command setup: COMMAND listeners run configure_env → EnvManager.create_venv → command.set_env, then configure_installer_for_event → configure_installer_for_command → Installer → command.set_installer; existing injected objects are retained.
- Install selection: InstallCommand.handle ensures project plugins unless disabled, validates incompatible options, configures extras/groups/dry-run/synchronization, then calls Installer.run; missing locks force update, while stale existing locks raise an error.
- Fresh resolution: Installer._do_install → Solver.solve → Solver._solve → resolve_version → VersionSolver.solve, whose loop propagates incompatibilities and chooses package versions; Solver then aggregates transitive metadata.
- Installation output: Transaction.calculate_operations → Installer._execute → Executor.execute → source-specific _install → WheelInstaller.install; after dependency success, InstallCommand calls EditableBuilder.build unless no-root, non-package mode, or dry-run applies.
- Add persistence: AddCommand.handle reads TOML → determines requirements → Factory.create_dependency → package.add_dependency → Locker.set_pyproject_data → installer update/whitelist/run; dependency success permits lock writing, then successful non-dry add writes pyproject.toml.
- Lock persistence: LockCommand.handle → Installer.lock disables operations → run; an existing non-regenerated lock enters _do_refresh and re-solves directory dependencies, otherwise update resolution writes through _write_lock_file → Locker.set_lock_data → _write_lock_data.
Testing signals
Tests and CI were inspected, not executed; test existence does not establish a passing result. The configured suite uses pytest with xdist and supporting mocking/coverage tools. Inspected tests exercise command guards, solver operation comparisons, authentication, lock serialization, plugin loading, and installation integrity. This is bounded coverage evidence, and mocked boundaries do not establish live index, keyring, Git, or platform behavior.
- pyproject declares pytest, pytest-cov, pytest-mock, pytest-randomly, pytest-xdist, and responses; pytest targets tests with logical-worker parallelism and network/skip_git_mock markers.
- The default test configuration excludes integration-marked tests unless --integration is supplied; the inspected CI explicitly invokes poetry run pytest --integration -v.
- Autouse fixtures replace user Config, keyring, configuration directories, and selected environment variables; Git.clone/Get_revision are mocked unless skip_git_mock applies, and the HTTP fixture uses responses.
- Install command tests assert dry-run state propagation and absence of editable builds, plus successful non-package-mode installation; the dry-run tests patch Installer.run and therefore do not verify actual dependency mutation.
- Executor tests check fixture-backed installation/hash metadata, rejection of filenames absent from the lock, and abortion of wheel installation after a mocked builtin-uninstall failure.
- Authenticator tests inspect mocked Authorization headers for URL/config credentials and HTTPS-to-HTTP restrictions; Locker tests assert unchanged contents after a version-comment-only difference and preservation of line endings.
- Plugin tests assert activation and invalid-type rejection, and mock addsitedir to check path handling; the solver test helper compares Transaction operations, including update endpoints and skipped state.
- Inspected CI defines Ubuntu/Windows/macOS runs across Python 3.10–3.15, mypy, build/install smoke checks, and external poetry-plugin-export tests; these are workflow declarations, not observed execution results.
Risk map
The main sensitive boundaries are plugin imports, backend execution, credentials, remote artifacts, and writes into Python environments. Source contains guards for credential scheme handling, archive hashes, and installation paths, alongside deliberate compatibility exceptions. Persistence and environment updates occur in separate steps, which deserves attention during failure-path changes. These are source observations or labeled hypotheses, not confirmed vulnerabilities or an exhaustive security audit.
- Plugin loading executes entry-point classes inside Poetry's process; existing project plugin paths are prepended and passed to addsitedir, which processes .pth files, unless application plugin loading is disabled.
- Package builds invoke backend subprocesses in ephemeral environments, and editable root builds can execute a configured build script via env.run; environment isolation here does not document an OS security sandbox.
- PasswordManager falls back to plaintext auth configuration when keyring is unavailable or disabled, with a warning; credential-storage changes therefore affect user-level sensitive state.
- Authenticator scopes configured credentials to host and chooses among same-host repositories by path affinity; it rejects HTTPS-configured credentials for non-HTTPS requests, but certificate configuration can explicitly disable verification.
- Archive hash validation is conditional on package.files; when present, missing filenames, unusable hash algorithms, or mismatches fail. Wheel RECORD content validation is disabled and validation issues are collected rather than immediately rejecting the wheel.
- Environment writes extend outside the checkout, including the system environment when virtualenv creation is disabled; WheelDestination checks target containment but permits overwriting existing files with a warning.
- Hypothesis: interrupted operations can leave partially changed environments or mismatched project/lock state because builtin uninstall removes without rollback, lock writing is direct, and add writes project TOML after installation/lock work. (inferred)
- Transaction deliberately treats some index source changes as equivalent: switching between PyPI and legacy sources can produce no update when installed source metadata is absent; provenance-sensitive changes must preserve or reassess this compatibility branch.
Extension points
Commands and plugins provide the clearest intentional extension surfaces. Repository construction, priority handling, and configuration normalization centralize additional integration choices. Build backend hooks and package-specific build constraints extend packaging behavior. Some interfaces expose mutable internal state, and plugin documentation explicitly warns that the public API is not stable.
- Built-in commands plug into COMMANDS and load_command: space-separated names map to dotted modules and title-cased Command classes, instantiated lazily.
- ApplicationPlugin uses the poetry.application.plugin entry-point group and registers command classes through command_loader.register_factory during activate.
- Generic plugins implement activate(poetry, io) and may manipulate project configuration and state; Factory loads and activates them after constructing the project repository pool.
- PluginManager discovers importlib.metadata entry points, checks classes against Plugin/ApplicationPlugin, instantiates them, and dispatches activation arguments; --no-plugins gates application loading.
- Project-required plugins use dependency syntax under tool.poetry.requires-plugins; ProjectPluginCache hashes the declaration with Python/Poetry versions and redirects purelib/platlib installation into .poetry/plugins.
- Factory.create_package_source selects PyPiRepository, LegacyRepository, or SinglePageRepository; RepositoryPool centralizes primary/supplemental/explicit priority and named-source lookup.
- Config.default_config and Config.get centralize settings and POETRY_* overrides, including package-specific installer.build-config-settings; new settings must account for normalization and nested lookup.
- BuildHandler selects BUILD_FORMATS or isolated backend builds; Factory converts per-package build constraints into dependency objects, and Chef passes constraints and config settings to wheel/editable builds.
Contributor handoff
Read the manifest, application, factory, and task-specific command before following changes into resolution or installation. Preserve distinctions among project roots, working directories, Poetry's runtime environment, and the target project's environment. Tests and CI were inspected, not executed; this brief provides bounded evidence rather than a passing baseline. For future authorized implementation work, select targeted tests and typing/style checks, with particular care around compatibility and external state.
- Start with pyproject.toml, Application's command loader/event setup, and Factory.create_poetry to locate runtime dependencies, dispatch conventions, and project assembly before editing a command.
- Preserve repository style: Ruff requires absolute imports, single-line imports, and future annotations; mypy checks src and tests in strict namespace-package mode.
- For command installation behavior, first inspect/run the relevant tests/console/commands test module in an authorized implementation task; dependency execution failures also need tests/installation coverage rather than only patched command tests. (inferred)
- Tests and CI were inspected, not executed in this investigation. Read tests/conftest.py before interpreting future results: defaults exclude integration tests and isolate user configuration, environment variables, and Git operations.
- Lock changes must retain format compatibility, the pre-2.3 dependency-group hash fallback, and line endings; existing tests assert avoiding version-comment-only rewrites and retaining EOL style.
- Keep success boundaries explicit: dependency operations precede updated-lock persistence, add writes TOML afterward, and install performs root editable building afterward; a root-build error can therefore follow successful dependency work.
- Do not confuse project source/config changes with self-update sources: self commands operate on Poetry's runtime files, and documented self update uses sources from its own configuration-directory project.
- Plugin internals have no stable public API; contributor guidance requires changed-behavior tests, applicable documentation, mypy, and pre-commit checks, whose configuration includes Ruff and workflow linting.
Languages
| Language | Files | Share of source |
|---|---|---|
| Python | 451 | 80% |
| TOML | 110 | 20% |
| JSON | 106 | docs/config |
| Markdown | 31 | docs/config |
| HTML | 26 | docs/config |
| YAML | 15 | docs/config |
| Shell | 2 | 0% |
| C | 1 | 0% |
Start reading here
Probable source entrypoint: pyproject.toml — inferred from the directory tree and manifest, not the upstream docs.
Source roots
src/ (195 files)
Repository structure
| Top-level path | Files |
|---|---|
tests/ | 726 |
src/ | 195 |
docs/ | 16 |
.github/ | 15 |
Docs found: README.mddocs/_index.mddocs/basic-usage.mddocs/building-extension-modules.mddocs/cli.mddocs/community.mddocs/configuration.mddocs/contributing.mddocs/dependency-specification.mddocs/faq.md
Package
pyproject.toml — true v2.5.1, 16 declared dependencies.
pre-commitcoverage##dulwichresponsesjaraco-classespytestpytest-covpytest-mockpytest-randomlypytest-xdist
CI & config entrypoints
README.md— Repository readme
Structural health signals
Average score 7.1/10 across 96 analyzed source files; 16 hotspots surfaced.
src/poetry/puzzle/provider.py— 1134 lines, score 5.7 (Large file, Deep nesting)src/poetry/installation/executor.py— 985 lines, score 5.7 (Large file, Deep nesting)src/poetry/inspection/lazy_wheel.py— 746 lines, score 5.9 (Large file, Deep nesting)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
d4fd21e4711a2026-09-27 — tests: make flaky test more robust (#11107)d4e206e68dce2026-09-27 — docs: fix the nonexistent IsDirectoryError name in a comment (#11068)4987d287330d2026-09-27 — Fix conditional assertion in build clean test (#11085)b486b0f385b92026-09-27 — fix: do not fail when uninstalling a path dependency whose source is gone (#11093)135631bbb93e2026-09-27 — docs: clarify `solver.min-release-age-exclude` and `solver.min-release-age-exclude-source` configuration (#11087)