-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Config files for auto linting #3264
Conversation
autoflake, autopep8, docformatter, isort, yapf
I applied the auto styling to three files (views, models and urls). Please, take a look at them and let me know any commen like "I don't like this format as this" or "I like this split here". I will tell you if we can change that with a setting so we can tweak and adapt to our needings. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks promising.
What would workflow be? The developer would still be responsible for running this before pushing?
I really like how this could easily automate us getting up to strict linting. I already have my tooling set to use strict linting by default, and am generally testing linting before on each file inside vim -- though I generally don't run a tox -e lint
step as CI will.
It doesn't seem like we can necessarily rely on this tooling to lint for us, but I haven't tried running these tools yet. We'd definitely need a wrapper script as we won't remember to run that block of commands before each PR. Currently, I view this more as a path for intermittent linting cleanup and a way to help automate bumping up the linting strictness, as the added steps likely won't be run manually. However, even if this is the case, if we can get to strict linting then CI will bring up issues with code quality, and it won't be an item that wastes review cycles -- definitely a win.
readthedocs/builds/urls.py
Outdated
"""URL configuration for builds app.""" | ||
""" | ||
URL configuration for builds app. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to pep257, this should be a single line:
https://www.python.org/dev/peps/pep-0257/#one-line-docstrings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I forgot to remove the hack in my config that I used for a non-standard proyect :)
--make-summaries-multilines
does the trick
readthedocs/builds/urls.py
Outdated
url(r'^(?P<project_slug>[-\w]+)/$', | ||
builds_redirect_list, | ||
name='old_builds_project_list'), | ||
url(r'^(?P<project_slug>[-\w]+)/(?P<pk>\d+)/$', builds_redirect_detail, name='old_builds_detail'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer line length of 80. Our linting is set to max of 100, but tried to eventually get down to 80
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that 80 is not enought, I liked working on a project that was 120. But anyway, my thoughts.
I we use 80, the tools will split it always at 80... it won't be lines with more columns. It's strict.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with a strict 80, most of our code is wrapped at 80 already. We have set max length 100 for some of the lines that haven't been rewrapped, but have been refactoring those out. 80 is (close) to pep8 compliant, so it's expected. I have a bunch of personal reasons to like 80, but those are just mine 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I won't ask why... :)
I just changed the 120 by 80 columns in all the places.
readthedocs/core/models.py
Outdated
class UserProfile(models.Model): | ||
""" | ||
Additional information about a User. | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same note on single line docstring here, but modern suggestion is no space before docstring, 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without the --make-summary-multi-line
it will be
"""Additional information about a User."""
Since, I removed that argument, it will be as you like.
but modern suggestion is no space before docstring
What do you mean with that?
) | ||
allow_email = models.BooleanField(_('Allow email'), | ||
help_text=_('Show your email on VCS ' | ||
'contributions.'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm very 👎 on python's standard indentation style for continuation lines. It makes for very unscannable code.
def foo():
if_this_is_a_very_long_function_member_name(then_arguments_end_up_way_over_here=True,
and_that_isnt_very_readable=['especially', 'when', 'we'
'start', 'cramming',
'things', 'this', 'way])
def bar():
this_is_much_more_readable(
sure_its_taller=True,
but=[
'diffs make more sense',
'the end of indentation is clear',
'i can rename things without cascading indentation changes',
]
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very configurable in yapf
. Let me know how you would like to format this and I will find the proper settings to make it fit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to get the bar
formatting, you just need to add a trailing comma to the last ]
and yapf will do the rest for you ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering your example, after running yapf with the settings that I pushed here:
--- yapfexample.py (original)
+++ yapfexample.py (reformatted)
@@ -1,15 +1,14 @@
def foo():
- if_this_is_a_very_long_function_member_name(then_arguments_end_up_way_over_here=True,
- and_that_isnt_very_readable=['especially', 'when', 'we'
- 'start', 'cramming',
- 'things', 'this', 'way'])
+ if_this_is_a_very_long_function_member_name(
+ then_arguments_end_up_way_over_here=True,
+ and_that_isnt_very_readable=['especially', 'when', 'we'
+ 'start', 'cramming', 'things', 'this', 'way'])
+
def bar():
this_is_much_more_readable(
- sure_its_taller=True,
- but=[
+ sure_its_taller=True, but=[
'diffs make more sense',
'the end of indentation is clear',
'i can rename things without cascading indentation changes',
- ]
- )
+ ])
Adding the trailing comma to the bar
function as I mentioned:
--- yapfexample.py (original)
+++ yapfexample.py (reformatted)
@@ -1,8 +1,9 @@
def foo():
- if_this_is_a_very_long_function_member_name(then_arguments_end_up_way_over_here=True,
- and_that_isnt_very_readable=['especially', 'when', 'we'
- 'start', 'cramming',
- 'things', 'this', 'way'])
+ if_this_is_a_very_long_function_member_name(
+ then_arguments_end_up_way_over_here=True,
+ and_that_isnt_very_readable=['especially', 'when', 'we'
+ 'start', 'cramming', 'things', 'this', 'way'])
+
def bar():
this_is_much_more_readable(
It's exactly as you want ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I add two trailing commas to the fist example:
--- yapfexample.py (original)
+++ yapfexample.py (reformatted)
@@ -1,8 +1,18 @@
def foo():
- if_this_is_a_very_long_function_member_name(then_arguments_end_up_way_over_here=True,
- and_that_isnt_very_readable=['especially', 'when', 'we'
- 'start', 'cramming',
- 'things', 'this', 'way',],)
+ if_this_is_a_very_long_function_member_name(
+ then_arguments_end_up_way_over_here=True,
+ and_that_isnt_very_readable=[
+ 'especially',
+ 'when',
+ 'we'
+ 'start',
+ 'cramming',
+ 'things',
+ 'this',
+ 'way',
+ ],
+ )
+
or just with one:
--- yapfexample.py (original)
+++ yapfexample.py (reformatted)
@@ -1,8 +1,10 @@
def foo():
- if_this_is_a_very_long_function_member_name(then_arguments_end_up_way_over_here=True,
- and_that_isnt_very_readable=['especially', 'when', 'we'
- 'start', 'cramming',
- 'things', 'this', 'way'],)
+ if_this_is_a_very_long_function_member_name(
+ then_arguments_end_up_way_over_here=True,
+ and_that_isnt_very_readable=['especially', 'when', 'we'
+ 'start', 'cramming', 'things', 'this', 'way'],
+ )
+
def bar():
this_is_much_more_readable(
As you can see, trailing comma plays a very important role. Basically, it means: "I want this splitted element by element each in one different line"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat! It's nice that it yapf
can be customized on a case by case basis like that. If I were to refactor the functions I wrote, I'd refactor exactly as the double trailing comma example does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. That's the idea...
Then, the review is just something like "I would add a trailing comma there so it's easier to read" ;)
(we will have tons of those, I know why I'm telling you :) )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just showed a couple of examples but I'm sure that there will be more complicated cases. The thing is that changing the settings to make it fit with our style wishes is simple and after changing that setting the refactor is automatic.
So, we can start with a close enough configuration and then we can continue tweaking as we discover that some cases are not covered as we want.
_('See paid advertising'), | ||
help_text=_('If unchecked, you will still see community ads.'), | ||
default=True, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For instance, I would refactor the above block to this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this diff block, I would have refactored the lines that were removed with the lines that were added here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, so you like what yapf did... Not sure that I'm following you :P
readthedocs/core/models.py
Outdated
) | ||
allow_email = models.BooleanField( | ||
_('Allow email'), help_text=_('Show your email on VCS ' | ||
'contributions.'), default=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I would avoid this mixed version.
help_text
gets broken up needlesslydefault
is almost hidden from scanning
I'm just ranting now 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It got broken because the string was already splitted. I think if I remove the '
and make the string just one thing it will be more intelligent.
Yes, it will be:
allow_email = models.BooleanField(
_('Allow email'), help_text=_('Show your email on VCS contributions.'), default=True)
and, if we add a trailing comma after default=True
, yapf will format it like this:
allow_email = models.BooleanField(
_('Allow email'),
help_text=_('Show your email on VCS contributions.'),
default=True,
)
readthedocs/projects/views/base.py
Outdated
import logging | ||
from builtins import object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
builtins
is not stdlib and should be in the block below.
Import order should be:
- futures
- stdlib
- third party
- this package
- relative files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't you think it's a good idea to separate django
as a section also, since it's kind of big?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, instead of mixing all the django imports with the third party section
flake8rc
Outdated
@@ -0,0 +1,3 @@ | |||
[flake8] | |||
ignore = E125,D100,D101,D102,D200,P101,FI15,FI16,FI12,FI11,FI17,FI50,FI53,FI54 | |||
max-line-length = 120 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be moved to setup.cfg? I'd like overlap between prospector and this here if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm... I don't have a right answer for this. I have all of this commands hooked into my Emacs configuration and they are ran when I save the file. So, the workflow for other people is something that we need to discuss.
Why we can't rely on this? These tools modify the files in place with
Yes. As I said, I have them hooked into Emacs. I wrote them to communicate to you what how I ran those commands. |
For instance, I use vim and I believe @ericholscher uses sublime, so there might not be a good way to do this without requiring all of us to either run a command pre-commit or work out a way to emulate your on-save feature.
I should have said solely rely on this. It will still be up to individual developers to run the autolinting before each commit -- or run periodically perhaps. |
I'm not really sure how the git pre-commit command would work. I like my on-save feature because I can see how it gets formatted and I can tweak a little in case that I "don't agree" with what yapf has done... For example, add more trailing commas :) But we can definetly figure out something that fit for everybody, even contributors. My other co-workers used PyCharm and they wrote some minor plugins for this purpose. |
yapf 0.20 released! 💛 https://github.com/google/yapf/blob/master/CHANGELOG#L5 I will need to check the new knobs and update this PR |
In our meeting, we decided:
@humitos the one piece I'd like to see to get this merged is a script (maybe in I'm treating |
I will do this, but in the future I think we could consider The steps a new contributor should follow are:
and he/she will see a very nice message with the test that passed and the ones that not I think we can go with a simple bash script that execute our commands over the files that where modified by the developer as a first approach since to use this Besides, we will need to propose a PR on those repos with the I know this is more work, but after seeing that output I think it's very nice for newcomers and easy to understand what's going wrong. It will be a mixture between, automatic tools ran and fixing some issues (for example Edit: there are some of the commands we want already done:
So, I think the only one that it's missing is |
This one could be considered in the future, also: https://github.com/asottile/pyupgrade |
Ha, I created mine. It was pretty simple using their own tool: https://github.com/pre-commit/pre-commit-mirror-maker Here is the repo: https://github.com/humitos/mirrors-docformatter |
Use the `pre-commit` tool to run some linting commands before each commit: http://pre-commit.com/
requirements/auto-lint.txt
Outdated
flake8-quotes | ||
flake8-string-format | ||
flake8-tidy-imports | ||
flake8-todo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This requirements.txt
file should be removed and use the additional_dependencies
option for the pre-commit
config files to install the "extra dependencies" for each of the plugins.
If you want to make a test over a specific file and see how
|
I was suggested another approach in Twitter by @juancarlospaco: connecting I'm not familiarized with prospector so I don't see clearly the benefits, but I @agjohnson mentioned something like this in the meeting so I suppose he will be interested on taking a look at this. |
I had to add a couple of "trailing commas" to keep the format as we want
Remove the auto-lint.txt requirements file since it's not needed anymore
@humitos and I discussed this yesterday. I think we are close to merge. We'll tweak rules as we go. None of this is mandatory yet, but we'll be probably using some of these rules to autolint to |
Oh, also, @humitos. Can we either revert the changes causing lint errors or fix the rules used? Error is:
|
D203 / 1 blank line required before class docstring
I just fixed the rules to fix the D203 automatically by changing a setting in yapf ;) Everything should be working now and prospector should pass. |
I think we are good here and we should merge this. In the daily basis workflow Then prospector will check inside the CI that the files passes all the prospector validations. (at this moment I didn't find a way to share the config files from the commands used by In case that |
Agreed! I'm looking forward to starting to use this workflow. Thanks @humitos for the tips! 🎉 |
What solved |
D203 is a blank line before the class docstring as here: 0f09a6f#diff-b8a9e4df8b14a1a4a0af561dcae3c19e What kind of error are you receiving? |
autoflake, autopep8, docformatter, isort, yapf
There are some things that we need to define:
If you want to go deeper, you should read all the knobs from
yapf
(which is the most agressive here) and tweak all the options as you wish). For now, I think it's a good start.I'm adding a couple of modified files after aplying these settings as examples so we can discuss a little on the automatic styling code and define if it worth.