From fa948b82de6c936b488ddfb7313b10ad0607a731 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 23 Jul 2026 07:43:09 -0700 Subject: [PATCH 1/3] update package metadata - updated license identification using SPDX identfier - harmonized MDAnalysis and MDAnalysisTests minimal versions - use mdanalysis_rtd_theme for docs as a dependency --- pyproject.toml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ea4f11f..9dafb98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,8 @@ build-backend = "setuptools.build_meta" [project] name = "pathsimanalysis" description = "Calculates the geometric similarity of molecular dynamics trajectories using path metrics such as the Hausdorff and Fréchet distances." -license = {file = "LICENSE" } +license = "LGPL-2.1-or-later" +license-files = ["LICENSE"] authors = [ {name = "MDAnalysis", email = "mdanalysis@numfocus.org"}, ] @@ -19,7 +20,6 @@ classifiers = [ "Development Status :: 6 - Mature", "Environment :: Console", "Intended Audience :: Science/Research", - "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)", "Programming Language :: C", "Programming Language :: Python", "Programming Language :: Python :: 3.11", @@ -30,7 +30,7 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", ] readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.11" dependencies = [ "MDAnalysis>=2.1.0", "scipy>=1.5.0", @@ -51,11 +51,11 @@ test = [ "pytest>=6.0", "pytest-xdist>=2.5", "pytest-cov>=3.0", - "MDAnalysisTests>=2.0.0", + "MDAnalysisTests>=2.1.0", ] doc = [ "sphinx", - "sphinx_rtd_theme", + "mdanalysis-sphinx-theme", ] [project.urls] @@ -73,7 +73,6 @@ testpaths = [ [tool.black] line-length = 80 -extend-exclude = "versioneer.py" [tool.versioningit] default-version = "1+unknown" From 9d1e73cc3a9d7f957143cb9294e09ebf27b84b90 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 23 Jul 2026 07:52:17 -0700 Subject: [PATCH 2/3] update CHANGELOG for release --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c86da..c037ff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,16 @@ The rules for this file: * accompany each entry with github issue/PR number (Issue #xyz) --> +## [1.2.1] -- 2026-07-23 + +### Authors +- orbeckst + +### Changed +- In accordance with SPEC0 the minimum Python version has been + raised to v3.11 (PR #36) +- Officially supports Python 3.11 - 3.14 + ## [1.2.0] -- 2024-11-22 ### Authors From 977b316d83faf0011a08e1af5137cf46baab8155 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 23 Jul 2026 08:02:11 -0700 Subject: [PATCH 3/3] updated deployment workflow - ensure proper version is installed for testing - wait for release to appear on PyPi - source: https://github.com/Becksteinlab/multibind --- .../actions/wait-for-pypi-version/action.yaml | 93 ++++++++ .github/workflows/deploy.yaml | 205 ++++++++++++++++-- 2 files changed, 276 insertions(+), 22 deletions(-) create mode 100644 .github/actions/wait-for-pypi-version/action.yaml diff --git a/.github/actions/wait-for-pypi-version/action.yaml b/.github/actions/wait-for-pypi-version/action.yaml new file mode 100644 index 0000000..8a0b5d4 --- /dev/null +++ b/.github/actions/wait-for-pypi-version/action.yaml @@ -0,0 +1,93 @@ +name: 'Wait for PyPI version' +description: 'Wait for a specific package version to become available on PyPI or TestPyPI' +inputs: + repository: + description: 'PyPI repository type: "pypi" or "testpypi"' + required: true + package: + description: 'Package name' + required: true + version: + description: 'Package version to wait for' + required: true + max_attempts: + description: 'Maximum number of retry attempts' + required: false + default: '30' + wait_seconds: + description: 'Seconds to wait between attempts' + required: false + default: '10' + +runs: + using: composite + steps: + - name: Install requests + shell: bash + run: | + python -m pip install --upgrade pip + pip install requests + + - name: Wait for version to be available + shell: python + env: + REPOSITORY: ${{ inputs.repository }} + PACKAGE: ${{ inputs.package }} + VERSION: ${{ inputs.version }} + MAX_ATTEMPTS: ${{ inputs.max_attempts }} + WAIT_SECONDS: ${{ inputs.wait_seconds }} + run: | + import os + import sys + import time + + import requests + + repository = os.environ["REPOSITORY"].strip().lower() + package = os.environ["PACKAGE"] + version = os.environ["VERSION"] + max_attempts = int(os.environ.get("MAX_ATTEMPTS", "30")) + wait_seconds = int(os.environ.get("WAIT_SECONDS", "10")) + + if repository == "testpypi": + api_url = f"https://test.pypi.org/pypi/{package}/json" + repo_name = "TestPyPI" + elif repository == "pypi": + api_url = f"https://pypi.org/pypi/{package}/json" + repo_name = "PyPI" + else: + print( + f"ERROR: repository must be 'pypi' or 'testpypi', got {repository!r}", + file=sys.stderr, + ) + sys.exit(1) + + for attempt in range(max_attempts): + try: + r = requests.get(api_url, timeout=10) + r.raise_for_status() + data = r.json() + versions = data.get("releases", {}) + keys = list(versions.keys()) + print("Available versions:", keys[-10:]) # Show last 10 versions + if version in versions: + print(f"✓ Version {version} is available on {repo_name}") + print(f"Version {version} is now available on {repo_name}") + sys.exit(0) + print(f"✗ Version {version} is NOT available on {repo_name}") + except Exception as e: + print(f"Error checking version: {e}") + + current = attempt + 1 + print( + f"Attempt {current}/{max_attempts}: Version {version} not yet available " + f"on {repo_name}, waiting {wait_seconds} seconds..." + ) + time.sleep(wait_seconds) + + print( + f"ERROR: Version {version} did not become available on {repo_name} " + f"after {max_attempts} attempts", + file=sys.stderr, + ) + sys.exit(1) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 1511067..fb84dcb 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -8,47 +8,208 @@ on: types: - published +concurrency: + group: "${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }}" + cancel-in-progress: false + +defaults: + run: + shell: bash -l {0} + jobs: - testpypi_push: + build: + name: Build package + if: github.repository == 'MDAnalysis/PathSimAnalysis' + runs-on: ubuntu-latest + outputs: + version: ${{ steps.extract-version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + pip install "setuptools>=61.2,<81" build twine + + - name: Build package (wheel and sdist) + run: python -m build + + - name: Check package + run: twine check dist/* + + - name: Extract package version + id: extract-version + run: | + WHEEL_FILE=$(ls dist/*.whl) + VERSION=$(basename "$WHEEL_FILE" | sed -n 's/pathsimanalysis-\([^-]*\)-.*/\1/p') + if [ -z "$VERSION" ]; then + pip install "$WHEEL_FILE" --quiet + VERSION=$(python -c "import pathsimanalysis; print(pathsimanalysis.__version__)") + pip uninstall -y pathsimanalysis --quiet + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Extracted version: $VERSION" + + - name: Upload dist files + uses: actions/upload-artifact@v7 + with: + name: dist-files + path: dist/ + retention-days: 1 + + test-pytest: + name: Run tests + if: github.repository == 'MDAnalysis/PathSimAnalysis' + runs-on: ubuntu-latest + needs: build + steps: + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - name: Download dist files + uses: actions/download-artifact@v8 + with: + name: dist-files + path: dist/ + + - name: Install wheel with test extras + run: | + python -m pip install --upgrade pip + WHEEL_FILE=$(ls dist/*.whl) + pip install "${WHEEL_FILE}[test]" + + - name: Test import + run: | + python -c "import pathsimanalysis; print(f'Package {pathsimanalysis.__version__} imported successfully')" + + - name: Run tests + run: pytest -v --pyargs pathsimanalysis.tests + + deploy-testpypi: + name: Deploy to TestPyPI + if: | + github.repository == 'MDAnalysis/PathSimAnalysis' && + (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) + runs-on: ubuntu-latest + needs: [build, test-pytest] environment: name: deploy url: https://test.pypi.org/p/pathsimanalysis permissions: id-token: write - if: | - github.repository == 'MDAnalysis/PathSimAnalysis' && - (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) - name: Build, upload and test pure Python wheels to TestPyPi - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 + - name: Download dist files + uses: actions/download-artifact@v8 + with: + name: dist-files + path: dist/ - - name: testpypi_deploy - uses: MDAnalysis/pypi-deployment@main + - name: Publish to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 with: - test_submission: true - tests: true - test_deps: 'pytest MDAnalysisTests' - package_name: 'pathsimanalysis' + repository-url: https://test.pypi.org/legacy/ + verbose: true + skip-existing: true - pypi_push: + deploy-pypi: + name: Deploy to PyPI + if: | + github.repository == 'MDAnalysis/PathSimAnalysis' && + (github.event_name == 'release' && github.event.action == 'published') + runs-on: ubuntu-latest + needs: [build, test-pytest] environment: name: deploy url: https://pypi.org/p/pathsimanalysis permissions: id-token: write + steps: + - name: Download dist files + uses: actions/download-artifact@v8 + with: + name: dist-files + path: dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + test-deployed-testpypi: + name: Test deployed package (TestPyPI) if: | github.repository == 'MDAnalysis/PathSimAnalysis' && - (github.event_name == 'release' && github.event.action == 'published') - name: Build, upload and test pure Python wheels to PyPi + (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) runs-on: ubuntu-latest + needs: [build, deploy-testpypi] + steps: + - name: Checkout repository for actions + uses: actions/checkout@v7 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.11" + + - name: Wait for version to be available on TestPyPI + uses: ./.github/actions/wait-for-pypi-version + with: + repository: testpypi + package: pathsimanalysis + version: ${{ needs.build.outputs.version }} + + - name: Install from TestPyPI + run: | + python -m pip install --upgrade pip + pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "pathsimanalysis[test]==${{ needs.build.outputs.version }}" + - name: Test import + run: | + python -c "import pathsimanalysis; print(f'Package {pathsimanalysis.__version__} imported successfully from TestPyPI')" + + - name: Run tests + run: pytest -v --pyargs pathsimanalysis.tests + + test-deployed-pypi: + name: Test deployed package (PyPI) + if: | + github.repository == 'MDAnalysis/PathSimAnalysis' && + (github.event_name == 'release' && github.event.action == 'published') + runs-on: ubuntu-latest + needs: [build, deploy-pypi] steps: - - uses: actions/checkout@v7 + - name: Checkout repository for actions + uses: actions/checkout@v7 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.11" - - name: pypi_deploy - uses: MDAnalysis/pypi-deployment@main + - name: Wait for version to be available on PyPI + uses: ./.github/actions/wait-for-pypi-version with: - package_name: 'pathsimanalysis' - tests: false + repository: pypi + package: pathsimanalysis + version: ${{ needs.build.outputs.version }} + + - name: Install from PyPI + run: | + python -m pip install --upgrade pip + pip install "pathsimanalysis[test]==${{ needs.build.outputs.version }}" + + - name: Test import + run: | + python -c "import pathsimanalysis; print(f'Package {pathsimanalysis.__version__} imported successfully from PyPI')" + + - name: Run tests + run: pytest -v --pyargs pathsimanalysis.tests