18

我目前正在尝试为 python 推出我自己的“marshal”代码,这样我就可以在 Google App Engine 上存储已编译的 python 代码,以便以动态方式提供脚本。大家都可以验证,GAE 不支持“marshal”,并且“pickle”不能序列化代码对象。

我发现我可以构造一个代码对象,types.CodeType()但它需要 12 个参数。

尽我所能,我找不到关于这个调用的任何文档,我真的需要构造代码对象,这样我才能exec()做到。我的问题是,有谁知道这个types.CodeType()“构造函数”的参数是什么或任何内省的方法?我已经使用了这里info()定义的函数,但它只吐出通用信息!

快速常见问题解答:

  • 问:为什么要编译代码?
  • 答:在 Google App Engine 上,CPU 时间会花费真金白银,而我可以节省的每一点 CPU 周期都很重要。
  • 问:为什么不用“元帅”?
  • 答:这是Google App Engine 中不支持的模块之一
  • 问:为什么不用“泡菜”?
  • A:Pickle 不支持代码对象的序列化。

更新

自 2011 年 7 月 7 日起,Google App Engine 基础架构不允许实例化代码对象,因此我的论点没有实际意义。希望这在未来能在 GAE 上得到解决。

4

4 回答 4

7

问题问:

这种类型的参数是什么。CodeType()“构造函数”

来自关于检查模块的python文档:

co_argcount: number of arguments (not including * or ** args)
co_code: string of raw compiled bytecode
co_consts: tuple of constants used in the bytecode
co_filename: name of file in which this code object was created
co_firstlineno: number of first line in Python source code
co_flags: bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
co_lnotab: encoded mapping of line numbers to bytecode indices
co_name: name with which this code object was defined
co_names: tuple of names of local variables
co_nlocals: number of local variables
co_stacksize: virtual machine stack space required
co_varnames: tuple of names of arguments and local variables

这篇博文有更详细的解释:http ://tech.blog.aknin.name/2010/07/03/pythons-innards-code-objects/

注意:博客文章谈论的是 python 3,而上面引用的 python 文档是 python 2.7。

于 2012-10-13T22:13:38.547 回答
6

C API 函数 PyCode_New (最少)记录在此处:http ://docs.python.org/c-api/code.html — 此函数的 C 源代码(Python 2.7)在此处:http://hg。 python.org/cpython/file/b5ac5e25d506/Objects/codeobject.c#l43

PyCodeObject *
PyCode_New(int argcount, int nlocals, int stacksize, int flags,
           PyObject *code, PyObject *consts, PyObject *names,
           PyObject *varnames, PyObject *freevars, PyObject *cellvars,
           PyObject *filename, PyObject *name, int firstlineno,
           PyObject *lnotab)

然而,在 Python 构造函数中,最后六个参数似乎交换了一点。这是提取 Python 传入的参数的 C 代码:http: //hg.python.org/cpython/file/b5ac5e25d506/Objects/codeobject.c#l247

if (!PyArg_ParseTuple(args, "iiiiSO!O!O!SSiS|O!O!:code",
                      &argcount, &nlocals, &stacksize, &flags,
                      &code,
                      &PyTuple_Type, &consts,
                      &PyTuple_Type, &names,
                      &PyTuple_Type, &varnames,
                      &filename, &name,
                      &firstlineno, &lnotab,
                      &PyTuple_Type, &freevars,
                      &PyTuple_Type, &cellvars))
    return NULL;

Python化的:

def __init__(self, argcount, nlocals, stacksize, flags, code,
                   consts, names, varnames, filename, name, 
                   firstlineno, lnotab, freevars=None, cellvars=None): # ...
于 2011-07-07T15:35:30.130 回答
5

我去拿了这里找到的代码,并删除了已弃用的“新”模块的依赖项。

import types, copy_reg
def code_ctor(*args):
    # delegate to new.code the construction of a new code object
    return types.CodeType(*args)
def reduce_code(co):
    # a reductor function must return a tuple with two items: first, the
    # constructor function to be called to rebuild the argument object
    # at a future de-serialization time; then, the tuple of arguments
    # that will need to be passed to the constructor function.
    if co.co_freevars or co.co_cellvars:
        raise ValueError, "Sorry, cannot pickle code objects from closures"
    return code_ctor, (co.co_argcount, co.co_nlocals, co.co_stacksize,
        co.co_flags, co.co_code, co.co_consts, co.co_names,
        co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno,
        co.co_lnotab)
# register the reductor to be used for pickling objects of type 'CodeType'
copy_reg.pickle(types.CodeType, reduce_code)
if __name__ == '__main__':
    # example usage of our new ability to pickle code objects
    import cPickle
    # a function (which, inside, has a code object, of course)
    def f(x): print 'Hello,', x
    # serialize the function's code object to a string of bytes
    pickled_code = cPickle.dumps(f.func_code)
    # recover an equal code object from the string of bytes
    recovered_code = cPickle.loads(pickled_code)
    # build a new function around the rebuilt code object
    g = types.FunctionType(recovered_code, globals( ))
    # check what happens when the new function gets called
    g('world')
于 2011-07-07T15:18:36.747 回答
2

回答您需要回答的问题,而不是您提出的问题:

目前,您无法在 App Engine Python 环境中执行任意字节码。尽管您可能能够访问字节码或代码对象,但您无法加载。

但是,您还有另一种选择:按实例缓存。将全局字典映射数据存储键(用于存储 Python 代码的数据存储条目)存储到编译的代码对象。如果缓存中不存在该对象,则从源代码编译它并将其存储在那里。您必须对每个实例进行编译工作,但不必对每个请求都进行编译,这样可以节省大量工作。

于 2011-07-08T00:07:58.957 回答