如何检测python源代码中的I / O(I / O的标准库方式)

时间:2011-02-15 00:06:18

标签: python optimization compiler-construction io abstract-syntax-tree

我为我的最后一年项目的一小部分python代码构建了一个优化编译器。我正在做的第一件事是测试变量是否涉及或导致I / O.如果我要静静地跟踪一个函数调用谚语中的兔子洞,我究竟怎么知道它涉及I / O?是否会调用内置的python函数,如print,input或内置的'file'对象函数调用来进行读写?

我没有太多时间来做这个项目(只有6个月),所以我完全无视人们在C中编写I / O,包装它的某种python对象并从python中调用它。

是否生成了表示是否存在I / O的字节代码?或者它与AST一样无益?

如果它是可撤销的,那就没什么大不了的,我只需要我的项目的I / O子集进行打印,输入读写。那或做活体分析。

感谢。

1 个答案:

答案 0 :(得分:2)

这并不像查看字节码那么简单,因为对事物的调用只是符号查找:

>>> def write_to_a_file(s):
    f = open('foo.txt', 'w')
    f.write(s)
    f.close()


>>> import dis
>>> dis.dis(write_to_a_file)
  2           0 LOAD_GLOBAL              0 (open)
              3 LOAD_CONST               1 ('foo.txt')
              6 LOAD_CONST               2 ('w')
              9 CALL_FUNCTION            2
             12 STORE_FAST               1 (f)

  3          15 LOAD_FAST                1 (f)
             18 LOAD_ATTR                1 (write)
             21 LOAD_FAST                0 (s)
             24 CALL_FUNCTION            1
             27 POP_TOP             

  4          28 LOAD_FAST                1 (f)
             31 LOAD_ATTR                2 (close)
             34 CALL_FUNCTION            0
             37 POP_TOP             
             38 LOAD_CONST               0 (None)
             41 RETURN_VALUE      

他们自己的字节码只是加载东西,调用东西和存储东西。如果你在字节码级别操作,你实际上必须查看有效负载。

Check out the current list of Python bytecodes你可以看到那里没有任何可以区分I / O调用的东西。

即使你要检查所有LOAD_GLOBAL个电话或LOAD_FAST个电话并应用白名单,这也不一定有效,因为有些模块提供I / O且字节码不是真的帮助你:

>>> def uses_a_module_for_io(s):
    import shutil
    shutil.copy(s, 'foo.txt')


>>> dis.dis(uses_a_module_for_io)
  2           0 LOAD_CONST               1 (-1)
              3 LOAD_CONST               0 (None)
              6 IMPORT_NAME              0 (shutil)
              9 STORE_FAST               1 (shutil)

  3          12 LOAD_FAST                1 (shutil)
             15 LOAD_ATTR                1 (copy)
             18 LOAD_FAST                0 (s)
             21 LOAD_CONST               2 ('foo.txt')
             24 CALL_FUNCTION            2
             27 POP_TOP             
             28 LOAD_CONST               0 (None)
             31 RETURN_VALUE  

>>> def doesnt_use_shutil_really(s):
    shutil = object()
    shutil.copy = lambda x,y: None
    shutil.copy(s, 'foo.txt')


>>> dis.dis(doesnt_use_shutil_really)
  2           0 LOAD_GLOBAL              0 (object)
              3 CALL_FUNCTION            0
              6 STORE_FAST               1 (shutil)

  3           9 LOAD_CONST               1 (<code object <lambda> at 011D8AD0, file "<pyshell#29>", line 3>)
             12 MAKE_FUNCTION            0
             15 LOAD_FAST                1 (shutil)
             18 STORE_ATTR               1 (copy)

  4          21 LOAD_FAST                1 (shutil)
             24 LOAD_ATTR                1 (copy)
             27 LOAD_FAST                0 (s)
             30 LOAD_CONST               2 ('foo.txt')
             33 CALL_FUNCTION            2
             36 POP_TOP             
             37 LOAD_CONST               0 (None)
             40 RETURN_VALUE        

请注意,LOAD_FAST的{​​{1}}可以是用户刚刚组成的内容。在我的情况下,我只是将它作为通用对象,但用户也可以在其路径上使用不同的shutil