PageRenderTime 88ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/objspace/std/sliceobject.py

https://bitbucket.org/pypy/pypy/
Python | 250 lines | 222 code | 17 blank | 11 comment | 49 complexity | d38ba40f80a6832f4a94794fb1e1bdeb MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """Slice object"""
  2. import sys
  3. from pypy.interpreter import gateway
  4. from pypy.interpreter.baseobjspace import W_Root
  5. from pypy.interpreter.error import OperationError, oefmt
  6. from pypy.interpreter.typedef import GetSetProperty, TypeDef
  7. from rpython.rlib.objectmodel import specialize
  8. from rpython.rlib import jit
  9. class W_SliceObject(W_Root):
  10. _immutable_fields_ = ['w_start', 'w_stop', 'w_step']
  11. def __init__(self, w_start, w_stop, w_step):
  12. assert w_start is not None
  13. assert w_stop is not None
  14. assert w_step is not None
  15. self.w_start = w_start
  16. self.w_stop = w_stop
  17. self.w_step = w_step
  18. def unwrap(w_slice, space):
  19. return slice(space.unwrap(w_slice.w_start), space.unwrap(w_slice.w_stop), space.unwrap(w_slice.w_step))
  20. def indices3(w_slice, space, length):
  21. if space.is_w(w_slice.w_step, space.w_None):
  22. step = 1
  23. else:
  24. step = _eval_slice_index(space, w_slice.w_step)
  25. if step == 0:
  26. raise oefmt(space.w_ValueError, "slice step cannot be zero")
  27. if space.is_w(w_slice.w_start, space.w_None):
  28. if step < 0:
  29. start = length - 1
  30. else:
  31. start = 0
  32. else:
  33. start = _eval_slice_index(space, w_slice.w_start)
  34. if start < 0:
  35. start += length
  36. if start < 0:
  37. if step < 0:
  38. start = -1
  39. else:
  40. start = 0
  41. elif start >= length:
  42. if step < 0:
  43. start = length - 1
  44. else:
  45. start = length
  46. if space.is_w(w_slice.w_stop, space.w_None):
  47. if step < 0:
  48. stop = -1
  49. else:
  50. stop = length
  51. else:
  52. stop = _eval_slice_index(space, w_slice.w_stop)
  53. if stop < 0:
  54. stop += length
  55. if stop < 0:
  56. if step < 0:
  57. stop = -1
  58. else:
  59. stop = 0
  60. elif stop >= length:
  61. if step < 0:
  62. stop = length - 1
  63. else:
  64. stop = length
  65. return start, stop, step
  66. def indices4(w_slice, space, length):
  67. start, stop, step = w_slice.indices3(space, length)
  68. if (step < 0 and stop >= start) or (step > 0 and start >= stop):
  69. slicelength = 0
  70. elif step < 0:
  71. slicelength = (stop - start + 1) / step + 1
  72. else:
  73. slicelength = (stop - start - 1) / step + 1
  74. return start, stop, step, slicelength
  75. def __repr__(self):
  76. return "<W_SliceObject(%r, %r, %r)>" % (
  77. self.w_start, self.w_stop, self.w_step)
  78. @staticmethod
  79. def descr__new__(space, w_slicetype, args_w):
  80. from pypy.objspace.std.sliceobject import W_SliceObject
  81. w_start = space.w_None
  82. w_stop = space.w_None
  83. w_step = space.w_None
  84. if len(args_w) == 1:
  85. w_stop, = args_w
  86. elif len(args_w) == 2:
  87. w_start, w_stop = args_w
  88. elif len(args_w) == 3:
  89. w_start, w_stop, w_step = args_w
  90. elif len(args_w) > 3:
  91. raise oefmt(space.w_TypeError, "slice() takes at most 3 arguments")
  92. else:
  93. raise oefmt(space.w_TypeError, "slice() takes at least 1 argument")
  94. w_obj = space.allocate_instance(W_SliceObject, w_slicetype)
  95. W_SliceObject.__init__(w_obj, w_start, w_stop, w_step)
  96. return w_obj
  97. def descr_repr(self, space):
  98. return space.wrap("slice(%s, %s, %s)" % (
  99. space.str_w(space.repr(self.w_start)),
  100. space.str_w(space.repr(self.w_stop)),
  101. space.str_w(space.repr(self.w_step))))
  102. def descr__reduce__(self, space):
  103. from pypy.objspace.std.sliceobject import W_SliceObject
  104. assert isinstance(self, W_SliceObject)
  105. return space.newtuple([
  106. space.type(self),
  107. space.newtuple([self.w_start, self.w_stop, self.w_step])])
  108. def descr_eq(self, space, w_other):
  109. # We need this because CPython considers that slice1 == slice1
  110. # is *always* True (e.g. even if slice1 was built with non-comparable
  111. # parameters
  112. if space.is_w(self, w_other):
  113. return space.w_True
  114. if not isinstance(w_other, W_SliceObject):
  115. return space.w_NotImplemented
  116. if space.eq_w(self.w_start, w_other.w_start) and \
  117. space.eq_w(self.w_stop, w_other.w_stop) and \
  118. space.eq_w(self.w_step, w_other.w_step):
  119. return space.w_True
  120. else:
  121. return space.w_False
  122. def descr_lt(self, space, w_other):
  123. if space.is_w(self, w_other):
  124. return space.w_False # see comments in descr_eq()
  125. if not isinstance(w_other, W_SliceObject):
  126. return space.w_NotImplemented
  127. if space.eq_w(self.w_start, w_other.w_start):
  128. if space.eq_w(self.w_stop, w_other.w_stop):
  129. return space.lt(self.w_step, w_other.w_step)
  130. else:
  131. return space.lt(self.w_stop, w_other.w_stop)
  132. else:
  133. return space.lt(self.w_start, w_other.w_start)
  134. def descr_indices(self, space, w_length):
  135. """S.indices(len) -> (start, stop, stride)
  136. Assuming a sequence of length len, calculate the start and stop
  137. indices, and the stride length of the extended slice described by
  138. S. Out of bounds indices are clipped in a manner consistent with the
  139. handling of normal slices.
  140. """
  141. length = space.getindex_w(w_length, space.w_OverflowError)
  142. start, stop, step = self.indices3(space, length)
  143. return space.newtuple([space.wrap(start), space.wrap(stop),
  144. space.wrap(step)])
  145. def slicewprop(name):
  146. def fget(space, w_obj):
  147. from pypy.objspace.std.sliceobject import W_SliceObject
  148. if not isinstance(w_obj, W_SliceObject):
  149. raise oefmt(space.w_TypeError, "descriptor is for 'slice'")
  150. return getattr(w_obj, name)
  151. return GetSetProperty(fget)
  152. W_SliceObject.typedef = TypeDef("slice",
  153. __doc__ = '''slice([start,] stop[, step])
  154. Create a slice object. This is used for extended slicing (e.g. a[0:10:2]).''',
  155. __new__ = gateway.interp2app(W_SliceObject.descr__new__),
  156. __repr__ = gateway.interp2app(W_SliceObject.descr_repr),
  157. __hash__ = None,
  158. __reduce__ = gateway.interp2app(W_SliceObject.descr__reduce__),
  159. __eq__ = gateway.interp2app(W_SliceObject.descr_eq),
  160. __lt__ = gateway.interp2app(W_SliceObject.descr_lt),
  161. start = slicewprop('w_start'),
  162. stop = slicewprop('w_stop'),
  163. step = slicewprop('w_step'),
  164. indices = gateway.interp2app(W_SliceObject.descr_indices),
  165. )
  166. W_SliceObject.typedef.acceptable_as_base_class = False
  167. # utility functions
  168. def _eval_slice_index(space, w_int):
  169. # note that it is the *callers* responsibility to check for w_None
  170. # otherwise you can get funny error messages
  171. try:
  172. return space.getindex_w(w_int, None) # clamp if long integer too large
  173. except OperationError as err:
  174. if not err.match(space, space.w_TypeError):
  175. raise
  176. raise oefmt(space.w_TypeError,
  177. "slice indices must be integers or None or have an "
  178. "__index__ method")
  179. def adapt_lower_bound(space, size, w_index):
  180. index = _eval_slice_index(space, w_index)
  181. if index < 0:
  182. index = index + size
  183. if index < 0:
  184. index = 0
  185. assert index >= 0
  186. return index
  187. def unwrap_start_stop(space, size, w_start, w_end):
  188. if space.is_none(w_start):
  189. start = 0
  190. else:
  191. start = adapt_lower_bound(space, size, w_start)
  192. if space.is_none(w_end):
  193. end = size
  194. assert end >= 0
  195. else:
  196. end = adapt_lower_bound(space, size, w_end)
  197. return start, end
  198. def normalize_simple_slice(space, length, w_start, w_stop):
  199. """Helper for the {get,set,del}slice implementations."""
  200. # this returns a pair (start, stop) which is usable for slicing
  201. # a sequence of the given length in the most friendly way, i.e.
  202. # guaranteeing that 0 <= start <= stop <= length.
  203. start = space.int_w(w_start)
  204. stop = space.int_w(w_stop)
  205. assert length >= 0
  206. if start < 0:
  207. start = 0
  208. # hack for the JIT, for slices with no end specified:
  209. # this avoids the two comparisons that follow
  210. if jit.isconstant(stop) and stop == sys.maxint:
  211. pass
  212. else:
  213. if stop < start:
  214. stop = start
  215. if stop <= length:
  216. return start, stop
  217. # here is the case where 'stop' is larger than the list
  218. stop = length
  219. if jit.isconstant(start) and start == 0:
  220. pass # no need to do the following check here
  221. elif start > stop:
  222. start = stop
  223. return start, stop