PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/module/__builtin__/functional.py

https://bitbucket.org/pypy/pypy/
Python | 561 lines | 442 code | 76 blank | 43 comment | 105 complexity | 2a2ba3f4f334fa85d8953935a2358a87 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """
  2. Interp-level definition of frequently used functionals.
  3. """
  4. import sys
  5. from pypy.interpreter.baseobjspace import W_Root
  6. from pypy.interpreter.error import OperationError, oefmt
  7. from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
  8. from pypy.interpreter.typedef import TypeDef
  9. from rpython.rlib import jit, rarithmetic
  10. from rpython.rlib.objectmodel import specialize
  11. from rpython.rlib.rarithmetic import r_uint, intmask
  12. from rpython.rlib.rbigint import rbigint
  13. def get_len_of_range(space, lo, hi, step):
  14. """
  15. Return number of items in range/xrange (lo, hi, step).
  16. Raise ValueError if step == 0 and OverflowError if the true value is too
  17. large to fit in a signed long.
  18. """
  19. # If lo >= hi, the range is empty.
  20. # Else if n values are in the range, the last one is
  21. # lo + (n-1)*step, which must be <= hi-1. Rearranging,
  22. # n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives
  23. # the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so
  24. # the RHS is non-negative and so truncation is the same as the
  25. # floor. Letting M be the largest positive long, the worst case
  26. # for the RHS numerator is hi=M, lo=-M-1, and then
  27. # hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough
  28. # precision to compute the RHS exactly.
  29. if step == 0:
  30. raise oefmt(space.w_ValueError, "step argument must not be zero")
  31. elif step < 0:
  32. lo, hi, step = hi, lo, -step
  33. if lo < hi:
  34. uhi = r_uint(hi)
  35. ulo = r_uint(lo)
  36. diff = uhi - ulo - 1
  37. n = intmask(diff // r_uint(step) + 1)
  38. if n < 0:
  39. raise oefmt(space.w_OverflowError, "result has too many items")
  40. else:
  41. n = 0
  42. return n
  43. @unwrap_spec(w_step=WrappedDefault(1))
  44. def range_int(space, w_x, w_y=None, w_step=None):
  45. """Return a list of integers in arithmetic position from start (defaults
  46. to zero) to stop - 1 by step (defaults to 1). Use a negative step to
  47. get a list in decending order."""
  48. if w_y is None:
  49. w_start = space.wrap(0)
  50. w_stop = w_x
  51. else:
  52. w_start = w_x
  53. w_stop = w_y
  54. if space.isinstance_w(w_stop, space.w_float):
  55. raise oefmt(space.w_TypeError,
  56. "range() integer end argument expected, got float.")
  57. if space.isinstance_w(w_start, space.w_float):
  58. raise oefmt(space.w_TypeError,
  59. "range() integer start argument expected, got float.")
  60. if space.isinstance_w(w_step, space.w_float):
  61. raise oefmt(space.w_TypeError,
  62. "range() integer step argument expected, got float.")
  63. w_start = space.int(w_start)
  64. w_stop = space.int(w_stop)
  65. w_step = space.int(w_step)
  66. try:
  67. start = space.int_w(w_start)
  68. stop = space.int_w(w_stop)
  69. step = space.int_w(w_step)
  70. except OperationError as e:
  71. if not e.match(space, space.w_OverflowError):
  72. raise
  73. return range_with_longs(space, w_start, w_stop, w_step)
  74. howmany = get_len_of_range(space, start, stop, step)
  75. if space.config.objspace.std.withliststrategies:
  76. return range_withspecialized_implementation(space, start,
  77. step, howmany)
  78. res_w = [None] * howmany
  79. v = start
  80. for idx in range(howmany):
  81. res_w[idx] = space.wrap(v)
  82. v += step
  83. return space.newlist(res_w)
  84. def range_withspecialized_implementation(space, start, step, length):
  85. assert space.config.objspace.std.withliststrategies
  86. from pypy.objspace.std.listobject import make_range_list
  87. return make_range_list(space, start, step, length)
  88. bigint_one = rbigint.fromint(1)
  89. def range_with_longs(space, w_start, w_stop, w_step):
  90. start = lo = space.bigint_w(w_start)
  91. hi = space.bigint_w(w_stop)
  92. step = st = space.bigint_w(w_step)
  93. if not step.tobool():
  94. raise oefmt(space.w_ValueError, "step argument must not be zero")
  95. elif step.sign < 0:
  96. lo, hi, st = hi, lo, st.neg()
  97. if lo.lt(hi):
  98. diff = hi.sub(lo).sub(bigint_one)
  99. n = diff.floordiv(st).add(bigint_one)
  100. try:
  101. howmany = n.toint()
  102. except OverflowError:
  103. raise oefmt(space.w_OverflowError, "result has too many items")
  104. else:
  105. howmany = 0
  106. res_w = [None] * howmany
  107. v = start
  108. for idx in range(howmany):
  109. res_w[idx] = space.newlong_from_rbigint(v)
  110. v = v.add(step)
  111. return space.newlist(res_w)
  112. min_jitdriver = jit.JitDriver(name='min',
  113. greens=['has_key', 'has_item', 'w_type'], reds='auto')
  114. max_jitdriver = jit.JitDriver(name='max',
  115. greens=['has_key', 'has_item', 'w_type'], reds='auto')
  116. def make_min_max(unroll):
  117. @specialize.arg(2)
  118. def min_max_impl(space, args, implementation_of):
  119. if implementation_of == "max":
  120. compare = space.gt
  121. jitdriver = max_jitdriver
  122. else:
  123. compare = space.lt
  124. jitdriver = min_jitdriver
  125. any_kwds = bool(args.keywords)
  126. args_w = args.arguments_w
  127. if len(args_w) > 1:
  128. if unroll and len(args_w) == 2 and not any_kwds:
  129. # a fast path for the common case, useful for interpreted
  130. # mode and to reduce the length of the jit trace
  131. w0, w1 = args_w
  132. if space.is_true(compare(w1, w0)):
  133. return w1
  134. else:
  135. return w0
  136. w_sequence = space.newtuple(args_w)
  137. elif len(args_w):
  138. w_sequence = args_w[0]
  139. else:
  140. raise oefmt(space.w_TypeError,
  141. "%s() expects at least one argument",
  142. implementation_of)
  143. w_key = None
  144. if any_kwds:
  145. kwds = args.keywords
  146. if kwds[0] == "key" and len(kwds) == 1:
  147. w_key = args.keywords_w[0]
  148. else:
  149. raise oefmt(space.w_TypeError,
  150. "%s() got unexpected keyword argument",
  151. implementation_of)
  152. w_iter = space.iter(w_sequence)
  153. w_type = space.type(w_iter)
  154. has_key = w_key is not None
  155. has_item = False
  156. w_max_item = None
  157. w_max_val = None
  158. while True:
  159. if not unroll:
  160. jitdriver.jit_merge_point(has_key=has_key, has_item=has_item, w_type=w_type)
  161. try:
  162. w_item = space.next(w_iter)
  163. except OperationError as e:
  164. if not e.match(space, space.w_StopIteration):
  165. raise
  166. break
  167. if has_key:
  168. w_compare_with = space.call_function(w_key, w_item)
  169. else:
  170. w_compare_with = w_item
  171. if not has_item or \
  172. space.is_true(compare(w_compare_with, w_max_val)):
  173. has_item = True
  174. w_max_item = w_item
  175. w_max_val = w_compare_with
  176. if w_max_item is None:
  177. raise oefmt(space.w_ValueError, "arg is an empty sequence")
  178. return w_max_item
  179. if unroll:
  180. min_max_impl = jit.unroll_safe(min_max_impl)
  181. return min_max_impl
  182. min_max_unroll = make_min_max(True)
  183. min_max_normal = make_min_max(False)
  184. @specialize.arg(2)
  185. def min_max(space, args, implementation_of):
  186. if not jit.we_are_jitted() or len(args.arguments_w) != 1 and \
  187. jit.loop_unrolling_heuristic(args.arguments_w, len(args.arguments_w)):
  188. return min_max_unroll(space, args, implementation_of)
  189. else:
  190. return min_max_normal(space, args, implementation_of)
  191. min_max._always_inline = True
  192. def max(space, __args__):
  193. """max(iterable[, key=func]) -> value
  194. max(a, b, c, ...[, key=func]) -> value
  195. With a single iterable argument, return its largest item.
  196. With two or more arguments, return the largest argument.
  197. """
  198. return min_max(space, __args__, "max")
  199. def min(space, __args__):
  200. """min(iterable[, key=func]) -> value
  201. min(a, b, c, ...[, key=func]) -> value
  202. With a single iterable argument, return its smallest item.
  203. With two or more arguments, return the smallest argument.
  204. """
  205. return min_max(space, __args__, "min")
  206. class W_Enumerate(W_Root):
  207. def __init__(self, w_iter_or_list, start, w_start):
  208. # 'w_index' should never be a wrapped int here; if it would be,
  209. # then it is actually None and the unwrapped int is in 'index'.
  210. self.w_iter_or_list = w_iter_or_list
  211. self.index = start
  212. self.w_index = w_start
  213. def descr___new__(space, w_subtype, w_iterable, w_start=None):
  214. from pypy.objspace.std.listobject import W_ListObject
  215. if w_start is None:
  216. start = 0
  217. else:
  218. w_start = space.index(w_start)
  219. if space.is_w(space.type(w_start), space.w_int):
  220. start = space.int_w(w_start)
  221. w_start = None
  222. else:
  223. start = -1
  224. if start == 0 and type(w_iterable) is W_ListObject:
  225. w_iter = w_iterable
  226. else:
  227. w_iter = space.iter(w_iterable)
  228. self = space.allocate_instance(W_Enumerate, w_subtype)
  229. self.__init__(w_iter, start, w_start)
  230. return space.wrap(self)
  231. def descr___iter__(self, space):
  232. return space.wrap(self)
  233. def descr_next(self, space):
  234. from pypy.objspace.std.listobject import W_ListObject
  235. w_index = self.w_index
  236. w_iter_or_list = self.w_iter_or_list
  237. w_item = None
  238. if w_index is None:
  239. index = self.index
  240. if type(w_iter_or_list) is W_ListObject:
  241. try:
  242. w_item = w_iter_or_list.getitem(index)
  243. except IndexError:
  244. self.w_iter_or_list = None
  245. raise OperationError(space.w_StopIteration, space.w_None)
  246. self.index = index + 1
  247. elif w_iter_or_list is None:
  248. raise OperationError(space.w_StopIteration, space.w_None)
  249. else:
  250. try:
  251. newval = rarithmetic.ovfcheck(index + 1)
  252. except OverflowError:
  253. w_index = space.wrap(index)
  254. self.w_index = space.add(w_index, space.wrap(1))
  255. self.index = -1
  256. else:
  257. self.index = newval
  258. w_index = space.wrap(index)
  259. else:
  260. self.w_index = space.add(w_index, space.wrap(1))
  261. if w_item is None:
  262. w_item = space.next(self.w_iter_or_list)
  263. return space.newtuple([w_index, w_item])
  264. def descr___reduce__(self, space):
  265. from pypy.interpreter.mixedmodule import MixedModule
  266. w_mod = space.getbuiltinmodule('_pickle_support')
  267. mod = space.interp_w(MixedModule, w_mod)
  268. w_new_inst = mod.get('enumerate_new')
  269. w_index = self.w_index
  270. if w_index is None:
  271. w_index = space.wrap(self.index)
  272. w_info = space.newtuple([self.w_iter_or_list, w_index])
  273. return space.newtuple([w_new_inst, w_info])
  274. # exported through _pickle_support
  275. def _make_enumerate(space, w_iter_or_list, w_index):
  276. if space.is_w(space.type(w_index), space.w_int):
  277. index = space.int_w(w_index)
  278. w_index = None
  279. else:
  280. index = -1
  281. return space.wrap(W_Enumerate(w_iter_or_list, index, w_index))
  282. W_Enumerate.typedef = TypeDef("enumerate",
  283. __new__=interp2app(W_Enumerate.descr___new__.im_func),
  284. __iter__=interp2app(W_Enumerate.descr___iter__),
  285. next=interp2app(W_Enumerate.descr_next),
  286. __reduce__=interp2app(W_Enumerate.descr___reduce__),
  287. )
  288. def reversed(space, w_sequence):
  289. """Return a iterator that yields items of sequence in reverse."""
  290. w_reversed = None
  291. if space.is_oldstyle_instance(w_sequence):
  292. w_reversed = space.findattr(w_sequence, space.wrap("__reversed__"))
  293. else:
  294. w_reversed_descr = space.lookup(w_sequence, "__reversed__")
  295. if w_reversed_descr is not None:
  296. w_reversed = space.get(w_reversed_descr, w_sequence)
  297. if w_reversed is not None:
  298. return space.call_function(w_reversed)
  299. return space.wrap(W_ReversedIterator(space, w_sequence))
  300. class W_ReversedIterator(W_Root):
  301. def __init__(self, space, w_sequence):
  302. self.remaining = space.len_w(w_sequence) - 1
  303. if space.lookup(w_sequence, "__getitem__") is None:
  304. raise oefmt(space.w_TypeError,
  305. "reversed() argument must be a sequence")
  306. self.w_sequence = w_sequence
  307. def descr___iter__(self, space):
  308. return space.wrap(self)
  309. def descr_length(self, space):
  310. return space.wrap(0 if self.remaining == -1 else self.remaining + 1)
  311. def descr_next(self, space):
  312. if self.remaining >= 0:
  313. w_index = space.wrap(self.remaining)
  314. try:
  315. w_item = space.getitem(self.w_sequence, w_index)
  316. except OperationError as e:
  317. if not e.match(space, space.w_StopIteration):
  318. raise
  319. else:
  320. self.remaining -= 1
  321. return w_item
  322. # Done
  323. self.remaining = -1
  324. raise OperationError(space.w_StopIteration, space.w_None)
  325. def descr___reduce__(self, space):
  326. from pypy.interpreter.mixedmodule import MixedModule
  327. w_mod = space.getbuiltinmodule('_pickle_support')
  328. mod = space.interp_w(MixedModule, w_mod)
  329. w_new_inst = mod.get('reversed_new')
  330. info_w = [self.w_sequence, space.wrap(self.remaining)]
  331. w_info = space.newtuple(info_w)
  332. return space.newtuple([w_new_inst, w_info])
  333. W_ReversedIterator.typedef = TypeDef("reversed",
  334. __iter__ = interp2app(W_ReversedIterator.descr___iter__),
  335. __length_hint__ = interp2app(W_ReversedIterator.descr_length),
  336. next = interp2app(W_ReversedIterator.descr_next),
  337. __reduce__ = interp2app(W_ReversedIterator.descr___reduce__),
  338. )
  339. W_ReversedIterator.typedef.acceptable_as_base_class = False
  340. # exported through _pickle_support
  341. def _make_reversed(space, w_seq, w_remaining):
  342. w_type = space.gettypeobject(W_ReversedIterator.typedef)
  343. iterator = space.allocate_instance(W_ReversedIterator, w_type)
  344. iterator.w_sequence = w_seq
  345. iterator.remaining = space.int_w(w_remaining)
  346. return space.wrap(iterator)
  347. class W_XRange(W_Root):
  348. def __init__(self, space, start, len, step, promote_step=False):
  349. self.space = space
  350. self.start = start
  351. self.len = len
  352. self.step = step
  353. self.promote_step = promote_step
  354. def descr_new(space, w_subtype, w_start, w_stop=None, w_step=None):
  355. start = space.int_w(w_start)
  356. if space.is_none(w_step): # no step argument provided
  357. step = 1
  358. promote_step = True
  359. else:
  360. step = space.int_w(w_step)
  361. promote_step = False
  362. if space.is_none(w_stop): # only 1 argument provided
  363. start, stop = 0, start
  364. else:
  365. stop = space.int_w(w_stop)
  366. howmany = get_len_of_range(space, start, stop, step)
  367. obj = space.allocate_instance(W_XRange, w_subtype)
  368. W_XRange.__init__(obj, space, start, howmany, step, promote_step)
  369. return space.wrap(obj)
  370. def descr_repr(self):
  371. if self.start == 0 and self.step == 1:
  372. s = "xrange(%d)" % (self._get_stop(),)
  373. elif self.step == 1:
  374. s = "xrange(%d, %d)" % (self.start, self._get_stop())
  375. else:
  376. s = "xrange(%d, %d, %d)" %(self.start, self._get_stop(), self.step)
  377. return self.space.wrap(s)
  378. def descr_len(self):
  379. return self.space.wrap(self.len)
  380. @unwrap_spec(i='index')
  381. def descr_getitem(self, i):
  382. # xrange does NOT support slicing
  383. space = self.space
  384. len = self.len
  385. if i < 0:
  386. i += len
  387. if 0 <= i < len:
  388. return space.wrap(self.start + i * self.step)
  389. raise oefmt(space.w_IndexError, "xrange object index out of range")
  390. def descr_iter(self):
  391. if self.promote_step and self.step == 1:
  392. stop = self.start + self.len
  393. return self.space.wrap(W_XRangeStepOneIterator(self.space,
  394. self.start,
  395. stop))
  396. else:
  397. return self.space.wrap(W_XRangeIterator(self.space, self.start,
  398. self.len, self.step))
  399. def descr_reversed(self):
  400. last = self.start + (self.len - 1) * self.step
  401. return self.space.wrap(W_XRangeIterator(self.space, last, self.len,
  402. -self.step))
  403. def descr_reduce(self):
  404. space = self.space
  405. return space.newtuple(
  406. [space.type(self),
  407. space.newtuple([space.wrap(self.start),
  408. space.wrap(self._get_stop()),
  409. space.wrap(self.step)])
  410. ])
  411. def _get_stop(self):
  412. if not self.len:
  413. return self.start
  414. step = self.step
  415. last = self.start + (self.len - 1) * step
  416. if step > 0:
  417. return sys.maxint if last > sys.maxint - step else last + step
  418. minint = -sys.maxint - 1
  419. return minint if last < minint - step else last + step
  420. W_XRange.typedef = TypeDef("xrange",
  421. __new__ = interp2app(W_XRange.descr_new.im_func),
  422. __repr__ = interp2app(W_XRange.descr_repr),
  423. __getitem__ = interp2app(W_XRange.descr_getitem),
  424. __iter__ = interp2app(W_XRange.descr_iter),
  425. __len__ = interp2app(W_XRange.descr_len),
  426. __reversed__ = interp2app(W_XRange.descr_reversed),
  427. __reduce__ = interp2app(W_XRange.descr_reduce),
  428. )
  429. W_XRange.typedef.acceptable_as_base_class = False
  430. class W_XRangeIterator(W_Root):
  431. def __init__(self, space, current, remaining, step):
  432. self.space = space
  433. self.current = current
  434. self.remaining = remaining
  435. self.step = step
  436. def descr_iter(self):
  437. return self.space.wrap(self)
  438. def descr_next(self):
  439. return self.next()
  440. def next(self):
  441. if self.remaining > 0:
  442. item = self.current
  443. self.current = item + self.step
  444. self.remaining -= 1
  445. return self.space.wrap(item)
  446. raise OperationError(self.space.w_StopIteration, self.space.w_None)
  447. def descr_len(self):
  448. return self.space.wrap(self.get_remaining())
  449. def descr_reduce(self):
  450. from pypy.interpreter.mixedmodule import MixedModule
  451. space = self.space
  452. w_mod = space.getbuiltinmodule('_pickle_support')
  453. mod = space.interp_w(MixedModule, w_mod)
  454. new_inst = mod.get('xrangeiter_new')
  455. w = space.wrap
  456. nt = space.newtuple
  457. tup = [w(self.current), w(self.get_remaining()), w(self.step)]
  458. return nt([new_inst, nt(tup)])
  459. def get_remaining(self):
  460. return self.remaining
  461. W_XRangeIterator.typedef = TypeDef("rangeiterator",
  462. __iter__ = interp2app(W_XRangeIterator.descr_iter),
  463. __length_hint__ = interp2app(W_XRangeIterator.descr_len),
  464. next = interp2app(W_XRangeIterator.descr_next),
  465. __reduce__ = interp2app(W_XRangeIterator.descr_reduce),
  466. )
  467. W_XRangeIterator.typedef.acceptable_as_base_class = False
  468. class W_XRangeStepOneIterator(W_XRangeIterator):
  469. _immutable_fields_ = ['stop']
  470. def __init__(self, space, start, stop):
  471. self.space = space
  472. self.current = start
  473. self.stop = stop
  474. self.step = 1
  475. def next(self):
  476. if self.current < self.stop:
  477. item = self.current
  478. self.current = item + 1
  479. return self.space.wrap(item)
  480. raise OperationError(self.space.w_StopIteration, self.space.w_None)
  481. def get_remaining(self):
  482. return self.stop - self.current