Skip to content

Commit

Permalink
Merge pull request #15 from zopefoundation/central-version-checks
Browse files Browse the repository at this point in the history
Centralize the Python version checks
  • Loading branch information
Michael Howitz committed Jan 31, 2017
2 parents cf1485b + 5addea8 commit 7dc6e5f
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 62 deletions.
23 changes: 3 additions & 20 deletions src/RestrictedPython/Guards.py
Expand Up @@ -15,7 +15,7 @@
# AccessControl.ZopeGuards contains a large set of wrappers for builtins.
# DocumentTemplate.DT_UTil contains a few.

import sys
from ._compat import IS_PY2


try:
Expand Down Expand Up @@ -104,8 +104,7 @@
'ZeroDivisionError',
]

version = sys.version_info
if version >= (2, 7) and version < (2, 8):
if IS_PY2:
_safe_names.extend([
'basestring',
'cmp',
Expand All @@ -118,22 +117,6 @@
'StandardError',
])

if version >= (3, 0):
_safe_names.extend([

])
_safe_exceptions.extend([

])

if version >= (3, 5):
_safe_names.extend([

])
_safe_exceptions.extend([

])


for name in _safe_names:
safe_builtins[name] = getattr(builtins, name)
Expand Down Expand Up @@ -246,7 +229,7 @@ def __init__(self, ob):
def _full_write_guard():
# Nested scope abuse!
# safetype and Wrapper variables are used by guard()
safetype = {dict: True, list: True}.has_key if version < (3, 0) else {dict: True, list: True}.keys
safetype = {dict: True, list: True}.has_key if IS_PY2 else {dict: True, list: True}.keys
Wrapper = _write_wrapper()

def guard(ob):
Expand Down
8 changes: 8 additions & 0 deletions src/RestrictedPython/_compat.py
@@ -0,0 +1,8 @@
import sys


_version = sys.version_info
IS_PY2 = _version.major == 2
IS_PY3 = _version.major == 3
IS_PY34_OR_GREATER = _version.major == 3 and _version.minor >= 4
IS_PY35_OR_GREATER = _version.major == 3 and _version.minor >= 5
42 changes: 20 additions & 22 deletions src/RestrictedPython/transformer.py
Expand Up @@ -22,9 +22,13 @@
# http://docs.plone.org/develop/styleguide/python.html


from ._compat import IS_PY2
from ._compat import IS_PY3
from ._compat import IS_PY34_OR_GREATER
from ._compat import IS_PY35_OR_GREATER

import ast
import contextlib
import sys


# if any of the ast Classes should not be whitelisted, please comment them out
Expand Down Expand Up @@ -144,9 +148,7 @@
ast.FloorDiv: '//='
}


version = sys.version_info
if version >= (2, 7) and version < (2, 8):
if IS_PY2:
AST_WHITELIST.extend([
ast.Print,
ast.Raise,
Expand All @@ -155,7 +157,7 @@
ast.ExceptHandler,
])

if version >= (3, 4):
if IS_PY3:
AST_WHITELIST.extend([
ast.Bytes,
ast.Starred,
Expand All @@ -166,7 +168,7 @@
ast.withitem
])

if version >= (3, 5):
if IS_PY35_OR_GREATER:
IOPERATOR_TO_STR[ast.MatMult] = '@='

AST_WHITELIST.extend([
Expand All @@ -178,10 +180,6 @@
#ast.AsyncWith, # No Async Elements should be supported
])

if version >= (3, 6):
AST_WHITELIST.extend([
])


# When new ast nodes are generated they have no 'lineno' and 'col_offset'.
# This function copies these two fields from the incoming node
Expand Down Expand Up @@ -282,7 +280,7 @@ def guard_iter(self, node):
return node

def is_starred(self, ob):
if version.major == 3:
if IS_PY3:
return isinstance(ob, ast.Starred)
else:
return False
Expand Down Expand Up @@ -409,7 +407,7 @@ def gen_unpack_wrapper(self, node, target, ctx='store'):
try_body = [ast.Assign(targets=[target], value=converter)]
finalbody = [self.gen_del_stmt(tmp_name)]

