Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added TooLongVariableNameViolation #291

Merged
merged 10 commits into from Nov 10, 2018
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@ We used to have incremental versioning before `0.1.0`.

### Features

- Adds `TooLongNameViolation`
- **Breaking**: removes `--max-conditions` and `--max-elifs` options
- **Breaking**: removes `--max-offset-blocks`
- **Breaking**: changes default `TooManyConditionsViolation` threshold from `3` to `4`
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/noqa.py
Expand Up @@ -36,6 +36,7 @@ def nested(): ... # noqa: Z430
consecutive__underscores = 4 # noqa: Z116
cls = 5 # noqa: Z117
__author__ = 'Nikita Sobolev' # noqa: Z410
extremely_long_name_that_needs_to_be_shortened_to_work_fine = 2 # noqa: Z118

some._execute() # noqa: Z441

Expand Down
2 changes: 2 additions & 0 deletions tests/test_checker/test_module_names.py
Expand Up @@ -16,6 +16,8 @@
('123py.py', naming.WrongModuleNamePatternViolation),
('version_1.py', naming.UnderscoredNumberNameViolation),
('__private.py', naming.PrivateNameViolation),
('oh_no_not_an_extremely_super_duper_unreasonably_long_name.py',
naming.TooLongNameViolation),
])
def test_module_names(filename, error, default_options):
"""Ensures that checker works with module names."""
Expand Down
1 change: 1 addition & 0 deletions tests/test_checker/test_noqa.py
Expand Up @@ -41,6 +41,7 @@ def test_noqa_fixture_disabled(absolute_path, all_violations):
'Z115': 1,
'Z116': 1,
'Z117': 1,
'Z118': 1,

'Z200': 0,
'Z201': 0,
Expand Down
162 changes: 162 additions & 0 deletions tests/test_visitors/test_ast/test_naming/test_naming.py
@@ -0,0 +1,162 @@
# -*- coding: utf-8 -*-

import pytest

from wemake_python_styleguide.constants import VARIABLE_NAMES_BLACKLIST
from wemake_python_styleguide.violations.naming import (
ConsecutiveUnderscoresInNameViolation,
PrivateNameViolation,
TooLongNameViolation,
TooShortNameViolation,
UnderscoredNumberNameViolation,
WrongVariableNameViolation,
)
from wemake_python_styleguide.visitors.ast.naming import WrongNameVisitor


@pytest.mark.parametrize('wrong_name', VARIABLE_NAMES_BLACKLIST)
def test_wrong_variable_name(
assert_errors,
assert_error_text,
parse_ast_tree,
naming_template,
default_options,
mode,
wrong_name,
):
"""Ensures that wrong names are not allowed."""
tree = parse_ast_tree(mode(naming_template.format(wrong_name)))

visitor = WrongNameVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [WrongVariableNameViolation])
assert_error_text(visitor, wrong_name)


def test_short_variable_name(
assert_errors,
assert_error_text,
parse_ast_tree,
naming_template,
default_options,
mode,
):
"""Ensures that short names are not allowed."""
short_name = 'y'
tree = parse_ast_tree(mode(naming_template.format(short_name)))

visitor = WrongNameVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [TooShortNameViolation])
assert_error_text(visitor, short_name)


def test_private_variable_name(
assert_errors,
assert_error_text,
parse_ast_tree,
naming_template,
default_options,
mode,
):
"""Ensures that private names are not allowed."""
private_name = '__private'
tree = parse_ast_tree(mode(naming_template.format(private_name)))

visitor = WrongNameVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [PrivateNameViolation])
assert_error_text(visitor, private_name)


@pytest.mark.parametrize('underscored_name', [
'with__underscore',
'mutliple__under__score',
'triple___underscore',
])
def test_underscored_variable_name(
assert_errors,
assert_error_text,
parse_ast_tree,
naming_template,
default_options,
mode,
underscored_name,
):
"""Ensures that underscored names are not allowed."""
tree = parse_ast_tree(mode(naming_template.format(underscored_name)))

