Git Product home page Git Product logo

yapf's Introduction

YAPF

PyPI Version Build Status Actions Status Coverage Status

Introduction

YAPF is a Python formatter based on clang-format (developed by Daniel Jasper). In essence, the algorithm takes the code and calculates the best formatting that conforms to the configured style. It takes away a lot of the drudgery of maintaining your code.

The ultimate goal is that the code YAPF produces is as good as the code that a programmer would write if they were following the style guide.

Note YAPF is not an official Google product (experimental or otherwise), it is just code that happens to be owned by Google.

Installation

To install YAPF from PyPI:

$ pip install yapf

YAPF is still considered in "beta" stage, and the released version may change often; therefore, the best way to keep up-to-date with the latest development is to clone this repository or install directly from github:

$ pip install git+https://github.com/google/yapf.git

Note that if you intend to use YAPF as a command-line tool rather than as a library, installation is not necessary. YAPF supports being run as a directory by the Python interpreter. If you cloned/unzipped YAPF into DIR, it's possible to run:

$ PYTHONPATH=DIR python DIR/yapf [options] ...

Using YAPF within your favorite editor

YAPF is supported by multiple editors via community extensions or plugins. See Editor Support for more info.

Required Python versions

YAPF supports Python 3.7+.

Usage

usage: yapf [-h] [-v] [-d | -i | -q] [-r | -l START-END] [-e PATTERN]
            [--style STYLE] [--style-help] [--no-local-style] [-p] [-m] [-vv]
            [files ...]

Formatter for Python code.

positional arguments:
  files                 reads from stdin when no files are specified.

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -d, --diff            print the diff for the fixed source
  -i, --in-place        make changes to files in place
  -q, --quiet           output nothing and set return value
  -r, --recursive       run recursively over directories
  -l START-END, --lines START-END
                        range of lines to reformat, one-based
  -e PATTERN, --exclude PATTERN
                        patterns for files to exclude from formatting
  --style STYLE         specify formatting style: either a style name (for
                        example "pep8" or "google"), or the name of a file
                        with style settings. The default is pep8 unless a
                        .style.yapf or setup.cfg or pyproject.toml file
                        located in the same directory as the source or one of
                        its parent directories (for stdin, the current
                        directory is used).
  --style-help          show style settings and exit; this output can be saved
                        to .style.yapf to make your settings permanent
  --no-local-style      don't search for local style definition
  -p, --parallel        run YAPF in parallel when formatting multiple files.
  -m, --print-modified  print out file names of modified files
  -vv, --verbose        print out file names while processing

Return Codes

Normally YAPF returns zero on successful program termination and non-zero otherwise.

If --diff is supplied, YAPF returns zero when no changes were necessary, non-zero otherwise (including program error). You can use this in a CI workflow to test that code has been YAPF-formatted.

Excluding files from formatting (.yapfignore or pyproject.toml)

In addition to exclude patterns provided on commandline, YAPF looks for additional patterns specified in a file named .yapfignore or pyproject.toml located in the working directory from which YAPF is invoked.

.yapfignore's syntax is similar to UNIX's filename pattern matching:

*       matches everything
?       matches any single character
[seq]   matches any character in seq
[!seq]  matches any character not in seq

Note that no entry should begin with ./.

If you use pyproject.toml, exclude patterns are specified by ignore_patterns key in [tool.yapfignore] section. For example:

[tool.yapfignore]
ignore_patterns = [
  "temp/**/*.py",
  "temp2/*.py"
]

Formatting style

The formatting style used by YAPF is configurable and there are many "knobs" that can be used to tune how YAPF does formatting. See the style.py module for the full list.

To control the style, run YAPF with the --style argument. It accepts one of the predefined styles (e.g., pep8 or google), a path to a configuration file that specifies the desired style, or a dictionary of key/value pairs.

The config file is a simple listing of (case-insensitive) key = value pairs with a [style] heading. For example:

[style]
based_on_style = pep8
spaces_before_comment = 4
split_before_logical_operator = true

The based_on_style setting determines which of the predefined styles this custom style is based on (think of it like subclassing). Four styles are predefined:

  • pep8 (default)
  • google (based off of the Google Python Style Guide)
  • yapf (for use with Google open source projects)
  • facebook

See _STYLE_NAME_TO_FACTORY in style.py for details.

It's also possible to do the same on the command line with a dictionary. For example:

--style='{based_on_style: pep8, indent_width: 2}'

This will take the pep8 base style and modify it to have two space indentations.

YAPF will search for the formatting style in the following manner:

  1. Specified on the command line
  2. In the [style] section of a .style.yapf file in either the current directory or one of its parent directories.
  3. In the [yapf] section of a setup.cfg file in either the current directory or one of its parent directories.
  4. In the [tool.yapf] section of a pyproject.toml file in either the current directory or one of its parent directories.
  5. In the [style] section of a ~/.config/yapf/style file in your home directory.

If none of those files are found, the default style PEP8 is used.

Example

An example of the type of formatting that YAPF can do, it will take this ugly code:

x = {  'a':37,'b':42,

'c':927}

y = 'hello ''world'
z = 'hello '+'world'
a = 'hello {}'.format('world')
class foo  (     object  ):
  def f    (self   ):
    return       37*-+2
  def g(self, x,y=42):
      return y
def f  (   a ) :
  return      37+-+a[42-x :  y**3]

and reformat it into:

x = {'a': 37, 'b': 42, 'c': 927}

y = 'hello ' 'world'
z = 'hello ' + 'world'
a = 'hello {}'.format('world')


class foo(object):
    def f(self):
        return 37 * -+2

    def g(self, x, y=42):
        return y


def f(a):
    return 37 + -+a[42 - x:y**3]

Example as a module

The two main APIs for calling YAPF are FormatCode and FormatFile, these share several arguments which are described below:

>>> from yapf.yapflib.yapf_api import FormatCode  # reformat a string of code

>>> formatted_code, changed = FormatCode("f ( a = 1, b = 2 )")
>>> formatted_code
'f(a=1, b=2)\n'
>>> changed
True

A style_config argument: Either a style name or a path to a file that contains formatting style settings. If None is specified, use the default style as set in style.DEFAULT_STYLE_FACTORY.

>>> FormatCode("def g():\n  return True", style_config='pep8')[0]
'def g():\n    return True\n'

A lines argument: A list of tuples of lines (ints), [start, end], that we want to format. The lines are 1-based indexed. It can be used by third-party code (e.g., IDEs) when reformatting a snippet of code rather than a whole file.

>>> FormatCode("def g( ):\n    a=1\n    b = 2\n    return a==b", lines=[(1, 1), (2, 3)])[0]
'def g():\n    a = 1\n    b = 2\n    return a==b\n'

A print_diff (bool): Instead of returning the reformatted source, return a diff that turns the formatted source into reformatted source.

>>> print(FormatCode("a==b", filename="foo.py", print_diff=True)[0])
--- foo.py (original)
+++ foo.py (reformatted)
@@ -1 +1 @@
-a==b
+a == b

Note: the filename argument for FormatCode is what is inserted into the diff, the default is <unknown>.

FormatFile returns reformatted code from the passed file along with its encoding:

>>> from yapf.yapflib.yapf_api import FormatFile  # reformat a file

>>> print(open("foo.py").read())  # contents of file
a==b

>>> reformatted_code, encoding, changed = FormatFile("foo.py")
>>> formatted_code
'a == b\n'
>>> encoding
'utf-8'
>>> changed
True

The in_place argument saves the reformatted code back to the file:

>>> FormatFile("foo.py", in_place=True)[:2]
(None, 'utf-8')

>>> print(open("foo.py").read())  # contents of file (now fixed)
a == b

Formatting diffs

Options:

usage: yapf-diff [-h] [-i] [-p NUM] [--regex PATTERN] [--iregex PATTERN][-v]
                 [--style STYLE] [--binary BINARY]

This script reads input from a unified diff and reformats all the changed
lines. This is useful to reformat all the lines touched by a specific patch.
Example usage for git/svn users:

  git diff -U0 --no-color --relative HEAD^ | yapf-diff -i
  svn diff --diff-cmd=diff -x-U0 | yapf-diff -p0 -i

It should be noted that the filename contained in the diff is used
unmodified to determine the source file to update. Users calling this script
directly should be careful to ensure that the path in the diff is correct
relative to the current working directory.

