• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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