diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6da5134f..ac749860 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,11 +51,14 @@ jobs: - "3.14" - "pypy3.9" - "pypy3.10" + - "pypy3.11" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 @@ -80,27 +83,6 @@ jobs: # because we monkeypatch typing under some circumstances. python -c 'import typing_extensions; import test.__main__' test_typing -v - linting: - name: Lint - - # no reason to run this as a cron job - if: github.event_name != 'schedule' - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3" - cache: "pip" - cache-dependency-path: "test-requirements.txt" - - name: Install dependencies - run: pip install -r test-requirements.txt - - name: Lint implementation - run: ruff check - create-issue-on-failure: name: Create an issue if daily tests failed runs-on: ubuntu-latest diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 47704723..e078218f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,12 +24,16 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.x" - name: Check package metadata - run: python scripts/check_package.py ${{ github.ref }} + env: + GITHUB_REF: ${{ github.ref }} + run: python scripts/check_package.py "${GITHUB_REF}" - name: Install pypa/build run: | # Be wary of running `pip install` here, since it becomes easy for us to @@ -52,6 +56,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: @@ -63,9 +69,10 @@ jobs: path: dist/ - name: Install wheel run: | - export path_to_file=$(find dist -type f -name "typing_extensions-*.whl") + path_to_file="$(find dist -type f -name "typing_extensions-*.whl")" + export path_to_file echo "::notice::Installing wheel: $path_to_file" - python -m pip install --user $path_to_file + python -m pip install --user "$path_to_file" python -m pip list - name: Run typing_extensions tests against installed package run: rm src/typing_extensions.py && python src/test_typing_extensions.py @@ -78,6 +85,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: @@ -89,10 +98,11 @@ jobs: path: dist/ - name: Unpack and test source distribution run: | - export path_to_file=$(find dist -type f -name "typing_extensions-*.tar.gz") + path_to_file="$(find dist -type f -name "typing_extensions-*.tar.gz")" + export path_to_file echo "::notice::Unpacking source distribution: $path_to_file" - tar xzf $path_to_file -C dist/ - cd ${path_to_file%.tar.gz}/src + tar xzf "$path_to_file" -C dist/ + cd "${path_to_file%.tar.gz}/src" python test_typing_extensions.py test-sdist-installed: @@ -103,6 +113,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: @@ -114,9 +126,10 @@ jobs: path: dist/ - name: Install source distribution run: | - export path_to_file=$(find dist -type f -name "typing_extensions-*.tar.gz") + path_to_file="$(find dist -type f -name "typing_extensions-*.tar.gz")" + export path_to_file echo "::notice::Installing source distribution: $path_to_file" - python -m pip install --user $path_to_file + python -m pip install --user "$path_to_file" python -m pip list - name: Run typing_extensions tests against installed package run: rm src/typing_extensions.py && python src/test_typing_extensions.py @@ -144,6 +157,6 @@ jobs: name: python-package-distributions path: dist/ - name: Ensure exactly one sdist and one wheel have been downloaded - run: test $(ls dist/*.tar.gz | wc -l) = 1 && test $(ls dist/*.whl | wc -l) = 1 + run: test "$(find dist/*.tar.gz | wc -l | xargs)" = 1 && test "$(find dist/*.whl | wc -l | xargs)" = 1 - name: Publish distribution to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc diff --git a/.github/workflows/third_party.yml b/.github/workflows/third_party.yml index a15735b0..4e2e895f 100644 --- a/.github/workflows/third_party.yml +++ b/.github/workflows/third_party.yml @@ -35,7 +35,7 @@ jobs: github.repository == 'python/typing_extensions' || github.event_name != 'schedule' steps: - - run: true + - run: "true" pydantic: name: pydantic tests @@ -63,6 +63,7 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Add local version of typing_extensions as a dependency run: cd pydantic; uv add --editable ../typing-extensions-latest - name: Install pydantic test dependencies @@ -94,11 +95,12 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Install typing_inspect test dependencies run: | set -x cd typing_inspect - uv pip install --system -r test-requirements.txt --exclude-newer $(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD) + uv pip install --system -r test-requirements.txt --exclude-newer "$(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD)" - name: Install typing_extensions latest run: uv pip install --system "typing-extensions @ ./typing-extensions-latest" - name: List all installed dependencies @@ -131,11 +133,12 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Install pycroscope test requirements run: | set -x cd pycroscope - uv pip install --system 'pycroscope[tests] @ .' --exclude-newer $(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD) + uv pip install --system 'pycroscope[tests] @ .' --exclude-newer "$(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD)" - name: Install typing_extensions latest run: uv pip install --system "typing-extensions @ ./typing-extensions-latest" - name: List all installed dependencies @@ -168,11 +171,12 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Install typeguard test requirements run: | set -x cd typeguard - uv pip install --system "typeguard @ ." --group test --exclude-newer $(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD) + uv pip install --system "typeguard @ ." --group test --exclude-newer "$(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD)" - name: Install typing_extensions latest run: uv pip install --system "typing-extensions @ ./typing-extensions-latest" - name: List all installed dependencies @@ -205,6 +209,7 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Configure git for typed-argument-parser tests # typed-argument parser does this in their CI, # and the tests fail unless we do this @@ -215,8 +220,8 @@ jobs: run: | set -x cd typed-argument-parser - uv pip install --system "typed-argument-parser @ ." --exclude-newer $(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD) - uv pip install --system pytest --exclude-newer $(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD) + uv pip install --system "typed-argument-parser @ ." --exclude-newer "$(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD)" + uv pip install --system pytest --exclude-newer "$(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD)" - name: Install typing_extensions latest run: uv pip install --system "typing-extensions @ ./typing-extensions-latest" - name: List all installed dependencies @@ -249,11 +254,12 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Install mypy test requirements run: | set -x cd mypy - uv pip install --system -r test-requirements.txt --exclude-newer $(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD) + uv pip install --system -r test-requirements.txt --exclude-newer "$(git show -s --date=format:'%Y-%m-%dT%H:%M:%SZ' --format=%cd HEAD)" uv pip install --system -e . - name: Install typing_extensions latest run: uv pip install --system "typing-extensions @ ./typing-extensions-latest" @@ -284,6 +290,7 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Install pdm for cattrs run: pip install pdm - name: Add latest typing-extensions as a dependency @@ -291,8 +298,6 @@ jobs: cd cattrs pdm remove typing-extensions pdm add --dev ../typing-extensions-latest - pdm update --group=docs pendulum # pinned version in lockfile is incompatible with py313 as of 2025/05/05 - pdm sync --clean - name: Install cattrs test dependencies run: cd cattrs; pdm install --dev -G :all - name: List all installed dependencies @@ -328,6 +333,7 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Install sqlalchemy test dependencies run: uv pip install --system tox setuptools - name: List installed dependencies @@ -364,6 +370,7 @@ jobs: uses: actions/checkout@v4 with: path: typing-extensions-latest + persist-credentials: false - name: Install uv run: curl -LsSf https://astral.sh/uv/install.sh | sh - name: Run litestar tests diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..47f43d6b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,49 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.2 + hooks: + - id: ruff + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-docstring-first + - id: check-yaml + - id: check-toml + - id: check-merge-conflict + - id: check-case-conflict + - id: forbid-submodules + - id: mixed-line-ending + args: [--fix=lf] + - repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v1.0.0 + hooks: + - id: sphinx-lint + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.33.0 + hooks: + - id: check-dependabot + - id: check-github-workflows + - id: check-readthedocs + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.24.1 + hooks: + - id: validate-pyproject + additional_dependencies: ["validate-pyproject-schema-store[all]"] + - repo: https://github.com/rhysd/actionlint + rev: v1.7.7 + hooks: + - id: actionlint + additional_dependencies: + # actionlint has a shellcheck integration which extracts shell scripts in `run:` steps from GitHub Actions + # and checks these with shellcheck. This is arguably its most useful feature, + # but the integration only works if shellcheck is installed + - "github.com/wasilibs/go-shellcheck/cmd/shellcheck@v0.10.0" + - repo: https://github.com/woodruffw/zizmor-pre-commit + rev: v1.11.0 + hooks: + - id: zizmor + - repo: meta + hooks: + - id: check-hooks-apply diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 60419be8..5de3b9a3 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -10,4 +10,3 @@ build: sphinx: configuration: doc/conf.py - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b030d56..086ba3f7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,6 +51,31 @@ Running these commands in the `src/` directory ensures that the local file `typing_extensions.py` is used, instead of any other version of the library you may have installed. +# Linting + +Linting is done via pre-commit. We recommend running pre-commit via a tool such +as [uv](https://docs.astral.sh/uv/) or [pipx](https://pipx.pypa.io/stable/) so +that pre-commit and its dependencies are installed into an isolated environment +located outside your `typing_extensions` clone. Running pre-commit this way +ensures that you don't accidentally install a version of `typing_extensions` +from PyPI into a virtual environment inside your `typing_extensions` clone, +which could easily happen if pre-commit depended (directly or indirectly) on +`typing_extensions`. If a version of `typing_extensions` from PyPI *was* +installed into a project-local virtual environment, it could lead to +unpredictable results when running `typing_extensions` tests locally. + +To run the linters using uv: + +``` +uvx pre-commit run -a +``` + +Or using pipx: + +``` +pipx run pre-commit run -a +``` + # Workflow for PyPI releases - Make sure you follow the versioning policy in the documentation diff --git a/doc/make.bat b/doc/make.bat index 32bb2452..954237b9 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -1,35 +1,35 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/pyproject.toml b/pyproject.toml index 38475b93..66528698 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,10 +91,13 @@ ignore = [ "UP019", "UP035", "UP038", + "UP045", # X | None instead of Optional[X] # Not relevant here - "RUF012", - "RUF022", - "RUF023", + "RUF012", # Use ClassVar for mutables + "RUF022", # Unsorted __all__ + "RUF023", # Unsorted __slots__ + "B903", # Use dataclass / namedtuple + "RUF031", # parentheses for tuples in subscripts # Ruff doesn't understand the globals() assignment; we test __all__ # directly in test_all_names_in___all__. "F822", @@ -109,6 +112,9 @@ ignore = [ "E306", "E501", "E701", + # Harmful for tests if applied. + "RUF036", # None not at end of Union + "RUF041", # nested Literal ] [tool.ruff.lint.isort] diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 4b0fc81e..00000000 --- a/test-requirements.txt +++ /dev/null @@ -1 +0,0 @@ -ruff==0.9.6