Skip to content

build(docs): let Dependabot reach the patched flask, starlette and mysql-connector-python - #5250

Open
ocelotl wants to merge 5 commits into
open-telemetry:mainfrom
ocelotl:docs_requirements_ceilings
Open

ocelotl wants to merge 5 commits into
open-telemetry:mainfrom
ocelotl:docs_requirements_ceilings

Conversation

@ocelotl

@ocelotl ocelotl commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Closes #5249, which explains the problem this fixes from the version specifier arithmetic up.

Five lines in docs-requirements.txt, carrying 6 open Dependabot alerts that no amount of Dependabot configuration can reach, because the pins forbid every version that would fix them.

Definitions

  • A ceiling is the upper bound a version specifier places on a dependency. flask~=2.0 carries a ceiling of <3.0, because the compatible-release operator ~= pins the last component given and allows anything above it within the same preceding component.

  • A floor is the lower bound. flask~=2.0 carries a floor of >=2.0.

  • The patched version is the earliest release of a package that resolves a given advisory, reported by GitHub as first_patched_version. For example, the patched version for the advisory behind alert 716 is flask 3.1.3.

The mechanism

A ceiling below the patched version is not a delay, it is a permanent block. Dependabot proposes a version that satisfies the declared specifier. When no such version resolves the advisory, it proposes nothing at all, and the alert stays open with no pull request behind it and no indication that the pin is the reason.

This differs from the other causes in this series. It is not a parsing failure, as with the path based dependencies in #5221 and #5223, and not a suppression recorded by closing a pull request, as in #5235. Dependabot is working correctly here and is being told the fix is not allowed.

The three pins

package was now alerts advisory severity patched version
starlette ~=0.50 >=1.3.1,<1.8 1105, 1136, 1137, 1139 2 high, 2 medium 1.0.1, 1.1.0, 1.3.1
mysql-connector-python ~=8.0 >=9.1.0 312 high 9.1.0
flask ~=2.0 >=3.1.3 716 low 3.1.3

starlette is the largest of the three. Four separate advisories resolve across 1.0.1, 1.1.0 and 1.3.1, and ~=0.50 means >=0.50,<1.0, so it excludes all three. 1.3.1 is the floor that clears every one of the four, which is why the new specifier is not 1.0.1.

Each change is its own commit, so any one of the three can be dropped without touching the others, with the caveat in The indirect pins that had to move with flask.

Why I read these ceilings as stale rather than deliberate

A ceiling in a docs requirements file is often there on purpose, holding the documentation build against an API the instrumentation is known to work with. That would be a reason to leave it and dismiss the alerts instead, so I checked each one against what the instrumentation itself declares.

None of the three is corroborated:

instrumentation/opentelemetry-instrumentation-flask/.../package.py:5      _instruments = ("flask >= 1.0",)
instrumentation/opentelemetry-instrumentation-starlette/.../package.py:5  _instruments = ("starlette >= 0.13",)
instrumentation/opentelemetry-instrumentation-mysql/.../package.py:7      _instruments = ("mysql-connector-python >= 8.0",)

Every one declares a floor and no ceiling. That is a claim each package makes about itself rather than a test, so I also checked which versions CI actually exercises:

package versions CI exercises covers the patched version
flask 2.1.3, 2.2.0, 3.0.2, >=3.1.0 in test-requirements-0..3.txt yes, >=3.1.0 resolves to 3.1.3
mysql-connector-python 8.3.0, 9.0.0, 26.7.0 in test-requirements-0..2.txt bracketed: 9.1.0 sits between tested 9.0.0 and 26.7.0
starlette 0.13.8 and 0.46.2 in test-requirements.{oldest,latest}.txt no, nothing tests 1.x at all

For flask and mysql-connector-python the declaration is backed by tests that really run, so raising those two lines moves to versions the repository already exercises. For starlette it is not: the highest version any test touches is 0.46.2, below the 1.0 boundary, so the entire range this change moves into is untested.

Because of that gap the starlette line keeps a ceiling, added in a fifth commit. This is the one place in this pull request where a ceiling is the right answer, and the reasoning is in The starlette ceiling, which is deliberate.

