|
49 | 49 | from pyink.mode import Mode as Mode # re-exported
|
50 | 50 | -from pyink.mode import Preview, TargetVersion, supports_feature
|
51 | 51 | +from pyink.mode import Preview, QuoteStyle, TargetVersion, supports_feature
|
52 |
| - from pyink.nodes import ( |
53 |
| - STARS, |
54 |
| - is_number_token, |
55 |
| -@@ -90,12 +91,11 @@ from pyink.ranges import ( |
| 52 | + from pyink.nodes import STARS, is_number_token, is_simple_decorator_expression, syms |
| 53 | + from pyink.output import color_diff, diff, dump_to_file, err, ipynb_diff, out |
| 54 | + from pyink.parsing import ( # noqa F401 |
| 55 | +@@ -90,9 +91,8 @@ from pyink.ranges import ( |
56 | 56 | parse_line_ranges,
|
57 | 57 | sanitized_lines,
|
58 | 58 | )
|
59 | 59 | +from pyink import ink
|
60 | 60 | from pyink.report import Changed, NothingChanged, Report
|
61 |
| - from pyink.trans import iter_fexpr_spans |
62 |
| - |
63 |
| - from google3.devtools.python.pyformat import import_sorting |
64 | 61 | -from blib2to3.pgen2 import token
|
65 | 62 | -from blib2to3.pytree import Leaf, Node
|
66 | 63 |
|
|
471 | 468 | yield complete_line
|
472 | 469 |
|
473 | 470 | def visit_default(self, node: LN) -> Iterator[Line]:
|
474 |
| -@@ -156,7 +182,9 @@ class LineGenerator(Visitor[Line]): |
475 |
| - node.prefix = "" |
476 |
| - if self.mode.string_normalization and node.type == token.STRING: |
477 |
| - node.value = normalize_string_prefix(node.value) |
478 |
| -- node.value = normalize_string_quotes(node.value) |
479 |
| -+ node.value = normalize_string_quotes( |
480 |
| -+ node.value, preferred_quote=self.mode.preferred_quote |
481 |
| -+ ) |
482 |
| - if node.type == token.NUMBER: |
483 |
| - normalize_numeric_literal(node) |
484 |
| - if node.type not in WHITESPACE: |
485 | 471 | @@ -166,26 +194,27 @@ class LineGenerator(Visitor[Line]):
|
486 | 472 | def visit_test(self, node: Node) -> Iterator[Line]:
|
487 | 473 | """Visit an `x if y else z` test"""
|
|
562 | 548 | + yield from self.line(_DEDENT)
|
563 | 549 |
|
564 | 550 | else:
|
565 |
| -- if not node.parent or not is_stub_suite(node.parent): |
| 551 | +- if node.parent and is_stub_suite(node.parent): |
566 | 552 | + if (
|
567 |
| -+ not (self.mode.is_pyi or not self.mode.is_pyink) |
568 |
| -+ or not node.parent |
569 |
| -+ or not is_stub_suite(node.parent, self.mode) |
| 553 | ++ (self.mode.is_pyi or not self.mode.is_pyink) |
| 554 | ++ and node.parent |
| 555 | ++ and is_stub_suite(node.parent, self.mode) |
570 | 556 | + ):
|
571 |
| - yield from self.line() |
572 |
| - yield from self.visit_default(node) |
573 |
| - |
| 557 | + node.prefix = "" |
| 558 | + yield from self.visit_default(node) |
| 559 | + return |
574 | 560 | @@ -414,7 +453,10 @@ class LineGenerator(Visitor[Line]):
|
575 | 561 | yield from self.visit_default(node)
|
576 | 562 |
|
|
584 | 570 |
|
585 | 571 | if is_docstring(leaf, self.mode) and not re.search(r"\\\s*\n", leaf.value):
|
586 | 572 | @@ -428,7 +470,9 @@ class LineGenerator(Visitor[Line]):
|
587 |
| - # formatting as visit_default() is called *after*. To avoid a |
588 |
| - # situation where this function formats a docstring differently on |
589 |
| - # the second pass, normalize it early. |
| 573 | + # see padding logic below), there's a possibility for unstable |
| 574 | + # formatting. To avoid a situation where this function formats a |
| 575 | + # docstring differently on the second pass, normalize it early. |
590 | 576 | - docstring = normalize_string_quotes(docstring)
|
591 | 577 | + docstring = normalize_string_quotes(
|
592 | 578 | + docstring, preferred_quote=self.mode.preferred_quote
|
|
628 | 614 | self.visit_del_stmt = partial(v, keywords=Ø, parens={"del"})
|
629 | 615 | self.visit_async_funcdef = self.visit_async_stmt
|
630 | 616 | self.visit_decorated = self.visit_decorators
|
| 617 | +@@ -499,7 +556,9 @@ def visit_STRING( |
| 618 | + |
| 619 | + if self.mode.string_normalization and leaf.type == token.STRING: |
| 620 | + leaf.value = normalize_string_prefix(leaf.value) |
| 621 | +- leaf.value = normalize_string_quotes(leaf.value) |
| 622 | ++ leaf.value = normalize_string_quotes( |
| 623 | ++ leaf.value, preferred_quote=self.mode.preferred_quote |
| 624 | ++ ) |
| 625 | + yield from self.visit_default(leaf) |
| 626 | + |
| 627 | + def visit_NUMBER(self, leaf: Leaf) -> Iterator[Line]: |
631 | 628 | @@ -577,10 +628,19 @@ def transform_line(
|
632 | 629 |
|
633 | 630 | ll = mode.line_length
|
|
1012 | 1009 | + return Quote.DOUBLE
|
1013 | 1010 | --- a/nodes.py
|
1014 | 1011 | +++ b/nodes.py
|
1015 |
| -@@ -532,8 +532,8 @@ def first_leaf_of(node: LN) -> Optional[ |
1016 |
| - |
1017 |
| - |
1018 |
| - def is_arith_like(node: LN) -> bool: |
1019 |
| -- """Whether node is an arithmetic or a binary arithmetic expression""" |
1020 |
| -- return node.type in { |
1021 |
| -+ """Whether node is an arithmetic or a binary arithmetic expression""" |
1022 |
| -+ return node.type in { |
1023 |
| - syms.arith_expr, |
1024 |
| - syms.shift_expr, |
1025 |
| - syms.xor_expr, |
1026 |
| -@@ -542,14 +542,14 @@ def is_arith_like(node: LN) -> bool: |
1027 |
| - |
1028 |
| - |
1029 |
| - def is_docstring(leaf: Leaf, mode: Mode) -> bool: |
1030 |
| -- if leaf.type != token.STRING: |
1031 |
| -- return False |
1032 |
| -+ if leaf.type != token.STRING: |
1033 |
| -+ return False |
1034 |
| - |
1035 |
| -- prefix = get_string_prefix(leaf.value) |
1036 |
| -- if set(prefix).intersection("bBfF"): |
1037 |
| -- return False |
1038 |
| -+ prefix = get_string_prefix(leaf.value) |
1039 |
| -+ if set(prefix).intersection("bBfF"): |
1040 |
| -+ return False |
1041 |
| - |
1042 |
| -- if ( |
1043 |
| -+ if ( |
1044 |
| - Preview.unify_docstring_detection in mode |
1045 |
| - and leaf.parent |
1046 |
| - and leaf.parent.type == syms.simple_stmt |
1047 |
| -@@ -557,20 +557,22 @@ def is_docstring(leaf: Leaf, mode: Mode) |
1048 |
| - and leaf.parent.parent |
1049 |
| - and leaf.parent.parent.type == syms.file_input |
1050 |
| - ): |
1051 |
| -- return True |
1052 |
| -+ return True |
1053 |
| - |
1054 |
| -- if prev_siblings_are( |
1055 |
| -+ if prev_siblings_are( |
1056 |
| - leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt] |
1057 |
| - ): |
1058 |
| -- return True |
1059 |
| -+ return True |
1060 |
| - |
1061 |
| -- # Multiline docstring on the same line as the `def`. |
1062 |
| -- if prev_siblings_are(leaf.parent, [syms.parameters, token.COLON, syms.simple_stmt]): |
1063 |
| -- # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python |
1064 |
| -- # grammar. We're safe to return True without further checks. |
1065 |
| -- return True |
1066 |
| -+ # Multiline docstring on the same line as the `def`. |
1067 |
| -+ if prev_siblings_are( |
1068 |
| -+ leaf.parent, [syms.parameters, token.COLON, syms.simple_stmt] |
1069 |
| -+ ): |
1070 |
| -+ # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python |
1071 |
| -+ # grammar. We're safe to return True without further checks. |
1072 |
| -+ return True |
1073 |
| - |
1074 |
| -- return False |
1075 |
| -+ return False |
1076 |
| - |
1077 |
| - |
1078 |
| - def is_empty_tuple(node: LN) -> bool: |
1079 | 1012 | @@ -763,9 +765,13 @@ def is_function_or_class(node: Node) ->
|
1080 | 1013 | return node.type in {syms.funcdef, syms.classdef, syms.async_funcdef}
|
1081 | 1014 |
|
|
1094 | 1027 | # If there is a comment, we want to keep it.
|
1095 | 1028 | --- a/pyproject.toml
|
1096 | 1029 | +++ b/pyproject.toml
|
1097 |
| -@@ -1,51 +1,23 @@ |
| 1030 | +@@ -1,52 +1,23 @@ |
1098 | 1031 | -# Example configuration for Black.
|
1099 | 1032 | -
|
1100 | 1033 | -# NOTE: you have to use single-quoted strings in TOML for regular expressions.
|
|
1112 | 1045 | -extend-exclude = '''
|
1113 | 1046 | -/(
|
1114 | 1047 | - # The following are specific to Black, you probably don't want those.
|
1115 |
| -- tests/data |
1116 |
| -- | profiling |
1117 |
| --)/ |
| 1048 | +- tests/data/ |
| 1049 | +- | profiling/ |
| 1050 | +- | scripts/generate_schema.py # Uses match syntax |
| 1051 | +-) |
1118 | 1052 | -'''
|
1119 | 1053 | -# We use the unstable style for formatting Black itself. If you
|
1120 | 1054 | -# want bug-free formatting, you should keep this off. If you want
|
|
1155 | 1089 | classifiers = [
|
1156 | 1090 | "Development Status :: 5 - Production/Stable",
|
1157 | 1091 | "Environment :: Console",
|
1158 |
| -@@ -70,51 +42,34 @@ dependencies = [ |
| 1092 | +@@ -70,53 +42,35 @@ dependencies = [ |
1159 | 1093 | "platformdirs>=2",
|
1160 | 1094 | "tomli>=1.1.0; python_version < '3.11'",
|
1161 | 1095 | "typing_extensions>=4.0.1; python_version < '3.11'",
|
1162 |
| -+ "black==24.3.0", |
| 1096 | ++ "black==24.8.0", |
1163 | 1097 | ]
|
1164 | 1098 | -dynamic = ["readme", "version"]
|
1165 | 1099 | +dynamic = ["version"]
|
|
1185 | 1119 | +pyink = "pyink:patched_main"
|
1186 | 1120 |
|
1187 | 1121 | [project.urls]
|
| 1122 | +-Documentation = "https://black.readthedocs.io/" |
1188 | 1123 | -Changelog = "https://github.com/psf/black/blob/main/CHANGES.md"
|
1189 |
| --Homepage = "https://github.com/psf/black" |
| 1124 | +-Repository = "https://github.com/psf/black" |
| 1125 | +-Issues = "https://github.com/psf/black/issues" |
1190 | 1126 | -
|
1191 | 1127 | -[tool.hatch.metadata.hooks.fancy-pypi-readme]
|
1192 | 1128 | -content-type = "text/markdown"
|
|
1195 | 1131 | - { path = "CHANGES.md" },
|
1196 | 1132 | -]
|
1197 | 1133 | +Changelog = "https://github.com/google/pyink/blob/pyink/CHANGES.md"
|
1198 |
| -+Homepage = "https://github.com/google/pyink" |
| 1134 | ++Repository = "https://github.com/google/pyink" |
| 1135 | ++Issues = "https://github.com/google/pyink/issues" |
1199 | 1136 |
|
1200 | 1137 | [tool.hatch.version]
|
1201 | 1138 | source = "vcs"
|
|
1261 | 1198 | --- a/strings.py
|
1262 | 1199 | +++ b/strings.py
|
1263 | 1200 | @@ -8,6 +8,7 @@ from functools import lru_cache
|
1264 |
| - from typing import Final, List, Match, Pattern |
| 1201 | + from typing import Final, List, Match, Pattern, Tuple |
1265 | 1202 |
|
1266 | 1203 | from pyink._width_table import WIDTH_TABLE
|
1267 | 1204 | +from pyink.mode import Quote
|
|
1279 | 1216 | +
|
1280 | 1217 | + For three quotes strings, always use double-quote.
|
1281 | 1218 |
|
1282 |
| - Adds or removes backslashes as appropriate. Doesn't parse and fix |
1283 |
| - strings nested in f-strings. |
| 1219 | + Adds or removes backslashes as appropriate. |
| 1220 | + """ |
1284 | 1221 | @@ -234,8 +237,8 @@ def normalize_string_quotes(s: str) -> s
|
1285 | 1222 | if new_escape_count > orig_escape_count:
|
1286 | 1223 | return s # Do not introduce more escaping
|
|
1302 | 1239 | +pyink = false
|
1303 | 1240 | --- a/tests/test_black.py
|
1304 | 1241 | +++ b/tests/test_black.py
|
1305 |
| -@@ -1666,9 +1666,9 @@ class BlackTestCase(BlackBaseTestCase): |
1306 |
| - src_dir.mkdir() |
1307 |
| - |
1308 |
| - root_pyproject = root / "pyproject.toml" |
1309 |
| -- root_pyproject.write_text("[tool.black]", encoding="utf-8") |
1310 |
| -+ root_pyproject.write_text("[tool.pyink]", encoding="utf-8") |
1311 |
| - src_pyproject = src_dir / "pyproject.toml" |
1312 |
| -- src_pyproject.write_text("[tool.black]", encoding="utf-8") |
1313 |
| -+ src_pyproject.write_text("[tool.pyink]", encoding="utf-8") |
1314 |
| - src_python = src_dir / "foo.py" |
1315 |
| - src_python.touch() |
1316 |
| - |
1317 |
| -@@ -1699,7 +1699,7 @@ class BlackTestCase(BlackBaseTestCase): |
1318 |
| - |
1319 |
| - src_sub_python = src_sub / "bar.py" |
1320 |
| - |
1321 |
| -- # we skip src_sub_pyproject since it is missing the [tool.black] section |
1322 |
| -+ # we skip src_sub_pyproject since it is missing the [tool.pyink] section |
1323 |
| - self.assertEqual( |
1324 |
| - pyink.find_project_root((src_sub_python,)), |
1325 |
| - (src_dir.resolve(), "pyproject.toml"), |
1326 | 1242 | @@ -2772,6 +2772,82 @@ class TestFileCollection:
|
1327 | 1243 | stdin_filename=stdin_filename,
|
1328 | 1244 | )
|
|
1432 | 1348 | skip_install = True
|
1433 | 1349 | commands =
|
1434 | 1350 | pip install -e .
|
1435 |
| -- black --check {toxinidir}/src {toxinidir}/tests |
| 1351 | +- black --check {toxinidir}/src {toxinidir}/tests {toxinidir}/docs {toxinidir}/scripts |
1436 | 1352 | -
|
1437 | 1353 | -[testenv:generate_schema]
|
1438 | 1354 | -setenv = PYTHONWARNDEFAULTENCODING =
|
|
1441 | 1357 | -commands =
|
1442 | 1358 | - pip install -e .
|
1443 | 1359 | - python {toxinidir}/scripts/generate_schema.py --outfile {toxinidir}/src/black/resources/black.schema.json
|
1444 |
| -+ pyink --check {toxinidir}/src {toxinidir}/tests |
| 1360 | ++ pyink --check {toxinidir}/src {toxinidir}/tests {toxinidir}/docs {toxinidir}/scripts |
1445 | 1361 | --- a/trans.py
|
1446 | 1362 | +++ b/trans.py
|
1447 | 1363 | @@ -28,8 +28,8 @@ from typing import (
|
|
0 commit comments