diff --git a/docs/CHANGES.rst b/docs/CHANGES.rst index e489829..7e48fff 100644 --- a/docs/CHANGES.rst +++ b/docs/CHANGES.rst @@ -25,6 +25,9 @@ Changes - Imports like `from _a import b` or `from a._b import x` are now forbidden. +- Bring test coverage to 100 %. + + 4.0b3 (2018-04-12) ------------------ diff --git a/setup.cfg b/setup.cfg index 2bd4c9b..409438b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -53,6 +53,8 @@ omit = [coverage:report] precision = 2 +show_missing = True +sort = Name [coverage:html] directory = _build/coverage diff --git a/src/RestrictedPython/transformer.py b/src/RestrictedPython/transformer.py index 6b882c7..b67238c 100644 --- a/src/RestrictedPython/transformer.py +++ b/src/RestrictedPython/transformer.py @@ -313,8 +313,11 @@ def gen_unpack_wrapper(self, node, target, ctx='store'): ctx = ast.Store() elif ctx == 'param': ctx = ast.Param() - else: - raise Exception('Unsupported context type.') + else: # pragma: no cover + # Only store and param are defined ctx. + raise NotImplementedError( + 'Unsupported context type: "{name}".'.format(name=type(ctx)), + ) # This node is used to catch the tuple in a tmp variable. tmp_target = ast.Name(tmp_name, ctx) @@ -380,8 +383,9 @@ def transform_slice(self, slice_): dims.elts.append(self.transform_slice(item)) return dims - else: - raise Exception("Unknown slice type: {0}".format(slice_)) + else: # pragma: no cover + # Index, Slice and ExtSlice are only defined Slice types. + raise NotImplementedError("Unknown slice type: {0}".format(slice_)) def check_name(self, node, name, allow_magic_methods=False): """Check names if they are allowed. @@ -897,8 +901,10 @@ def visit_Attribute(self, node): node.value = new_value return node - else: - return self.node_contents_visit(node) + else: # pragma: no cover + # Impossible Case only ctx Load, Store and Del are defined in ast. + raise NotImplementedError( + "Unknown ctx type: {0}".format(type(node.ctx))) # Subscripting @@ -942,8 +948,10 @@ def visit_Subscript(self, node): node.value = new_value return node - else: - return node + else: # pragma: no cover + # Impossible Case only ctx Load, Store and Del are defined in ast. + raise NotImplementedError( + "Unknown ctx type: {0}".format(type(node.ctx))) def visit_Index(self, node): """ @@ -1088,8 +1096,14 @@ def visit_AugAssign(self, node): copy_locations(new_node, node) return new_node - - return node + else: # pragma: no cover + # Impossible Case - Only Node Types: + # * Name + # * Attribute + # * Subscript + # defined, those are checked before. + raise NotImplementedError( + "Unknown target type: {0}".format(type(node.target))) def visit_Print(self, node): """Checks and mutates a print statement. @@ -1297,6 +1311,7 @@ def visit_Lambda(self, node): node = self.node_contents_visit(node) if IS_PY3: + # Implicit Tuple unpacking is not anymore avaliable in Python3 return node # Check for tuple parameters which need _getiter_ protection @@ -1397,7 +1412,7 @@ def visit_Module(self, node): # Inject the print collector after 'from __future__ import ....' position = 0 - for position, child in enumerate(node.body): + for position, child in enumerate(node.body): # pragma: no branch if not isinstance(child, ast.ImportFrom): break diff --git a/tests/test_Guards.py b/tests/test_Guards.py index 0155668..8f1014a 100644 --- a/tests/test_Guards.py +++ b/tests/test_Guards.py @@ -1,6 +1,7 @@ from RestrictedPython._compat import IS_PY2 from RestrictedPython.Guards import guarded_unpack_sequence from RestrictedPython.Guards import safe_builtins +from RestrictedPython.Guards import safer_getattr from tests import e_eval from tests import e_exec @@ -187,3 +188,29 @@ def test_Guards__safer_getattr__2(e_exec): assert 'Using format() on a unicode is not safe.' == str(err.value) else: assert 'Using format() on a str is not safe.' == str(err.value) + + +SAFER_GETATTR_ALLOWED = """\ +class A: + + def __init__(self, value): + self.value = value + +a = A(2) +result = getattr(a, 'value') +""" + + +@pytest.mark.parametrize(*e_exec) +def test_Guards__safer_getattr__3(e_exec): + """It allows to use `safer_getattr`.""" + restricted_globals = dict( + __builtins__=safe_builtins, + __name__=None, + __metaclass__=type, + _write_=lambda x: x, + getattr=safer_getattr, + result=None, + ) + e_exec(SAFER_GETATTR_ALLOWED, restricted_globals) + assert restricted_globals['result'] == 2 diff --git a/tests/transformer/test_lambda.py b/tests/transformer/test_lambda.py index c882ad4..4218226 100644 --- a/tests/transformer/test_lambda.py +++ b/tests/transformer/test_lambda.py @@ -1,5 +1,6 @@ from RestrictedPython._compat import IS_PY2 from RestrictedPython._compat import IS_PY3 +from RestrictedPython.Eval import default_guarded_getiter from RestrictedPython.Guards import guarded_unpack_sequence from tests import c_exec from tests import e_exec @@ -117,3 +118,58 @@ def test_RestrictingNodeTransformer__visit_Lambda__9( assert 2 == _getiter_.call_count _getiter_.assert_any_call((1, (2, 3))) _getiter_.assert_any_call((2, 3)) + + +LAMBDA_FUNC_1 = """ +g = lambda x: x ** 2 +""" + + +@pytest.mark.parametrize(*e_exec) +def test_RestrictingNodeTransformer__visit_Lambda__10(e_exec): + """Simple lambda functions are allowed.""" + restricted_globals = dict( + g=None, + ) + e_exec(LAMBDA_FUNC_1, restricted_globals) + assert 4 == restricted_globals['g'](2) + + +LAMBDA_FUNC_2 = """ +g = lambda (x, y) : (x ** 2, x + y) +""" + + +@pytest.mark.skipif( + IS_PY3, + reason="tuple parameter unpacking is gone in python 3") +@pytest.mark.parametrize(*e_exec) +def test_RestrictingNodeTransformer__visit_Lambda__11(e_exec): + """Lambda functions with tuple unpacking are allowed.""" + restricted_globals = dict( + g=None, + _unpack_sequence_=guarded_unpack_sequence, + _getiter_=default_guarded_getiter, + ) + e_exec(LAMBDA_FUNC_2, restricted_globals) + assert (9, 5) == restricted_globals['g']((3, 2)) + + +LAMBDA_FUNC_3 = """ +g = lambda (x, y), z : (x ** y, x + z) +""" + + +@pytest.mark.skipif( + IS_PY3, + reason="tuple parameter unpacking is gone in python 3") +@pytest.mark.parametrize(*e_exec) +def test_RestrictingNodeTransformer__visit_Lambda__12(e_exec): + """Lambda functions with tuple unpacking and simple params are allowed.""" + restricted_globals = dict( + g=None, + _unpack_sequence_=guarded_unpack_sequence, + _getiter_=default_guarded_getiter, + ) + e_exec(LAMBDA_FUNC_3, restricted_globals) + assert (64, 6) == restricted_globals['g']((4, 3), 2) diff --git a/tox.ini b/tox.ini index ab98662..8e6bfae 100644 --- a/tox.ini +++ b/tox.ini @@ -64,7 +64,7 @@ commands = coverage combine coverage html coverage xml - coverage report + coverage report --fail-under=100.0 [testenv:isort-apply] basepython = python2.7