One further fact, which this pull request does not address: 5 of the 9 starlette alerts here are on instrumentation/opentelemetry-instrumentation-fastapi/test-requirements.oldest.txt. Only 4 are on docs-requirements.txt.

The indirect pins that had to move with flask

The flask change does not resolve on its own, which I found when the docs job failed in ten seconds against the forty it normally takes. docs-requirements.txt ends with a block headed # indirect dependency pins holding markupsafe==2.0.1 and itsdangerous==2.0.1, the versions flask 2 needed. flask 3.1.3 requires markupsafe >= 2.1.1 and itsdangerous >= 2.2.0, so the combination is unsatisfiable, and uv says so plainly:

error: No solution found when resolving dependencies
  cause: Because flask>=3.1.3 depends on markupsafe>=2.1.1 and you require flask>=3.1.3,
         we can conclude that you require markupsafe>=2.1.1. And because you require
         markupsafe==2.0.1, we can conclude that your requirements are unsatisfiable.

A fourth commit turns both into floors at the versions flask asks for. They become floors rather than bumped exact pins because an exact pin is a ceiling, and a ceiling on a transitive dependency blocks Dependabot in precisely the way the three this pull request removes did.

The consequence for dropping commits: the flask commit and this one travel together. The starlette and mysql-connector-python commits are independent of both.

The starlette ceiling, which is deliberate

starlette>=1.3.1,<1.8 is the one line here that keeps an upper bound, and it is not the same kind of bound as the three being removed.

Both pyproject.toml files in this repository declare requires-python = ">=3.10". starlette 1.0.0 through 1.7.0 require Python >=3.10, and 1.8.0 onward require >=3.11. Left unbounded, the selected version depends on the interpreter: the docs job runs 3.11 and picked 1.10.0, while a 3.10 environment backs off to 1.7.0. Nothing fails in either case, but the documentation was being built against a starlette that part of the supported Python range cannot install.

<1.8 makes both pick 1.7.0. All four advisories are fixed in 1.0.1, 1.1.0 and 1.3.1, so no alert coverage is lost, which I checked by testing every release the specifier permits against every advisory's vulnerable range.

The distinction worth drawing: the three ceilings this pull request removes were floors of their day that nobody revisited, and they encoded nothing checkable. This one states a fact that can be checked against requires-python, and it should be raised when the repository drops Python 3.10.

The decision I made that you may want to reverse

I removed the ceilings on flask and mysql-connector-python rather than raising them. flask>=3.1.3 instead of flask>=3.1.3,<4.0. starlette is the exception, for the reason given above.

The argument for removing them: a ceiling is what caused this, and raising it to <4.0 schedules the same problem for whenever flask 4 ships with a security fix. The instrumentation declares no ceiling, so the documentation build tracking whatever the instrumentation supports is the consistent position. docs-requirements.txt affects only the documentation build, ships in no artifact, and a break shows up immediately in CI rather than reaching a user.

The argument against: a major release can break the documentation build with no warning, and confluent-kafka>= 1.8.2,< 3.0.0 and kafka-python>=2.0,<3.0 in the same file show the repository is willing to carry explicit major ceilings.

I went with removal because it is the only one of the two that stops this recurring, but the alternative is one character per line and I will change it on request.

What I could and could not verify

Verified:

  • The new floors clear every one of the 6 alerts. I read each alert's vulnerable_version_range and first_patched_version from the API rather than inferring them: < 3.1.3 for flask, < 9.1.0 for mysql-connector-python, and <= 1.0.0, < 1.1.0, < 1.1.0 and >= 0.4.1, < 1.3.1 for the four starlette advisories.

  • Each instrumentation's _instruments declaration carries no upper bound, quoted above with its line number.

  • The diff is five lines in one file. No other file changes.

  • The narrowed starlette specifier clears all four of its advisories. I tested each release it permits, 1.3.1 through 1.7.0, against each advisory's vulnerable range, and none of the seven remains vulnerable to any of the four.

  • The file resolves on Python 3.10 as well as 3.11. uv pip compile --python-version=3.10 produces 132 pins with starlette at 1.7.0.

  • The documentation builds. I had listed this as unverified when I opened the pull request, and the docs job then failed, which is what surfaced the indirect pin conflict above. With the fourth commit it passes in 41 seconds, against 45 seconds on an unrelated pull request of mine in the same fork and 38 seconds on ci: add a Dependabot configuration #5236, so the build is doing its normal work rather than exiting early.

  • The whole file resolves. uv pip compile --python-version=3.11 docs-requirements.txt produces 129 pins, with flask at 3.1.3, starlette at 1.10.0, mysql-connector-python at 26.10.0, markupsafe at 3.0.4 and itsdangerous at 2.2.0.

