diff --git a/.github/workflows/diff_shades.yml b/.github/workflows/diff_shades.yml index 0e1aab0..51a448a 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 9d5f308..48f101c 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 ca548ec..a7cde47 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 5c8eb35..9c41d1d 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 e5d931a..514fd14 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 f04118a..c75d6fe 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 4918bc6..44dd97e 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 35bddb5..7774c59 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 d4c1208..90bf705 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 78e9564..f534dbb 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 6fd7252..d283ea6 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")