Drop support for Python 3.7 (#3765)

This commit is contained in:
Shantanu 2023-07-05 10:08:04 -07:00 committed by GitHub
parent cf4cc29819
commit b4dca26c7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 43 additions and 213 deletions

View File

@ -22,7 +22,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] python-version: ["3.8", "3.9", "3.10", "3.11"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View File

@ -31,7 +31,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "pypy-3.7", "pypy-3.8"] python-version: ["3.8", "3.9", "3.10", "3.11", "pypy-3.8"]
os: [ubuntu-latest, macOS-latest, windows-latest] os: [ubuntu-latest, macOS-latest, windows-latest]
steps: steps:

View File

@ -6,6 +6,9 @@
<!-- Include any especially major or disruptive changes here --> <!-- Include any especially major or disruptive changes here -->
- Runtime support for Python 3.7 has been removed. Formatting 3.7 code will still be
supported until further notice (#3765)
### Stable style ### Stable style
<!-- Changes that affect Black's stable style --> <!-- Changes that affect Black's stable style -->

View File

@ -334,60 +334,6 @@ To run _Black_ on a key press (e.g. F9 below), add this:
nnoremap <F9> :Black<CR> nnoremap <F9> :Black<CR>
``` ```
#### Troubleshooting
**How to get Vim with Python 3.6?** On Ubuntu 17.10 Vim comes with Python 3.6 by
default. On macOS with Homebrew run: `brew install vim`. When building Vim from source,
use: `./configure --enable-python3interp=yes`. There's many guides online how to do
this.
**I get an import error when using _Black_ from a virtual environment**: If you get an
error message like this:
```text
Traceback (most recent call last):
File "<string>", line 63, in <module>
File "/home/gui/.vim/black/lib/python3.7/site-packages/black.py", line 45, in <module>
from typed_ast import ast3, ast27
File "/home/gui/.vim/black/lib/python3.7/site-packages/typed_ast/ast3.py", line 40, in <module>
from typed_ast import _ast3
ImportError: /home/gui/.vim/black/lib/python3.7/site-packages/typed_ast/_ast3.cpython-37m-x86_64-linux-gnu.so: undefined symbool: PyExc_KeyboardInterrupt
```
Then you need to install `typed_ast` directly from the source code. The error happens
because `pip` will download [Python wheels](https://pythonwheels.com/) if they are
available. Python wheels are a new standard of distributing Python packages and packages
that have Cython and extensions written in C are already compiled, so the installation
is much more faster. The problem here is that somehow the Python environment inside Vim
does not match with those already compiled C extensions and these kind of errors are the
result. Luckily there is an easy fix: installing the packages from the source code.
The package that causes problems is:
- [typed-ast](https://pypi.org/project/typed-ast/)
Now remove those two packages:
```console
$ pip uninstall typed-ast -y
```
And now you can install them with:
```console
$ pip install --no-binary :all: typed-ast
```
The C extensions will be compiled and now Vim's Python environment will match. Note that
you need to have the GCC compiler and the Python development files installed (on
Ubuntu/Debian do `sudo apt-get install build-essential python3-dev`).
If you later want to update _Black_, you should do it like this:
```console
$ pip install -U black --no-binary typed-ast
```
### With ALE ### With ALE
1. Install [`ale`](https://github.com/dense-analysis/ale) 1. Install [`ale`](https://github.com/dense-analysis/ale)

View File

@ -2,7 +2,7 @@
# Specify the target platform details in config, so your developers are # Specify the target platform details in config, so your developers are
# free to run mypy on Windows, Linux, or macOS and get consistent # free to run mypy on Windows, Linux, or macOS and get consistent
# results. # results.
python_version=3.7 python_version=3.8
mypy_path=src mypy_path=src

View File

@ -33,7 +33,7 @@ build-backend = "hatchling.build"
name = "black" name = "black"
description = "The uncompromising code formatter." description = "The uncompromising code formatter."
license = { text = "MIT" } license = { text = "MIT" }
requires-python = ">=3.7" requires-python = ">=3.8"
authors = [ authors = [
{ name = "Łukasz Langa", email = "lukasz@langa.pl" }, { name = "Łukasz Langa", email = "lukasz@langa.pl" },
] ]
@ -69,7 +69,6 @@ dependencies = [
"pathspec>=0.9.0", "pathspec>=0.9.0",
"platformdirs>=2", "platformdirs>=2",
"tomli>=1.1.0; python_version < '3.11'", "tomli>=1.1.0; python_version < '3.11'",
"typed-ast>=1.4.2; python_version < '3.8' and implementation_name == 'cpython'",
"typing_extensions>=3.10.0.0; python_version < '3.10'", "typing_extensions>=3.10.0.0; python_version < '3.10'",
] ]
dynamic = ["readme", "version"] dynamic = ["readme", "version"]
@ -121,8 +120,6 @@ enable-by-default = false
dependencies = [ dependencies = [
"hatch-mypyc>=0.16.0", "hatch-mypyc>=0.16.0",
"mypy==1.3", "mypy==1.3",
# Required stubs to be removed when the packages support PEP 561 themselves
"types-typed-ast>=1.4.2",
] ]
require-runtime-dependencies = true require-runtime-dependencies = true
exclude = [ exclude = [
@ -145,7 +142,7 @@ options = { debug_level = "0" }
[tool.cibuildwheel] [tool.cibuildwheel]
build-verbosity = 1 build-verbosity = 1
# So these are the environments we target: # So these are the environments we target:
# - Python: CPython 3.7+ only # - Python: CPython 3.8+ only
# - Architecture (64-bit only): amd64 / x86_64, universal2, and arm64 # - Architecture (64-bit only): amd64 / x86_64, universal2, and arm64
# - OS: Linux (no musl), Windows, and macOS # - OS: Linux (no musl), Windows, and macOS
build = "cp3*-*" build = "cp3*-*"
@ -208,9 +205,6 @@ filterwarnings = [
# this is mitigated by a try/catch in https://github.com/psf/black/pull/3198/ # this is mitigated by a try/catch in https://github.com/psf/black/pull/3198/
# this ignore can be removed when support for aiohttp 3.x is dropped. # this ignore can be removed when support for aiohttp 3.x is dropped.
'''ignore:Middleware decorator is deprecated since 4\.0 and its behaviour is default, you can simply remove this decorator:DeprecationWarning''', '''ignore:Middleware decorator is deprecated since 4\.0 and its behaviour is default, you can simply remove this decorator:DeprecationWarning''',
# this is mitigated by https://github.com/python/cpython/issues/79071 in python 3.8+
# this ignore can be removed when support for 3.7 is dropped.
'''ignore:Bare functions are deprecated, use async ones:DeprecationWarning''',
# aiohttp is using deprecated cgi modules - Safe to remove when fixed: # aiohttp is using deprecated cgi modules - Safe to remove when fixed:
# https://github.com/aio-libs/aiohttp/issues/6905 # https://github.com/aio-libs/aiohttp/issues/6905
'''ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning''', '''ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning''',

View File

@ -52,13 +52,7 @@ def main() -> None:
f.write(f"""# Generated by {basename(__file__)} f.write(f"""# Generated by {basename(__file__)}
# wcwidth {wcwidth.__version__} # wcwidth {wcwidth.__version__}
# Unicode {wcwidth.list_versions()[-1]} # Unicode {wcwidth.list_versions()[-1]}
import sys from typing import Final, List, Tuple
from typing import List, Tuple
if sys.version_info < (3, 8):
from typing_extensions import Final
else:
from typing import Final
WIDTH_TABLE: Final[List[Tuple[int, int, int]]] = [ WIDTH_TABLE: Final[List[Tuple[int, int, int]]] = [
""") """)

View File

@ -1,13 +1,7 @@
# Generated by make_width_table.py # Generated by make_width_table.py
# wcwidth 0.2.6 # wcwidth 0.2.6
# Unicode 15.0.0 # Unicode 15.0.0
import sys from typing import Final, List, Tuple
from typing import List, Tuple
if sys.version_info < (3, 8):
from typing_extensions import Final
else:
from typing import Final
WIDTH_TABLE: Final[List[Tuple[int, int, int]]] = [ WIDTH_TABLE: Final[List[Tuple[int, int, int]]] = [
(0, 0, 0), (0, 0, 0),

View File

@ -1,13 +1,7 @@
"""Builds on top of nodes.py to track brackets.""" """Builds on top of nodes.py to track brackets."""
import sys
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union from typing import Dict, Final, Iterable, List, Optional, Sequence, Set, Tuple, Union
if sys.version_info < (3, 8):
from typing_extensions import Final
else:
from typing import Final
from black.nodes import ( from black.nodes import (
BRACKET, BRACKET,

View File

@ -1,13 +1,7 @@
import re import re
import sys
from dataclasses import dataclass from dataclasses import dataclass
from functools import lru_cache from functools import lru_cache
from typing import Iterator, List, Optional, Union from typing import Final, Iterator, List, Optional, Union
if sys.version_info >= (3, 8):
from typing import Final
else:
from typing_extensions import Final
from black.nodes import ( from black.nodes import (
CLOSING_BRACKETS, CLOSING_BRACKETS,

View File

@ -4,19 +4,13 @@
chosen by the user. chosen by the user.
""" """
import sys
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum, auto from enum import Enum, auto
from hashlib import sha256 from hashlib import sha256
from operator import attrgetter from operator import attrgetter
from typing import Dict, Set from typing import Dict, Final, Set
from warnings import warn from warnings import warn
if sys.version_info < (3, 8):
from typing_extensions import Final
else:
from typing import Final
from black.const import DEFAULT_LINE_LENGTH from black.const import DEFAULT_LINE_LENGTH

View File

@ -3,12 +3,8 @@
""" """
import sys import sys
from typing import Generic, Iterator, List, Optional, Set, Tuple, TypeVar, Union from typing import Final, Generic, Iterator, List, Optional, Set, Tuple, TypeVar, Union
if sys.version_info >= (3, 8):
from typing import Final
else:
from typing_extensions import Final
if sys.version_info >= (3, 10): if sys.version_info >= (3, 10):
from typing import TypeGuard from typing import TypeGuard
else: else:

View File

@ -2,14 +2,8 @@
Parse Python code and perform AST validation. Parse Python code and perform AST validation.
""" """
import ast import ast
import platform
import sys import sys
from typing import Any, Iterable, Iterator, List, Set, Tuple, Type, Union from typing import Final, Iterable, Iterator, List, Set, Tuple
if sys.version_info < (3, 8):
from typing_extensions import Final
else:
from typing import Final
from black.mode import VERSION_TO_FEATURES, Feature, TargetVersion, supports_feature from black.mode import VERSION_TO_FEATURES, Feature, TargetVersion, supports_feature
from black.nodes import syms from black.nodes import syms
@ -20,25 +14,6 @@
from blib2to3.pgen2.tokenize import TokenError from blib2to3.pgen2.tokenize import TokenError
from blib2to3.pytree import Leaf, Node from blib2to3.pytree import Leaf, Node
ast3: Any
_IS_PYPY = platform.python_implementation() == "PyPy"
try:
from typed_ast import ast3
except ImportError:
if sys.version_info < (3, 8) and not _IS_PYPY:
print(
"The typed_ast package is required but not installed.\n"
"You can upgrade to Python 3.8+ or install typed_ast with\n"
"`python3 -m pip install typed-ast`.",
file=sys.stderr,
)
sys.exit(1)
else:
ast3 = ast
PY2_HINT: Final = "Python 2 support was removed in version 22.0." PY2_HINT: Final = "Python 2 support was removed in version 22.0."
@ -147,31 +122,14 @@ def lib2to3_unparse(node: Node) -> str:
def parse_single_version( def parse_single_version(
src: str, version: Tuple[int, int], *, type_comments: bool src: str, version: Tuple[int, int], *, type_comments: bool
) -> Union[ast.AST, ast3.AST]: ) -> ast.AST:
filename = "<unknown>" filename = "<unknown>"
# typed-ast is needed because of feature version limitations in the builtin ast 3.8>
if sys.version_info >= (3, 8) and version >= (3,):
return ast.parse( return ast.parse(
src, filename, feature_version=version, type_comments=type_comments src, filename, feature_version=version, type_comments=type_comments
) )
if _IS_PYPY:
# PyPy 3.7 doesn't support type comment tracking which is not ideal, but there's
# not much we can do as typed-ast won't work either.
if sys.version_info >= (3, 8):
return ast3.parse(src, filename, type_comments=type_comments)
else:
return ast3.parse(src, filename)
else:
if type_comments:
# Typed-ast is guaranteed to be used here and automatically tracks type
# comments separately.
return ast3.parse(src, filename, feature_version=version[1])
else:
return ast.parse(src, filename)
def parse_ast(src: str) -> ast.AST:
def parse_ast(src: str) -> Union[ast.AST, ast3.AST]:
# TODO: support Python 4+ ;) # TODO: support Python 4+ ;)
versions = [(3, minor) for minor in range(3, sys.version_info[1] + 1)] versions = [(3, minor) for minor in range(3, sys.version_info[1] + 1)]
@ -193,9 +151,6 @@ def parse_ast(src: str) -> Union[ast.AST, ast3.AST]:
raise SyntaxError(first_error) raise SyntaxError(first_error)
ast3_AST: Final[Type[ast3.AST]] = ast3.AST
def _normalize(lineend: str, value: str) -> str: def _normalize(lineend: str, value: str) -> str:
# To normalize, we strip any leading and trailing space from # To normalize, we strip any leading and trailing space from
# each line... # each line...
@ -206,22 +161,24 @@ def _normalize(lineend: str, value: str) -> str:
return normalized.strip() return normalized.strip()
def stringify_ast(node: Union[ast.AST, ast3.AST], depth: int = 0) -> Iterator[str]: def stringify_ast(node: ast.AST, depth: int = 0) -> Iterator[str]:
"""Simple visitor generating strings to compare ASTs by content.""" """Simple visitor generating strings to compare ASTs by content."""
node = fixup_ast_constants(node) if (
isinstance(node, ast.Constant)
and isinstance(node.value, str)
and node.kind == "u"
):
# It's a quirk of history that we strip the u prefix over here. We used to
# rewrite the AST nodes for Python version compatibility and we never copied
# over the kind
node.kind = None
yield f"{' ' * depth}{node.__class__.__name__}(" yield f"{' ' * depth}{node.__class__.__name__}("
type_ignore_classes: Tuple[Type[Any], ...]
for field in sorted(node._fields): # noqa: F402 for field in sorted(node._fields): # noqa: F402
# TypeIgnore will not be present using pypy < 3.8, so need for this
if not (_IS_PYPY and sys.version_info < (3, 8)):
# TypeIgnore has only one field 'lineno' which breaks this comparison # TypeIgnore has only one field 'lineno' which breaks this comparison
type_ignore_classes = (ast3.TypeIgnore,) if isinstance(node, ast.TypeIgnore):
if sys.version_info >= (3, 8):
type_ignore_classes += (ast.TypeIgnore,)
if isinstance(node, type_ignore_classes):
break break
try: try:
@ -237,22 +194,16 @@ def stringify_ast(node: Union[ast.AST, ast3.AST], depth: int = 0) -> Iterator[st
# parentheses and they change the AST. # parentheses and they change the AST.
if ( if (
field == "targets" field == "targets"
and isinstance(node, (ast.Delete, ast3.Delete)) and isinstance(node, ast.Delete)
and isinstance(item, (ast.Tuple, ast3.Tuple)) and isinstance(item, ast.Tuple)
): ):
for elt in item.elts: for elt in item.elts:
yield from stringify_ast(elt, depth + 2) yield from stringify_ast(elt, depth + 2)
elif isinstance(item, (ast.AST, ast3.AST)): elif isinstance(item, ast.AST):
yield from stringify_ast(item, depth + 2) yield from stringify_ast(item, depth + 2)
# Note that we are referencing the typed-ast ASTs via global variables and not elif isinstance(value, ast.AST):
# direct module attribute accesses because that breaks mypyc. It's probably
# something to do with the ast3 variables being marked as Any leading
# mypy to think this branch is always taken, leaving the rest of the code
# unanalyzed. Tighting up the types for the typed-ast AST types avoids the
# mypyc crash.
elif isinstance(value, (ast.AST, ast3_AST)):
yield from stringify_ast(value, depth + 2) yield from stringify_ast(value, depth + 2)
else: else:
@ -271,17 +222,3 @@ def stringify_ast(node: Union[ast.AST, ast3.AST], depth: int = 0) -> Iterator[st
yield f"{' ' * (depth+2)}{normalized!r}, # {value.__class__.__name__}" yield f"{' ' * (depth+2)}{normalized!r}, # {value.__class__.__name__}"
yield f"{' ' * depth}) # /{node.__class__.__name__}" yield f"{' ' * depth}) # /{node.__class__.__name__}"
def fixup_ast_constants(node: Union[ast.AST, ast3.AST]) -> Union[ast.AST, ast3.AST]:
"""Map ast nodes deprecated in 3.8 to Constant."""
if isinstance(node, (ast.Str, ast3.Str, ast.Bytes, ast3.Bytes)):
return ast.Constant(value=node.s)
if isinstance(node, (ast.Num, ast3.Num)):
return ast.Constant(value=node.n)
if isinstance(node, (ast.NameConstant, ast3.NameConstant)):
return ast.Constant(value=node.value)
return node

View File

@ -5,16 +5,10 @@
import re import re
import sys import sys
from functools import lru_cache from functools import lru_cache
from typing import List, Match, Pattern from typing import Final, List, Match, Pattern
from blib2to3.pytree import Leaf
if sys.version_info < (3, 8):
from typing_extensions import Final
else:
from typing import Final
from black._width_table import WIDTH_TABLE from black._width_table import WIDTH_TABLE
from blib2to3.pytree import Leaf
STRING_PREFIX_CHARS: Final = "furbFURB" # All possible string prefix characters. STRING_PREFIX_CHARS: Final = "furbFURB" # All possible string prefix characters.
STRING_PREFIX_RE: Final = re.compile( STRING_PREFIX_RE: Final = re.compile(

View File

@ -2,7 +2,6 @@
String transformers that can split and merge strings. String transformers that can split and merge strings.
""" """
import re import re
import sys
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
@ -12,9 +11,11 @@
ClassVar, ClassVar,
Collection, Collection,
Dict, Dict,
Final,
Iterable, Iterable,
Iterator, Iterator,
List, List,
Literal,
Optional, Optional,
Sequence, Sequence,
Set, Set,
@ -23,11 +24,6 @@
Union, Union,
) )
if sys.version_info < (3, 8):
from typing_extensions import Final, Literal
else:
from typing import Literal, Final
from mypy_extensions import trait from mypy_extensions import trait
from black.comments import contains_pragma_comment from black.comments import contains_pragma_comment

View File

@ -3,10 +3,7 @@
import sys import sys
from typing import Dict from typing import Dict
if sys.version_info < (3, 8): from typing import Final
from typing_extensions import Final
else:
from typing import Final
# Taken from Python (r53757) and modified to include some tokens # Taken from Python (r53757) and modified to include some tokens
# originally monkeypatched in by pgen2.tokenize # originally monkeypatched in by pgen2.tokenize

View File

@ -42,10 +42,7 @@
cast, cast,
) )
if sys.version_info >= (3, 8): from typing import Final
from typing import Final
else:
from typing_extensions import Final
from blib2to3.pgen2.token import * from blib2to3.pgen2.token import *
from blib2to3.pgen2.grammar import Grammar from blib2to3.pgen2.grammar import Grammar