The problem
A dependency declared in a pyproject.toml or in a PEP 723 # /// script block gets four things reported about it — a new version, staleness, a yank, and a vulnerability — and not one of them can be steered for a single reference. A requirements.txt pin one line away in the same repository takes the full marker language.
The gap is widest for the warnings. A pin left on a yanked release is reported at every run, a version an advisory names is reported at every run, and a project that has assessed either and decided to live with it has no way to say so. The staleness warning now reaches dependencies declared without an exact pin as well, so it reaches more references than before and remains just as unsilenceable.
The file can hold a marker
Probed 2026-08-15:
- TOML accepts a trailing comment on an array element, and
tomllib parses the spec back without it: "humanize==4.15.0", # update-time: ignore reads as humanize==4.15.0.
- uv reads a
# /// script block whose dependency array carries such a comment without complaint: a block holding "humanize==4.10.0", # update-time: ignore reported both its dependencies normally under uv tree --script.
Freezing works too, which is not obvious: uv resolves the versions but Update-time writes the pins, and uv lock --upgrade leaves the pyproject.toml byte-identical. A pin Update-time declines to rewrite therefore stays where it is, because the pin itself constrains what uv resolves.
Which scopes hold something back
| Scope |
Holds something back? |
bare ignore, ignore[update] |
yes — Update-time writes these pins itself, so declining to rewrite one freezes it |
stale, bare and with a threshold |
yes — the staleness check is Update-time's own |
yanked |
yes for an exact pin, redundant for a dependency that pins no version |
vulnerable, with an identifier or a level |
yes for an exact pin, redundant for a dependency that pins no version |
a bound, allow[update<3.13] or ignore[minor-update] |
not as things stand — see below |
cooldown |
not as things stand — see below |
hash-drift |
never — no Python reference carries a hash |
A bound filters the candidate versions before the highest is picked, and Update-time never sees candidates here: uv tree --outdated reports one version per dependency, the latest. A bound could only accept or reject that single version, so a reference on 3.12.8 bounded below 3.13 would stay on 3.12.8 rather than advancing to a freshly published 3.12.9, which is not what the bound promises everywhere else. A per-reference cooldown has the same problem, and uv applies the cooldown per run through exclude-newer rather than per dependency.
Both become possible if Update-time resolves these pins against PyPI itself — get_latest_version already takes a version bound and a cooldown — but that changes who chooses the version for a pyproject.toml, which is a decision of its own rather than a detail of this issue.
Scope
- Read a marker for a dependency in a
pyproject.toml and in a # /// script block, and honour the scopes marked "yes" above.
- Report the rest as redundant or invalid, each in its own words, as the other file kinds already do.
- Report a recognised marker and what it held back at
DEBUG, as elsewhere.
The design constraint
Placement decides how far this reaches. A trailing comment works only when the array puts each dependency on a line of its own, and dependencies = ["a==1", "b==2"] gives neither dependency a line. A [tool.update-time] table keyed by dependency name has no such limit: it reaches a single-line array, and it is the idiomatic place for tool configuration in a Python project. It costs the property that makes the comment form readable, though, since the marker no longer sits beside the reference it governs, and a PEP 723 block would need the same table inside it.
The two file kinds are read by one reader, so whichever form is chosen has to work commented out inside a # /// script block as well as in plain TOML.
Out of scope
- Bounds and a per-reference cooldown, for the reason above.
- Markers in a
package.json, which is strict JSON and needs a placement of its own.
- Changing which dependencies are updated, or how uv resolves them.
Documentation
- The per-type "Markers" answer under "Python dependencies", which says these dependencies take none.
- The per-type "Yanked dependencies" and "Vulnerable dependencies" answers, which each say the pin takes no marker to silence the warning.
- "Where to put a marker", which says a dependency updated through uv, npm, or pnpm takes no marker, and the overview under "What is updated", which says a delegated dependency takes no marker.
- The redundant-marker and invalid-marker sections, for the scopes that decide nothing here.
- A changelog entry under
[Unreleased].
Open questions
- Trailing comment,
[tool.update-time] table, or both? The comment cannot serve a single-line array, and the table cannot sit beside the reference.
- What does a marker mean for a dependency the file declares more than once — in the project's array and in a dependency group, say? The comment form answers per line; a table answers per name.
- Should
yanked and vulnerable on a dependency that pins no version be reported as redundant here, in the same words a requirements.txt requirement without an exact pin already gets?
The problem
A dependency declared in a
pyproject.tomlor in a PEP 723# /// scriptblock gets four things reported about it — a new version, staleness, a yank, and a vulnerability — and not one of them can be steered for a single reference. Arequirements.txtpin one line away in the same repository takes the full marker language.The gap is widest for the warnings. A pin left on a yanked release is reported at every run, a version an advisory names is reported at every run, and a project that has assessed either and decided to live with it has no way to say so. The staleness warning now reaches dependencies declared without an exact pin as well, so it reaches more references than before and remains just as unsilenceable.
The file can hold a marker
Probed 2026-08-15:
tomllibparses the spec back without it:"humanize==4.15.0", # update-time: ignorereads ashumanize==4.15.0.# /// scriptblock whose dependency array carries such a comment without complaint: a block holding"humanize==4.10.0", # update-time: ignorereported both its dependencies normally underuv tree --script.Freezing works too, which is not obvious: uv resolves the versions but Update-time writes the pins, and
uv lock --upgradeleaves thepyproject.tomlbyte-identical. A pin Update-time declines to rewrite therefore stays where it is, because the pin itself constrains what uv resolves.Which scopes hold something back
ignore,ignore[update]stale, bare and with a thresholdyankedvulnerable, with an identifier or a levelallow[update<3.13]orignore[minor-update]cooldownhash-driftA bound filters the candidate versions before the highest is picked, and Update-time never sees candidates here:
uv tree --outdatedreports one version per dependency, the latest. A bound could only accept or reject that single version, so a reference on3.12.8bounded below3.13would stay on3.12.8rather than advancing to a freshly published3.12.9, which is not what the bound promises everywhere else. A per-referencecooldownhas the same problem, and uv applies the cooldown per run throughexclude-newerrather than per dependency.Both become possible if Update-time resolves these pins against PyPI itself —
get_latest_versionalready takes a version bound and a cooldown — but that changes who chooses the version for apyproject.toml, which is a decision of its own rather than a detail of this issue.Scope
pyproject.tomland in a# /// scriptblock, and honour the scopes marked "yes" above.DEBUG, as elsewhere.The design constraint
Placement decides how far this reaches. A trailing comment works only when the array puts each dependency on a line of its own, and
dependencies = ["a==1", "b==2"]gives neither dependency a line. A[tool.update-time]table keyed by dependency name has no such limit: it reaches a single-line array, and it is the idiomatic place for tool configuration in a Python project. It costs the property that makes the comment form readable, though, since the marker no longer sits beside the reference it governs, and a PEP 723 block would need the same table inside it.The two file kinds are read by one reader, so whichever form is chosen has to work commented out inside a
# /// scriptblock as well as in plain TOML.Out of scope
package.json, which is strict JSON and needs a placement of its own.Documentation
[Unreleased].Open questions
[tool.update-time]table, or both? The comment cannot serve a single-line array, and the table cannot sit beside the reference.yankedandvulnerableon a dependency that pins no version be reported as redundant here, in the same words arequirements.txtrequirement without an exact pin already gets?