optional arguments:
  -h, --help            show this help message and exit
  -i, --in-place        apply edits to files instead of displaying a diff
  -p NUM, --prefix NUM  strip the smallest prefix containing P slashes
  --regex PATTERN       custom pattern selecting file paths to reformat
                        (case sensitive, overrides -iregex)
  --iregex PATTERN      custom pattern selecting file paths to reformat
                        (case insensitive, overridden by -regex)
  -v, --verbose         be more verbose, ineffective without -i
  --style STYLE         specify formatting style: either a style name (for
                        example "pep8" or "google"), or the name of a file
                        with style settings. The default is pep8 unless a
                        .style.yapf or setup.cfg or pyproject.toml file
                        located in the same directory as the source or one of
                        its parent directories (for stdin, the current
                        directory is used).
  --binary BINARY       location of binary to use for YAPF

Python features not yet supported

Knobs

ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT

Align closing bracket with visual indentation.

ALLOW_MULTILINE_LAMBDAS

Allow lambdas to be formatted on more than one line.

ALLOW_MULTILINE_DICTIONARY_KEYS

Allow dictionary keys to exist on multiple lines. For example:

    x = {
        ('this is the first element of a tuple',
         'this is the second element of a tuple'):
             value,
    }

ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS

Allow splitting before a default / named assignment in an argument list.

ALLOW_SPLIT_BEFORE_DICT_VALUE

Allow splits before the dictionary value.

ARITHMETIC_PRECEDENCE_INDICATION

Let spacing indicate operator precedence. For example:

    a = 1 * 2 + 3 / 4
    b = 1 / 2 - 3 * 4
    c = (1 + 2) * (3 - 4)
    d = (1 - 2) / (3 + 4)
    e = 1 * 2 - 3
    f = 1 + 2 + 3 + 4

will be formatted as follows to indicate precedence:

    a = 1*2 + 3/4
    b = 1/2 - 3*4
    c = (1+2) * (3-4)
    d = (1-2) / (3+4)
    e = 1*2 - 3
    f = 1 + 2 + 3 + 4

BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION

Sets the number of desired blank lines surrounding top-level function and class definitions. For example:

    class Foo:
        pass
                       # <------ having two blank lines here
                       # <------ is the default setting
    class Bar:
        pass

BLANK_LINE_BEFORE_CLASS_DOCSTRING

Insert a blank line before a class-level docstring.

BLANK_LINE_BEFORE_MODULE_DOCSTRING

Insert a blank line before a module docstring.

BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF

Insert a blank line before a def or class immediately nested within another def or class. For example:

    class Foo:
                       # <------ this blank line
        def method():
            pass

BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES

Sets the number of desired blank lines between top-level imports and variable definitions. Useful for compatibility with tools like isort.

COALESCE_BRACKETS

Do not split consecutive brackets. Only relevant when DEDENT_CLOSING_BRACKETS or INDENT_CLOSING_BRACKETS is set. For example:

    call_func_that_takes_a_dict(
        {
            'key1': 'value1',
            'key2': 'value2',
        }
    )

would reformat to:

    call_func_that_takes_a_dict({
        'key1': 'value1',
        'key2': 'value2',
    })

COLUMN_LIMIT

The column limit (or max line-length)

CONTINUATION_ALIGN_STYLE

The style for continuation alignment. Possible values are:

  • SPACE: Use spaces for continuation alignment. This is default behavior.
  • FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns (i.e. CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs or CONTINUATION_INDENT_WIDTH spaces) for continuation alignment.
  • VALIGN-RIGHT: Vertically align continuation lines to multiple of INDENT_WIDTH columns. Slightly right (one tab or a few spaces) if cannot vertically align continuation lines with indent characters.

CONTINUATION_INDENT_WIDTH

Indent width used for line continuations.

DEDENT_CLOSING_BRACKETS

Put closing brackets on a separate line, dedented, if the bracketed expression can't fit in a single line. Applies to all kinds of brackets, including function definitions and calls. For example:

    config = {
        'key1': 'value1',
        'key2': 'value2',
    }  # <--- this bracket is dedented and on a separate line

    time_series = self.remote_client.query_entity_counters(
        entity='dev3246.region1',
        key='dns.query_latency_tcp',
        transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
        start_ts=now()-timedelta(days=3),
        end_ts=now(),
    )  # <--- this bracket is dedented and on a separate line

DISABLE_ENDING_COMMA_HEURISTIC

Disable the heuristic which places each list element on a separate line if the list is comma-terminated.

Note: The behavior of this flag changed in v0.40.3. Before, if this flag was true, we would split lists that contained a trailing comma or a comment. Now, we have a separate flag, DISABLE_SPLIT_LIST_WITH_COMMENT, that controls splitting when a list contains a comment. To get the old behavior, set both flags to true. More information in CHANGELOG.md.

DISABLE_SPLIT_LIST_WITH_COMMENT

Don't put every element on a new line within a list that contains interstitial comments.

Without this flag (default):

[
  a,
  b,  #
  c
]

With this flag:

[
  a, b,  #
  c
]

This mirrors the behavior of clang-format and is useful for forming "logical groups" of elements in a list. It also works in function declarations.

EACH_DICT_ENTRY_ON_SEPARATE_LINE

Place each dictionary entry onto its own line.

FORCE_MULTILINE_DICT

Respect EACH_DICT_ENTRY_ON_SEPARATE_LINE even if the line is shorter than COLUMN_LIMIT.

I18N_COMMENT

The regex for an internationalization comment. The presence of this comment stops reformatting of that line, because the comments are required to be next to the string they translate.

I18N_FUNCTION_CALL

The internationalization function call names. The presence of this function stops reformatting on that line, because the string it has cannot be moved away from the i18n comment.

INDENT_BLANK_LINES

Set to True to prefer indented blank lines rather than empty

INDENT_CLOSING_BRACKETS

Put closing brackets on a separate line, indented, if the bracketed expression can't fit in a single line. Applies to all kinds of brackets, including function definitions and calls. For example:

    config = {
        'key1': 'value1',
        'key2': 'value2',
        }  # <--- this bracket is indented and on a separate line

    time_series = self.remote_client.query_entity_counters(
        entity='dev3246.region1',
        key='dns.query_latency_tcp',
        transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
        start_ts=now()-timedelta(days=3),
        end_ts=now(),
        )  # <--- this bracket is indented and on a separate line

INDENT_DICTIONARY_VALUE

Indent the dictionary value if it cannot fit on the same line as the dictionary key. For example:

    config = {
        'key1':
            'value1',
        'key2': value1 +
                value2,
    }

INDENT_WIDTH

The number of columns to use for indentation.

JOIN_MULTIPLE_LINES

Join short lines into one line. E.g., single line if statements.

NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS

Do not include spaces around selected binary operators. For example:

    1 + 2 * 3 - 4 / 5

will be formatted as follows when configured with *, /:

    1 + 2*3 - 4/5

SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET

Insert a space between the ending comma and closing bracket of a list, etc.

SPACE_INSIDE_BRACKETS

Use spaces inside brackets, braces, and parentheses.  For example:
        method_call( 1 )
        my_dict[ 3 ][ 1 ][ get_index( *args, **kwargs ) ]
        my_set = { 1, 2, 3 }

SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN

Set to True to prefer spaces around the assignment operator for default or keyword arguments.

SPACES_AROUND_DICT_DELIMITERS

Adds a space after the opening '{' and before the ending '}' dict delimiters.

        {1: 2}

will be formatted as:

        { 1: 2 }

SPACES_AROUND_LIST_DELIMITERS

Adds a space after the opening '[' and before the ending ']' list delimiters.

    [1, 2]

will be formatted as:

    [ 1, 2 ]

SPACES_AROUND_POWER_OPERATOR

Set to True to prefer using spaces around **.

SPACES_AROUND_SUBSCRIPT_COLON

Use spaces around the subscript / slice operator. For example:

    my_list[1 : 10 : 2]
SPACES_AROUND_TUPLE_DELIMITERS

Adds a space after the opening '(' and before the ending ')' tuple delimiters.

    (1, 2, 3)

will be formatted as:

    ( 1, 2, 3 )

SPACES_BEFORE_COMMENT

