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 Help():
35  """Return dict mapping style names to help strings."""
36  return _STYLE_HELP
37
38
39def SetGlobalStyle(style):
40  """Set a style dict."""
41  global _style
42  global _GLOBAL_STYLE_FACTORY
43  factory = _GetStyleFactory(style)
44  if factory:
45    _GLOBAL_STYLE_FACTORY = factory
46  _style = style
47
48
49_STYLE_HELP = dict(
50    ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=textwrap.dedent("""\
51      Align closing bracket with visual indentation."""),
52    ALLOW_MULTILINE_LAMBDAS=textwrap.dedent("""\
53      Allow lambdas to be formatted on more than one line."""),
54    ALLOW_MULTILINE_DICTIONARY_KEYS=textwrap.dedent("""\
55      Allow dictionary keys to exist on multiple lines. For example:
56
57        x = {
58            ('this is the first element of a tuple',
59             'this is the second element of a tuple'):
60                 value,
61        }"""),
62    ALLOW_SPLIT_BEFORE_DICT_VALUE=textwrap.dedent("""\
63      Allow splits before the dictionary value."""),
64    BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=textwrap.dedent("""\
65      Insert a blank line before a 'def' or 'class' immediately nested
66      within another 'def' or 'class'. For example:
67
68        class Foo:
69                           # <------ this blank line
70          def method():
71            ..."""),
72    BLANK_LINE_BEFORE_CLASS_DOCSTRING=textwrap.dedent("""\
73      Insert a blank line before a class-level docstring."""),
74    BLANK_LINE_BEFORE_MODULE_DOCSTRING=textwrap.dedent("""\
75      Insert a blank line before a module docstring."""),
76    BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=textwrap.dedent("""\
77      Number of blank lines surrounding top-level function and class
78      definitions."""),
79    COALESCE_BRACKETS=textwrap.dedent("""\
80      Do not split consecutive brackets. Only relevant when
81      dedent_closing_brackets is set. For example:
82
83         call_func_that_takes_a_dict(
84             {
85                 'key1': 'value1',
86                 'key2': 'value2',
87             }
88         )
89
90      would reformat to:
91
92         call_func_that_takes_a_dict({
93             'key1': 'value1',
94             'key2': 'value2',
95         })"""),
96    COLUMN_LIMIT=textwrap.dedent("""\
97      The column limit."""),
98    CONTINUATION_ALIGN_STYLE=textwrap.dedent("""\
99      The style for continuation alignment. Possible values are:
100
101      - SPACE: Use spaces for continuation alignment. This is default behavior.
102      - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns
103        (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs) for continuation
104        alignment.
105      - LESS: Slightly left if cannot vertically align continuation lines with
106        indent characters.
107      - VALIGN-RIGHT: Vertically align continuation lines with indent
108        characters. Slightly right (one more indent character) if cannot
109        vertically align continuation lines with indent characters.
110
111      For options FIXED, and VALIGN-RIGHT are only available when USE_TABS is
112      enabled."""),
113    CONTINUATION_INDENT_WIDTH=textwrap.dedent("""\
114      Indent width used for line continuations."""),
115    DEDENT_CLOSING_BRACKETS=textwrap.dedent("""\
116      Put closing brackets on a separate line, dedented, if the bracketed
117      expression can't fit in a single line. Applies to all kinds of brackets,
118      including function definitions and calls. For example:
119
120        config = {
121            'key1': 'value1',
122            'key2': 'value2',
123        }        # <--- this bracket is dedented and on a separate line
124
125        time_series = self.remote_client.query_entity_counters(
126            entity='dev3246.region1',
127            key='dns.query_latency_tcp',
128            transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
129            start_ts=now()-timedelta(days=3),
130            end_ts=now(),
131        )        # <--- this bracket is dedented and on a separate line"""),
132    DISABLE_ENDING_COMMA_HEURISTIC=textwrap.dedent("""\
133      Disable the heuristic which places each list element on a separate line
134      if the list is comma-terminated."""),
135    EACH_DICT_ENTRY_ON_SEPARATE_LINE=textwrap.dedent("""\
136      Place each dictionary entry onto its own line."""),
137    I18N_COMMENT=textwrap.dedent("""\
138      The regex for an i18n comment. The presence of this comment stops
139      reformatting of that line, because the comments are required to be
140      next to the string they translate."""),
141    I18N_FUNCTION_CALL=textwrap.dedent("""\
142      The i18n function call names. The presence of this function stops
143      reformattting on that line, because the string it has cannot be moved
144      away from the i18n comment."""),
145    INDENT_DICTIONARY_VALUE=textwrap.dedent("""\
146      Indent the dictionary value if it cannot fit on the same line as the
147      dictionary key. For example:
148
149        config = {
150            'key1':
151                'value1',
152            'key2': value1 +
153                    value2,
154        }"""),
155    INDENT_WIDTH=textwrap.dedent("""\
156      The number of columns to use for indentation."""),
157    JOIN_MULTIPLE_LINES=textwrap.dedent("""\
158      Join short lines into one line. E.g., single line 'if' statements."""),
159    NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=textwrap.dedent("""\
160      Do not include spaces around selected binary operators. For example:
161
162        1 + 2 * 3 - 4 / 5
163
164      will be formatted as follows when configured with *,/:
165
166        1 + 2*3 - 4/5
167
168      """),
169    SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=textwrap.dedent("""\
170      Insert a space between the ending comma and closing bracket of a list,
171      etc."""),
172    SPACES_AROUND_POWER_OPERATOR=textwrap.dedent("""\
173      Use spaces around the power operator."""),
174    SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=textwrap.dedent("""\
175      Use spaces around default or named assigns."""),
176    SPACES_BEFORE_COMMENT=textwrap.dedent("""\
177      The number of spaces required before a trailing comment."""),
178    SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=textwrap.dedent("""\
179      Split before arguments if the argument list is terminated by a
180      comma."""),
181    SPLIT_ALL_COMMA_SEPARATED_VALUES=textwrap.dedent("""\
182      Split before arguments"""),
183    SPLIT_BEFORE_BITWISE_OPERATOR=textwrap.dedent("""\
184      Set to True to prefer splitting before '&', '|' or '^' rather than
185      after."""),
186    SPLIT_BEFORE_CLOSING_BRACKET=textwrap.dedent("""\
187      Split before the closing bracket if a list or dict literal doesn't fit on
188      a single line."""),
189    SPLIT_BEFORE_DICT_SET_GENERATOR=textwrap.dedent("""\
190      Split before a dictionary or set generator (comp_for). For example, note
191      the split before the 'for':
192
193        foo = {
194            variable: 'Hello world, have a nice day!'
195            for variable in bar if variable != 42
196        }"""),
197    SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN=textwrap.dedent("""\
198      Split after the opening paren which surrounds an expression if it doesn't
199      fit on a single line.
200      """),
201    SPLIT_BEFORE_FIRST_ARGUMENT=textwrap.dedent("""\
202      If an argument / parameter list is going to be split, then split before
203      the first argument."""),
204    SPLIT_BEFORE_LOGICAL_OPERATOR=textwrap.dedent("""\
205      Set to True to prefer splitting before 'and' or 'or' rather than
206      after."""),
207    SPLIT_BEFORE_NAMED_ASSIGNS=textwrap.dedent("""\
208      Split named assignments onto individual lines."""),
209    SPLIT_COMPLEX_COMPREHENSION=textwrap.dedent("""\
210      Set to True to split list comprehensions and generators that have
211      non-trivial expressions and multiple clauses before each of these
212      clauses. For example:
213
214        result = [
215            a_long_var + 100 for a_long_var in xrange(1000)
216            if a_long_var % 10]
217
218      would reformat to something like:
219
220        result = [
221            a_long_var + 100
222            for a_long_var in xrange(1000)
223            if a_long_var % 10]
224      """),
225    SPLIT_PENALTY_AFTER_OPENING_BRACKET=textwrap.dedent("""\
226      The penalty for splitting right after the opening bracket."""),
227    SPLIT_PENALTY_AFTER_UNARY_OPERATOR=textwrap.dedent("""\
228      The penalty for splitting the line after a unary operator."""),
229    SPLIT_PENALTY_BEFORE_IF_EXPR=textwrap.dedent("""\
230      The penalty for splitting right before an if expression."""),
231    SPLIT_PENALTY_BITWISE_OPERATOR=textwrap.dedent("""\
232      The penalty of splitting the line around the '&', '|', and '^'
233      operators."""),
234    SPLIT_PENALTY_COMPREHENSION=textwrap.dedent("""\
235      The penalty for splitting a list comprehension or generator
236      expression."""),
237    SPLIT_PENALTY_EXCESS_CHARACTER=textwrap.dedent("""\
238      The penalty for characters over the column limit."""),
239    SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=textwrap.dedent("""\
240      The penalty incurred by adding a line split to the unwrapped line. The
241      more line splits added the higher the penalty."""),
242    SPLIT_PENALTY_IMPORT_NAMES=textwrap.dedent("""\
243      The penalty of splitting a list of "import as" names. For example:
244
245        from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
246                                                                  long_argument_2,
247                                                                  long_argument_3)
248
249      would reformat to something like:
250
251        from a_very_long_or_indented_module_name_yada_yad import (
252            long_argument_1, long_argument_2, long_argument_3)
253      """),
254    SPLIT_PENALTY_LOGICAL_OPERATOR=textwrap.dedent("""\
255      The penalty of splitting the line around the 'and' and 'or'
256      operators."""),
257    USE_TABS=textwrap.dedent("""\
258      Use the Tab character for indentation."""),
259    # BASED_ON_STYLE='Which predefined style this style is based on',
260)
261
262
263def CreatePEP8Style():
264  return dict(
265      ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=True,
266      ALLOW_MULTILINE_LAMBDAS=False,
267      ALLOW_MULTILINE_DICTIONARY_KEYS=False,
268      ALLOW_SPLIT_BEFORE_DICT_VALUE=True,
269      BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=False,
270      BLANK_LINE_BEFORE_CLASS_DOCSTRING=False,
271      BLANK_LINE_BEFORE_MODULE_DOCSTRING=False,
272      BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=2,
273      COALESCE_BRACKETS=False,
274      COLUMN_LIMIT=79,
275      CONTINUATION_ALIGN_STYLE='SPACE',
276      CONTINUATION_INDENT_WIDTH=4,
277      DEDENT_CLOSING_BRACKETS=False,
278      DISABLE_ENDING_COMMA_HEURISTIC=False,
279      EACH_DICT_ENTRY_ON_SEPARATE_LINE=True,
280      I18N_COMMENT='',
281      I18N_FUNCTION_CALL='',
282      INDENT_DICTIONARY_VALUE=False,
283      INDENT_WIDTH=4,
284      JOIN_MULTIPLE_LINES=True,
285      SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=True,
286      SPACES_AROUND_POWER_OPERATOR=False,
287      NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=set(),
288      SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=False,
289      SPACES_BEFORE_COMMENT=2,
290      SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=False,
291      SPLIT_ALL_COMMA_SEPARATED_VALUES=False,
292      SPLIT_BEFORE_BITWISE_OPERATOR=True,
293      SPLIT_BEFORE_CLOSING_BRACKET=True,
294      SPLIT_BEFORE_DICT_SET_GENERATOR=True,
295      SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN=False,
296      SPLIT_BEFORE_FIRST_ARGUMENT=False,
297      SPLIT_BEFORE_LOGICAL_OPERATOR=True,
298      SPLIT_BEFORE_NAMED_ASSIGNS=True,
299      SPLIT_COMPLEX_COMPREHENSION=False,
300      SPLIT_PENALTY_AFTER_OPENING_BRACKET=30,
301      SPLIT_PENALTY_AFTER_UNARY_OPERATOR=10000,
302      SPLIT_PENALTY_BEFORE_IF_EXPR=0,
303      SPLIT_PENALTY_BITWISE_OPERATOR=300,
304      SPLIT_PENALTY_COMPREHENSION=80,
305      SPLIT_PENALTY_EXCESS_CHARACTER=4500,
306      SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=30,
307      SPLIT_PENALTY_IMPORT_NAMES=0,
308      SPLIT_PENALTY_LOGICAL_OPERATOR=300,
309      USE_TABS=False,
310  )
311
312
313def CreateGoogleStyle():
314  style = CreatePEP8Style()
315  style['ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'] = False
316  style['BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF'] = True
317  style['COLUMN_LIMIT'] = 80
318  style['INDENT_WIDTH'] = 4
319  style['I18N_COMMENT'] = r'#\..*'
320  style['I18N_FUNCTION_CALL'] = ['N_', '_']
321  style['SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET'] = False
322  style['SPLIT_BEFORE_BITWISE_OPERATOR'] = False
323  style['SPLIT_BEFORE_DICT_SET_GENERATOR'] = False
324  style['SPLIT_BEFORE_LOGICAL_OPERATOR'] = False
325  style['SPLIT_COMPLEX_COMPREHENSION'] = True
326  style['SPLIT_PENALTY_COMPREHENSION'] = 2100
327  return style
328
329
330def CreateChromiumStyle():
331  style = CreateGoogleStyle()
332  style['ALLOW_MULTILINE_DICTIONARY_KEYS'] = True
333  style['INDENT_DICTIONARY_VALUE'] = True
334  style['INDENT_WIDTH'] = 2
335  style['JOIN_MULTIPLE_LINES'] = False
336  style['SPLIT_BEFORE_BITWISE_OPERATOR'] = True
337  style['SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN'] = True
338  return style
339
340
341def CreateFacebookStyle():
342  style = CreatePEP8Style()
343  style['ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT'] = False
344  style['COLUMN_LIMIT'] = 80
345  style['DEDENT_CLOSING_BRACKETS'] = True
346  style['INDENT_DICTIONARY_VALUE'] = True
347  style['JOIN_MULTIPLE_LINES'] = False
348  style['SPACES_BEFORE_COMMENT'] = 2
349  style['SPLIT_PENALTY_AFTER_OPENING_BRACKET'] = 0
350  style['SPLIT_PENALTY_BEFORE_IF_EXPR'] = 30
351  style['SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT'] = 30
352  style['SPLIT_BEFORE_LOGICAL_OPERATOR'] = False
353  style['SPLIT_BEFORE_BITWISE_OPERATOR'] = False
354  return style
355
356
357_STYLE_NAME_TO_FACTORY = dict(
358    pep8=CreatePEP8Style,
359    chromium=CreateChromiumStyle,
360    google=CreateGoogleStyle,
361    facebook=CreateFacebookStyle,
362)
363
364_DEFAULT_STYLE_TO_FACTORY = [
365    (CreateChromiumStyle(), CreateChromiumStyle),
366    (CreateFacebookStyle(), CreateFacebookStyle),
367    (CreateGoogleStyle(), CreateGoogleStyle),
368    (CreatePEP8Style(), CreatePEP8Style),
369]
370
371
372def _GetStyleFactory(style):
373  for def_style, factory in _DEFAULT_STYLE_TO_FACTORY:
374    if style == def_style:
375      return factory
376  return None
377
378
379def _ContinuationAlignStyleStringConverter(s):
380  """Option value converter for a continuation align style string."""
381  accepted_styles = ('SPACE', 'FIXED', 'VALIGN-RIGHT')
382  if s:
383    r = s.upper()
384    if r not in accepted_styles:
385      raise ValueError('unknown continuation align style: %r' % (s,))
386  else:
387    r = accepted_styles[0]
388  return r
389
390
391def _StringListConverter(s):
392  """Option value converter for a comma-separated list of strings."""
393  return [part.strip() for part in s.split(',')]
394
395
396def _StringSetConverter(s):
397  """Option value converter for a comma-separated set of strings."""
398  return set(part.strip() for part in s.split(','))
399
400
401def _BoolConverter(s):
402  """Option value converter for a boolean."""
403  return py3compat.CONFIGPARSER_BOOLEAN_STATES[s.lower()]
404
405
406# Different style options need to have their values interpreted differently when
407# read from the config file. This dict maps an option name to a "converter"
408# function that accepts the string read for the option's value from the file and
409# returns it wrapper in actual Python type that's going to be meaningful to
410# yapf.
411#
412# Note: this dict has to map all the supported style options.
413_STYLE_OPTION_VALUE_CONVERTER = dict(
414    ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=_BoolConverter,
415    ALLOW_MULTILINE_LAMBDAS=_BoolConverter,
416    ALLOW_MULTILINE_DICTIONARY_KEYS=_BoolConverter,
417    ALLOW_SPLIT_BEFORE_DICT_VALUE=_BoolConverter,
418    BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=_BoolConverter,
419    BLANK_LINE_BEFORE_CLASS_DOCSTRING=_BoolConverter,
420    BLANK_LINE_BEFORE_MODULE_DOCSTRING=_BoolConverter,
421    BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=int,
422    COALESCE_BRACKETS=_BoolConverter,
423    COLUMN_LIMIT=int,
424    CONTINUATION_ALIGN_STYLE=_ContinuationAlignStyleStringConverter,
425    CONTINUATION_INDENT_WIDTH=int,
426    DEDENT_CLOSING_BRACKETS=_BoolConverter,
427    DISABLE_ENDING_COMMA_HEURISTIC=_BoolConverter,
428    EACH_DICT_ENTRY_ON_SEPARATE_LINE=_BoolConverter,
429    I18N_COMMENT=str,
430    I18N_FUNCTION_CALL=_StringListConverter,
431    INDENT_DICTIONARY_VALUE=_BoolConverter,
432    INDENT_WIDTH=int,
433    JOIN_MULTIPLE_LINES=_BoolConverter,
434    NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=_StringSetConverter,
435    SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=_BoolConverter,
436    SPACES_AROUND_POWER_OPERATOR=_BoolConverter,
437    SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=_BoolConverter,
438    SPACES_BEFORE_COMMENT=int,
439    SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=_BoolConverter,
440    SPLIT_ALL_COMMA_SEPARATED_VALUES=_BoolConverter,
441    SPLIT_BEFORE_BITWISE_OPERATOR=_BoolConverter,
442    SPLIT_BEFORE_CLOSING_BRACKET=_BoolConverter,
443    SPLIT_BEFORE_DICT_SET_GENERATOR=_BoolConverter,
444    SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN=_BoolConverter,
445    SPLIT_BEFORE_FIRST_ARGUMENT=_BoolConverter,
446    SPLIT_BEFORE_LOGICAL_OPERATOR=_BoolConverter,
447    SPLIT_BEFORE_NAMED_ASSIGNS=_BoolConverter,
448    SPLIT_COMPLEX_COMPREHENSION=_BoolConverter,
449    SPLIT_PENALTY_AFTER_OPENING_BRACKET=int,
450    SPLIT_PENALTY_AFTER_UNARY_OPERATOR=int,
451    SPLIT_PENALTY_BEFORE_IF_EXPR=int,
452    SPLIT_PENALTY_BITWISE_OPERATOR=int,
453    SPLIT_PENALTY_COMPREHENSION=int,
454    SPLIT_PENALTY_EXCESS_CHARACTER=int,
455    SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=int,
456    SPLIT_PENALTY_IMPORT_NAMES=int,
457    SPLIT_PENALTY_LOGICAL_OPERATOR=int,
458    USE_TABS=_BoolConverter,
459)
460
461
462def CreateStyleFromConfig(style_config):
463  """Create a style dict from the given config.
464
465  Arguments:
466    style_config: either a style name or a file name. The file is expected to
467      contain settings. It can have a special BASED_ON_STYLE setting naming the
468      style which it derives from. If no such setting is found, it derives from
469      the default style. When style_config is None, the _GLOBAL_STYLE_FACTORY
470      config is created.
471
472  Returns:
473    A style dict.
474
475  Raises:
476    StyleConfigError: if an unknown style option was encountered.
477  """
478
479  def GlobalStyles():
480    for style, _ in _DEFAULT_STYLE_TO_FACTORY:
481      yield style
482
483  def_style = False
484  if style_config is None:
485    for style in GlobalStyles():
486      if _style == style:
487        def_style = True
488        break
489    if not def_style:
490      return _style
491    return _GLOBAL_STYLE_FACTORY()
492  if isinstance(style_config, dict):
493    config = _CreateConfigParserFromConfigDict(style_config)
494  elif isinstance(style_config, py3compat.basestring):
495    style_factory = _STYLE_NAME_TO_FACTORY.get(style_config.lower())
496    if style_factory is not None:
497      return style_factory()
498    if style_config.startswith('{'):
499      # Most likely a style specification from the command line.
500      config = _CreateConfigParserFromConfigString(style_config)
501    else:
502      # Unknown config name: assume it's a file name then.
503      config = _CreateConfigParserFromConfigFile(style_config)
504  return _CreateStyleFromConfigParser(config)
505
506
507def _CreateConfigParserFromConfigDict(config_dict):
508  config = py3compat.ConfigParser()
509  config.add_section('style')
510  for key, value in config_dict.items():
511    config.set('style', key, str(value))
512  return config
513
514
515def _CreateConfigParserFromConfigString(config_string):
516  """Given a config string from the command line, return a config parser."""
517  if config_string[0] != '{' or config_string[-1] != '}':
518    raise StyleConfigError(
519        "Invalid style dict syntax: '{}'.".format(config_string))
520  config = py3compat.ConfigParser()
521  config.add_section('style')
522  for key, value in re.findall(r'([a-zA-Z0-9_]+)\s*[:=]\s*([a-zA-Z0-9_]+)',
523                               config_string):
524    config.set('style', key, value)
525  return config
526
527
528def _CreateConfigParserFromConfigFile(config_filename):
529  """Read the file and return a ConfigParser object."""
530  if not os.path.exists(config_filename):
531    # Provide a more meaningful error here.
532    raise StyleConfigError(
533        '"{0}" is not a valid style or file path'.format(config_filename))
534  with open(config_filename) as style_file:
535    config = py3compat.ConfigParser()
536    config.read_file(style_file)
537    if config_filename.endswith(SETUP_CONFIG):
538      if not config.has_section('yapf'):
539        raise StyleConfigError(
540            'Unable to find section [yapf] in {0}'.format(config_filename))
541    elif config_filename.endswith(LOCAL_STYLE):
542      if not config.has_section('style'):
543        raise StyleConfigError(
544            'Unable to find section [style] in {0}'.format(config_filename))
545    else:
546      if not config.has_section('style'):
547        raise StyleConfigError(
548            'Unable to find section [style] in {0}'.format(config_filename))
549    return config
550
551
552def _CreateStyleFromConfigParser(config):
553  """Create a style dict from a configuration file.
554
555  Arguments:
556    config: a ConfigParser object.
557
558  Returns:
559    A style dict.
560
561  Raises:
562    StyleConfigError: if an unknown style option was encountered.
563  """
564  # Initialize the base style.
565  section = 'yapf' if config.has_section('yapf') else 'style'
566  if config.has_option('style', 'based_on_style'):
567    based_on = config.get('style', 'based_on_style').lower()
568    base_style = _STYLE_NAME_TO_FACTORY[based_on]()
569  elif config.has_option('yapf', 'based_on_style'):
570    based_on = config.get('yapf', 'based_on_style').lower()
571    base_style = _STYLE_NAME_TO_FACTORY[based_on]()
572  else:
573    base_style = _GLOBAL_STYLE_FACTORY()
574
575  # Read all options specified in the file and update the style.
576  for option, value in config.items(section):
577    if option.lower() == 'based_on_style':
578      # Now skip this one - we've already handled it and it's not one of the
579      # recognized style options.
580      continue
581    option = option.upper()
582    if option not in _STYLE_OPTION_VALUE_CONVERTER:
583      raise StyleConfigError('Unknown style option "{0}"'.format(option))
584    try:
585      base_style[option] = _STYLE_OPTION_VALUE_CONVERTER[option](value)
586    except ValueError:
587      raise StyleConfigError("'{}' is not a valid setting for {}.".format(
588          value, option))
589  return base_style
590
591
592# The default style - used if yapf is not invoked without specifically
593# requesting a formatting style.
594DEFAULT_STYLE = 'pep8'
595DEFAULT_STYLE_FACTORY = CreatePEP8Style
596_GLOBAL_STYLE_FACTORY = CreatePEP8Style
597
598# The name of the file to use for global style definition.
599GLOBAL_STYLE = (
600    os.path.join(
601        os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'), 'yapf',
602        'style'))
603
604# The name of the file to use for directory-local style definition.
605LOCAL_STYLE = '.style.yapf'
606
607# Alternative place for directory-local style definition. Style should be
608# specified in the '[yapf]' section.
609SETUP_CONFIG = 'setup.cfg'
610
611# TODO(eliben): For now we're preserving the global presence of a style dict.
612# Refactor this so that the style is passed around through yapf rather than
613# being global.
614_style = None
615SetGlobalStyle(_GLOBAL_STYLE_FACTORY())
616