Skip to content

fix: synchronize PyPI and pixi dependency version bounds#378

Merged
mkitti merged 4 commits into
mainfrom
sync-dep-bounds
Jun 3, 2026
Merged

fix: synchronize PyPI and pixi dependency version bounds#378
mkitti merged 4 commits into
mainfrom
sync-dep-bounds

Conversation

@mkitti

@mkitti mkitti commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Synchronize PyPI and pixi dependency version bounds

An alternative to #377. Rather than loosening the pixi caps to match the un-capped PyPI list, this PR tightens the PyPI list to match the pixi caps, so both dependency surfaces carry identical, SemVer-aware bounds.

Approach

Bounds follow SemVer principles — cap = major + 1 for >=1.0 packages, cap = minor + 1 for 0.x packages (where 0.x treats the minor as the breaking boundary) — with two documented exceptions that use a non-SemVer scheme and are intentionally left uncapped.

Changes

  • [project.dependencies] (PyPI): added upper caps to alembic, atlassian-python-api, authlib, cachetools, click, fastapi, itsdangerous, loguru, pandas, pydantic, pydantic-settings, sqlalchemy, uvicorn, matching the caps already present in the pixi block.
  • [tool.pixi.dependencies] (conda): added the click cap and the missing psycopg2-binary >=2.9.10,<3 entry.
  • First-party caps: x2s3 >=1.2.0,<2 and py-cluster-api >=0.6.0,<0.7 (PyPI-only; not on conda-forge, so they appear only in the PyPI block).
  • pixi.lock regenerated for the new pixi bounds.

Scheme exceptions (intentionally uncapped)

  • cryptography — uses a rolling-major scheme (bumps the major on every feature release, 41→48); a <next-major> cap would break on the next normal release. Left at >=41.0.0.
  • packaging — CalVer (24.0, 25.0, 26.x), not SemVer. Left at >=24.0.

Scope / what this PR does NOT do

  • No FastAPI or Starlette version change. fastapi stays at >=0.119.1,<0.120 and Starlette stays at the currently-locked 0.48.0. Bumping FastAPI (which drops the old starlette <0.49 cap and unblocks Starlette 1.x) is a behavioral upgrade and is deferred to its own follow-up PR.
  • httpx kept at >=0.28.1,<0.29 (unchanged).

After this, the two blocks list identical bounds for every shared runtime package; the only divergences are pixi-only build tooling (python/pip/nodejs) and the PyPI-only first-party packages.

🤖 Generated with Claude Code

mkitti and others added 3 commits June 2, 2026 17:40
Add SemVer-aware upper bounds to [project].dependencies to match the
caps already present in [tool.pixi.dependencies], and add the missing
psycopg2-binary entry to the pixi block.

- cap = major+1 for >=1.0 packages, minor+1 for 0.x packages
- cryptography (rolling-major) and packaging (CalVer) left uncapped
- httpx kept at <0.29
- x2s3 and py-cluster-api (first-party, PyPI-only) left uncapped for now

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Apply SemVer-aware upper bounds to the first-party PyPI-only deps:
x2s3 >=1.2.0,<2 (SemVer 1.x) and py-cluster-api >=0.6.0,<0.7 (0.x).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regenerated by the pre-push pixi solve after adding psycopg2-binary and
the click cap to [tool.pixi.dependencies].

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@allison-truhlar

Copy link
Copy Markdown
Collaborator

I think this makes sense to set the major version caps to be equal across PyPI and pixi requirements rather than removing the upper bound in the PyPI constraints.

I didn't check all the packages, but looking at crypotography, I don't think it uses only major versions, so potentially it's also a candidate for using a major version cap - see for example several security patches in major version 46: https://cryptography.io/en/latest/changelog/#v46-0-7, and each major version appears to have one or more items clearly marked as backwards incompatible in the change log. packaging does appear to use CalVer: https://packaging.pypa.io/en/stable/changelog.html

I have a larger question which perhaps comes from me not fully understanding how pixi and PyPI dependencies work in the pyproject.toml. I understand that we want to use conda-forge packages whenever possible to avoid licensing issues, and that this is possible via