The number of spaces required before a trailing comment. This can be a single value (representing the number of spaces before each trailing comment) or list of values (representing alignment column values; trailing comments within a block will be aligned to the first column value that is greater than the maximum line length within the block).

Note: Lists of values may need to be quoted in some contexts (eg. shells or editor config files).

For example, with spaces_before_comment=5:

    1 + 1 # Adding values

will be formatted as:

    1 + 1     # Adding values <-- 5 spaces between the end of the statement and comment

with spaces_before_comment="15, 20":

    1 + 1 # Adding values
    two + two # More adding

    longer_statement # This is a longer statement
    short # This is a shorter statement

    a_very_long_statement_that_extends_beyond_the_final_column # Comment
    short # This is a shorter statement

will be formatted as:

    1 + 1          # Adding values <-- end of line comments in block aligned to col 15
    two + two      # More adding

    longer_statement    # This is a longer statement <-- end of line comments in block aligned to col 20
    short               # This is a shorter statement

    a_very_long_statement_that_extends_beyond_the_final_column  # Comment <-- the end of line comments are aligned based on the line length
    short                                                       # This is a shorter statement

SPLIT_ALL_COMMA_SEPARATED_VALUES

If a comma separated list (dict, list, tuple, or function def) is on a line that is too long, split such that each element is on a separate line.

SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES

Variation on SPLIT_ALL_COMMA_SEPARATED_VALUES in which, if a subexpression with a comma fits in its starting line, then the subexpression is not split. This avoids splits like the one for b in this code:

    abcdef(
        aReallyLongThing: int,
        b: [Int,
            Int])

with the new knob this is split as:

    abcdef(
        aReallyLongThing: int,
        b: [Int, Int])

SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED

Split before arguments if the argument list is terminated by a comma.

SPLIT_BEFORE_ARITHMETIC_OPERATOR

Set to True to prefer splitting before +, -, *, /, //, or @ rather than after.

SPLIT_BEFORE_BITWISE_OPERATOR

Set to True to prefer splitting before &, | or ^ rather than after.

SPLIT_BEFORE_CLOSING_BRACKET

Split before the closing bracket if a list or dict literal doesn't fit on a single line.

SPLIT_BEFORE_DICT_SET_GENERATOR

Split before a dictionary or set generator (comp_for). For example, note the split before the for:

    foo = {
        variable: 'Hello world, have a nice day!'
        for variable in bar if variable != 42
    }

SPLIT_BEFORE_DOT

Split before the . if we need to split a longer expression:

    foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d))

would reformat to something like:

    foo = ('This is a really long string: {}, {}, {}, {}'
           .format(a, b, c, d))

SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN

Split after the opening paren which surrounds an expression if it doesn't fit on a single line.

SPLIT_BEFORE_FIRST_ARGUMENT

If an argument / parameter list is going to be split, then split before the first argument.

SPLIT_BEFORE_LOGICAL_OPERATOR

Set to True to prefer splitting before and or or rather than after.

SPLIT_BEFORE_NAMED_ASSIGNS

Split named assignments onto individual lines.

SPLIT_COMPLEX_COMPREHENSION

For list comprehensions and generator expressions with multiple clauses (e.g multiple for calls, if filter expressions) and which need to be reflowed, split each clause onto its own line. For example:

    result = [
        a_var + b_var for a_var in xrange(1000) for b_var in xrange(1000)
        if a_var % b_var]

would reformat to something like:

    result = [
        a_var + b_var
        for a_var in xrange(1000)
        for b_var in xrange(1000)
        if a_var % b_var]

SPLIT_PENALTY_AFTER_OPENING_BRACKET

The penalty for splitting right after the opening bracket.

SPLIT_PENALTY_AFTER_UNARY_OPERATOR

The penalty for splitting the line after a unary operator.

SPLIT_PENALTY_ARITHMETIC_OPERATOR

The penalty of splitting the line around the +, -, *, /, //, %, and @ operators.

SPLIT_PENALTY_BEFORE_IF_EXPR

The penalty for splitting right before an if expression.

SPLIT_PENALTY_BITWISE_OPERATOR

The penalty of splitting the line around the &, |, and ^ operators.

SPLIT_PENALTY_COMPREHENSION

The penalty for splitting a list comprehension or generator expression.

SPLIT_PENALTY_EXCESS_CHARACTER

The penalty for characters over the column limit.

SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT

The penalty incurred by adding a line split to the logical line. The more line splits added the higher the penalty.

SPLIT_PENALTY_IMPORT_NAMES

The penalty of splitting a list of import as names. For example:

    from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
                                                              long_argument_2,
                                                              long_argument_3)

would reformat to something like:

    from a_very_long_or_indented_module_name_yada_yad import (
        long_argument_1, long_argument_2, long_argument_3)

SPLIT_PENALTY_LOGICAL_OPERATOR

The penalty of splitting the line around the and and or operators.

USE_TABS

Use the Tab character for indentation.

(Potentially) Frequently Asked Questions

Why does YAPF destroy my awesome formatting?

YAPF tries very hard to get the formatting correct. But for some code, it won't be as good as hand-formatting. In particular, large data literals may become horribly disfigured under YAPF.

The reasons for this are manyfold. In short, YAPF is simply a tool to help with development. It will format things to coincide with the style guide, but that may not equate with readability.

What can be done to alleviate this situation is to indicate regions YAPF should ignore when reformatting something:

# yapf: disable
FOO = {
    # ... some very large, complex data literal.
}

BAR = [
    # ... another large data literal.
]
# yapf: enable

You can also disable formatting for a single literal like this:

BAZ = {
    (1, 2, 3, 4),
    (5, 6, 7, 8),
    (9, 10, 11, 12),
}  # yapf: disable

To preserve the nice dedented closing brackets, use the dedent_closing_brackets in your style. Note that in this case all brackets, including function definitions and calls, are going to use that style. This provides consistency across the formatted codebase.

Why Not Improve Existing Tools?

We wanted to use clang-format's reformatting algorithm. It's very powerful and designed to come up with the best formatting possible. Existing tools were created with different goals in mind, and would require extensive modifications to convert to using clang-format's algorithm.

Can I Use YAPF In My Program?

Please do! YAPF was designed to be used as a library as well as a command line tool. This means that a tool or IDE plugin is free to use YAPF.

I still get non-PEP8 compliant code! Why?

YAPF tries very hard to be fully PEP 8 compliant. However, it is paramount to not risk altering the semantics of your code. Thus, YAPF tries to be as safe as possible and does not change the token stream (e.g., by adding parentheses). All these cases however, can be easily fixed manually. For instance,

from my_package import my_function_1, my_function_2, my_function_3, my_function_4, my_function_5

FOO = my_variable_1 + my_variable_2 + my_variable_3 + my_variable_4 + my_variable_5 + my_variable_6 + my_variable_7 + my_variable_8

won't be split, but you can easily get it right by just adding parentheses:

from my_package import (my_function_1, my_function_2, my_function_3,
                        my_function_4, my_function_5)

FOO = (my_variable_1 + my_variable_2 + my_variable_3 + my_variable_4 +
       my_variable_5 + my_variable_6 + my_variable_7 + my_variable_8)

Gory Details

Algorithm Design

The main data structure in YAPF is the LogicalLine object. It holds a list of FormatToken\s, that we would want to place on a single line if there were no column limit. An exception being a comment in the middle of an expression statement will force the line to be formatted on more than one line. The formatter works on one LogicalLine object at a time.

An LogicalLine typically won't affect the formatting of lines before or after it. There is a part of the algorithm that may join two or more LogicalLine\s into one line. For instance, an if-then statement with a short body can be placed on a single line:

if a == 42: continue

YAPF's formatting algorithm creates a weighted tree that acts as the solution space for the algorithm. Each node in the tree represents the result of a formatting decision --- i.e., whether to split or not to split before a token. Each formatting decision has a cost associated with it. Therefore, the cost is realized on the edge between two nodes. (In reality, the weighted tree doesn't have separate edge objects, so the cost resides on the nodes themselves.)

For example, take the following Python code snippet. For the sake of this example, assume that line (1) violates the column limit restriction and needs to be reformatted.

def xxxxxxxxxxx(aaaaaaaaaaaa, bbbbbbbbb, cccccccc, dddddddd, eeeeee):  # 1
    pass                                                               # 2

