Commit 36780b2a authored by Stefan Behnel's avatar Stefan Behnel

implement cyfunction.__code__ (with empty byte code), including argument related co_* fields

parent 500f9a40
......@@ -5189,8 +5189,10 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
# self_object ExprNode or None
# binding bool
# module_name EncodedString Name of defining module
# code_object CodeObjectNode the PyCodeObject creator node
subexprs = ['code_object']
subexprs = []
self_object = None
binding = False
......@@ -5219,16 +5221,22 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
def generate_result_code(self, code):
if self.binding:
constructor = "__Pyx_CyFunction_NewEx"
if self.code_object:
code_object_result = ', ' + self.code_object.py_result()
else:
code_object_result = ', NULL'
else:
constructor = "PyCFunction_NewEx"
code_object_result = ''
py_mod_name = self.get_py_mod_name(code)
code.putln(
'%s = %s(&%s, %s, %s); %s' % (
'%s = %s(&%s, %s, %s%s); %s' % (
self.result(),
constructor,
self.pymethdef_cname,
self.self_result_code(),
py_mod_name,
code_object_result,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
......@@ -5244,6 +5252,65 @@ class InnerFunctionNode(PyCFunctionNode):
return "((PyObject*)%s)" % (Naming.cur_scope_cname)
return "NULL"
class CodeObjectNode(ExprNode):
# Create a PyCodeObject for a CyFunction instance.
#
# def_node DefNode the Python function node
# varnames TupleNode a tuple with all variable names
subexprs = ['varnames']
is_temp = False
def __init__(self, def_node):
ExprNode.__init__(self, def_node.pos, def_node=def_node)
args = list(def_node.args)
if def_node.star_arg:
args.append(def_node.star_arg)
if def_node.starstar_arg:
args.append(def_node.starstar_arg)
# FIXME: lacks non-args !
self.varnames = TupleNode(
def_node.pos,
args = [ IdentifierStringNode(arg.pos, unicode_value=arg.name,
value=StringEncoding.BytesLiteral(arg.name.utf8encode()))
for arg in args ],
is_temp = 0,
is_literal = 1)
def calculate_result_code(self):
return self.result_code
def generate_result_code(self, code):
self.result_code = code.get_py_const(py_object_type, 'codeobj_', cleanup_level=2)
code = code.get_cached_constants_writer()
code.mark_pos(self.pos)
func = self.def_node
func_name = code.get_py_string_const(
func.name, identifier=True, is_str=False, unicode_value=func.name)
# FIXME: better way to get the module file path at module init time? Encoding to use?
file_path = StringEncoding.BytesLiteral(func.pos[0].get_filenametable_entry().encode('utf8'))
file_path_const = code.get_py_string_const(file_path, identifier=False, is_str=True)
code.putln("%s = (PyObject*)__Pyx_PyCode_New(%d, %d, %d, 0, 0, %s, %s, %s, %s, %s, %s, %s, %s, %d, %s); %s" % (
self.result_code,
len(func.args), # argcount
func.num_kwonly_args, # kwonlyargcount (Py3 only)
len(self.varnames.args), # nlocals
Naming.empty_bytes, # code
Naming.empty_tuple, # consts
Naming.empty_tuple, # names (FIXME: all local non-args!)
self.varnames.result(), # varnames
Naming.empty_tuple, # freevars (FIXME)
Naming.empty_tuple, # cellvars (FIXME)
file_path_const, # filename
func_name, # name
self.pos[1], # firstlineno
Naming.empty_bytes, # lnotab
code.error_goto_if_null(self.result_code, self.pos),
))
class LambdaNode(InnerFunctionNode):
# Lambda expression node (only used as a function reference)
#
......@@ -8806,11 +8873,12 @@ typedef struct {
PyObject *func_weakreflist;
PyObject *func_name;
PyObject *func_doc;
PyObject *func_code;
} __pyx_CyFunctionObject;
static PyTypeObject *__pyx_CyFunctionType = 0;
static PyObject *__Pyx_CyFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module);
static PyObject *__Pyx_CyFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module, PyObject* code);
static int __Pyx_CyFunction_init(void);
""" % Naming.__dict__,
......@@ -8949,6 +9017,14 @@ __Pyx_CyFunction_get_closure(CYTHON_UNUSED __pyx_CyFunctionObject *op)
return Py_None;
}
static PyObject *
__Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op)
{
PyObject* result = (op->func_code) ? op->func_code : Py_None;
Py_INCREF(result);
return result;
}
static PyGetSetDef __pyx_CyFunction_getsets[] = {
{(char *) "func_doc", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
{(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
......@@ -8961,6 +9037,8 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = {
{(char *) "__globals__", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0},
{(char *) "func_closure", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0},
{(char *) "__closure__", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0},
{(char *) "func_code", (getter)__Pyx_CyFunction_get_code, 0, 0, 0},
{(char *) "__code__", (getter)__Pyx_CyFunction_get_code, 0, 0, 0},
{0, 0, 0, 0, 0}
};
......@@ -8989,7 +9067,7 @@ static PyMethodDef __pyx_CyFunction_methods[] = {
};
static PyObject *__Pyx_CyFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) {
static PyObject *__Pyx_CyFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module, PyObject* code) {
__pyx_CyFunctionObject *op = PyObject_GC_New(__pyx_CyFunctionObject, __pyx_CyFunctionType);
if (op == NULL)
return NULL;
......@@ -9002,6 +9080,8 @@ static PyObject *__Pyx_CyFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObjec
op->func_dict = NULL;
op->func_name = NULL;
op->func_doc = NULL;
Py_XINCREF(code);
op->func_code = code;
PyObject_GC_Track(op);
return (PyObject *)op;
}
......@@ -9016,6 +9096,7 @@ static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m)
Py_XDECREF(m->func_dict);
Py_XDECREF(m->func_name);
Py_XDECREF(m->func_doc);
Py_XDECREF(m->func_code);
PyObject_GC_Del(m);
}
......@@ -9026,6 +9107,7 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit,
Py_VISIT(m->func_dict);
Py_VISIT(m->func_name);
Py_VISIT(m->func_doc);
Py_VISIT(m->func_code);
return 0;
}
......
......@@ -567,9 +567,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
#if PY_MAJOR_VERSION < 3
#define __Pyx_BUILTIN_MODULE_NAME "__builtin__"
#define __Pyx_PyIdentifier_FromString(s) PyString_FromString(s)
#define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \\
PyCode_New(a, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
#else
#define __Pyx_BUILTIN_MODULE_NAME "builtins"
#define __Pyx_PyIdentifier_FromString(s) PyUnicode_FromString(s)
#define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) \\
PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
#endif
#if PY_MAJOR_VERSION >= 3
......
......@@ -2339,10 +2339,13 @@ class DefNode(FuncDefNode):
if genv.is_closure_scope:
rhs = self.py_cfunc_node = ExprNodes.InnerFunctionNode(
self.pos, pymethdef_cname = self.entry.pymethdef_cname)
self.pos, pymethdef_cname = self.entry.pymethdef_cname,
code_object = ExprNodes.CodeObjectNode(self))
else:
rhs = self.py_cfunc_node = ExprNodes.PyCFunctionNode(
self.pos, pymethdef_cname = self.entry.pymethdef_cname, binding = env.directives['binding'])
self.pos, pymethdef_cname = self.entry.pymethdef_cname,
binding = env.directives['binding'],
code_object = ExprNodes.CodeObjectNode(self))
if env.is_py_class_scope:
if not self.is_staticmethod and not self.is_classmethod:
......@@ -7806,11 +7809,9 @@ static void __Pyx_AddTraceback(const char *funcname, int %(CLINENO)s,
if (!py_funcname) goto bad;
py_globals = PyModule_GetDict(%(GLOBALS)s);
if (!py_globals) goto bad;
py_code = PyCode_New(
py_code = __Pyx_PyCode_New(
0, /*int argcount,*/
#if PY_MAJOR_VERSION >= 3
0, /*int kwonlyargcount,*/
#endif
0, /*int nlocals,*/
0, /*int stacksize,*/
0, /*int flags,*/
......
......@@ -2,6 +2,9 @@
# mode: run
# tag: cyfunction
import sys
IS_PY3 = sys.version_info[0] >= 3
def test_dict():
"""
>>> test_dict.foo = 123
......@@ -82,3 +85,67 @@ class BindingTest:
"""
def __repr__(self):
return '<BindingTest instance>'
def codeof(func):
if IS_PY3:
return func.__code__
else:
return func.func_code
def cy_no_arg(): pass
def cy_one_arg(a): pass
def cy_two_args(x, b): pass
def cy_default_args(x=1, b=2): pass
def test_code():
"""
>>> def no_arg(): pass
>>> def one_arg(a): pass
>>> def two_args(x, b): pass
>>> def default_args(x=1, b=2): pass
>>> codeof(no_arg).co_argcount
0
>>> codeof(cy_no_arg).co_argcount
0
>>> print(codeof(no_arg).co_name)
no_arg
>>> print(codeof(cy_no_arg).co_name)
cy_no_arg
>>> codeof(no_arg).co_varnames
()
>>> codeof(cy_no_arg).co_varnames
()
>>> codeof(one_arg).co_argcount
1
>>> codeof(cy_one_arg).co_argcount
1
>>> print(codeof(one_arg).co_name)
one_arg
>>> print(codeof(cy_one_arg).co_name)
cy_one_arg
>>> codeof(one_arg).co_varnames
('a',)
>>> codeof(cy_one_arg).co_varnames
('a',)
>>> codeof(two_args).co_argcount
2
>>> codeof(cy_two_args).co_argcount
2
>>> codeof(two_args).co_varnames
('x', 'b')
>>> codeof(cy_two_args).co_varnames
('x', 'b')
>>> codeof(default_args).co_argcount
2
>>> codeof(cy_default_args).co_argcount
2
>>> codeof(default_args).co_varnames
('x', 'b')
>>> codeof(cy_default_args).co_varnames
('x', 'b')
"""
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment