Browse Source

Merge pull request #468 from justanr/run-flake8-on-everything

Make flake8 run over everything
Peter Justin 7 years ago
parent
commit
1da5903306

+ 2 - 2
flaskbb/__init__.py

@@ -17,8 +17,8 @@ logger = logging.getLogger(__name__)
 from flaskbb.app import create_app  # noqa
 from flaskbb.app import create_app  # noqa
 
 
 
 
-# monkeypatch for https://github.com/wtforms/wtforms/issues/373
-# Taken from https://github.com/indico/indico/commit/c79c562866e5efdbeb5a3101cccc97df57906f76
+# monkeypatch for https://github.com/wtforms/wtforms/issues/373 Taken from
+# https://github.com/indico/indico/commit/c79c562866e5efdbeb5a3101cccc97df57906f76
 def _patch_wtforms_sqlalchemy():
 def _patch_wtforms_sqlalchemy():
     from ._compat import text_type
     from ._compat import text_type
     from wtforms.ext.sqlalchemy import fields
     from wtforms.ext.sqlalchemy import fields

+ 5 - 5
flaskbb/app.py

@@ -53,11 +53,11 @@ from flaskbb.utils.search import (ForumWhoosheer, PostWhoosheer,
 from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.translations import FlaskBBDomain
 from flaskbb.utils.translations import FlaskBBDomain
 
 
-from . import markup
-from .auth import views as auth_views
-from .forum import views as forum_views
-from .management import views as management_views
-from .user import views as user_views
+from . import markup  # noqa
+from .auth import views as auth_views  # noqa
+from .forum import views as forum_views  # noqa
+from .management import views as management_views  # noqa
+from .user import views as user_views  # noqa
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 

+ 39 - 10
flaskbb/auth/services/__init__.py

@@ -12,16 +12,45 @@
 """
 """
 
 
 from .activation import AccountActivator
 from .activation import AccountActivator
-from .authentication import (BlockTooManyFailedLogins, BlockUnactivatedUser,
-                             DefaultFlaskBBAuthProvider,
-                             FailedLoginConfiguration, MarkFailedLogin,
-                             PluginAuthenticationManager)
-from .factories import (account_activator_factory,
-                        authentication_manager_factory,
-                        reauthentication_manager_factory,
-                        registration_service_factory, reset_service_factory)
+from .authentication import (
+    BlockTooManyFailedLogins,
+    BlockUnactivatedUser,
+    DefaultFlaskBBAuthProvider,
+    FailedLoginConfiguration,
+    MarkFailedLogin,
+    PluginAuthenticationManager,
+)
+from .factories import (
+    account_activator_factory,
+    authentication_manager_factory,
+    reauthentication_manager_factory,
+    registration_service_factory,
+    reset_service_factory,
+)
 from .password import ResetPasswordService
 from .password import ResetPasswordService
 from .registration import (
 from .registration import (
-    EmailUniquenessValidator, UsernameRequirements,
-    UsernameUniquenessValidator, UsernameValidator
+    EmailUniquenessValidator,
+    UsernameRequirements,
+    UsernameUniquenessValidator,
+    UsernameValidator,
+)
+
+__all__ = (
+    "AccountActivator",
+    "account_activator_factory",
+    "authentication_manager_factory",
+    "BlockTooManyFailedLogins",
+    "BlockUnactivatedUser",
+    "DefaultFlaskBBAuthProvider",
+    "EmailUniquenessValidator",
+    "FailedLoginConfiguration",
+    "MarkFailedLogin",
+    "PluginAuthenticationManager",
+    "reauthentication_manager_factory",
+    "registration_service_factory",
+    "ResetPasswordService",
+    "reset_service_factory",
+    "UsernameRequirements",
+    "UsernameUniquenessValidator",
+    "UsernameValidator",
 )
 )

+ 1 - 1
flaskbb/auth/services/authentication.py

@@ -161,7 +161,7 @@ class PluginAuthenticationManager(AuthenticationManager):
                 raise StopAuthentication(_("Wrong username or password."))
                 raise StopAuthentication(_("Wrong username or password."))
             self.plugin_manager.hook.flaskbb_post_authenticate(user=user)
             self.plugin_manager.hook.flaskbb_post_authenticate(user=user)
             return user
             return user
-        except StopAuthentication as e:
+        except StopAuthentication:
             self.plugin_manager.hook.flaskbb_authentication_failed(
             self.plugin_manager.hook.flaskbb_authentication_failed(
                 identifier=identifier
                 identifier=identifier
             )
             )

+ 1 - 0
flaskbb/auth/services/factories.py

@@ -69,5 +69,6 @@ def account_activator_factory():
 def authentication_manager_factory():
 def authentication_manager_factory():
     return PluginAuthenticationManager(current_app.pluggy, db.session)
     return PluginAuthenticationManager(current_app.pluggy, db.session)
 
 
+
 def reauthentication_manager_factory():
 def reauthentication_manager_factory():
     return PluginReauthenticationManager(current_app.pluggy, db.session)
     return PluginReauthenticationManager(current_app.pluggy, db.session)

+ 1 - 1
flaskbb/auth/services/reauthentication.py

@@ -73,7 +73,7 @@ class PluginReauthenticationManager(ReauthenticateManager):
             if not result:
             if not result:
                 raise StopAuthentication(_("Wrong password."))
                 raise StopAuthentication(_("Wrong password."))
             self.plugin_manager.hook.flaskbb_post_reauth(user=user)
             self.plugin_manager.hook.flaskbb_post_reauth(user=user)
-        except StopAuthentication as e:
+        except StopAuthentication:
             self.plugin_manager.hook.flaskbb_reauth_failed(user=user)
             self.plugin_manager.hook.flaskbb_reauth_failed(user=user)
             raise
             raise
         finally:
         finally:

+ 1 - 1
flaskbb/auth/services/registration.py

@@ -48,7 +48,7 @@ class UsernameValidator(UserValidator):
             raise ValidationError(
             raise ValidationError(
                 'username',
                 'username',
                 _(
                 _(
-                    'Username must be between %(min)s and %(max)s characters long',
+                    'Username must be between %(min)s and %(max)s characters long',  # noqa
                     min=self._requirements.min,
                     min=self._requirements.min,
                     max=self._requirements.max
                     max=self._requirements.max
                 )
                 )

+ 3 - 3
flaskbb/auth/views.py

@@ -100,7 +100,7 @@ class Reauth(MethodView):
 
 
             reauth_manager = self.reauthentication_factory()
             reauth_manager = self.reauthentication_factory()
             try:
             try:
-                user = reauth_manager.reauthenticate(
+                reauth_manager.reauthenticate(
                     user=current_user, secret=form.password.data
                     user=current_user, secret=form.password.data
                 )
                 )
                 confirm_login()
                 confirm_login()
@@ -241,7 +241,7 @@ class ResetPassword(MethodView):
                     db.session.commit()
                     db.session.commit()
                 except Exception:
                 except Exception:
                     logger.exception(
                     logger.exception(
-                        "Error while finalizing database when resetting password"
+                        "Error while finalizing database when resetting password"  # noqa
                     )
                     )
                     db.session.rollback()
                     db.session.rollback()
 
 
@@ -310,7 +310,7 @@ class ActivateAccount(MethodView):
                 db.session.rollback()
                 db.session.rollback()
                 flash(
                 flash(
                     _(
                     _(
-                        "Could not activate account due to an unrecoverable error"
+                        "Could not activate account due to an unrecoverable error"  # noqa
                     ), "danger"
                     ), "danger"
                 )
                 )
 
 

+ 2 - 3
flaskbb/cli/main.py

@@ -18,7 +18,6 @@ from datetime import datetime
 
 
 import click
 import click
 import click_log
 import click_log
-import requests
 from celery.bin.celery import CeleryCommand
 from celery.bin.celery import CeleryCommand
 from flask import current_app
 from flask import current_app
 from flask.cli import FlaskGroup, ScriptInfo, with_appcontext
 from flask.cli import FlaskGroup, ScriptInfo, with_appcontext
@@ -57,7 +56,7 @@ class FlaskBBGroup(FlaskGroup):
             app = ctx.ensure_object(ScriptInfo).load_app()
             app = ctx.ensure_object(ScriptInfo).load_app()
             app.pluggy.hook.flaskbb_cli(cli=self, app=app)
             app.pluggy.hook.flaskbb_cli(cli=self, app=app)
             self._loaded_flaskbb_plugins = True
             self._loaded_flaskbb_plugins = True
-        except Exception as exc:
+        except Exception:
             logger.error(
             logger.error(
                 "Error while loading CLI Plugins",
                 "Error while loading CLI Plugins",
                 exc_info=traceback.format_exc()
                 exc_info=traceback.format_exc()
@@ -517,7 +516,7 @@ def generate_config(development, output, force):
 
 
     click.secho("Optional filepath to load a logging configuration file from. "
     click.secho("Optional filepath to load a logging configuration file from. "
                 "See the Python logging documentation for more detail.\n"
                 "See the Python logging documentation for more detail.\n"
-                "\thttps://docs.python.org/library/logging.config.html#logging-config-fileformat",
+                "\thttps://docs.python.org/library/logging.config.html#logging-config-fileformat",  # noqa
                 fg="cyan")
                 fg="cyan")
     default_conf["log_config_path"] = click.prompt(
     default_conf["log_config_path"] = click.prompt(
         click.style("Logging Config Path", fg="magenta"),
         click.style("Logging Config Path", fg="magenta"),

+ 2 - 1
flaskbb/core/auth/activation.py

@@ -29,7 +29,8 @@ class AccountActivator(ABC):
         form, but is recommended to take the form of a permanent communication
         form, but is recommended to take the form of a permanent communication
         such as email.
         such as email.
 
 
-        This method may raise :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`
+        This method may raise
+        :class:`ValidationError<flaskbb.core.exceptions.ValidationError>`
         to communicate a failure when creating the token for the user to
         to communicate a failure when creating the token for the user to
         activate their account with (such as when a user has requested a token
         activate their account with (such as when a user has requested a token
         be sent to an email that is not registered in the installation or
         be sent to an email that is not registered in the installation or

+ 2 - 2
flaskbb/core/auth/registration.py

@@ -43,7 +43,7 @@ class UserValidator(ABC):
 
 
         :param user_info: The provided registration information.
         :param user_info: The provided registration information.
         :type user_info: :class:`~flaskbb.core.auth.registration.UserRegistrationInfo`
         :type user_info: :class:`~flaskbb.core.auth.registration.UserRegistrationInfo`
-        """
+        """  # noqa
 
 
     def __call__(self, user_info):
     def __call__(self, user_info):
         return self.validate(user_info)
         return self.validate(user_info)
@@ -63,5 +63,5 @@ class UserRegistrationService(ABC):
 
 
         :param user_info: The provided user registration information.
         :param user_info: The provided user registration information.
         :type user_info: :class:`~flaskbb.core.auth.registration.UserRegistrationInfo`
         :type user_info: :class:`~flaskbb.core.auth.registration.UserRegistrationInfo`
-        """
+        """  # noqa
         pass
         pass

+ 0 - 1
flaskbb/exceptions.py

@@ -11,7 +11,6 @@ from werkzeug.exceptions import HTTPException, Forbidden
 from .core.exceptions import BaseFlaskBBError
 from .core.exceptions import BaseFlaskBBError
 
 
 
 
-
 class FlaskBBHTTPError(BaseFlaskBBError, HTTPException):
 class FlaskBBHTTPError(BaseFlaskBBError, HTTPException):
     description = "An internal error has occured"
     description = "An internal error has occured"
 
 

+ 6 - 4
flaskbb/extensions.py

@@ -43,9 +43,9 @@ class FlaskBBWhooshee(Whooshee):
         """
         """
         self.whoosheers.append(wh)
         self.whoosheers.append(wh)
         for model in wh.models:
         for model in wh.models:
-            event.listen(model, 'after_{0}'.format(INSERT_KWD), self.after_insert)
-            event.listen(model, 'after_{0}'.format(UPDATE_KWD), self.after_update)
-            event.listen(model, 'after_{0}'.format(DELETE_KWD), self.after_delete)
+            event.listen(model, 'after_{0}'.format(INSERT_KWD), self.after_insert)  # noqa
+            event.listen(model, 'after_{0}'.format(UPDATE_KWD), self.after_update)  # noqa
+            event.listen(model, 'after_{0}'.format(DELETE_KWD), self.after_delete)  # noqa
             query_class = getattr(model, 'query_class', None)
             query_class = getattr(model, 'query_class', None)
 
 
             if query_class is not None and isclass(query_class):
             if query_class is not None and isclass(query_class):
@@ -57,7 +57,9 @@ class FlaskBBWhooshee(Whooshee):
                 elif query_class not in (BaseQuery, SQLAQuery, WhoosheeQuery):
                 elif query_class not in (BaseQuery, SQLAQuery, WhoosheeQuery):
                     query_class_name = query_class.__name__
                     query_class_name = query_class.__name__
                     model.query_class = type(
                     model.query_class = type(
-                        "Whooshee{}".format(query_class_name), (query_class, self.query), {}
+                        "Whooshee{}".format(query_class_name),
+                        (query_class, self.query),
+                        {}
                     )
                     )
                 else:
                 else:
                     model.query_class = self.query
                     model.query_class = self.query

+ 321 - 223
flaskbb/fixtures/settings.py

@@ -8,12 +8,9 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
     :license: BSD, see LICENSE for more details.
 """
 """
-# import .. as .. to make it backwards compatible
-from flaskbb.utils.helpers import \
-    get_available_languages as available_languages, \
-    get_available_themes as available_themes
-
 from flaskbb.utils.forms import SettingValueType
 from flaskbb.utils.forms import SettingValueType
+from flaskbb.utils.helpers import get_available_languages as available_languages
+from flaskbb.utils.helpers import get_available_themes as available_themes
 
 
 
 
 def available_avatar_types():
 def available_avatar_types():
@@ -22,222 +19,323 @@ def available_avatar_types():
 
 
 fixture = (
 fixture = (
     # Settings Group
     # Settings Group
-    ('general', {
-        'name': "General Settings",
-        'description': "How many items per page are displayed.",
-        'settings': (
-            ('project_title', {
-                'value':        "FlaskBB",
-                'value_type':   SettingValueType.string,
-                'name':         "Project title",
-                'description':  "The title of the project.",
-            }),
-            ('project_subtitle', {
-                'value':        "A lightweight forum software in Flask",
-                'value_type':   SettingValueType.string,
-                'name':         "Project subtitle",
-                'description':  "A short description of the project.",
-            }),
-            ('project_copyright', {
-                'value':        "",
-                'value_type':   SettingValueType.string,
-                'name':         "Project Copyright",
-                'description':  "Copyright notice of the Project like '&copy; 2018 FlaskBB'. Leave blank to ignore.",
-            }),
-            ('posts_per_page', {
-                'value':        10,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 5},
-                'name':         "Posts per page",
-                'description':  "Number of posts displayed per page.",
-            }),
-            ('topics_per_page', {
-                'value':        10,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 5},
-                'name':         "Topics per page",
-                'description':  "Number of topics displayed per page.",
-            }),
-            ('users_per_page', {
-                'value':        10,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 5},
-                'name':         "Users per page",
-                'description':  "Number of users displayed per page.",
-            }),
-        ),
-    }),
-    ('auth', {
-        'name': 'Authentication Settings',
-        'description': 'Configurations for the Login and Register process.',
-        'settings': (
-            ('registration_enabled', {
-                'value':        True,
-                'value_type':   SettingValueType.boolean,
-                'name':         "Enable Registration",
-                'description':  "Enable or disable the registration",
-            }),
-            ('activate_account', {
-                'value':        True,
-                'value_type':   SettingValueType.boolean,
-                'name':         "Enable Account Activation",
-                'description':  "Enable to let the user activate their account by sending a email with an activation link."
-            }),
-            ('auth_username_min_length', {
-                'value':        3,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Username Minimum Length",
-                'description':  "The minimum length of the username. Changing this will only affect new registrations.",
-            }),
-            ('auth_username_max_length', {
-                'value':        20,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Username Maximum Length",
-                'description':  "The Maximum length of the username. Changing this will only affect new registrations.",
-            }),
-            ('auth_username_blacklist', {
-                'value':        "me,administrator,moderator",
-                'value_type':   SettingValueType.string,
-                'name':         "Username Blacklist",
-                'description':  "A comma seperated list with forbidden usernames.",
-            }),
-            ('auth_ratelimit_enabled', {
-                'value':        True,
-                'value_type':   SettingValueType.boolean,
-                'name':         "Enable Auth Rate Limiting",
-                'description':  "Enable rate limiting on 'auth' routes. This will limit the amount of requests per minute to a given amount and time.",
-            }),
-            ('auth_requests', {
-                'value':        20,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 1},
-                'name':         "Auth Requests",
-                'description':  "Number of requests on each 'auth' route before the user has to wait a given timeout until he can access the resource again.",
-            }),
-            ('auth_timeout', {
-                'value':        15,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Auth Timeout",
-                'description':  "The timeout for how long the user has to wait until he can access the resource again (in minutes).",
-            }),
-            ('login_recaptcha', {
-                'value':        5,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Login reCAPTCHA",
-                'description':  "Use a CAPTCHA after a specified amount of failed login attempts."
-            }),
-            ('recaptcha_enabled', {
-                'value':        False,
-                'value_type':   SettingValueType.boolean,
-                'name':         "Enable reCAPTCHA",
-                'description':  ("Helps to prevent bots from creating accounts. "
-                                 "For more information visit this link: <a href=http://www.google.com/recaptcha>http://www.google.com/recaptcha</a>"),
-            }),
-            ('recaptcha_public_key', {
-                'value':        "",
-                'value_type':   SettingValueType.string,
-                'name':         "reCAPTCHA Site Key",
-                'description':  "Your public recaptcha key ('Site key').",
-            }),
-            ('recaptcha_private_key', {
-                'value':        "",
-                'value_type':   SettingValueType.string,
-                'name':         "reCAPTCHA Secret Key",
-                'description':  "The private key ('Secret key'). Keep this a secret!",
-            }),
-        ),
-    }),
-    ('misc', {
-        'name': "Misc Settings",
-        'description': "Miscellaneous settings.",
-        'settings': (
-            ('message_quota', {
-                'value':        50,
-                'value_type':   SettingValueType.integer,
-                'extra':        {"min": 0},
-                'name':         "Private Message Quota",
-                'description':  "The amount of messages a user can have."
-            }),
-            ('online_last_minutes', {
-                'value':        15,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Online last minutes",
-                'description':  "How long a user can be inactive before he is marked as offline. 0 to disable it.",
-            }),
-            ('title_length', {
-                'value':        15,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Topic title length",
-                'description':  "The length of the topic title shown on the index."
-            }),
-            ('tracker_length', {
-                'value':        7,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Tracker length",
-                'description':  "The days for how long the forum should deal with unread topics. 0 to disable it."
-            }),
-            ('avatar_height', {
-                'value':        150,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Avatar Height",
-                'description':  "The allowed height of an avatar in pixels."
-            }),
-            ('avatar_width', {
-                'value':        150,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Avatar Width",
-                'description':  "The allowed width of an avatar in pixels."
-            }),
-            ('avatar_size', {
-                'value':        200,
-                'value_type':   SettingValueType.integer,
-                'extra':        {'min': 0},
-                'name':         "Avatar Size",
-                'description':  "The allowed size of the avatar in kilobytes."
-            }),
-            ('avatar_types', {
-                'value':        ["PNG", "JPEG", "GIF"],
-                'value_type':   SettingValueType.selectmultiple,
-                'extra':        {"choices": available_avatar_types},
-                'name':         "Avatar Types",
-                'description':  "The allowed types of an avatar. Such as JPEG, GIF or PNG."
-            }),
-            ('signature_enabled', {
-                'value':        True,
-                'value_type':   SettingValueType.boolean,
-                'extra':        {},
-                'name':         "Enable Signatures",
-                'description':  "Enable signatures in posts."
-            })
-        ),
-    }),
-    ('appearance', {
-        'name': "Appearance Settings",
-        "description": "Change the theme and language for your forum.",
-        "settings": (
-            ('default_theme', {
-                'value':        "aurora",
-                'value_type':   SettingValueType.select,
-                'extra':        {'choices': available_themes},
-                'name':         "Default Theme",
-                'description':  "Change the default theme for your forum."
-            }),
-            ('default_language', {
-                'value':        "en",
-                'value_type':   SettingValueType.select,
-                'extra':        {'choices': available_languages},
-                'name':         "Default Language",
-                'description':  "Change the default language for your forum."
-            }),
-        ),
-    }),
+    (
+        "general",
+        {
+            "name": "General Settings",
+            "description": "How many items per page are displayed.",
+            "settings": (
+                (
+                    "project_title",
+                    {
+                        "value": "FlaskBB",
+                        "value_type": SettingValueType.string,
+                        "name": "Project title",
+                        "description": "The title of the project.",
+                    },
+                ),
+                (
+                    "project_subtitle",
+                    {
+                        "value": "A lightweight forum software in Flask",
+                        "value_type": SettingValueType.string,
+                        "name": "Project subtitle",
+                        "description": "A short description of the project.",
+                    },
+                ),
+                (
+                    "project_copyright",
+                    {
+                        "value": "",
+                        "value_type": SettingValueType.string,
+                        "name": "Project Copyright",
+                        "description": "Copyright notice of the Project like '&copy; 2018 FlaskBB'. Leave blank to ignore.",  # noqa
+                    },
+                ),
+                (
+                    "posts_per_page",
+                    {
+                        "value": 10,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 5},
+                        "name": "Posts per page",
+                        "description": "Number of posts displayed per page.",
+                    },
+                ),
+                (
+                    "topics_per_page",
+                    {
+                        "value": 10,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 5},
+                        "name": "Topics per page",
+                        "description": "Number of topics displayed per page.",
+                    },
+                ),
+                (
+                    "users_per_page",
+                    {
+                        "value": 10,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 5},
+                        "name": "Users per page",
+                        "description": "Number of users displayed per page.",
+                    },
+                ),
+            ),
+        },
+    ),
+    (
+        "auth",
+        {
+            "name": "Authentication Settings",
+            "description": "Configurations for the Login and Register process.",
+            "settings": (
+                (
+                    "registration_enabled",
+                    {
+                        "value": True,
+                        "value_type": SettingValueType.boolean,
+                        "name": "Enable Registration",
+                        "description": "Enable or disable the registration",
+                    },
+                ),
+                (
+                    "activate_account",
+                    {
+                        "value": True,
+                        "value_type": SettingValueType.boolean,
+                        "name": "Enable Account Activation",
+                        "description": "Enable to let the user activate their account by sending a email with an activation link.",  # noqa
+                    },
+                ),
+                (
+                    "auth_username_min_length",
+                    {
+                        "value": 3,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Username Minimum Length",
+                        "description": "The minimum length of the username. Changing this will only affect new registrations.",  # noqa
+                    },
+                ),
+                (
+                    "auth_username_max_length",
+                    {
+                        "value": 20,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Username Maximum Length",
+                        "description": "The Maximum length of the username. Changing this will only affect new registrations.",  # noqa
+                    },
+                ),
+                (
+                    "auth_username_blacklist",
+                    {
+                        "value": "me,administrator,moderator",
+                        "value_type": SettingValueType.string,
+                        "name": "Username Blacklist",
+                        "description": "A comma seperated list with forbidden usernames.",  # noqa
+                    },
+                ),
+                (
+                    "auth_ratelimit_enabled",
+                    {
+                        "value": True,
+                        "value_type": SettingValueType.boolean,
+                        "name": "Enable Auth Rate Limiting",
+                        "description": "Enable rate limiting on 'auth' routes. This will limit the amount of requests per minute to a given amount and time.",  # noqa
+                    },
+                ),
+                (
+                    "auth_requests",
+                    {
+                        "value": 20,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 1},
+                        "name": "Auth Requests",
+                        "description": "Number of requests on each 'auth' route before the user has to wait a given timeout until he can access the resource again.",  # noqa
+                    },
+                ),
+                (
+                    "auth_timeout",
+                    {
+                        "value": 15,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Auth Timeout",
+                        "description": "The timeout for how long the user has to wait until he can access the resource again (in minutes).",  # noqa
+                    },
+                ),
+                (
+                    "login_recaptcha",
+                    {
+                        "value": 5,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Login reCAPTCHA",
+                        "description": "Use a CAPTCHA after a specified amount of failed login attempts.",  # noqa
+                    },
+                ),
+                (
+                    "recaptcha_enabled",
+                    {
+                        "value": False,
+                        "value_type": SettingValueType.boolean,
+                        "name": "Enable reCAPTCHA",
+                        "description": (
+                            "Helps to prevent bots from creating accounts. "
+                            "For more information visit this link: <a href=http://www.google.com/recaptcha>http://www.google.com/recaptcha</a>"  # noqa
+                        ),
+                    },
+                ),
+                (
+                    "recaptcha_public_key",
+                    {
+                        "value": "",
+                        "value_type": SettingValueType.string,
+                        "name": "reCAPTCHA Site Key",
+                        "description": "Your public recaptcha key ('Site key').",  # noqa
+                    },
+                ),
+                (
+                    "recaptcha_private_key",
+                    {
+                        "value": "",
+                        "value_type": SettingValueType.string,
+                        "name": "reCAPTCHA Secret Key",
+                        "description": "The private key ('Secret key'). Keep this a secret!",  # noqa
+                    },
+                ),
+            ),
+        },
+    ),
+    (
+        "misc",
+        {
+            "name": "Misc Settings",
+            "description": "Miscellaneous settings.",
+            "settings": (
+                (
+                    "message_quota",
+                    {
+                        "value": 50,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Private Message Quota",
+                        "description": "The amount of messages a user can have.",  # noqa
+                    },
+                ),
+                (
+                    "online_last_minutes",
+                    {
+                        "value": 15,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Online last minutes",
+                        "description": "How long a user can be inactive before he is marked as offline. 0 to disable it.",  # noqa
+                    },
+                ),
+                (
+                    "title_length",
+                    {
+                        "value": 15,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Topic title length",
+                        "description": "The length of the topic title shown on the index.",  # noqa
+                    },
+                ),
+                (
+                    "tracker_length",
+                    {
+                        "value": 7,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Tracker length",
+                        "description": "The days for how long the forum should deal with unread topics. 0 to disable it.",  # noqa
+                    },
+                ),
+                (
+                    "avatar_height",
+                    {
+                        "value": 150,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Avatar Height",
+                        "description": "The allowed height of an avatar in pixels.",  # noqa
+                    },
+                ),
+                (
+                    "avatar_width",
+                    {
+                        "value": 150,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Avatar Width",
+                        "description": "The allowed width of an avatar in pixels.",  # noqa
+                    },
+                ),
+                (
+                    "avatar_size",
+                    {
+                        "value": 200,
+                        "value_type": SettingValueType.integer,
+                        "extra": {"min": 0},
+                        "name": "Avatar Size",
+                        "description": "The allowed size of the avatar in kilobytes.",  # noqa
+                    },
+                ),
+                (
+                    "avatar_types",
+                    {
+                        "value": ["PNG", "JPEG", "GIF"],
+                        "value_type": SettingValueType.selectmultiple,
+                        "extra": {"choices": available_avatar_types},
+                        "name": "Avatar Types",
+                        "description": "The allowed types of an avatar. Such as JPEG, GIF or PNG.",  # noqa
+                    },
+                ),
+                (
+                    "signature_enabled",
+                    {
+                        "value": True,
+                        "value_type": SettingValueType.boolean,
+                        "extra": {},
+                        "name": "Enable Signatures",
+                        "description": "Enable signatures in posts.",
+                    },
+                ),
+            ),
+        },
+    ),
+    (
+        "appearance",
+        {
+            "name": "Appearance Settings",
+            "description": "Change the theme and language for your forum.",
+            "settings": (
+                (
+                    "default_theme",
+                    {
+                        "value": "aurora",
+                        "value_type": SettingValueType.select,
+                        "extra": {"choices": available_themes},
+                        "name": "Default Theme",
+                        "description": "Change the default theme for your forum.",  # noqa
+                    },
+                ),
+                (
+                    "default_language",
+                    {
+                        "value": "en",
+                        "value_type": SettingValueType.select,
+                        "extra": {"choices": available_languages},
+                        "name": "Default Language",
+                        "description": "Change the default language for your forum.",  # noqa
+                    },
+                ),
+            ),
+        },
+    ),
 )
 )

+ 23 - 11
flaskbb/forum/models.py

@@ -304,10 +304,10 @@ class Post(HideableCRUDMixin, db.Model):
                 if second_last_post:
                 if second_last_post:
                     # now lets update the second last post to the last post
                     # now lets update the second last post to the last post
                     self.topic.forum.last_post = second_last_post
                     self.topic.forum.last_post = second_last_post
-                    self.topic.forum.last_post_title = second_last_post.topic.title
+                    self.topic.forum.last_post_title = second_last_post.topic.title  # noqa
                     self.topic.forum.last_post_user = second_last_post.user
                     self.topic.forum.last_post_user = second_last_post.user
-                    self.topic.forum.last_post_username = second_last_post.username
-                    self.topic.forum.last_post_created = second_last_post.date_created
+                    self.topic.forum.last_post_username = second_last_post.username  # noqa
+                    self.topic.forum.last_post_created = second_last_post.date_created  # noqa
                 else:
                 else:
                     self.topic.forum.last_post = None
                     self.topic.forum.last_post = None
                     self.topic.forum.last_post_title = None
                     self.topic.forum.last_post_title = None
@@ -370,7 +370,10 @@ class Post(HideableCRUDMixin, db.Model):
         ).limit(1).first()
         ).limit(1).first()
 
 
         # should never be None, but deal with it anyways to be safe
         # should never be None, but deal with it anyways to be safe
-        if last_unhidden_post and self.date_created > last_unhidden_post.date_created:
+        if (
+            last_unhidden_post and
+            self.date_created > last_unhidden_post.date_created
+        ):
             self.topic.last_post = self
             self.topic.last_post = self
             self.second_last_post = last_unhidden_post
             self.second_last_post = last_unhidden_post
 
 
@@ -483,9 +486,9 @@ class Topic(HideableCRUDMixin, db.Model):
             self.title = title
             self.title = title
 
 
         if user:
         if user:
-            # setting the user here, even with setting the id, breaks the bulk insert
-            # stuff as they use the session.bulk_save_objects which does not trigger
-            # relationships
+            # setting the user here, even with setting the id, breaks the bulk
+            # insert stuff as they use the session.bulk_save_objects which does
+            # not trigger relationships
             self.user_id = user.id
             self.user_id = user.id
             self.username = user.username
             self.username = user.username
 
 
@@ -783,12 +786,17 @@ class Topic(HideableCRUDMixin, db.Model):
         if self.hidden:
         if self.hidden:
             post_count.append(Post.hidden != True)
             post_count.append(Post.hidden != True)
         else:
         else:
-            post_count.append(db.or_(Post.hidden != True, Post.id == self.first_post.id))
+            post_count.append(
+                db.or_(Post.hidden != True, Post.id == self.first_post.id)
+            )
 
 
         forum.post_count = Post.query.distinct().filter(*post_count).count()
         forum.post_count = Post.query.distinct().filter(*post_count).count()
 
 
     def _restore_topic_to_forum(self):
     def _restore_topic_to_forum(self):
-        if self.forum.last_post is None or self.forum.last_post_created < self.last_updated:
+        if (
+            self.forum.last_post is None or
+            self.forum.last_post_created < self.last_updated
+        ):
             self.forum.last_post = self.last_post
             self.forum.last_post = self.last_post
             self.forum.last_post_title = self.title
             self.forum.last_post_title = self.title
             self.forum.last_post_user = self.user
             self.forum.last_post_user = self.user
@@ -807,7 +815,9 @@ class Topic(HideableCRUDMixin, db.Model):
         """
         """
         # todo: Find circular import and break it
         # todo: Find circular import and break it
         from flaskbb.user.models import User
         from flaskbb.user.models import User
-        return User.query.distinct().filter(Post.topic_id == self.id, User.id == Post.user_id)
+        return User.query.distinct().filter(
+            Post.topic_id == self.id, User.id == Post.user_id
+        )
 
 
 
 
 @make_comparable
 @make_comparable
@@ -1010,7 +1020,9 @@ class Forum(db.Model, CRUDMixin):
         :param last_post: If set to ``True`` it will also try to update
         :param last_post: If set to ``True`` it will also try to update
                           the last post columns in the forum.
                           the last post columns in the forum.
         """
         """
-        topic_count = Topic.query.filter(Topic.forum_id == self.id, Topic.hidden != True).count()
+        topic_count = Topic.query.filter(
+            Topic.forum_id == self.id, Topic.hidden != True
+        ).count()
         post_count = Post.query.filter(
         post_count = Post.query.filter(
             Post.topic_id == Topic.id,
             Post.topic_id == Topic.id,
             Topic.forum_id == self.id,
             Topic.forum_id == self.id,

+ 2 - 0
flaskbb/management/__init__.py

@@ -14,4 +14,6 @@ import logging
 # force plugins to be loaded
 # force plugins to be loaded
 from . import plugins
 from . import plugins
 
 
+__all__ = ('plugins', )
+
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)

+ 5 - 2
flaskbb/plugins/spec.py

@@ -256,7 +256,8 @@ def flaskbb_authenticate(identifier, secret):
             if user is not None:
             if user is not None:
                 if has_too_many_failed_logins(user):
                 if has_too_many_failed_logins(user):
                     raise StopAuthentication(_(
                     raise StopAuthentication(_(
-                        "Your account is temporarily locked due to too many login attempts"
+                        "Your account is temporarily locked due to too many"
+                        " login attempts"
                     ))
                     ))
 
 
         @impl(tryfirst=True)
         @impl(tryfirst=True)
@@ -357,7 +358,9 @@ def flaskbb_reauth_attempt(user, secret):
         @impl
         @impl
         def flaskbb_reauth_attempt(user, secret):
         def flaskbb_reauth_attempt(user, secret):
             if user.login_attempts > 5:
             if user.login_attempts > 5:
-                raise StopAuthentication(_("Too many failed authentication attempts"))
+                raise StopAuthentication(
+                    _("Too many failed authentication attempts")
+                )
     """
     """
 
 
 
 

+ 4 - 4
flaskbb/plugins/utils.py

@@ -24,9 +24,9 @@ def template_hook(name, silent=True, is_markup=True, **kwargs):
     :param name: The name of the hook.
     :param name: The name of the hook.
     :param silent: If set to ``False``, it will raise an exception if a hook
     :param silent: If set to ``False``, it will raise an exception if a hook
                    doesn't exist. Defauls to ``True``.
                    doesn't exist. Defauls to ``True``.
-    :param is_markup: Determines if the hook should return a Markup object or not.
-                      Setting to False returns a TemplateEventResult object. The
-                      default is True.
+    :param is_markup: Determines if the hook should return a Markup object or
+                   not. Setting to False returns a TemplateEventResult object.
+                   The default is True.
     :param kwargs: Additional kwargs that should be passed to the hook.
     :param kwargs: Additional kwargs that should be passed to the hook.
     """
     """
     try:
     try:
@@ -60,7 +60,7 @@ def remove_zombie_plugins_from_db():
     Returns the names of the deleted plugins.
     Returns the names of the deleted plugins.
     """
     """
     d_fs_plugins = [p[0] for p in current_app.pluggy.list_disabled_plugins()]
     d_fs_plugins = [p[0] for p in current_app.pluggy.list_disabled_plugins()]
-    d_db_plugins = [p.name for p in PluginRegistry.query.filter_by(enabled=False).all()]
+    d_db_plugins = [p.name for p in PluginRegistry.query.filter_by(enabled=False).all()]  # noqa
 
 
     plugin_names = [p.name for p in PluginRegistry.query.all()]
     plugin_names = [p.name for p in PluginRegistry.query.all()]
 
 

+ 2 - 0
flaskbb/tokens/__init__.py

@@ -8,3 +8,5 @@
 """
 """
 
 
 from .serializer import FlaskBBTokenSerializer
 from .serializer import FlaskBBTokenSerializer
+
+__all__ = ("FlaskBBTokenSerializer",)

+ 2 - 0
flaskbb/user/__init__.py

@@ -13,4 +13,6 @@ import logging
 # force plugins to be loaded
 # force plugins to be loaded
 from . import plugins
 from . import plugins
 
 
+__all__ = ('plugins',)
+
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)

+ 20 - 12
flaskbb/utils/database.py

@@ -85,13 +85,17 @@ class HideableQuery(BaseQuery):
 
 
     def __new__(cls, *args, **kwargs):
     def __new__(cls, *args, **kwargs):
         inst = super(HideableQuery, cls).__new__(cls)
         inst = super(HideableQuery, cls).__new__(cls)
-        with_hidden = kwargs.pop(
-            '_with_hidden', False
-        ) or (current_user and current_user.permissions.get('viewhidden', False))
+        include_hidden = kwargs.pop("_with_hidden", False)
+        has_view_hidden = current_user and current_user.permissions.get(
+            "viewhidden", False
+        )
+        with_hidden = include_hidden or has_view_hidden
         if args or kwargs:
         if args or kwargs:
             super(HideableQuery, inst).__init__(*args, **kwargs)
             super(HideableQuery, inst).__init__(*args, **kwargs)
             entity = inst._mapper_zero().class_
             entity = inst._mapper_zero().class_
-            return inst.filter(entity.hidden != True) if not with_hidden else inst
+            return inst.filter(
+                entity.hidden != True
+            ) if not with_hidden else inst
         return inst
         return inst
 
 
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
@@ -99,16 +103,20 @@ class HideableQuery(BaseQuery):
 
 
     def with_hidden(self):
     def with_hidden(self):
         return self.__class__(
         return self.__class__(
-            db.class_mapper(self._mapper_zero().class_), session=db.session(), _with_hidden=True
+            db.class_mapper(self._mapper_zero().class_),
+            session=db.session(),
+            _with_hidden=True,
         )
         )
 
 
     def _get(self, *args, **kwargs):
     def _get(self, *args, **kwargs):
         return super(HideableQuery, self).get(*args, **kwargs)
         return super(HideableQuery, self).get(*args, **kwargs)
 
 
     def get(self, *args, **kwargs):
     def get(self, *args, **kwargs):
-        include_hidden = kwargs.pop('include_hidden', False)
+        include_hidden = kwargs.pop("include_hidden", False)
         obj = self.with_hidden()._get(*args, **kwargs)
         obj = self.with_hidden()._get(*args, **kwargs)
-        return obj if obj is not None and (include_hidden or not obj.hidden) else None
+        return obj if obj is not None and (
+            include_hidden or not obj.hidden
+        ) else None
 
 
 
 
 class HideableMixin(object):
 class HideableMixin(object):
@@ -121,16 +129,16 @@ class HideableMixin(object):
     def hidden_by_id(cls):
     def hidden_by_id(cls):
         return db.Column(
         return db.Column(
             db.Integer,
             db.Integer,
-            db.ForeignKey('users.id', name='fk_{}_hidden_by'.format(cls.__name__)),
-            nullable=True
+            db.ForeignKey(
+                "users.id", name="fk_{}_hidden_by".format(cls.__name__)
+            ),
+            nullable=True,
         )
         )
 
 
     @declared_attr
     @declared_attr
     def hidden_by(cls):
     def hidden_by(cls):
         return db.relationship(
         return db.relationship(
-            'User',
-            uselist=False,
-            foreign_keys=[cls.hidden_by_id],
+            "User", uselist=False, foreign_keys=[cls.hidden_by_id]
         )
         )
 
 
     def hide(self, user, *args, **kwargs):
     def hide(self, user, *args, **kwargs):

+ 12 - 4
flaskbb/utils/helpers.py

@@ -39,7 +39,6 @@ from flaskbb._compat import (iteritems, range_method, text_type, string_types,
                              to_bytes, to_unicode)
                              to_bytes, to_unicode)
 from flaskbb.extensions import babel, redis_store
 from flaskbb.extensions import babel, redis_store
 from flaskbb.utils.settings import flaskbb_config
 from flaskbb.utils.settings import flaskbb_config
-from jinja2 import Markup
 from PIL import ImageFile
 from PIL import ImageFile
 from pytz import UTC
 from pytz import UTC
 from werkzeug.local import LocalProxy
 from werkzeug.local import LocalProxy
@@ -126,7 +125,10 @@ def do_topic_action(topics, user, action, reverse):
 
 
     elif action == "delete":
     elif action == "delete":
         if not Permission(CanDeleteTopic):
         if not Permission(CanDeleteTopic):
-            flash(_("You do not have the permissions to delete these topics."), "danger")
+            flash(
+                _("You do not have the permissions to delete these topics."),
+                "danger"
+            )
             return False
             return False
 
 
         for topic in topics:
         for topic in topics:
@@ -135,7 +137,10 @@ def do_topic_action(topics, user, action, reverse):
 
 
     elif action == 'hide':
     elif action == 'hide':
         if not Permission(Has('makehidden')):
         if not Permission(Has('makehidden')):
-            flash(_("You do not have the permissions to hide these topics."), "danger")
+            flash(
+                _("You do not have the permissions to hide these topics."),
+                "danger"
+            )
             return False
             return False
 
 
         for topic in topics:
         for topic in topics:
@@ -146,7 +151,10 @@ def do_topic_action(topics, user, action, reverse):
 
 
     elif action == 'unhide':
     elif action == 'unhide':
         if not Permission(Has('makehidden')):
         if not Permission(Has('makehidden')):
-            flash(_("You do not have the permissions to unhide these topics."), "danger")
+            flash(
+                _("You do not have the permissions to unhide these topics."),
+                "danger"
+            )
             return False
             return False
 
 
         for topic in topics:
         for topic in topics:

+ 5 - 1
flaskbb/utils/populate.py

@@ -131,7 +131,11 @@ def update_settings_from_fixture(fixture, overwrite_group=False,
                     or setting.settingsgroup != group.key
                     or setting.settingsgroup != group.key
                 )
                 )
 
 
-            if (setting is not None and overwrite_setting and setting_is_different) or setting is None:
+            if (
+                setting is not None and
+                overwrite_setting and
+                setting_is_different
+            ) or setting is None:
                 if setting is not None:
                 if setting is not None:
                     setting.value = settings[1]["value"]
                     setting.value = settings[1]["value"]
                     setting.value_type = settings[1]["value_type"]
                     setting.value_type = settings[1]["value_type"]

+ 11 - 4
flaskbb/utils/requirements.py

@@ -155,19 +155,25 @@ class ForumNotLocked(Requirement):
 
 
 
 
 class CanAccessForum(Requirement):
 class CanAccessForum(Requirement):
+
     def fulfill(self, user, request):
     def fulfill(self, user, request):
         if not current_forum:
         if not current_forum:
-            raise FlaskBBError('Could not load forum data')
+            raise FlaskBBError("Could not load forum data")
 
 
-        return set([g.id for g in current_forum.groups]) & set([g.id for g in user.groups])
+        forum_groups = {g.id for g in current_forum.groups}
+        user_groups = {g.id for g in user.groups}
+        return forum_groups & user_groups
 
 
 
 
 class CanAccessTopic(Requirement):
 class CanAccessTopic(Requirement):
+
     def fulfill(self, user, request):
     def fulfill(self, user, request):
         if not current_forum:
         if not current_forum:
-            raise FlaskBBError('Could not load topic data')
+            raise FlaskBBError("Could not load topic data")
 
 
-        return set([g.id for g in current_forum.groups]) & set([g.id for g in user.groups])
+        forum_groups = {g.id for g in current_forum.groups}
+        user_groups = {g.id for g in user.groups}
+        return forum_groups & user_groups
 
 
 
 
 def IsAtleastModeratorInForum(forum_id=None, forum=None):
 def IsAtleastModeratorInForum(forum_id=None, forum=None):
@@ -260,6 +266,7 @@ def TplCanEditPost(request):
         )(user, request)
         )(user, request)
     return _
     return _
 
 
+
 TplCanDeletePost = TplCanEditPost
 TplCanDeletePost = TplCanEditPost
 
 
 
 

+ 4 - 3
tox.ini

@@ -10,7 +10,7 @@ setenv =
     COVERAGE_FILE = tests/.coverage.{envname}
     COVERAGE_FILE = tests/.coverage.{envname}
     PYTHONDONTWRITEBYTECODE=1
     PYTHONDONTWRITEBYTECODE=1
 commands =
 commands =
-    coverage run -m pytest {toxinidir}/tests
+    coverage run -m pytest {toxinidir}/tests {toxinidir}/flaskbb
 
 
 [testenv:cov-report]
 [testenv:cov-report]
 skip_install = true
 skip_install = true
@@ -33,9 +33,10 @@ commands =
 
 
 
 
 [flake8]
 [flake8]
-ignore = E712, E711, C901
+ignore = E712, E711, C901, W503
 max-complexity = 10
 max-complexity = 10
+max-line-length = 80
+exclude = flaskbb/configs/default.py,flaskbb/_compat.py
 
 
 [pytest]
 [pytest]
 addopts =  -vvl --strict --flake8 --capture fd
 addopts =  -vvl --strict --flake8 --capture fd
-