[pypy-commit] pypy default: merge better-PyDict_Next into default, which also provides a basic but non-functioning GetSetProperty-to-PyGetSetDescrObject
mattip
pypy.commits at gmail.com
Sun Dec 18 11:00:02 EST 2016
Author: Matti Picus <matti.picus at gmail.com>
Branch:
Changeset: r89149:1483fff7dd66
Date: 2016-12-18 17:53 +0200
http://bitbucket.org/pypy/pypy/changeset/1483fff7dd66/
Log: merge better-PyDict_Next into default, which also provides a basic
but non-functioning GetSetProperty-to-PyGetSetDescrObject
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -602,7 +602,7 @@
GLOBALS['%s#%s' % (cpyname, pypy_decl)] = ('PyTypeObject*', pypyexpr)
for cpyname in '''PyMethodObject PyListObject PyLongObject
- PyDictObject PyClassObject'''.split():
+ PyClassObject'''.split():
FORWARD_DECLS.append('typedef struct { PyObject_HEAD } %s'
% (cpyname, ))
build_exported_objects()
diff --git a/pypy/module/cpyext/bufferobject.py b/pypy/module/cpyext/bufferobject.py
--- a/pypy/module/cpyext/bufferobject.py
+++ b/pypy/module/cpyext/bufferobject.py
@@ -31,7 +31,7 @@
dealloc=buffer_dealloc,
realize=buffer_realize)
-def buffer_attach(space, py_obj, w_obj):
+def buffer_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyBufferObject with the given (str) buffer object.
"""
diff --git a/pypy/module/cpyext/bytearrayobject.py b/pypy/module/cpyext/bytearrayobject.py
--- a/pypy/module/cpyext/bytearrayobject.py
+++ b/pypy/module/cpyext/bytearrayobject.py
@@ -7,7 +7,7 @@
PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL)
from pypy.module.cpyext.pyerrors import PyErr_BadArgument
from pypy.module.cpyext.pyobject import (
- PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, track_reference,
+ PyObject, PyObjectP, Py_DecRef, make_ref, from_ref,
make_typedescr, get_typedescr, Py_IncRef)
# Type PyByteArrayObject represents a mutable array of bytes.
# The Python API is that of a sequence;
diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py
--- a/pypy/module/cpyext/bytesobject.py
+++ b/pypy/module/cpyext/bytesobject.py
@@ -73,7 +73,7 @@
py_str.c_ob_sstate = rffi.cast(rffi.INT, 0) # SSTATE_NOT_INTERNED
return py_str
-def bytes_attach(space, py_obj, w_obj):
+def bytes_attach(space, py_obj, w_obj, w_userdata=None):
"""
Copy RPython string object contents to a PyBytesObject. The
c_ob_sval must not be modified.
diff --git a/pypy/module/cpyext/complexobject.py b/pypy/module/cpyext/complexobject.py
--- a/pypy/module/cpyext/complexobject.py
+++ b/pypy/module/cpyext/complexobject.py
@@ -29,7 +29,7 @@
attach=complex_attach,
realize=complex_realize)
-def complex_attach(space, py_obj, w_obj):
+def complex_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyComplexObject with the given complex object. The
value must not be modified.
diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py
--- a/pypy/module/cpyext/dictobject.py
+++ b/pypy/module/cpyext/dictobject.py
@@ -1,11 +1,66 @@
from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rlib.objectmodel import specialize
+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.classdict import ClassDictStrategy
+from pypy.interpreter.typedef import GetSetProperty
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, build_type_checkers, Py_ssize_t,
- Py_ssize_tP, CONST_STRING)
-from pypy.module.cpyext.pyobject import PyObject, PyObjectP, as_pyobj
+ Py_ssize_tP, CONST_STRING, PyObjectFields, cpython_struct,
+ bootstrap_function)
+from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, as_pyobj,
+ make_typedescr, track_reference, create_ref, from_ref, decref,
+ Py_IncRef)
+from pypy.module.cpyext.object import _dealloc
from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
-from pypy.interpreter.error import OperationError
-from rpython.rlib.objectmodel import specialize
+
+PyDictObjectStruct = lltype.ForwardReference()
+PyDictObject = lltype.Ptr(PyDictObjectStruct)
+PyDictObjectFields = PyObjectFields + \
+ (("ob_keys", PyObject),)
+cpython_struct("PyDictObject", PyDictObjectFields, PyDictObjectStruct)
+
+ at bootstrap_function
+def init_dictobject(space):
+ "Type description of PyDictObject"
+ make_typedescr(space.w_dict.layout.typedef,
+ basestruct=PyDictObject.TO,
+ attach=dict_attach,
+ dealloc=dict_dealloc,
+ realize=dict_realize)
+
+def dict_attach(space, py_obj, w_obj, w_userdata=None):
+ """
+ Fills a newly allocated PyDictObject with the given dict object.
+ """
+ py_dict = rffi.cast(PyDictObject, py_obj)
+ py_dict.c_ob_keys = lltype.nullptr(PyObject.TO)
+ # Problems: if this dict is a typedict, we may have unbound GetSetProperty
+ # functions in the dict. The corresponding PyGetSetDescrObject must be
+ # bound to a class, but the actual w_type will be unavailable later on.
+ # Solution: use the w_userdata argument when assigning a PyTypeObject's
+ # tp_dict slot to pass a w_type in, and force creation of the pair here
+ if not space.is_w(w_userdata, space.gettypefor(GetSetProperty)):
+ # do not do this for type dict of GetSetProperty, that would recurse
+ w_vals = space.call_method(space.w_dict, "values", w_obj)
+ vals = space.listview(w_vals)
+ for w_v in vals:
+ if isinstance(w_v, GetSetProperty):
+ pyobj = as_pyobj(space, w_v, w_userdata)
+ # refcnt will be REFCNT_FROM_PYPY, no need to inc or dec
+
+def dict_realize(space, py_obj):
+ """
+ Creates the dict in the interpreter
+ """
+ w_obj = space.newdict()
+ track_reference(space, py_obj, w_obj)
+
+ at cpython_api([PyObject], lltype.Void, header=None)
+def dict_dealloc(space, py_obj):
+ py_dict = rffi.cast(PyDictObject, py_obj)
+ decref(space, py_dict.c_ob_keys)
+ py_dict.c_ob_keys = lltype.nullptr(PyObject.TO)
+ _dealloc(space, py_obj)
@cpython_api([], PyObject)
def PyDict_New(space):
@@ -181,9 +236,9 @@
}
The dictionary p should not be mutated during iteration. It is safe
- (since Python 2.1) to modify the values of the keys as you iterate over the
- dictionary, but only so long as the set of keys does not change. For
- example:
+ (since Python 2.1) to modify the values but not the keys as you iterate
+ over the dictionary, the keys must not change.
+ For example:
PyObject *key, *value;
Py_ssize_t pos = 0;
@@ -199,34 +254,32 @@
}
Py_DECREF(o);
}"""
+
if w_dict is None:
return 0
- # XXX XXX PyDict_Next is not efficient. Storing an iterator would probably
- # work, but we can't work out how to not leak it if iteration does
- # not complete. Alternatively, we could add some RPython-only
- # dict-iterator method to move forward by N steps.
-
- w_dict.ensure_object_strategy() # make sure both keys and values can
- # be borrwed
- try:
- w_iter = space.call_method(space.w_dict, "iteritems", w_dict)
- pos = ppos[0]
- while pos:
- space.call_method(w_iter, "next")
- pos -= 1
-
- w_item = space.call_method(w_iter, "next")
- w_key, w_value = space.fixedview(w_item, 2)
- if pkey:
- pkey[0] = as_pyobj(space, w_key)
- if pvalue:
- pvalue[0] = as_pyobj(space, w_value)
- ppos[0] += 1
- except OperationError as e:
- if not e.match(space, space.w_StopIteration):
- raise
+ pos = ppos[0]
+ py_obj = as_pyobj(space, w_dict)
+ py_dict = rffi.cast(PyDictObject, py_obj)
+ if pos == 0:
+ # Store the current keys in the PyDictObject.
+ decref(space, py_dict.c_ob_keys)
+ w_keys = space.call_method(space.w_dict, "keys", w_dict)
+ py_dict.c_ob_keys = create_ref(space, w_keys)
+ Py_IncRef(space, py_dict.c_ob_keys)
+ else:
+ w_keys = from_ref(space, py_dict.c_ob_keys)
+ ppos[0] += 1
+ if pos >= space.len_w(w_keys):
+ decref(space, py_dict.c_ob_keys)
+ py_dict.c_ob_keys = lltype.nullptr(PyObject.TO)
return 0
+ w_key = space.listview(w_keys)[pos]
+ w_value = space.getitem(w_dict, w_key)
+ if pkey:
+ pkey[0] = as_pyobj(space, w_key)
+ if pvalue:
+ pvalue[0] = as_pyobj(space, w_value)
return 1
@specialize.memo()
diff --git a/pypy/module/cpyext/floatobject.py b/pypy/module/cpyext/floatobject.py
--- a/pypy/module/cpyext/floatobject.py
+++ b/pypy/module/cpyext/floatobject.py
@@ -22,7 +22,7 @@
attach=float_attach,
realize=float_realize)
-def float_attach(space, py_obj, w_obj):
+def float_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyFloatObject with the given float object. The
value must not be modified.
diff --git a/pypy/module/cpyext/frameobject.py b/pypy/module/cpyext/frameobject.py
--- a/pypy/module/cpyext/frameobject.py
+++ b/pypy/module/cpyext/frameobject.py
@@ -30,7 +30,7 @@
dealloc=frame_dealloc,
realize=frame_realize)
-def frame_attach(space, py_obj, w_obj):
+def frame_attach(space, py_obj, w_obj, w_userdata=None):
"Fills a newly allocated PyFrameObject with a frame object"
frame = space.interp_w(PyFrame, w_obj)
py_frame = rffi.cast(PyFrameObject, py_obj)
diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py
--- a/pypy/module/cpyext/funcobject.py
+++ b/pypy/module/cpyext/funcobject.py
@@ -51,7 +51,7 @@
PyMethod_Check, PyMethod_CheckExact = build_type_checkers("Method", Method)
PyCode_Check, PyCode_CheckExact = build_type_checkers("Code", PyCode)
-def function_attach(space, py_obj, w_obj):
+def function_attach(space, py_obj, w_obj, w_userdata=None):
py_func = rffi.cast(PyFunctionObject, py_obj)
assert isinstance(w_obj, Function)
py_func.c_func_name = make_ref(space, space.wrap(w_obj.name))
@@ -63,7 +63,7 @@
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
-def code_attach(space, py_obj, w_obj):
+def code_attach(space, py_obj, w_obj, w_userdata=None):
py_code = rffi.cast(PyCodeObject, py_obj)
assert isinstance(w_obj, PyCode)
py_code.c_co_name = make_ref(space, space.wrap(w_obj.co_name))
diff --git a/pypy/module/cpyext/include/dictobject.h b/pypy/module/cpyext/include/dictobject.h
--- a/pypy/module/cpyext/include/dictobject.h
+++ b/pypy/module/cpyext/include/dictobject.h
@@ -7,6 +7,10 @@
extern "C" {
#endif
+typedef struct {
+ PyObject_HEAD
+ PyObject *ob_keys; /* a private place to put keys during PyDict_Next */
+} PyDictObject;
#ifdef __cplusplus
}
diff --git a/pypy/module/cpyext/intobject.py b/pypy/module/cpyext/intobject.py
--- a/pypy/module/cpyext/intobject.py
+++ b/pypy/module/cpyext/intobject.py
@@ -24,7 +24,7 @@
attach=int_attach,
realize=int_realize)
-def int_attach(space, py_obj, w_obj):
+def int_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyIntObject with the given int object. The
value must not be modified.
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -44,7 +44,7 @@
attach=cfunction_attach,
dealloc=cfunction_dealloc)
-def cfunction_attach(space, py_obj, w_obj):
+def cfunction_attach(space, py_obj, w_obj, w_userdata=None):
assert isinstance(w_obj, W_PyCFunctionObject)
py_func = rffi.cast(PyCFunctionObject, py_obj)
py_func.c_m_ml = w_obj.ml
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -61,7 +61,7 @@
pyobj.c_ob_type = pytype
return pyobj
- def attach(self, space, pyobj, w_obj):
+ def attach(self, space, pyobj, w_obj, w_userdata=None):
pass
def realize(self, space, obj):
@@ -111,8 +111,8 @@
return tp_dealloc.api_func
if tp_attach:
- def attach(self, space, pyobj, w_obj):
- tp_attach(space, pyobj, w_obj)
+ def attach(self, space, pyobj, w_obj, w_userdata=None):
+ tp_attach(space, pyobj, w_obj, w_userdata)
if tp_realize:
def realize(self, space, ref):
@@ -152,7 +152,7 @@
class InvalidPointerException(Exception):
pass
-def create_ref(space, w_obj):
+def create_ref(space, w_obj, w_userdata=None):
"""
Allocates a PyObject, and fills its fields with info from the given
interpreter object.
@@ -173,7 +173,7 @@
assert py_obj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY
py_obj.c_ob_refcnt -= 1
#
- typedescr.attach(space, py_obj, w_obj)
+ typedescr.attach(space, py_obj, w_obj, w_userdata)
return py_obj
def track_reference(space, py_obj, w_obj):
@@ -228,7 +228,7 @@
assert isinstance(w_type, W_TypeObject)
return get_typedescr(w_type.layout.typedef).realize(space, ref)
-def as_pyobj(space, w_obj):
+def as_pyobj(space, w_obj, w_userdata=None):
"""
Returns a 'PyObject *' representing the given intepreter object.
This doesn't give a new reference, but the returned 'PyObject *'
@@ -240,7 +240,7 @@
assert not is_pyobj(w_obj)
py_obj = rawrefcount.from_obj(PyObject, w_obj)
if not py_obj:
- py_obj = create_ref(space, w_obj)
+ py_obj = create_ref(space, w_obj, w_userdata)
return py_obj
else:
return lltype.nullptr(PyObject.TO)
@@ -269,14 +269,14 @@
return hop.inputconst(lltype.Bool, hop.s_result.const)
@specialize.ll()
-def make_ref(space, obj):
+def make_ref(space, obj, w_userdata=None):
"""Increment the reference counter of the PyObject and return it.
Can be called with either a PyObject or a W_Root.
"""
if is_pyobj(obj):
pyobj = rffi.cast(PyObject, obj)
else:
- pyobj = as_pyobj(space, obj)
+ pyobj = as_pyobj(space, obj, w_userdata)
if pyobj:
assert pyobj.c_ob_refcnt > 0
pyobj.c_ob_refcnt += 1
diff --git a/pypy/module/cpyext/pytraceback.py b/pypy/module/cpyext/pytraceback.py
--- a/pypy/module/cpyext/pytraceback.py
+++ b/pypy/module/cpyext/pytraceback.py
@@ -28,7 +28,7 @@
dealloc=traceback_dealloc)
-def traceback_attach(space, py_obj, w_obj):
+def traceback_attach(space, py_obj, w_obj, w_userdata=None):
py_traceback = rffi.cast(PyTracebackObject, py_obj)
traceback = space.interp_w(PyTraceback, w_obj)
if traceback.next is None:
diff --git a/pypy/module/cpyext/sliceobject.py b/pypy/module/cpyext/sliceobject.py
--- a/pypy/module/cpyext/sliceobject.py
+++ b/pypy/module/cpyext/sliceobject.py
@@ -25,7 +25,7 @@
attach=slice_attach,
dealloc=slice_dealloc)
-def slice_attach(space, py_obj, w_obj):
+def slice_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PySliceObject with the given slice object. The
fields must not be modified.
diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py
--- a/pypy/module/cpyext/test/test_dictobject.py
+++ b/pypy/module/cpyext/test/test_dictobject.py
@@ -1,7 +1,7 @@
import py
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.test.test_api import BaseApiTest
-from pypy.module.cpyext.api import Py_ssize_tP, PyObjectP
+from pypy.module.cpyext.api import Py_ssize_tP, PyObjectP, PyTypeObjectPtr
from pypy.module.cpyext.pyobject import make_ref, from_ref
from pypy.interpreter.error import OperationError
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
@@ -181,6 +181,27 @@
raises(OperationError, space.call_method, w_proxy, 'clear')
assert api.PyDictProxy_Check(w_proxy)
+ def test_typedict1(self, space, api):
+ py_type = make_ref(space, space.w_int)
+ py_dict = rffi.cast(PyTypeObjectPtr, py_type).c_tp_dict
+ ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw')
+
+ ppos[0] = 0
+ pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
+ pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
+ try:
+ w_copy = space.newdict()
+ while api.PyDict_Next(py_dict, ppos, pkey, pvalue):
+ w_key = from_ref(space, pkey[0])
+ w_value = from_ref(space, pvalue[0])
+ space.setitem(w_copy, w_key, w_value)
+ finally:
+ lltype.free(ppos, flavor='raw')
+ lltype.free(pkey, flavor='raw')
+ lltype.free(pvalue, flavor='raw')
+ api.Py_DecRef(py_type) # release borrowed references
+ # do something with w_copy ?
+
class AppTestDictObject(AppTestCpythonExtensionBase):
def test_dictproxytype(self):
module = self.import_extension('foo', [
@@ -225,3 +246,16 @@
d = {"a": 1}
raises(AttributeError, module.update, d, [("c", 2)])
+ def test_typedict2(self):
+ module = self.import_extension('foo', [
+ ("get_type_dict", "METH_O",
+ '''
+ PyObject* value = args->ob_type->tp_dict;
+ if (value == NULL) value = Py_None;
+ Py_INCREF(value);
+ return value;
+ '''),
+ ])
+ d = module.get_type_dict(1)
+ assert d['real'].__get__(1, 1) == 1
+
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -63,7 +63,7 @@
p[i] = lltype.nullptr(PyObject.TO)
return py_obj
-def tuple_attach(space, py_obj, w_obj):
+def tuple_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyTupleObject with the given tuple object. The
buffer must not be modified.
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -32,7 +32,7 @@
from pypy.module.cpyext.state import State
from pypy.module.cpyext.structmember import PyMember_GetOne, PyMember_SetOne
from pypy.module.cpyext.typeobjectdefs import (
- PyGetSetDef, PyMemberDef, newfunc,
+ PyGetSetDef, PyMemberDef, newfunc, getter, setter,
PyNumberMethods, PyMappingMethods, PySequenceMethods, PyBufferProcs)
from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
@@ -61,6 +61,7 @@
self.w_type = w_type
doc = set = get = None
if doc:
+ # XXX dead code?
doc = rffi.charp2str(getset.c_doc)
if getset.c_get:
get = GettersAndSetters.getter.im_func
@@ -73,6 +74,21 @@
def PyDescr_NewGetSet(space, getset, w_type):
return space.wrap(W_GetSetPropertyEx(getset, w_type))
+def make_GetSet(space, getsetprop):
+ py_getsetdef = lltype.malloc(PyGetSetDef, flavor='raw')
+ doc = getsetprop.doc
+ if doc:
+ py_getsetdef.c_doc = rffi.str2charp(doc)
+ else:
+ py_getsetdef.c_doc = rffi.cast(rffi.CCHARP, 0)
+ py_getsetdef.c_name = rffi.str2charp(getsetprop.getname(space))
+ # XXX FIXME - actually assign these !!!
+ py_getsetdef.c_get = rffi.cast(getter, 0)
+ py_getsetdef.c_set = rffi.cast(setter, 0)
+ py_getsetdef.c_closure = rffi.cast(rffi.VOIDP, 0)
+ return py_getsetdef
+
+
class W_MemberDescr(GetSetProperty):
name = 'member_descriptor'
def __init__(self, member, w_type):
@@ -158,7 +174,7 @@
realize=methoddescr_realize,
)
-def memberdescr_attach(space, py_obj, w_obj):
+def memberdescr_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyMemberDescrObject with the given W_MemberDescr
object. The values must not be modified.
@@ -177,17 +193,21 @@
track_reference(space, obj, w_obj)
return w_obj
-def getsetdescr_attach(space, py_obj, w_obj):
+def getsetdescr_attach(space, py_obj, w_obj, w_userdata=None):
"""
Fills a newly allocated PyGetSetDescrObject with the given W_GetSetPropertyEx
object. The values must not be modified.
"""
py_getsetdescr = rffi.cast(PyGetSetDescrObject, py_obj)
+ if isinstance(w_obj, GetSetProperty):
+ py_getsetdef = make_GetSet(space, w_obj)
+ assert space.isinstance_w(w_userdata, space.w_type)
+ w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata)
# XXX assign to d_dname, d_type?
assert isinstance(w_obj, W_GetSetPropertyEx)
py_getsetdescr.c_d_getset = w_obj.getset
-def methoddescr_attach(space, py_obj, w_obj):
+def methoddescr_attach(space, py_obj, w_obj, w_userdata=None):
py_methoddescr = rffi.cast(PyMethodDescrObject, py_obj)
# XXX assign to d_dname, d_type?
assert isinstance(w_obj, W_PyCFunctionObject)
@@ -663,7 +683,7 @@
return rffi.cast(PyObject, heaptype)
-def type_attach(space, py_obj, w_type):
+def type_attach(space, py_obj, w_type, w_userdata=None):
"""
Fills a newly allocated PyTypeObject from an existing type.
"""
@@ -890,7 +910,9 @@
if w_obj.is_cpytype():
Py_DecRef(space, pto.c_tp_dict)
w_dict = w_obj.getdict(space)
- pto.c_tp_dict = make_ref(space, w_dict)
+ # pass in the w_obj to convert any values that are
+ # unbound GetSetProperty into bound PyGetSetDescrObject
+ pto.c_tp_dict = make_ref(space, w_dict, w_obj)
@cpython_api([PyTypeObjectPtr, PyTypeObjectPtr], rffi.INT_real, error=CANNOT_FAIL)
def PyType_IsSubtype(space, a, b):
diff --git a/pypy/module/cpyext/unicodeobject.py b/pypy/module/cpyext/unicodeobject.py
--- a/pypy/module/cpyext/unicodeobject.py
+++ b/pypy/module/cpyext/unicodeobject.py
@@ -62,7 +62,7 @@
py_uni.c_defenc = lltype.nullptr(PyObject.TO)
return py_uni
-def unicode_attach(space, py_obj, w_obj):
+def unicode_attach(space, py_obj, w_obj, w_userdata=None):
"Fills a newly allocated PyUnicodeObject with a unicode string"
py_unicode = rffi.cast(PyUnicodeObject, py_obj)
py_unicode.c_length = len(space.unicode_w(w_obj))
More information about the pypy-commit
mailing list