1

I want to prohibit certain operators being used in a function, for example, say I want to make sure the ** power operator is not being used in the following function:

def pow(x, y):
    return x ** y

I have used inspect.getsource to get the source and checked if "**" is in the source but that will wrongly cause an assert error if there happens to be "**" in the source elsewhere so how to actually check the code for the existence of a particular operator?

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • I think you would have to put in a dummy class instance, in which the forbidden operations (e.g., __pow__) raise an exception. – mdurant Sep 16 '14 at 20:06

3 Answers3

9

I'd personally go for Ashwini's answer if you have 3.4 at hand, however, if you need to be a bit more compatible and don't mind parsing the source, then you can make use of ast, eg:

import inspect
import ast

def pow(x, y):
    return x ** y

nodes = ast.walk(ast.parse(inspect.getsource(pow)))
has_power = any(isinstance(node, ast.Pow) for node in nodes)
Jon Clements
  • 138,671
  • 33
  • 247
  • 280
  • I am using python 2.7, I should have specified. This is exactly what I was looking for, I had looked at `ast.walk` but was missing the `ast.parse` to make it work. – Padraic Cunningham Sep 16 '14 at 21:00
5

Note: Byte code we are dealing with here is a CPython implementation detail. Don't expect it to work on other implementations of Python. Prefer Jon Clements's approach.


In CPython 3.4+ you can use dis.get_instructions to check if BINARY_POWER instruction is present in function's code object or not(Also explained in What's new in Python 3.4 doc):

>>> import dis
>>> def search_instruction(code_object, instr_code):
        for ins in dis.get_instructions(code_object):
            if ins.opcode == instr_code:
                return True
        return False
... 
>>> def f1():                                       
    s = x ** 2
...     
>>> def f2():
    s = 'x ** 2'
...     
>>> dis.opmap['BINARY_POWER']
19
>>> search_instruction(f1.__code__, 19)
True
>>> search_instruction(f2.__code__, 19)
False

For CPython 2.X specifically you can try byteplay package available on PyPI(its Python 3 fork: https://github.com/serprex/byteplay).:

>>> import byteplay
>>> def search_instruction(code_object, instr_code):
        for ins, _ in byteplay.Code.from_code(code_object).code:
                if ins == instr_code:
                    return True
        return False
...     
>>> search_instruction(f1.__code__, 19)
True
>>> search_instruction(f2.__code__, 19)
False

Related: Bytecode: What, Why, and How to Hack it - Dr. Ryan F Kelly

Community
  • 1
  • 1
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
1

Maybe using the dis module?:

In [9]: dis.dis(pow)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_FAST                1 (y)
              6 BINARY_POWER
              7 RETURN_VALUE

You probably need to redirect stdout to a stringIO to parse it in Python<3.4

Community
  • 1
  • 1
Zah
  • 6,394
  • 1
  • 22
  • 34