Config files for auto linting (#3264)
* Config files for auto linting autoflake, autopep8, docformatter, isort, yapf * readthedocs.projects.views.base with auto styling applied * readthedocs.core.models auto styling applied * readthedocs.builds.urls with auto styling applied * Do not make multi line summaries * Improve isort settings * Use 80 columns as maximum * pre-commit config file Use the `pre-commit` tool to run some linting commands before each commit: http://pre-commit.com/ * Update yapf config file to 0.20.0 * Add i18n functions to yapf config * Remove django as a special section for isort * Rename config to be automatically discovered * Add annoying as a third party lib for isort * Use the new config for all the files modified as a test I had to add a couple of "trailing commas" to keep the format as we want * Install additional dependencies for each of the hooks Remove the auto-lint.txt requirements file since it's not needed anymore * Use default config name for autopep8 * Include autoflake as pre-commit hook * Exclude D105 from flake8 D105: Missing docstring in magic method * Minor fixes from flake8 * autoflake with --in-place * Arguments for docformatter * Autofix D203 with yapf D203 / 1 blank line required before class docstring * Ignore local_settings.py on prospectortheme-0.6.2
parent
c0de92d173
commit
d18ddc9bb5
|
@ -0,0 +1,3 @@
|
|||
[flake8]
|
||||
ignore = E125,D100,D101,D102,D105,D200,D211,P101,FI15,FI16,FI12,FI11,FI17,FI50,FI53,FI54
|
||||
max-line-length = 80
|
|
@ -0,0 +1,10 @@
|
|||
[settings]
|
||||
line_length=80
|
||||
indent=' '
|
||||
multi_line_output=4
|
||||
default_section=FIRSTPARTY
|
||||
known_readthedocs=readthedocs
|
||||
known_readthedocsinc=readthedocsinc
|
||||
known_third_party=celery,stripe,requests,pytz,builtins,django,annoying
|
||||
sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
|
||||
add_imports=from __future__ import division, from __future__ import print_function, from __future__ import unicode_literals
|
|
@ -0,0 +1,54 @@
|
|||
exclude: '^$'
|
||||
fail_fast: false
|
||||
repos:
|
||||
- repo: git@github.com:pre-commit/pre-commit-hooks
|
||||
sha: v1.1.1
|
||||
hooks:
|
||||
- id: autopep8-wrapper
|
||||
- id: check-added-large-files
|
||||
- id: debug-statements
|
||||
- id: double-quote-string-fixer
|
||||
- id: end-of-file-fixer
|
||||
- id: fix-encoding-pragma
|
||||
- id: flake8
|
||||
additional_dependencies: [
|
||||
'flake8-blind-except',
|
||||
'flake8-coding',
|
||||
'flake8-comprehensions',
|
||||
'flake8-debugger',
|
||||
'flake8-deprecated',
|
||||
'flake8-docstrings',
|
||||
'flake8-meiqia',
|
||||
'flake8-mutable',
|
||||
'flake8-pep3101',
|
||||
'flake8-print',
|
||||
'flake8-quotes',
|
||||
'flake8-string-format',
|
||||
'flake8-tidy-imports',
|
||||
'flake8-todo']
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: git@github.com:pre-commit/mirrors-yapf.git
|
||||
sha: v0.20.0
|
||||
hooks:
|
||||
- id: yapf
|
||||
exclude: 'migrations|settings|scripts'
|
||||
additional_dependencies: ['futures']
|
||||
|
||||
- repo: git@github.com:FalconSocial/pre-commit-python-sorter.git
|
||||
sha: b57843b0b874df1d16eb0bef00b868792cb245c2
|
||||
hooks:
|
||||
- id: python-import-sorter
|
||||
args: ['--silent-overwrite']
|
||||
|
||||
- repo: git@github.com:humitos/mirrors-docformatter.git
|
||||
sha: v0.8
|
||||
hooks:
|
||||
- id: docformatter
|
||||
args: ['--in-place', '--wrap-summaries=80', '--wrap-descriptions=80', '--pre-summary-newline', '--no-blank']
|
||||
|
||||
- repo: git@github.com:humitos/mirrors-autoflake.git
|
||||
sha: v1.0
|
||||
hooks:
|
||||
- id: autoflake
|
||||
args: ['--in-place', '--remove-all-unused-imports', '--remove-unused-variable']
|
|
@ -0,0 +1,203 @@
|
|||
# https://github.com/google/yapf
|
||||
# current version used in this repo
|
||||
# yapf==0.20.0
|
||||
|
||||
|
||||
[style]
|
||||
# Align closing bracket with visual indentation.
|
||||
ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=True
|
||||
|
||||
# Allow lambdas to be formatted on more than one line.
|
||||
ALLOW_MULTILINE_LAMBDAS=False
|
||||
|
||||
# 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_MULTILINE_DICTIONARY_KEYS=False
|
||||
|
||||
# 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():
|
||||
# ...
|
||||
BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=True
|
||||
|
||||
# Insert a blank line before a class-level docstring.
|
||||
BLANK_LINE_BEFORE_CLASS_DOCSTRING=True
|
||||
|
||||
# Do not split consecutive brackets. Only relevant when
|
||||
# dedent_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',
|
||||
# })
|
||||
COALESCE_BRACKETS=True
|
||||
|
||||
# The column limit.
|
||||
COLUMN_LIMIT=80
|
||||
|
||||
# Indent width used for line continuations.
|
||||
CONTINUATION_INDENT_WIDTH=4
|
||||
|
||||
# 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
|
||||
DEDENT_CLOSING_BRACKETS=False
|
||||
|
||||
# Place each dictionary entry onto its own line.
|
||||
EACH_DICT_ENTRY_ON_SEPARATE_LINE=False
|
||||
|
||||
# The regex for an i18n 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_COMMENT=
|
||||
|
||||
# The i18n function call names. The presence of this function stops
|
||||
# reformattting on that line, because the string it has cannot be moved
|
||||
# away from the i18n comment.
|
||||
I18N_FUNCTION_CALL=["_", "ugettext", "gettext"]
|
||||
|
||||
# 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_DICTIONARY_VALUE=False
|
||||
|
||||
# The number of columns to use for indentation.
|
||||
INDENT_WIDTH=4
|
||||
|
||||
# Join short lines into one line. E.g., single line 'if' statements.
|
||||
JOIN_MULTIPLE_LINES=True
|
||||
|
||||
# Use spaces around the power operator.
|
||||
SPACES_AROUND_POWER_OPERATOR=True
|
||||
|
||||
# Do not include spaces around selected binary operators. For example:
|
||||
# 1 + 2 * 3 - 4 / 5
|
||||
# will be formatted as follows when configured with a value "*,/":
|
||||
# 1 + 2*3 - 4/5
|
||||
NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS=False
|
||||
|
||||
# Set to True to prefer spaces around the assignment operator for
|
||||
# default or keyword arguments.
|
||||
SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN=False
|
||||
|
||||
# The number of spaces required before a trailing comment.
|
||||
SPACES_BEFORE_COMMENT=2
|
||||
|
||||
# Insert a space between the ending comma and closing bracket of a list,
|
||||
# etc.
|
||||
SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=False
|
||||
|
||||
# Split before arguments if the argument list is terminated by a
|
||||
# comma.
|
||||
SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED=True
|
||||
|
||||
# Set to True to prefer splitting before '&', '|' or '^' rather than
|
||||
# after.
|
||||
SPLIT_BEFORE_BITWISE_OPERATOR=False
|
||||
|
||||
# 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_DICT_SET_GENERATOR=True
|
||||
|
||||
# Split after the opening paren which surrounds an expression if it doesn't fit on a single line.
|
||||
SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN=True
|
||||
|
||||
# If an argument / parameter list is going to be split, then split
|
||||
# before the first argument.
|
||||
SPLIT_BEFORE_FIRST_ARGUMENT=True
|
||||
|
||||
# Set to True to prefer splitting before 'and' or 'or' rather than
|
||||
# after.
|
||||
SPLIT_BEFORE_LOGICAL_OPERATOR=False
|
||||
|
||||
# Split named assignments onto individual lines.
|
||||
SPLIT_BEFORE_NAMED_ASSIGNS=False
|
||||
|
||||
# For list comprehensions and generator expressions with multiple clauses
|
||||
SPLIT_COMPLEX_COMPREHENSION=False
|
||||
|
||||
# The penalty for splitting right after the opening bracket.
|
||||
SPLIT_PENALTY_AFTER_OPENING_BRACKET=2000
|
||||
|
||||
# The penalty for splitting the line after a unary operator.
|
||||
SPLIT_PENALTY_AFTER_UNARY_OPERATOR=10000
|
||||
|
||||
# The penalty for splitting right before an if expression.
|
||||
SPLIT_PENALTY_BEFORE_IF_EXPR=0
|
||||
|
||||
# The penalty of splitting the line around the '&', '|', and '^'
|
||||
# operators.
|
||||
SPLIT_PENALTY_BITWISE_OPERATOR=300
|
||||
|
||||
# The penalty for splitting a list comprehension or generator expression.
|
||||
SPLIT_PENALTY_COMPREHENSION=80
|
||||
|
||||
# The penalty for characters over the column limit.
|
||||
SPLIT_PENALTY_EXCESS_CHARACTER=2600
|
||||
|
||||
# The penalty incurred by adding a line split to the unwrapped
|
||||
# line. The more line splits added the higher the penalty.
|
||||
SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT=30
|
||||
|
||||
# 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_IMPORT_NAMES=0
|
||||
|
||||
# The penalty of splitting the line around the 'and' and 'or'
|
||||
# operators.
|
||||
SPLIT_PENALTY_LOGICAL_OPERATOR=300
|
||||
|
||||
# Use the Tab character for indentation.
|
||||
USE_TABS=False
|
||||
|
||||
# Allow splits before the dictionary value.
|
||||
ALLOW_SPLIT_BEFORE_DICT_VALUE=False
|
|
@ -34,11 +34,29 @@ label. Those tickets are meant to be standalone and can be worked on ad-hoc.
|
|||
When contributing code, then please follow the standard Contribution
|
||||
Guidelines set forth at `contribution-guide.org`_.
|
||||
|
||||
We have a strict code style that it's easy to follow since you just
|
||||
have to run a couple of commands and they will do everything for
|
||||
you. These commands are a mix between `autoflake`_, `autopep8`_,
|
||||
`docformatter`_, `isort`_, `unify`_ and `yapf`_::
|
||||
|
||||
$ autoflake --remove-all-unused-imports --remove-unused-variables --keep-useless-pass
|
||||
$ autopep8
|
||||
$ docformatter --wrap-summaries=80 --wrap-descriptions=80 --pre-summary-newline --no-blank
|
||||
$ isort
|
||||
$ unify --quote="'"
|
||||
$ yapf --exclude=*migrations* --exclude=*settings* --exclude=*scripts* --parallel
|
||||
|
||||
.. _Feature Overview: https://github.com/rtfd/readthedocs.org/issues?direction=desc&labels=Feature+Overview&page=1&sort=updated&state=open
|
||||
.. _Good First Issue: https://github.com/rtfd/readthedocs.org/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22
|
||||
.. _Sprintable: https://github.com/rtfd/readthedocs.org/issues?q=is%3Aopen+is%3Aissue+label%3ASprintable
|
||||
.. _contribution-guide.org: http://www.contribution-guide.org/#submitting-bugs
|
||||
|
||||
.. _autoflake: https://github.com/myint/autoflake
|
||||
.. _autopep8: https://github.com/hhatto/autopep8
|
||||
.. _docformatter: https://github.com/myint/docformatter
|
||||
.. _unify: https://github.com/myint/unify
|
||||
.. _yapf: https://github.com/google/yapf
|
||||
|
||||
Triaging tickets
|
||||
----------------
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ ignore-paths:
|
|||
|
||||
ignore-patterns:
|
||||
- /migrations/
|
||||
- local_settings.py
|
||||
|
||||
pep8:
|
||||
full: true
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""URL configuration for builds app."""
|
||||
from __future__ import (
|
||||
absolute_import, division, print_function, unicode_literals)
|
||||
|
||||
from __future__ import absolute_import
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import builds_redirect_detail, builds_redirect_list
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^(?P<project_slug>[-\w]+)/(?P<pk>\d+)/$',
|
||||
url(
|
||||
r'^(?P<project_slug>[-\w]+)/(?P<pk>\d+)/$',
|
||||
builds_redirect_detail,
|
||||
name='old_builds_detail'),
|
||||
|
||||
url(r'^(?P<project_slug>[-\w]+)/$',
|
||||
name='old_builds_detail',
|
||||
),
|
||||
url(
|
||||
r'^(?P<project_slug>[-\w]+)/$',
|
||||
builds_redirect_list,
|
||||
name='old_builds_project_list'),
|
||||
name='old_builds_project_list',
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,55 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Models for the core app."""
|
||||
from __future__ import (
|
||||
absolute_import, division, print_function, unicode_literals)
|
||||
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
|
||||
from annoying.fields import AutoOneToOneField
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||
from annoying.fields import AutoOneToOneField
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
|
||||
STANDARD_EMAIL = "anonymous@readthedocs.org"
|
||||
STANDARD_EMAIL = 'anonymous@readthedocs.org'
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class UserProfile (models.Model):
|
||||
class UserProfile(models.Model):
|
||||
|
||||
"""Additional information about a User."""
|
||||
|
||||
user = AutoOneToOneField('auth.User', verbose_name=_('User'),
|
||||
related_name='profile')
|
||||
user = AutoOneToOneField(
|
||||
'auth.User', verbose_name=_('User'), related_name='profile')
|
||||
whitelisted = models.BooleanField(_('Whitelisted'), default=False)
|
||||
banned = models.BooleanField(_('Banned'), default=False)
|
||||
homepage = models.CharField(_('Homepage'), max_length=100, blank=True)
|
||||
allow_ads = models.BooleanField(_('See paid advertising'),
|
||||
allow_ads = models.BooleanField(
|
||||
_('See paid advertising'),
|
||||
help_text=_('If unchecked, you will still see community ads.'),
|
||||
default=True,
|
||||
)
|
||||
allow_email = models.BooleanField(_('Allow email'),
|
||||
help_text=_('Show your email on VCS '
|
||||
'contributions.'),
|
||||
default=True)
|
||||
allow_email = models.BooleanField(
|
||||
_('Allow email'),
|
||||
help_text=_('Show your email on VCS contributions.'),
|
||||
default=True,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return (ugettext("%(username)s's profile")
|
||||
% {'username': self.user.username})
|
||||
return (
|
||||
ugettext("%(username)s's profile") %
|
||||
{'username': self.user.username})
|
||||
|
||||
def get_absolute_url(self):
|
||||
return ('profiles_profile_detail', (),
|
||||
{'username': self.user.username})
|
||||
return ('profiles_profile_detail', (), {'username': self.user.username})
|
||||
|
||||
get_absolute_url = models.permalink(get_absolute_url)
|
||||
|
||||
def get_contribution_details(self):
|
||||
"""
|
||||
Gets the line to put into commits to attribute the author.
|
||||
Get the line to put into commits to attribute the author.
|
||||
|
||||
Returns a tuple (name, email)
|
||||
"""
|
||||
if self.user.first_name and self.user.last_name:
|
||||
name = '%s %s' % (self.user.first_name, self.user.last_name)
|
||||
name = '{} {}'.format(self.user.first_name, self.user.last_name)
|
||||
else:
|
||||
name = self.user.username
|
||||
if self.allow_email:
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Mix-in classes for project views."""
|
||||
from __future__ import absolute_import
|
||||
from builtins import object
|
||||
from __future__ import (
|
||||
absolute_import, division, print_function, unicode_literals)
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from builtins import object
|
||||
from django.conf import settings
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from ..models import Project
|
||||
from ..exceptions import ProjectSpamError
|
||||
from ..models import Project
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -19,10 +22,10 @@ USER_MATURITY_DAYS = getattr(settings, 'USER_MATURITY_DAYS', 7)
|
|||
|
||||
class ProjectOnboardMixin(object):
|
||||
|
||||
"""Add project onboard context data to project object views"""
|
||||
"""Add project onboard context data to project object views."""
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add onboard context data"""
|
||||
"""Add onboard context data."""
|
||||
context = super(ProjectOnboardMixin, self).get_context_data(**kwargs)
|
||||
# If more than 1 project, don't show onboarding at all. This could
|
||||
# change in the future, to onboard each user maybe?
|
||||
|
@ -47,13 +50,13 @@ class ProjectOnboardMixin(object):
|
|||
# Mixins
|
||||
class ProjectAdminMixin(object):
|
||||
|
||||
"""Mixin class that provides project sublevel objects
|
||||
"""
|
||||
Mixin class that provides project sublevel objects.
|
||||
|
||||
This mixin uses several class level variables
|
||||
|
||||
project_url_field
|
||||
The URL kwarg name for the project slug
|
||||
|
||||
"""
|
||||
|
||||
project_url_field = 'project_slug'
|
||||
|
@ -63,33 +66,33 @@ class ProjectAdminMixin(object):
|
|||
return self.model.objects.filter(project=self.project)
|
||||
|
||||
def get_project(self):
|
||||
"""Return project determined by url kwarg"""
|
||||
"""Return project determined by url kwarg."""
|
||||
if self.project_url_field not in self.kwargs:
|
||||
return None
|
||||
return get_object_or_404(
|
||||
Project.objects.for_admin_user(user=self.request.user),
|
||||
slug=self.kwargs[self.project_url_field]
|
||||
)
|
||||
slug=self.kwargs[self.project_url_field])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add project to context data"""
|
||||
"""Add project to context data."""
|
||||
context = super(ProjectAdminMixin, self).get_context_data(**kwargs)
|
||||
context['project'] = self.get_project()
|
||||
return context
|
||||
|
||||
def get_form(self, data=None, files=None, **kwargs):
|
||||
"""Pass in project to form class instance"""
|
||||
"""Pass in project to form class instance."""
|
||||
kwargs['project'] = self.get_project()
|
||||
return self.form_class(data, files, **kwargs)
|
||||
|
||||
|
||||
class ProjectSpamMixin(object):
|
||||
|
||||
"""Protects POST views from spammers"""
|
||||
"""Protects POST views from spammers."""
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if request.user.profile.banned:
|
||||
log.error('Rejecting project POST from shadowbanned user %s',
|
||||
log.error(
|
||||
'Rejecting project POST from shadowbanned user %s',
|
||||
request.user)
|
||||
return HttpResponseRedirect(self.get_failure_url())
|
||||
try:
|
||||
|
@ -99,7 +102,8 @@ class ProjectSpamMixin(object):
|
|||
if request.user.date_joined > date_maturity:
|
||||
request.user.profile.banned = True
|
||||
request.user.profile.save()
|
||||
log.error('Spam detected from new user, shadowbanned user %s',
|
||||
log.error(
|
||||
'Spam detected from new user, shadowbanned user %s',
|
||||
request.user)
|
||||
else:
|
||||
log.error('Spam detected from user %s', request.user)
|
||||
|
|
Loading…
Reference in New Issue