Skip to content

feat: modernize Python tooling (pyproject.toml + uv + semantic-release) - #425

Open
irfanuddinahmad wants to merge 6 commits into
openedx:masterfrom
irfanuddinahmad:irfanuddinahmad/modernize-python-tooling
Open

feat: modernize Python tooling (pyproject.toml + uv + semantic-release)#425
irfanuddinahmad wants to merge 6 commits into
openedx:masterfrom
irfanuddinahmad:irfanuddinahmad/modernize-python-tooling

Conversation

@irfanuddinahmad

@irfanuddinahmad irfanuddinahmad commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Modernize event-tracking to uv + pyproject.toml (PEP 621/735) + python-semantic-release.

Part of openedx/public-engineering#506 (tracked in openedx/public-engineering#514).

  • Move eventtracking/ to src/eventtracking/ (org-wide decision, in scope this cycle)
  • Replace setup.py/setup.cfg with pyproject.toml (PEP 621 static metadata)
  • Switch from pip-compile to uv with PEP 735 dependency groups; commit uv.lock
  • Update tox.ini to use tox-uv with uv-venv-lock-runner
  • Update CI to use astral-sh/setup-uv
  • Add python-semantic-release + release.yml, targeting PyPI OIDC trusted publishing

Removed

Deleted files: setup.py, setup.cfg, requirements/*, .github/workflows/pypi-publish.yml (replaced by release.yml's publish_to_pypi job)

Makefile targets: none dropped — compile-requirements-equivalent (upgrade) is rewritten to use edx_lint write_uv_constraints + uv lock --upgrade instead of pip-compile; all other targets (test.unit, test.integration, test.performance, style, lint, install, develop, doc, report, requirements) kept the same name and behavior, just re-pointed at uv.

Not included

  • No ruff migration — per the 2026-07-15 decision on #506, ruff is explicitly out of scope for this cycle.
  • No separate quality/docs tox envs — this repo's original tox.ini ran pycodestyle/pylint in the same env as pytest (not a separate env), so that structure is preserved rather than introduced new.
  • Dropped the celery54 tox factor and requirements/celery54.txt: that file was a byte-for-byte duplicate of pins already present in base.txt (extracted via grep in the old make upgrade target), and the tox envlist only ever exercised one celery version — not an actual matrix. uv.lock pins the resolved celery version directly, so the separate file added no information.

Versioning

Dynamic, via setuptools-scm (version_scheme = "only-version", local_scheme = "no-local-version", fallback_version = "0.0.0"). Pre-flight checked: the latest git tag (v4.0.2, via git tag --sort=-v:refname) matches PyPI's actual latest published version (4.0.2), so there's no missing-tag gap for semantic-release to trip over.

Testing Notes

Verified locally at each phase:

  • uv lock resolves cleanly (96 packages), uv sync --group dev/--group ci succeed
  • uv run tox -e py312-django52 — the unit-test command passes (96 passed, ~97% branch coverage, --cov-fail-under=95 satisfied) and both quality commands (pycodestyle, pylint) pass cleanly
  • Editable install (uv pip install -e .) correctly resolves eventtracking under the new src/ layout; wheel build excludes test files
  • python -m build with SETUPTOOLS_SCM_PRETEND_VERSION produces a correctly versioned sdist/wheel (matching release.yml's build_command)
  • semantic-release version --print computes a real next version from this branch's conventional commit history

Could not verify locally: the test.integration command (pytest -k integration) needs a live MongoDB, provided in CI via supercharge/mongodb-github-action — this only runs in CI, same as before this PR.

Pre-merge blockers (not fixable from this PR)

  1. PyPI OIDC trusted publisher needs to be configured for event-tracking before the first automated release — otherwise publish_to_pypi will simply fail on the first release attempt.
  2. .github/workflows/upgrade-python-requirements.yml calls the org's shared reusable workflow (openedx/.github/.github/workflows/upgrade-python-requirements.yml), which hardcodes requirements/ in its add-paths for the fork PR-creation step. Now that requirements/ is deleted, that scheduled job will keep running but silently stop producing real dependency-upgrade PRs (empty diff each time) — it won't fail loudly, so nothing will alert anyone. This can't be fixed from this repo; it needs a fix in openedx/.github's reusable workflow (parameterize add_paths, or auto-detect uv.lock vs. requirements/).

Code reviewer notes

  • release.yml's action versions (python-semantic-release@v10.6.1, publish-action@v10.6.1, upload-artifact@v7, download-artifact@v8) intentionally use plain tags matching openedx/XBlock's actual, currently-releasing release.yml, rather than being SHA-pinned — release.yml only runs on push to master, never PR CI, so a hand-verified bad SHA pin here is unusually easy to introduce invisibly. Only pypa/gh-action-pypi-publish is SHA-pinned (ba38be9e461d3875417946c167d0b5f3d385a247 / v1.14.1), because its @release/v1 ref is a floating branch, not a version tag.
  • Coverage config now uses the standard omit patterns (*/tests/*, */migrations/*, etc.) on top of this repo's existing */*performance*/*/*integration*/*/admin.py/*/apps.py omissions. Verified this doesn't cause a coverage drop — actual coverage stays ~97%, comfortably above the existing 95% threshold.

This PR was created with Claude Code.

Irfan Ahmad added 4 commits July 28, 2026 10:29
Move eventtracking/ to src/eventtracking/ per the org-wide decision that
src/ layout is in scope for this modernization cycle (openedx/public-
engineering#506, decision comment 2026-07-15). This fixes editable-install
resolution for tools like mypy, which don't resolve flat-layout packages
correctly under `pip install -e .`.

Update setup.py (package_dir/find_packages), .coveragerc, tox.ini,
Makefile, and doc/conf.py to reference the new src/ path. Verified: an
editable install still exposes `eventtracking` importable from repo root,
and the full test suite (96 passed) plus pycodestyle/pylint both pass
unchanged against the new location.
Replace setup.py/setup.cfg with PEP 621 static metadata in pyproject.toml:
- Static `dependencies` list (from requirements/base.in), with a lower
  bound on Django matching the only currently-tested version (5.2)
- setuptools-scm derives the version from git tags (version_scheme =
  "only-version", local_scheme = "no-local-version", fallback_version =
  "0.0.0"); remove the now-permanently-stale hardcoded __version__ from
  eventtracking/__init__.py
- SPDX license expression ("AGPL-3.0") + license-files, matching sibling
  repos already modernized under this same effort
- [tool.setuptools.exclude-package-data] excludes tests from the wheel
- coverage config ported from .coveragerc into [tool.coverage.*], with the
  standard omit patterns (tests/migrations/pycache/settings) added on top
  of the repo's existing performance/integration/admin/apps omissions --
  verified this does not drop the 95% coverage threshold (still ~97%)
- pycodestyle's setup.cfg config moved into tox.ini's own [pycodestyle]
  section (pycodestyle does not read pyproject.toml, so porting it there
  would be a silent no-op) rather than setup.cfg, which is deleted
- Draft [dependency-groups] and edx_lint scaffolding added here; wired up
  fully in the next (uv) commit

Verified locally: editable install resolves version/metadata correctly,
wheel build excludes test files, full test suite (96 passed) and
pycodestyle/pylint both still pass unchanged.
- Add PEP 735 [dependency-groups] (test-base/test/quality/doc/ci/dev)
  mapping the old requirements/*.in layering, plus [tool.edx_lint]
  uv_constraints for the repo-specific celery<6.0 pin (the old
  backports.zoneinfo;python_version<"3.9" constraint is dropped -- dead
  weight now that requires-python is >=3.12)
- Run `edx_lint write_uv_constraints` to populate
  [tool.uv].constraint-dependencies from the org's common constraints
- Generate and commit uv.lock
- Delete requirements/; the celery54.txt test-matrix file is dropped too,
  since it duplicated pins already present in base.txt/uv.lock and the
  tox envlist only ever exercised one celery version
- Update MANIFEST.in: drop its two now-dangling `include requirements/...`
  lines (grepped the whole repo for other stale requirements/ references;
  the only remaining one is in pypi-publish.yml, which is replaced in the
  next commit)
- tox.ini: tox-uv>=1, uv-venv-lock-runner, dependency_groups = ci;
  pycodestyle's setup.cfg settings moved into tox.ini's own [pycodestyle]
  section (dropped in the metadata commit); envlist simplified from
  py{312}-celery{54}-django{52} to py312-django52 since celery54 was a
  single-value factor, not an actual matrix
- Makefile: uv sync --group dev/ci, uv run for all tool invocations;
  upgrade target now runs edx_lint write_uv_constraints + uv lock --upgrade
- CI workflow: astral-sh/setup-uv pinned to a commit SHA (replaces the
  separate setup-python step), uv sync --group ci, uv run tox -e
  ${{ matrix.toxenv }}, matrix job named after toxenv, job-level
  permissions: contents: read, fetch-depth: 0 on checkout (so
  setuptools-scm can see tags during test runs), and a workflow_call
  trigger so release.yml can reuse it in the next commit

Known gap flagged for the PR description: this repo has a
.github/workflows/upgrade-python-requirements.yml calling the org's
shared reusable workflow, which hardcodes requirements/ in its add-paths
-- that scheduled job will keep running but silently stop producing real
upgrade PRs now that requirements/ is gone. Cannot be fixed from this repo.

Verified locally: uv lock resolves, uv sync --group dev/ci succeed,
uv run tox -e py312-django52 passes the unit-test command (96 passed,
~97% coverage) and both quality commands (pycodestyle, pylint) cleanly;
the integration-test command needs a live MongoDB (provided in CI by
supercharge/mongodb-github-action) so it could not be exercised locally.
- [tool.semantic_release]: build_command uses `python -m build` (the
  action's environment has no uv available), major_on_zero = false and
  allow_zero_version = true (no-op today since the repo is past 1.0, but
  set for consistency and so it never auto-jumps to 1.0.0 if ever reset
  to 0.x)
- Add .github/workflows/release.yml: run_tests reuses ci.yml via
  workflow_call; release job runs python-semantic-release and uploads a
  dist artifact; publish_to_pypi job publishes via PyPI OIDC trusted
  publishing (id-token: write, no stored token). Action versions
  (python-semantic-release@v10.6.1, publish-action@v10.6.1,
  upload-artifact@v7, download-artifact@v8) match openedx/XBlock's
  actual, already-releasing release.yml exactly rather than being
  SHA-pinned -- release.yml only runs on push to master, never PR CI, so
  a hand-verified SHA pin here is unusually easy to get wrong invisibly
  (confirmed in several sibling migration PRs under this same effort).
  The one exception is pypa/gh-action-pypi-publish, pinned to commit SHA
  ba38be9e461d3875417946c167d0b5f3d385a247 (v1.14.1) because its
  `@release/v1` ref is a floating branch, not a tag -- confirmed root
  cause of a real production publish failure on a sibling repo. All three
  touched SHAs (checkout, setup-uv, gh-action-pypi-publish) were verified
  directly against `gh api repos/<owner>/<repo>/commits/<sha>`.
- Delete .github/workflows/pypi-publish.yml, the old token-based publish
  workflow (triggered on `release: published`); release.yml's
  publish_to_pypi job replaces it. Grepped all workflow files for any
  other pypi-publish/pypa-gh-action reference; none found.
- commitlint.yml already existed and needed no changes.

Verified locally: `python -m build` with SETUPTOOLS_SCM_PRETEND_VERSION
produces a correctly versioned sdist/wheel, and
`semantic-release version --print` (run against a release-branch-shaped
ref) computes a real next version from this branch's own conventional
commits.

Pre-flight check (per migration skill): latest git tag v4.0.2 matches
PyPI's actual latest published version (4.0.2) -- confirmed via
`git tag --sort=-v:refname` and the PyPI JSON API, so no missing-tag gap
exists here.

Flagged as a pre-merge blocker in the PR description (not fixable from
this repo/PR): PyPI OIDC trusted publisher needs to be configured for
`event-tracking` before the first automated release, or publish_to_pypi
will simply fail.
@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @irfanuddinahmad!

This repository is currently maintained by @openedx/committers-events.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks openedx-webhooks added open-source-contribution PR author is not from Axim or 2U core contributor PR author is a Core Contributor (who may or may not have write access to this repo). labels Jul 28, 2026
@github-project-automation github-project-automation Bot moved this to Needs Triage in Contributions Jul 28, 2026
@mphilbrick211 mphilbrick211 moved this from Needs Triage to Ready for Review in Contributions Jul 28, 2026
@mphilbrick211
mphilbrick211 requested a review from a team July 28, 2026 21:28
Irfan Ahmad added 2 commits July 30, 2026 14:58
changelog: "false" was blindly copied from the reference template with
no ticket requirement to disable it. CHANGELOG.rst still exists and is
useful historical documentation, so wire up PSR to keep it updated in
place instead of throwing that away.

- Remove changelog: "false" from release.yml's PSR step
- Add [tool.semantic_release.changelog] config (mode="update", RST
  output) so new releases insert above the existing history
- Add the .. changelog-insertion-marker as the first line of
  CHANGELOG.rst
- Set tag_format = "v{version}" explicitly to match this repo's actual
  v-prefixed tag convention (verified against git tag history)
Resolves modify/delete conflicts on requirements/*.txt: master gained a
routine dependency-upgrade commit after this branch deleted the
requirements/ directory in Phase 2 of the tooling migration. Keeping
the deletion, since replacing requirements/*.txt with uv.lock +
pyproject.toml dependency groups is this migration's actual intent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core contributor PR author is a Core Contributor (who may or may not have write access to this repo). open-source-contribution PR author is not from Axim or 2U

Projects

Status: Ready for Review

Development

Successfully merging this pull request may close these issues.

3 participants