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