diff --git a/.github/workflows/diff_shades.yml b/.github/workflows/diff_shades.yml index 0e1aab00e34..51a448a12a5 100644 --- a/.github/workflows/diff_shades.yml +++ b/.github/workflows/diff_shades.yml @@ -26,7 +26,7 @@ jobs: - name: Install diff-shades and support dependencies run: | - python -m pip install 'click==8.1.3' packaging urllib3 + python -m pip install 'click>=8.1.7' packaging urllib3 python -m pip install https://github.com/ichard26/diff-shades/archive/stable.zip - name: Calculate run configuration & metadata @@ -64,7 +64,7 @@ jobs: - name: Install diff-shades and support dependencies run: | python -m pip install https://github.com/ichard26/diff-shades/archive/stable.zip - python -m pip install 'click==8.1.3' packaging urllib3 + python -m pip install 'click>=8.1.7' packaging urllib3 # After checking out old revisions, this might not exist so we'll use a copy. cat scripts/diff_shades_gha_helper.py > helper.py git config user.name "diff-shades-gha" diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 9d5f308e4e8..48f101c206f 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12.4", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12.4", "3.13"] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pypi_upload.yml b/.github/workflows/pypi_upload.yml index ca548ecdd65..a7cde47b229 100644 --- a/.github/workflows/pypi_upload.yml +++ b/.github/workflows/pypi_upload.yml @@ -74,7 +74,7 @@ jobs: | pyp 'json.dumps({"only": x, "os": "ubuntu-latest"})' } | pyp 'json.dumps(list(map(json.loads, lines)))' > /tmp/matrix env: - CIBW_BUILD: "cp38-* cp312-*" + CIBW_BUILD: "cp39-* cp312-*" CIBW_ARCHS_LINUX: x86_64 - id: set-matrix run: echo "include=$(cat /tmp/matrix)" | tee -a $GITHUB_OUTPUT diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c8eb35ff1c..9c41d1db547 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12.4", "3.13", "pypy-3.9"] + python-version: ["3.9", "3.10", "3.11", "3.12.4", "3.13", "pypy-3.9"] os: [ubuntu-latest, macOS-latest, windows-latest] steps: diff --git a/CHANGES.md b/CHANGES.md index e5d931a50bb..514fd14036b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,7 @@ - Black will issue an error when used with Python 3.12.5, due to an upstream memory safety issue in Python 3.12.5 that can cause Black's AST safety checks to fail. Please use Python 3.12.6 or Python 3.12.4 instead. (#4447) +- Black no longer supports running with Python 3.8 (#4452) ### Stable style diff --git a/pyproject.toml b/pyproject.toml index f04118a8674..c75d6fec266 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ [tool.black] line-length = 88 -target-version = ['py38'] +target-version = ['py39'] include = '\.pyi?$' extend-exclude = ''' /( @@ -34,7 +34,7 @@ build-backend = "hatchling.build" name = "black" description = "The uncompromising code formatter." license = { text = "MIT" } -requires-python = ">=3.8" +requires-python = ">=3.9" authors = [ { name = "Ɓukasz Langa", email = "lukasz@langa.pl" }, ] @@ -55,7 +55,6 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -152,7 +151,7 @@ options = { debug_level = "0" } build-verbosity = 1 # So these are the environments we target: -# - Python: CPython 3.8+ only +# - Python: CPython 3.9+ only # - Architecture (64-bit only): amd64 / x86_64, universal2, and arm64 # - OS: Linux (no musl), Windows, and macOS build = "cp3*" @@ -232,7 +231,7 @@ branch = true # Specify the target platform details in config, so your developers are # free to run mypy on Windows, Linux, or macOS and get consistent # results. -python_version = "3.8" +python_version = "3.9" mypy_path = "src" strict = true # Unreachable blocks have been an issue when compiling mypyc, let's try to avoid 'em in the first place. diff --git a/src/black/__init__.py b/src/black/__init__.py index 4918bc67610..44dd97e6ac8 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -549,6 +549,7 @@ def main( # noqa: C901 """The uncompromising code formatter.""" ctx.ensure_object(dict) + assert sys.version_info >= (3, 9), "Black requires Python 3.9+" if sys.version_info[:3] == (3, 12, 5): out( "Python 3.12.5 has a memory safety issue that can cause Black's " diff --git a/src/black/cache.py b/src/black/cache.py index 35bddb573d2..7774c594ac2 100644 --- a/src/black/cache.py +++ b/src/black/cache.py @@ -139,8 +139,7 @@ def write(self, sources: Iterable[Path]) -> None: with tempfile.NamedTemporaryFile( dir=str(self.cache_file.parent), delete=False ) as f: - # We store raw tuples in the cache because pickling NamedTuples - # doesn't work with mypyc on Python 3.8, and because it's faster. + # We store raw tuples in the cache because it's faster. data: Dict[str, Tuple[float, int, str]] = { k: (*v,) for k, v in self.file_data.items() } diff --git a/src/black/files.py b/src/black/files.py index d4c1208076b..90bf705acc7 100644 --- a/src/black/files.py +++ b/src/black/files.py @@ -272,8 +272,6 @@ def resolves_outside_root_or_cannot_stat( root directory. Also returns True if we failed to resolve the path. """ try: - if sys.version_info < (3, 8, 6): - path = path.absolute() # https://bugs.python.org/issue33660 resolved_path = _cached_resolve(path) except OSError as e: if report: diff --git a/src/black/schema.py b/src/black/schema.py index 78e9564cdbc..f534dbb028d 100644 --- a/src/black/schema.py +++ b/src/black/schema.py @@ -1,6 +1,5 @@ import importlib.resources import json -import sys from typing import Any @@ -11,10 +10,6 @@ def get_schema(tool_name: str = "black") -> Any: pkg = "black.resources" fname = "black.schema.json" - if sys.version_info < (3, 9): - with importlib.resources.open_text(pkg, fname, encoding="utf-8") as f: - return json.load(f) - - schema = importlib.resources.files(pkg).joinpath(fname) # type: ignore[unreachable] + schema = importlib.resources.files(pkg).joinpath(fname) with schema.open(encoding="utf-8") as f: return json.load(f) diff --git a/tests/test_black.py b/tests/test_black.py index 6fd725208d2..d283ea62de4 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -2157,8 +2157,9 @@ def test_cache_single_file_already_cached(self) -> None: @event_loop() def test_cache_multiple_files(self) -> None: mode = DEFAULT_MODE - with cache_dir() as workspace, patch( - "concurrent.futures.ProcessPoolExecutor", new=ThreadPoolExecutor + with ( + cache_dir() as workspace, + patch("concurrent.futures.ProcessPoolExecutor", new=ThreadPoolExecutor), ): one = (workspace / "one.py").resolve() one.write_text("print('hello')", encoding="utf-8") @@ -2180,9 +2181,10 @@ def test_no_cache_when_writeback_diff(self, color: bool) -> None: with cache_dir() as workspace: src = (workspace / "test.py").resolve() src.write_text("print('hello')", encoding="utf-8") - with patch.object(black.Cache, "read") as read_cache, patch.object( - black.Cache, "write" - ) as write_cache: + with ( + patch.object(black.Cache, "read") as read_cache, + patch.object(black.Cache, "write") as write_cache, + ): cmd = [str(src), "--diff"] if color: cmd.append("--color") @@ -2311,8 +2313,9 @@ def test_write_cache_creates_directory_if_needed(self) -> None: @event_loop() def test_failed_formatting_does_not_get_cached(self) -> None: mode = DEFAULT_MODE - with cache_dir() as workspace, patch( - "concurrent.futures.ProcessPoolExecutor", new=ThreadPoolExecutor + with ( + cache_dir() as workspace, + patch("concurrent.futures.ProcessPoolExecutor", new=ThreadPoolExecutor), ): failing = (workspace / "failing.py").resolve() failing.write_text("not actually python", encoding="utf-8")