For line (1), the algorithm will build a tree where each node (a FormattingDecisionState object) is the state of the line at that token given the decision to split before the token or not. Note: the FormatDecisionState objects are copied by value so each node in the graph is unique and a change in one doesn't affect other nodes.

Heuristics are used to determine the costs of splitting or not splitting. Because a node holds the state of the tree up to a token's insertion, it can easily determine if a splitting decision will violate one of the style requirements. For instance, the heuristic is able to apply an extra penalty to the edge when not splitting between the previous token and the one being added.

There are some instances where we will never want to split the line, because doing so will always be detrimental (i.e., it will require a backslash-newline, which is very rarely desirable). For line (1), we will never want to split the first three tokens: def, xxxxxxxxxxx, and (. Nor will we want to split between the ) and the : at the end. These regions are said to be "unbreakable." This is reflected in the tree by there not being a "split" decision (left hand branch) within the unbreakable region.

Now that we have the tree, we determine what the "best" formatting is by finding the path through the tree with the lowest cost.

And that's it!

yapf's People

Contributors

alanhdu avatar alexey-pelykh avatar b5y avatar bouweandela avatar brianmego avatar bwendling avatar campos-ddc avatar cardenb avatar davidbrownell avatar dependabot[bot] avatar driax avatar eliben avatar ericboyangan avatar ethanfurman avatar github-actions[bot] avatar harahu avatar hartwork avatar hayd avatar hugo-ricateau avatar isanbard avatar joachimmetz avatar monosans avatar msuozzo avatar ppolewicz avatar sbc100 avatar spitfire1900 avatar thiagowfx avatar wade-arista avatar xoox avatar yinyin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yapf's Issues

yapf will line break right before the closing bracket of a tuple

This is somewhat of an aesthetic preference, so obviously feel free to close this as notabugwontfix, but the following code:

def aaaaaaaaaaa(*args):
    pass


bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = aaaaaaaaaaa(
    ("...", "."), "..",
    ".............................................."
)

is reformatted as

def aaaaaaaaaaa(*args):
    pass


bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = aaaaaaaaaaa(("...", "."
    ), "..", "..............................................")

Having the tuple argument split over the line like that with the closing bracket on the next line looks really bad. Note that putting the closing bracket on the same line as the opening bracket would not even come close to the 80 character column limit.

Please don't recommend 'sudo python setup.py install'

Installing packages system-wide as root is problematic for several reasons. (Clobbering or shadowing system versions of the module, system packages clobbering or shadowing user-installed modules, version management issues.)

Instead of recommending 'sudo', recommend users use 'python setup.py install --user' -- or better yet, a virtualenv + pip.

Integration as a http://pre-commit.com/ hook

This isn't really such a bug report as more of an inquisition about how to approach this.

As requested here: pre-commit/pre-commit-hooks#50 we've had some interest in integrating yapf with our pre-commit framework.

There's two basic approaches to doing this so I'd like to get your opinions on this (I'd like to contribute to making either of them happen):

  1. Provide the proper metadata in google/yapf for running hooks (basically involves checking in a file similar to this: https://github.com/pre-commit/pre-commit-hooks/blob/master/hooks.yaml (except of course it'd just list yapf)).
  2. Provide library mechanisms to fix files and we'd write a wrapper around yapf in one of our repositories.

Option 1 is ideal for us as we don't have to deal with dependencies, breaking apis, etc. especially when the project is in an early stage and potentially volatile (and I don't think yapf is on pypi yet?)

Either way, the general interface for a pre-commit hook to work with our framework is as follows:

  • Installable in an isolated environment
  • Exposes an executable which:
    • Takes a list of filenames to check / fix (in the case of yapf, either)
    • Returns 0 when everything is A-OK or no changes are made, and nonzero otherwise

I'm not sure how much of that already happens but I'd be willing to pitch in to make it work (and test it to prevent regressions, etc.).

Given those assumptions it'd be as simple as checking in a file named hooks.yaml which has contents probably similar to this:

-   id: yapf
    name: yapf
    description: "Runs yapf 'Yet Another Python Formatter' over python source"
    entry: yapf
    language: python
    files: \.py$
    args: [-i]

Thanks in advance,

Anthony

Add examples

Before installing yapf, I'd usually want to see some examples of what it does. A weirdly formatted file should come out quite nicely, and an already well-formatted file should come out at least just as well-formatted. I suggest adding some sample input/output combinations either to the Readme or a sepeate Examples file.

The examples could just be copied from some of the tests, but it's much more nice to see some stuff without browsing though the code.

yapf will crash if you run it on a file with a byte order mark

In the interests of not having github eat the important details, here is an example file encoded as a python string literal:

"\ufeff# -*- coding: utf-8 -*-\n\nresult = 'passed'\n"

(Yes, I know that a bom in a utf-8 file doesn't make sense. Don't ask me, I just find 'em).

Running on python 3.4.2 with the latest master this will produce the following lib2to3 error:

    Traceback (most recent call last):
      File "/home/david/yapf/yapf/yapflib/pytree_utils.py", line 85, in ParseCodeToTree
        tree = parser_driver.parse_string(code, debug=False)
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/driver.py", line 106, in parse_string
        return self.parse_tokens(tokens, debug)
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/driver.py", line 71, in parse_tokens
        if p.addtoken(type, value, (prefix, start)):
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/parse.py", line 116, in addtoken
        ilabel = self.classify(type, value, context)
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/parse.py", line 172, in classify
        raise ParseError("bad token", type, value, context)
    lib2to3.pgen2.parse.ParseError: bad token: type=56, value='\ufeff', context=('', (1, 0))

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/runpy.py", line 170, in _run_module_as_main
        "__main__", mod_spec)
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/runpy.py", line 85, in _run_code
        exec(code, run_globals)
      File "/home/david/yapf/yapf/__main__.py", line 18, in <module>
        sys.exit(yapf.main(sys.argv))
      File "/home/david/yapf/yapf/__init__.py", line 102, in main
        print_diff=args.diff)
      File "/home/david/yapf/yapf/__init__.py", line 124, in FormatFiles
        filename, style_config=style_config, lines=lines, print_diff=print_diff)
      File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 67, in FormatFile
        print_diff=print_diff)
      File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 88, in FormatCode
        tree = pytree_utils.ParseCodeToTree(unformatted_source.rstrip() + '\n')
      File "/home/david/yapf/yapf/yapflib/pytree_utils.py", line 91, in ParseCodeToTree
        tree = parser_driver.parse_string(code, debug=False)
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/driver.py", line 106, in parse_string
        return self.parse_tokens(tokens, debug)
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/driver.py", line 71, in parse_tokens
        if p.addtoken(type, value, (prefix, start)):
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/parse.py", line 116, in addtoken
        ilabel = self.classify(type, value, context)
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/parse.py", line 172, in classify
        raise ParseError("bad token", type, value, context)
    lib2to3.pgen2.parse.ParseError: bad token: type=56, value='\ufeff', context=('', (1, 0))

Running 2to3 itself on the file works correctly.

yapf: disable disables just enough to generate invalid syntax

The following syntactically valid python:

class SplitPenaltyTest(unittest.TestCase):
  def testUnbreakable(self):
    self._CheckPenalties(tree, [
    ])  # yapf: disable

Is reformatted into the following invalid python:

class SplitPenaltyTest(unittest.TestCase):
    def testUnbreakable(self):
    self._CheckPenalties(tree, [
    ])  # yapf: disable

yapf --style=pep8 sometimes inserts a blank line it shouldn't

This code is pep8 correct, because timeout is a local function definition and thus has only one blank line in front of it:

import signal


try:
    signal.SIGALRM
    # ..................................................................
    # ...............................................................

    def timeout(seconds=1):
        pass
except:
    pass

However yapf reformats it as:

import signal

try:
    signal.SIGALRM

    # ..................................................................
    # ...............................................................


    def timeout(seconds=1):
        pass
except:
    pass

With two blank lines before timeout.

pep8 checker says "E303 too many blank lines (2)"

SyntaxError in yapflib.verifier

The following code:

class YapfTest(unittest.TestCase):
  def testSimple(self):
    unformatted_code = textwrap.dedent(u"""\
        print('foo')
        """)

Gives the following error:

INTERNAL ERROR: 
        unformatted_code = textwrap.dedent(u"""\
        print('foo')
        """)
