Skip to content

Instantly share code, notes, and snippets.

@Jinmo
Created April 17, 2018 21:19
Show Gist options
  • Select an option

  • Save Jinmo/5bba8f5497c5f31734a3e3ded8c74412 to your computer and use it in GitHub Desktop.

Select an option

Save Jinmo/5bba8f5497c5f31734a3e3ded8c74412 to your computer and use it in GitHub Desktop.

Revisions

  1. Jinmo created this gist Apr 17, 2018.
    57 changes: 57 additions & 0 deletions literal.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    from ast import parse, iter_fields, AST

    class NotAllowedException(Exception):
    def __init__(self, cls):
    self.cls = cls
    pass

    def __str__(self):
    return '%r is not allowed for execution' % self.cls

    class Executor(object):
    def __init__(self, whitelist=None):
    self.allowed_types = set()

    if whitelist is not None:
    for c in whitelist:
    self.allow(c)

    def allow(self, source):
    node = parse(source)
    self._allow_node(node)

    def _allow_node(self, node):
    if isinstance(node, list):
    for x in node:
    self._allow_node(x)
    elif isinstance(node, AST):
    self.allowed_types.add(node.__class__)
    for name, field in iter_fields(node):
    self._allow_node(field)

    def check_allowed(self, node):
    if isinstance(node, list):
    for x in node:
    self.check_allowed(x)
    elif isinstance(node, AST):
    if node.__class__ not in self.allowed_types:
    raise NotAllowedException(node.__class__)
    for name, field in iter_fields(node):
    self.check_allowed(field)

    def __call__(self, source): # execute
    node = parse(source)
    try:
    self.check_allowed(node)
    except NotAllowedException:
    raise
    return eval(source)

    if __name__ == '__main__':

    ex = Executor((
    '1 - 1',
    '1 + 1'
    ))
    print ex('100 + 100')
    print ex('1 * 1')