Repository navigation
feat: modernize Python tooling (pyproject.toml + uv + semantic-release) - #316
Conversation
|
Thanks for the pull request, @irfanuddinahmad! This repository is currently maintained by 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 approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo 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:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere 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:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
Replace setup.py/setup.cfg with PEP 621 [project] metadata and setuptools-scm for git-tag-based versioning. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Replace requirements/*.in + *.txt with PEP 735 dependency-groups in pyproject.toml and a single uv.lock. Update tox.ini to use tox-uv's uv-venv-lock-runner, update Makefile targets, and switch CI (including the mysql8 migrations check) to install uv and run tests via `uv run tox`. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Replace the manual GitHub-release-triggered publish workflow with python-semantic-release: pushes to master with conventional commits now automatically bump the version, tag it, and publish to PyPI. Co-Authored-By: Claude Sonnet 5 <[email protected]>
semantic-release defaults to a "v{version}" tag format, but this repo's
existing release tags are bare version numbers -- without tag_format set,
semantic-release wouldn't recognize any prior release.
Also restores the ignore=E501,W503,W504 pycodestyle setting that was
dropped when setup.cfg was deleted -- without it the quality tox env
fails on pre-existing long lines that were previously suppressed.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Every uv sync/uv run invocation in this repo names an explicit --group, but uv's implicit default group (named "dev") was still being synced alongside it, silently pulling the entire dev/test/quality/ci superset into every target. Verified with `uv sync --group ci`. Also adds .venv/ to .gitignore alongside the existing venv/ entry. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Parent issue openedx/public-engineering#506 asks for OIDC trusted-publisher PyPI auth, not a stored token. Grant id-token: write on publish_to_pypi and drop the explicit __token__/PYPI_UPLOAD_TOKEN credentials -- pypa/gh-action-pypi-publish uses OIDC automatically once the permission is present and no credentials are given. Co-Authored-By: Claude Sonnet 5 <[email protected]>
49d1199 to
4380c0c
Compare
@release/v1 is a floating branch ref -- xblocks-core's release just failed with "docker: manifest unknown" because the Docker image tag it resolved to at checkout time wasn't published on ghcr.io yet. Pin to the exact commit backing the current v1.14.0 release instead, consistent with this repo's own SHA-pinning rule for every other action. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Reviewer feedback: this should be set across the whole batch, not just repos currently on 0.x, so no repo in this effort can ever auto-jump to 1.0.0 as an accidental side effect if it's reset to 0.x in the future. Note this is a no-op for repos already past 1.0 -- major_on_zero only governs the 0.x -> 1.0.0 transition, not 1.x -> 2.0.0 (there's no PSR setting that suppresses major bumps once past 1.0; that's normal SemVer behavior for a breaking-change commit at any version). Co-Authored-By: Claude Sonnet 5 <[email protected]>
Moves taxonomy/ to src/taxonomy/, in line with the reference implementation for this modernization effort (openedx/sample-plugin) and openedx/forum#281. - pyproject.toml: add where = ["src"] to packages.find - tox.ini: prefix src/ onto pylint/pycodestyle/pydocstyle/isort targets in the quality env - Makefile: prefix src/taxonomy onto the 3 localization targets that cd into the package (compile_translations, detect_changed_source_translations, dummy_translations); compile_translations' relative ../manage.py climb updated to ../../manage.py to account for the extra nesting level - test_settings.py: LOCALE_PATHS root() call updated - docs/conf.py: sphinx-apidoc call updated to point at src/taxonomy - MANIFEST.in: recursive-include path updated Verified: uv sync (no lock drift), full django42 test run (315 passed), quality (pylint/pycodestyle/pydocstyle/isort) and pii-annotations envs pass, docs env passes end to end -- sphinx-apidoc generates real API docs from the new path, uv build --wheel + twine check pass.
Same category of fixes farhan flagged on the batch's other reviewed PRs: - ci.yml + mysql8-migrations.yml: add enable-cache/python-version to astral-sh/setup-uv and drop the now-redundant actions/setup-python step - pyproject.toml: add [tool.uv] package = true - Drop CHANGELOG.rst from the dynamic readme file list and delete it -- python-semantic-release + GitHub Releases is the changelog of record. Also removes the now-stale MANIFEST.in include and the docs/changelog.rst page (and its toctree entry). - Migrate .coveragerc into [tool.coverage.run] and delete the old file Verified: uv sync (no lock drift), full django52 test run (315 passed), quality, pii-annotations, and docs envs all pass end to end -- docs env's apidoc + wheel build + twine check confirm the readme/coverage config changes work correctly.
Both referenced files/processes that no longer exist post-migration: CHANGELOG.rst was deleted (python-semantic-release + GitHub Releases is the changelog of record now), and __version__ in taxonomy/__init__.py is no longer manually bumped (versioning is automated by semantic-release based on conventional commits). Neither is a manual per-PR task anymore.
… wrapping, dynamic readme) Cross-checked against review comments/fixes from openedx-ledger#242, edx-enterprise-subsidy-client#222, enterprise-access#1015, and enterprise-subsidy#441. - Add test-django42/test-django52 dependency-groups (each layering a Django version pin on top of the shared test group) and declare [tool.uv].conflicts between them, replacing the old single test group + tox deps= overlay. Update tox.ini's [testenv] to select the group by factor and add the previously-missing django42 job to the CI matrix -- only django52 was actually being exercised in CI before this. - Wrap remaining bare tool invocations in the Makefile (coverage erase, pytest --cov-report html, pytest (test target), diff-cover, i18n_tool, manage.py compilemessages) with `uv run`. Transifex's tx binary is left bare since it's installed via curl, not uv/pip. - Drop the redundant dynamic readme field; keep only version as dynamic. - Regenerate uv.lock for the new dependency groups; verified both py312-django42 and py312-django52 tox environments resolve and run.
# Conflicts: # CHANGELOG.rst # requirements/ci.txt # requirements/dev.txt # requirements/doc.txt # requirements/pip-tools.txt # requirements/pip.txt # requirements/test.txt # src/taxonomy/__init__.py
Make requires recipe lines to be indented with a literal tab character. An earlier edit in this migration accidentally used spaces for several targets, breaking 'make selfcheck' and CI.
The uv/pyproject.toml modernization (openedx/public-engineering#506) switched coverage measurement to [tool.coverage.run], which correctly excludes src/taxonomy/tests/*.py from the package's own coverage report instead of counting those (trivially self-covered) test files toward the reported percentage. That's a one-time drop in the reported baseline (98.91% -> 98.70%, 90 files -> 86 files) caused purely by measurement scope, not a regression: every production file's hit/miss counts are unchanged. Add a documented threshold so codecov/project absorbs this one-time drop while still catching genuine future regressions, matching the same fix applied in sibling modernization PRs (ccx-keys#190, opaque-keys#461, openedx-chem#161).
…gelog generation
A previous pass deleted CHANGELOG.rst and disabled changelog generation
(changelog: "false") in release.yml with no ticket justification -- the
changelog is genuinely useful historical documentation and semantic-release
can keep it current automatically. Restore CHANGELOG.rst from the commit
right before it was deleted, add the insertion-marker line PSR's "update"
mode looks for, wire up [tool.semantic_release.changelog] to update the
existing RST file in place, and remove changelog: "false" from release.yml.
tag_format = "{version}" was already correctly set to match this repo's
bare-version tag convention (verified against `git tag --sort=-v:refname`),
so it's left unchanged.
python-semantic-release's release.yml now creates the tag, GitHub release, and publishes to PyPI automatically on merge -- these were no longer real manual steps for a contributor to perform.
| path: dist | ||
|
|
||
| - name: Publish to PyPi | ||
| uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 |
There was a problem hiding this comment.
Please make sure the pypi publish is working successfully, as previously the hash pypa version was creating some issue and we used the stable version for pypa
uses: pypa/gh-action-pypi-publish@release/v1
There was a problem hiding this comment.
Fixed — reverted to the stable @release/v1 tag.
There was a problem hiding this comment.
Update: this has been reverted again — pypa/gh-action-pypi-publish is now SHA-pinned to dc37677b2e1c63e2034f94d8a5b11f265b73ba33 (v1.14.2) in commit 22838e9, matching every other action pin in this file against openedx/sample-plugin's current release.yml, which this org has since designated as the gold-standard reference for these pins.
This reverses the "reverted to stable @release/v1 tag" fix noted just below, because your original ask here has since been superseded: on a sibling repo's PR in this same modernization effort, you directed matching sample-plugin exactly, and sample-plugin SHA-pins this action. The SHA was independently verified against the GitHub API (commit lookup + resolving through the v1.14.2 tag object) before use.
|
|
||
| Unreleased | ||
|
|
||
| [4.0.0] |
There was a problem hiding this comment.
Why we are removing old release logs?
There was a problem hiding this comment.
Fixed — restored the removed entries. CHANGELOG.rst now has the full history back, plus the .. changelog-insertion-marker line so future automated entries land in the right place without touching the existing history.
There was a problem hiding this comment.
Follow-up: after this was fixed by restoring the full history in commit e58b416, a later merge-from-master (bringing in master's own [4.0.0] / "chore: upgrade requirements" changelog entry) silently dropped that entry again during the merge, despite no textual conflict on this file. Caught it by diffing this branch's CHANGELOG.rst against current master directly. Restored in commit 6db2fb7 — the file now matches master's CHANGELOG.rst exactly.
# Conflicts: # requirements/ci.txt # requirements/constraints.txt # requirements/dev.txt # requirements/doc.txt # requirements/pip-tools.txt # requirements/pip.txt # requirements/test.txt
"0.0.0.dev0" is the exact fallback_version value that crashed 17 tests
in a sibling repo (openedx-events) -- runtime code that parses
__version__ via tuple(map(int, __version__.split("."))) chokes on the
non-numeric "dev0" segment. No current consumer here does that (this
repo's own __version__ usage, if any, only interpolates it as a
string), but there's no reason to keep a fallback value from the exact
banned-pattern class when a plain int-parseable "0.0.0" is equally
valid and strictly safer.
- mysql8-migrations.yml: replaced uv pip uninstall/install --no-binary (x2) with a single native `uv sync --group mysql8 --no-binary-package mysqlclient --no-binary-package xmlsec`. mysqlclient/xmlsec weren't pulled in by any existing group (confirmed: a plain `uv sync --group dev` installs neither), so added a new `mysql8` dependency-group for them -- this workflow-only need is the same as master's pre-migration behavior, where they came in only via this same job's own pip uninstall+reinstall step, not via requirements/dev.txt or test.txt. Verified the sync+flag combo genuinely triggers source builds for both (confirmed via -v output and a real build attempt, blocked locally only by a missing macOS pkg-config/mysql-dev system dependency that the CI runner's own `apt-get install libxmlsec1-dev` step already covers). - codecov.yml: removed the permanent `threshold: 1%`, per salman2013's question (#316 (comment)) on whether this was meant to be temporary. It wasn't actually needed: this status check isn't required for merging, and `target: auto` compares each PR against its own base commit, so the one-time coverage- scope discontinuity (98.91% -> 98.70%) only ever affected this PR's own diff display -- once merged, 98.70% becomes the new baseline for every future comparison, with no lingering gap for a permanent threshold to paper over. Kept the explanatory comment, dropped the actual tolerance, since a standing 1% regression-masking allowance had no real job to do and only downside. Found while auditing this repo for uv pip usage per the lessons learned on openedx-platform#38915.
Conflicts, both resolved the same way as the analogous ones on openedx-platform#38915 and edx-enterprise#2654: - .github/workflows/ci.yml: kept our astral-sh/setup-uv (no actions/setup- python needed), adopted master's actions/checkout bump to v7.0.1. - .github/workflows/pypi-publish.yml: modify/delete -- kept deleted. Superseded by this migration's new release.yml; master's change was only a routine actions/checkout version bump on a file this branch already replaced.
Set changelog: "false" on the PSR release step and remove the [tool.semantic_release.changelog] config / insertion marker. Checked against openedx/XBlock's actual production release.yml (the one repo in this effort that has cut real automated releases) -- every run passes changelog: false and invokes `semantic-release -v version --no-changelog`, and the repo has zero github-actions[bot] commits ever. The auto-changelog config this migration previously added was only ever verified via a local dry-run prototype, never against a real release. feanil flagged the same issue on openedx/DoneXBlock#388: "I thought we were not going to add changelogs since they can't be updated by python-semantic-release the way we have it setup."
Resolved: kept our deletion of pypi-publish.yml (superseded by release.yml) and our uv-based ci.yml/mysql8-migrations.yml over master's trivial setup-python SHA bump.
…migration # Conflicts: # requirements/ci.txt # requirements/constraints.txt # requirements/dev.txt # requirements/doc.txt # requirements/pip-tools.txt # requirements/pip.txt # requirements/test.txt
This repo has immutable releases enabled, which freezes a release's assets the moment it's published. The old flow (main PSR step publishes the release, a separate publish-action step attaches assets afterward) can never work under that constraint -- it would 422 on the first real release. Matches the fix already proven and merged on openedx/sample-plugin#57 and validated end-to-end on openedx/event-tracking#434: build without publishing (vcs_release: false), then create the release with dist/* attached in one gh release create call.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
openedx/sample-plugin's release.yml is this org's designated reference for action pins in this migration effort. This repo's release.yml had drifted from it on every pin: - actions/checkout: v7.0.0 (9c091bb2) -> v7.0.1 (3d3c42e5), matching sample-plugin - python-semantic-release: v10.5.3 (350c48fc) -> v10.6.2 (9a026e93) - actions/upload-artifact: v4.6.2 (ea165f8d) -> v7.0.1 (043fb46d) - actions/download-artifact: v4.3.0 (d3f86a10) -> v8.0.1 (3e5f45b2) - pypa/gh-action-pypi-publish: unpinned @release/v1 -> SHA-pinned v1.14.2 (dc37677b) The pypa/gh-action-pypi-publish change reverses earlier guidance on this PR: salman2013 previously asked (review comment #3710602930) for this action to be reverted off a SHA pin back to @release/v1 due to a prior publish issue with the hashed version. That guidance has since been superseded -- the same reviewer, on a sibling repo's PR in this same migration effort, directed matching sample-plugin exactly, which now means SHA-pinning this action like every other one in the file. Every SHA above was independently verified against the GitHub API (commit lookup + tag ref, resolving through the annotated tag object where applicable) before use, per this org's history of fabricated/ swapped SHAs in this exact file. Co-Authored-By: Claude Sonnet 5 <[email protected]>
salman2013 asked (review comment #3710606973) why old release log entries were being removed; this was previously addressed by restoring CHANGELOG.rst's full history in commit e58b416. But three later merge-from-master commits on this branch (6012451, then 1804d5d) brought in master's tip without carrying over master's own [4.0.0] / "chore: upgrade requirements" changelog entry -- despite there being no textual conflict on this file (the entry existed cleanly on master's side with no competing change on the branch side), the merge results ended up without it, so it was silently lost again. Confirmed via `git diff origin/master -- CHANGELOG.rst` before this fix: the only difference between this branch and current master was the missing [4.0.0] entry. Restored it so the file now matches master exactly, keeping this PR's changelog history complete and consistent with what will already be on master when this merges. Co-Authored-By: Claude Sonnet 5 <[email protected]>
4e45689 removed the hardcoded __version__ = "3.0.0" (correctly, since version is now dynamic via setuptools-scm) but never replaced it with the importlib.metadata equivalent, unlike sibling repos in this migration batch. taxonomy.__version__ has been silently missing since. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Points at "see PR description" for an explanation -- fragile since the PR description is external and mutable. Coverage config itself stays out of scope for this migration; just dropping the comment, no change to target/threshold behavior. Co-Authored-By: Claude Sonnet 5 <[email protected]>
| sed '/^[dD]jango==/d' requirements/test.txt > requirements/test.tmp | ||
| mv requirements/test.tmp requirements/test.txt | ||
| compile-requirements: ## generate the uv.lock file without upgrading packages | ||
| uv lock |
There was a problem hiding this comment.
as per the old pip command, PIP_COMPILE = pip-compile --rebuild --upgrade $(PIP_COMPILE_OPTS)
i think uv lock --upgrade is the better option in uv.
There was a problem hiding this comment.
Confirmed -- the upgrade target already uses uv lock --upgrade.
…e job Matches openedx/sample-plugin's current release.yml; the custom OPENEDX_SEMANTIC_RELEASE_GITHUB_TOKEN secret isn't needed.
Makefile targets were fully inlining commands that tox.ini already defines as standalone envs. pii_check delegates to tox -e pii-annotations, matching the actual tox env name (not pii_check). Co-Authored-By: Claude Sonnet 5 <[email protected]>
Makefile now assumes an already-synced/activated local env; uv run stays in CI workflow steps only. See openedx/ccx-keys#190 (review comment r4097501942). Co-Authored-By: Claude Sonnet 5 <[email protected]>
test-all had drifted to depend only on `test` (a single pytest run), making it identical to `validate` and no longer actually covering py312-django42/py312-django52 like its own help text claims. Restore the bare `tox` call so it runs the full envlist. Also corrected the tag_format comment, which claimed no v-prefixed tag existed -- v4.0.0 already does, same commit as the old bare 4.0.0 tag. Per feanil's review on #316: discussion_r4097760680, r4097760715. Co-Authored-By: Claude Sonnet 5 <[email protected]>
CI stopped exercising django42 in 2026-02 (3b842b7), keeping it only in tox.ini's envlist for local/manual use; the migration's ci.yml regenerated the matrix from tox.ini and silently reintroduced it as a CI job. Per this effort's policy, a migration PR must not expand CI scope beyond what pre-existed. Coverage step already correctly targets django52, so no other change is needed. Co-Authored-By: Claude Sonnet 5 <[email protected]>
…v10.7.0
- dev omitted {include-group = "doc"}, so `uv sync --group dev` never
provisioned doc8/Sphinx and `make docs` failed on a fresh clone.
Matches the pattern already used in openedx/edx-enterprise's
pyproject.toml.
- Bump python-semantic-release action pin from v10.6.2 to v10.7.0,
matching openedx/sample-plugin's current live pin.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
This repo's release workflow disables python-semantic-release's changelog generation (changelog: "false") and GitHub Releases is the actual changelog now -- confirmed CHANGELOG.rst was never updated for the real 4.1.0 release (PR #316), so it's no longer part of the real workflow. The entry here was also just wrong: it claimed [4.1.0], but that version number was already taken by #316 before this PR merges. Since this branch is a feat: commit on top of the now-released 4.1.0, merging will actually produce 4.2.0, not 4.1.0. Simplest fix is to stop hand-maintaining this file rather than add a corrected-but-still-redundant entry. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Summary
Modernizes this repo's Python tooling per the org-wide standardization tracked in openedx/public-engineering#513 (and the parent openedx/public-engineering#506):
pyproject.toml(PEP 621, setuptools-scm for git-tag-based versioning), replacingsetup.py/setup.cfgpip-compiletouv:requirements/*.in+*.txtare replaced by PEP 735[dependency-groups]+ a singleuv.lockpython-semantic-release: pushes tomasterwith conventional commits now automatically bump the version, tag it, and publish to PyPI (reusing the existingPYPI_UPLOAD_TOKENsecret).github/workflows/mysql8-migrations.yml, which still installed fromrequirements/Part of openedx/public-engineering#513.
Test plan
uv lockresolves cleanly (148 packages)uv run tox -e py312-django52: 315 tests passed;pii-annotations/docstoxenvs passpython -m build --wheel+twine check: pass, version correctly derived from git tagqualitytoxenv surfaces pre-existing pycodestyle line-length violations that predate this migration (not a regression)Codecov
The standard
omit=["*/tests/*", ...]config excludes test modules from coverage measurement (previously counted, and trivially ~100% covered since their own lines execute whenever the tests run). This causes a small, expected drop in reported project coverage % (98.91% → 98.70%) with no actual change in production-code coverage.target: autoon the project check has no fixed threshold — it compares against each PR's own base commit, so this one-time drop becomes the new baseline once merged.Release readiness (pre-merge blocker)
taxonomy-connector→ GitHub repo
openedx/taxonomy-connector, workflowrelease.ymlDo not merge until both boxes are checked -- until then,
publish_to_pypiwill failon first merge to
master(this PR switches the workflow to OIDC; it does notconfigure the trusted publisher itself, which is a PyPI project-settings action with
no API we can drive from here). Tracked across this whole effort in a consolidated
comment on openedx/public-engineering#506.
🤖 Generated with Claude Code