python-3.xpycharmcode-inspection

Expected type 'Type[Add | Sub | Mult | Div | Pow | BitXor | USub]', got 'Type[operator]' instead


I use a nifty class that evaluates string expressionsand converts them to their math equivalent :

# Taken out of context for MVCE

import ast
import operator as op

OPERATORS = {
    ast.Add: op.add,
    ast.Sub: op.sub,
    ast.Mult: op.mul,
    ast.Div: op.truediv,
    ast.Pow: op.pow,
    ast.BitXor: op.xor,
    ast.USub: op.neg
}

def eval_expr(expr):

    return eval_(ast.parse(expr, mode='eval').body)

def eval_(node):
    if isinstance(node, ast.Num):  # <number>
        value = node.n
    elif isinstance(node, ast.BinOp):  # <left> <operator> <right>
        value = OPERATORS[type(node.op)](eval_(node.left), eval_(node.right))
    elif isinstance(node, ast.UnaryOp):  # <operator> <operand> e.g., -1
        value = OPERATORS[type(node.op)](eval_(node.operand))
    else:
        raise TypeError(node)

    return value

x = eval_expr("1 + 2")
print(x)

PyCharm code inspection highlights the instances of type(node.op) as problematic:

Expected type 'Type[Add | Sub | Mult | Div | Pow | BitXor | USub]' (matched generic type '_KT'), got 'Type[operator]' instead
Expected type 'Type[Add | Sub | Mult | Div | Pow | BitXor | USub]' (matched generic type '_KT'), got 'Type[unaryop]' instead

The class seems to function just fine, but my OCD wants to know how this could be refactored to avoid the inspection warnings. Or is this a PyCharm inspection gremlin?


Solution

  • The type checker is warning you that your dictionary that maps AST node types for operators to their implementations is incomplete. The type checker knows all of the possible types of node.op (which it seems to be describing as subtypes of the ast.operator and ast.unaryop parent types), and has noticed that your dictionary doesn't handle them all.

    Since there are operators that you haven't included, it's possible for a parsable expression (like, say "2 << 5" which does a left shift, or "~31" which does a bitwise inversion) to fail to be handled by your code.

    While I don't use PyCharm and thus can't test it for myself, you can probably satisfy its type checker by adding some error handling to your code, so that operator types you don't support will still be dealt with appropriately, rather than causing an uncaught exception (such as a KeyError from the dictionary) to leak out. For instance, you could use OPERATORS.get(type(node.op)) and then test for None before calling the result. If the operator type isn't in the dictionary, you'd raise an exception of your own.