[tool.pixi.workspace]
channels = ["conda-forge"]

But are the [tool.pixi.dependencies] only used in the dev environment? If so, why are we maintaining this list separate from the the dependencies list? The Fileglancer version we deploy is installed from PyPI, which will be using the dependencies in the dependencies list, so is there any advantage to having the dev environment packages being sourced from conda-forge, especially when there is this greater risk for dependency drift between dev and production if we add additional dependencies? Again, this is very likely me missing something.

@mkitti

mkitti commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

I didn't check all the packages, but looking at crypotography, I don't think it uses only major versions, so potentially it's also a candidate for using a major version cap - see for example several security patches in major version 46: https://cryptography.io/en/latest/changelog/#v46-0-7, and each major version appears to have one or more items clearly marked as backwards incompatible in the change log. packaging does appear to use CalVer: https://packaging.pypa.io/en/stable/changelog.html

I posted a full analysis of all the packages of this on the comment in #377:
#377 (comment)

@mkitti

mkitti commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

I have a larger question which perhaps comes from me not fully understanding how pixi and PyPI dependencies work in the pyproject.toml. I understand that we want to use conda-forge packages whenever possible to avoid licensing issues, and that this is possible via

[tool.pixi.workspace]
channels = ["conda-forge"]

But are the [tool.pixi.dependencies] only used in the dev environment? If so, why are we maintaining this list separate from the the dependencies list? The Fileglancer version we deploy is installed from PyPI, which will be using the dependencies in the dependencies list, so is there any advantage to having the dev environment packages being sourced from conda-forge, especially when there is this greater risk for dependency drift between dev and production if we add additional dependencies? Again, this is very likely me missing something.

There are two package repositories that we use, PyPI and conda-forge. There are not particularly differences in licensing issues between them. The main controversy is if we would want to use packages from Anaconda, Inc's default channels, which we do not, although we do now have a subscription that would permit us to do so.

  • PyPI is a Python-only package repository organized by the Python Packaging Authority, although some Python packages include "wheels" which may contain foreign artifacts and binaries
  • As far as I can tell, we only package fileglancer for PyPI. While we could and probably should also package fileglancer for conda-forge, via pixi build we have not done so yet.
  • conda-forge is not specific to Python. It contains packages from a wide array of languages. Non-Python packages are not packaged into wheels but are usually in their own conda-forge packages.
  • I personally prefer to install Python packages via conda-forge because I think it makes more sense to package non-python packages as their own packages rather than using wheels. This makes it easier for multiple Python packages to depend on the same non-Python components rather than duplicating them in a possibly incompatible manner.
  • Because Python and conda-forge are distinct packaging systems, there is not a one-to-one correspondence between PyPI and conda-forge packages in all cases. Even if there is a one-to-one correspondence in name, there versions might not necessarily line up with each other.
  • While we have chosen to deploy from our PyPI package via pip, my recommendation is actually that we should deploy via pixi directly.
  • We probably could eliminate some redundant entries in our pyproject.toml because pixi is able to resolve some of the differences between PyPI and conda-forge.

@allison-truhlar

Copy link
Copy Markdown
Collaborator

crypotography, I don't think it uses only major versions, so potentially it's also a candidate for using a major version cap - see for example several security patches in major version 46: https://cryptography.io/en/latest/changelog/#v46-0-7, and each major version appears to have one or more items clearly marked as backwards incompatible in the change log

@mkitti I did see the full analysis in #377. My point was I checked the ones that Claude decided to leave uncapped, since these would be the exceptions to the general rule of capping at the major version change. I think crypotography would be a candidate for leaving a major cap. Looks at the changelog in the docs, it does seem that each major version has items marked as backwards incompatible with the previous version.

@allison-truhlar

Copy link
Copy Markdown
Collaborator
  • We probably could eliminate some redundant entries in our pyproject.toml because pixi is able to resolve some of the differences between PyPI and conda-forge.

