strategy:
fail-fast: false
matrix:
- python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
+ python-version: ["3.8", "3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v3
strategy:
fail-fast: false
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]
steps:
<!-- 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
<!-- Changes that affect Black's stable style -->
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
1. Install [`ale`](https://github.com/dense-analysis/ale)
# 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.7
+python_version=3.8
mypy_path=src
name = "black"
description = "The uncompromising code formatter."
license = { text = "MIT" }
-requires-python = ">=3.7"
+requires-python = ">=3.8"
authors = [
{ name = "Łukasz Langa", email = "lukasz@langa.pl" },
]
"pathspec>=0.9.0",
"platformdirs>=2",
"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'",
]
dynamic = ["readme", "version"]
dependencies = [
"hatch-mypyc>=0.16.0",
"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
exclude = [
[tool.cibuildwheel]
build-verbosity = 1
# 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
# - OS: Linux (no musl), Windows, and macOS
build = "cp3*-*"
# 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.
'''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:
# https://github.com/aio-libs/aiohttp/issues/6905
'''ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning''',
f.write(f"""# Generated by {basename(__file__)}
# wcwidth {wcwidth.__version__}
# Unicode {wcwidth.list_versions()[-1]}
-import sys
-from typing import List, Tuple
-
-if sys.version_info < (3, 8):
- from typing_extensions import Final
-else:
- from typing import Final
+from typing import Final, List, Tuple
WIDTH_TABLE: Final[List[Tuple[int, int, int]]] = [
""")
# Generated by make_width_table.py
# wcwidth 0.2.6
# Unicode 15.0.0
-import sys
-from typing import List, Tuple
-
-if sys.version_info < (3, 8):
- from typing_extensions import Final
-else:
- from typing import Final
+from typing import Final, List, Tuple
WIDTH_TABLE: Final[List[Tuple[int, int, int]]] = [
(0, 0, 0),
"""Builds on top of nodes.py to track brackets."""
-import sys
from dataclasses import dataclass, field
-from typing import Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
-
-if sys.version_info < (3, 8):
- from typing_extensions import Final
-else:
- from typing import Final
+from typing import Dict, Final, Iterable, List, Optional, Sequence, Set, Tuple, Union
from black.nodes import (
BRACKET,
import re
-import sys
from dataclasses import dataclass
from functools import lru_cache
-from typing import Iterator, List, Optional, Union
-
-if sys.version_info >= (3, 8):
- from typing import Final
-else:
- from typing_extensions import Final
+from typing import Final, Iterator, List, Optional, Union
from black.nodes import (
CLOSING_BRACKETS,
chosen by the user.
"""
-import sys
from dataclasses import dataclass, field
from enum import Enum, auto
from hashlib import sha256
from operator import attrgetter
-from typing import Dict, Set
+from typing import Dict, Final, Set
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
"""
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):
from typing import TypeGuard
else:
Parse Python code and perform AST validation.
"""
import ast
-import platform
import sys
-from typing import Any, Iterable, Iterator, List, Set, Tuple, Type, Union
-
-if sys.version_info < (3, 8):
- from typing_extensions import Final
-else:
- from typing import Final
+from typing import Final, Iterable, Iterator, List, Set, Tuple
from black.mode import VERSION_TO_FEATURES, Feature, TargetVersion, supports_feature
from black.nodes import syms
from blib2to3.pgen2.tokenize import TokenError
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."
def parse_single_version(
src: str, version: Tuple[int, int], *, type_comments: bool
-) -> Union[ast.AST, ast3.AST]:
+) -> ast.AST:
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(
- 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)
+ return ast.parse(
+ src, filename, feature_version=version, type_comments=type_comments
+ )
-def parse_ast(src: str) -> Union[ast.AST, ast3.AST]:
+def parse_ast(src: str) -> ast.AST:
# TODO: support Python 4+ ;)
versions = [(3, minor) for minor in range(3, sys.version_info[1] + 1)]
raise SyntaxError(first_error)
-ast3_AST: Final[Type[ast3.AST]] = ast3.AST
-
-
def _normalize(lineend: str, value: str) -> str:
# To normalize, we strip any leading and trailing space from
# each line...
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."""
- 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__}("
- type_ignore_classes: Tuple[Type[Any], ...]
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
- type_ignore_classes = (ast3.TypeIgnore,)
- if sys.version_info >= (3, 8):
- type_ignore_classes += (ast.TypeIgnore,)
- if isinstance(node, type_ignore_classes):
- break
+ # TypeIgnore has only one field 'lineno' which breaks this comparison
+ if isinstance(node, ast.TypeIgnore):
+ break
try:
value: object = getattr(node, field)
# parentheses and they change the AST.
if (
field == "targets"
- and isinstance(node, (ast.Delete, ast3.Delete))
- and isinstance(item, (ast.Tuple, ast3.Tuple))
+ and isinstance(node, ast.Delete)
+ and isinstance(item, ast.Tuple)
):
for elt in item.elts:
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)
- # Note that we are referencing the typed-ast ASTs via global variables and not
- # 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)):
+ elif isinstance(value, ast.AST):
yield from stringify_ast(value, depth + 2)
else:
yield f"{' ' * (depth+2)}{normalized!r}, # {value.__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
import re
import sys
from functools import lru_cache
-from typing import 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 typing import Final, List, Match, Pattern
from black._width_table import WIDTH_TABLE
+from blib2to3.pytree import Leaf
STRING_PREFIX_CHARS: Final = "furbFURB" # All possible string prefix characters.
STRING_PREFIX_RE: Final = re.compile(
String transformers that can split and merge strings.
"""
import re
-import sys
from abc import ABC, abstractmethod
from collections import defaultdict
from dataclasses import dataclass
ClassVar,
Collection,
Dict,
+ Final,
Iterable,
Iterator,
List,
+ Literal,
Optional,
Sequence,
Set,
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 black.comments import contains_pragma_comment
import sys
from typing import Dict
-if sys.version_info < (3, 8):
- from typing_extensions import Final
-else:
- from typing import Final
+from typing import Final
# Taken from Python (r53757) and modified to include some tokens
# originally monkeypatched in by pgen2.tokenize
cast,
)
-if sys.version_info >= (3, 8):
- from typing import Final
-else:
- from typing_extensions import Final
+from typing import Final
from blib2to3.pgen2.token import *
from blib2to3.pgen2.grammar import Grammar