【问题标题】:How to detect I/O in python source code(standard library way of I/O)如何检测python源代码中的I/O(I/O的标准库方式)
【发布时间】:2011-02-15 00:06:18
【问题描述】:

我正在为我最后一年的项目的一小部分 python 代码构建一个优化编译器。我要做的第一件事是测试变量是否涉及或导致 I/O。如果我要在众所周知的兔子洞中静态跟踪函数调用,我怎么知道它涉及 I/O?是否会调用内置的 python 函数,例如打印、输入或内置的“文件”对象函数调用来读取和写入?

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

生成的字节码是否指示是否有 I/O?还是和 AST 一样无用?

如果它是可撤消的,没什么大不了的,我将只为我的项目打印 I/O 子集,输入读取和写入。或者做活性分析。

谢谢。

【问题讨论】:

  • 在动态类型语言上通过静态分析来做出这些决定几乎是不可能的。这就是为什么静态优化动态类型语言和静态类型语言要困难得多的原因。
  • @Glenn 别担心。我可以将我正在使用的 python 子集更改为或多或少的限制。但我的问题仍然存在。 python I/O的基本函数调用是什么?它们会是内置函数 open("blah.txt", "rw").read 和 write 吗?还是会根据实现(比如第三方库)而改变?

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


【解决方案1】:

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

>>> 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        

请注意,shutilLOAD_FAST 可以是用户自己编造的。就我而言,我只是将其设为通用对象,但用户也可以在其路径上使用不同的 shutil

【讨论】:

  • 简而言之:Python是一种动态语言,不能对其进行静态分析。
猜你喜欢
  • 1970-01-01
  • 2012-01-20
  • 1970-01-01
  • 1970-01-01
  • 2016-02-20
  • 2015-05-24
  • 1970-01-01
  • 1970-01-01
  • 2012-01-12
相关资源
最近更新 更多