diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 43bdc7a671b484..cc1fa7cb42d67e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -41,8 +41,8 @@ env: UV_CACHE_VERSION: 1 MYPY_CACHE_VERSION: 9 HA_SHORT_VERSION: "2025.1" - DEFAULT_PYTHON: "3.12" - ALL_PYTHON_VERSIONS: "['3.12', '3.13']" + DEFAULT_PYTHON: "3.13" + ALL_PYTHON_VERSIONS: "['3.13']" # 10.3 is the oldest supported version # - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022) # 10.6 is the current long-term-support @@ -773,6 +773,10 @@ jobs: needs: - info - base + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REMOTE: cdce8p + BRANCH: dev steps: - name: Check out code from GitHub uses: actions/checkout@v4.2.2 @@ -782,10 +786,32 @@ jobs: with: python-version: ${{ env.DEFAULT_PYTHON }} check-latest: true + + - name: Fetch mypy version string (${{ env.BRANCH }}) + id: fetch-version + run: | + echo "Checking mypy branch: ${{ env.BRANCH }}" + base=$(curl -sS \ + https://raw.githubusercontent.com/${{ env.REMOTE }}/mypy/${{ env.BRANCH }}/mypy/version.py | \ + grep -e ^__version__ | \ + cut -d '=' -f2 | tr -d ' "') + sha=$(gh api \ + -H "Accept: application/vnd.github+json" \ + /repos/${{ env.REMOTE }}/mypy/git/ref/heads/${{ env.BRANCH }} | \ + jq -r '.object.sha') + if [[ $base == *"+dev" ]]; then + version="v$base.$sha" + else + version="v$base" + fi + echo "version=$version" >> $GITHUB_OUTPUT + echo "name=(${{ env.BRANCH }} -- $version)" >> $GITHUB_OUTPUT + echo "Found: $version" + - name: Generate partial mypy restore key id: generate-mypy-key run: | - mypy_version=$(cat requirements_test.txt | grep mypy | cut -d '=' -f 3) + mypy_version=${{ steps.fetch-version.outputs.version }} echo "version=$mypy_version" >> $GITHUB_OUTPUT echo "key=mypy-${{ env.MYPY_CACHE_VERSION }}-$mypy_version-${{ env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT @@ -798,8 +824,108 @@ jobs: key: >- ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ needs.info.outputs.python_cache_key }} + + - name: Remove old mypy version + run: | + . venv/bin/activate + pip uninstall -y mypy + - name: Restore custom mypy version in venv ${{ steps.fetch-version.outputs.name }} + id: cache-venv-mypy + uses: actions/cache/restore@v4 + with: + path: | + venv/lib/python*/site-packages/*__mypyc.cpython-*.so + venv/lib/python*/site-packages/mypy* + venv/bin/mypy + key: >- + ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv_mypy-${{ + env.CACHE_VERSION }}-${{ steps.fetch-version.outputs.version }}-compiled + restore-keys: | + ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv_mypy-${{ + env.CACHE_VERSION }}-${{ steps.fetch-version.outputs.version }}-compiled-custom + ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv_mypy-${{ + env.CACHE_VERSION }}-${{ steps.fetch-version.outputs.version }} + - name: Install compiled version if available + if: steps.cache-venv-mypy.outputs.cache-hit != 'true' + id: install-compiled + continue-on-error: true + run: | + . venv/bin/activate + tag=${{ steps.fetch-version.outputs.version }} + echo "$tag" + res=$(gh api \ + -H "Accept: application/vnd.github+json" \ + /repos/mypyc/mypy_mypyc-wheels/releases/tags/$tag) + py=$(echo ${{ env.DEFAULT_PYTHON }} | cut -d '.' -f 1,2 | tr -d '.') + wheel_name=$py-manylinux + echo "$wheel_name" + url=$(echo $res | jq -r \ + --arg search_name "$wheel_name" \ + '.assets[] | select(.name | test($search_name)) .browser_download_url') + if [[ -n $url ]]; then + echo "Found compiled version" + pip install -U $url + echo "status=done" >> $GITHUB_OUTPUT + echo "key-suffix=-compiled" >> $GITHUB_OUTPUT + exit 0 + fi + - name: Install compiled version if available (custom) + if: | + steps.cache-venv-mypy.outputs.cache-hit != 'true' + && steps.install-compiled.outputs.status != 'done' + && endsWith( steps.cache-venv-mypy.outputs.cache-matched-key, '-compiled-custom' ) != true + id: install-compiled-custom + continue-on-error: true + run: | + . venv/bin/activate + tag=${{ steps.fetch-version.outputs.version }} + echo "$tag" + res=$(gh api \ + -H "Accept: application/vnd.github+json" \ + /repos/${{ env.REMOTE }}/mypy-wheels/releases/tags/$tag) + py=$(echo ${{ env.DEFAULT_PYTHON }} | cut -d '.' -f 1,2 | tr -d '.') + wheel_name=$py-manylinux + echo "$wheel_name" + url=$(echo $res | jq -r \ + --arg search_name "$wheel_name" \ + '.assets[] | select(.name | test($search_name)) .browser_download_url') + if [[ -n $url ]]; then + echo "Found compiled version" + pip install -U $url + echo "status=done" >> $GITHUB_OUTPUT + echo "key-suffix=-compiled-custom" >> $GITHUB_OUTPUT + exit 0 + fi + - name: Install custom dependencies + if: | + steps.cache-venv-mypy.outputs.cache-matched-key == '' + && steps.install-compiled.outputs.status != 'done' + && steps.install-compiled-custom.outputs.status != 'done' + run: | + . venv/bin/activate + python --version + pip install -U git+https://github.com/${{ env.REMOTE }}/mypy.git@${{ env.BRANCH }} + - name: Save mypy version in venv + if: | + steps.cache-venv-mypy.outputs.cache-hit != 'true' + && ( + steps.cache-venv-mypy.outputs.cache-matched-key == '' + || steps.install-compiled.outputs.status == 'done' + || steps.install-compiled-custom.outputs.status == 'done' + ) + uses: actions/cache/save@v4 + with: + path: | + venv/lib/python*/site-packages/*__mypyc.cpython-*.so + venv/lib/python*/site-packages/mypy* + venv/bin/mypy + key: >- + ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv_mypy-${{ + env.CACHE_VERSION }}-${{ steps.fetch-version.outputs.version }}${{ + steps.install-compiled.outputs.key-suffix || steps.install-compiled-custom.outputs.key-suffix }} + - name: Restore mypy cache - uses: actions/cache@v4.2.0 + uses: actions/cache/restore@v4.2.0 with: path: .mypy_cache key: >- @@ -817,6 +943,9 @@ jobs: run: | . venv/bin/activate python --version + git branch --show-current + # For issues with serialize, add --cache-dir=/dev/null + # To install types: --install-types --non-interactive mypy homeassistant pylint - name: Run mypy (partially) if: needs.info.outputs.test_full_suite == 'false' @@ -825,6 +954,14 @@ jobs: . venv/bin/activate python --version mypy homeassistant/components/${{ needs.info.outputs.integrations_glob }} + - name: Save mypy cache + if: success() || failure() + uses: actions/cache/save@v4 + with: + path: .mypy_cache + key: >- + ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ + steps.generate-mypy-key.outputs.key }} prepare-pytest-full: runs-on: ubuntu-24.04 diff --git a/mypy.ini b/mypy.ini index ce51adc3816259..dc52c7cf7db6b6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -8,6 +8,7 @@ platform = linux plugins = pydantic.mypy show_error_codes = true follow_imports = normal +show_traceback = true local_partial_types = true strict_equality = true no_implicit_optional = true diff --git a/script/hassfest/mypy_config.py b/script/hassfest/mypy_config.py index ec4d4b3d3a9d78..c7e2126fe15d36 100644 --- a/script/hassfest/mypy_config.py +++ b/script/hassfest/mypy_config.py @@ -36,8 +36,11 @@ "plugins": "pydantic.mypy", "show_error_codes": "true", "follow_imports": "normal", + "show_traceback": "true", # "enable_incomplete_feature": ", ".join( # noqa: FLY002 - # [] + # [ + # "PreciseTupleTypes" + # ] # ), # Enable some checks globally. "local_partial_types": "true",