1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
|
from rpython.rlib import rerased, jit
from rpython.rlib.objectmodel import keepalive_until_here
from pypy.interpreter.error import OperationError, oefmt
from pypy.objspace.std.listobject import (
ListStrategy, UNROLL_CUTOFF, W_ListObject, ObjectListStrategy)
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t, PyObject, PyObjectP,
generic_cpy_call, PyVarObject, PyObject)
from pypy.module.cpyext.pyobject import make_ref, from_ref, as_pyobj, incref
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.objspace.std import tupleobject
from pypy.module.cpyext.tupleobject import PyTuple_Check, PyTuple_SetItem
from pypy.module.cpyext.pyobject import decref
from pypy.module.cpyext.dictobject import PyDict_Check
@cpython_api([PyObject, Py_ssize_t], PyObject)
def PySequence_Repeat(space, w_obj, count):
"""Return the result of repeating sequence object o count times, or NULL on
failure. This is the equivalent of the Python expression o * count.
"""
return space.mul(w_obj, space.newint(count))
@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
def PySequence_Check(space, w_obj):
"""Return 1 if the object provides sequence protocol, and 0 otherwise.
This function always succeeds."""
return int(space.issequence_w(w_obj))
@cpython_api([PyObject], Py_ssize_t, error=-1)
def PySequence_Size(space, w_obj):
"""
Returns the number of objects in sequence o on success, and -1 on failure.
For objects that do not provide sequence protocol, this is equivalent to the
Python expression len(o)."""
return space.len_w(w_obj)
@cpython_api([PyObject], Py_ssize_t, error=-1)
def PySequence_Length(space, w_obj):
return space.len_w(w_obj)
@cpython_api([PyObject, CONST_STRING], PyObject)
def PySequence_Fast(space, w_obj, m):
"""Returns the sequence o as a tuple, unless it is already a tuple or list, in
which case o is returned. Use PySequence_Fast_GET_ITEM() to access the
members of the result. Returns NULL on failure. If the object cannot be
converted to a sequence, and raises a TypeError, raise a new TypeError with
m as the message text. If the conversion otherwise, fails, reraise the
original exception"""
if isinstance(w_obj, tupleobject.W_AbstractTupleObject):
return w_obj # CCC avoid the double conversion that occurs here
if isinstance(w_obj, W_ListObject):
# note: we used to call w_obj.convert_to_cpy_strategy() here,
# but we really have to call it from PySequence_Fast_GET_ITEM()
# because some people never call PySequence_Fast() if they know
# the object is a list.
return w_obj
try:
return tupleobject.W_TupleObject(space.fixedview(w_obj))
except OperationError as e:
if e.match(space, space.w_TypeError):
raise OperationError(space.w_TypeError, space.newtext(rffi.charp2str(m)))
raise e
# CCC this should be written as a C macro, at least for the tuple case
@cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_is_ll=True)
def PySequence_Fast_GET_ITEM(space, py_obj, index):
"""Return the ith element of o, assuming that o was returned by
PySequence_Fast(), o is not NULL, and that i is within bounds.
"""
py_obj = rffi.cast(PyObject, py_obj)
if PyTuple_Check(space, py_obj):
from pypy.module.cpyext.tupleobject import PyTupleObject
py_tuple = rffi.cast(PyTupleObject, py_obj)
return py_tuple.c_ob_item[index]
else:
from pypy.module.cpyext.listobject import PyList_GET_ITEM
w_obj = from_ref(space, py_obj)
return PyList_GET_ITEM(space, w_obj, index)
@cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL)
def PySequence_Fast_GET_SIZE(space, py_obj):
"""Returns the length of o, assuming that o was returned by
PySequence_Fast() and that o is not NULL. The size can also be
gotten by calling PySequence_Size() on o, but
PySequence_Fast_GET_SIZE() is faster because it can assume o is a list
or tuple."""
py_obj = rffi.cast(PyObject, py_obj)
if PyTuple_Check(space, py_obj):
from pypy.module.cpyext.tupleobject import PyTupleObject
py_varobj = rffi.cast(PyVarObject, py_obj)
return py_varobj.c_ob_size
else:
from pypy.module.cpyext.listobject import PyList_GET_SIZE
w_obj = from_ref(space, py_obj)
return PyList_GET_SIZE(space, w_obj)
@cpython_api([rffi.VOIDP], PyObjectP)
def PySequence_Fast_ITEMS(space, py_obj):
"""Return the underlying array of PyObject pointers. Assumes that o was returned
by PySequence_Fast() and o is not NULL.
Note, if a list gets resized, the reallocation may relocate the items array.
So, only use the underlying array pointer in contexts where the sequence
cannot change.
"""
py_obj = rffi.cast(PyObject, py_obj)
if PyTuple_Check(space, py_obj):
from pypy.module.cpyext.tupleobject import PyTupleObject
py_tuple = rffi.cast(PyTupleObject, py_obj)
return rffi.cast(PyObjectP, py_tuple.c_ob_item)
else:
from pypy.module.cpyext.listobject import get_list_storage
w_obj = from_ref(space, py_obj)
assert isinstance(w_obj, W_ListObject)
storage = get_list_storage(space, w_obj)
return rffi.cast(PyObjectP, storage._elems)
@cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject)
def PySequence_GetSlice(space, w_obj, start, end):
"""Return the slice of sequence object o between i1 and i2, or NULL on
failure. This is the equivalent of the Python expression o[i1:i2]."""
return space.getslice(w_obj, space.newint(start), space.newint(end))
@cpython_api([PyObject, Py_ssize_t, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
def PySequence_SetSlice(space, w_obj, start, end, w_value):
"""Assign the sequence object v to the slice in sequence object o from i1 to
i2. This is the equivalent of the Python statement o[i1:i2] = v."""
space.setslice(w_obj, space.newint(start), space.newint(end), w_value)
return 0
@cpython_api([PyObject, Py_ssize_t, Py_ssize_t], rffi.INT_real, error=-1)
def PySequence_DelSlice(space, w_obj, start, end):
"""Delete the slice in sequence object o from i1 to i2. Returns -1 on
failure. This is the equivalent of the Python statement del o[i1:i2]."""
space.delslice(w_obj, space.newint(start), space.newint(end))
return 0
@cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_is_ll=True)
def PySequence_ITEM(space, w_obj, i):
"""Return the ith element of o or NULL on failure. Macro form of
PySequence_GetItem() but without checking that
PySequence_Check(o)() is true and without adjustment for negative
indices.
This function used an int type for i. This might require
changes in your code for properly supporting 64-bit systems."""
# XXX we should call Py*_GET_ITEM() instead of Py*_GetItem()
# from here, but we cannot because we are also called from
# PySequence_GetItem()
py_obj = as_pyobj(space, w_obj)
if isinstance(w_obj, tupleobject.W_TupleObject):
from pypy.module.cpyext.tupleobject import PyTuple_GetItem
py_res = PyTuple_GetItem(space, py_obj, i)
incref(space, py_res)
keepalive_until_here(w_obj)
return py_res
if isinstance(w_obj, W_ListObject):
from pypy.module.cpyext.listobject import PyList_GetItem
py_res = PyList_GetItem(space, py_obj, i)
incref(space, py_res)
keepalive_until_here(w_obj)
return py_res
as_sequence = py_obj.c_ob_type.c_tp_as_sequence
if as_sequence and as_sequence.c_sq_item:
ret = generic_cpy_call(space, as_sequence.c_sq_item, w_obj, i)
return make_ref(space, ret)
w_ret = space.getitem(w_obj, space.newint(i))
return make_ref(space, w_ret)
@cpython_api([PyObject, Py_ssize_t], PyObject, result_is_ll=True)
def PySequence_GetItem(space, w_obj, i):
"""Return the ith element of o, or NULL on failure. This is the equivalent of
the Python expression o[i]."""
if i < 0:
l = PySequence_Length(space, w_obj)
i += l
if i < 0:
# Prevent things like 'abc'[-4] from turning into 'abc'[-1]
# since this can end up calling space.getitem()
raise oefmt(space.w_IndexError,
"%T index out of range", w_obj)
return PySequence_ITEM(space, w_obj, i)
@cpython_api([PyObject], PyObject)
def PySequence_List(space, w_obj):
"""Return a list object with the same contents as the arbitrary sequence o. The
returned list is guaranteed to be new."""
return space.call_function(space.w_list, w_obj)
@cpython_api([PyObject], PyObject)
def PySequence_Tuple(space, w_obj):
"""Return a tuple object with the same contents as the arbitrary sequence o or
NULL on failure. If o is a tuple, a new reference will be returned,
otherwise a tuple will be constructed with the appropriate contents. This is
equivalent to the Python expression tuple(o)."""
return space.call_function(space.w_tuple, w_obj)
@cpython_api([PyObject, PyObject], PyObject)
def PySequence_Concat(space, w_o1, w_o2):
"""Return the concatenation of o1 and o2 on success, and NULL on failure.
This is the equivalent of the Python expression o1 + o2."""
return space.add(w_o1, w_o2)
@cpython_api([PyObject, PyObject], PyObject)
def PySequence_InPlaceConcat(space, w_o1, w_o2):
"""Return the concatenation of o1 and o2 on success, and NULL on failure.
The operation is done in-place when o1 supports it. This is the equivalent
of the Python expression o1 += o2."""
return space.inplace_add(w_o1, w_o2)
@cpython_api([PyObject, Py_ssize_t], PyObject)
def PySequence_InPlaceRepeat(space, w_o, count):
"""Return the result of repeating sequence object o count times, or NULL on
failure. The operation is done in-place when o supports it. This is the
equivalent of the Python expression o *= count.
This function used an int type for count. This might require
changes in your code for properly supporting 64-bit systems."""
return space.inplace_mul(w_o, space.newint(count))
@cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
def PySequence_Contains(space, w_obj, w_value):
"""Determine if o contains value. If an item in o is equal to value,
return 1, otherwise return 0. On error, return -1. This is
equivalent to the Python expression value in o."""
w_res = space.contains(w_obj, w_value)
return space.int_w(w_res)
@cpython_api([PyObject], PyObject)
def PySeqIter_New(space, w_seq):
"""Return an iterator that works with a general sequence object, seq. The
iteration ends when the sequence raises IndexError for the subscripting
operation.
"""
# XXX check for bad internal call
return space.newseqiter(w_seq)
@cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
def PySequence_SetItem(space, w_o, i, w_v):
"""Assign object v to the ith element of o. Returns -1 on failure. This
is the equivalent of the Python statement o[i] = v. This function does
not steal a reference to v."""
if PyDict_Check(space, w_o) or not PySequence_Check(space, w_o):
raise oefmt(space.w_TypeError,
"'%T' object does not support item assignment", w_o)
space.setitem(w_o, space.newint(i), w_v)
return 0
@cpython_api([PyObject, Py_ssize_t], rffi.INT_real, error=-1)
def PySequence_DelItem(space, w_o, i):
"""Delete the ith element of object o. Returns -1 on failure. This is the
equivalent of the Python statement del o[i]."""
space.delitem(w_o, space.newint(i))
return 0
@cpython_api([PyObject, PyObject], Py_ssize_t, error=-1)
def PySequence_Index(space, w_seq, w_obj):
"""Return the first index i for which o[i] == value. On error, return
-1. This is equivalent to the Python expression o.index(value).
This function returned an int type. This might require changes
in your code for properly supporting 64-bit systems."""
w_iter = space.iter(w_seq)
idx = 0
while True:
try:
w_next = space.next(w_iter)
except OperationError as e:
if e.match(space, space.w_StopIteration):
break
raise
if space.eq_w(w_next, w_obj):
return idx
idx += 1
raise oefmt(space.w_ValueError, "sequence.index(x): x not in sequence")
class CPyListStrategy(ListStrategy):
erase, unerase = rerased.new_erasing_pair("cpylist")
erase = staticmethod(erase)
unerase = staticmethod(unerase)
def _check_index(self, index, length):
if index < 0:
index = length + index
if index < 0 or index >= length:
raise IndexError
return index
def getitem(self, w_list, index):
storage = self.unerase(w_list.lstorage)
index = self._check_index(index, storage._length)
return from_ref(w_list.space, storage._elems[index])
def setitem(self, w_list, index, w_obj):
storage = self.unerase(w_list.lstorage)
index = self._check_index(index, storage._length)
py_old = storage._elems[index]
storage._elems[index] = make_ref(w_list.space, w_obj)
decref(w_list.space, py_old)
def length(self, w_list):
storage = self.unerase(w_list.lstorage)
return storage._length
def getslice(self, w_list, start, stop, step, length):
w_list.switch_to_object_strategy()
return w_list.strategy.getslice(w_list, start, stop, step, length)
def getitems(self, w_list):
# called when switching list strategy, so convert storage
storage = self.unerase(w_list.lstorage)
retval = [None] * storage._length
for i in range(storage._length):
retval[i] = from_ref(w_list.space, storage._elems[i])
return retval
@jit.unroll_safe
def getitems_unroll(self, w_list):
storage = self.unerase(w_list.lstorage)
retval = [None] * storage._length
for i in range(storage._length):
retval[i] = from_ref(w_list.space, storage._elems[i])
return retval
@jit.look_inside_iff(lambda self, w_list:
jit.loop_unrolling_heuristic(w_list, w_list.length(),
UNROLL_CUTOFF))
def getitems_fixedsize(self, w_list):
return self.getitems_unroll(w_list)
def copy_into(self, w_list, w_other):
w_other.strategy = self
w_other.lstorage = self.getstorage_copy(w_list)
def clone(self, w_list, sizehint=0):
storage = self.getstorage_copy(w_list)
w_clone = W_ListObject.from_storage_and_strategy(self.space, storage,
self)
return w_clone
def getitems_copy(self, w_list):
return self.getitems(w_list) # getitems copies anyway
def getstorage_copy(self, w_list):
lst = self.getitems(w_list)
return self.erase(CPyListStorage(w_list.space, lst))
#------------------------------------------
# all these methods fail or switch strategy and then call ListObjectStrategy's method
def setslice(self, w_list, start, stop, step, length):
w_list.switch_to_object_strategy()
w_list.strategy.setslice(w_list, start, stop, step, length)
def init_from_list_w(self, w_list, list_w):
raise NotImplementedError
def _resize_hint(self, w_list, hint):
pass
def find(self, w_list, w_item, start, stop):
w_list.switch_to_object_strategy()
return w_list.strategy.find(w_list, w_item, start, stop)
def append(self, w_list, w_item):
w_list.switch_to_object_strategy()
w_list.strategy.append(w_list, w_item)
def inplace_mul(self, w_list, times):
w_list.switch_to_object_strategy()
w_list.strategy.inplace_mul(w_list, times)
def deleteslice(self, w_list, start, step, slicelength):
w_list.switch_to_object_strategy()
w_list.strategy.deleteslice(w_list, start, step, slicelength)
def pop(self, w_list, index):
w_list.switch_to_object_strategy()
return w_list.strategy.pop(w_list, index)
def pop_end(self, w_list):
w_list.switch_to_object_strategy()
return w_list.strategy.pop_end(w_list)
def insert(self, w_list, index, w_item):
w_list.switch_to_object_strategy()
w_list.strategy.insert(w_list, index, w_item)
def extend(self, w_list, w_any):
w_list.switch_to_object_strategy()
w_list.strategy.extend(w_list, w_any)
def _extend_from_list(self, w_list, w_other):
w_list.switch_to_object_strategy()
w_list.strategy._extend_from_list(w_list, w_other)
def _extend_from_iterable(self, w_list, w_iterable):
w_list.switch_to_object_strategy()
w_list.strategy._extend_from_iterable(w_list, w_iterable)
def reverse(self, w_list):
w_list.switch_to_object_strategy()
w_list.strategy.reverse(w_list)
def sort(self, w_list, reverse):
w_list.switch_to_object_strategy()
w_list.descr_sort(w_list.space, reverse=reverse)
def is_empty_strategy(self):
return False
PyObjectList = lltype.Ptr(lltype.Array(PyObject, hints={'nolength': True}))
class CPyListStorage(object):
def __init__(self, space, lst):
self.space = space
self._elems = lltype.malloc(PyObjectList.TO, len(lst), flavor='raw')
self._length = len(lst)
self._allocated = len(lst)
for i, item in enumerate(lst):
self._elems[i] = make_ref(space, lst[i])
def __del__(self):
for i in range(self._length):
decref(self.space, self._elems[i])
lltype.free(self._elems, flavor='raw')
|