47
47
from blib2to3 .pgen2 .parse import ParseError
48
48
49
49
50
- __version__ = "18.9b0"
50
+ __version__ = "18.9b0.post1 "
51
51
DEFAULT_LINE_LENGTH = 88
52
52
DEFAULT_EXCLUDES = (
53
53
r"/(\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist)/"
@@ -116,6 +116,7 @@ class FileMode(Flag):
116
116
PYI = 2
117
117
NO_STRING_NORMALIZATION = 4
118
118
NO_NUMERIC_UNDERSCORE_NORMALIZATION = 8
119
+ SINGLE_QUOTE = 16
119
120
120
121
@classmethod
121
122
def from_configuration (
@@ -125,6 +126,7 @@ def from_configuration(
125
126
pyi : bool ,
126
127
skip_string_normalization : bool ,
127
128
skip_numeric_underscore_normalization : bool ,
129
+ single_quote : bool ,
128
130
) -> "FileMode" :
129
131
mode = cls .AUTO_DETECT
130
132
if py36 :
@@ -135,6 +137,8 @@ def from_configuration(
135
137
mode |= cls .NO_STRING_NORMALIZATION
136
138
if skip_numeric_underscore_normalization :
137
139
mode |= cls .NO_NUMERIC_UNDERSCORE_NORMALIZATION
140
+ if single_quote :
141
+ mode |= cls .SINGLE_QUOTE
138
142
return mode
139
143
140
144
@@ -210,6 +214,11 @@ def read_pyproject_toml(
210
214
is_flag = True ,
211
215
help = "Don't normalize underscores in numeric literals." ,
212
216
)
217
+ @click .option (
218
+ "--single-quote" ,
219
+ is_flag = True ,
220
+ help = "Use single quotes instead of double quotes in strings." ,
221
+ )
213
222
@click .option (
214
223
"--check" ,
215
224
is_flag = True ,
@@ -301,6 +310,7 @@ def main(
301
310
py36 : bool ,
302
311
skip_string_normalization : bool ,
303
312
skip_numeric_underscore_normalization : bool ,
313
+ single_quote : bool ,
304
314
quiet : bool ,
305
315
verbose : bool ,
306
316
include : str ,
@@ -315,6 +325,7 @@ def main(
315
325
pyi = pyi ,
316
326
skip_string_normalization = skip_string_normalization ,
317
327
skip_numeric_underscore_normalization = skip_numeric_underscore_normalization ,
328
+ single_quote = single_quote ,
318
329
)
319
330
if config and verbose :
320
331
out (f"Using configuration from { config } ." , bold = False , fg = "blue" )
@@ -631,13 +642,15 @@ def format_str(
631
642
is_pyi = bool (mode & FileMode .PYI )
632
643
py36 = bool (mode & FileMode .PYTHON36 ) or is_python36 (src_node )
633
644
normalize_strings = not bool (mode & FileMode .NO_STRING_NORMALIZATION )
645
+ single_quote = bool (mode & FileMode .SINGLE_QUOTE )
634
646
normalize_fmt_off (src_node )
635
647
lines = LineGenerator (
636
648
remove_u_prefix = py36 or "unicode_literals" in future_imports ,
637
649
is_pyi = is_pyi ,
638
650
normalize_strings = normalize_strings ,
639
651
allow_underscores = py36
640
652
and not bool (mode & FileMode .NO_NUMERIC_UNDERSCORE_NORMALIZATION ),
653
+ single_quote = single_quote ,
641
654
)
642
655
elt = EmptyLineTracker (is_pyi = is_pyi )
643
656
empty_line = Line ()
@@ -1431,6 +1444,7 @@ class LineGenerator(Visitor[Line]):
1431
1444
1432
1445
is_pyi : bool = False
1433
1446
normalize_strings : bool = True
1447
+ single_quote : bool = False
1434
1448
current_line : Line = Factory (Line )
1435
1449
remove_u_prefix : bool = False
1436
1450
allow_underscores : bool = False
@@ -1474,7 +1488,7 @@ def visit_default(self, node: LN) -> Iterator[Line]:
1474
1488
normalize_prefix (node , inside_brackets = any_open_brackets )
1475
1489
if self .normalize_strings and node .type == token .STRING :
1476
1490
normalize_string_prefix (node , remove_u_prefix = self .remove_u_prefix )
1477
- normalize_string_quotes (node )
1491
+ normalize_string_quotes (node , single_quote = self . single_quote )
1478
1492
if node .type == token .NUMBER :
1479
1493
normalize_numeric_literal (node , self .allow_underscores )
1480
1494
if node .type not in WHITESPACE :
@@ -2500,7 +2514,7 @@ def normalize_string_prefix(leaf: Leaf, remove_u_prefix: bool = False) -> None:
2500
2514
leaf .value = f"{ new_prefix } { match .group (2 )} "
2501
2515
2502
2516
2503
- def normalize_string_quotes (leaf : Leaf ) -> None :
2517
+ def normalize_string_quotes (leaf : Leaf , single_quote : bool = False ) -> None :
2504
2518
"""Prefer double quotes but only if it doesn't cause more escaping.
2505
2519
2506
2520
Adds or removes backslashes as appropriate. Doesn't parse and fix
@@ -2509,18 +2523,28 @@ def normalize_string_quotes(leaf: Leaf) -> None:
2509
2523
Note: Mutates its argument.
2510
2524
"""
2511
2525
value = leaf .value .lstrip ("furbFURB" )
2512
- if value [:3 ] == '"""' :
2526
+
2527
+ quote_char = '"'
2528
+ alt_quote_char = "'"
2529
+ triple_quote_chars = '"""'
2530
+ alt_triple_quote_chars = "'''"
2531
+
2532
+ if single_quote :
2533
+ quote_char = "'"
2534
+ alt_quote_char = '"'
2535
+
2536
+ if value [:3 ] == triple_quote_chars :
2513
2537
return
2514
2538
2515
- elif value [:3 ] == "'''" :
2516
- orig_quote = "'''"
2517
- new_quote = '"""'
2518
- elif value [0 ] == '"' :
2519
- orig_quote = '"'
2520
- new_quote = "'"
2539
+ elif value [:3 ] == alt_triple_quote_chars :
2540
+ orig_quote = alt_triple_quote_chars
2541
+ new_quote = triple_quote_chars
2542
+ elif value [0 ] == quote_char :
2543
+ orig_quote = quote_char
2544
+ new_quote = alt_quote_char
2521
2545
else :
2522
- orig_quote = "'"
2523
- new_quote = '"'
2546
+ orig_quote = alt_quote_char
2547
+ new_quote = quote_char
2524
2548
first_quote_pos = leaf .value .find (orig_quote )
2525
2549
if first_quote_pos == - 1 :
2526
2550
return # There's an internal error
@@ -2553,16 +2577,16 @@ def normalize_string_quotes(leaf: Leaf) -> None:
2553
2577
if "\\ " in str (m ):
2554
2578
# Do not introduce backslashes in interpolated expressions
2555
2579
return
2556
- if new_quote == '"""' and new_body [- 1 : ] == '"' :
2580
+ if new_quote == triple_quote_chars and new_body [- 1 ] == triple_quote_chars [ 0 ] :
2557
2581
# edge case:
2558
- new_body = new_body [:- 1 ] + ' \\ "'
2582
+ new_body = new_body [:- 1 ] + " \\ " + triple_quote_chars [ 0 ]
2559
2583
orig_escape_count = body .count ("\\ " )
2560
2584
new_escape_count = new_body .count ("\\ " )
2561
2585
if new_escape_count > orig_escape_count :
2562
2586
return # Do not introduce more escaping
2563
2587
2564
- if new_escape_count == orig_escape_count and orig_quote == '"' :
2565
- return # Prefer double quotes
2588
+ if new_escape_count == orig_escape_count and orig_quote == quote_char :
2589
+ return
2566
2590
2567
2591
leaf .value = f"{ prefix } { new_quote } { new_body } { new_quote } "
2568
2592
0 commit comments