Traceback (most recent call last):
  File "/home/david/yapf/yapf/yapflib/verifier.py", line 38, in VerifyCode
    compile(textwrap.dedent(code).encode('UTF-8'), '<string>', 'exec')
  File "<string>", line 4
    """)
      ^
SyntaxError: invalid syntax

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/david/yapf/yapf/yapflib/verifier.py", line 41, in VerifyCode
    ast.parse(textwrap.dedent(code.lstrip('\n')).lstrip(), '<string>', 'exec')
  File "/usr/lib/python3.2/ast.py", line 36, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "<string>", line 3
    """)
      ^
SyntaxError: invalid syntax

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.2/runpy.py", line 160, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python3.2/runpy.py", line 73, in _run_code
    exec(code, run_globals)
  File "/home/david/yapf/yapf/__main__.py", line 18, in <module>
    sys.exit(yapf.main(sys.argv))
  File "/home/david/yapf/yapf/__init__.py", line 102, in main
    print_diff=args.diff)
  File "/home/david/yapf/yapf/__init__.py", line 124, in FormatFiles
    filename, style_config=style_config, lines=lines, print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 67, in FormatFile
    print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 110, in FormatCode
    reformatted_source = reformatter.Reformat(uwlines)
  File "/home/david/yapf/yapf/yapflib/reformatter.py", line 73, in Reformat
    verifier.VerifyCode(formatted_code[-1])
  File "/home/david/yapf/yapf/yapflib/verifier.py", line 45, in VerifyCode
    compile(normalized_code.encode('UTF-8'), '<string>', 'exec')
  File "<string>", line 3
    """)
      ^
SyntaxError: invalid syntax

...as you can probably tell, by this point I have a fuzzer doing most of the work for me (about the first half + a few of the subsequent ones have been hand produced) and this is from turning it on the yapf codebase itself. If you want it I'd be happy to give you a copy of the fuzzer.

Broken indentation

yapf seems to break the indentation of Python 3.4's bisect.py. Note that the lines after the else: should be indented one more level than what yapf is currently doing.

$ python -m yapf -d bisect.py
--- bisect.py   (original)
+++ bisect.py   (reformatted)
@@ -1,4 +1,5 @@
 """Bisection algorithms."""
+

 def insort_right(a, x, lo=0, hi=None):
     """Insert item x in list a, and keep it sorted assuming a is sorted.
@@ -14,12 +15,15 @@
     if hi is None:
         hi = len(a)
     while lo < hi:
-        mid = (lo+hi)//2
+        mid = (lo + hi) // 2
         if x < a[mid]: hi = mid
-        else: lo = mid+1
+        else:
+        lo = mid + 1
     a.insert(lo, x)

-insort = insort_right   # backward compatibility
+
+insort = insort_right # backward compatibility
+

 def bisect_right(a, x, lo=0, hi=None):
     """Return the index where to insert item x in list a, assuming a is sorted.
@@ -37,12 +41,15 @@
     if hi is None:
         hi = len(a)
     while lo < hi:
-        mid = (lo+hi)//2
+        mid = (lo + hi) // 2
         if x < a[mid]: hi = mid
-        else: lo = mid+1
+        else:
+        lo = mid + 1
     return lo

-bisect = bisect_right   # backward compatibility
+
+bisect = bisect_right # backward compatibility
+

 def insort_left(a, x, lo=0, hi=None):
     """Insert item x in list a, and keep it sorted assuming a is sorted.
@@ -58,9 +65,10 @@
     if hi is None:
         hi = len(a)
     while lo < hi:
-        mid = (lo+hi)//2
-        if a[mid] < x: lo = mid+1
-        else: hi = mid
+        mid = (lo + hi) // 2
+        if a[mid] < x: lo = mid + 1
+        else:
+        hi = mid
     a.insert(lo, x)


@@ -80,9 +88,10 @@
     if hi is None:
         hi = len(a)
     while lo < hi:
-        mid = (lo+hi)//2
-        if a[mid] < x: lo = mid+1
-        else: hi = mid
+        mid = (lo + hi) // 2
+        if a[mid] < x: lo = mid + 1
+        else:
+        hi = mid
     return lo

 # Overwrite above definitions with a fast C implementation

Here is a minimal example:

if True: pass
else: pass
--- foo.py  (original)
+++ foo.py  (reformatted)
@@ -1,2 +1,3 @@
 if True: pass
-else: pass
+else:
+pass

Similarly:

class Foo(object): pass
--- foo.py  (original)
+++ foo.py  (reformatted)
@@ -1 +1,2 @@
-class Foo(object): pass
+class Foo(object):
+pass

Thanks

TypeError: unorderable types because cur_token.node_split_penalty is None

When running yapf over this code:

class CookieJar(RAMCookieJar):

    def purge_old_cookies(self):
        cookies = [c for c in self.allCookies()
                   if c.isSessionCookie() or c.expirationDate() >= now]

I get:

Traceback (most recent call last):  
  File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main  
    "__main__", mod_spec)  
  File "/usr/lib/python3.4/runpy.py", line 85, in _run_code  
    exec(code, run_globals)  
  File "/home/florian/proj/qutebrowser/.venv/lib/python3.4/site-packages/yapf/__main__.py", line 18, in <module>  
    sys.exit(yapf.main(sys.argv))  
  File "/home/florian/proj/qutebrowser/.venv/lib/python3.4/site-packages/yapf/__init__.py", line 102, in main  
    print_diff=args.diff)  
  File "/home/florian/proj/qutebrowser/.venv/lib/python3.4/site-packages/yapf/__init__.py", line 124, in FormatFiles  
    filename, style_config=style_config, lines=lines, print_diff=print_diff)  
  File "/home/florian/proj/qutebrowser/.venv/lib/python3.4/site-packages/yapf/yapflib/yapf_api.py", line 68, in FormatFile  
    print_diff=print_diff)  
  File "/home/florian/proj/qutebrowser/.venv/lib/python3.4/site-packages/yapf/yapflib/yapf_api.py", line 101, in FormatCode  
    uwl.CalculateFormattingInformation()  
  File "/home/florian/proj/qutebrowser/.venv/lib/python3.4/site-packages/yapf/yapflib/unwrapped_line.py", line 77, in CalculateFormattingInformation  
    token.split_penalty += _SplitPenalty(prev_token, token)  
  File "/home/florian/proj/qutebrowser/.venv/lib/python3.4/site-packages/yapf/yapflib/unwrapped_line.py", line 342, in _SplitPenalty  
    if cur_token.node_split_penalty > 0:  
TypeError: unorderable types: NoneType() > int()

chokes on `rb"literal"`

yapf currently chokes on raw bytestring literals:

bash-4.3$ cat /tmp/rb_str_literal.py
rb'literal'
bash-4.3$ python3 -m yapf -i /tmp/rb_str_literal.py
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/site-packages/yapf/yapflib/pytree_utils.py", line 96, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib2to3/pgen2/driver.py", line 71, in parse_tokens
    if p.addtoken(type, value, (prefix, start)):
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib2to3/pgen2/parse.py", line 159, in addtoken
    raise ParseError("bad input", type, value, context)
lib2to3.pgen2.parse.ParseError: bad input: type=3, value="'literal'", context=('', (1, 2))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/runpy.py", line 170, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/usr/local/lib/python3.4/site-packages/yapf/__main__.py", line 18, in <module>
    sys.exit(yapf.main(sys.argv))
  File "/usr/local/lib/python3.4/site-packages/yapf/__init__.py", line 109, in main
    print_diff=args.diff, verify=args.verify)
  File "/usr/local/lib/python3.4/site-packages/yapf/__init__.py", line 133, in FormatFiles
    print_diff=print_diff, verify=verify)
  File "/usr/local/lib/python3.4/site-packages/yapf/yapflib/yapf_api.py", line 72, in FormatFile
    verify=verify)
  File "/usr/local/lib/python3.4/site-packages/yapf/yapflib/yapf_api.py", line 95, in FormatCode
    tree = pytree_utils.ParseCodeToTree(unformatted_source.rstrip() + '\n')
  File "/usr/local/lib/python3.4/site-packages/yapf/yapflib/pytree_utils.py", line 101, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib2to3/pgen2/driver.py", line 71, in parse_tokens
    if p.addtoken(type, value, (prefix, start)):
  File "/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib2to3/pgen2/parse.py", line 159, in addtoken
    raise ParseError("bad input", type, value, context)
