Skip to content

Commit

Permalink
Drop support for parsing Python 2 (#3933)
Browse files Browse the repository at this point in the history
  • Loading branch information
hauntsaninja committed Oct 9, 2023
1 parent a8f68f5 commit 715f60c
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 59 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit 715f60c

Please sign in to comment.