if version.major == 2:
if IS_PY2:
cleanup = ast.TryFinally(body=try_body, finalbody=finalbody)
else:
cleanup = ast.Try(
Expand All @@ -431,7 +429,7 @@ def gen_unpack_wrapper(self, node, target, ctx='store'):
return (tmp_target, cleanup)

def gen_none_node(self):
if version >= (3, 4):
if IS_PY34_OR_GREATER:
return ast.NameConstant(value=None)
else:
return ast.Name(id='None', ctx=ast.Load())
Expand Down Expand Up @@ -517,7 +515,7 @@ def check_function_argument_names(self, node):
# which is gone in python3.
# See https://www.python.org/dev/peps/pep-3113/

if version.major == 2:
if IS_PY2:
# Needed to handle nested 'tuple parameter unpacking'.
# For example 'def foo((a, b, (c, (d, e)))): pass'
to_check = list(node.args.args)
Expand Down Expand Up @@ -956,17 +954,17 @@ def visit_Call(self, node):
# '*args' can be detected by 'ast.Starred' nodes.
# '**kwargs' can be deteced by 'keyword' nodes with 'arg=None'.

if version < (3, 5):
if (node.starargs is not None) or (node.kwargs is not None):
needs_wrap = True
else:
if IS_PY35_OR_GREATER:
for pos_arg in node.args:
if isinstance(pos_arg, ast.Starred):
needs_wrap = True

for keyword_arg in node.keywords:
if keyword_arg.arg is None:
needs_wrap = True
else:
if (node.starargs is not None) or (node.kwargs is not None):
needs_wrap = True

node = self.generic_visit(node)

Expand Down Expand Up @@ -1364,7 +1362,7 @@ def visit_ExceptHandler(self, node):

node = self.generic_visit(node)

if version.major == 3:
if IS_PY3:
return node

if not isinstance(node.name, ast.Tuple):
Expand All @@ -1385,7 +1383,7 @@ def visit_With(self, node):

node = self.generic_visit(node)

if version.major == 2:
if IS_PY2:
items = [node]
else:
items = node.items
Expand Down Expand Up @@ -1422,7 +1420,7 @@ def visit_FunctionDef(self, node):
node = self.generic_visit(node)
self.inject_print_collector(node)

if version.major == 3:
if IS_PY3:
return node

# Protect 'tuple parameter unpacking' with '_getiter_'.
Expand All @@ -1447,7 +1445,7 @@ def visit_Lambda(self, node):

node = self.generic_visit(node)

if version.major == 3:
if IS_PY3:
return node

# Check for tuple parameters which need _getiter_ protection
Expand Down
9 changes: 4 additions & 5 deletions tests/test_print_stmt.py
@@ -1,19 +1,18 @@
from RestrictedPython.PrintCollector import PrintCollector

import pytest
from RestrictedPython._compat import IS_PY2, IS_PY3
import RestrictedPython
import pytest
import six
import sys


pytestmark = pytest.mark.skipif(
sys.version_info.major == 3,
IS_PY3,
reason="print statement no longer exists in Python 3")


compilers = ('compiler', [RestrictedPython.compile.compile_restricted_exec])

if sys.version_info.major == 2:
if IS_PY2:
from RestrictedPython import RCompile
compilers[1].append(RCompile.compile_restricted_exec)

Expand Down
29 changes: 14 additions & 15 deletions tests/test_transformer.py
@@ -1,18 +1,17 @@
from RestrictedPython.Guards import guarded_iter_unpack_sequence
from RestrictedPython.Guards import guarded_unpack_sequence

from RestrictedPython._compat import IS_PY2, IS_PY3
import RestrictedPython
import contextlib
import pytest
import RestrictedPython
import six
import sys
import types


# Define the arguments for @pytest.mark.parametrize to be able to test both the
# old and the new implementation to be equal:
compile = ('compile', [RestrictedPython.compile.compile_restricted_exec])
if sys.version_info < (3,):
if IS_PY2:
from RestrictedPython import RCompile
compile[1].append(RCompile.compile_restricted_exec)

Expand Down Expand Up @@ -62,7 +61,7 @@ def no_exec():
"""


@pytest.mark.skipif(sys.version_info >= (3, 0),
@pytest.mark.skipif(IS_PY3,
reason="exec statement no longer exists in Python 3")
@pytest.mark.parametrize(*compile)
def test_transformer__RestrictingNodeTransformer__generic_visit__102(compile):
Expand All @@ -72,7 +71,7 @@ def test_transformer__RestrictingNodeTransformer__generic_visit__102(compile):


@pytest.mark.skipif(
sys.version_info < (3, 0),
IS_PY2,
reason="exec statement in Python 3 raises SyntaxError itself")
@pytest.mark.parametrize(*compile)
def test_transformer__RestrictingNodeTransformer__generic_visit__103(compile):
Expand Down Expand Up @@ -249,7 +248,7 @@ def test_transformer__RestrictingNodeTransformer__visit_Attribute__7(compile, mo
assert ret == 2


@pytest.mark.skipif(sys.version_info < (3, 0),
@pytest.mark.skipif(IS_PY2,
reason="exec is a statement in Python 2")
@pytest.mark.parametrize(*compile)
def test_transformer__RestrictingNodeTransformer__visit_Call__1(compile):
Expand Down Expand Up @@ -704,7 +703,7 @@ def test_transformer__RestrictingNodeTransformer__visit_FunctionDef_1(compile):
assert code is None
assert errors[0] == err_msg

if sys.version_info.major == 2:
if IS_PY2:
code, errors = compile("def foo((a, _bad)): pass")[:2]
assert code is None
assert errors[0] == err_msg
Expand All @@ -715,7 +714,7 @@ def test_transformer__RestrictingNodeTransformer__visit_FunctionDef_1(compile):
assert code is None
assert errors[0] == err_msg

if sys.version_info.major == 3:
if IS_PY3:
code, errors = compile("def foo(good, *, _bad): pass")[:2]
assert code is None
assert errors[0] == err_msg
Expand All @@ -731,7 +730,7 @@ def nested_with_order((a, b), (c, d)):


@pytest.mark.skipif(
sys.version_info.major == 3,
IS_PY3,
reason="tuple parameter unpacking is gone in python 3")
@pytest.mark.parametrize(*compile)
def test_transformer__RestrictingNodeTransformer__visit_FunctionDef_2(compile, mocker):
Expand Down Expand Up @@ -798,7 +797,7 @@ def test_transformer__RestrictingNodeTransformer__visit_Lambda_1(compile):
assert code is None
assert errors[0] == err_msg

if sys.version_info.major == 2:
if IS_PY2:
# The old one did not support tuples at all.
if compile is RestrictedPython.compile.compile_restricted_exec:
code, errors = compile("lambda (a, _bad): None")[:2]
Expand All @@ -809,14 +808,14 @@ def test_transformer__RestrictingNodeTransformer__visit_Lambda_1(compile):
assert code is None
assert errors[0] == err_msg

if sys.version_info.major == 3:
if IS_PY3:
code, errors = compile("lambda good, *, _bad: None")[:2]
assert code is None
assert errors[0] == err_msg


@pytest.mark.skipif(
sys.version_info.major == 3,
IS_PY3,
reason="tuple parameter unpacking is gone in python 3")
@pytest.mark.parametrize(*compile)
def test_transformer__RestrictingNodeTransformer__visit_Lambda_2(compile, mocker):
Expand Down Expand Up @@ -870,7 +869,7 @@ def test_transformer__RestrictingNodeTransformer__visit_Assign(compile, mocker):


@pytest.mark.skipif(
sys.version_info.major == 2,
IS_PY2,
reason="starred assignments are python3 only")
@pytest.mark.parametrize(*compile)
def test_transformer__RestrictingNodeTransformer__visit_Assign2(compile, mocker):
Expand Down Expand Up @@ -1001,7 +1000,7 @@ def tuple_unpack(err):


@pytest.mark.skipif(
sys.version_info.major == 3,
IS_PY3,
reason="tuple unpacking on exceptions is gone in python3")
@pytest.mark.parametrize(*compile)
def test_transformer__RestrictingNodeTransformer__visit_ExceptHandler(compile, mocker):
Expand Down
5 changes: 5 additions & 0 deletions tox.ini
Expand Up @@ -46,6 +46,11 @@ basepython = python2.7
deps = isort
commands = isort --check-only --recursive {toxinidir}/src {posargs}

[testenv:isort-apply]
basepython = python2.7
deps = isort
commands = isort --apply --recursive {toxinidir}/src {posargs}

[testenv:flake8]
basepython = python2.7
deps = flake8
Expand Down

0 comments on commit 7dc6e5f

Please sign in to comment.