Require version in [project] table in non-package mode - #11028
vikasvardhanv wants to merge 1 commit into
Conversation
Per PEP 621, a [project] table that declares a name must also declare a
version, either statically or via [project.dynamic]. In package mode this
is already enforced by the core schema validation, but in non-package mode
the requirement was skipped, so a pyproject.toml such as:
[project]
name = "test"
dependencies = ["mkdocs"]
[tool.poetry]
package-mode = false
was silently accepted by `poetry check`, `poetry lock`, etc.
Add a validation check in Factory.validate that flags a [project] table
with a name but no version (and no "version" in dynamic) when running in
non-package mode. The check is gated on non-package mode so that package
mode does not emit a duplicate error alongside the existing core message.
Closes python-poetry#10032
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/poetry/factory.py" line_range="391" />
<code_context>
+ poetry_config.get("package-mode", True) is False
+ and project.get("name") is not None
+ and "version" not in project
+ and "version" not in project.get("dynamic", [])
+ ):
+ results["errors"].append(
</code_context>
<issue_to_address>
**issue (bug_risk):** Factory.validate() raises TypeError when [project].dynamic is set to a non-iterable value such as null or an integer, instead of returning the schema validation errors for the malformed project configuration.
**Triggers:** When package mode is false, the project has a name but no version, and dynamic is present with a non-iterable value.
**Suggested fix:** Check that `project.get("dynamic")` is a list before testing membership, or use a type-safe default such as `dynamic = project.get("dynamic") if isinstance(project.get("dynamic"), list) else []`.
```suggestion
and "version" not in (project.get("dynamic") if isinstance(project.get("dynamic"), list) else [])
```
</issue_to_address>Sourcery assessment
Approval pending. 1 finding to address first.
Blocking findings: src/poetry/factory.py:391
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| poetry_config.get("package-mode", True) is False | ||
| and project.get("name") is not None | ||
| and "version" not in project | ||
| and "version" not in project.get("dynamic", []) |
There was a problem hiding this comment.
issue (bug_risk): Factory.validate() raises TypeError when [project].dynamic is set to a non-iterable value such as null or an integer, instead of returning the schema validation errors for the malformed project configuration.
Triggers: When package mode is false, the project has a name but no version, and dynamic is present with a non-iterable value.
Suggested fix: Check that project.get("dynamic") is a list before testing membership, or use a type-safe default such as dynamic = project.get("dynamic") if isinstance(project.get("dynamic"), list) else [].
| and "version" not in project.get("dynamic", []) | |
| and "version" not in (project.get("dynamic") if isinstance(project.get("dynamic"), list) else []) |
Closes #10032
Bug
A
pyproject.tomlusing the PEP 621[project]table with anamebut noversion, in non-package mode, was silently accepted:poetry checkreportedAll set!(andpoetry lock/syncproceeded) even though PEP 621 requires aversionin the[project]table unless it is listed in[project.dynamic].Root cause
Factory.validate()relies on the core schema validation to enforce theversionrequirement, but that check only fires in package mode (Either [project.version] or [tool.poetry.version] is required in package mode.). In non-package mode the requirement was never checked, so a[project]table with a name but no version passed validation.Fix
Add a targeted check in
Factory.validate()that, in non-package mode, flags a[project]table which declares anamebut has neither a staticversionnorversionlisted indynamic.The check is gated on non-package mode so that package mode continues to emit only the existing core message (no duplicate error). Valid cases remain valid:
versiondeclared statically -> OKversionlisted in[project.dynamic]-> OK[tool.poetry]-only projects (no[project]table) -> unaffectedTesting
test_validate_non_package_mode_requires_project_version(parametrized: missing version -> error; static version -> OK; dynamic version -> OK) andtest_validate_package_mode_missing_version_not_duplicated(ensures no duplicate error in package mode). The missing-version case fails onmainand passes with this change.pytest tests/test_factory.py tests/console/commands/test_check.py tests/json/test_schema.py-> 66 passed.ruff check,ruff format --check, andmypypass on the changed files.