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

Deprecate Python 2 formatting support #2523

Merged
merged 2 commits into from Oct 31, 2021
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
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -10,6 +10,7 @@
- Bumped typed-ast version minimum to 1.4.3 for 3.10 compatiblity (#2519)
- Fixed a Python 3.10 compatibility issue where the loop argument was still being passed
even though it has been removed (#2580)
- Deprecate Python 2 formatting support (#2523)

### _Blackd_

Expand Down
10 changes: 7 additions & 3 deletions docs/faq.md
Expand Up @@ -74,13 +74,17 @@ disabled-by-default counterpart W504. E203 should be disabled while changes are

## Does Black support Python 2?

```{warning}
Python 2 support has been deprecated since 21.10b0.

This support will be dropped in the first stable release, expected for January 2022.
See [The Black Code Style](the_black_code_style/index.rst) for details.
```

For formatting, yes! [Install](getting_started.md#installation) with the `python2` extra
to format Python 2 files too! In terms of running _Black_ though, Python 3.6 or newer is
required.

Note that this support will be dropped in the first stable release, expected for
January 2022. See [The Black Code Style](the_black_code_style/index.rst) for details.

## Why does my linter or typechecker complain after I format my code?

Some linters and other tools use magical comments (e.g., `# noqa`, `# type: ignore`) to
Expand Down
17 changes: 16 additions & 1 deletion src/black/__init__.py
Expand Up @@ -1061,6 +1061,15 @@ def f(
versions = mode.target_versions
else:
versions = detect_target_versions(src_node)

# TODO: fully drop support and this code hopefully in January 2022 :D
if TargetVersion.PY27 in mode.target_versions or versions == {TargetVersion.PY27}:
msg = (
"DEPRECATION: Python 2 support will be removed in the first stable release"
"expected in January 2022."
)
err(msg, fg="yellow", bold=True)

normalize_fmt_off(src_node)
lines = LineGenerator(
mode=mode,
Expand Down Expand Up @@ -1103,7 +1112,7 @@ def decode_bytes(src: bytes) -> Tuple[FileContent, Encoding, NewLine]:
return tiow.read(), encoding, newline


def get_features_used(node: Node) -> Set[Feature]:
def get_features_used(node: Node) -> Set[Feature]: # noqa: C901
"""Return a set of (relatively) new Python features used in this file.

Currently looking for:
Expand All @@ -1113,6 +1122,7 @@ def get_features_used(node: Node) -> Set[Feature]:
- positional only arguments in function signatures and lambdas;
- assignment expression;
- relaxed decorator syntax;
- print / exec statements;
"""
features: Set[Feature] = set()
for n in node.pre_order():
Expand Down Expand Up @@ -1161,6 +1171,11 @@ def get_features_used(node: Node) -> Set[Feature]:
if argch.type in STARS:
features.add(feature)

elif n.type == token.PRINT_STMT:
features.add(Feature.PRINT_STMT)
elif n.type == token.EXEC_STMT:
features.add(Feature.EXEC_STMT)

return features


Expand Down
10 changes: 9 additions & 1 deletion src/black/mode.py
Expand Up @@ -41,9 +41,17 @@ class Feature(Enum):
RELAXED_DECORATORS = 10
FORCE_OPTIONAL_PARENTHESES = 50

# temporary for Python 2 deprecation
PRINT_STMT = 200
EXEC_STMT = 201


VERSION_TO_FEATURES: Dict[TargetVersion, Set[Feature]] = {
TargetVersion.PY27: {Feature.ASYNC_IDENTIFIERS},
TargetVersion.PY27: {
Feature.ASYNC_IDENTIFIERS,
Feature.PRINT_STMT,
Feature.EXEC_STMT,
},
TargetVersion.PY33: {Feature.UNICODE_LITERALS, Feature.ASYNC_IDENTIFIERS},
TargetVersion.PY34: {Feature.UNICODE_LITERALS, Feature.ASYNC_IDENTIFIERS},
TargetVersion.PY35: {
Expand Down
3 changes: 3 additions & 0 deletions src/blib2to3/pgen2/token.py
Expand Up @@ -74,6 +74,9 @@
COLONEQUAL: Final = 59
N_TOKENS: Final = 60
NT_OFFSET: Final = 256
# temporary for Python 2 deprecation
PRINT_STMT: Final = 316
EXEC_STMT: Final = 288
# --end constants--

tok_name: Final[Dict[int, str]] = {}
Expand Down
19 changes: 17 additions & 2 deletions tests/test_black.py
Expand Up @@ -1401,14 +1401,14 @@ def test_docstring_reformat_for_py27(self) -> None:
)
expected = 'def foo():\n """Testing\n Testing"""\n print "Foo"\n'

result = CliRunner().invoke(
result = BlackRunner().invoke(
black.main,
["-", "-q", "--target-version=py27"],
input=BytesIO(source),
)

self.assertEqual(result.exit_code, 0)
actual = result.output
actual = result.stdout
self.assertFormatEqual(actual, expected)

@staticmethod
Expand Down Expand Up @@ -2017,6 +2017,21 @@ def test_get_sources_with_stdin_filename_and_force_exclude(self) -> None:
)


@pytest.mark.parametrize("explicit", [True, False], ids=["explicit", "autodetection"])
def test_python_2_deprecation_with_target_version(explicit: bool) -> None:
args = [
"--config",
str(THIS_DIR / "empty.toml"),
str(DATA_DIR / "python2.py"),
"--check",
]
if explicit:
args.append("--target-version=py27")
with cache_dir():
result = BlackRunner().invoke(black.main, args)
assert "DEPRECATION: Python 2 support will be removed" in result.stderr


with open(black.__file__, "r", encoding="utf-8") as _bf:
black_source_lines = _bf.readlines()

Expand Down