diff --git a/.github/workflows/cicd_tests.yml b/.github/workflows/cicd_tests.yml new file mode 100644 index 0000000000..cfb5dce328 --- /dev/null +++ b/.github/workflows/cicd_tests.yml @@ -0,0 +1,328 @@ +name: tests + +on: + # quick tests for pull requests and the releasing branches + push: + branches: + - dev + - main + - releasing/* + paths-ignore: # skip if only docs are modified + - '**.md' + - '**.rst' + - 'docs/**' + pull_request: + head_ref-ignore: + - dev + paths-ignore: # skip if only docs are modified + - '**.md' + - '**.rst' + - 'docs/**' + +concurrency: + # automatically cancel the previously triggered workflows when there's a newer version + group: cicd-tests-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +# Supported versions of Python and PyTorch are listed here for use below and as documentation +env: + # supported versions of Python + PYTHON_VER1: '3.10' + PYTHON_VER2: '3.11' + PYTHON_VER3: '3.12' + PYTHON_VER4: '3.13' + PYTHON_VER5: '3.14' + # supported versions of PyTorch + PYTORCH_VER1: '2.8.0' + PYTORCH_VER2: '2.9.1' + PYTORCH_VER3: '2.10.0' + PYTORCH_VER4: '2.11.0' + TORCHVISION_VER: '0.23.0' # used for testing with lowest PyTorch version + PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python # https://github.com/Project-MONAI/MONAI/issues/4354 + +# These jobs run the CICD tests, type checking, and testing packaging. These use the minimum supported versions of +# Python and PyTorch in many places using the above environment variables but also hard coded where necessary. +# When support is dropped for a version it is important to update these as appropriate. + +jobs: + static-checks: # Perform static type and other checks using runtests.sh + runs-on: ubuntu-latest + strategy: + matrix: + opt: ["codeformat", "pytype", "mypy"] + steps: + - name: Clean unused tools + run: | + find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc /usr/local/.ghcup + sudo docker system prune -f + + - uses: actions/checkout@v6 + - name: Set up Python ${{ env.PYTHON_VER1 }} + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VER1 }} + cache: 'pip' + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + python -m pip install --no-build-isolation -r requirements-dev.txt + - name: Lint and type check + run: | + # clean up temporary files + $(pwd)/runtests.sh --build --clean + # Github actions have multiple cores, so parallelize pytype + $(pwd)/runtests.sh --build --${{ matrix.opt }} -j $(nproc --all) + + min-dep: # Test with minumum dependencies installed for different OS, Python, and PyTorch combinations + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # Base test cases are to test on all three OSes with the lowest versions of Python and PyTorch, other cases are + # added below as special cases to cover other Python and PyTorch versions under Ubuntu + os: [windows-latest, macOS-latest, ubuntu-latest] + python-version: ['3.10'] + pytorch-version: ['2.8.0'] + include: + # Test Python versions under Ubuntu with lowest PyTorch version supported + - os: ubuntu-latest + pytorch-version: '2.8.0' + python-version: '3.11' + - os: ubuntu-latest + pytorch-version: '2.8.0' + python-version: '3.12' + - os: ubuntu-latest + pytorch-version: '2.8.0' + python-version: '3.13' + - os: ubuntu-latest + pytorch-version: '2.9.0' + python-version: '3.14' + + # Test PyTorch versions under Ubuntu with lowest Python version supported + - os: ubuntu-latest + python-version: '3.10' + pytorch-version: '2.9.1' + - os: ubuntu-latest + python-version: '3.10' + pytorch-version: '2.10.0' + - os: ubuntu-latest + python-version: '3.10' + pytorch-version: '2.11.0' + + timeout-minutes: 40 + steps: + - if: runner.os == 'Linux' + name: Clean unused tools + run: | + find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc /usr/local/.ghcup + sudo docker system prune -f + - uses: actions/checkout@v6 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: Prepare pip wheel + run: | + which python + python -m pip install --upgrade pip wheel + python -m pip install --user more-itertools>=8.0 + - name: Install the minimum dependencies + run: | + # min. requirements + python -m pip install torch==${{ matrix.pytorch-version }} + python -m pip install -r requirements-min.txt + python -m pip list + BUILD_MONAI=0 python setup.py develop # no compile of extensions + shell: bash + - if: matrix.os == 'linux-gpu-runner' + name: Print GPU Info + run: | + nvidia-smi + python -c 'import torch; print(torch.rand(2,2).to("cuda:0"))' + shell: bash + - name: Run quick tests + run: | + python -c 'import torch; print(torch.__version__); print(torch.rand(5,3))' + python -c "import monai; monai.config.print_config()" + # ./runtests.sh --min + shell: bash + env: + QUICKTEST: True + NGC_API_KEY: ${{ secrets.NGC_API_KEY }} + NGC_ORG: ${{ secrets.NGC_ORG }} + NGC_TEAM: ${{ secrets.NGC_TEAM }} + + full-dep: # Test with full dependencies installed for different OS runners + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-latest, macOS-latest, ubuntu-latest] + timeout-minutes: 120 + env: + QUICKTEST: True + steps: + - if: runner.os == 'Linux' + name: Clean unused tools + run: | + find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc /usr/local/.ghcup + sudo docker system prune -f + - if: runner.os == 'windows' + name: Config pagefile (Windows only) + uses: al-cheb/configure-pagefile-action@v1.5 + with: + minimum-size: 8GB + maximum-size: 16GB + disk-root: "D:" + - uses: actions/checkout@v6 + - name: Set up Python ${{ env.PYTHON_VER1 }} + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VER1 }} + cache: 'pip' + - name: Prepare pip wheel + run: | + which python + python -m pip install --upgrade pip wheel + - if: runner.os == 'windows' + name: Install torch cpu from pytorch.org (Windows only) + run: | + python -m pip install torch==${PYTORCH_VER1} torchvision==${TORCHVISION_VER}+cpu --index-url https://download.pytorch.org/whl/cpu + shell: bash + - if: runner.os == 'Linux' + name: Install itk pre-release (Linux only) + run: | + python -m pip install --pre -U itk + - name: Install the complete dependencies + run: | + python -m pip install --user --upgrade pip wheel pybind11 + python -m pip install torch==${PYTORCH_VER1} torchvision==${TORCHVISION_VER} + cat "requirements-dev.txt" + python -m pip install --no-build-isolation -r requirements-dev.txt + python -m pip list + python -m pip install -e . # test no compile installation + shell: bash + - name: Run compiled (${{ runner.os }}) + run: | + python -m pip uninstall -y monai + BUILD_MONAI=1 python -m pip install -e . # compile the cpp extensions + shell: bash + - name: Run quick tests + run: | + python -c 'import torch; print(torch.__version__); print(torch.rand(5,3))' + python -c "import monai; monai.config.print_config()" + # python -m unittest -v + + packaging: # Test package generation + runs-on: ubuntu-latest + env: + QUICKTEST: True + shell: bash + steps: + - name: Clean unused tools + run: | + find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc /usr/local/.ghcup + sudo docker system prune -f + + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Set up Python ${{ env.PYTHON_VER1 }} + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VER1 }} + cache: 'pip' + - name: Install dependencies + run: | + python -m pip install --user --upgrade pip setuptools wheel twine packaging + # install the latest pytorch for testing + # however, "pip install monai*.tar.gz" will build cpp/cuda with an isolated + # fresh torch installation according to pyproject.toml + python -m pip install torch==${PYTORCH_VER1} torchvision --extra-index-url https://download.pytorch.org/whl/cpu + - name: Check packages + run: | + pip uninstall monai + pip list | grep -iv monai + git fetch --depth=1 origin +refs/tags/*:refs/tags/* + set -e + + # build tar.gz and wheel + python setup.py check -m -s + python setup.py sdist bdist_wheel + python -m twine check dist/* + - run: echo "pwd=$PWD" >> $GITHUB_OUTPUT + id: root + - run: echo "tmp_dir=$(mktemp -d)" >> $GITHUB_OUTPUT + id: mktemp + - name: Move packages + run: | + printf ${{ steps.root.outputs.pwd }} + printf ${{ steps.mktemp.outputs.tmp_dir }} + # move packages to a temp dir + cp dist/monai* "${{ steps.mktemp.outputs.tmp_dir }}" + rm -r build dist monai.egg-info + cd "${{ steps.mktemp.outputs.tmp_dir }}" + ls -al + - name: Install wheel file + working-directory: ${{ steps.mktemp.outputs.tmp_dir }} + run: | + # install from wheel + python -m pip install monai*.whl --extra-index-url https://download.pytorch.org/whl/cpu + python -c 'import monai; monai.config.print_config()' 2>&1 | grep -iv "unknown" + python -c 'import monai; print(monai.__file__)' + python -m pip uninstall -y monai + rm monai*.whl + - name: Install source archive + working-directory: ${{ steps.mktemp.outputs.tmp_dir }} + run: | + # install from tar.gz + name=$(ls *.tar.gz | head -n1) + echo $name + python -m pip install $name[all] --extra-index-url https://download.pytorch.org/whl/cpu + python -c 'import monai; monai.config.print_config()' 2>&1 | grep -iv "unknown" + python -c 'import monai; print(monai.__file__)' + - name: Quick test + working-directory: ${{ steps.mktemp.outputs.tmp_dir }} + run: | + # run min tests + cp ${{ steps.root.outputs.pwd }}/requirements*.txt . + cp -r ${{ steps.root.outputs.pwd }}/tests . + ls -al + python -m pip install --no-build-isolation -r requirements-dev.txt --extra-index-url https://download.pytorch.org/whl/cpu + python -m unittest -v + + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Set up Python ${{ env.PYTHON_VER1 }} + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VER1 }} + cache: 'pip' + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel + python -m pip install -r docs/requirements.txt --extra-index-url https://download.pytorch.org/whl/cpu + - name: Make html + run: | + cd docs/ + make clean + make html 2>&1 | tee tmp_log + if [[ $(grep -c "ERROR:" tmp_log) != 0 ]]; then echo "found errors"; grep "ERROR:" tmp_log; exit 1; fi + sed '/WARNING.*pip/d' tmp_log > tmp_log1; mv tmp_log1 tmp_log # monai#7133 + if [[ $(grep -c "WARNING:" tmp_log) != 0 ]]; then echo "found warnings"; grep "WARNING:" tmp_log; exit 1; fi + shell: bash diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml index 6103e30ae6..5a3936980a 100644 --- a/.github/workflows/conda.yml +++ b/.github/workflows/conda.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - python-version: ["3.9", "3.10"] + python-version: ["3.10", "3.11"] runs-on: ${{ matrix.os }} timeout-minutes: 46 # equal to max + 3*std over the last 600 successful runs env: diff --git a/.github/workflows/cron-ngc-bundle.yml b/.github/workflows/cron-ngc-bundle.yml index 766e5372b8..35c9deba53 100644 --- a/.github/workflows/cron-ngc-bundle.yml +++ b/.github/workflows/cron-ngc-bundle.yml @@ -18,10 +18,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v6 with: - python-version: '3.9' + python-version: '3.10' - name: cache weekly timestamp id: pip-cache run: echo "datew=$(date '+%Y-%V')" >> $GITHUB_OUTPUT diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index dca48dd9c8..f1ff708a43 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -67,7 +67,7 @@ jobs: if pgrep python; then pkill python; fi shell: bash - name: Upload coverage - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: false files: ./coverage.xml @@ -115,7 +115,7 @@ jobs: if pgrep python; then pkill python; fi shell: bash - name: Upload coverage - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: false files: ./coverage.xml @@ -220,7 +220,7 @@ jobs: if pgrep python; then pkill python; fi shell: bash - name: Upload coverage - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: false files: ./coverage.xml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 86ac1bf733..78b05c954c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -26,10 +26,10 @@ jobs: with: ref: dev fetch-depth: 0 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v6 with: - python-version: '3.9' + python-version: '3.10' - shell: bash run: | git describe @@ -37,7 +37,7 @@ jobs: python setup.py build cat build/lib/monai/_version.py - name: Upload version - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: _version.py path: build/lib/monai/_version.py diff --git a/.github/workflows/pythonapp-gpu.yml b/.github/workflows/pythonapp-gpu.yml index 036ef50f41..7de9936e9b 100644 --- a/.github/workflows/pythonapp-gpu.yml +++ b/.github/workflows/pythonapp-gpu.yml @@ -53,7 +53,7 @@ jobs: if [ ${{ matrix.environment }} = "PT230+CUDA124" ] then - PYVER=3.9 PYSFX=3 DISTUTILS=python3-distutils && \ + PYVER=3.10 PYSFX=3 DISTUTILS=python3-distutils && \ apt-get update && apt-get install -y --no-install-recommends \ curl \ pkg-config \ @@ -125,6 +125,6 @@ jobs: shell: bash - name: Upload coverage if: ${{ github.head_ref != 'dev' && github.event.pull_request.merged != true }} - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: files: ./coverage.xml diff --git a/.github/workflows/pythonapp-min.yml b/.github/workflows/pythonapp-min.yml deleted file mode 100644 index 84edbd9283..0000000000 --- a/.github/workflows/pythonapp-min.yml +++ /dev/null @@ -1,170 +0,0 @@ -# Jenkinsfile.monai-premerge -name: premerge-min - -on: - # quick tests for pull requests and the releasing branches - push: - branches: - - dev - - main - - releasing/* - pull_request: - head_ref-ignore: - - dev - -concurrency: - # automatically cancel the previously triggered workflows when there's a newer version - group: build-min-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - # caching of these jobs: - # - docker-py3-pip- (shared) - # - ubuntu py37 pip- - # - os-latest-pip- (shared) - min-dep-os: # min dependencies installed tests for different OS - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [windows-latest, macOS-latest, ubuntu-latest] - timeout-minutes: 40 - steps: - - uses: actions/checkout@v6 - - name: Set up Python 3.9 - uses: actions/setup-python@v6 - with: - python-version: '3.9' - - name: Prepare pip wheel - run: | - which python - python -m pip install --upgrade pip wheel - - name: cache weekly timestamp - id: pip-cache - run: | - echo "datew=$(date '+%Y-%V')" >> $GITHUB_OUTPUT - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - shell: bash - - name: cache for pip - uses: actions/cache@v5 - id: cache - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ${{ matrix.os }}-latest-pip-${{ steps.pip-cache.outputs.datew }} - - name: Install the dependencies - run: | - # min. requirements - python -m pip install torch --index-url https://download.pytorch.org/whl/cpu - python -m pip install -r requirements-min.txt - python -m pip list - BUILD_MONAI=0 python setup.py develop # no compile of extensions - shell: bash - - name: Run quick tests (CPU ${{ runner.os }}) - run: | - python -c 'import torch; print(torch.__version__); print(torch.rand(5,3))' - python -c "import monai; monai.config.print_config()" - ./runtests.sh --min - shell: bash - env: - QUICKTEST: True - NGC_API_KEY: ${{ secrets.NGC_API_KEY }} - NGC_ORG: ${{ secrets.NGC_ORG }} - NGC_TEAM: ${{ secrets.NGC_TEAM }} - - min-dep-py3: # min dependencies installed tests for different python - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ['3.9', '3.10', '3.11', '3.12'] - timeout-minutes: 40 - steps: - - uses: actions/checkout@v6 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - name: Prepare pip wheel - run: | - which python - python -m pip install --user --upgrade pip setuptools wheel - python -m pip install --user more-itertools>=8.0 - - name: cache weekly timestamp - id: pip-cache - run: | - echo "datew=$(date '+%Y-%V')" >> $GITHUB_OUTPUT - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - shell: bash - - name: cache for pip - uses: actions/cache@v5 - id: cache - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ubuntu-latest-latest-pip-${{ steps.pip-cache.outputs.datew }} - - name: Install the dependencies - run: | - # min. requirements - python -m pip install torch --extra-index-url https://download.pytorch.org/whl/cpu - python -m pip install -r requirements-min.txt - python -m pip list - BUILD_MONAI=0 python setup.py develop # no compile of extensions - shell: bash - - name: Run quick tests (CPU ${{ runner.os }}) - run: | - python -c 'import torch; print(torch.__version__); print(torch.rand(5,3))' - python -c "import monai; monai.config.print_config()" - ./runtests.sh --min - env: - QUICKTEST: True - NGC_API_KEY: ${{ secrets.NGC_API_KEY }} - NGC_ORG: ${{ secrets.NGC_ORG }} - NGC_TEAM: ${{ secrets.NGC_TEAM }} - - min-dep-pytorch: # min dependencies installed tests for different pytorch - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - pytorch-version: ['2.5.1', '2.6.0', '2.7.1', '2.8.0'] - timeout-minutes: 40 - steps: - - uses: actions/checkout@v6 - - name: Set up Python 3.9 - uses: actions/setup-python@v6 - with: - python-version: '3.9' - - name: Prepare pip wheel - run: | - which python - python -m pip install --user --upgrade pip setuptools wheel - python -m pip install --user more-itertools>=8.0 - - name: cache weekly timestamp - id: pip-cache - run: | - echo "datew=$(date '+%Y-%V')" >> $GITHUB_OUTPUT - echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - shell: bash - - name: cache for pip - uses: actions/cache@v5 - id: cache - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ubuntu-latest-latest-pip-${{ steps.pip-cache.outputs.datew }} - - name: Install the dependencies - run: | - # min. requirements - python -m pip install torch==${{ matrix.pytorch-version }} - python -m pip install -r requirements-min.txt - python -m pip list - BUILD_MONAI=0 python setup.py develop # no compile of extensions - shell: bash - - name: Run quick tests (pytorch ${{ matrix.pytorch-version }}) - run: | - python -c 'import torch; print(torch.__version__); print(torch.rand(5,3))' - python -c "import monai; monai.config.print_config()" - ./runtests.sh --min - env: - QUICKTEST: True - NGC_API_KEY: ${{ secrets.NGC_API_KEY }} - NGC_ORG: ${{ secrets.NGC_ORG }} - NGC_TEAM: ${{ secrets.NGC_TEAM }} diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml deleted file mode 100644 index 0e898636fb..0000000000 --- a/.github/workflows/pythonapp.yml +++ /dev/null @@ -1,203 +0,0 @@ -# Jenkinsfile.monai-premerge -name: premerge - -on: - # quick tests for pull requests and the releasing branches - push: - branches: - - dev - - main - - releasing/* - pull_request: - head_ref-ignore: - - dev - -concurrency: - # automatically cancel the previously triggered workflows when there's a newer version - group: build-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - # caching of these jobs: - # - docker-py3-pip- (shared) - # - ubuntu py37 pip- - # - os-latest-pip- (shared) - flake8-py3: - runs-on: ubuntu-latest - strategy: - matrix: - opt: ["codeformat", "pytype", "mypy"] - steps: - - uses: actions/checkout@v6 - - name: Set up Python 3.9 - uses: actions/setup-python@v6 - with: - python-version: '3.9' - cache: 'pip' - - name: Install dependencies - run: | - find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; - python -m pip install --upgrade pip wheel - python -m pip install --no-build-isolation -r requirements-dev.txt - - name: Lint and type check - run: | - # clean up temporary files - $(pwd)/runtests.sh --build --clean - # Github actions have 2 cores, so parallelize pytype - $(pwd)/runtests.sh --build --${{ matrix.opt }} -j 2 - - quick-py3: # full dependencies installed tests for different OS - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [windows-latest, macOS-latest, ubuntu-latest] - timeout-minutes: 120 - steps: - - if: runner.os == 'windows' - name: Config pagefile (Windows only) - uses: al-cheb/configure-pagefile-action@v1.5 - with: - minimum-size: 8GB - maximum-size: 16GB - disk-root: "D:" - - uses: actions/checkout@v6 - - name: Set up Python 3.9 - uses: actions/setup-python@v6 - with: - python-version: '3.9' - cache: 'pip' - - name: Prepare pip wheel - run: | - which python - python -m pip install --upgrade pip wheel - - if: runner.os == 'windows' - name: Install torch cpu from pytorch.org (Windows only) - run: | - python -m pip install torch==2.5.1 torchvision==0.20.1+cpu --index-url https://download.pytorch.org/whl/cpu - - if: runner.os == 'Linux' - name: Install itk pre-release (Linux only) - run: | - python -m pip install --pre -U itk - find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; - - name: Install the dependencies - run: | - python -m pip install --user --upgrade pip wheel - python -m pip install torch==2.5.1 torchvision==0.20.1 - cat "requirements-dev.txt" - python -m pip install --no-build-isolation -r requirements-dev.txt - python -m pip list - python -m pip install -e . # test no compile installation - shell: bash - - name: Run compiled (${{ runner.os }}) - run: | - python -m pip uninstall -y monai - BUILD_MONAI=1 python -m pip install -e . # compile the cpp extensions - shell: bash - - name: Run quick tests (CPU ${{ runner.os }}) - run: | - python -c 'import torch; print(torch.__version__); print(torch.rand(5,3))' - python -c "import monai; monai.config.print_config()" - python -m unittest -v - env: - QUICKTEST: True - PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python # https://github.com/Project-MONAI/MONAI/issues/4354 - - packaging: - runs-on: ubuntu-latest - env: - QUICKTEST: True - shell: bash - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - name: Set up Python 3.9 - uses: actions/setup-python@v6 - with: - python-version: '3.9' - cache: 'pip' - - name: Install dependencies - run: | - find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; - python -m pip install --user --upgrade pip setuptools wheel twine packaging - # install the latest pytorch for testing - # however, "pip install monai*.tar.gz" will build cpp/cuda with an isolated - # fresh torch installation according to pyproject.toml - python -m pip install torch>=2.5.1 torchvision --extra-index-url https://download.pytorch.org/whl/cpu - - name: Check packages - run: | - pip uninstall monai - pip list | grep -iv monai - git fetch --depth=1 origin +refs/tags/*:refs/tags/* - set -e - - # build tar.gz and wheel - python setup.py check -m -s - python setup.py sdist bdist_wheel - python -m twine check dist/* - - run: echo "pwd=$PWD" >> $GITHUB_OUTPUT - id: root - - run: echo "tmp_dir=$(mktemp -d)" >> $GITHUB_OUTPUT - id: mktemp - - name: Move packages - run: | - printf ${{ steps.root.outputs.pwd }} - printf ${{ steps.mktemp.outputs.tmp_dir }} - # move packages to a temp dir - cp dist/monai* "${{ steps.mktemp.outputs.tmp_dir }}" - rm -r build dist monai.egg-info - cd "${{ steps.mktemp.outputs.tmp_dir }}" - ls -al - - name: Install wheel file - working-directory: ${{ steps.mktemp.outputs.tmp_dir }} - run: | - # install from wheel - python -m pip install monai*.whl --extra-index-url https://download.pytorch.org/whl/cpu - python -c 'import monai; monai.config.print_config()' 2>&1 | grep -iv "unknown" - python -c 'import monai; print(monai.__file__)' - python -m pip uninstall -y monai - rm monai*.whl - - name: Install source archive - working-directory: ${{ steps.mktemp.outputs.tmp_dir }} - run: | - # install from tar.gz - name=$(ls *.tar.gz | head -n1) - echo $name - python -m pip install $name[all] --extra-index-url https://download.pytorch.org/whl/cpu - python -c 'import monai; monai.config.print_config()' 2>&1 | grep -iv "unknown" - python -c 'import monai; print(monai.__file__)' - - name: Quick test - working-directory: ${{ steps.mktemp.outputs.tmp_dir }} - run: | - # run min tests - cp ${{ steps.root.outputs.pwd }}/requirements*.txt . - cp -r ${{ steps.root.outputs.pwd }}/tests . - ls -al - python -m pip install --no-build-isolation -r requirements-dev.txt --extra-index-url https://download.pytorch.org/whl/cpu - python -m unittest -v - env: - PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python # https://github.com/Project-MONAI/MONAI/issues/4354 - - build-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - name: Set up Python 3.9 - uses: actions/setup-python@v6 - with: - python-version: '3.9' - cache: 'pip' - - name: Install dependencies - run: | - python -m pip install --upgrade pip wheel - python -m pip install -r docs/requirements.txt --extra-index-url https://download.pytorch.org/whl/cpu - - name: Make html - run: | - cd docs/ - make clean - make html 2>&1 | tee tmp_log - if [[ $(grep -c "ERROR:" tmp_log) != 0 ]]; then echo "found errors"; grep "ERROR:" tmp_log; exit 1; fi - sed '/WARNING.*pip/d' tmp_log > tmp_log1; mv tmp_log1 tmp_log # monai#7133 - if [[ $(grep -c "WARNING:" tmp_log) != 0 ]]; then echo "found warnings"; grep "WARNING:" tmp_log; exit 1; fi - shell: bash diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d20d280e1..d7df47ae19 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v6 with: @@ -64,14 +64,14 @@ jobs: env: QUICKTEST: True - - if: matrix.python-version == '3.9' && startsWith(github.ref, 'refs/tags/') + - if: matrix.python-version == '3.10' && startsWith(github.ref, 'refs/tags/') name: Upload artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: dist path: dist/ - - if: matrix.python-version == '3.9' && startsWith(github.ref, 'refs/tags/') + - if: matrix.python-version == '3.10' && startsWith(github.ref, 'refs/tags/') name: Check artifacts run: | ls -al dist/ @@ -79,7 +79,7 @@ jobs: ls -al dist/ # remove publishing to Test PyPI as it is moved to blossom - # - if: matrix.python-version == '3.9' && startsWith(github.ref, 'refs/tags/') + # - if: matrix.python-version == '3.10' && startsWith(github.ref, 'refs/tags/') # name: Publish to Test PyPI # uses: pypa/gh-action-pypi-publish@release/v1 # with: @@ -97,10 +97,10 @@ jobs: # full history so that we can git describe with: fetch-depth: 0 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v6 with: - python-version: '3.9' + python-version: '3.10' - shell: bash run: | find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; @@ -109,7 +109,7 @@ jobs: python setup.py build cat build/lib/monai/_version.py - name: Upload version - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: _version.py path: build/lib/monai/_version.py diff --git a/.github/workflows/setupapp.yml b/.github/workflows/setupapp.yml index c81ac3139b..63162b2dac 100644 --- a/.github/workflows/setupapp.yml +++ b/.github/workflows/setupapp.yml @@ -72,7 +72,7 @@ jobs: if pgrep python; then pkill python; fi shell: bash - name: Upload coverage - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: false files: ./coverage.xml @@ -108,7 +108,7 @@ jobs: BUILD_MONAI=1 ./runtests.sh --build --coverage --quick --min coverage xml --ignore-errors - name: Upload coverage - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: false files: ./coverage.xml @@ -116,10 +116,10 @@ jobs: install: # pip install from github url, the default branch is dev runs-on: ubuntu-latest steps: - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v6 with: - python-version: '3.9' + python-version: '3.10' - name: cache weekly timestamp id: pip-cache run: | diff --git a/.github/workflows/weekly-preview.yml b/.github/workflows/weekly-preview.yml index 9f63c5bc90..a74585aa38 100644 --- a/.github/workflows/weekly-preview.yml +++ b/.github/workflows/weekly-preview.yml @@ -12,10 +12,10 @@ jobs: opt: ["codeformat", "pytype", "mypy"] steps: - uses: actions/checkout@v6 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v6 with: - python-version: '3.9' + python-version: '3.10' - name: cache weekly timestamp id: pip-cache run: | @@ -46,10 +46,10 @@ jobs: with: ref: dev fetch-depth: 0 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v6 with: - python-version: '3.9' + python-version: '3.10' - name: Install setuptools run: | python -m pip install --user --upgrade setuptools wheel packaging diff --git a/docs/.readthedocs.yaml b/docs/.readthedocs.yaml index d2e2ee74a5..0212e806e8 100644 --- a/docs/.readthedocs.yaml +++ b/docs/.readthedocs.yaml @@ -6,7 +6,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.9" + python: "3.10" sphinx: configuration: docs/source/conf.py python: diff --git a/docs/requirements.txt b/docs/requirements.txt index f44f41a6b2..7f4db0f704 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,7 +6,7 @@ itk>=5.2 nibabel parameterized scikit-image>=0.19.0 -scipy>=1.12.0; python_version >= '3.9' +scipy>=1.12.0 tensorboard commonmark==0.9.1 recommonmark==0.6.0 diff --git a/docs/source/installation.md b/docs/source/installation.md index 646b34a23f..5123bc3e6b 100644 --- a/docs/source/installation.md +++ b/docs/source/installation.md @@ -19,7 +19,7 @@ --- -MONAI's core functionality is written in Python 3 (>= 3.9) and only requires [Numpy](https://numpy.org/) and [Pytorch](https://pytorch.org/). +MONAI's core functionality is written in Python 3 (>= 3.10) and only requires [Numpy](https://numpy.org/) and [Pytorch](https://pytorch.org/). The package is currently distributed via Github as the primary source code repository, and the Python package index (PyPI). The pre-built Docker images are made available on DockerHub. @@ -245,7 +245,7 @@ this will install PyTorch as well as `pytorch-cuda`, please follow https://pytor ```bash git clone https://github.com/Project-MONAI/MONAI.git cd MONAI/ -conda create -n python= # eg 3.9 +conda create -n python= # eg 3.10 conda env update -n -f environment-dev.yml ``` diff --git a/monai/_version.py b/monai/_version.py index 256d3654d9..f234227104 100644 --- a/monai/_version.py +++ b/monai/_version.py @@ -15,7 +15,7 @@ import re import subprocess import sys -from typing import Callable, Dict +from collections.abc import Callable import functools @@ -54,8 +54,8 @@ class NotThisMethod(Exception): """Exception raised if a method is not valid for the current scenario.""" -LONG_VERSION_PY: Dict[str, str] = {} -HANDLERS: Dict[str, Dict[str, Callable]] = {} +LONG_VERSION_PY: dict[str, str] = {} +HANDLERS: dict[str, dict[str, Callable]] = {} def register_vcs_handler(vcs, method): # decorator @@ -145,7 +145,7 @@ def git_get_keywords(versionfile_abs): # _version.py. keywords = {} try: - with open(versionfile_abs, "r") as fobj: + with open(versionfile_abs) as fobj: for line in fobj: if line.strip().startswith("git_refnames ="): mo = re.search(r'=\s*"(.*)"', line) diff --git a/monai/apps/auto3dseg/auto_runner.py b/monai/apps/auto3dseg/auto_runner.py index d06effcd1a..8e2b3f423b 100644 --- a/monai/apps/auto3dseg/auto_runner.py +++ b/monai/apps/auto3dseg/auto_runner.py @@ -560,7 +560,7 @@ def set_device_info( cmd_prefix: command line prefix for subprocess running in BundleAlgo and EnsembleRunner. Default using env "CMD_PREFIX" or None, examples are: - - single GPU/CPU or multinode bcprun: "python " or "/opt/conda/bin/python3.9 ", + - single GPU/CPU or multinode bcprun: "python " or "/opt/conda/bin/python3.10", - single node multi-GPU running "torchrun --nnodes=1 --nproc_per_node=2 " If user define this prefix, please make sure --nproc_per_node matches cuda_visible_device or diff --git a/monai/apps/detection/transforms/dictionary.py b/monai/apps/detection/transforms/dictionary.py index f52ab53531..737ec70b36 100644 --- a/monai/apps/detection/transforms/dictionary.py +++ b/monai/apps/detection/transforms/dictionary.py @@ -125,10 +125,8 @@ def __init__(self, box_keys: KeysCollection, box_ref_image_keys: str, allow_miss super().__init__(box_keys, allow_missing_keys) box_ref_image_keys_tuple = ensure_tuple(box_ref_image_keys) if len(box_ref_image_keys_tuple) > 1: - raise ValueError( - "Please provide a single key for box_ref_image_keys.\ - All boxes of box_keys are attached to box_ref_image_keys." - ) + raise ValueError("Please provide a single key for box_ref_image_keys.\ + All boxes of box_keys are attached to box_ref_image_keys.") self.box_ref_image_keys = box_ref_image_keys def __call__(self, data: Mapping[Hashable, NdarrayOrTensor]) -> dict[Hashable, NdarrayOrTensor]: @@ -290,8 +288,8 @@ def __init__( box_ref_image_keys_tuple = ensure_tuple(box_ref_image_keys) if len(box_ref_image_keys_tuple) > 1: raise ValueError( - "Please provide a single key for box_ref_image_keys.\ - All boxes of box_keys are attached to box_ref_image_keys." + "Please provide a single key for `box_ref_image_keys`. " + "All boxes of box_keys are attached to `box_ref_image_keys`." ) self.box_ref_image_keys = box_ref_image_keys self.image_meta_key = image_meta_key or f"{box_ref_image_keys}_{image_meta_key_postfix}" @@ -311,8 +309,7 @@ def extract_affine(self, data: Mapping[Hashable, torch.Tensor]) -> tuple[Ndarray raise ValueError(f"{meta_key} is not found. Please check whether it is the correct the image meta key.") if "affine" not in meta_dict: raise ValueError( - f"'affine' is not found in {meta_key}. \ - Please check whether it is the correct the image meta key." + f"Key 'affine' not found in {meta_key}. Please check it is the correct the image meta key." ) affine: NdarrayOrTensor = meta_dict["affine"] @@ -816,15 +813,15 @@ def __init__( box_keys_tuple = ensure_tuple(box_keys) if len(box_keys_tuple) != 1: raise ValueError( - "Please provide a single key for box_keys.\ - All label_keys are attached to this box_keys." + "Please provide at least one key for `box_keys`. All `label_keys` are attached to this `box_keys`." ) box_ref_image_keys_tuple = ensure_tuple(box_ref_image_keys) if len(box_ref_image_keys_tuple) != 1: raise ValueError( - "Please provide a single key for box_ref_image_keys.\ - All box_keys and label_keys are attached to this box_ref_image_keys." + "Please provide at least one key for `box_ref_image_keys`. " + "All box_keys and label_keys are attached to this `box_ref_image_keys`." ) + self.label_keys = ensure_tuple(label_keys) super().__init__(box_keys_tuple, allow_missing_keys) @@ -1091,10 +1088,8 @@ def __init__( box_keys_tuple = ensure_tuple(box_keys) if len(box_keys_tuple) != 1: - raise ValueError( - "Please provide a single key for box_keys.\ - All label_keys are attached to this box_keys." - ) + raise ValueError("Please provide a single key for box_keys.\ + All label_keys are attached to this box_keys.") self.box_keys = box_keys_tuple[0] self.label_keys = ensure_tuple(label_keys) diff --git a/monai/apps/detection/utils/anchor_utils.py b/monai/apps/detection/utils/anchor_utils.py index 20f6fc6025..9ee1964215 100644 --- a/monai/apps/detection/utils/anchor_utils.py +++ b/monai/apps/detection/utils/anchor_utils.py @@ -124,10 +124,7 @@ def __init__( aspect_ratios = (aspect_ratios,) * len(self.sizes) if len(self.sizes) != len(aspect_ratios): - raise ValueError( - "len(sizes) and len(aspect_ratios) should be equal. \ - It represents the number of feature maps." - ) + raise ValueError("The number of feature maps, `len(sizes)` and `len(aspect_ratios)`, should be equal.") spatial_dims = len(ensure_tuple(aspect_ratios[0][0])) + 1 spatial_dims = look_up_option(spatial_dims, [2, 3]) @@ -173,14 +170,14 @@ def generate_anchors( aspect_ratios_t = torch.as_tensor(aspect_ratios, dtype=dtype, device=device) # sized (M,) or (M,2) if (self.spatial_dims >= 3) and (len(aspect_ratios_t.shape) != 2): raise ValueError( - f"In {self.spatial_dims}-D image, aspect_ratios for each level should be \ - {len(aspect_ratios_t.shape) - 1}-D. But got aspect_ratios with shape {aspect_ratios_t.shape}." + f"In {self.spatial_dims}-D image, aspect_ratios for each level should be " + f"{len(aspect_ratios_t.shape) - 1}-D. Got aspect_ratios with shape {aspect_ratios_t.shape}." ) if (self.spatial_dims >= 3) and (aspect_ratios_t.shape[1] != self.spatial_dims - 1): raise ValueError( - f"In {self.spatial_dims}-D image, aspect_ratios for each level should has \ - shape (_,{self.spatial_dims - 1}). But got aspect_ratios with shape {aspect_ratios_t.shape}." + f"In {self.spatial_dims}-D image, aspect_ratios for each level should have " + f"shape (_,{self.spatial_dims - 1}). Got aspect_ratios with shape {aspect_ratios_t.shape}." ) # if 2d, w:h = 1:aspect_ratios diff --git a/monai/apps/pathology/inferers/inferer.py b/monai/apps/pathology/inferers/inferer.py index 392cba221f..37be148d45 100644 --- a/monai/apps/pathology/inferers/inferer.py +++ b/monai/apps/pathology/inferers/inferer.py @@ -11,8 +11,8 @@ from __future__ import annotations -from collections.abc import Sequence -from typing import Any, Callable +from collections.abc import Callable, Sequence +from typing import Any import numpy as np import torch diff --git a/monai/apps/pathology/transforms/post/array.py b/monai/apps/pathology/transforms/post/array.py index 561ed3ae20..5ab272f018 100644 --- a/monai/apps/pathology/transforms/post/array.py +++ b/monai/apps/pathology/transforms/post/array.py @@ -12,8 +12,7 @@ from __future__ import annotations import warnings -from collections.abc import Sequence -from typing import Callable +from collections.abc import Callable, Sequence import numpy as np import torch diff --git a/monai/apps/reconstruction/transforms/array.py b/monai/apps/reconstruction/transforms/array.py index 911d7a06bb..64fc8cdd2d 100644 --- a/monai/apps/reconstruction/transforms/array.py +++ b/monai/apps/reconstruction/transforms/array.py @@ -62,8 +62,8 @@ def __init__( """ if len(center_fractions) != len(accelerations): raise ValueError( - "Number of center fractions \ - should match number of accelerations" + "Number of center fractions `len(center_fractions)` " + "should match number of accelerations `len(accelerations)`." ) self.center_fractions = center_fractions diff --git a/monai/auto3dseg/utils.py b/monai/auto3dseg/utils.py index 211f23c415..ff1be57629 100644 --- a/monai/auto3dseg/utils.py +++ b/monai/auto3dseg/utils.py @@ -407,7 +407,8 @@ def _prepare_cmd_default(cmd: str, cmd_prefix: str | None = None, **kwargs: Any) Args: cmd: the command or script to run in the distributed job. - cmd_prefix: the command prefix to run the script, e.g., "python", "python -m", "python3", "/opt/conda/bin/python3.9 ". + cmd_prefix: the command prefix to run the script, + e.g., "python", "python -m", "python3", "/opt/conda/bin/python3.10". kwargs: the keyword arguments to be passed to the script. Returns: diff --git a/monai/bundle/scripts.py b/monai/bundle/scripts.py index fa9ba27096..ab02cd552e 100644 --- a/monai/bundle/scripts.py +++ b/monai/bundle/scripts.py @@ -17,13 +17,13 @@ import re import urllib import warnings -from collections.abc import Mapping, Sequence +from collections.abc import Callable, Mapping, Sequence from functools import partial from pathlib import Path from pydoc import locate from shutil import copyfile from textwrap import dedent -from typing import Any, Callable +from typing import Any import torch from torch.cuda import is_available diff --git a/monai/bundle/utils.py b/monai/bundle/utils.py index 53d619f234..d37d7f1c05 100644 --- a/monai/bundle/utils.py +++ b/monai/bundle/utils.py @@ -124,10 +124,8 @@ "run_name": None, # may fill it at runtime "save_execute_config": True, - "is_not_rank0": ( - "$torch.distributed.is_available() \ - and torch.distributed.is_initialized() and torch.distributed.get_rank() > 0" - ), + "is_not_rank0": ("$torch.distributed.is_available() \ + and torch.distributed.is_initialized() and torch.distributed.get_rank() > 0"), # MLFlowHandler config for the trainer "trainer": { "_target_": "MLFlowHandler", diff --git a/monai/config/type_definitions.py b/monai/config/type_definitions.py index 48c0547e31..afe13abf37 100644 --- a/monai/config/type_definitions.py +++ b/monai/config/type_definitions.py @@ -13,7 +13,7 @@ import os from collections.abc import Collection, Hashable, Iterable, Sequence -from typing import TypeVar, Union +from typing import TypeVar import numpy as np import torch @@ -53,7 +53,7 @@ # convenience to end-users. All supplied values will be # internally converted to a tuple of `Hashable`'s before # use -KeysCollection = Union[Collection[Hashable], Hashable] +KeysCollection = Collection[Hashable] | Hashable #: IndexSelection # @@ -61,13 +61,13 @@ # that store a subset of indices to select items from a List or Array like objects. # The indices must be integers, and if a container of indices is specified, the # container must be iterable. -IndexSelection = Union[Iterable[int], int] +IndexSelection = Iterable[int] | int #: Type of datatypes: Adapted from https://github.com/numpy/numpy/blob/v1.21.4/numpy/typing/_dtype_like.py#L121 -DtypeLike = Union[np.dtype, type, str, None] +DtypeLike = np.dtype | type | str | None #: NdarrayOrTensor: Union of numpy.ndarray and torch.Tensor to be used for typing -NdarrayOrTensor = Union[np.ndarray, torch.Tensor] +NdarrayOrTensor = np.ndarray | torch.Tensor #: NdarrayTensor # @@ -76,11 +76,11 @@ NdarrayTensor = TypeVar("NdarrayTensor", bound=NdarrayOrTensor) #: TensorOrList: The TensorOrList type is used for defining `batch-first Tensor` or `list of channel-first Tensor`. -TensorOrList = Union[torch.Tensor, Sequence[torch.Tensor]] +TensorOrList = torch.Tensor | Sequence[torch.Tensor] #: PathLike: The PathLike type is used for defining a file path. -PathLike = Union[str, os.PathLike] +PathLike = str | os.PathLike #: SequenceStr # string or a sequence of strings for `mode` types. -SequenceStr = Union[Sequence[str], str] +SequenceStr = Sequence[str] | str diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py index c300476a6b..08697c3fee 100644 --- a/monai/data/image_reader.py +++ b/monai/data/image_reader.py @@ -22,7 +22,7 @@ from collections.abc import Callable, Iterable, Iterator, Sequence from dataclasses import dataclass from pathlib import Path -from typing import TYPE_CHECKING, Any, Union +from typing import TYPE_CHECKING, Any import numpy as np from torch.utils.data._utils.collate import np_str_obj_array_pattern @@ -60,7 +60,7 @@ if TYPE_CHECKING: import cupy - NdarrayOrCupy = Union[np.ndarray, cupy.ndarray] + NdarrayOrCupy = np.ndarray | cupy.ndarray else: NdarrayOrCupy = Any diff --git a/monai/engines/evaluator.py b/monai/engines/evaluator.py index 836b407ac5..62d5f83847 100644 --- a/monai/engines/evaluator.py +++ b/monai/engines/evaluator.py @@ -12,8 +12,8 @@ from __future__ import annotations import warnings -from collections.abc import Iterable, Sequence -from typing import TYPE_CHECKING, Any, Callable +from collections.abc import Callable, Iterable, Sequence +from typing import TYPE_CHECKING, Any import torch from torch.utils.data import DataLoader diff --git a/monai/engines/trainer.py b/monai/engines/trainer.py index 15033cabac..921d54a59c 100644 --- a/monai/engines/trainer.py +++ b/monai/engines/trainer.py @@ -12,8 +12,8 @@ from __future__ import annotations import warnings -from collections.abc import Iterable, Sequence -from typing import TYPE_CHECKING, Any, Callable +from collections.abc import Callable, Iterable, Sequence +from typing import TYPE_CHECKING, Any import torch from torch.optim.optimizer import Optimizer diff --git a/monai/losses/dice.py b/monai/losses/dice.py index cd76ec1323..b4558f930c 100644 --- a/monai/losses/dice.py +++ b/monai/losses/dice.py @@ -204,9 +204,8 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: else: if self.class_weight.shape[0] != num_of_classes: raise ValueError( - """the length of the `weight` sequence should be the same as the number of classes. - If `include_background=False`, the weight should not include - the background category class 0.""" + "The length of the `weight` sequence should be the same as the number of classes. " + "If `include_background=False`, the weight should not include the background category class 0." ) if self.class_weight.min() < 0: raise ValueError("the value/values of the `weight` should be no less than 0.") diff --git a/monai/losses/focal_loss.py b/monai/losses/focal_loss.py index caa237fca8..7773cbdc9a 100644 --- a/monai/losses/focal_loss.py +++ b/monai/losses/focal_loss.py @@ -184,9 +184,8 @@ def forward(self, input: torch.Tensor, target: torch.Tensor) -> torch.Tensor: else: if self.class_weight.shape[0] != num_of_classes: raise ValueError( - """the length of the `weight` sequence should be the same as the number of classes. - If `include_background=False`, the weight should not include - the background category class 0.""" + "The length of the `weight` sequence should be the same as the number of classes. " + "If `include_background=False`, the weight should not include the background category class 0." ) if self.class_weight.min() < 0: raise ValueError("the value/values of the `weight` should be no less than 0.") diff --git a/monai/losses/hausdorff_loss.py b/monai/losses/hausdorff_loss.py index b75433e1da..017606ac08 100644 --- a/monai/losses/hausdorff_loss.py +++ b/monai/losses/hausdorff_loss.py @@ -17,7 +17,7 @@ from __future__ import annotations import warnings -from typing import Callable +from collections.abc import Callable import torch from torch.nn.modules.loss import _Loss diff --git a/monai/losses/sure_loss.py b/monai/losses/sure_loss.py index cc02317f72..53583b9051 100644 --- a/monai/losses/sure_loss.py +++ b/monai/losses/sure_loss.py @@ -11,7 +11,7 @@ from __future__ import annotations -from typing import Callable +from collections.abc import Callable import torch import torch.nn as nn diff --git a/monai/metrics/average_precision.py b/monai/metrics/average_precision.py index 53c41aeca5..7dd277bde6 100644 --- a/monai/metrics/average_precision.py +++ b/monai/metrics/average_precision.py @@ -12,13 +12,10 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, cast +from typing import cast import numpy as np - -if TYPE_CHECKING: - import numpy.typing as npt - +import numpy.typing as npt import torch from monai.utils import Average, look_up_option diff --git a/monai/metrics/rocauc.py b/monai/metrics/rocauc.py index 57a8a072b4..72f0b3730c 100644 --- a/monai/metrics/rocauc.py +++ b/monai/metrics/rocauc.py @@ -12,13 +12,10 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, cast +from typing import cast import numpy as np - -if TYPE_CHECKING: - import numpy.typing as npt - +import numpy.typing as npt import torch from monai.utils import Average, look_up_option diff --git a/monai/networks/layers/conjugate_gradient.py b/monai/networks/layers/conjugate_gradient.py index 93a45930d7..9432f15b6c 100644 --- a/monai/networks/layers/conjugate_gradient.py +++ b/monai/networks/layers/conjugate_gradient.py @@ -11,7 +11,7 @@ from __future__ import annotations -from typing import Callable +from collections.abc import Callable import torch from torch import nn diff --git a/monai/networks/nets/dynunet.py b/monai/networks/nets/dynunet.py index ec418469bb..d130b886a7 100644 --- a/monai/networks/nets/dynunet.py +++ b/monai/networks/nets/dynunet.py @@ -12,7 +12,6 @@ # isort: dont-add-import: from __future__ import annotations from collections.abc import Sequence -from typing import Optional, Union import torch import torch.nn as nn @@ -33,7 +32,7 @@ class DynUNetSkipLayer(nn.Module): forward passes of the network. """ - heads: Optional[list[torch.Tensor]] + heads: list[torch.Tensor] | None def __init__(self, index, downsample, upsample, next_layer, heads=None, super_head=None): super().__init__() @@ -133,13 +132,13 @@ def __init__( spatial_dims: int, in_channels: int, out_channels: int, - kernel_size: Sequence[Union[Sequence[int], int]], - strides: Sequence[Union[Sequence[int], int]], - upsample_kernel_size: Sequence[Union[Sequence[int], int]], - filters: Optional[Sequence[int]] = None, - dropout: Optional[Union[tuple, str, float]] = None, - norm_name: Union[tuple, str] = ("INSTANCE", {"affine": True}), - act_name: Union[tuple, str] = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), + kernel_size: Sequence[Sequence[int] | int], + strides: Sequence[Sequence[int] | int], + upsample_kernel_size: Sequence[Sequence[int] | int], + filters: Sequence[int] | None = None, + dropout: tuple | str | float | None = None, + norm_name: tuple | str = ("INSTANCE", {"affine": True}), + act_name: tuple | str = ("leakyrelu", {"inplace": True, "negative_slope": 0.01}), deep_supervision: bool = False, deep_supr_num: int = 1, res_block: bool = False, @@ -326,10 +325,10 @@ def get_module_list( self, in_channels: list[int], out_channels: list[int], - kernel_size: Sequence[Union[Sequence[int], int]], - strides: Sequence[Union[Sequence[int], int]], + kernel_size: Sequence[Sequence[int] | int], + strides: Sequence[Sequence[int] | int], conv_block: nn.Module, - upsample_kernel_size: Optional[Sequence[Union[Sequence[int], int]]] = None, + upsample_kernel_size: Sequence[Sequence[int] | int] | None = None, trans_bias: bool = False, ): layers = [] diff --git a/monai/networks/nets/vista3d.py b/monai/networks/nets/vista3d.py index 5490931d8d..232b8c1c11 100644 --- a/monai/networks/nets/vista3d.py +++ b/monai/networks/nets/vista3d.py @@ -12,8 +12,8 @@ from __future__ import annotations import math -from collections.abc import Sequence -from typing import Any, Callable +from collections.abc import Callable, Sequence +from typing import Any import numpy as np import torch diff --git a/monai/optimizers/lr_finder.py b/monai/optimizers/lr_finder.py index aa2e4567b3..8692d2622f 100644 --- a/monai/optimizers/lr_finder.py +++ b/monai/optimizers/lr_finder.py @@ -14,8 +14,9 @@ import pickle import types import warnings +from collections.abc import Callable from functools import partial -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any import numpy as np import torch diff --git a/monai/transforms/adaptors.py b/monai/transforms/adaptors.py index 5a0c24c7f6..b3c8b34f5c 100644 --- a/monai/transforms/adaptors.py +++ b/monai/transforms/adaptors.py @@ -123,7 +123,7 @@ def __call__(self, img, seg): from __future__ import annotations -from typing import Callable +from collections.abc import Callable __all__ = ["adaptor", "apply_alias", "to_kwargs", "FunctionSignature"] diff --git a/monai/transforms/intensity/dictionary.py b/monai/transforms/intensity/dictionary.py index fac46f2487..0c25d4ac99 100644 --- a/monai/transforms/intensity/dictionary.py +++ b/monai/transforms/intensity/dictionary.py @@ -17,8 +17,7 @@ from __future__ import annotations -from collections.abc import Hashable, Mapping, Sequence -from typing import Callable +from collections.abc import Callable, Hashable, Mapping, Sequence import numpy as np diff --git a/monai/transforms/io/array.py b/monai/transforms/io/array.py index f0c1d1949d..049779f606 100644 --- a/monai/transforms/io/array.py +++ b/monai/transforms/io/array.py @@ -20,10 +20,9 @@ import sys import traceback import warnings -from collections.abc import Sequence +from collections.abc import Callable, Sequence from pathlib import Path from pydoc import locate -from typing import Callable import numpy as np import torch diff --git a/monai/transforms/io/dictionary.py b/monai/transforms/io/dictionary.py index be1e78db8a..4927450c7d 100644 --- a/monai/transforms/io/dictionary.py +++ b/monai/transforms/io/dictionary.py @@ -17,9 +17,8 @@ from __future__ import annotations -from collections.abc import Hashable, Mapping +from collections.abc import Callable, Hashable, Mapping from pathlib import Path -from typing import Callable import numpy as np diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 55dab79c3d..d6b5ed8641 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -18,7 +18,7 @@ from collections.abc import Callable, Sequence from copy import deepcopy from itertools import zip_longest -from typing import Any, Optional, Union, cast +from typing import Any, cast import numpy as np import torch @@ -118,7 +118,7 @@ "RandSimulateLowResolution", ] -RandRange = Optional[Union[Sequence[Union[tuple[float, float], float]], float]] +RandRange = Sequence[tuple[float, float] | float] | float | None class SpatialResample(InvertibleTransform, LazyTransform): diff --git a/monai/transforms/utility/array.py b/monai/transforms/utility/array.py index 7df6e2c5ef..b39dd93931 100644 --- a/monai/transforms/utility/array.py +++ b/monai/transforms/utility/array.py @@ -18,10 +18,10 @@ import sys import time import warnings -from collections.abc import Hashable, Mapping, Sequence +from collections.abc import Callable, Hashable, Mapping, Sequence from copy import deepcopy from functools import partial -from typing import Any, Callable +from typing import Any import numpy as np import torch diff --git a/monai/utils/component_store.py b/monai/utils/component_store.py index bf0d632ddd..360cea8fe3 100644 --- a/monai/utils/component_store.py +++ b/monai/utils/component_store.py @@ -12,10 +12,10 @@ from __future__ import annotations from collections import namedtuple -from collections.abc import Iterable +from collections.abc import Callable, Iterable from keyword import iskeyword from textwrap import dedent, indent -from typing import Any, Callable, TypeVar +from typing import Any, TypeVar T = TypeVar("T") diff --git a/monai/utils/decorators.py b/monai/utils/decorators.py index a784510c64..4a8b098ce1 100644 --- a/monai/utils/decorators.py +++ b/monai/utils/decorators.py @@ -15,8 +15,7 @@ __all__ = ["RestartGenerator", "MethodReplacer"] -from collections.abc import Generator -from typing import Callable +from collections.abc import Callable, Generator class RestartGenerator: diff --git a/monai/visualize/gradient_based.py b/monai/visualize/gradient_based.py index c54c9cd4ca..a8d81eb579 100644 --- a/monai/visualize/gradient_based.py +++ b/monai/visualize/gradient_based.py @@ -11,8 +11,9 @@ from __future__ import annotations +from collections.abc import Callable from functools import partial -from typing import Any, Callable +from typing import Any import torch diff --git a/pyproject.toml b/pyproject.toml index 3e63c38d3f..325622b66a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,14 +3,15 @@ requires = [ "wheel", "setuptools", "more-itertools>=8.0", - "torch>=2.4.1", + "torch>=2.8.0", "ninja", "packaging" ] [tool.black] line-length = 120 -target-version = ['py39', 'py310', 'py311', 'py312'] +target-version = ['py310'] +skip-magic-trailing-comma = true include = '\.pyi?$' exclude = ''' ( @@ -30,6 +31,7 @@ exclude = ''' | dist )/ # also separately exclude a file named versioneer.py + | versioneer.py | monai/_version.py ) ''' @@ -40,7 +42,7 @@ exclude = "monai/bundle/__main__.py" [tool.ruff] line-length = 120 -target-version = "py39" +target-version = "py310" [tool.ruff.lint] select = [ diff --git a/requirements-dev.txt b/requirements-dev.txt index 16d91a39a7..a948de701c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,7 @@ -r requirements-min.txt pytorch-ignite gdown>=4.7.3 -scipy>=1.12.0; python_version >= '3.9' +scipy>=1.12.0 itk>=5.2 nibabel pillow!=8.3.0 # https://github.com/python-pillow/Pillow/issues/5571 @@ -14,7 +14,7 @@ mccabe pep8-naming pycodestyle pyflakes -black==25.1.0 +black>=26.3.1 isort>=5.1, <6, !=6.0.0 ruff>=0.14.11,<0.15 pytype>=2020.6.1, <=2024.4.11; platform_system != "Windows" @@ -24,7 +24,8 @@ ninja torchio torchvision psutil -cucim-cu12; platform_system == "Linux" and python_version >= "3.9" and python_version <= "3.10" +cucim-cu12; platform_system == "Linux" and python_version <= "3.10" +cucim-cu13; platform_system == "Linux" and python_version >= '3.11' openslide-python openslide-bin imagecodecs; platform_system == "Linux" or platform_system == "Darwin" @@ -61,3 +62,4 @@ pyamg>=5.0.0, <5.3.0 git+https://github.com/facebookresearch/segment-anything.git@6fdee8f2727f4506cfbbe553e23b895e27956588 onnx_graphsurgeon polygraphy +pybind11 diff --git a/requirements.txt b/requirements.txt index c43fb21f92..7d283182a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -torch>=2.4.1; platform_system != "Windows" -torch>=2.4.1, !=2.7.0; platform_system == "Windows" +torch>=2.8.0 numpy>=1.24,<3.0 diff --git a/runtests.sh b/runtests.sh index a1551fe8dd..01401af4d7 100755 --- a/runtests.sh +++ b/runtests.sh @@ -516,9 +516,9 @@ then if [ $doBlackFix = true ] then - ${cmdPrefix}"${PY_EXE}" -m black --skip-magic-trailing-comma "$homedir" + ${cmdPrefix}"${PY_EXE}" -m black "$homedir" else - ${cmdPrefix}"${PY_EXE}" -m black --skip-magic-trailing-comma --check "$homedir" + ${cmdPrefix}"${PY_EXE}" -m black --check "$homedir" fi black_status=$? diff --git a/setup.cfg b/setup.cfg index c24eae2ee6..724d1eceb3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,9 +21,10 @@ classifiers = Intended Audience :: Healthcare Industry Programming Language :: C++ Programming Language :: Python :: 3 - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 Topic :: Scientific/Engineering Topic :: Scientific/Engineering :: Artificial Intelligence Topic :: Scientific/Engineering :: Medical Science Apps. @@ -33,7 +34,7 @@ classifiers = Typing :: Typed [options] -python_requires = >= 3.9 +python_requires = >= 3.10 # for compiling and develop setup only # no need to specify the versions so that we could # compile for multiple targeted versions. @@ -42,7 +43,7 @@ setup_requires = ninja packaging install_requires = - torch>=2.4.1 + torch>=2.8.0 numpy>=1.24,<3.0 [options.extras_require] @@ -50,7 +51,7 @@ all = nibabel ninja scikit-image>=0.14.2 - scipy>=1.12.0; python_version >= '3.9' + scipy>=1.12.0 pillow tensorboard gdown>=4.7.3 @@ -61,7 +62,7 @@ all = tqdm>=4.47.0 lmdb psutil - cucim-cu12; platform_system == "Linux" and python_version >= '3.9' and python_version <= '3.10' + cucim-cu12; platform_system == "Linux" and python_version <= '3.10' cucim-cu13; platform_system == "Linux" and python_version >= '3.11' openslide-python openslide-bin @@ -69,7 +70,7 @@ all = imagecodecs; platform_system == "Linux" or platform_system == "Darwin" pandas einops - transformers>=4.36.0, <4.41.0; python_version <= '3.10' + transformers>=4.53.0 mlflow>=2.12.2 clearml>=1.10.0rc0 matplotlib>=3.6.3 @@ -83,7 +84,7 @@ all = nni; platform_system == "Linux" and "arm" not in platform_machine and "aarch" not in platform_machine optuna onnx>=1.13.0 - onnxruntime; python_version <= '3.10' + onnxruntime zarr lpips==0.1.4 nvidia-ml-py @@ -96,7 +97,7 @@ ninja = skimage = scikit-image>=0.14.2 scipy = - scipy>=1.12.0; python_version >= '3.9' + scipy>=1.12.0 pillow = pillow!=8.3.0 tensorboard = @@ -118,7 +119,7 @@ lmdb = psutil = psutil cucim = - cucim-cu12; platform_system == "Linux" and python_version >= '3.9' and python_version <= '3.10' + cucim-cu12; platform_system == "Linux" and python_version <= '3.10' cucim-cu13; platform_system == "Linux" and python_version >= '3.11' openslide = openslide-python diff --git a/tests/bundle/test_config_item.py b/tests/bundle/test_config_item.py index 4909ecf6be..e7becb66bc 100644 --- a/tests/bundle/test_config_item.py +++ b/tests/bundle/test_config_item.py @@ -12,8 +12,8 @@ from __future__ import annotations import unittest +from collections.abc import Callable from functools import partial -from typing import Callable import torch from parameterized import parameterized diff --git a/tests/test_utils.py b/tests/test_utils.py index e365237401..d040a10a51 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -28,13 +28,13 @@ import traceback import unittest import warnings -from collections.abc import Iterable +from collections.abc import Callable, Iterable from contextlib import contextmanager from functools import partial, reduce from itertools import product from pathlib import Path from subprocess import PIPE, Popen -from typing import Any, Callable +from typing import Any from urllib.error import ContentTooShortError, HTTPError import numpy as np