diff --git a/.travis.yml b/.travis.yml index 3819d99..64dd9f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,13 +8,17 @@ python: - pypy-5.4 env: - ENVIRON=py + - ENVIRON=py27-rp3 - ENVIRON=isort,flake8,docs matrix: exclude: - env: ENVIRON=isort,flake8,docs + - env: ENVIRON=py27-rp3 include: - python: "3.6" env: ENVIRON=isort,flake8,docs + - python: "2.7" + env: ENVIRON=py27-rp3 install: - pip install tox coveralls coverage script: diff --git a/src/RestrictedPython/tests/security_in_syntax.py b/src/RestrictedPython/tests/security_in_syntax.py deleted file mode 100644 index 08bb41a..0000000 --- a/src/RestrictedPython/tests/security_in_syntax.py +++ /dev/null @@ -1,24 +0,0 @@ -# These are all supposed to raise a SyntaxError when using -# compile_restricted() but not when using compile(). -# Each function in this module is compiled using compile_restricted(). - - -def keyword_arg_with_bad_name(): - def f(okname=1, __badname=2): - pass - - -def no_augmeneted_assignment_to_sub(): - a[b] += c - - -def no_augmeneted_assignment_to_attr(): - a.b += c - - -def no_augmeneted_assignment_to_slice(): - a[x:y] += c - - -def no_augmeneted_assignment_to_slice2(): - a[x:y:z] += c diff --git a/src/RestrictedPython/tests/security_in_syntax26.py b/src/RestrictedPython/tests/security_in_syntax26.py deleted file mode 100644 index 8762617..0000000 --- a/src/RestrictedPython/tests/security_in_syntax26.py +++ /dev/null @@ -1,19 +0,0 @@ -# These are all supposed to raise a SyntaxError when using -# compile_restricted() but not when using compile(). -# Each function in this module is compiled using compile_restricted(). - - -def with_as_bad_name(): - with x as _leading_underscore: - pass - - -def relative_import_as_bad_name(): - from .x import y as _leading_underscore - - -def except_as_bad_name(): - try: - 1 / 0 - except Exception as _leading_underscore: - pass diff --git a/src/RestrictedPython/tests/testREADME.py b/src/RestrictedPython/tests/testREADME.py index fe2b5c5..6800c4d 100644 --- a/src/RestrictedPython/tests/testREADME.py +++ b/src/RestrictedPython/tests/testREADME.py @@ -23,4 +23,4 @@ def test_suite(): return unittest.TestSuite([ - DocFileSuite('README.txt', package='RestrictedPython'), ]) + DocFileSuite('README.rst', package='RestrictedPython'), ]) diff --git a/src/RestrictedPython/tests/testRestrictions.py b/src/RestrictedPython/tests/testRestrictions.py index f285a70..8daf82b 100644 --- a/src/RestrictedPython/tests/testRestrictions.py +++ b/src/RestrictedPython/tests/testRestrictions.py @@ -296,9 +296,6 @@ def test_Denied(self): self.fail('%s() did not trip security' % k) def test_SyntaxSecurity(self): - self._test_SyntaxSecurity('security_in_syntax.py') - if sys.version_info >= (2, 6): - self._test_SyntaxSecurity('security_in_syntax26.py') if sys.version_info >= (2, 7): self._test_SyntaxSecurity('security_in_syntax27.py') @@ -358,33 +355,6 @@ def test_StackSize(self): 'should have been at least %d, but was only %d' % (k, ss, rss)) - def test_BeforeAndAfter(self): - from RestrictedPython.RCompile import RModule - from RestrictedPython.tests import before_and_after - from compiler import parse - - defre = re.compile(r'def ([_A-Za-z0-9]+)_(after|before)\(') - - beforel = [name for name in before_and_after.__dict__ - if name.endswith("_before")] - - for name in beforel: - before = getattr(before_and_after, name) - before_src = get_source(before) - before_src = re.sub(defre, r'def \1(', before_src) - rm = RModule(before_src, '') - tree_before = rm._get_tree() - - after = getattr(before_and_after, name[:-6] + 'after') - after_src = get_source(after) - after_src = re.sub(defre, r'def \1(', after_src) - tree_after = parse(after_src) - - self.assertEqual(str(tree_before), str(tree_after)) - - rm.compile() - verify(rm.getCode()) - def _test_BeforeAndAfter(self, mod): from RestrictedPython.RCompile import RModule from compiler import parse @@ -411,26 +381,6 @@ def _test_BeforeAndAfter(self, mod): rm.compile() verify(rm.getCode()) - if sys.version_info[:2] >= (2, 4): - def test_BeforeAndAfter24(self): - from RestrictedPython.tests import before_and_after24 - self._test_BeforeAndAfter(before_and_after24) - - if sys.version_info[:2] >= (2, 5): - def test_BeforeAndAfter25(self): - from RestrictedPython.tests import before_and_after25 - self._test_BeforeAndAfter(before_and_after25) - - if sys.version_info[:2] >= (2, 6): - def test_BeforeAndAfter26(self): - from RestrictedPython.tests import before_and_after26 - self._test_BeforeAndAfter(before_and_after26) - - if sys.version_info[:2] >= (2, 7): - def test_BeforeAndAfter27(self): - from RestrictedPython.tests import before_and_after27 - self._test_BeforeAndAfter(before_and_after27) - def _compile_file(self, name): path = os.path.join(_HERE, name) f = open(path, "r") diff --git a/src/RestrictedPython/transformer.py b/src/RestrictedPython/transformer.py index 341de69..7884177 100644 --- a/src/RestrictedPython/transformer.py +++ b/src/RestrictedPython/transformer.py @@ -1259,8 +1259,7 @@ def visit_ExceptHandler(self, node): return node def visit_With(self, node): - """Protects tuple unpacking on with statements. """ - + """Protect tuple unpacking on with statements. """ node = self.node_contents_visit(node) if IS_PY2: diff --git a/tests/__init__.py b/tests/__init__.py index 51a994b..9e787b4 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,12 +5,13 @@ def _execute(compile_func): """Factory to create an execute function.""" - def _execute(source): - code, errors = compile_func(source)[:2] - assert errors == (), errors - assert code is not None - glb = {} - exec(code, glb) + def _execute(source, glb=None): + result = compile_func(source) + assert result.errors == (), result.errors + assert result.code is not None + if glb is None: + glb = {} + exec(result.code, glb) return glb return _execute diff --git a/tests/test_transformer.py b/tests/test_transformer.py index 334fa42..47d7147 100644 --- a/tests/test_transformer.py +++ b/tests/test_transformer.py @@ -70,7 +70,7 @@ def bad_name(): @pytest.mark.parametrize(*compile) def test_transformer__RestrictingNodeTransformer__visit_Name__1(compile): - """It is an error if a variable name starts with `_`.""" + """It is an error if a variable name starts with `__`.""" result = compile(BAD_NAME_STARTING_WITH_UNDERSCORE) assert result.errors == ( 'Line 2: "__" is an invalid variable name because it starts with "_"',) @@ -84,7 +84,7 @@ def overrideGuardWithName(): @pytest.mark.parametrize(*compile) def test_transformer__RestrictingNodeTransformer__visit_Name__2(compile): - """It is an error if a variable name ends with `__roles__`.""" + """It is an error if a variable name starts with `_`.""" result = compile(BAD_NAME_OVERRIDE_GUARD_WITH_NAME) assert result.errors == ( 'Line 2: "_getattr" is an invalid variable name because ' @@ -100,7 +100,7 @@ def _getattr(o): @pytest.mark.parametrize(*compile) def test_transformer__RestrictingNodeTransformer__visit_Name__3(compile): - """It is an error if a variable name ends with `__roles__`.""" + """It is an error if a function name starts with `_`.""" result = compile(BAD_NAME_OVERRIDE_OVERRIDE_GUARD_WITH_FUNCTION) assert result.errors == ( 'Line 2: "_getattr" is an invalid variable name because it ' @@ -116,13 +116,29 @@ class _getattr: @pytest.mark.parametrize(*compile) def test_transformer__RestrictingNodeTransformer__visit_Name__4(compile): - """It is an error if a variable name ends with `__roles__`.""" + """It is an error if a class name starts with `_`.""" result = compile(BAD_NAME_OVERRIDE_GUARD_WITH_CLASS) assert result.errors == ( 'Line 2: "_getattr" is an invalid variable name because it ' 'starts with "_"',) +BAD_NAME_IN_WITH = """\ +def with_as_bad_name(): + with x as _leading_underscore: + pass +""" + + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Name__4_5(compile): + """It is an error if a variable in with starts with `_`.""" + result = compile(BAD_NAME_IN_WITH) + assert result.errors == ( + 'Line 2: "_leading_underscore" is an invalid variable name because ' + 'it starts with "_"',) + + BAD_NAME_ENDING_WITH___ROLES__ = """\ def bad_name(): myvar__roles__ = 12 @@ -639,9 +655,10 @@ def test_transformer__RestrictingNodeTransformer__visit_Subscript_2( _write_.reset_mock() -@pytest.mark.parametrize(*compile) -def test_transformer__RestrictingNodeTransformer__visit_AugAssign( - compile, mocker): +@pytest.mark.parametrize(*execute) +def test_transformer__RestrictingNodeTransformer__visit_AugAssign__1( + execute, mocker): + """It allows augmented assign for variables.""" _inplacevar_ = mocker.stub() _inplacevar_.side_effect = lambda op, val, expr: val + expr @@ -652,24 +669,47 @@ def test_transformer__RestrictingNodeTransformer__visit_AugAssign( 'z': 0 } - result = compile("a += x + z") - assert result.errors == () - exec(result.code, glb) - + execute("a += x + z", glb) assert glb['a'] == 2 _inplacevar_.assert_called_once_with('+=', 1, 1) _inplacevar_.reset_mock() + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_AugAssign__2(compile): + """It forbids augmented assign of attributes.""" result = compile("a.a += 1") assert result.errors == ( 'Line 1: Augmented assignment of attributes is not allowed.',) + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_AugAssign__3(compile): + """It forbids augmented assign of subscripts.""" result = compile("a[a] += 1") assert result.errors == ( 'Line 1: Augmented assignment of object items and slices is not ' 'allowed.',) +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_AugAssign__4(compile): + """It forbids augmented assign of slices.""" + result = compile("a[x:y] += 1") + assert result.errors == ( + 'Line 1: Augmented assignment of object items and slices is not ' + 'allowed.',) + + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_AugAssign__5(compile): + """It forbids augmented assign of slices with steps.""" + result = compile("a[x:y:z] += 1") + assert result.errors == ( + 'Line 1: Augmented assignment of object items and slices is not ' + 'allowed.',) + + # def f(a, b, c): pass # f(*two_element_sequence, **dict_with_key_c) # @@ -1268,37 +1308,74 @@ def test_transformer__RestrictingNodeTransformer__visit_ExceptHandler__2( 'it starts with "_"',) -@pytest.mark.parametrize(*compile) -def test_transformer__RestrictingNodeTransformer__visit_Import(compile): - errmsg = 'Line 1: "%s" is an invalid variable name ' \ - 'because it starts with "_"' +import_errmsg = ( + 'Line 1: "%s" is an invalid variable name because it starts with "_"') + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import__1(compile): + """It allows importing a module.""" result = compile('import a') assert result.errors == () assert result.code is not None + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import__2(compile): + """It denies importing a module starting with `_`.""" result = compile('import _a') - assert result.errors == (errmsg % '_a',) + assert result.errors == (import_errmsg % '_a',) + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import__3(compile): + """It denies importing a module starting with `_` as something.""" result = compile('import _a as m') - assert result.errors == (errmsg % '_a',) + assert result.errors == (import_errmsg % '_a',) + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import__4(compile): + """It denies importing a module as something starting with `_`.""" result = compile('import a as _m') - assert result.errors == (errmsg % '_m',) + assert result.errors == (import_errmsg % '_m',) + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import__5(compile): + """It allows importing from a module.""" result = compile('from a import m') assert result.errors == () assert result.code is not None + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import_6(compile): + """It allows importing from a module starting with `_`.""" result = compile('from _a import m') assert result.errors == () assert result.code is not None + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import__7(compile): + """It denies importing from a module as something starting with `_`.""" result = compile('from a import m as _n') - assert result.errors == (errmsg % '_n',) + assert result.errors == (import_errmsg % '_n',) + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import__8(compile): + """It denies as-importing something starting with `_` from a module.""" result = compile('from a import _m as n') - assert result.errors == (errmsg % '_m',) + assert result.errors == (import_errmsg % '_m',) + + +@pytest.mark.parametrize(*compile) +def test_transformer__RestrictingNodeTransformer__visit_Import__9(compile): + """It denies relative from importing as something starting with `_`.""" + result = compile('from .x import y as _leading_underscore') + assert result.errors == (import_errmsg % '_leading_underscore',) @pytest.mark.parametrize(*compile) diff --git a/tox.ini b/tox.ini index 63ada03..fc58538 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,7 @@ envlist = flake8, coverage-clean, py27, + py27-rp3, py34, py35, py36, @@ -25,6 +26,15 @@ deps = pytest-remove-stale-bytecode pytest-mock +[testenv:py27-rp3] +commands = + zope-testrunner --path=src/RestrictedPython --all {posargs} +setenv = + COVERAGE_FILE=.coverage.{envname} +deps = + .[test] + zope.testrunner + [testenv:coverage-clean] deps = coverage skip_install = true