Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop support for parsing Python 2 #3933

Merged
merged 3 commits into from Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -40,6 +40,9 @@

<!-- Changes to Black's terminal output and error messages -->

- Black no longer attempts to provide special errors for attempting to format Python 2
code (#3933)

### _Blackd_

<!-- Changes to blackd -->
Expand Down
22 changes: 5 additions & 17 deletions src/black/parsing.py
Expand Up @@ -3,7 +3,7 @@
"""
import ast
import sys
from typing import Final, Iterable, Iterator, List, Set, Tuple
from typing import Iterable, Iterator, List, Set, Tuple

from black.mode import VERSION_TO_FEATURES, Feature, TargetVersion, supports_feature
from black.nodes import syms
Expand All @@ -14,8 +14,6 @@
from blib2to3.pgen2.tokenize import TokenError
from blib2to3.pytree import Leaf, Node

PY2_HINT: Final = "Python 2 support was removed in version 22.0."


class InvalidInput(ValueError):
"""Raised when input source code fails all parse attempts."""
Expand All @@ -26,9 +24,9 @@ def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]:
# No target_version specified, so try all grammars.
return [
# Python 3.7-3.9
pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords,
pygram.python_grammar_async_keywords,
# Python 3.0-3.6
pygram.python_grammar_no_print_statement_no_exec_statement,
pygram.python_grammar,
# Python 3.10+
pygram.python_grammar_soft_keywords,
]
Expand All @@ -39,12 +37,10 @@ def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]:
target_versions, Feature.ASYNC_IDENTIFIERS
) and not supports_feature(target_versions, Feature.PATTERN_MATCHING):
# Python 3.7-3.9
grammars.append(
pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords
)
grammars.append(pygram.python_grammar_async_keywords)
if not supports_feature(target_versions, Feature.ASYNC_KEYWORDS):
# Python 3.0-3.6
grammars.append(pygram.python_grammar_no_print_statement_no_exec_statement)
grammars.append(pygram.python_grammar)
if any(Feature.PATTERN_MATCHING in VERSION_TO_FEATURES[v] for v in target_versions):
# Python 3.10+
grammars.append(pygram.python_grammar_soft_keywords)
Expand Down Expand Up @@ -89,14 +85,6 @@ def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) -
# Choose the latest version when raising the actual parsing error.
assert len(errors) >= 1
exc = errors[max(errors)]

if matches_grammar(src_txt, pygram.python_grammar) or matches_grammar(
src_txt, pygram.python_grammar_no_print_statement
):
original_msg = exc.args[0]
msg = f"{original_msg}\n{PY2_HINT}"
raise InvalidInput(msg) from None

raise exc from None

if isinstance(result, Leaf):
Expand Down
7 changes: 2 additions & 5 deletions src/blib2to3/Grammar.txt
Expand Up @@ -80,17 +80,15 @@ vfplist: vfpdef (',' vfpdef)* [',']

stmt: simple_stmt | compound_stmt
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (type_stmt | expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | exec_stmt | assert_stmt)
small_stmt: (type_stmt | expr_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | assert_stmt)
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
annassign: ':' test ['=' (yield_expr|testlist_star_expr)]
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '>>=' | '**=' | '//=')
# For normal and annotated assignments, additional restrictions enforced by the interpreter
print_stmt: 'print' ( [ test (',' test)* [','] ] |
'>>' test [ (',' test)+ [','] ] )
del_stmt: 'del' exprlist
pass_stmt: 'pass'
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
Expand All @@ -109,7 +107,6 @@ import_as_names: import_as_name (',' import_as_name)* [',']
dotted_as_names: dotted_as_name (',' dotted_as_name)*
dotted_name: NAME ('.' NAME)*
global_stmt: ('global' | 'nonlocal') NAME (',' NAME)*
exec_stmt: 'exec' expr ['in' test [',' test]]
assert_stmt: 'assert' test [',' test]
type_stmt: "type" NAME [typeparams] '=' expr

Expand Down
41 changes: 10 additions & 31 deletions src/blib2to3/pygram.py
Expand Up @@ -63,7 +63,6 @@ class _python_symbols(Symbols):
encoding_decl: int
eval_input: int
except_clause: int
exec_stmt: int
expr: int
expr_stmt: int
exprlist: int
Expand Down Expand Up @@ -97,7 +96,6 @@ class _python_symbols(Symbols):
pattern: int
patterns: int
power: int
print_stmt: int
raise_stmt: int
return_stmt: int
shift_expr: int
Expand Down Expand Up @@ -153,22 +151,16 @@ class _pattern_symbols(Symbols):


python_grammar: Grammar
python_grammar_no_print_statement: Grammar
python_grammar_no_print_statement_no_exec_statement: Grammar
python_grammar_no_print_statement_no_exec_statement_async_keywords: Grammar
python_grammar_no_exec_statement: Grammar
pattern_grammar: Grammar
python_grammar_async_keywords: Grammar
python_grammar_soft_keywords: Grammar

pattern_grammar: Grammar
python_symbols: _python_symbols
pattern_symbols: _pattern_symbols


def initialize(cache_dir: Union[str, "os.PathLike[str]", None] = None) -> None:
global python_grammar
global python_grammar_no_print_statement
global python_grammar_no_print_statement_no_exec_statement
global python_grammar_no_print_statement_no_exec_statement_async_keywords
global python_grammar_async_keywords
global python_grammar_soft_keywords
global python_symbols
global pattern_grammar
Expand All @@ -180,38 +172,25 @@ def initialize(cache_dir: Union[str, "os.PathLike[str]", None] = None) -> None:
os.path.dirname(__file__), "PatternGrammar.txt"
)

# Python 2
python_grammar = driver.load_packaged_grammar("blib2to3", _GRAMMAR_FILE, cache_dir)
python_grammar.version = (2, 0)
assert "print" not in python_grammar.keywords
assert "exec" not in python_grammar.keywords

soft_keywords = python_grammar.soft_keywords.copy()
python_grammar.soft_keywords.clear()

python_symbols = _python_symbols(python_grammar)

# Python 2 + from __future__ import print_function
python_grammar_no_print_statement = python_grammar.copy()
del python_grammar_no_print_statement.keywords["print"]

# Python 3.0-3.6
python_grammar_no_print_statement_no_exec_statement = python_grammar.copy()
del python_grammar_no_print_statement_no_exec_statement.keywords["print"]
del python_grammar_no_print_statement_no_exec_statement.keywords["exec"]
python_grammar_no_print_statement_no_exec_statement.version = (3, 0)
python_grammar.version = (3, 0)

# Python 3.7+
python_grammar_no_print_statement_no_exec_statement_async_keywords = (
python_grammar_no_print_statement_no_exec_statement.copy()
)
python_grammar_no_print_statement_no_exec_statement_async_keywords.async_keywords = (
True
)
python_grammar_no_print_statement_no_exec_statement_async_keywords.version = (3, 7)
python_grammar_async_keywords = python_grammar.copy()
python_grammar_async_keywords.async_keywords = True
python_grammar_async_keywords.version = (3, 7)

# Python 3.10+
python_grammar_soft_keywords = (
python_grammar_no_print_statement_no_exec_statement_async_keywords.copy()
)
python_grammar_soft_keywords = python_grammar_async_keywords.copy()
python_grammar_soft_keywords.soft_keywords = soft_keywords
python_grammar_soft_keywords.version = (3, 10)

Expand Down
6 changes: 0 additions & 6 deletions tests/test_format.py
Expand Up @@ -155,12 +155,6 @@ def test_fast_cases(filename: str) -> None:
assert_format(source, expected, fast=True)


def test_python_2_hint() -> None:
with pytest.raises(black.parsing.InvalidInput) as exc_info:
assert_format("print 'daylily'", "print 'daylily'")
exc_info.match(black.parsing.PY2_HINT)


@pytest.mark.filterwarnings("ignore:invalid escape sequence.*:DeprecationWarning")
def test_docstring_no_string_normalization() -> None:
"""Like test_docstring but with string normalization off."""
Expand Down