yapf
yapf copied to clipboard
Knob to set blank lines near imports, classes, functions and methods
BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF
allows adding a blank line before nested classes or methods.
Ideally, a knob would allow setting a specified amount of blank lines near:
- Top-level imports
- Top-level classes and functions
- Classes
- Methods
- Top-level comments
Alternatively, offer a knob can allow "maximum blank lines", thus allowing blank lines after imports (for example). Right now they get formatted away.
This is possible. I'll mark it as an "enhancement". Thanks!
yapf
and isort
currently handle blank lines near Top-level imports and comments differently. isort
is happy with this:
from foo import bar
# Public
def baz():
pass
but yapf
wants to remove a line between the import and the comment.
from foo import bar
# Public
def baz():
pass
Irrespective of isort
's behaviour, it would be nice to control the number of spaces here so there's 2 (ideally configurable) both above and below the comment. Should behave the same when there's a constant too.
from foo import bar
QUX = 'quux'
# Public
def baz():
pass
I have the same "infinite formatting loop" in PyCharm. In the JavaScript world, you have eslint --fix and you have WebStorm following your .eslintrc file - so there's no such issue.
This is one of the reasons I don't actually use yapf
but rather a series of unify
, autopep8
, flake8
and pylint
. I guess I'll add isort
too. :)
Adding the knobs shouldn't be too hard. I'll bump up the priority here.
I would also add a request knobs around number of blank lines inside functions (currently always exactly 1) and inside dictionaries (always exactly 0).
Kinda like eslint's no-multiple-empty-lines's max
option.
Inside a function I'd like to have the ability to use 2 lines to break up different sections, and inside long dictionaries, I'd like to be able to group logical sets of key/values.
A knob BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES
will be available in version 0.31.0
of YAPF
Hi, I'm not sure I've understood how to properly use the BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES
knob
Could you guide me with this ? - Here are my current configs files and an example code -
With this settings, I think that isort
would add a space after import pytest
and yapf
would remove it
( I think the docstring should probably be at the top to avoid this )
-
isort.cfg
[settings] profile = google py_version = auto line_length = 120 atomic = True from_first = True ensure_newline_before_comments = True
-
.style.yapf
[style] based_on_style=google column_limit=120 BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES=1
-
example.py
# !/usr/bin/env python # some comments import os import pytest """ Some docstring. """ def some_function(): pass
@diegovalenzuelaiturra Your isort config is missing lines_after_imports
explicitly set to something, otherwise the default will be different depending on the code. According to the isort docs:
lines_after_imports: Forces a certain number of lines after the imports and before the first line of functional code. By default this is 2 lines if the first line of code is a class or function. Otherwise it's 1.
The number you set there should be the same you use for blank_lines_between_top_level_imports_and_variables
(lowercase) in the YAPF config or there will always be a conflict otherwise.
There are a few special cases where the YAPF knob will not work as expected and will always conflict with what isort does, and it is when imports are immediately followed by either a comment, a docstring, function calls or conditionals. The knob does its job properly when a variable, a function definition or a class is what follows right after the imports.
Unfortunately, the problem is that it is very complex to implement the knob to work with comments, function calls or conditionals because they can be interspersed anywhere, even in between imports. And the way the YAPF tokenizer works would need to be refactored in such a way that it is able to look ahead by many lines in advance rather than just comparing in between "last", "current" and "next" lines (which is what it currently does).
A simple (but not so great) solution to let both tools coexist for those files which cause trouble is to disable YAPF for the scope of the imports section. For example, supposing you set the number of lines after imports to be 2 (two) for both isort and YAPF, this is how your script should look like:
# !/usr/bin/env python
# some comments
# yapf: disable
import os
import pytest
# yapf: enable
"""
Some docstring.
"""
def some_function():
pass
I hope this helps.
Hello!
Thank you @arareko for the great explanation, that is exactly the issue that I'm struggling with. The examples from @jamesbeith are very clear too.
the problem is that it is very complex to implement [...] A simple (but not so great) solution to let both tools coexist [...]
Four files in my codebase are affected, and I've been able to fix three of them with a single # yapf: disable
either in the first line after imports (e.g. a try
), or in the last import.
For the one case I couldn't fix that way, I thought of this (a dummy function or variable):
import os
import pytest
_ = 'google/yapf#347'
OTHER_VAR = object()
...
Or perhaps:
import os
import pytest
def _imports_end():
"""Play nice with isort, yapf#347"""
OTHER_VAR = object()
...
Aesthetically, I'd say I'll take a single # yapf: disable
directive over either of the above, but—since it's in a single file for now—I think I prefer these, to # yapf: disable
+ # yapf: disable
pairs (until #429 is implemented? Is that one less complex to implement, to what is remaining here? @arareko)
Aesthetically, I'd say I'll take a single
# yapf: disable
directive over either of the above, but—since it's in a single file for now—I think I prefer these, to# yapf: disable
+# yapf: disable
pairs (until #429 is implemented? Is that one less complex to implement, to what is remaining here? @arareko)
@camillesf I actually am not very aware of #429 (I'm not a YAPF developer but a very occasional contributor), and right now can't remember what the code looks like to actually know if it would be less complex to implement or not.
With regards to your examples, IMO they only make your code a little more cryptic and less easier to understand. The big advantage of the # yapf:
directives is that they look like comments (to both the developer and the Python interpreter) and are very explicit about what their intention is.