visitor = WrongNameVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [ConsecutiveUnderscoresInNameViolation])
assert_error_text(visitor, underscored_name)


@pytest.mark.parametrize('number_suffix', [
'number_5',
'between_45_letters',
'with_multiple_groups_4_5',
])
def test_number_prefix_variable_name(
assert_errors,
assert_error_text,
parse_ast_tree,
naming_template,
default_options,
mode,
number_suffix,
):
"""Ensures that number suffix names are not allowed."""
tree = parse_ast_tree(mode(naming_template.format(number_suffix)))

visitor = WrongNameVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [UnderscoredNumberNameViolation])
assert_error_text(visitor, number_suffix)


@pytest.mark.parametrize('correct_name', [
'snake_case',
'_protected_or_unused',
'with_number5',
'xy',
])
def test_naming_correct(
assert_errors,
parse_ast_tree,
naming_template,
default_options,
mode,
correct_name,
):
"""Ensures that correct names are allowed."""
tree = parse_ast_tree(mode(naming_template.format(correct_name)))

visitor = WrongNameVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [])


def test_long_variable_name(
assert_errors,
assert_error_text,
parse_ast_tree,
naming_template,
default_options,
mode,
):
"""Ensures that short names are not allowed."""
long_name = 'incredibly_long_name_that_should_not_pass_the_long_name_test'
tree = parse_ast_tree(mode(naming_template.format(long_name)))

visitor = WrongNameVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [TooLongNameViolation])
assert_error_text(visitor, long_name)
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-

from wemake_python_styleguide.violations.naming import TooLongNameViolation
from wemake_python_styleguide.visitors.ast.naming import WrongNameVisitor


def test_long_variable_name(
assert_errors,
assert_error_text,
parse_ast_tree,
naming_template,
default_options,
mode,
):
"""Ensures that short names are not allowed."""
long_name = 'incredibly_long_name_that_should_not_pass_the_long_name_test'
tree = parse_ast_tree(mode(naming_template.format(long_name)))

visitor = WrongNameVisitor(default_options, tree=tree)
visitor.run()

assert_errors(visitor, [TooLongNameViolation])
assert_error_text(visitor, long_name)
Expand Up @@ -3,6 +3,7 @@
import pytest

from wemake_python_styleguide.violations.naming import (
TooLongNameViolation,
TooShortNameViolation,
WrongModuleNamePatternViolation,
)
Expand Down Expand Up @@ -37,3 +38,31 @@ def test_length_option(assert_errors, assert_error_text, options):

assert_errors(visitor, [TooShortNameViolation])
assert_error_text(visitor, filename.replace('.py', ''))


@pytest.mark.parametrize('filename', [
'super_long_name_that_needs_to_be_much_shorter_to_fit_the_rule.py',
'package/another_ridiculously_lengthly_name_that_defies_this_rule.py',
'/root/please_do_not_ever_make_names_long_and_confusing_like_this.py',
'C:/hello_there_this_is_another_very_long_name_that_will_not_work.py',
])
def test_too_long_filename(assert_errors, filename, default_options):
"""Testing that long file names are restricted."""
visitor = WrongModuleNameVisitor(default_options, filename=filename)
visitor.run()

assert_errors(visitor, [
TooLongNameViolation,
])


def test_max_length_option(assert_errors, assert_error_text, options):
"""Ensures that option `--max-name-length` works."""
max_length = 55
filename = 'very_long_name_that_should_not_pass_unless_changed_shorter.py'
option_values = options(max_name_length=max_length)
visitor = WrongModuleNameVisitor(option_values, filename=filename)
visitor.run()

assert_errors(visitor, [TooLongNameViolation])
assert_error_text(visitor, filename.replace('.py', ''))
26 changes: 26 additions & 0 deletions wemake_python_styleguide/logics/naming/logical.py
Expand Up @@ -160,3 +160,29 @@ def does_contain_consecutive_underscores(name: str) -> bool:
return True

return False


def is_too_long_name(
name: str,
max_length: int = defaults.MAX_NAME_LENGTH,
) -> bool:
"""
Checks for too long variable names.

>>> is_too_long_name('test')
False

>>> is_too_long_name('this_is_twentynine_characters')
False

>>> is_too_long_name('this_should_fail', max_length=10)
True

>>> is_too_long_name('this_is_thirty_characters_long', max_length=30)
False

>>> is_too_long_name('this_is_thirty_characters_long', max_length=29)
True

"""
return len(name) > max_length
9 changes: 9 additions & 0 deletions wemake_python_styleguide/options/config.py
Expand Up @@ -43,6 +43,9 @@ class Configuration(object):
- ``min-name-length`` - minimum number of chars to define a valid
variable and module name, defaults to
:str:`wemake_python_styleguide.options.defaults.MIN_NAME_LENGTH`
- ``max-name-length`` - maximum number of chars to define a valid
variable and module name, defaults to
:str:`wemake_python_styleguide.options.defaults.MAX_NAME_LENGTH`
- ``i-control-code`` - whether you control ones who use your code,
more rules are enforced when you do control it, defaults to
:str:`wemake_python_styleguide.options.defaults.I_CONTROL_CODE`
Expand Down Expand Up @@ -178,6 +181,12 @@ class Configuration(object):
'Minimum required length of variable and module names.',
),

_Option(
'--max-name-length',
defaults.MAX_NAME_LENGTH,
'Maximum possible length of the variable and module names.',
),

_Option(
'--i-control-code',
defaults.I_CONTROL_CODE,
Expand Down
3 changes: 3 additions & 0 deletions wemake_python_styleguide/options/defaults.py
Expand Up @@ -21,6 +21,9 @@
#: Minimum variable's name length.
MIN_NAME_LENGTH: Final = 2

#: Maximum variable and module name length:
MAX_NAME_LENGTH: Final = 55

#: Whether you control ones who use your code.
I_CONTROL_CODE: Final = True

Expand Down
1 change: 1 addition & 0 deletions wemake_python_styleguide/types.py
Expand Up @@ -54,6 +54,7 @@ class or structure.
# General:
min_name_length: int
i_control_code: bool
max_name_length: int

# Complexity:
max_arguments: int
Expand Down
44 changes: 43 additions & 1 deletion wemake_python_styleguide/violations/naming.py
Expand Up @@ -121,7 +121,7 @@
UpperCaseAttributeViolation
ConsecutiveUnderscoresInNameViolation
ReservedArgumentNameViolation

TooLongNameViolation

Module names
------------
Expand All @@ -141,6 +141,7 @@
.. autoclass:: UpperCaseAttributeViolation
.. autoclass:: ConsecutiveUnderscoresInNameViolation
.. autoclass:: ReservedArgumentNameViolation
.. autoclass:: TooLongNameViolation

"""

Expand Down Expand Up @@ -515,3 +516,44 @@ def __init__(self):

error_template = 'Found name reserved for first argument: {0}'
code = 117


@final
class TooLongNameViolation(MaybeASTViolation):
"""
Forbids to have long short variable or module names.

Reasoning:
Too long names are unreadable.
It is better to use shorter alternative.
Long names also indicate that this variable is too complex,
Casva marked this conversation as resolved.
Show resolved Hide resolved
maybe it may require some documentation.

Solution:
Think of another name. Give more context to it.

This rule checks: modules, variables, attributes,
functions, methods, and classes.

Example::

# Correct:
total_price = 25
average_age = 45

# Wrong:
final_price_after_fifteen_percent_sales_tax_and_gratuity = 30
total_age_of_all_participants_in_the_survey_divided_by_twelve = 2

Configuration:
This rule is configurable with ``--max-name-length``.
Default:
:str:`wemake_python_styleguide.options.defaults.MAX_NAME_LENGTH`

.. versionadded:: 0.1.0
.. versionchanged:: 0.4.0

"""

error_template = 'Found too long name: {0}'
code = 118