Skip to content

Commit 2291cc1

Browse files
mcprogersobolevn
authored andcommitted
Add configuration file parser (#29)
1 parent f743b96 commit 2291cc1

File tree

8 files changed

+166
-5
lines changed

8 files changed

+166
-5
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,29 @@ poetry run doc8 -q docs
3838
```
3939

4040
It's OK if some tests are skipped.
41+
42+
43+
## Configuration
44+
45+
You can adjust configuration via CLI option:
46+
47+
```sh
48+
flake8 --max-returns 7
49+
```
50+
51+
or configuration option in `tox.ini`/`setup.cfg`.
52+
53+
```ini
54+
max-returns = 7
55+
```
56+
57+
There are the following options:
58+
59+
- `max-returns` - maximum allowed number of `return` statements in one function. Default value is 6.
60+
61+
- `max-local-variables` - maximum allowed number of local variables in one function. Default is 10.
62+
63+
- `max-expressions` - maximum allowed number of expressions in one function. Default value is 10.
64+
65+
- `max-arguments` - maximum allowed number of arguments in one function. Default value is 5.
66+

tests/test_options/test_config.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import subprocess
4+
5+
6+
def test_max_variables_cli_option(absolute_path):
7+
"""Test to check max-local-variables cli option."""
8+
filename = absolute_path('fixtures', 'complexity', 'wrong_variables.py')
9+
option_flag = '--max-local-variables'
10+
option_value = '100'
11+
process = subprocess.Popen(
12+
['flake8', filename, option_flag, option_value],
13+
stdout=subprocess.PIPE,
14+
stderr=subprocess.PIPE,
15+
)
16+
stdout, _ = process.communicate()
17+
assert stdout.count(b'WPS150') == 0
18+
19+
20+
def test_max_arguments_cli_option(absolute_path):
21+
"""Test to check max-arguments cli option."""
22+
filename = absolute_path('fixtures', 'complexity', 'wrong_arguments.py')
23+
option_flag = '--max-arguments'
24+
option_value = '100'
25+
process = subprocess.Popen(
26+
['flake8', filename, option_flag, option_value],
27+
stdout=subprocess.PIPE,
28+
stderr=subprocess.PIPE,
29+
)
30+
stdout, _ = process.communicate()
31+
assert stdout.count(b'WPS151') == 0
32+
33+
34+
def test_max_returns_cli_option(absolute_path):
35+
"""Test to check max-returns cli option."""
36+
filename = absolute_path('fixtures', 'complexity', 'wrong_returns.py')
37+
option_flag = '--max-returns'
38+
option_value = '100'
39+
process = subprocess.Popen(
40+
['flake8', filename, option_flag, option_value],
41+
stdout=subprocess.PIPE,
42+
stderr=subprocess.PIPE,
43+
)
44+
stdout, _ = process.communicate()
45+
assert stdout.count(b'WPS153') == 0
46+
47+
48+
def test_max_expressions_cli_options(absolute_path):
49+
"""Test to check max-expressions cli option."""
50+
filename = absolute_path('fixtures', 'complexity', 'wrong_expressions.py')
51+
option_flag = '--max-expressions'
52+
option_value = '100'
53+
process = subprocess.Popen(
54+
['flake8', filename, option_flag, option_value],
55+
stdout=subprocess.PIPE,
56+
stderr=subprocess.PIPE,
57+
)
58+
stdout, _ = process.communicate()
59+
assert stdout.count(b'WPS154') == 0

wemake_python_styleguide/checker.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from ast import Module
44
from typing import Generator, Tuple
55

6+
from wemake_python_styleguide.options.config import Configuration
67
from wemake_python_styleguide.version import version
78
from wemake_python_styleguide.visitors.high_complexity import ComplexityVisitor
89
from wemake_python_styleguide.visitors.wrong_function_call import (
@@ -32,6 +33,9 @@ class Checker(object):
3233
name = 'wemake-python-styleguide'
3334
version = version
3435

36+
config = Configuration()
37+
options = None # So that mypy could detect the attribute
38+
3539
def __init__(self, tree: Module, filename: str = '-') -> None:
3640
"""Creates new checker instance."""
3741
self.tree = tree
@@ -48,6 +52,16 @@ def __init__(self, tree: Module, filename: str = '-') -> None:
4852
WrongModuleMetadataVisitor,
4953
)
5054

55+
@classmethod
56+
def add_options(cls, parser):
57+
"""Calls Configuration instance method for registering options."""
58+
cls.config.register_options(parser)
59+
60+
@classmethod
61+
def parse_options(cls, options):
62+
"""Parses registered options for providing to the visiter."""
63+
cls.options = options
64+
5165
def run(self) -> Generator[CheckResult, None, None]:
5266
"""
5367
Runs the checker.
@@ -56,6 +70,7 @@ def run(self) -> Generator[CheckResult, None, None]:
5670
"""
5771
for visitor_class in self._visitors:
5872
visiter = visitor_class()
73+
visiter.provide_options(self.options)
5974
visiter.visit(self.tree)
6075

6176
for error in visiter.errors:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# -*- coding: utf-8 -*-
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from wemake_python_styleguide.options import defaults
4+
5+
6+
class Configuration(object):
7+
"""Provides method for registering options for WPS flake8 plugin."""
8+
9+
def register_options(self, parser):
10+
"""Registers options for WPS plugin."""
11+
parser.add_option(
12+
'--max-returns',
13+
parse_from_config=True,
14+
type='int',
15+
default=defaults.MAX_RETURNS,
16+
help='Maximum allowed number of return statements in one function.',
17+
)
18+
19+
parser.add_option(
20+
'--max-local-variables',
21+
parse_from_config=True,
22+
type='int',
23+
default=defaults.MAX_LOCAL_VARIABLES,
24+
help='Maximum allowed number of local variables in one function.',
25+
)
26+
27+
parser.add_option(
28+
'--max-expressions',
29+
parse_from_config=True,
30+
type='int',
31+
default=defaults.MAX_EXPRESSIONS,
32+
help='Maximum allowed number of expressions in one function.',
33+
)
34+
35+
parser.add_option(
36+
'--max-arguments',
37+
parse_from_config=True,
38+
type='int',
39+
default=defaults.MAX_ARGUMENTS,
40+
help='Maximum allowed number of arguments in one function.',
41+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""Constants with default values for configuration."""
4+
5+
MAX_RETURNS = 6
6+
7+
MAX_LOCAL_VARIABLES = 10
8+
9+
MAX_EXPRESSIONS = 10
10+
11+
MAX_ARGUMENTS = 5

wemake_python_styleguide/visitors/base/visitor.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ def errors(self) -> List[BaseStyleViolation]:
2222
def add_error(self, error: BaseStyleViolation) -> None:
2323
"""Adds error to the visitor."""
2424
self._errors.append(error)
25+
26+
def provide_options(self, options) -> None:
27+
"""Provides options for checking."""
28+
self.options = options

wemake_python_styleguide/visitors/high_complexity.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ComplexityVisitor(BaseNodeVisitor):
1919
"""This class checks for code with high complexity."""
2020

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

2525
self.expressions: DefaultDict[str, int] = defaultdict(int)
@@ -48,6 +48,7 @@ def _is_method(self, function_type: Optional[str]) -> bool:
4848
def _check_arguments_count(self, node: ast.FunctionDef):
4949
counter = 0
5050
has_extra_self_or_cls = 0
51+
max_arguments_count = self.options.max_arguments
5152
if self._is_method(getattr(node, 'function_type', None)):
5253
has_extra_self_or_cls = 1
5354

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

63-
if counter > 5 + has_extra_self_or_cls: # TODO: config
64+
if counter > max_arguments_count + has_extra_self_or_cls:
6465
self.add_error(
6566
TooManyArgumentsViolation(node, text=node.name),
6667
)
6768

6869
def _update_variables(self, function: ast.FunctionDef):
70+
max_local_variables_count = self.options.max_local_variables
6971
self.variables[function.name] += 1
70-
if self.variables[function.name] == 9 + 1: # TODO: config
72+
if self.variables[function.name] == max_local_variables_count:
7173
self.add_error(
7274
TooManyLocalsViolation(function, text=function.name),
7375
)
7476

7577
def _update_returns(self, function: ast.FunctionDef):
78+
max_returns_count = self.options.max_returns
7679
self.returns[function.name] += 1
77-
if self.returns[function.name] == 5 + 1: # TODO: config
80+
if self.returns[function.name] == max_returns_count:
7881
self.add_error(
7982
TooManyReturnsViolation(function, text=function.name),
8083
)
8184

8285
def _update_expression(self, function: ast.FunctionDef):
86+
max_expressions_count = self.options.max_expressions
8387
self.expressions[function.name] += 1
84-
if self.expressions[function.name] == 10: # TODO: config
88+
if self.expressions[function.name] == max_expressions_count:
8589
self.add_error(
8690
TooManyExpressionsViolation(function, text=function.name),
8791
)

0 commit comments

Comments
 (0)