Skip to content

Commit

Permalink
Merge 0698095 into 1c29dac
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Zhu committed Aug 23, 2018
2 parents 1c29dac + 0698095 commit 006bb8b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 1 deletion.
18 changes: 18 additions & 0 deletions simpleeval.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ def __init__(self, operators=None, functions=None, names=None):
elif isinstance(self.names, dict) and "None" not in self.names:
self.names["None"] = None

# py3.6, f-strings
if hasattr(ast, 'JoinedStr'):
self.nodes[ast.JoinedStr] = self._eval_joinedstr # f-string
self.nodes[ast.FormattedValue] = self._eval_formattedvalue # formatted value in f-string

def eval(self, expr):
""" evaluate an expresssion, using the operators, functions and
names previously set up. """
Expand Down Expand Up @@ -433,6 +438,19 @@ def _eval_slice(self, node):
step = self._eval(node.step)
return slice(lower, upper, step)

def _eval_joinedstr(self, node):
evaluated_values = [str(self._eval(n)) for n in node.values]
if sum(len(v) for v in evaluated_values) > MAX_STRING_LENGTH:
raise IterableTooLong("Sorry, I will not evaluate something this long.")
return ''.join(evaluated_values)

def _eval_formattedvalue(self, node):
if node.format_spec:
fmt = "{:" + self._eval(node.format_spec) + "}"
return fmt.format(self._eval(node.value))
else:
return self._eval(node.value)


class EvalWithCompoundTypes(SimpleEval):
"""
Expand Down
59 changes: 58 additions & 1 deletion test_simpleeval.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"""
# pylint: disable=too-many-public-methods, missing-docstring

import sys
import unittest
import operator
import ast
Expand Down Expand Up @@ -154,6 +154,14 @@ def test_is(self):
self.t('1 is not None', True)
self.t('None is not None', False)

def test_fstring(self):
if sys.version_info >= (3, 6, 0):
self.t('f""', "")
self.t('f"stuff"', "stuff")
self.t('f"one is {1} and two is {2}"', "one is 1 and two is 2")
self.t('f"1+1 is {1+1}"', "1+1 is 2")
self.t('f"{\'dramatic\':!<11}"', "dramatic!!!")


class TestFunctions(DRYTest):
""" Functions for expressions to play with """
Expand Down Expand Up @@ -328,6 +336,10 @@ def test_string_length(self):
with self.assertRaises(simpleeval.IterableTooLong):
self.t("'" + (50000 * "stuff") + "'", 0)

if sys.version_info >= (3, 6, 0):
with self.assertRaises(simpleeval.IterableTooLong):
self.t("f'{\"foo\"*50000}'", 0)

def test_bytes_array_test(self):
self.t("'20000000000000000000'.encode() * 5000",
'20000000000000000000'.encode() * 5000)
Expand Down Expand Up @@ -405,6 +417,51 @@ def test_string_format(self):
with self.assertRaises(simpleeval.FeatureNotAvailable):
self.t('"{string.__class__}".format(string="things")', 0)

if sys.version_info >= (3, 6, 0):
self.s.names['x'] = 42

with self.assertRaises(simpleeval.FeatureNotAvailable):
self.t('f"{x.__class__}"', 0)

self.s.names['x'] = lambda y: y

with self.assertRaises(simpleeval.FeatureNotAvailable):
self.t('f"{x.__globals__}"', 0)

class EscapeArtist(object):
@staticmethod
def trapdoor():
return 42

@staticmethod
def _quasi_private():
return 84

self.s.names['houdini'] = EscapeArtist() # let's just retest this, but in a f-string

with self.assertRaises(simpleeval.FeatureNotAvailable):
self.t('f"{houdini.trapdoor.__globals__}"', 0)

with self.assertRaises(simpleeval.FeatureNotAvailable):
self.t('f"{houdini.trapdoor.func_globals}"', 0)

with self.assertRaises(simpleeval.FeatureNotAvailable):
self.t('f"{houdini._quasi_private()}"', 0)

# and test for changing '_' to '__':

dis = simpleeval.DISALLOW_PREFIXES
simpleeval.DISALLOW_PREFIXES = ['func_']

self.t('f"{houdini.trapdoor()}"', "42")
self.t('f"{houdini._quasi_private()}"', "84")

# and return things to normal

simpleeval.DISALLOW_PREFIXES = dis



class TestCompoundTypes(DRYTest):
""" Test the compound-types edition of the library """

Expand Down

0 comments on commit 006bb8b

Please sign in to comment.