lib2to3.pgen2.parse.ParseError: bad input: type=3, value="'literal'", context=('', (1, 2))

This is using revision 7a01d00.

E129 visually indented line with same indent as next logical line

yapf formats:

def response():
    if (defined and
            dirs is _dirs_undefined and dictionary is _dictionary_undefined):
        return to

as

def response():
    if (defined and dirs is _dirs_undefined and
        dictionary is _dictionary_undefined):
        return to

pep8 checker gives E129. I also agree with it that this looks misleading.

pyformat thinks the original is correct and leaves it untouched. I would personally have formatted as

def response():
    if (
        defined and
        dirs is _dirs_undefined and dictionary is _dictionary_undefined
    ):
        return to

but I'm not wedded to that and suspect you like whitespace less than I do. :-)

yap --style=pep8 sometimes deletes required blank lines

The following code:

def noop(x):
    return x


timeout_settings = None


# This is a comment not a blank line
# This is a comment not a blank line
@noop
@noop
def test_slow_failing_test_1(x):
    pass

is reformatted to:

def noop(x):
    return x


timeout_settings = None

# This is a comment not a blank line
# This is a comment not a blank line
@noop
@noop
def test_slow_failing_test_1(x):
    pass

With a single blank line between timeout_settings and the start of the comment. This is incorrect. ( E302 expected 2 blank lines, found 1)

Both comments and both decorators appear to be necessary to trigger this behaviour.

Pathologically slow example

The following takes 25 seconds to reformat on my machine. In comparison, many significantly larger files run in under a second:

specialElements = frozenset((
    (namespaces['html'], 'address'),
    (namespaces['html'], 'applet'),
    (namespaces['html'], 'area'),
    (namespaces['html'], 'article'),
    (namespaces['html'], 'aside'),
    (namespaces['html'], 'base'),
    (namespaces['html'], 'basefont'),
    (namespaces['html'], 'bgsound'),
    (namespaces['html'], 'blockquote'),
    (namespaces['html'], 'body'),
    (namespaces['html'], 'br'),
    (namespaces['html'], 'button'),
    (namespaces['html'], 'caption'),
    (namespaces['html'], 'center'),
    (namespaces['html'], 'col'),
    (namespaces['html'], 'colgroup'),
    (namespaces['html'], 'command'),
    (namespaces['html'], 'dd'),
    (namespaces['html'], 'details'),
    (namespaces['html'], 'dir'),
    (namespaces['html'], 'div'),
    (namespaces['html'], 'dl'),
    (namespaces['html'], 'dt'),
    (namespaces['html'], 'embed'),
    (namespaces['html'], 'fieldset'),
    (namespaces['html'], 'figure'),
    (namespaces['html'], 'footer'),
    (namespaces['html'], 'form'),
    (namespaces['html'], 'frame'),
    (namespaces['html'], 'frameset'),
    (namespaces['html'], 'h1'),
    (namespaces['html'], 'h2'),
    (namespaces['html'], 'h3'),
    (namespaces['html'], 'h4'),
    (namespaces['html'], 'h5'),
    (namespaces['html'], 'h6'),
    (namespaces['html'], 'head'),
    (namespaces['html'], 'header'),
    (namespaces['html'], 'hr'),
    (namespaces['html'], 'html'),
    (namespaces['html'], 'iframe'),
    # Note that image is commented out in the spec as "this isn't an
    # element that can end up on the stack, so it doesn't matter,"
    (namespaces['html'], 'image'),
    (namespaces['html'], 'img'),
    (namespaces['html'], 'input'),
    (namespaces['html'], 'isindex'),
    (namespaces['html'], 'li'),
    (namespaces['html'], 'link'),
    (namespaces['html'], 'listing'),
    (namespaces['html'], 'marquee'),
    (namespaces['html'], 'menu'),
    (namespaces['html'], 'meta'),
    (namespaces['html'], 'nav'),
    (namespaces['html'], 'noembed'),
    (namespaces['html'], 'noframes'),
    (namespaces['html'], 'noscript'),
    (namespaces['html'], 'object'),
    (namespaces['html'], 'ol'),
    (namespaces['html'], 'p'),
    (namespaces['html'], 'param'),
    (namespaces['html'], 'plaintext'),
    (namespaces['html'], 'pre'),
    (namespaces['html'], 'script'),
    (namespaces['html'], 'section'),
    (namespaces['html'], 'select'),
    (namespaces['html'], 'style'),
    (namespaces['html'], 'table'),
    (namespaces['html'], 'tbody'),
    (namespaces['html'], 'td'),
    (namespaces['html'], 'textarea'),
    (namespaces['html'], 'tfoot'),
    (namespaces['html'], 'th'),
    (namespaces['html'], 'thead'),
    (namespaces['html'], 'title'),
    (namespaces['html'], 'tr'),
    (namespaces['html'], 'ul'),
    (namespaces['html'], 'wbr'),
    (namespaces['html'], 'xmp'),
    (namespaces['svg'], 'foreignObject')
))

yapf does not explicitly handle different encodings

yapf will error if you run it on a file that cannot be handled by the encoding set by your LANG environment variable. e.g. consider the following file:

# encoding: utf-8

x = "☃"

If you set LANG=C and run yapf on it this will crash with

    Traceback (most recent call last):
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/runpy.py", line 170, in _run_module_as_main
        "__main__", mod_spec)
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/runpy.py", line 85, in _run_code
        exec(code, run_globals)
      File "/home/david/yapf/yapf/__main__.py", line 18, in <module>
        sys.exit(yapf.main(sys.argv))
      File "/home/david/yapf/yapf/__init__.py", line 102, in main
        print_diff=args.diff)
      File "/home/david/yapf/yapf/__init__.py", line 124, in FormatFiles
        filename, style_config=style_config, lines=lines, print_diff=print_diff)
      File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 59, in FormatFile
        original_source = ReadFile(filename, logging.warning)
      File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 142, in ReadFile
        source = fd.read()
      File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/encodings/ascii.py", line 26, in decode
        return codecs.ascii_decode(input, self.errors)[0]
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 24: ordinal not in range(128)

(Took me forever to figure out this was what was going on here. I was experiencing a lot of crashes when running yapf in a subprocess but couldn't reproduce them. Finally figured out it was that the environment was getting overridden)

E122 continuation line outdented inside class body

yapf formats:

class eld(d):
    if str(geom.geom_type).upper(
    ) != self.geom_type and not self.geom_type == 'GEOMETRY':
        ror(code='om_type')

as:

class eld(d):
    if str(geom.geom_type).upper(
) != self.geom_type and not self.geom_type == 'GEOMETRY':
        ror(code='om_type')

This is both pep8 wrong and really confusing to read.

yapf --style=pep8 will line break in the middle of a keyword argument (E251)

The following pep8 correct code:

class A(object):
    def b(self):
        if self.aaaaaaaaaaaaaaaaaaaa not in self.bbbbbbbbbb(
            cccccccccccccccccccc=True
        ):
            pass

Will be reformatted to the following non pep8 compliant (and ugly) code:

class A(object):
    def b(self):
        if self.aaaaaaaaaaaaaaaaaaaa not in self.bbbbbbbbbb(cccccccccccccccccccc
                                                            =True):
            pass

The line break around the equals is not kosher. pep8 requires keeping kwargs together without spaces.

yapf will sometimes eat comments

If I have a python file with just the following line:

#"

Then running yapf on the file will eat the comment and produce an empty file.

exec used as a method breaks lib2to3

Formatting the following crashes (under both 2.7.8 and 3.4.2):

methods.exec(sys.modules[name])

The error is:

lib2to3.pgen2.parse.ParseError: bad input: type=1, value='exec', context=('', (1, 8))

Running 2to3 on the same code produces the same parse error, so this is a limitation of the library.

yapf with --style=pep8 puts imports on one line even if that breaks the character limit

Consider e.g.

from __future__ import division, print_function, absolute_import, unicode_literals

(there's nothing special about this being a from future import, it's just an example of a long import from my project)

This import line is more than 82 characters but yapf puts it all on one line. It does this even if the code was originally broken across multiple lines.

Comment between two decorators produces internal error

The following:

def foo(x):
    return x

@foo
# A comment
@foo
def inner(self):
    pass

Produces the following error:

INTERNAL ERROR: 
# A comment
@foo
Traceback (most recent call last):
  File "/home/david/.pyenv/versions/2.7.8/lib/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/home/david/.pyenv/versions/2.7.8/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/home/david/yapf/yapf/__main__.py", line 18, in <module>
    sys.exit(yapf.main(sys.argv))
  File "/home/david/yapf/yapf/__init__.py", line 102, in main
    print_diff=args.diff)
  File "/home/david/yapf/yapf/__init__.py", line 124, in FormatFiles
    filename, style_config=style_config, lines=lines, print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 67, in FormatFile
    print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 110, in FormatCode
    reformatted_source = reformatter.Reformat(uwlines)
  File "/home/david/yapf/yapf/yapflib/reformatter.py", line 73, in Reformat
    verifier.VerifyCode(formatted_code[-1])
  File "/home/david/yapf/yapf/yapflib/verifier.py", line 45, in VerifyCode
    compile(normalized_code.encode('UTF-8'), '<string>', 'exec')
  File "<string>", line 2
    @foo
       ^
