Skip to content

Commit

Permalink
Merge pull request #34 from jamescooke/context-managers
Browse files Browse the repository at this point in the history
Handle context managers
  • Loading branch information
jamescooke committed Jul 11, 2018
2 parents 6c0ce31 + ee0663b commit 47294ab
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 32 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Unreleased_
See also `latest documentation
<https://flake8-aaa.readthedocs.io/en/latest/>`_.

Changed
-------

* Improved loading of Act blocks so that they can be found within context
managers.

0.3.0_ - 2018/06/28
-------------------

Expand Down
34 changes: 25 additions & 9 deletions flake8_aaa/act_block.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .exceptions import NotActionBlock
import ast

from .helpers import node_is_pytest_raises, node_is_result_assignment
from .types import ActBlockType

Expand All @@ -19,25 +20,40 @@ def __init__(self, node, block_type):
self.node = node
self.block_type = block_type

@classmethod
def build_body(cls, body):
"""
Args:
body (list (ast.node)): List of nodes from a block.
Returns:
list (ActBlock)
"""
act_blocks = []
for child_node in body:
act_blocks += ActBlock.build(child_node)
return act_blocks

@classmethod
def build(cls, node):
"""
Args:
node (ast.node): A node, decorated with ``ASTTokens``.
Returns:
ActBlock
Raises:
NotActionBlock: When ``node`` does not look like an Act block.
list(ActBlock)
"""
if node_is_result_assignment(node):
return cls(node, ActBlockType.result_assignment)
return [cls(node, ActBlockType.result_assignment)]
elif node_is_pytest_raises(node):
return cls(node, ActBlockType.pytest_raises)
return [cls(node, ActBlockType.pytest_raises)]

# Check if line marked with '# act'
if node.first_token.line.strip().endswith('# act'):
return cls(node, ActBlockType.marked_act)
return [cls(node, ActBlockType.marked_act)]

# Recurse if it's a context manager
if isinstance(node, ast.With):
return cls.build_body(node.body)

raise NotActionBlock()
return []
7 changes: 0 additions & 7 deletions flake8_aaa/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@ class Flake8AAAException(Exception):
pass


class NotActionBlock(Flake8AAAException):
"""
Used when parsing if lines of a function should be considered Action
blocks.
"""


class ValidationError(Flake8AAAException):
"""
Attributes:
Expand Down
9 changes: 2 additions & 7 deletions flake8_aaa/function.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .act_block import ActBlock
from .arrange_block import ArrangeBlock
from .assert_block import AssertBlock
from .exceptions import NotActionBlock, ValidationError
from .exceptions import ValidationError
from .helpers import function_is_noop
from .types import ActBlockType

Expand Down Expand Up @@ -50,12 +50,7 @@ def load_act_block(self):
Raises:
ValidationError
"""
act_blocks = []
for child_node in self.node.body:
try:
act_blocks.append(ActBlock.build(child_node))
except NotActionBlock:
continue
act_blocks = ActBlock.build_body(self.node.body)

if len(act_blocks) < 1:
raise ValidationError(self.node.lineno, self.node.col_offset, 'AAA01 no Act block found in test')
Expand Down
37 changes: 28 additions & 9 deletions tests/act_block/test_build.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pytest

from flake8_aaa.act_block import ActBlock
from flake8_aaa.exceptions import NotActionBlock
from flake8_aaa.types import ActBlockType

# TODO act blocks need testing with 'result =' indented
Expand All @@ -20,9 +19,11 @@ def test_not_actions(first_node_with_tokens):
def test_raises_block(first_node_with_tokens):
result = ActBlock.build(first_node_with_tokens.body[0])

assert isinstance(result, ActBlock)
assert result.node == first_node_with_tokens.body[0]
assert result.block_type == ActBlockType.pytest_raises
assert isinstance(result, list)
assert len(result) == 1
assert isinstance(result[0], ActBlock)
assert result[0].node == first_node_with_tokens.body[0]
assert result[0].block_type == ActBlockType.pytest_raises


@pytest.mark.parametrize(
Expand All @@ -35,9 +36,26 @@ def test_raises_block(first_node_with_tokens):
def test(expected_type, first_node_with_tokens):
result = ActBlock.build(first_node_with_tokens)

assert isinstance(result, ActBlock)
assert result.node == first_node_with_tokens
assert result.block_type == expected_type
assert isinstance(result, list)
assert len(result) == 1
assert isinstance(result[0], ActBlock)
assert result[0].node == first_node_with_tokens
assert result[0].block_type == expected_type


@pytest.mark.parametrize(
'code_str', [
"with mock.patch('stats.deletion_manager.deleted'):\n result = existing_user.delete()",
]
)
def test_nested(first_node_with_tokens):
result = ActBlock.build(first_node_with_tokens)

assert isinstance(result, list)
assert len(result) == 1
assert isinstance(result[0], ActBlock)
assert result[0].block_type == ActBlockType.result_assignment
assert result[0].node == first_node_with_tokens.body[0]


@pytest.mark.parametrize(
Expand All @@ -51,5 +69,6 @@ def test(expected_type, first_node_with_tokens):
]
)
def test_not_actions(first_node_with_tokens):
with pytest.raises(NotActionBlock):
ActBlock.build(first_node_with_tokens)
result = ActBlock.build(first_node_with_tokens)

assert result == []
74 changes: 74 additions & 0 deletions tests/function/test_load_act_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,69 @@ def test_raises_in_assert(function):
assert result.node.first_token.line == ' result = existing_user.delete()\n'


@pytest.mark.parametrize(
'code_str',
[
'''
def test(existing_user):
with mock.patch('stats.deletion_manager.deleted'):
result = existing_user.delete()
assert result is True
assert result.retrieved is False
'''
],
ids=['act in context manager'],
)
def test_in_cm(function):
result = function.load_act_block()

assert isinstance(result, ActBlock)
assert result.block_type == ActBlockType.result_assignment
assert result.node.first_token.line == ' result = existing_user.delete()\n'


@pytest.mark.parametrize(
'code_str',
[
'''
def test_no_recreate(existing_user):
with mock.patch('stats.creation_manager.created'):
with pytest.raises(ValidationError):
existing_user.create()
'''
],
ids=['pytest raises in context manager'],
)
def test_raises_in_cm(function):
result = function.load_act_block()

assert isinstance(result, ActBlock)
assert result.block_type == ActBlockType.pytest_raises
assert result.node.first_token.line == ' with pytest.raises(ValidationError):\n'


@pytest.mark.parametrize(
'code_str',
[
'''
def test_creation(stub_user):
with mock.patch('stats.creation_manager.created'):
stub_user.create() # act
assert stub_user.exists()
'''
],
ids=['marked act block in context manager'],
)
def test_marked_in_cm(function):
result = function.load_act_block()

assert isinstance(result, ActBlock)
assert result.block_type == ActBlockType.marked_act
assert result.node.first_token.line == ' stub_user.create() # act\n'


# --- FAILURES ---


Expand Down Expand Up @@ -86,6 +149,17 @@ def test():
eggs = 1 # act
assert chickens + eggs == 2
''',
'''
def test_read(self):
with open('data') as data_file:
result = data_file.read()
assert result == ''
result = data_file.read()
assert result == ''
''',
]
)
Expand Down

0 comments on commit 47294ab

Please sign in to comment.