How do you determine which ones you can remove from tool.pixi.dependencies?

@mkitti

mkitti commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Analysis: Are the [tool.pixi.dependencies] conda overrides necessary?

I investigated whether the duplication between [project.dependencies] and [tool.pixi.dependencies] in pyproject.toml is necessary, by running a controlled experiment.

Experiment

Created a branch that removed all entries from [tool.pixi.dependencies] that were already listed in [project.dependencies] (keeping only python, pip, and nodejs). Ran pixi clean && pixi install on both branches and compared pixi list output.

How pixi handles [project.dependencies]

Per the pixi docs, pixi reads [project.dependencies] and automatically treats them as PyPI dependencies. With its conda-first strategy, if the same package also appears in [tool.pixi.dependencies], the conda-forge version wins.

Results

The [tool.pixi.dependencies] overrides make a real difference:

Source of installation:

Package With conda overrides Without conda overrides
alembic, fastapi, pandas, pydantic, sqlalchemy, uvicorn, etc. conda-forge PyPI

Version differences (conda-forge lags PyPI):

Package conda version PyPI version
authlib 1.6.9 1.7.2
cryptography 46.0.7 48.0.0
pydantic 2.12.5 2.13.4
sqlalchemy 2.0.49 2.0.50
numpy 2.4.3 2.4.6

Additional transitive conda dependencies pulled in only when using conda overrides: libopenblas, libpq, libgfortran, greenlet, uvloop, watchfiles, websockets, krb5, openldap, lxml, and more — because conda packages declare their own native library dependencies.

Notable example: pandas from conda is only 13.48 MiB vs 37.95 MiB from PyPI, because the conda version links against the shared conda-provided OpenBLAS instead of bundling its own.

Conclusion

The duplication is intentional and meaningful. Removing the conda overrides silently falls back to PyPI wheels, losing:

  • conda-vetted binary builds linked against shared native libraries
  • Consistent native dependency management (BLAS, libpq, etc.)

The version bounds do need to stay synchronized between the two sections (which is what this PR does) to avoid solver conflicts.

…installed in fileglancer pypi build

- the current fileglancer package available on pypi has cryotgraphy 48.0.0. updated both the pypi dependencies and the dev environment dependencies to require >=48, <49. cryptography can have breaking changes across major releases.
@allison-truhlar

Copy link
Copy Markdown
Collaborator

Okay - after looking into whether there was some option available to have pixi automatically convert the PyPI deps into conda-forge packages in the dev environment, it seems not. prefix-dev/pixi#5981 (comment) It does look like it might newly exist for builds, in a preview pixi-build feature.

@allison-truhlar allison-truhlar self-requested a review June 3, 2026 19:27

@allison-truhlar allison-truhlar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an lower and upper bound on crypotraphy, per my earlier comment. If that looks okay to you, @mkitti, then this looks good to me for you to merge.

@mkitti

mkitti commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

The cryptography upper bound is a conservative choice. While there are backwards incompatible changes frequently, it is not clear that these are backwards incompatible for fileglancer. In fact, this package is never imported directly by fileglancer currently. We're only using the bound since it's an optional transitive dependency of python-jose and authlib. This is probably ok to merge, but I think we should revisit the cryptography bound next week.

Assessment from Antigravity CLI / Gemini Flash 3.5:

1. Cryptography’s Rapid Major Version Cycle

  Since version 3.0, the PyCA (Python Cryptography Authority) has adopted a rapid release cycle, bumping the major version number frequently (e.g., v41, v42, ... v46) to reflect
  backend changes (such as Rust compilation toolchain updates) or deprecations of legacy cryptographic algorithms.

  • The Risk of "Dependency Hell": If you place a strict upper bound (such as  <47.0.0  or  <48.0.0 ) in  pyproject.toml , your package will immediately block downstream
  installations and updates as soon as PyCA releases a new major version (which happens every few months). This forces downstream integrators to wait for a  fileglancer  release
  simply to update their environment.

