]>
Commit | Line | Data |
---|---|---|
1 | # Copyright 2015 Google Inc. All Rights Reserved. | |
2 | # | |
3 | # Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | # you may not use this file except in compliance with the License. | |
5 | # You may obtain a copy of the License at | |
6 | # | |
7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
8 | # | |
9 | # Unless required by applicable law or agreed to in writing, software | |
10 | # distributed under the License is distributed on an "AS IS" BASIS, | |
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | # See the License for the specific language governing permissions and | |
13 | # limitations under the License. | |
14 | """Python formatting style settings.""" | |
15 | ||
16 | import os | |
17 | import re | |
18 | import textwrap | |
19 | from configparser import ConfigParser | |
20 | ||
21 | from yapf.yapflib import errors | |
22 | ||
23 | ||
24 | class StyleConfigError(errors.YapfError): | |
25 | """Raised when there's a problem reading the style configuration.""" | |
26 | pass | |
27 | ||
28 | ||
29 | def Get(setting_name): | |
30 | """Get a style setting.""" | |
31 | return _style[setting_name] | |
32 | ||
33 | ||
34 | def GetOrDefault(setting_name, default_value): | |
35 | """Get a style setting or default value if the setting does not exist.""" | |
36 | return _style.get(setting_name, default_value) | |
37 | ||
38 | ||
39 | def Help(): | |
40 | """Return dict mapping style names to help strings.""" | |
41 | return _STYLE_HELP | |
42 | ||
43 | ||
44 | def SetGlobalStyle(style): | |
45 | """Set a style dict.""" | |
46 | global _style | |
47 | global _GLOBAL_STYLE_FACTORY | |
48 | factory = _GetStyleFactory(style) | |
49 | if factory: | |
50 | _GLOBAL_STYLE_FACTORY = factory | |
51 | _style = style | |
52 | ||
53 | ||
54 | _STYLE_HELP = dict( | |
55 | # BASED_ON_STYLE='Which predefined style this style is based on', | |
56 | ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=textwrap.dedent("""\ | |
57 | Align closing bracket with visual indentation. | |
58 | """), | |
59 | ALLOW_MULTILINE_DICTIONARY_KEYS=textwrap.dedent("""\ | |
60 | Allow dictionary keys to exist on multiple lines. For example: | |
61 | ||
62 | x = { | |
63 | ('this is the first element of a tuple', | |
64 | 'this is the second element of a tuple'): | |
65 | value, | |
66 | } | |
67 | """), | |
68 | ALLOW_MULTILINE_LAMBDAS=textwrap.dedent("""\ | |
69 | Allow lambdas to be formatted on more than one line. | |
70 | """), | |
71 | ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS=textwrap.dedent("""\ | |
72 | Allow splitting before a default / named assignment in an argument list. | |
73 | """), | |
74 | ALLOW_SPLIT_BEFORE_DICT_VALUE=textwrap.dedent("""\ | |
75 | Allow splits before the dictionary value. | |
76 | """), | |
77 | ARITHMETIC_PRECEDENCE_INDICATION=textwrap.dedent("""\ | |
78 | Let spacing indicate operator precedence. For example: | |
79 | ||
80 | a = 1 * 2 + 3 / 4 | |
81 | b = 1 / 2 - 3 * 4 | |
82 | c = (1 + 2) * (3 - 4) | |
83 | d = (1 - 2) / (3 + 4) | |
84 | e = 1 * 2 - 3 | |
85 | f = 1 + 2 + 3 + 4 | |
86 | ||
87 | will be formatted as follows to indicate precedence: | |
88 | ||
89 | a = 1*2 + 3/4 | |
90 | b = 1/2 - 3*4 | |
91 | c = (1+2) * (3-4) | |
92 | d = (1-2) / (3+4) | |
93 | e = 1*2 - 3 | |
94 | f = 1 + 2 + 3 + 4 | |
95 | ||
96 | """), | |
97 | BLANK_LINE_BEFORE_CLASS_DOCSTRING=textwrap.dedent("""\ | |
98 | Insert a blank line before a class-level docstring. | |
99 | """), | |
100 | BLANK_LINE_BEFORE_MODULE_DOCSTRING=textwrap.dedent("""\ | |
101 | Insert a blank line before a module docstring. | |
102 | """), | |
103 | BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=textwrap.dedent("""\ | |
104 | Insert a blank line before a 'def' or 'class' immediately nested | |
105 | within another 'def' or 'class'. For example: | |
106 | ||
107 | class Foo: | |
108 | # <------ this blank line | |
109 | def method(): | |
110 | pass | |
111 | """), | |
112 | BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=textwrap.dedent("""\ | |
113 | Number of blank lines surrounding top-level function and class | |
114 | definitions. | |
115 | """), | |
116 | BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES=textwrap.dedent("""\ | |
117 | Number of blank lines between top-level imports and variable | |
118 | definitions. | |
119 | """), | |
120 | COALESCE_BRACKETS=textwrap.dedent("""\ | |
121 | Do not split consecutive brackets. Only relevant when | |
122 | dedent_closing_brackets is set. For example: | |
123 | ||
124 | call_func_that_takes_a_dict( | |
125 | { | |
126 | 'key1': 'value1', | |
127 | 'key2': 'value2', | |
128 | } | |
129 | ) | |
130 | ||
131 | would reformat to: | |
132 | ||
133 | call_func_that_takes_a_dict({ | |
134 | 'key1': 'value1', | |
135 | 'key2': 'value2', | |
136 | }) | |
137 | """), | |
138 | COLUMN_LIMIT=textwrap.dedent("""\ | |
139 | The column limit. | |
140 | """), | |
141 | CONTINUATION_ALIGN_STYLE=textwrap.dedent("""\ | |
142 | The style for continuation alignment. Possible values are: | |
143 | ||
144 | - SPACE: Use spaces for continuation alignment. This is default behavior. | |
145 | - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns | |
146 | (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs or | |
147 | CONTINUATION_INDENT_WIDTH spaces) for continuation alignment. | |
148 | - VALIGN-RIGHT: Vertically align continuation lines to multiple of | |
149 | INDENT_WIDTH columns. Slightly right (one tab or a few spaces) if | |
150 | cannot vertically align continuation lines with indent characters. | |
151 | """), | |
152 | CONTINUATION_INDENT_WIDTH=textwrap.dedent("""\ | |
153 | Indent width used for line continuations. | |
154 | """), | |
155 | DEDENT_CLOSING_BRACKETS=textwrap.dedent("""\ | |
156 | Put closing brackets on a separate line, dedented, if the bracketed | |
157 | expression can't fit in a single line. Applies to all kinds of brackets, | |
158 | including function definitions and calls. For example: | |
159 | ||
160 | config = { | |
161 | 'key1': 'value1', | |
162 | 'key2': 'value2', | |
163 | } # <--- this bracket is dedented and on a separate line | |
164 | ||
165 | time_series = self.remote_client.query_entity_counters( | |
166 | entity='dev3246.region1', | |
167 | key='dns.query_latency_tcp', | |
168 | transform=Transformation.AVERAGE(window=timedelta(seconds=60)), | |
169 | start_ts=now()-timedelta(days=3), | |
170 | end_ts=now(), | |
171 | ) # <--- this bracket is dedented and on a separate line | |
172 | """), | |
173 | DISABLE_ENDING_COMMA_HEURISTIC=textwrap.dedent("""\ | |
174 | Disable the heuristic which places each list element on a separate line | |
175 | if the list is comma-terminated. | |
176 | """), | |
177 | EACH_DICT_ENTRY_ON_SEPARATE_LINE=textwrap.dedent("""\ | |
178 | Place each dictionary entry onto its own line. | |
179 | """), | |
180 | FORCE_MULTILINE_DICT=textwrap.dedent("""\ | |
181 | Require multiline dictionary even if it would normally fit on one line. | |
182 | For example: | |
183 | ||
184 | config = { | |
185 | 'key1': 'value1' | |
186 | } | |
187 | """), | |
188 | I18N_COMMENT=textwrap.dedent("""\ | |
189 | The regex for an i18n comment. The presence of this comment stops | |
190 | reformatting of that line, because the comments are required to be | |
191 | next to the string they translate. | |
192 | """), | |
193 | I18N_FUNCTION_CALL=textwrap.dedent("""\ | |
194 | The i18n function call names. The presence of this function stops | |
195 | reformattting on that line, because the string it has cannot be moved | |
196 | away from the i18n comment. | |
197 | """), | |
198 | INDENT_CLOSING_BRACKETS=textwrap.dedent("""\ | |
199 | Put closing brackets on a separate line, indented, if the bracketed | |
200 | expression can't fit in a single line. Applies to all kinds of brackets, | |
201 | including function definitions and calls. For example: | |
202 | ||
203 | config = { | |
204 | 'key1': 'value1', | |
205 | 'key2': 'value2', | |
206 | } # <--- this bracket is indented and on a separate line | |
207 | ||
208 | time_series = self.remote_client.query_entity_counters( | |
209 | entity='dev3246.region1', | |
210 | key='dns.query_latency_tcp', | |
211 | transform=Transformation.AVERAGE(window=timedelta(seconds=60)), | |
212 | start_ts=now()-timedelta(days=3), | |
213 | end_ts=now(), | |
214 | ) # <--- this bracket is indented and on a separate line | |
215 | """), | |
216 | INDENT_DICTIONARY_VALUE=textwrap.dedent("""\ | |
217 | Indent the dictionary value if it cannot fit on the same line as the | |
218 | dictionary key. For example: | |
219 | ||
220 | config = { | |
221 | 'key1': | |
222 | 'value1', | |
223 | 'key2': value1 + | |
224 | value2, | |
225 | } | |
226 | """), | |
227 | INDENT_BLANK_LINES=textwrap.dedent("""\ | |
228 | Indent blank lines. | |
229 | """), | |
230 | INDENT_WIDTH=textwrap.dedent("""\ | |
231 | The number of columns to use for indentation. | |
232 | """), | |
233 | JOIN_MULTIPLE_LINES=textwrap.dedent("""\ | |
234 | Join short lines into one line. E.g., single line 'if' statements. | |
235 | """), | |
236 | NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=textwrap.dedent("""\ | |
237 | Do not include spaces around selected binary operators. For example: | |
238 | ||
239 | 1 + 2 * 3 - 4 / 5 | |
240 | ||
241 | will be formatted as follows when configured with "*,/": | |
242 | ||
243 | 1 + 2*3 - 4/5 | |
244 | """), | |
245 | SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=textwrap.dedent("""\ | |
246 | Insert a space between the ending comma and closing bracket of a list, | |
247 | etc. | |
248 | """), | |
249 | SPACE_INSIDE_BRACKETS=textwrap.dedent("""\ | |
250 | Use spaces inside brackets, braces, and parentheses. For example: | |
251 | ||
252 | method_call( 1 ) | |
253 | my_dict[ 3 ][ 1 ][ get_index( *args, **kwargs ) ] | |
254 | my_set = { 1, 2, 3 } | |
255 | """), | |
256 | SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=textwrap.dedent("""\ | |
257 | Use spaces around default or named assigns. | |
258 | """), | |
259 | SPACES_AROUND_DICT_DELIMITERS=textwrap.dedent("""\ | |
260 | Adds a space after the opening '{' and before the ending '}' dict | |
261 | delimiters. | |
262 | ||
263 | {1: 2} | |
264 | ||
265 | will be formatted as: | |
266 | ||
267 | { 1: 2 } | |
268 | """), | |
269 | SPACES_AROUND_LIST_DELIMITERS=textwrap.dedent("""\ | |
270 | Adds a space after the opening '[' and before the ending ']' list | |
271 | delimiters. | |
272 | ||
273 | [1, 2] | |
274 | ||
275 | will be formatted as: | |
276 | ||
277 | [ 1, 2 ] | |
278 | """), | |
279 | SPACES_AROUND_POWER_OPERATOR=textwrap.dedent("""\ | |
280 | Use spaces around the power operator. | |
281 | """), | |
282 | SPACES_AROUND_SUBSCRIPT_COLON=textwrap.dedent("""\ | |
283 | Use spaces around the subscript / slice operator. For example: | |
284 | ||
285 | my_list[1 : 10 : 2] | |
286 | """), | |
287 | SPACES_AROUND_TUPLE_DELIMITERS=textwrap.dedent("""\ | |
288 | Adds a space after the opening '(' and before the ending ')' tuple | |
289 | delimiters. | |
290 | ||
291 | (1, 2, 3) | |
292 | ||
293 | will be formatted as: | |
294 | ||
295 | ( 1, 2, 3 ) | |
296 | """), | |
297 | SPACES_BEFORE_COMMENT=textwrap.dedent("""\ | |
298 | The number of spaces required before a trailing comment. | |
299 | This can be a single value (representing the number of spaces | |
300 | before each trailing comment) or list of values (representing | |
301 | alignment column values; trailing comments within a block will | |
302 | be aligned to the first column value that is greater than the maximum | |
303 | line length within the block). For example: | |
304 | ||
305 | With spaces_before_comment=5: | |
306 | ||
307 | 1 + 1 # Adding values | |
308 | ||
309 | will be formatted as: | |
310 | ||
311 | 1 + 1 # Adding values <-- 5 spaces between the end of the | |
312 | # statement and comment | |
313 | ||
314 | With spaces_before_comment=15, 20: | |
315 | ||
316 | 1 + 1 # Adding values | |
317 | two + two # More adding | |
318 | ||
319 | longer_statement # This is a longer statement | |
320 | short # This is a shorter statement | |
321 | ||
322 | a_very_long_statement_that_extends_beyond_the_final_column # Comment | |
323 | short # This is a shorter statement | |
324 | ||
325 | will be formatted as: | |
326 | ||
327 | 1 + 1 # Adding values <-- end of line comments in block | |
328 | # aligned to col 15 | |
329 | two + two # More adding | |
330 | ||
331 | longer_statement # This is a longer statement <-- end of line | |
332 | # comments in block aligned to col 20 | |
333 | short # This is a shorter statement | |
334 | ||
335 | a_very_long_statement_that_extends_beyond_the_final_column # Comment <-- the end of line comments are aligned based on the line length | |
336 | short # This is a shorter statement | |
337 | ||
338 | """), # noqa | |
339 | SPLIT_ALL_COMMA_SEPARATED_VALUES=textwrap.dedent("""\ | |
340 | Split before arguments. | |
341 | """), | |
342 | SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES=textwrap.dedent("""\ | |
343 | Split before arguments, but do not split all subexpressions recursively | |
344 | (unless needed). | |
345 | """), | |
346 | SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=textwrap.dedent("""\ | |
347 | Split before arguments if the argument list is terminated by a | |
348 | comma. | |
349 | """), | |
350 | SPLIT_BEFORE_ARITHMETIC_OPERATOR=textwrap.dedent("""\ | |
351 | Set to True to prefer splitting before '+', '-', '*', '/', '//', or '@' | |
352 | rather than after. | |
353 | """), | |
354 | SPLIT_BEFORE_BITWISE_OPERATOR=textwrap.dedent("""\ | |
355 | Set to True to prefer splitting before '&', '|' or '^' rather than | |
356 | after. | |
357 | """), | |
358 | SPLIT_BEFORE_CLOSING_BRACKET=textwrap.dedent("""\ | |
359 | Split before the closing bracket if a list or dict literal doesn't fit on | |
360 | a single line. | |
361 | """), | |
362 | SPLIT_BEFORE_DICT_SET_GENERATOR=textwrap.dedent("""\ | |
363 | Split before a dictionary or set generator (comp_for). For example, note | |
364 | the split before the 'for': | |
365 | ||
366 | foo = { | |
367 | variable: 'Hello world, have a nice day!' | |
368 | for variable in bar if variable != 42 | |
369 | } | |
370 | """), | |
371 | SPLIT_BEFORE_DOT=textwrap.dedent("""\ | |
372 | Split before the '.' if we need to split a longer expression: | |
373 | ||
374 | foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d)) | |
375 | ||
376 | would reformat to something like: | |
377 | ||
378 | foo = ('This is a really long string: {}, {}, {}, {}' | |
379 | .format(a, b, c, d)) | |
380 | """), # noqa | |
381 | SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN=textwrap.dedent("""\ | |
382 | Split after the opening paren which surrounds an expression if it doesn't | |
383 | fit on a single line. | |
384 | """), | |
385 | SPLIT_BEFORE_FIRST_ARGUMENT=textwrap.dedent("""\ | |
386 | If an argument / parameter list is going to be split, then split before | |
387 | the first argument. | |
388 | """), | |
389 | SPLIT_BEFORE_LOGICAL_OPERATOR=textwrap.dedent("""\ | |
390 | Set to True to prefer splitting before 'and' or 'or' rather than | |
391 | after. | |
392 | """), | |
393 | SPLIT_BEFORE_NAMED_ASSIGNS=textwrap.dedent("""\ | |
394 | Split named assignments onto individual lines. | |
395 | """), | |
396 | SPLIT_COMPLEX_COMPREHENSION=textwrap.dedent("""\ | |
397 | Set to True to split list comprehensions and generators that have | |
398 | non-trivial expressions and multiple clauses before each of these | |
399 | clauses. For example: | |
400 | ||
401 | result = [ | |
402 | a_long_var + 100 for a_long_var in xrange(1000) | |
403 | if a_long_var % 10] | |
404 | ||
405 | would reformat to something like: | |
406 | ||
407 | result = [ | |
408 | a_long_var + 100 | |
409 | for a_long_var in xrange(1000) | |
410 | if a_long_var % 10] | |
411 | """), | |
412 | SPLIT_PENALTY_AFTER_OPENING_BRACKET=textwrap.dedent("""\ | |
413 | The penalty for splitting right after the opening bracket. | |
414 | """), | |
415 | SPLIT_PENALTY_AFTER_UNARY_OPERATOR=textwrap.dedent("""\ | |
416 | The penalty for splitting the line after a unary operator. | |
417 | """), | |
418 | SPLIT_PENALTY_ARITHMETIC_OPERATOR=textwrap.dedent("""\ | |
419 | The penalty of splitting the line around the '+', '-', '*', '/', '//', | |
420 | `%`, and '@' operators. | |
421 | """), | |
422 | SPLIT_PENALTY_BEFORE_IF_EXPR=textwrap.dedent("""\ | |
423 | The penalty for splitting right before an if expression. | |
424 | """), | |
425 | SPLIT_PENALTY_BITWISE_OPERATOR=textwrap.dedent("""\ | |
426 | The penalty of splitting the line around the '&', '|', and '^' operators. | |
427 | """), | |
428 | SPLIT_PENALTY_COMPREHENSION=textwrap.dedent("""\ | |
429 | The penalty for splitting a list comprehension or generator | |
430 | expression. | |
431 | """), | |
432 | SPLIT_PENALTY_EXCESS_CHARACTER=textwrap.dedent("""\ | |
433 | The penalty for characters over the column limit. | |
434 | """), | |
435 | SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=textwrap.dedent("""\ | |
436 | The penalty incurred by adding a line split to the logical line. The | |
437 | more line splits added the higher the penalty. | |
438 | """), | |
439 | SPLIT_PENALTY_IMPORT_NAMES=textwrap.dedent("""\ | |
440 | The penalty of splitting a list of "import as" names. For example: | |
441 | ||
442 | from a_very_long_or_indented_module_name_yada_yad import (long_argument_1, | |
443 | long_argument_2, | |
444 | long_argument_3) | |
445 | ||
446 | would reformat to something like: | |
447 | ||
448 | from a_very_long_or_indented_module_name_yada_yad import ( | |
449 | long_argument_1, long_argument_2, long_argument_3) | |
450 | """), # noqa | |
451 | SPLIT_PENALTY_LOGICAL_OPERATOR=textwrap.dedent("""\ | |
452 | The penalty of splitting the line around the 'and' and 'or' operators. | |
453 | """), | |
454 | USE_TABS=textwrap.dedent("""\ | |
455 | Use the Tab character for indentation. | |
456 | """), | |
457 | ) | |
458 | ||
459 | ||
460 | def CreatePEP8Style(): | |
461 | """Create the PEP8 formatting style.""" | |
462 | return dict( | |
463 | ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=True, | |
464 | ALLOW_MULTILINE_DICTIONARY_KEYS=False, | |
465 | ALLOW_MULTILINE_LAMBDAS=False, | |
466 | ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS=True, | |
467 | ALLOW_SPLIT_BEFORE_DICT_VALUE=True, | |
468 | ARITHMETIC_PRECEDENCE_INDICATION=False, | |
469 | BLANK_LINE_BEFORE_CLASS_DOCSTRING=False, | |
470 | BLANK_LINE_BEFORE_MODULE_DOCSTRING=False, | |
471 | BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=True, | |
472 | BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=2, | |
473 | BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES=1, | |
474 | COALESCE_BRACKETS=False, | |
475 | COLUMN_LIMIT=79, | |
476 | CONTINUATION_ALIGN_STYLE='SPACE', | |
477 | CONTINUATION_INDENT_WIDTH=4, | |
478 | DEDENT_CLOSING_BRACKETS=False, | |
479 | DISABLE_ENDING_COMMA_HEURISTIC=False, | |
480 | EACH_DICT_ENTRY_ON_SEPARATE_LINE=True, | |
481 | FORCE_MULTILINE_DICT=False, | |
482 | I18N_COMMENT='', | |
483 | I18N_FUNCTION_CALL='', | |
484 | INDENT_CLOSING_BRACKETS=False, | |
485 | INDENT_DICTIONARY_VALUE=False, | |
486 | INDENT_WIDTH=4, | |
487 | INDENT_BLANK_LINES=False, | |
488 | JOIN_MULTIPLE_LINES=True, | |
489 | NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=set(), | |
490 | SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=True, | |
491 | SPACE_INSIDE_BRACKETS=False, | |
492 | SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=False, | |
493 | SPACES_AROUND_DICT_DELIMITERS=False, | |
494 | SPACES_AROUND_LIST_DELIMITERS=False, | |
495 | SPACES_AROUND_POWER_OPERATOR=False, | |
496 | SPACES_AROUND_SUBSCRIPT_COLON=False, | |
497 | SPACES_AROUND_TUPLE_DELIMITERS=False, | |
498 | SPACES_BEFORE_COMMENT=2, | |
499 | SPLIT_ALL_COMMA_SEPARATED_VALUES=False, | |
500 | SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES=False, | |
501 | SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=False, | |
502 | SPLIT_BEFORE_ARITHMETIC_OPERATOR=False, | |
503 | SPLIT_BEFORE_BITWISE_OPERATOR=True, | |
504 | SPLIT_BEFORE_CLOSING_BRACKET=True, | |
505 | SPLIT_BEFORE_DICT_SET_GENERATOR=True, | |
506 | SPLIT_BEFORE_DOT=False, | |
507 | SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN=False, | |
508 | SPLIT_BEFORE_FIRST_ARGUMENT=False, | |
509 | SPLIT_BEFORE_LOGICAL_OPERATOR=True, | |
510 | SPLIT_BEFORE_NAMED_ASSIGNS=True, | |
511 | SPLIT_COMPLEX_COMPREHENSION=False, | |
512 | SPLIT_PENALTY_AFTER_OPENING_BRACKET=300, | |
513 | SPLIT_PENALTY_AFTER_UNARY_OPERATOR=10000, | |
514 | SPLIT_PENALTY_ARITHMETIC_OPERATOR=300, | |
515 | SPLIT_PENALTY_BEFORE_IF_EXPR=0, | |
516 | SPLIT_PENALTY_BITWISE_OPERATOR=300, | |
517 | SPLIT_PENALTY_COMPREHENSION=80, | |
518 | SPLIT_PENALTY_EXCESS_CHARACTER=7000, | |
519 | SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=30, | |
520 | SPLIT_PENALTY_IMPORT_NAMES=0, | |
521 | SPLIT_PENALTY_LOGICAL_OPERATOR=300, | |
522 | USE_TABS=False, | |
523 | ) | |
524 | ||
525 | ||
526 | def CreateGoogleStyle(): | |
527 | """Create the Google formatting style.""" | |
528 | style = CreatePEP8Style() | |
529 | style['ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'] = False | |
530 | style['COLUMN_LIMIT'] = 80 | |
531 | style['INDENT_DICTIONARY_VALUE'] = True | |
532 | style['INDENT_WIDTH'] = 4 | |
533 | style['I18N_COMMENT'] = r'#\..*' | |
534 | style['I18N_FUNCTION_CALL'] = ['N_', '_'] | |
535 | style['JOIN_MULTIPLE_LINES'] = False | |
536 | style['SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET'] = False | |
537 | style['SPLIT_BEFORE_BITWISE_OPERATOR'] = False | |
538 | style['SPLIT_BEFORE_DICT_SET_GENERATOR'] = False | |
539 | style['SPLIT_BEFORE_LOGICAL_OPERATOR'] = False | |
540 | style['SPLIT_COMPLEX_COMPREHENSION'] = True | |
541 | style['SPLIT_PENALTY_COMPREHENSION'] = 2100 | |
542 | return style | |
543 | ||
544 | ||
545 | def CreateYapfStyle(): | |
546 | """Create the YAPF formatting style.""" | |
547 | style = CreateGoogleStyle() | |
548 | style['ALLOW_MULTILINE_DICTIONARY_KEYS'] = True | |
549 | style['ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS'] = False | |
550 | style['INDENT_WIDTH'] = 2 | |
551 | style['SPLIT_BEFORE_BITWISE_OPERATOR'] = True | |
552 | style['SPLIT_BEFORE_DOT'] = True | |
553 | style['SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN'] = True | |
554 | return style | |
555 | ||
556 | ||
557 | def CreateFacebookStyle(): | |
558 | """Create the Facebook formatting style.""" | |
559 | style = CreatePEP8Style() | |
560 | style['ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'] = False | |
561 | style['BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF'] = False | |
562 | style['COLUMN_LIMIT'] = 80 | |
563 | style['DEDENT_CLOSING_BRACKETS'] = True | |
564 | style['INDENT_CLOSING_BRACKETS'] = False | |
565 | style['INDENT_DICTIONARY_VALUE'] = True | |
566 | style['JOIN_MULTIPLE_LINES'] = False | |
567 | style['SPACES_BEFORE_COMMENT'] = 2 | |
568 | style['SPLIT_PENALTY_AFTER_OPENING_BRACKET'] = 0 | |
569 | style['SPLIT_PENALTY_BEFORE_IF_EXPR'] = 30 | |
570 | style['SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT'] = 30 | |
571 | style['SPLIT_BEFORE_BITWISE_OPERATOR'] = False | |
572 | style['SPLIT_BEFORE_LOGICAL_OPERATOR'] = False | |
573 | return style | |
574 | ||
575 | ||
576 | _STYLE_NAME_TO_FACTORY = dict( | |
577 | facebook=CreateFacebookStyle, | |
578 | google=CreateGoogleStyle, | |
579 | pep8=CreatePEP8Style, | |
580 | yapf=CreateYapfStyle, | |
581 | ) | |
582 | ||
583 | _DEFAULT_STYLE_TO_FACTORY = [ | |
584 | (CreateFacebookStyle(), CreateFacebookStyle), | |
585 | (CreateGoogleStyle(), CreateGoogleStyle), | |
586 | (CreatePEP8Style(), CreatePEP8Style), | |
587 | (CreateYapfStyle(), CreateYapfStyle), | |
588 | ] | |
589 | ||
590 | ||
591 | def _GetStyleFactory(style): | |
592 | for def_style, factory in _DEFAULT_STYLE_TO_FACTORY: | |
593 | if style == def_style: | |
594 | return factory | |
595 | return None | |
596 | ||
597 | ||
598 | def _ContinuationAlignStyleStringConverter(s): | |
599 | """Option value converter for a continuation align style string.""" | |
600 | accepted_styles = ('SPACE', 'FIXED', 'VALIGN-RIGHT') | |
601 | if s: | |
602 | r = s.strip('"\'').replace('_', '-').upper() | |
603 | if r not in accepted_styles: | |
604 | raise ValueError('unknown continuation align style: %r' % (s,)) | |
605 | else: | |
606 | r = accepted_styles[0] | |
607 | return r | |
608 | ||
609 | ||
610 | def _StringListConverter(s): | |
611 | """Option value converter for a comma-separated list of strings.""" | |
612 | return [part.strip() for part in s.split(',')] | |
613 | ||
614 | ||
615 | def _StringSetConverter(s): | |
616 | """Option value converter for a comma-separated set of strings.""" | |
617 | if len(s) > 2 and s[0] in '"\'': | |
618 | s = s[1:-1] | |
619 | return {part.strip() for part in s.split(',')} | |
620 | ||
621 | ||
622 | def _BoolConverter(s): | |
623 | """Option value converter for a boolean.""" | |
624 | return ConfigParser.BOOLEAN_STATES[s.lower()] | |
625 | ||
626 | ||
627 | def _IntListConverter(s): | |
628 | """Option value converter for a comma-separated list of integers.""" | |
629 | s = s.strip() | |
630 | if s.startswith('[') and s.endswith(']'): | |
631 | s = s[1:-1] | |
632 | ||
633 | return [int(part.strip()) for part in s.split(',') if part.strip()] | |
634 | ||
635 | ||
636 | def _IntOrIntListConverter(s): | |
637 | """Option value converter for an integer or list of integers.""" | |
638 | if len(s) > 2 and s[0] in '"\'': | |
639 | s = s[1:-1] | |
640 | return _IntListConverter(s) if ',' in s else int(s) | |
641 | ||
642 | ||
643 | # Different style options need to have their values interpreted differently when | |
644 | # read from the config file. This dict maps an option name to a "converter" | |
645 | # function that accepts the string read for the option's value from the file and | |
646 | # returns it wrapper in actual Python type that's going to be meaningful to | |
647 | # yapf. | |
648 | # | |
649 | # Note: this dict has to map all the supported style options. | |
650 | _STYLE_OPTION_VALUE_CONVERTER = dict( | |
651 | ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=_BoolConverter, | |
652 | ALLOW_MULTILINE_DICTIONARY_KEYS=_BoolConverter, | |
653 | ALLOW_MULTILINE_LAMBDAS=_BoolConverter, | |
654 | ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS=_BoolConverter, | |
655 | ALLOW_SPLIT_BEFORE_DICT_VALUE=_BoolConverter, | |
656 | ARITHMETIC_PRECEDENCE_INDICATION=_BoolConverter, | |
657 | BLANK_LINE_BEFORE_CLASS_DOCSTRING=_BoolConverter, | |
658 | BLANK_LINE_BEFORE_MODULE_DOCSTRING=_BoolConverter, | |
659 | BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=_BoolConverter, | |
660 | BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=int, | |
661 | BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES=int, | |
662 | COALESCE_BRACKETS=_BoolConverter, | |
663 | COLUMN_LIMIT=int, | |
664 | CONTINUATION_ALIGN_STYLE=_ContinuationAlignStyleStringConverter, | |
665 | CONTINUATION_INDENT_WIDTH=int, | |
666 | DEDENT_CLOSING_BRACKETS=_BoolConverter, | |
667 | DISABLE_ENDING_COMMA_HEURISTIC=_BoolConverter, | |
668 | EACH_DICT_ENTRY_ON_SEPARATE_LINE=_BoolConverter, | |
669 | FORCE_MULTILINE_DICT=_BoolConverter, | |
670 | I18N_COMMENT=str, | |
671 | I18N_FUNCTION_CALL=_StringListConverter, | |
672 | INDENT_BLANK_LINES=_BoolConverter, | |
673 | INDENT_CLOSING_BRACKETS=_BoolConverter, | |
674 | INDENT_DICTIONARY_VALUE=_BoolConverter, | |
675 | INDENT_WIDTH=int, | |
676 | JOIN_MULTIPLE_LINES=_BoolConverter, | |
677 | NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=_StringSetConverter, | |
678 | SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=_BoolConverter, | |
679 | SPACE_INSIDE_BRACKETS=_BoolConverter, | |
680 | SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=_BoolConverter, | |
681 | SPACES_AROUND_DICT_DELIMITERS=_BoolConverter, | |
682 | SPACES_AROUND_LIST_DELIMITERS=_BoolConverter, | |
683 | SPACES_AROUND_POWER_OPERATOR=_BoolConverter, | |
684 | SPACES_AROUND_SUBSCRIPT_COLON=_BoolConverter, | |
685 | SPACES_AROUND_TUPLE_DELIMITERS=_BoolConverter, | |
686 | SPACES_BEFORE_COMMENT=_IntOrIntListConverter, | |
687 | SPLIT_ALL_COMMA_SEPARATED_VALUES=_BoolConverter, | |
688 | SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES=_BoolConverter, | |
689 | SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=_BoolConverter, | |
690 | SPLIT_BEFORE_ARITHMETIC_OPERATOR=_BoolConverter, | |
691 | SPLIT_BEFORE_BITWISE_OPERATOR=_BoolConverter, | |
692 | SPLIT_BEFORE_CLOSING_BRACKET=_BoolConverter, | |
693 | SPLIT_BEFORE_DICT_SET_GENERATOR=_BoolConverter, | |
694 | SPLIT_BEFORE_DOT=_BoolConverter, | |
695 | SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN=_BoolConverter, | |
696 | SPLIT_BEFORE_FIRST_ARGUMENT=_BoolConverter, | |
697 | SPLIT_BEFORE_LOGICAL_OPERATOR=_BoolConverter, | |
698 | SPLIT_BEFORE_NAMED_ASSIGNS=_BoolConverter, | |
699 | SPLIT_COMPLEX_COMPREHENSION=_BoolConverter, | |
700 | SPLIT_PENALTY_AFTER_OPENING_BRACKET=int, | |
701 | SPLIT_PENALTY_AFTER_UNARY_OPERATOR=int, | |
702 | SPLIT_PENALTY_ARITHMETIC_OPERATOR=int, | |
703 | SPLIT_PENALTY_BEFORE_IF_EXPR=int, | |
704 | SPLIT_PENALTY_BITWISE_OPERATOR=int, | |
705 | SPLIT_PENALTY_COMPREHENSION=int, | |
706 | SPLIT_PENALTY_EXCESS_CHARACTER=int, | |
707 | SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=int, | |
708 | SPLIT_PENALTY_IMPORT_NAMES=int, | |
709 | SPLIT_PENALTY_LOGICAL_OPERATOR=int, | |
710 | USE_TABS=_BoolConverter, | |
711 | ) | |
712 | ||
713 | ||
714 | def CreateStyleFromConfig(style_config): | |
715 | """Create a style dict from the given config. | |
716 | ||
717 | Arguments: | |
718 | style_config: either a style name or a file name. The file is expected to | |
719 | contain settings. It can have a special BASED_ON_STYLE setting naming the | |
720 | style which it derives from. If no such setting is found, it derives from | |
721 | the default style. When style_config is None, the _GLOBAL_STYLE_FACTORY | |
722 | config is created. | |
723 | ||
724 | Returns: | |
725 | A style dict. | |
726 | ||
727 | Raises: | |
728 | StyleConfigError: if an unknown style option was encountered. | |
729 | """ | |
730 | ||
731 | def GlobalStyles(): | |
732 | for style, _ in _DEFAULT_STYLE_TO_FACTORY: | |
733 | yield style | |
734 | ||
735 | def_style = False | |
736 | if style_config is None: | |
737 | for style in GlobalStyles(): | |
738 | if _style == style: | |
739 | def_style = True | |
740 | break | |
741 | if not def_style: | |
742 | return _style | |
743 | return _GLOBAL_STYLE_FACTORY() | |
744 | ||
745 | if isinstance(style_config, dict): | |
746 | config = _CreateConfigParserFromConfigDict(style_config) | |
747 | elif isinstance(style_config, str): | |
748 | style_factory = _STYLE_NAME_TO_FACTORY.get(style_config.lower()) | |
749 | if style_factory is not None: | |
750 | return style_factory() | |
751 | if style_config.startswith('{'): | |
752 | # Most likely a style specification from the command line. | |
753 | config = _CreateConfigParserFromConfigString(style_config) | |
754 | else: | |
755 | # Unknown config name: assume it's a file name then. | |
756 | config = _CreateConfigParserFromConfigFile(style_config) | |
757 | return _CreateStyleFromConfigParser(config) | |
758 | ||
759 | ||
760 | def _CreateConfigParserFromConfigDict(config_dict): | |
761 | config = ConfigParser() | |
762 | config.add_section('style') | |
763 | for key, value in config_dict.items(): | |
764 | config.set('style', key, str(value)) | |
765 | return config | |
766 | ||
767 | ||
768 | def _CreateConfigParserFromConfigString(config_string): | |
769 | """Given a config string from the command line, return a config parser.""" | |
770 | if config_string[0] != '{' or config_string[-1] != '}': | |
771 | raise StyleConfigError( | |
772 | "Invalid style dict syntax: '{}'.".format(config_string)) | |
773 | config = ConfigParser() | |
774 | config.add_section('style') | |
775 | for key, value, _ in re.findall( | |
776 | r'([a-zA-Z0-9_]+)\s*[:=]\s*' | |
777 | r'(?:' | |
778 | r'((?P<quote>[\'"]).*?(?P=quote)|' | |
779 | r'[a-zA-Z0-9_]+)' | |
780 | r')', config_string): # yapf: disable | |
781 | config.set('style', key, value) | |
782 | return config | |
783 | ||
784 | ||
785 | def _CreateConfigParserFromConfigFile(config_filename): | |
786 | """Read the file and return a ConfigParser object.""" | |
787 | if not os.path.exists(config_filename): | |
788 | # Provide a more meaningful error here. | |
789 | raise StyleConfigError( | |
790 | '"{0}" is not a valid style or file path'.format(config_filename)) | |
791 | config = ConfigParser() | |
792 | ||
793 | if config_filename.endswith(PYPROJECT_TOML): | |
794 | try: | |
795 | import tomli as tomllib | |
796 | except ImportError: | |
797 | raise errors.YapfError( | |
798 | 'tomli package is needed for using pyproject.toml as a ' | |
799 | 'configuration file') | |
800 | ||
801 | with open(config_filename, 'rb') as style_file: | |
802 | pyproject_toml = tomllib.load(style_file) | |
803 | style_dict = pyproject_toml.get('tool', {}).get('yapf', None) | |
804 | if style_dict is None: | |
805 | raise StyleConfigError( | |
806 | 'Unable to find section [tool.yapf] in {0}'.format(config_filename)) | |
807 | config.add_section('style') | |
808 | for k, v in style_dict.items(): | |
809 | config.set('style', k, str(v)) | |
810 | return config | |
811 | ||
812 | with open(config_filename) as style_file: | |
813 | config.read_file(style_file) | |
814 | ||
815 | if config_filename.endswith(SETUP_CONFIG): | |
816 | if not config.has_section('yapf'): | |
817 | raise StyleConfigError( | |
818 | 'Unable to find section [yapf] in {0}'.format(config_filename)) | |
819 | return config | |
820 | ||
821 | if config_filename.endswith(LOCAL_STYLE): | |
822 | if not config.has_section('style'): | |
823 | raise StyleConfigError( | |
824 | 'Unable to find section [style] in {0}'.format(config_filename)) | |
825 | return config | |
826 | ||
827 | if not config.has_section('style'): | |
828 | raise StyleConfigError( | |
829 | 'Unable to find section [style] in {0}'.format(config_filename)) | |
830 | return config | |
831 | ||
832 | ||
833 | def _CreateStyleFromConfigParser(config): | |
834 | """Create a style dict from a configuration file. | |
835 | ||
836 | Arguments: | |
837 | config: a ConfigParser object. | |
838 | ||
839 | Returns: | |
840 | A style dict. | |
841 | ||
842 | Raises: | |
843 | StyleConfigError: if an unknown style option was encountered. | |
844 | """ | |
845 | # Initialize the base style. | |
846 | section = 'yapf' if config.has_section('yapf') else 'style' | |
847 | if config.has_option('style', 'based_on_style'): | |
848 | based_on = config.get('style', 'based_on_style').lower() | |
849 | base_style = _STYLE_NAME_TO_FACTORY[based_on]() | |
850 | elif config.has_option('yapf', 'based_on_style'): | |
851 | based_on = config.get('yapf', 'based_on_style').lower() | |
852 | base_style = _STYLE_NAME_TO_FACTORY[based_on]() | |
853 | else: | |
854 | base_style = _GLOBAL_STYLE_FACTORY() | |
855 | ||
856 | # Read all options specified in the file and update the style. | |
857 | for option, value in config.items(section): | |
858 | if option.lower() == 'based_on_style': | |
859 | # Now skip this one - we've already handled it and it's not one of the | |
860 | # recognized style options. | |
861 | continue | |
862 | option = option.upper() | |
863 | if option not in _STYLE_OPTION_VALUE_CONVERTER: | |
864 | raise StyleConfigError('Unknown style option "{0}"'.format(option)) | |
865 | try: | |
866 | base_style[option] = _STYLE_OPTION_VALUE_CONVERTER[option](value) | |
867 | except ValueError: | |
868 | raise StyleConfigError("'{}' is not a valid setting for {}.".format( | |
869 | value, option)) | |
870 | return base_style | |
871 | ||
872 | ||
873 | # The default style - used if yapf is not invoked without specifically | |
874 | # requesting a formatting style. | |
875 | DEFAULT_STYLE = 'pep8' | |
876 | DEFAULT_STYLE_FACTORY = CreatePEP8Style | |
877 | _GLOBAL_STYLE_FACTORY = CreatePEP8Style | |
878 | ||
879 | # The name of the file to use for global style definition. | |
880 | GLOBAL_STYLE = ( | |
881 | os.path.join( | |
882 | os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'), 'yapf', | |
883 | 'style')) | |
884 | ||
885 | # The name of the file to use for directory-local style definition. | |
886 | LOCAL_STYLE = '.style.yapf' | |
887 | ||
888 | # Alternative place for directory-local style definition. Style should be | |
889 | # specified in the '[yapf]' section. | |
890 | SETUP_CONFIG = 'setup.cfg' | |
891 | ||
892 | # Style definition by local pyproject.toml file. Style should be specified | |
893 | # in the '[tool.yapf]' section. | |
894 | PYPROJECT_TOML = 'pyproject.toml' | |
895 | ||
896 | # TODO(eliben): For now we're preserving the global presence of a style dict. | |
897 | # Refactor this so that the style is passed around through yapf rather than | |
898 | # being global. | |
899 | _style = None | |
900 | SetGlobalStyle(_GLOBAL_STYLE_FACTORY()) |