PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/cpyext/sequence.py

https://bitbucket.org/pypy/pypy/
Python | 392 lines | 245 code | 65 blank | 82 comment | 23 complexity | 0f14c8b69e1c0c0e9e7cd7907c295071 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from rpython.rlib import rerased, jit
  2. from pypy.interpreter.error import OperationError, oefmt
  3. from pypy.objspace.std.listobject import (
  4. ListStrategy, UNROLL_CUTOFF, W_ListObject, ObjectListStrategy)
  5. from pypy.module.cpyext.api import (
  6. cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t, PyObject, PyObjectP)
  7. from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
  8. from rpython.rtyper.lltypesystem import rffi, lltype
  9. from pypy.objspace.std import tupleobject
  10. from pypy.module.cpyext.tupleobject import PyTuple_Check, PyTuple_SetItem
  11. from pypy.module.cpyext.pyobject import decref
  12. from pypy.module.cpyext.dictobject import PyDict_Check
  13. @cpython_api([PyObject, Py_ssize_t], PyObject)
  14. def PySequence_Repeat(space, w_obj, count):
  15. """Return the result of repeating sequence object o count times, or NULL on
  16. failure. This is the equivalent of the Python expression o * count.
  17. """
  18. return space.mul(w_obj, space.wrap(count))
  19. @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
  20. def PySequence_Check(space, w_obj):
  21. """Return 1 if the object provides sequence protocol, and 0 otherwise.
  22. This function always succeeds."""
  23. return int(space.issequence_w(w_obj))
  24. @cpython_api([PyObject], Py_ssize_t, error=-1)
  25. def PySequence_Size(space, w_obj):
  26. """
  27. Returns the number of objects in sequence o on success, and -1 on failure.
  28. For objects that do not provide sequence protocol, this is equivalent to the
  29. Python expression len(o)."""
  30. return space.len_w(w_obj)
  31. @cpython_api([PyObject], Py_ssize_t, error=-1)
  32. def PySequence_Length(space, w_obj):
  33. return space.len_w(w_obj)
  34. @cpython_api([PyObject, CONST_STRING], PyObject)
  35. def PySequence_Fast(space, w_obj, m):
  36. """Returns the sequence o as a tuple, unless it is already a tuple or list, in
  37. which case o is returned. Use PySequence_Fast_GET_ITEM() to access the
  38. members of the result. Returns NULL on failure. If the object is not a
  39. sequence, raises TypeError with m as the message text."""
  40. if isinstance(w_obj, W_ListObject):
  41. # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM
  42. w_obj.convert_to_cpy_strategy(space)
  43. return w_obj
  44. try:
  45. return W_ListObject.newlist_cpyext(space, space.listview(w_obj))
  46. except OperationError:
  47. raise OperationError(space.w_TypeError, space.wrap(rffi.charp2str(m)))
  48. @cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_borrowed=True)
  49. def PySequence_Fast_GET_ITEM(space, w_obj, index):
  50. """Return the ith element of o, assuming that o was returned by
  51. PySequence_Fast(), o is not NULL, and that i is within bounds.
  52. """
  53. if isinstance(w_obj, W_ListObject):
  54. return w_obj.getitem(index)
  55. elif isinstance(w_obj, tupleobject.W_TupleObject):
  56. return w_obj.wrappeditems[index]
  57. raise oefmt(space.w_TypeError,
  58. "PySequence_Fast_GET_ITEM called but object is not a list or "
  59. "sequence")
  60. @cpython_api([rffi.VOIDP], Py_ssize_t, error=CANNOT_FAIL)
  61. def PySequence_Fast_GET_SIZE(space, w_obj):
  62. """Returns the length of o, assuming that o was returned by
  63. PySequence_Fast() and that o is not NULL. The size can also be
  64. gotten by calling PySequence_Size() on o, but
  65. PySequence_Fast_GET_SIZE() is faster because it can assume o is a list
  66. or tuple."""
  67. if isinstance(w_obj, W_ListObject):
  68. return w_obj.length()
  69. elif isinstance(w_obj, tupleobject.W_TupleObject):
  70. return len(w_obj.wrappeditems)
  71. raise oefmt(space.w_TypeError,
  72. "PySequence_Fast_GET_SIZE called but object is not a list or "
  73. "sequence")
  74. @cpython_api([rffi.VOIDP], PyObjectP)
  75. def PySequence_Fast_ITEMS(space, w_obj):
  76. """Return the underlying array of PyObject pointers. Assumes that o was returned
  77. by PySequence_Fast() and o is not NULL.
  78. Note, if a list gets resized, the reallocation may relocate the items array.
  79. So, only use the underlying array pointer in contexts where the sequence
  80. cannot change.
  81. """
  82. if isinstance(w_obj, W_ListObject):
  83. cpy_strategy = space.fromcache(CPyListStrategy)
  84. if w_obj.strategy is cpy_strategy:
  85. return w_obj.get_raw_items() # asserts it's a cpyext strategy
  86. raise oefmt(space.w_TypeError,
  87. "PySequence_Fast_ITEMS called but object is not the result of "
  88. "PySequence_Fast")
  89. @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], PyObject)
  90. def PySequence_GetSlice(space, w_obj, start, end):
  91. """Return the slice of sequence object o between i1 and i2, or NULL on
  92. failure. This is the equivalent of the Python expression o[i1:i2]."""
  93. return space.getslice(w_obj, space.wrap(start), space.wrap(end))
  94. @cpython_api([PyObject, Py_ssize_t, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
  95. def PySequence_SetSlice(space, w_obj, start, end, w_value):
  96. """Assign the sequence object v to the slice in sequence object o from i1 to
  97. i2. This is the equivalent of the Python statement o[i1:i2] = v."""
  98. space.setslice(w_obj, space.wrap(start), space.wrap(end), w_value)
  99. return 0
  100. @cpython_api([PyObject, Py_ssize_t, Py_ssize_t], rffi.INT_real, error=-1)
  101. def PySequence_DelSlice(space, w_obj, start, end):
  102. """Delete the slice in sequence object o from i1 to i2. Returns -1 on
  103. failure. This is the equivalent of the Python statement del o[i1:i2]."""
  104. space.delslice(w_obj, space.wrap(start), space.wrap(end))
  105. return 0
  106. @cpython_api([rffi.VOIDP, Py_ssize_t], PyObject)
  107. def PySequence_ITEM(space, w_obj, i):
  108. """Return the ith element of o or NULL on failure. Macro form of
  109. PySequence_GetItem() but without checking that
  110. PySequence_Check(o)() is true and without adjustment for negative
  111. indices.
  112. This function used an int type for i. This might require
  113. changes in your code for properly supporting 64-bit systems."""
  114. return space.getitem(w_obj, space.wrap(i))
  115. @cpython_api([PyObject, Py_ssize_t], PyObject)
  116. def PySequence_GetItem(space, w_obj, i):
  117. """Return the ith element of o, or NULL on failure. This is the equivalent of
  118. the Python expression o[i]."""
  119. return PySequence_ITEM(space, w_obj, i)
  120. @cpython_api([PyObject], PyObject)
  121. def PySequence_List(space, w_obj):
  122. """Return a list object with the same contents as the arbitrary sequence o. The
  123. returned list is guaranteed to be new."""
  124. return space.call_function(space.w_list, w_obj)
  125. @cpython_api([PyObject], PyObject)
  126. def PySequence_Tuple(space, w_obj):
  127. """Return a tuple object with the same contents as the arbitrary sequence o or
  128. NULL on failure. If o is a tuple, a new reference will be returned,
  129. otherwise a tuple will be constructed with the appropriate contents. This is
  130. equivalent to the Python expression tuple(o)."""
  131. return space.call_function(space.w_tuple, w_obj)
  132. @cpython_api([PyObject, PyObject], PyObject)
  133. def PySequence_Concat(space, w_o1, w_o2):
  134. """Return the concatenation of o1 and o2 on success, and NULL on failure.
  135. This is the equivalent of the Python expression o1 + o2."""
  136. return space.add(w_o1, w_o2)
  137. @cpython_api([PyObject, PyObject], PyObject)
  138. def PySequence_InPlaceConcat(space, w_o1, w_o2):
  139. """Return the concatenation of o1 and o2 on success, and NULL on failure.
  140. The operation is done in-place when o1 supports it. This is the equivalent
  141. of the Python expression o1 += o2."""
  142. return space.inplace_add(w_o1, w_o2)
  143. @cpython_api([PyObject, Py_ssize_t], PyObject)
  144. def PySequence_InPlaceRepeat(space, w_o, count):
  145. """Return the result of repeating sequence object o count times, or NULL on
  146. failure. The operation is done in-place when o supports it. This is the
  147. equivalent of the Python expression o *= count.
  148. This function used an int type for count. This might require
  149. changes in your code for properly supporting 64-bit systems."""
  150. return space.inplace_mul(w_o, space.wrap(count))
  151. @cpython_api([PyObject, PyObject], rffi.INT_real, error=-1)
  152. def PySequence_Contains(space, w_obj, w_value):
  153. """Determine if o contains value. If an item in o is equal to value,
  154. return 1, otherwise return 0. On error, return -1. This is
  155. equivalent to the Python expression value in o."""
  156. w_res = space.contains(w_obj, w_value)
  157. return space.int_w(w_res)
  158. @cpython_api([PyObject], PyObject)
  159. def PySeqIter_New(space, w_seq):
  160. """Return an iterator that works with a general sequence object, seq. The
  161. iteration ends when the sequence raises IndexError for the subscripting
  162. operation.
  163. """
  164. # XXX check for bad internal call
  165. return space.newseqiter(w_seq)
  166. @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
  167. def PySequence_SetItem(space, w_o, i, w_v):
  168. """Assign object v to the ith element of o. Returns -1 on failure. This
  169. is the equivalent of the Python statement o[i] = v. This function does
  170. not steal a reference to v."""
  171. if PyDict_Check(space, w_o) or not PySequence_Check(space, w_o):
  172. raise oefmt(space.w_TypeError,
  173. "'%T' object does not support item assignment", w_o)
  174. space.setitem(w_o, space.wrap(i), w_v)
  175. return 0
  176. @cpython_api([PyObject, Py_ssize_t], rffi.INT_real, error=-1)
  177. def PySequence_DelItem(space, w_o, i):
  178. """Delete the ith element of object o. Returns -1 on failure. This is the
  179. equivalent of the Python statement del o[i]."""
  180. space.delitem(w_o, space.wrap(i))
  181. return 0
  182. @cpython_api([PyObject, PyObject], Py_ssize_t, error=-1)
  183. def PySequence_Index(space, w_seq, w_obj):
  184. """Return the first index i for which o[i] == value. On error, return
  185. -1. This is equivalent to the Python expression o.index(value).
  186. This function returned an int type. This might require changes
  187. in your code for properly supporting 64-bit systems."""
  188. w_iter = space.iter(w_seq)
  189. idx = 0
  190. while True:
  191. try:
  192. w_next = space.next(w_iter)
  193. except OperationError as e:
  194. if e.match(space, space.w_StopIteration):
  195. break
  196. raise
  197. if space.is_true(space.eq(w_next, w_obj)):
  198. return idx
  199. idx += 1
  200. raise oefmt(space.w_ValueError, "sequence.index(x): x not in sequence")
  201. class CPyListStrategy(ListStrategy):
  202. erase, unerase = rerased.new_erasing_pair("empty")
  203. erase = staticmethod(erase)
  204. unerase = staticmethod(unerase)
  205. def _check_index(self, index, length):
  206. if index < 0:
  207. index = length + index
  208. if index < 0 or index >= length:
  209. raise IndexError
  210. return index
  211. def getitem(self, w_list, index):
  212. storage = self.unerase(w_list.lstorage)
  213. index = self._check_index(index, storage._length)
  214. return from_ref(w_list.space, storage._elems[index])
  215. def setitem(self, w_list, index, w_obj):
  216. storage = self.unerase(w_list.lstorage)
  217. index = self._check_index(index, storage._length)
  218. decref(w_list.space, storage._elems[index])
  219. storage._elems[index] = make_ref(w_list.space, w_obj)
  220. def length(self, w_list):
  221. storage = self.unerase(w_list.lstorage)
  222. return storage._length
  223. def get_raw_items(self, w_list):
  224. storage = self.unerase(w_list.lstorage)
  225. return storage._elems
  226. def getslice(self, w_list, start, stop, step, length):
  227. w_list.switch_to_object_strategy()
  228. return w_list.strategy.getslice(w_list, start, stop, step, length)
  229. def getitems(self, w_list):
  230. # called when switching list strategy, so convert storage
  231. storage = self.unerase(w_list.lstorage)
  232. retval = [None] * storage._length
  233. for i in range(storage._length):
  234. retval[i] = from_ref(w_list.space, storage._elems[i])
  235. return retval
  236. @jit.unroll_safe
  237. def getitems_unroll(self, w_list):
  238. storage = self.unerase(w_list.lstorage)
  239. retval = [None] * storage._length
  240. for i in range(storage._length):
  241. retval[i] = from_ref(w_list.space, storage._elems[i])
  242. return retval
  243. @jit.look_inside_iff(lambda self, w_list:
  244. jit.loop_unrolling_heuristic(w_list, w_list.length(),
  245. UNROLL_CUTOFF))
  246. def getitems_fixedsize(self, w_list):
  247. return self.getitems_unroll(w_list)
  248. #------------------------------------------
  249. # all these methods fail or switch strategy and then call ListObjectStrategy's method
  250. def setslice(self, w_list, start, stop, step, length):
  251. w_list.switch_to_object_strategy()
  252. w_list.strategy.setslice(w_list, start, stop, step, length)
  253. def get_sizehint(self):
  254. return -1
  255. def init_from_list_w(self, w_list, list_w):
  256. raise NotImplementedError
  257. def clone(self, w_list):
  258. storage = w_list.lstorage # lstorage is tuple, no need to clone
  259. w_clone = W_ListObject.from_storage_and_strategy(self.space, storage,
  260. self)
  261. w_clone.switch_to_object_strategy()
  262. return w_clone
  263. def copy_into(self, w_list, w_other):
  264. w_list.switch_to_object_strategy()
  265. w_list.strategy.copy_into(w_list, w_other)
  266. def _resize_hint(self, w_list, hint):
  267. pass
  268. def find(self, w_list, w_item, start, stop):
  269. w_list.switch_to_object_strategy()
  270. return w_list.strategy.find(w_list, w_item, start, stop)
  271. def getitems_copy(self, w_list):
  272. w_list.switch_to_object_strategy()
  273. return w_list.strategy.getitems_copy(w_list)
  274. def getstorage_copy(self, w_list):
  275. raise NotImplementedError
  276. def append(self, w_list, w_item):
  277. w_list.switch_to_object_strategy()
  278. w_list.strategy.append(w_list, w_item)
  279. def inplace_mul(self, w_list, times):
  280. w_list.switch_to_object_strategy()
  281. w_list.strategy.inplace_mul(w_list, times)
  282. def deleteslice(self, w_list, start, step, slicelength):
  283. w_list.switch_to_object_strategy()
  284. w_list.strategy.deleteslice(w_list, start, step, slicelength)
  285. def pop(self, w_list, index):
  286. w_list.switch_to_object_strategy()
  287. w_list.strategy.pop(w_list, index)
  288. def pop_end(self, w_list):
  289. w_list.switch_to_object_strategy()
  290. w_list.strategy.pop_end(w_list)
  291. def insert(self, w_list, index, w_item):
  292. w_list.switch_to_object_strategy()
  293. w_list.strategy.insert(w_list, index, w_item)
  294. def extend(self, w_list, w_any):
  295. w_list.switch_to_object_strategy()
  296. w_list.strategy.extend(w_list, w_any)
  297. def _extend_from_list(self, w_list, w_other):
  298. w_list.switch_to_object_strategy()
  299. w_list.strategy._extend_from_list(w_list, w_other)
  300. def _extend_from_iterable(self, w_list, w_iterable):
  301. w_list.switch_to_object_strategy()
  302. w_list.strategy._extend_from_iterable(w_list, w_iterable)
  303. def reverse(self, w_list):
  304. w_list.switch_to_object_strategy()
  305. w_list.strategy.reverse(w_list)
  306. def sort(self, w_list, reverse):
  307. w_list.switch_to_object_strategy()
  308. w_list.descr_sort(w_list.space, reverse=reverse)
  309. def is_empty_strategy(self):
  310. return False
  311. PyObjectList = lltype.Ptr(lltype.Array(PyObject, hints={'nolength': True}))
  312. class CPyListStorage(object):
  313. def __init__(self, space, lst):
  314. self.space = space
  315. self._elems = lltype.malloc(PyObjectList.TO, len(lst), flavor='raw')
  316. self._length = len(lst)
  317. self._allocated = len(lst)
  318. for i, item in enumerate(lst):
  319. self._elems[i] = make_ref(space, lst[i])
  320. def __del__(self):
  321. for i in range(self._length):
  322. decref(self.space, self._elems[i])
  323. lltype.free(self._elems, flavor='raw')