2. Security Criticality (CVE Patches)

   cryptography  is a security-critical package. Vulnerabilities (CVEs) are patched rapidly, and those fixes are typically rolled out in newer major or minor releases.

  • Blocking Security Updates: Imposing an upper bound can prevent environments from pulling in crucial security patches, leaving them vulnerable. The standard industry guideline is
  to avoid upper-bounding security libraries unless there is a verified, unfixable incompatibility.

3. Indirect/Transitive Dependency Usage

  A search of the  fileglancer  codebase shows that  cryptography  is not imported directly in your Python code.

  • Instead, it is pulled in as a transitive dependency of packages like  authlib ,  python-jose , and  atlassian-python-api .
  • Specifying  cryptography >=41.0.0  in  pyproject.toml  is useful for enforcing a safe minimum version, but placing an upper bound on it is redundant. If a future version of
  cryptography  breaks downstream packages like  authlib , those libraries will manage their own compatibility limits, or you can temporarily pin the dependency when a concrete
  incompatibility is discovered.

4. Lockfiles vs. Abstract Dependencies

   fileglancer  uses modern environment and dependency managers (Pixi via  pixi.lock  and uv via  uv.lock ).

  • The Best of Both Worlds:
      • In  pyproject.toml : Keep the abstract constraint open ( cryptography >=41.0.0 ) to give downstream consumers maximum installation flexibility.
      • In  pixi.lock  /  uv.lock : Your deployment and test environments will lock the exact, verified version of  cryptography  (currently  46.0.7 ), ensuring production stability
      and reproducibility without imposing restrictive boundaries on downstream developers.

  ──────

Recommendation Summary

  • Do not add an upper bound to  cryptography  in  pyproject.toml . Keep it as  cryptography >=41.0.0  (or raise the lower bound if a newer minimum secure version is desired).
  • Rely on lockfiles ( pixi.lock  and  uv.lock ) to pin the tested version for deployment stability.
  • Use automated tools (such as Dependabot or regular lockfile updates) to keep the pinned version in your lockfiles up-to-date with security releases.
  

Assessment by Claude Opus 4.8

● Here's how fileglancer uses the cryptography package:

It's used transitively, not directly

There is no import cryptography anywhere in fileglancer's source. It's declared as a direct runtime dependency (cryptography >=41.0.0) purely to nail down the crypto backend for the
OKTA OAuth/OIDC authentication system. Two of fileglancer's auth dependencies need it:

  • python-jose — fileglancer imports from jose import jwt, JWTError in fileglancer/auth.py to decode OKTA ID tokens (verify_id_token). python-jose is backend-pluggable; the
    cryptography extra (python-jose[cryptography]) makes it use cryptography for the JWT/JWK signing-algorithm primitives.
  • authlib — imported as from authlib.integrations.starlette_client import OAuth in auth.py (and server.py) to run the actual OKTA OAuth flow. Authlib unconditionally requires
    cryptography.

Why it's pinned explicitly

The git history tells the story:

  • bc33421 feat: Adds OKTA OAuth authentication system — introduced the dependency on JWT/OAuth.
  • d829309 fix: cryptography backend for python-jose — the key commit. It replaced python-jose-cryptodome with cryptography:
  • "python-jose-cryptodome >=1.3.2"
  • cryptography >=41.0.0
  • The commit message: "was installing an old version of python-jose and the cryptodome backend, which wasn't compatible with updated Python versions." So cryptography was
    deliberately chosen over the PyCryptodome backend for Python-version compatibility.

Relevance to the dependency-bounds work

This is also exactly why #378 leaves cryptography uncapped (>=41.0.0, no ): it uses a rolling-major release scheme (41→48), and since fileglancer never touches its API
directly — it's only the signing backend behind python-jose/authlib — there's no fileglancer-level API surface that a major bump could break. The real compatibility constraints come
from python-jose and authlib, which carry their own cryptography floors.

@mkitti mkitti merged commit 8349c0c into main Jun 3, 2026
5 checks passed
@mkitti mkitti deleted the sync-dep-bounds branch June 3, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants