Skip to content

Commit

Permalink
Restricts to use protected attributes, closes #272
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn committed Oct 27, 2018
1 parent 3da9bf1 commit 46a7fc8
Show file tree
Hide file tree
Showing 23 changed files with 488 additions and 172 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ to the project during `#hactoberfest`. List of awesome people:
- Forbids variable names with more than one consecutive underscore
- Restricts the maximum number of base classes aka mixins
- Forbids importing protected names
- Forbids using protected methods and attributes
- Forbids `yield` inside `__init__` method

### Bugfixes
Expand Down
5 changes: 4 additions & 1 deletion tests/fixtures/noqa.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from .version import get_version # noqa: Z300
import sys as sys # noqa: Z113
from some import _protected # noqa: Z440

full_name = u'Nikita Sobolev' # noqa: Z302
phone_number = 555_123_999 # noqa: Z303
Expand All @@ -29,9 +30,11 @@ def nested(): ... # noqa: Z430
value = 1 # noqa: Z110
x = 2 # noqa: Z111
__private = 3 # noqa: Z112
consecutive__underscores = 4 # noqa: Z116
__author__ = 'Nikita Sobolev' # noqa: Z410

nodes = [node for node in 'abc' if node != 'a' if node != 'b'] # noqa: Z307
some._execute() # noqa: Z441


class BadClass: # noqa: Z306
Expand Down Expand Up @@ -63,7 +66,7 @@ class Nested: # noqa: Z306,Z431
assert hex_number == hex_number # noqa: Z312

for symbol in 'abc': # noqa: Z436
break
...
else:
...

Expand Down
3 changes: 3 additions & 0 deletions tests/test_checker/test_noqa.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def test_noqa_fixture_disabled(absolute_path):
'Z111': 1,
'Z112': 1,
'Z113': 1,
'Z116': 1,

'Z220': 1,
'Z224': 1,
Expand Down Expand Up @@ -54,6 +55,8 @@ def test_noqa_fixture_disabled(absolute_path):
'Z435': 1,
'Z436': 1,
'Z437': 1,
'Z440': 1,
'Z441': 1,
}

process = subprocess.Popen(
Expand Down
2 changes: 1 addition & 1 deletion tests/test_violations/test_implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ def test_visitor_returns_location():

def test_checker_default_location():
"""Ensures that `BaseViolation` returns correct location."""
assert BaseViolation(None)._location() == (0, 0)
assert BaseViolation(None)._location() == (0, 0) # noqa: Z441
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-

import pytest

from wemake_python_styleguide.visitors.ast.attributes import (
ProtectedAttributeViolation,
WrongAttributeVisitor,
)

# Incorrect:

protected_attribute_assigned = 'some._protected = 1'
protected_attribute_accessed = 'print(some._protected)'
protected_method_called = 'some._method()'
protected_method_called_params = 'some._method(12, 33)'

protected_container_attribute = """
class Test(object):
def __init__(self):
self.container._print = 1
"""

protected_container_method = """
class Test(object):
def __init__(self):
self.container._print()
"""

# Correct:

protected_name_definition = '_protected = 1'
protected_name_attr_definition = '_protected.some = 1'

protected_self_attribute = """
class Test(object):
def __init__(self):
self._print = 1
"""

protected_self_method = """
class Test(object):
def __init__(self):
self._print()
"""

protected_cls_attribute = """
class Test(object):
@classmethod
def method(cls):
cls._print = 'some'
"""

protected_cls_method = """
class Test(object):
@classmethod
def method(cls):
cls._print()
"""

protected_attribute_definition = """
class Test(object):
_protected = 1
"""


@pytest.mark.parametrize('code', [
protected_attribute_assigned,
protected_attribute_accessed,
protected_method_called,
protected_method_called_params,
protected_container_attribute,
protected_container_method,
])
def test_protected_attribute_is_restricted(
assert_errors,
parse_ast_tree,
code,
default_options,
):
"""Ensures that it is impossible to use protected attributes."""
tree = parse_ast_tree(code)

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

assert_errors(visitor, [ProtectedAttributeViolation])


@pytest.mark.parametrize('code', [
protected_name_definition,
protected_name_attr_definition,
protected_self_attribute,
protected_self_method,
protected_cls_attribute,
protected_cls_method,
protected_attribute_definition,
])
def test_protected_attribute_is_allowed(
assert_errors,
parse_ast_tree,
code,
default_options,
):
"""Ensures that it is possible to use protected attributes."""
tree = parse_ast_tree(code)

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

assert_errors(visitor, [])
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
from wemake_python_styleguide.violations.consistency import (
FormattedStringViolation,
)
from wemake_python_styleguide.visitors.ast.strings import WrongStringVisitor
from wemake_python_styleguide.visitors.ast.builtins import WrongStringVisitor

regular_string = "'some value'"
binary_string = "b'binary'"
unicode_string = "u'unicode'"
string_variable = "some = '123'"
formated_string = "'x + y = {0}'.format(2)"
procent_format = "'x = %d' % 1"
key_formated_string = "'x + y = {res}'.format(res=2)"
variable_format = """
some = 'x = {0}'
Expand All @@ -22,8 +25,11 @@

@pytest.mark.parametrize('code', [
regular_string,
binary_string,
unicode_string,
string_variable,
formated_string,
procent_format,
key_formated_string,
variable_format,
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

import pytest

from wemake_python_styleguide.constants import MAGIC_NUMBERS_WHITELIST
from wemake_python_styleguide.violations.best_practices import (
MagicNumberViolation,
)
from wemake_python_styleguide.visitors.ast.numbers import (
MAGIC_NUMBERS_WHITELIST,
MagicNumberVisitor,
)
from wemake_python_styleguide.visitors.ast.builtins import MagicNumberVisitor

# Correct usages:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ def __init__(self, *args, **kwargs):
yield self
"""

regular_method_with_yield = """
class ModuleMembersVisitor(object):
def method(self, *args, **kwargs):
yield self
"""

iter_with_yield = """
class ModuleMembersVisitor(object):
def __iter__(self, *args, **kwargs):
yield self
"""

async_init_without_yield = """
class ModuleMembersVisitor(object):
async def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -54,6 +66,8 @@ def test_init_generator(
@pytest.mark.parametrize('code', [
init_without_yield,
async_init_without_yield,
regular_method_with_yield,
iter_with_yield,
])
def test_init_regular(
assert_errors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ def test_same_complexity(parse_ast_tree, default_options):
simple_visitor.run()
typed_visitor.run()

assert len(simple_visitor._lines) == 1
assert len(simple_visitor._lines[1]) == 3
assert len(simple_visitor._lines[1]) == len(typed_visitor._lines[1])
assert len(simple_visitor._lines) == 1 # noqa: Z441
assert len(simple_visitor._lines[1]) == 3 # noqa: Z441
assert len(typed_visitor._lines[1]) == 3 # noqa: Z441


@pytest.mark.parametrize('code, complexity', [
Expand All @@ -127,8 +127,8 @@ def test_exact_complexity(parse_ast_tree, default_options, code, complexity):
visitor = JonesComplexityVisitor(default_options, tree=tree)
visitor.run()

assert len(visitor._lines) == 1
assert len(visitor._lines[1]) == complexity
assert len(visitor._lines) == 1 # noqa: Z441
assert len(visitor._lines[1]) == complexity # noqa: Z441


@pytest.mark.parametrize('code, number_of_lines', [
Expand All @@ -147,4 +147,4 @@ def test_that_some_nodes_are_ignored(
visitor = JonesComplexityVisitor(default_options, tree=tree)
visitor.run()

assert len(visitor._lines) == number_of_lines
assert len(visitor._lines) == number_of_lines # noqa: Z441
22 changes: 19 additions & 3 deletions tests/test_visitors/test_ast/test_imports/test_protected_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import pytest

from wemake_python_styleguide.violations.naming import ProtectedModuleViolation
from wemake_python_styleguide.violations.best_practices import (
ProtectedModuleViolation,
)
from wemake_python_styleguide.visitors.ast.imports import WrongImportVisitor

import_public = 'import public'
Expand All @@ -19,11 +21,18 @@
import_from_public,
import_from_public_path,
])
def test_correct_import(assert_errors, parse_ast_tree, code, default_options):
def test_correct_import(
assert_errors,
parse_ast_tree,
code,
default_options,
):
"""Testing that correct imports are allowed."""
tree = parse_ast_tree(code)

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

assert_errors(visitor, [])


Expand All @@ -32,9 +41,16 @@ def test_correct_import(assert_errors, parse_ast_tree, code, default_options):
import_from_protected,
import_from_protected_path,
])
def test_incorrect_import(assert_errors, parse_ast_tree, code, default_options):
def test_incorrect_import(
assert_errors,
parse_ast_tree,
code,
default_options,
):
"""Testing that imports from protected modules are restricted."""
tree = parse_ast_tree(code)

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

assert_errors(visitor, [ProtectedModuleViolation])

0 comments on commit 46a7fc8

Please sign in to comment.