Skip to content

Commit

Permalink
Add configuration file parser (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
mcproger authored and sobolevn committed Jul 11, 2018
1 parent f743b96 commit 2291cc1
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 5 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,29 @@ poetry run doc8 -q docs
```

It's OK if some tests are skipped.


## Configuration

You can adjust configuration via CLI option:

```sh
flake8 --max-returns 7
```

or configuration option in `tox.ini`/`setup.cfg`.

```ini
max-returns = 7
```

There are the following options:

- `max-returns` - maximum allowed number of `return` statements in one function. Default value is 6.

- `max-local-variables` - maximum allowed number of local variables in one function. Default is 10.

- `max-expressions` - maximum allowed number of expressions in one function. Default value is 10.

- `max-arguments` - maximum allowed number of arguments in one function. Default value is 5.

59 changes: 59 additions & 0 deletions tests/test_options/test_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-

import subprocess


def test_max_variables_cli_option(absolute_path):
"""Test to check max-local-variables cli option."""
filename = absolute_path('fixtures', 'complexity', 'wrong_variables.py')
option_flag = '--max-local-variables'
option_value = '100'
process = subprocess.Popen(
['flake8', filename, option_flag, option_value],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout, _ = process.communicate()
assert stdout.count(b'WPS150') == 0


def test_max_arguments_cli_option(absolute_path):
"""Test to check max-arguments cli option."""
filename = absolute_path('fixtures', 'complexity', 'wrong_arguments.py')
option_flag = '--max-arguments'
option_value = '100'
process = subprocess.Popen(
['flake8', filename, option_flag, option_value],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout, _ = process.communicate()
assert stdout.count(b'WPS151') == 0


def test_max_returns_cli_option(absolute_path):
"""Test to check max-returns cli option."""
filename = absolute_path('fixtures', 'complexity', 'wrong_returns.py')
option_flag = '--max-returns'
option_value = '100'
process = subprocess.Popen(
['flake8', filename, option_flag, option_value],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout, _ = process.communicate()
assert stdout.count(b'WPS153') == 0


def test_max_expressions_cli_options(absolute_path):
"""Test to check max-expressions cli option."""
filename = absolute_path('fixtures', 'complexity', 'wrong_expressions.py')
option_flag = '--max-expressions'
option_value = '100'
process = subprocess.Popen(
['flake8', filename, option_flag, option_value],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout, _ = process.communicate()
assert stdout.count(b'WPS154') == 0
15 changes: 15 additions & 0 deletions wemake_python_styleguide/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from ast import Module
from typing import Generator, Tuple

from wemake_python_styleguide.options.config import Configuration
from wemake_python_styleguide.version import version
from wemake_python_styleguide.visitors.high_complexity import ComplexityVisitor
from wemake_python_styleguide.visitors.wrong_function_call import (
Expand Down Expand Up @@ -32,6 +33,9 @@ class Checker(object):
name = 'wemake-python-styleguide'
version = version

config = Configuration()
options = None # So that mypy could detect the attribute

def __init__(self, tree: Module, filename: str = '-') -> None:
"""Creates new checker instance."""
self.tree = tree
Expand All @@ -48,6 +52,16 @@ def __init__(self, tree: Module, filename: str = '-') -> None:
WrongModuleMetadataVisitor,
)

@classmethod
def add_options(cls, parser):
"""Calls Configuration instance method for registering options."""
cls.config.register_options(parser)

@classmethod
def parse_options(cls, options):
"""Parses registered options for providing to the visiter."""
cls.options = options

def run(self) -> Generator[CheckResult, None, None]:
"""
Runs the checker.
Expand All @@ -56,6 +70,7 @@ def run(self) -> Generator[CheckResult, None, None]:
"""
for visitor_class in self._visitors:
visiter = visitor_class()
visiter.provide_options(self.options)
visiter.visit(self.tree)

for error in visiter.errors:
Expand Down
1 change: 1 addition & 0 deletions wemake_python_styleguide/options/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
41 changes: 41 additions & 0 deletions wemake_python_styleguide/options/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-

from wemake_python_styleguide.options import defaults


class Configuration(object):
"""Provides method for registering options for WPS flake8 plugin."""

def register_options(self, parser):
"""Registers options for WPS plugin."""
parser.add_option(
'--max-returns',
parse_from_config=True,
type='int',
default=defaults.MAX_RETURNS,
help='Maximum allowed number of return statements in one function.',
)

parser.add_option(
'--max-local-variables',
parse_from_config=True,
type='int',
default=defaults.MAX_LOCAL_VARIABLES,
help='Maximum allowed number of local variables in one function.',
)

parser.add_option(
'--max-expressions',
parse_from_config=True,
type='int',
default=defaults.MAX_EXPRESSIONS,
help='Maximum allowed number of expressions in one function.',
)

parser.add_option(
'--max-arguments',
parse_from_config=True,
type='int',
default=defaults.MAX_ARGUMENTS,
help='Maximum allowed number of arguments in one function.',
)
11 changes: 11 additions & 0 deletions wemake_python_styleguide/options/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-

"""Constants with default values for configuration."""

MAX_RETURNS = 6

MAX_LOCAL_VARIABLES = 10

MAX_EXPRESSIONS = 10

MAX_ARGUMENTS = 5
4 changes: 4 additions & 0 deletions wemake_python_styleguide/visitors/base/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ def errors(self) -> List[BaseStyleViolation]:
def add_error(self, error: BaseStyleViolation) -> None:
"""Adds error to the visitor."""
self._errors.append(error)

def provide_options(self, options) -> None:
"""Provides options for checking."""
self.options = options
14 changes: 9 additions & 5 deletions wemake_python_styleguide/visitors/high_complexity.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ComplexityVisitor(BaseNodeVisitor):
"""This class checks for code with high complexity."""

def __init__(self) -> None:
"""Creates counters for tracked metrics."""
"""Creates config parser instance and counters for tracked metrics."""
super().__init__()

self.expressions: DefaultDict[str, int] = defaultdict(int)
Expand Down Expand Up @@ -48,6 +48,7 @@ def _is_method(self, function_type: Optional[str]) -> bool:
def _check_arguments_count(self, node: ast.FunctionDef):
counter = 0
has_extra_self_or_cls = 0
max_arguments_count = self.options.max_arguments
if self._is_method(getattr(node, 'function_type', None)):
has_extra_self_or_cls = 1

Expand All @@ -60,28 +61,31 @@ def _check_arguments_count(self, node: ast.FunctionDef):
if node.args.kwarg:
counter += 1

if counter > 5 + has_extra_self_or_cls: # TODO: config
if counter > max_arguments_count + has_extra_self_or_cls:
self.add_error(
TooManyArgumentsViolation(node, text=node.name),
)

def _update_variables(self, function: ast.FunctionDef):
max_local_variables_count = self.options.max_local_variables
self.variables[function.name] += 1
if self.variables[function.name] == 9 + 1: # TODO: config
if self.variables[function.name] == max_local_variables_count:
self.add_error(
TooManyLocalsViolation(function, text=function.name),
)

def _update_returns(self, function: ast.FunctionDef):
max_returns_count = self.options.max_returns
self.returns[function.name] += 1
if self.returns[function.name] == 5 + 1: # TODO: config
if self.returns[function.name] == max_returns_count:
self.add_error(
TooManyReturnsViolation(function, text=function.name),
)

def _update_expression(self, function: ast.FunctionDef):
max_expressions_count = self.options.max_expressions
self.expressions[function.name] += 1
if self.expressions[function.name] == 10: # TODO: config
if self.expressions[function.name] == max_expressions_count:
self.add_error(
TooManyExpressionsViolation(function, text=function.name),
)
Expand Down

0 comments on commit 2291cc1

Please sign in to comment.