Not verified: I did not confirm that merging this makes Dependabot open the pull requests. It removes the stated obstacle, but the behaviour can only be observed once it is on the default branch.

Worth a reviewer's eye rather than mine: the resolution lands mysql-connector-python at 26.10.0, a long way above both the 8.x the pin allowed and the 9.1.0 the advisory needs, because nothing now caps it. If that jump is unwelcome, a ceiling on that one line is the targeted answer and still clears alert 312.

flask~=2.0 means >=2.0,<3.0, and the advisory behind alert 716 is fixed
in 3.1.3, so Dependabot cannot propose a version that both resolves it
and satisfies the pin. The ceiling is not a statement about what the
instrumentation supports, which declares flask >= 1.0 with no upper
bound in
instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/package.py.
…rements

mysql-connector-python~=8.0 means >=8.0,<9.0, and the advisory behind
alert 312, rated high, is fixed in 9.1.0, so the pin excludes every
version that resolves it. The instrumentation declares
mysql-connector-python >= 8.0 with no upper bound in
instrumentation/opentelemetry-instrumentation-mysql/src/opentelemetry/instrumentation/mysql/package.py,
so the ceiling came from the docs requirements alone.
starlette~=0.50 means >=0.50,<1.0, and the four advisories behind alerts
1105, 1136, 1137 and 1139, two of them rated high, are fixed across
1.0.1, 1.1.0 and 1.3.1. Every one of those is excluded by the pin, so
this is the single largest ceiling of the three. 1.3.1 is the floor that
clears all four. The instrumentation declares starlette >= 0.13 with no
upper bound in
instrumentation/opentelemetry-instrumentation-starlette/src/opentelemetry/instrumentation/starlette/package.py.
The flask change in 2cdad82 does not resolve on its own. flask 3.1.3
requires markupsafe >= 2.1.1 and itsdangerous >= 2.2.0, while the
indirect dependency pins at the end of docs-requirements.txt held both
at 2.0.1, which is what flask 2 needed. uv rejected the combination
outright:

  Because flask>=3.1.3 depends on markupsafe>=2.1.1 and you require
  flask>=3.1.3, we can conclude that you require markupsafe>=2.1.1.
  And because you require markupsafe==2.0.1, we can conclude that your
  requirements are unsatisfiable.

Both become floors at the versions flask asks for rather than exact
pins, because an exact pin is a ceiling and a ceiling on a transitive
dependency blocks Dependabot the same way the three it replaces did.
89c2beb left starlette unbounded above, and the resolution then
depends on the interpreter. Both pyproject.toml files here declare
requires-python = ">=3.10", starlette 1.0.0 through 1.7.0 require
Python >= 3.10, and 1.8.0 onward require >= 3.11. So the docs job on
3.11 selected 1.10.0 while a 3.10 environment would select 1.7.0, and
the documentation was built against a starlette that part of the
supported range cannot install.

A ceiling of < 1.8 makes both select 1.7.0. It still clears all four
advisories, which are fixed in 1.0.1, 1.1.0 and 1.3.1, so no alert
coverage is lost.

Unlike the three ceilings this branch removes, this one is not a stale
floor of the day. It states a fact that is checkable against
requires-python, and it should be raised when the repository drops
Python 3.10 rather than left to rot.
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Pull request dashboard status

Waiting on reviewers · refreshed 2026-10-30 15:38 UTC

Review the latest changes.

Also blocked by: Merge conflicts.

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Skip Changelog PRs that do not require a CHANGELOG.md entry

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

6 security alerts cannot be fixed because docs-requirements.txt forbids every patched version

1 participant