diff --git a/.ci/azure/deploy.yml b/.ci/azure/deploy.yml index b53a56f89..4a2679113 100644 --- a/.ci/azure/deploy.yml +++ b/.ci/azure/deploy.yml @@ -1,66 +1,66 @@ jobs: -- job: - displayName: "Deploy Docs" - pool: - vmImage: ubuntu-latest + - job: + displayName: "Deploy Docs" + pool: + vmImage: ubuntu-latest - steps: - # No need to checkout the repo here! - - checkout: none + steps: + # No need to checkout the repo here! + - checkout: none - - bash: | - echo $IS_TAG - echo $IS_MAIN - echo $BRANCH_NAME - displayName: Report branch parameters + - bash: | + echo $IS_TAG + echo $IS_MAIN + echo $BRANCH_NAME + displayName: Report branch parameters - - task: DownloadPipelineArtifact@2 - inputs: - buildType: 'current' - artifactName: 'html_docs' - targetPath: 'html' + - task: DownloadPipelineArtifact@2 + inputs: + buildType: "current" + artifactName: "html_docs" + targetPath: "html" - - bash: | - ls -l html - displayName: Report downloaded cache contents. + - bash: | + ls -l html + displayName: Report downloaded cache contents. - - bash: | - git config --global user.name ${GH_NAME} - git config --global user.email ${GH_EMAIL} - git config --list | grep user. - displayName: 'Configure git' - env: - GH_NAME: $(gh.name) - GH_EMAIL: $(gh.email) + - bash: | + git config --global user.name ${GH_NAME} + git config --global user.email ${GH_EMAIL} + git config --list | grep user. + displayName: "Configure git" + env: + GH_NAME: $(gh.name) + GH_EMAIL: $(gh.email) - # upload documentation to discretize-docs gh-pages on tags - - bash: | - git clone -q --branch gh-pages --depth 1 https://${GH_TOKEN}@github.com/simpeg/discretize-docs.git - displayName: Checkout doc repository - env: - GH_TOKEN: $(gh.token) + # upload documentation to discretize-docs gh-pages on tags + - bash: | + git clone -q --branch gh-pages --depth 1 https://${GH_TOKEN}@github.com/simpeg/discretize-docs.git + displayName: Checkout doc repository + env: + GH_TOKEN: $(gh.token) - - bash: | - cd discretize-docs - rm -rf "en/$BRANCH_NAME" - mv ../html "en/$BRANCH_NAME" - touch .nojekyll - displayName: Set Doc Folder + - bash: | + cd discretize-docs + rm -rf "en/$BRANCH_NAME" + mv ../html "en/$BRANCH_NAME" + touch .nojekyll + displayName: Set Doc Folder - - bash: | - # Update latest symlink - cd discretize-docs/en - rm -f latest - ln -s "$BRANCH_NAME" latest - displayName: Point Latest to tag - condition: eq(variables.IS_TAG, true) + - bash: | + # Update latest symlink + cd discretize-docs/en + rm -f latest + ln -s "$BRANCH_NAME" latest + displayName: Point Latest to tag + condition: eq(variables.IS_TAG, true) - - bash: | - # Commit and push - cd discretize-docs - git add --all - git commit -am "Azure CI commit ref $(Build.SourceVersion)" - git push - displayName: Push documentation to discretize-docs - env: - GH_TOKEN: $(gh.token) + - bash: | + # Commit and push + cd discretize-docs + git add --all + git commit -am "Azure CI commit ref $(Build.SourceVersion)" + git push + displayName: Push documentation to discretize-docs + env: + GH_TOKEN: $(gh.token) diff --git a/.ci/azure/docs.yml b/.ci/azure/docs.yml index 51b3fcb6f..2b4060072 100644 --- a/.ci/azure/docs.yml +++ b/.ci/azure/docs.yml @@ -1,36 +1,35 @@ jobs: -- job: BuildDocs - displayName: "Build Documentation" - pool: - vmImage: ubuntu-latest - variables: - python.version: "3.11" - doc.build: True - PYVISTA_OFF_SCREEN: True - DISPLAY: ":99" - steps: - - bash: - git fetch --tags - displayName: Fetch tags + - job: BuildDocs + displayName: "Build Documentation" + pool: + vmImage: ubuntu-latest + variables: + python.version: "3.13" + doc.build: True + PYVISTA_OFF_SCREEN: True + DISPLAY: ":99" + steps: + - bash: git fetch --tags + displayName: Fetch tags - - bash: echo "##vso[task.prependpath]$CONDA/bin" - displayName: Add conda to PATH + - bash: echo "##vso[task.prependpath]$CONDA/bin" + displayName: Add conda to PATH - - bash: .ci/azure/setup_env.sh - displayName: Setup discretize environment + - bash: .ci/azure/setup_env.sh + displayName: Setup discretize environment - - bash: | - source activate discretize-test - make -C docs html - displayName: 'Building HTML' + - bash: | + source activate discretize-test + make -C docs html + displayName: "Building HTML" - - bash: | - source activate discretize-test - make -C docs linkcheck - displayName: 'Checking Links' + - bash: | + source activate discretize-test + make -C docs linkcheck + displayName: "Checking Links" - - task: PublishPipelineArtifact@1 - inputs: - targetPath: 'docs/_build/html' - artifact: 'html_docs' - parallel: true + - task: PublishPipelineArtifact@1 + inputs: + targetPath: "docs/_build/html" + artifact: "html_docs" + parallel: true diff --git a/.ci/azure/sdist.yml b/.ci/azure/sdist.yml index 60c2c1361..7cb724e8a 100644 --- a/.ci/azure/sdist.yml +++ b/.ci/azure/sdist.yml @@ -1,29 +1,28 @@ jobs: -- job: - displayName: "Build source dist." - pool: - vmImage: ubuntu-latest - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: "3.11" + - job: + displayName: "Build source dist." + pool: + vmImage: ubuntu-latest + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.11" - - bash: - git fetch --tags - displayName: Fetch tags + - bash: git fetch --tags + displayName: Fetch tags - - bash: | - set -o errexit - python -m pip install --upgrade pip - pip install build - displayName: Install source build tools. + - bash: | + set -o errexit + python -m pip install --upgrade pip + pip install build + displayName: Install source build tools. - - bash: | - python -m build --skip-dependency-check --sdist . - ls -la dist - displayName: Build Source + - bash: | + python -m build --skip-dependency-check --sdist . + ls -la dist + displayName: Build Source - - task: PublishPipelineArtifact@1 - inputs: - targetPath: 'dist' - artifact: 'source_dist' \ No newline at end of file + - task: PublishPipelineArtifact@1 + inputs: + targetPath: "dist" + artifact: "source_dist" diff --git a/.ci/azure/setup_env.sh b/.ci/azure/setup_env.sh index 11baca133..dd42ea428 100755 --- a/.ci/azure/setup_env.sh +++ b/.ci/azure/setup_env.sh @@ -17,6 +17,9 @@ fi if [[ "$is_free_threaded" == "true" || "$is_bare" == "true" ]]; then cp .ci/environment_test_bare.yml environment_test_with_pyversion.yml echo " - python-freethreading="$PYTHON_VERSION >> environment_test_with_pyversion.yml +elif [[ "$do_doc" == "true" ]]; then + cp .ci/environment_docs.yml environment_test_with_pyversion.yml + echo " - python="$PYTHON_VERSION >> environment_test_with_pyversion.yml else cp .ci/environment_test.yml environment_test_with_pyversion.yml echo " - python="$PYTHON_VERSION >> environment_test_with_pyversion.yml diff --git a/.ci/azure/style.yml b/.ci/azure/style.yml index 3604f9e45..5e61e61e7 100644 --- a/.ci/azure/style.yml +++ b/.ci/azure/style.yml @@ -1,4 +1,17 @@ jobs: + - job: + displayName: Check version consistency + pool: + vmImage: ubuntu-latest + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.13" + - bash: pip install pyyaml + displayName: "Install dependencies to run the checks" + - script: python .ci/check_version_sync.py + displayName: "Check versions" + - job: displayName: Run style checks with Black pool: @@ -6,7 +19,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: "3.11" + versionSpec: "3.13" - bash: .ci/install_style.sh displayName: "Install dependencies to run the checks" - script: black --check . @@ -19,8 +32,8 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: "3.11" + versionSpec: "3.13" - bash: .ci/install_style.sh displayName: "Install dependencies to run the checks" - script: flake8 - displayName: "Run flake8" \ No newline at end of file + displayName: "Run flake8" diff --git a/.ci/azure/test.yml b/.ci/azure/test.yml index 04599008a..f978eb062 100644 --- a/.ci/azure/test.yml +++ b/.ci/azure/test.yml @@ -1,105 +1,105 @@ jobs: -- job: - strategy: - matrix: - linux-Python311: - image: ubuntu-latest - python.version: '3.11' - coverage: True - linux-Python312: - image: ubuntu-latest - python.version: '3.12' - linux-Python313: - image: ubuntu-latest - python.version: '3.13' - linux-Python313t: - image: ubuntu-latest - python.version: '3.13' - environ.bare: True - python.freethreading: True - coverage: True - linux-Python314: - image: ubuntu-latest - environ.bare: True - python.version: '3.14' - linux-Python314t: - image: ubuntu-latest - python.version: '3.14' - environ.bare: True - python.freethreading: True - osx-Python311: - image: macOS-latest - python.version: '3.11' - osx-Python312: - image: macOS-latest - python.version: '3.12' - osx-Python313: - image: macOS-latest - python.version: '3.13' - osx-Python313t: - image: macOS-latest - python.version: '3.13' - python.freethreading: True - osx-Python314: - image: macOS-latest - python.version: '3.14' - environ.bare: True - osx-Python314t: - image: macOS-latest - python.version: '3.14' - environ.bare: True - python.freethreading: True - win-Python311: - image: windows-latest - python.version: '3.11' - win-Python312: - image: windows-latest - python.version: '3.12' - win-Python313: - image: windows-latest - python.version: '3.13' - win-Python313t: - image: windows-latest - python.version: '3.13' - environ.bare: True - python.freethreading: True - win-Python314: - image: windows-latest - environ.bare: True - python.version: '3.14' - win-Python314t: - image: windows-latest - python.version: '3.14' - environ.bare: True - python.freethreading: True - displayName: "${{ variables.image }} ${{ variables.python.version }}" - pool: - vmImage: $(image) - variables: - varOS: $(Agent.OS) - ARCH: $(Agent.OSArchitecture) - steps: - - bash: .ci/azure/setup_miniconda_macos.sh - displayName: Install miniconda on mac - condition: eq(variables.varOS, 'Darwin') + - job: + strategy: + matrix: + linux-Python311: + image: ubuntu-latest + python.version: "3.11" + linux-Python312: + image: ubuntu-latest + python.version: "3.12" + linux-Python313: + image: ubuntu-latest + python.version: "3.13" + coverage: True + linux-Python313t: + image: ubuntu-latest + python.version: "3.13" + environ.bare: True + python.freethreading: True + coverage: True + linux-Python314: + image: ubuntu-latest + environ.bare: True + python.version: "3.14" + linux-Python314t: + image: ubuntu-latest + python.version: "3.14" + environ.bare: True + python.freethreading: True + osx-Python311: + image: macOS-latest + python.version: "3.11" + osx-Python312: + image: macOS-latest + python.version: "3.12" + osx-Python313: + image: macOS-latest + python.version: "3.13" + osx-Python313t: + image: macOS-latest + python.version: "3.13" + python.freethreading: True + osx-Python314: + image: macOS-latest + python.version: "3.14" + environ.bare: True + osx-Python314t: + image: macOS-latest + python.version: "3.14" + environ.bare: True + python.freethreading: True + win-Python311: + image: windows-latest + python.version: "3.11" + win-Python312: + image: windows-latest + python.version: "3.12" + win-Python313: + image: windows-latest + python.version: "3.13" + win-Python313t: + image: windows-latest + python.version: "3.13" + environ.bare: True + python.freethreading: True + win-Python314: + image: windows-latest + environ.bare: True + python.version: "3.14" + win-Python314t: + image: windows-latest + python.version: "3.14" + environ.bare: True + python.freethreading: True + displayName: "${{ variables.image }} ${{ variables.python.version }}" + pool: + vmImage: $(image) + variables: + varOS: $(Agent.OS) + ARCH: $(Agent.OSArchitecture) + steps: + - bash: .ci/azure/setup_miniconda_macos.sh + displayName: Install miniconda on mac + condition: eq(variables.varOS, 'Darwin') - - bash: echo "##vso[task.prependpath]$CONDA/bin" - displayName: Add conda to PATH - condition: ne(variables.varOS, 'Windows_NT') + - bash: echo "##vso[task.prependpath]$CONDA/bin" + displayName: Add conda to PATH + condition: ne(variables.varOS, 'Windows_NT') - - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" - displayName: Add conda to PATH - condition: eq(variables.varOS, 'Windows_NT') + - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" + displayName: Add conda to PATH + condition: eq(variables.varOS, 'Windows_NT') - - bash: .ci/azure/setup_env.sh - displayName: Setup discretize environment + - bash: .ci/azure/setup_env.sh + displayName: Setup discretize environment - - bash: .ci/azure/run_tests.sh - displayName: 'Testing' + - bash: .ci/azure/run_tests.sh + displayName: "Testing" - - bash: | - curl -Os https://uploader.codecov.io/latest/linux/codecov - chmod +x codecov - ./codecov - displayName: 'Upload coverage to codecov.io' - condition: variables.coverage + - bash: | + curl -Os https://uploader.codecov.io/latest/linux/codecov + chmod +x codecov + ./codecov + displayName: "Upload coverage to codecov.io" + condition: variables.coverage diff --git a/.ci/check_version_sync.py b/.ci/check_version_sync.py new file mode 100644 index 000000000..3432614e0 --- /dev/null +++ b/.ci/check_version_sync.py @@ -0,0 +1,245 @@ +"""Verify version pins are consistent across pyproject.toml, .pre-commit-config.yaml, +and .ci/environment*.yml files. + +Exit 1 if any mismatch is found, 0 otherwise. +""" + +import re +import sys +import tomllib +import pathlib + +try: + import yaml +except ImportError: + print( + "ERROR: pyyaml is required. Install it with: pip install pyyaml", + file=sys.stderr, + ) + sys.exit(2) + +ROOT = pathlib.Path(__file__).parent.parent +PYPROJECT = ROOT / "pyproject.toml" +PRE_COMMIT_CONFIG = ROOT / ".pre-commit-config.yaml" +CI_DIR = ROOT / ".ci" + +# Maps a substring of the pre-commit repo URL to the corresponding pip package name. +# Only repos whose rev tracks the package version should be listed here. +PRECOMMIT_REPO_PACKAGES = { + "psf/black": "black", + "pycqa/flake8": "flake8", +} + + +def normalize_name(name: str) -> str: + """Normalize a package name to a canonical form for comparison.""" + return re.sub(r"[-_.]+", "_", name).lower() + + +def parse_pip_req(req: str): + """Return (normalized_name, op, version) for a pip requirement string, or None.""" + m = re.match( + r"^([A-Za-z0-9][A-Za-z0-9._\-]*)\s*(==|>=|<=|!=|~=)\s*(\S+)$", req.strip() + ) + if m: + return normalize_name(m.group(1)), m.group(2), m.group(3) + return None + + +def parse_conda_dep(dep: str): + """Return (normalized_name, op, version) for a conda dependency string, or None. + + Normalizes conda's single-= exact-version operator to == for comparison. + """ + m = re.match( + r"^([A-Za-z0-9][A-Za-z0-9._\-]*)\s*(==|>=|<=|!=|=)\s*(\S+)$", dep.strip() + ) + if m: + op = "==" if m.group(2) == "=" else m.group(2) + return normalize_name(m.group(1)), op, m.group(3) + return None + + +def load_pyproject(): + """Return (optional_deps, main_deps, tool_black_version). + + optional_deps: {group: {normalized_name: (op, version)}} + main_deps: {normalized_name: (op, version)} + tool_black_version: plain version string from [tool.black] required-version, or None + """ + with open(PYPROJECT, "rb") as f: + data = tomllib.load(f) + + optional_deps: dict[str, dict[str, tuple[str, str]]] = {} + for group, reqs in data["project"]["optional-dependencies"].items(): + parsed = {} + for req in reqs: + result = parse_pip_req(req) + if result: + name, op, ver = result + parsed[name] = (op, ver) + optional_deps[group] = parsed + + main_deps: dict[str, tuple[str, str]] = {} + for req in data["project"].get("dependencies", []): + result = parse_pip_req(req) + if result: + name, op, ver = result + main_deps[name] = (op, ver) + + tool_black_version: str | None = ( + data.get("tool", {}).get("black", {}).get("required-version") + ) + + return optional_deps, main_deps, tool_black_version + + +def load_precommit(): + """Return {normalized_name: (op, version)} from .pre-commit-config.yaml.""" + with open(PRE_COMMIT_CONFIG) as f: + config = yaml.safe_load(f) + + result: dict[str, tuple[str, str]] = {} + for repo_cfg in config.get("repos", []): + repo_url = repo_cfg.get("repo", "") + rev = repo_cfg.get("rev", "") + + for url_fragment, pkg_name in PRECOMMIT_REPO_PACKAGES.items(): + if url_fragment.lower() in repo_url.lower(): + result[normalize_name(pkg_name)] = ("==", rev) + + for hook in repo_cfg.get("hooks", []): + for dep in hook.get("additional_dependencies", []): + parsed = parse_pip_req(dep) + if parsed: + name, op, ver = parsed + result[name] = (op, ver) + + return result + + +def load_conda_env(env_file: pathlib.Path): + """Return {normalized_name: (op, version)} from a conda environment YAML.""" + with open(env_file) as f: + env = yaml.safe_load(f) + + result: dict[str, tuple[str, str]] = {} + for dep in env.get("dependencies", []): + if isinstance(dep, str): + parsed = parse_conda_dep(dep) + if parsed: + name, op, ver = parsed + result[name] = (op, ver) + elif isinstance(dep, dict): + for pip_dep in dep.get("pip", []): + parsed = parse_pip_req(pip_dep) + if parsed: + name, op, ver = parsed + result[name] = (op, ver) + return result + + +def main() -> int: + optional_deps, main_deps, tool_black_version = load_pyproject() + precommit_versions = load_precommit() + style_deps = optional_deps.get("style", {}) + doc_deps = optional_deps.get("doc", {}) + + errors: list[str] = [] + + # Check A: pyproject.toml [style] ↔ .pre-commit-config.yaml + for pkg, (op, ver) in style_deps.items(): + if pkg in precommit_versions: + pre_op, pre_ver = precommit_versions[pkg] + if (pre_op, pre_ver) != (op, ver): + errors.append( + f" {pkg}: pyproject.toml [style] has {op}{ver}," + f" .pre-commit-config.yaml has {pre_op}{pre_ver}" + ) + else: + errors.append( + f" {pkg}: in pyproject.toml [style] but not found" + f" in .pre-commit-config.yaml" + ) + if errors: + print( + "FAIL: pyproject.toml [style] and .pre-commit-config.yaml are out of sync:" + ) + for e in errors: + print(e) + errors.clear() + style_check_failed = True + else: + style_check_failed = False + + # Check A2: [tool.black] required-version ↔ black in [style] + black_key = normalize_name("black") + if tool_black_version is not None and black_key in style_deps: + style_op, style_ver = style_deps[black_key] + if style_ver != tool_black_version: + print( + f"FAIL: black version mismatch within pyproject.toml:\n" + f" [style] has black{style_op}{style_ver}," + f" [tool.black] required-version = '{tool_black_version}'" + ) + style_check_failed = True + + # Check B: pyproject.toml [doc] ↔ .ci/environment_docs.yml + env_docs_file = CI_DIR / "environment_docs.yml" + doc_check_failed = False + if env_docs_file.exists(): + conda_doc = load_conda_env(env_docs_file) + for pkg, (op, ver) in doc_deps.items(): + if op != "==": + continue # only track exact pins + if pkg in conda_doc: + c_op, c_ver = conda_doc[pkg] + if (c_op, c_ver) != (op, ver): + errors.append( + f" {pkg}: pyproject.toml [doc] has =={ver}," + f" environment_docs.yml has {c_op}{c_ver}" + ) + else: + errors.append( + f" {pkg}: in pyproject.toml [doc] (=={ver})" + f" but not found in environment_docs.yml" + ) + if errors: + print( + "FAIL: pyproject.toml [doc] and .ci/environment_docs.yml" + " are out of sync:" + ) + for e in errors: + print(e) + errors.clear() + doc_check_failed = True + + # Check C: pyproject.toml [dependencies] ↔ each .ci/environment*.yml + dep_check_failed = False + for env_file in sorted(CI_DIR.glob("environment*.yml")): + conda_env = load_conda_env(env_file) + for pkg, (op, ver) in main_deps.items(): + if pkg in conda_env: + c_op, c_ver = conda_env[pkg] + if (c_op, c_ver) != (op, ver): + errors.append( + f" {pkg}: pyproject.toml [dependencies] has {op}{ver}," + f" {env_file.name} has {c_op}{c_ver}" + ) + if errors: + print( + f"FAIL: pyproject.toml [dependencies] and .ci/{env_file.name}" + f" are out of sync:" + ) + for e in errors: + print(e) + errors.clear() + dep_check_failed = True + + if any([style_check_failed, doc_check_failed, dep_check_failed]): + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.ci/environment_docs.yml b/.ci/environment_docs.yml new file mode 100644 index 000000000..a7a08c6ab --- /dev/null +++ b/.ci/environment_docs.yml @@ -0,0 +1,35 @@ +name: discretize-test +channels: + - conda-forge +dependencies: + - numpy>=1.22.4 + - scipy>=1.12 + + # optionals + - vtk>=9 + - pyvista + - omf + - matplotlib + + # documentation + - sphinx==9.1 + - pydata-sphinx-theme==0.19.0 + - sphinx-gallery==0.21.0 + - numpydoc==1.10.0 + - jupyter + - graphviz + - pillow + - pooch + + # testing + - sympy + - pytest + - pytest-cov + + # Building + - pip + - meson-python>=0.15.0 + - meson + - ninja + - cython>=3.1.0 + - setuptools_scm diff --git a/.ci/environment_test.yml b/.ci/environment_test.yml index 5b577ea89..58455cc24 100644 --- a/.ci/environment_test.yml +++ b/.ci/environment_test.yml @@ -3,24 +3,14 @@ channels: - conda-forge dependencies: - numpy>=1.22.4 - - scipy>=1.8 + - scipy>=1.12 # optionals - - vtk>=6 + - vtk>=9 - pyvista - omf - matplotlib - # documentation - - sphinx==8.1.3 - - pydata-sphinx-theme==0.16.1 - - sphinx-gallery==0.19.0 - - numpydoc==1.9.0 - - jupyter - - graphviz - - pillow - - pooch - # testing - sympy - pytest @@ -28,7 +18,7 @@ dependencies: # Building - pip - - meson-python>=0.14.0 + - meson-python>=0.15.0 - meson - ninja - cython>=3.1.0 diff --git a/.ci/environment_test_bare.yml b/.ci/environment_test_bare.yml index 7443946a9..00296beac 100644 --- a/.ci/environment_test_bare.yml +++ b/.ci/environment_test_bare.yml @@ -12,7 +12,7 @@ dependencies: # Building - pip - - meson-python>=0.14.0 + - meson-python>=0.15.0 - meson - ninja - cython>=3.1.0 diff --git a/.github/workflows/build_distributions.yml b/.github/workflows/build_distributions.yml index 396d0f5bf..763f93ac4 100644 --- a/.github/workflows/build_distributions.yml +++ b/.github/workflows/build_distributions.yml @@ -9,13 +9,21 @@ jobs: strategy: matrix: # macos-15-intel is an Intel runner, macos-14 is Apple silicon - os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-14] + os: + [ + ubuntu-latest, + ubuntu-24.04-arm, + windows-latest, + windows-11-arm, + macos-15-intel, + macos-14, + ] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Build wheels - uses: pypa/cibuildwheel@v3.2.0 + uses: pypa/cibuildwheel@v4.1.0 - uses: actions/upload-artifact@v4 with: @@ -26,7 +34,7 @@ jobs: name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e33561e47..bd568d81b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,27 @@ repos: + - repo: local + hooks: + - id: check-version-sync + name: check version pin consistency + entry: python .ci/check_version_sync.py + language: python + additional_dependencies: [pyyaml] + pass_filenames: false + files: ^(pyproject\.toml|\.pre-commit-config\.yaml|\.ci/environment.*\.yml)$ - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.3.0 + rev: 26.5.1 hooks: - id: black language_version: python3.11 - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 + rev: 7.3.0 hooks: - id: flake8 language_version: python3.11 additional_dependencies: - - flake8-bugbear==23.12.2 - - flake8-builtins==2.2.0 + - flake8-bugbear==25.11.29 + - flake8-builtins==3.1.0 - flake8-mutable==1.2.0 - - flake8-rst-docstrings==0.3.0 + - flake8-rst-docstrings==0.4.0 - flake8-docstrings==1.7.0 - - flake8-pyproject==1.2.3 \ No newline at end of file + - flake8-pyproject==1.2.4 diff --git a/discretize/__init__.py b/discretize/__init__.py index 73432f1e7..e23302999 100644 --- a/discretize/__init__.py +++ b/discretize/__init__.py @@ -53,15 +53,13 @@ file_test = os.path.dirname(os.path.abspath(__file__)) + "/_extensions/tree_ext.pyx" if os.path.isfile(file_test): # Then we are being run from a repository - raise ImportError( - """ + raise ImportError(""" It would appear that discretize is being imported from its source code directory and is unable to load its compiled extension modules. Try changing your directory and re-launching your python interpreter. If this was intentional, you need to install discretize in an editable mode. - """ - ) + """) else: raise err from discretize import tests diff --git a/discretize/_extensions/simplex_helpers.pyx b/discretize/_extensions/simplex_helpers.pyx index a09e3aae7..9301e183d 100644 --- a/discretize/_extensions/simplex_helpers.pyx +++ b/discretize/_extensions/simplex_helpers.pyx @@ -8,7 +8,6 @@ from libcpp.unordered_map cimport unordered_map from cython.operator cimport dereference cimport cython cimport numpy as np -from cython cimport view from libc.math cimport sqrt cdef extern from "triplet.h": diff --git a/discretize/mixins/mesh_io.py b/discretize/mixins/mesh_io.py index a9218db0b..4d519afba 100644 --- a/discretize/mixins/mesh_io.py +++ b/discretize/mixins/mesh_io.py @@ -192,12 +192,8 @@ def _readModelUBC_2D(mesh, file_name): model.extend([float(val) for val in line.split()]) model = np.asarray(model) if not len(model) == mesh.nC: - raise Exception( - """Something is not right, expected size is {:d} - but unwrap vector is size {:d}""".format( - mesh.nC, len(model) - ) - ) + raise Exception("""Something is not right, expected size is {:d} + but unwrap vector is size {:d}""".format(mesh.nC, len(model))) return model.reshape(mesh.vnC, order="F")[:, ::-1].reshape(-1, order="F") diff --git a/discretize/mixins/vtk_mod.py b/discretize/mixins/vtk_mod.py index 82a54817f..1112beaa8 100644 --- a/discretize/mixins/vtk_mod.py +++ b/discretize/mixins/vtk_mod.py @@ -946,7 +946,7 @@ def vtk_to_simplex_mesh(SimplexMesh, vtuGrid): _, _nps = load_vtk() # check if all of the cells are the same type - cell_types = np.unique(_nps.vtk_to_numpy(vtuGrid.GetCellTypesArray())) + cell_types = np.unique(_nps.vtk_to_numpy(vtuGrid.GetCellTypes())) if len(cell_types) > 1: raise ValueError( "Incompatible unstructured grid. All cell's must have the same type." diff --git a/discretize/utils/interpolation_utils.py b/discretize/utils/interpolation_utils.py index 2c3c866e2..8140b52f2 100644 --- a/discretize/utils/interpolation_utils.py +++ b/discretize/utils/interpolation_utils.py @@ -26,8 +26,7 @@ ) if os.path.isfile(file_test): # Then we are being run from a repository - print( - """ + print(""" Unable to import interputils_cython. It would appear that discretize is being imported from its repository. @@ -36,8 +35,7 @@ python setup.py build_ext --inplace to build the cython code. - """ - ) + """) _interpCython = False diff --git a/pyproject.toml b/pyproject.toml index ca8148a9d..e38f7039e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,12 +18,8 @@ dynamic = ["version"] description = 'Discretization tools for finite volume and inverse problems' readme = 'README.rst' requires-python = '>=3.11' -authors = [ - {name = 'SimPEG developers', email = 'rowanc1@gmail.com'}, -] -keywords = [ - 'finite volume', 'discretization', 'pde', 'ode' -] +authors = [{ name = 'SimPEG developers', email = 'rowanc1@gmail.com' }] +keywords = ['finite volume', 'discretization', 'pde', 'ode'] # Note: Python and NumPy upper version bounds should be set correctly in # release branches, see: @@ -57,36 +53,31 @@ file = 'LICENSE' [project.optional-dependencies] plot = ["matplotlib"] -viz = ["vtk>=6", "pyvista"] +viz = ["vtk>=9", "pyvista"] omf = ["omf"] all = ["discretize[plot,viz,omf]"] doc = [ - "sphinx==8.1.3", - "pydata-sphinx-theme==0.16.1", - "sphinx-gallery==0.19.0", - "numpydoc==1.9.0", + "sphinx==9.1", + "pydata-sphinx-theme==0.19.0", + "sphinx-gallery==0.21.0", + "numpydoc==1.10.0", "jupyter", "graphviz", "pillow", "pooch", "discretize[all]", ] -test = [ - "pytest", - "pytest-cov", - "sympy", - "discretize[doc,all]", -] +test = ["pytest", "pytest-cov", "sympy", "discretize[doc,all]"] # when changing these, make sure to keep it consistent with .pre-commit-config. style = [ - "black==24.3.0", - "flake8==7.0.0", - "flake8-bugbear==23.12.2", - "flake8-builtins==2.2.0", + "black==26.5.1", + "flake8==7.3.0", + "flake8-bugbear==25.11.29", + "flake8-builtins==3.1.0", "flake8-mutable==1.2.0", - "flake8-rst-docstrings==0.3.0", + "flake8-rst-docstrings==0.4.0", "flake8-docstrings==1.7.0", - "flake8-pyproject==1.2.3", + "flake8-pyproject==1.2.4", ] build = [ "meson-python>=0.15.0", @@ -105,12 +96,10 @@ Repository = 'http://github.com/simpeg/discretize.git' [tool.setuptools_scm] [tool.cibuildwheel] -# skip building wheels for python 3.6, 3.7, 3.8, 3.9, all pypy versions, and specialty linux -# processors (still does arm builds though). -# skip windows 32bit -skip = "cp38-* cp39-* cp310-* *_ppc64le *_i686 *_s390x *-win32 cp310-win_arm64" +build = "cp311* cp312* cp313* cp314*" +skip = "*_ppc64le *_i686 *_s390x *-win32" +# skip specialty linux and windows 32bit build-verbosity = 3 -enable = ["cpython-freethreading"] # test importing discretize to make sure externals are loadable. test-command = 'python -c "import discretize; print(discretize.__version__)"' @@ -118,9 +107,7 @@ test-command = 'python -c "import discretize; print(discretize.__version__)"' # use the visual studio compilers [tool.cibuildwheel.windows.config-settings] -setup-args = [ - '--vsenv' -] +setup-args = ['--vsenv'] [tool.coverage.run] branch = true @@ -152,8 +139,8 @@ exclude_also = [ ] [tool.black] -required-version = '24.3.0' -target-version = ['py38', 'py39', 'py310', 'py311'] +required-version = '26.5.1' +target-version = ['py311', 'py312', 'py313', 'py314'] [tool.flake8] extend-ignore = [ @@ -184,7 +171,7 @@ exclude = [ 'docs/tutorials/*', 'docs/*', 'discretize/_extensions/*.py', - '.ci/*' + '.ci/*', ] per-file-ignores = [ # disable unused-imports errors on __init__.py @@ -212,7 +199,10 @@ rst-roles = [ 'ref', 'data', # Python programming language: - 'py:func','py:mod','py:attr','py:meth', + 'py:func', + 'py:mod', + 'py:attr', + 'py:meth', ] rst-directives = [ diff --git a/tests/boundaries/test_errors.py b/tests/boundaries/test_errors.py index 41430b0a1..73cd2b343 100644 --- a/tests/boundaries/test_errors.py +++ b/tests/boundaries/test_errors.py @@ -2,7 +2,6 @@ import numpy as np import discretize - rng = np.random.default_rng(53679) diff --git a/tests/boundaries/test_tensor_boundary_poisson.py b/tests/boundaries/test_tensor_boundary_poisson.py index 5f5b191d6..4af12f53a 100644 --- a/tests/boundaries/test_tensor_boundary_poisson.py +++ b/tests/boundaries/test_tensor_boundary_poisson.py @@ -5,7 +5,6 @@ from discretize import utils from scipy.sparse.linalg import spsolve - MESHTYPES = ["uniformTensorMesh"] diff --git a/tests/tree/test_safeguards.py b/tests/tree/test_safeguards.py index 63d19c478..86127d70c 100644 --- a/tests/tree/test_safeguards.py +++ b/tests/tree/test_safeguards.py @@ -8,7 +8,6 @@ from discretize import TreeMesh from discretize.tree_mesh import TreeMeshNotFinalizedError - PROPERTIES = [ "average_cell_to_face", "average_cell_to_face_x", diff --git a/tutorials/inner_products/3_calculus.py b/tutorials/inner_products/3_calculus.py index 0a3de5248..910ff476e 100644 --- a/tutorials/inner_products/3_calculus.py +++ b/tutorials/inner_products/3_calculus.py @@ -32,7 +32,6 @@ import numpy as np import matplotlib.pyplot as plt - ##################################################### # Gradient # -------- diff --git a/tutorials/pde/1_poisson.py b/tutorials/pde/1_poisson.py index d8665d1b7..77b042d4b 100644 --- a/tutorials/pde/1_poisson.py +++ b/tutorials/pde/1_poisson.py @@ -97,7 +97,6 @@ import numpy as np from discretize.utils import sdiag - ############################################### # # Solving the Problem