SyntaxError: invalid syntax

Invalid unicode will crash yapf with an exception rather than an error message

In the continuing strategy of "here's a python string literal for a file exhibiting this problem to avoid github being clever", the following string passes ast.parse (in python 3.4.2) but causes yapf to crash when a file with precisely these contents is passed to it and run under the same python version. I think this is because yapf has an assumption baked in that all source is valid utf-8.

String:

"# а\x91а\x96б\x9fб\x80б\x81б\x82б\x83б\x84б\x85б\x86б\x87б\x88б\x89б\x8aб\x8bб\x8cб\x8dб\x8eб\x8f <- Cyrillic characters\n'а\x8eб\x82т\x84\x96аЄ'\n"

Error:

INTERNAL ERROR: # а�а�б�б�б�б�б�б�б
б�б�б�б�б�б�б�б�б�б� <- Cyrillic characters
Traceback (most recent call last):
  File "/home/david/yapf/yapf/yapflib/verifier.py", line 38, in VerifyCode
    compile(textwrap.dedent(code).encode('UTF-8'), '<string>', 'exec')
  File "<string>", line 2
    б�б�б�б�б�б�б�б�б�б� <- Cyrillic characters
                       ^
SyntaxError: invalid character in identifier

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/david/yapf/yapf/yapflib/verifier.py", line 41, in VerifyCode
    ast.parse(textwrap.dedent(code.lstrip('\n')).lstrip(), '<string>', 'exec')
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "<string>", line 2
    б�б�б�б�б�б�б�б�б�б� <- Cyrillic characters
                       ^
SyntaxError: invalid character in identifier

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/runpy.py", line 170, in _run_module_as_main
    "__main__", mod_spec)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/david/yapf/yapf/__main__.py", line 18, in <module>
    sys.exit(yapf.main(sys.argv))
  File "/home/david/yapf/yapf/__init__.py", line 102, in main
    print_diff=args.diff)
  File "/home/david/yapf/yapf/__init__.py", line 124, in FormatFiles
    filename, style_config=style_config, lines=lines, print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 67, in FormatFile
    print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 110, in FormatCode
    reformatted_source = reformatter.Reformat(uwlines)
  File "/home/david/yapf/yapf/yapflib/reformatter.py", line 73, in Reformat
    verifier.VerifyCode(formatted_code[-1])
  File "/home/david/yapf/yapf/yapflib/verifier.py", line 45, in VerifyCode
    compile(normalized_code.encode('UTF-8'), '<string>', 'exec')
  File "<string>", line 1
    б�б�б�б�б�б�б�б�б�б� <- Cyrillic characters
                       ^
SyntaxError: invalid character in identifier

yapf --style=pep8 continuation line may not distinguish itself from next line

The following code:

class Foo(object):

    def bar(self):
        if self.solo_generator is None and len(
                self.generators + self.next_batch) == 1:
            pass

reformats as

class Foo(object):
    def bar(self):
        if self.solo_generator is None and len(
            self.generators + self.next_batch) == 1:
            pass

Which the pep8 error checker says "E125 continuation line with same indent as next logical line" to

Feature request: Error early on unsupported python versions

i.e. just exit immediately with a big message saying "yapf doesn't support running on this version of python", mostly so you don't get twits like me running it on 3.2 and getting confused when it produces strange error messages later. :-)

yapf --style=pep8 produces "E126 continuation line over-indented for hanging indent"

The following code:

class NAryTreeStrategy():

    def produce_template(self, context, pv):
        if True:
            children = tuple((self.branch_key_strategy.draw_template(
                context, pv.branch_key_parameter),
                self.produce_template(context, pv))
                for _ in range(10))
            return children

Is reformatted as:

class NAryTreeStrategy():
    def produce_template(self, context, pv):
        if True:
            children = tuple((self.branch_key_strategy.draw_template(
                context, pv.branch_key_parameter),
                              self.produce_template(context, pv))
                             for _ in range(10))
            return children

Which causes the pep8 checker to report "E126 continuation line over-indented for hanging indent"

lib2to3 parse error on well formed code that 2to3 passes fine

The following code:

def response(                  using=None):
    if (context_instance is _context_instance_undefined and
        dirs is _dirs_undefined and dictionary is _dictionary_undefined):
            return to

Gives me the following exception when run under python 3.4:

Traceback (most recent call last):
  File "/home/david/yapf/yapf/yapflib/pytree_utils.py", line 85, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/driver.py", line 71, in parse_tokens
    if p.addtoken(type, value, (prefix, start)):
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/parse.py", line 159, in addtoken
    raise ParseError("bad input", type, value, context)
lib2to3.pgen2.parse.ParseError: bad input: type=6, value='', context=('\n', (5, 0))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/runpy.py", line 170, in _run_module_as_main
    "__main__", mod_spec)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/david/yapf/yapf/__main__.py", line 18, in <module>
    sys.exit(yapf.main(sys.argv))
  File "/home/david/yapf/yapf/__init__.py", line 102, in main
    print_diff=args.diff)
  File "/home/david/yapf/yapf/__init__.py", line 124, in FormatFiles
    filename, style_config=style_config, lines=lines, print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 67, in FormatFile
    print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 88, in FormatCode
    tree = pytree_utils.ParseCodeToTree(unformatted_source)
  File "/home/david/yapf/yapf/yapflib/pytree_utils.py", line 91, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/driver.py", line 71, in parse_tokens
    if p.addtoken(type, value, (prefix, start)):
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/lib2to3/pgen2/parse.py", line 159, in addtoken
    raise ParseError("bad input", type, value, context)
lib2to3.pgen2.parse.ParseError: bad input: type=6, value='', context=('\n', (5, 0))

Running the normal 2to3 command line utility on this file works fine, so I think there's something about what you're passing in to 2to3.

Closing bracket can be lined up with the wrong opening bracket

This produces the same pep error as #26 but I think is not the same bug. Obviously feel free to close it as a duplicate if I'm wrong.

The following code:

class A(object):

    def f(self, aaaaaaaaa, bbbbbbbbbbbbb, row):
        if True:
            if row[4] is None or row[5] is None:
                bbbbbbbbbbbbb['..............'] = row[
                    5] if row[5] is not None else 5

is reformatted by yapf --style=pep8 as

class A(object):
    def f(self, aaaaaaaaa, bbbbbbbbbbbbb, row):
        if True:
            if row[4] is None or row[5] is None:
                bbbbbbbbbbbbb['..............'] = row[5] if row[5
                             ] is not None else 5

Note that the closing bracket on the following line is being lined up with the opening bracket from an entirely different indexing expression. This is both a pep8 violation and also rather misleading.

PEP8 style recommends two spaces before comment, yapf does one.

From an email from Terry Reedy:

[...] style.py has

The number of spaces required before a trailing comment.

SPACES_BEFORE_COMMENT=1,

PEP8 actually says "Inline comments should be separated by at least two spaces from the statement.", which is what you have in the google style.

Exception when formatting list comprehensions on Python 3

