Safe expression parser in Python

前端 未结 5 1109
小鲜肉
小鲜肉 2020-12-01 16:23

How can I allow users to execute mathematical expressions in a safe way? Do I need to write a full parser?

Is there something like ast.literal_eval(), but for expres

相关标签:
5条回答
  • 2020-12-01 16:58

    What sort of expressions do you want? Variable assignment? Function evaluation?

    SymPy aims to become a full-fledged Python CAS.

    0 讨论(0)
  • 2020-12-01 17:00

    Few weeks ago I did similar thing, but for logical expressions (or, and, not, comparisons, parentheses etc.). I did this using Ply parser. I have created simple lexer and parser. Parser generated AST tree that was later use to perform calculations. Doing this in that way allow you to fully control what user enter, because only expressions that are compatible with grammar will be parsed.

    0 讨论(0)
  • 2020-12-01 17:00

    maths functions will consist of numeric and punctuation characters, possible 'E' or 'e' if you allow scientific notation for rational numbers, and the only (other) legal use of alpha characters will be if you allow/provide specific maths functions (e.g. stddev). So, should be trivial to run along the string for alpha characters and check the next little bit isn't suspicious, then simply eval the string in a try/except block.

    Re the comments this reply has received... I agree this approach is playing with fire. Still, that doesn't mean it can't be done safely. I'm new to python (< 2 months), so may not know the workarounds to which this is vulnerable (and of course a new Python version could always render the code unsafe in the future), but - for what little it's worth (mainly my own amusement) - here's my crack at it:

    def evalMaths(s):
        i = 0
        while i < len(s):
            while s[i].isalpha() and i < len(s):
                idn += s[i]
                i += 1
            if (idn and idn != 'e' and idn != 'abs' and idn != 'round'):
                raise Exception("you naughty boy: don't " + repr(idn))
            else:
                i += 1
        return eval(s)
    

    I would be very interested to hear if/how it can be circumvented... (^_^) BTW / I know you can call functions like abs2783 or _983 - if they existed, but they won't. I mean something practical.

    In fact, if anyone can do so, I'll create a question with 200 bounty and accept their answer.

    0 讨论(0)
  • 2020-12-01 17:04

    The Pyparsing examples page lists several expression parsers:

    http://pyparsing.wikispaces.com/file/view/fourFn.py - A conventional arithmetic infix notation parser/evaluator implementation using pyparsing (despite its name, this actually does 5-function arithmetic, plus several trig functions)

    http://pyparsing.wikispaces.com/file/view/simpleBool.py - A boolean infix notation parser/evaluator, using a pyparsing helper method operatorPrecedence, which simplifies the definition of infix operator notations

    http://pyparsing.wikispaces.com/file/view/simpleArith.py http://pyparsing.wikispaces.com/file/view/eval_arith.py - A pair of examples recasting fourFn.py using operatorPrecedence. The first just parses and returns a parse tree, the second adds evaluation logic.

    0 讨论(0)
  • 2020-12-01 17:12

    Yes. Even if there were an equivalent of ast.literal_eval() for expressions, a Python expression can be lots of things other than just a pure mathematical expression, for example an arbitrary function call.

    It wouldn't surprise me if there's already a good mathematical expression parser/evaluator available out there in some open-source module, but if not, it's pretty easy to write one of your own.

    0 讨论(0)
提交回复
热议问题