The following code causes an error when yapf is run under python 3 (I've tried on both 3.2 and 3.4):

def given(y):
    [k for k in () if k in y]

It gives the following error:

Traceback (most recent call last):
  File "/home/david/.pyenv/versions/3.4.2/bin/yapf", line 11, in <module>
    sys.exit(run_main())
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/site-packages/yapf/__init__.py", line 154, in run_main
    sys.exit(main(sys.argv))
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/site-packages/yapf/__init__.py", line 102, in main
    print_diff=args.diff)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/site-packages/yapf/__init__.py", line 124, in FormatFiles
    filename, style_config=style_config, lines=lines, print_diff=print_diff)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/site-packages/yapf/yapflib/yapf_api.py", line 68, in FormatFile
    print_diff=print_diff)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/site-packages/yapf/yapflib/yapf_api.py", line 101, in FormatCode
    uwl.CalculateFormattingInformation()
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/site-packages/yapf/yapflib/unwrapped_line.py", line 77, in CalculateFormattingInformation
    token.split_penalty += _SplitPenalty(prev_token, token)
  File "/home/david/.pyenv/versions/3.4.2/lib/python3.4/site-packages/yapf/yapflib/unwrapped_line.py", line 342, in _SplitPenalty
    if cur_token.node_split_penalty > 0:
TypeError: unorderable types: NoneType() > int()

I've tried this both against the latest version on pip and the current version of master.

yapf does not strip blank lines from the end of a file

I suppose this one is more of a feature request. pyformat will strip blank lines from the end of a file (pep8 checker reports the blank line at the end of the file as W391), but yapf will not, meaning a file that could easily be formatted into PEP8 compliance will not be.

W503: yapf inserts line break before rather than after a binary operator

The following:

def w(context): {
    'e_link': (
        not is_popup and context['has_delete_permission'] and
        ange and t('e', True)
    ),
}

Gets reformatted as:

def w(context):
    {
        'e_link': (not is_popup and context['has_delete_permission'] and ange
                   and t('e', True)),
    }

Which is wrong. The "and" should not be starting a line according to pep8.

yapf --style=pep8 does not put spaces after a trailing comma

Reformatting the following (pep8 correct) code:

trailing_comma = [
    1,
]

Results in the following:

trailing_comma = [1,]

PEP8 wants whitespace after the trailing comma. I can't immediately find a reference in PEP8 itself, but the pep8 checker program calls this E231.

Consistent indentation for peer values

Many Django projects have code like this:

urlpatterns = patterns('',
    url(r'^$', 'homepage_view'),
    url(r'^/login/$', 'login_view'),
    url(r'^/login/$', 'logout_view'),
    url(r'^/user/(?P<username>\w+)/$', 'profile_view')
)

With the PEP-8 preset, this shows the alignment algorithm's attempts to fit as much as possible on each line which tends to require a little more care scanning to distinguish the arguments:

urlpatterns = patterns('', url(r'^$', 'homepage_view'), url(r'^/login/$',
                                                            'login_view'),
                       url(r'^/login/$',
                           'logout_view'), url(r'^/user/(?P<username>\w+)/$',
                                               'profile_view')
                      )

A quick look through style.py doesn't seem to offer a way to have it attempt to fit everything on a line but, when impossible, use a common starting position all of the elements at the same level to avoid what happens to the last element in the previous example:

urlpatterns = patterns(
    '',
    url(r'^$', 'homepage_view'),
    url(r'^/login/$', 'login_view'),
    url(r'^/login/$', 'logout_view'),
    url(r'^/user/(?P<username>\w+)/$', 'profile_view'),
)

or a more compact version which avoids wrapping within a term:

urlpatterns = patterns('', url(r'^$', 'homepage_view'), 
                       url(r'^/login/$', 'login_view'),
                       url(r'^/login/$', 'logout_view'),
                       url(r'^/user/(?P<username>\w+)/$', 'profile_view'))

Another lib2to3 parse error

This one is not caused by whitespace as far as I can tell, or at least not at the end of the file:

def walk(top, topdown=True, onerror=None, followlinks=False):
            yield from walk(new_path, topdown, onerror, followlinks)

Produces the error:

Traceback (most recent call last):
  File "/home/david/yapf/yapf/yapflib/pytree_utils.py", line 85, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/usr/lib/python3.2/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/usr/lib/python3.2/lib2to3/pgen2/driver.py", line 71, in parse_tokens
    if p.addtoken(type, value, (prefix, start)):
  File "/usr/lib/python3.2/lib2to3/pgen2/parse.py", line 159, in addtoken
    raise ParseError("bad input", type, value, context)
lib2to3.pgen2.parse.ParseError: bad input: type=5, value='    ', context=('', (1, 0))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.2/runpy.py", line 160, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python3.2/runpy.py", line 73, in _run_code
    exec(code, run_globals)
  File "/home/david/yapf/yapf/__main__.py", line 18, in <module>
    sys.exit(yapf.main(sys.argv))
  File "/home/david/yapf/yapf/__init__.py", line 98, in main
    lines=lines))
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 88, in FormatCode
    tree = pytree_utils.ParseCodeToTree(unformatted_source.rstrip() + '\n')
  File "/home/david/yapf/yapf/yapflib/pytree_utils.py", line 91, in ParseCodeToTree
    tree = parser_driver.parse_string(code, debug=False)
  File "/usr/lib/python3.2/lib2to3/pgen2/driver.py", line 106, in parse_string
    return self.parse_tokens(tokens, debug)
  File "/usr/lib/python3.2/lib2to3/pgen2/driver.py", line 71, in parse_tokens
    if p.addtoken(type, value, (prefix, start)):
  File "/usr/lib/python3.2/lib2to3/pgen2/parse.py", line 159, in addtoken
    raise ParseError("bad input", type, value, context)
lib2to3.pgen2.parse.ParseError: bad input: type=5, value='    ', context=('', (1, 0))

yapf cannot handle 3.x metaclass syntax when run under 2.7

The following code will produce an internal error when yapf is run under 2.7:

class ABC(metaclass=type):
    pass

The error is:

INTERNAL ERROR: class ABC(metaclass=type):
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/home/david/yapf/yapf/__main__.py", line 18, in <module>
    sys.exit(yapf.main(sys.argv))
  File "/home/david/yapf/yapf/__init__.py", line 102, in main
    print_diff=args.diff)
  File "/home/david/yapf/yapf/__init__.py", line 124, in FormatFiles
    filename, style_config=style_config, lines=lines, print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 67, in FormatFile
    print_diff=print_diff)
  File "/home/david/yapf/yapf/yapflib/yapf_api.py", line 110, in FormatCode
    reformatted_source = reformatter.Reformat(uwlines)
  File "/home/david/yapf/yapf/yapflib/reformatter.py", line 73, in Reformat
    verifier.VerifyCode(formatted_code[-1])
  File "/home/david/yapf/yapf/yapflib/verifier.py", line 45, in VerifyCode
    compile(normalized_code.encode('UTF-8'), '<string>', 'exec')
  File "<string>", line 1
    class ABC(metaclass=type):
                       ^
SyntaxError: invalid syntax

Future imports aren't taken into account when checking syntax

This means that code like this will cause a SyntaxError:

from __future__ import print_function

def call_my_function(the_function):
    the_function("hi")

if __name__ == "__main__":
    call_my_function(print)

with output/traceback like so:

INTERNAL ERROR: 
    call_my_function(print)
Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/home/flyte/dev/yapf/yapf/__main__.py", line 18, in <module>
    sys.exit(yapf.main(sys.argv))
  File "yapf/__init__.py", line 102, in main
    print_diff=args.diff)
  File "yapf/__init__.py", line 124, in FormatFiles
    filename, style_config=style_config, lines=lines, print_diff=print_diff)
  File "yapf/yapflib/yapf_api.py", line 68, in FormatFile
    print_diff=print_diff)
  File "yapf/yapflib/yapf_api.py", line 113, in FormatCode
    reformatted_source = reformatter.Reformat(uwlines)
  File "yapf/yapflib/reformatter.py", line 73, in Reformat
    verifier.VerifyCode(formatted_code[-1])
  File "yapf/yapflib/verifier.py", line 45, in VerifyCode
    compile(normalized_code.encode('UTF-8'), '<string>', 'exec')
  File "<string>", line 1
    call_my_function(print)
                         ^
SyntaxError: invalid syntax

The problem is that each line is taken on its own merit without taking into account imports from future.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.