Skip to content

Commit 45da4e0

Browse files
committed
Improvements to past.builtins: map, filter, etc.
These now pass more of the Py2.7 unit tests
1 parent 81d9a44 commit 45da4e0

File tree

5 files changed

+366
-131
lines changed

5 files changed

+366
-131
lines changed

past/builtins/__init__.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,32 @@
1-
from past.builtins.noniterators import (filter, map, range, zip)
2-
from past.builtins.types import basestring, dict, str #, unicode
3-
# from past.builtins.misc import (ascii, chr, hex, input, oct, open, raw_input, unichr)
4-
from past.builtins.misc import cmp, execfile, raw_input, unichr, unicode
1+
"""
2+
A resurrection of some old functions from Python 2. These should be used
3+
sparingly, to help with porting efforts, since code using them is no
4+
longer standard Python 3 code.
5+
6+
We provide these builtin functions which have no equivalent on Py3:
7+
8+
- cmp()
9+
- execfile()
10+
11+
These aliases are also provided:
12+
13+
- raw_input() <- input()
14+
- unicode() <- str()
15+
- unichr() <- chr()
516
17+
For reference, the following Py2 builtin functions are available from
18+
these standard locations on both Py2.6+ and Py3:
19+
20+
- reduce() <- functools.reduce()
21+
- reload() <- imp.reload()
22+
23+
"""
24+
25+
from past.builtins.noniterators import (filter, map, range, reduce, zip)
26+
from past.builtins.types import basestring, dict, str, long, unicode
27+
# from past.builtins.misc import (ascii, chr, hex, input, oct, open, raw_input, unichr)
28+
from past.builtins.misc import (apply, cmp, execfile, intern, raw_input,
29+
reload, unichr, unicode, xrange)
630
from past import utils
731

832

@@ -13,7 +37,8 @@
1337
# Only shadow builtins on Py3; no new names
1438
__all__ = ['filter', 'map', 'range', 'zip',
1539
'basestring', 'dict', 'str',
16-
'cmp', 'execfile', 'raw_input', 'unichr', 'unicode'
40+
'cmp', 'execfile', 'raw_input', 'reduce', 'reload',
41+
'unichr', 'unicode', 'xrange'
1742
# 'ascii', 'chr', 'hex', 'input', 'oct', 'open', 'unichr',
1843
# 'bytes', 'dict', 'int', 'range', 'round', 'str', 'super',
1944
]

past/builtins/misc.py

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,29 @@
1-
"""
2-
A resurrection of some old functions from Python 2. These should be used
3-
sparingly, to help with porting efforts, since code using them is no
4-
longer standard Python 3 code.
5-
6-
We provide these builtin functions which have no equivalent on Py3:
7-
8-
- cmp()
9-
- execfile()
10-
11-
These aliases are also provided:
12-
13-
- raw_input() <- input()
14-
- unicode() <- str()
15-
- unichr() <- chr()
16-
17-
For reference, the following Py2 builtin functions are available from
18-
these standard locations on both Py2.6+ and Py3:
19-
20-
- reduce() <- functools.reduce()
21-
- reload() <- imp.reload()
22-
23-
"""
24-
251
from __future__ import unicode_literals
262
import sys
273

28-
from future.utils import PY3
4+
from future.utils import PY3, exec_
295

306

317
if PY3:
32-
# Bring back the cmp function
8+
def apply(f, *args, **kw):
9+
return f(*args, **kw)
3310
cmp = lambda a, b: (a > b) - (a < b)
11+
from sys import intern
3412
raw_input = input
13+
from imp import reload
3514
unicode = str
3615
unichr = chr
16+
xrange = range
3717
else:
3818
import __builtin__
19+
apply = __builtin__.apply
3920
cmp = __builtin__.cmp
21+
intern = __builtin__.intern
4022
raw_input = __builtin__.raw_input
23+
reload = __builtin__.reload
4124
unicode = __builtin__.unicode
4225
unichr = __builtin__.unichr
26+
xrange = __builtin__.xrange
4327

4428

4529
if PY3:
@@ -91,5 +75,9 @@ def execfile(filename, myglobals=None, mylocals=None):
9175
else:
9276
__builtin__.execfile(filename)
9377

78+
if PY3:
79+
__all__ = ['apply', 'cmp', 'execfile', 'intern', 'raw_input',
80+
'reload', 'unichr', 'unicode', 'xrange']
81+
else:
82+
__all__ = []
9483

95-
__all__ = ['cmp', 'raw_input', 'unichr', 'unicode', 'execfile']

past/builtins/noniterators.py

Lines changed: 224 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,41 +19,255 @@
1919

2020
from __future__ import division, absolute_import, print_function
2121

22-
import itertools
22+
from itertools import chain, starmap
23+
import itertools # since zip_longest doesn't exist on Py2
24+
from past.builtins.types import basestring
2325
from past.utils import PY3
2426

27+
28+
def flatmap(f, items):
29+
return chain.from_iterable(map(f, items))
30+
31+
2532
if PY3:
2633
import builtins
2734

2835
# list-producing versions of the major Python iterating functions
29-
def oldfilter(*args, **kwargs):
30-
return list(builtins.filter(*args, **kwargs))
36+
def oldfilter(*args):
37+
"""
38+
filter(function or None, sequence) -> list, tuple, or string
39+
40+
Return those items of sequence for which function(item) is true.
41+
If function is None, return the items that are true. If sequence
42+
is a tuple or string, return the same type, else return a list.
43+
"""
44+
mytype = type(args[1])
45+
if isinstance(args[1], basestring):
46+
return mytype().join(builtins.filter(*args))
47+
elif isinstance(args[1], (tuple, list)):
48+
return mytype(builtins.filter(*args))
49+
else:
50+
# Fall back to list. Is this the right thing to do?
51+
return list(builtins.filter(*args))
52+
53+
# This is surprisingly difficult to get right. For example, the
54+
# solutions here fail with the test cases in the docstring below:
55+
# http://stackoverflow.com/questions/8072755/
56+
def oldmap(func, *iterables):
57+
"""
58+
map(function, sequence[, sequence, ...]) -> list
59+
60+
Return a list of the results of applying the function to the
61+
items of the argument sequence(s). If more than one sequence is
62+
given, the function is called with an argument list consisting of
63+
the corresponding item of each sequence, substituting None for
64+
missing values when not all sequences have the same length. If
65+
the function is None, return a list of the items of the sequence
66+
(or a list of tuples if more than one sequence).
67+
68+
Test cases:
69+
>>> oldmap(None, 'hello world')
70+
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
3171
32-
def oldmap(*args, **kwargs):
33-
return list(builtins.map(*args, **kwargs))
72+
>>> oldmap(None, range(4))
73+
[0, 1, 2, 3]
74+
75+
More test cases are in past.tests.test_builtins.
76+
"""
77+
zipped = itertools.zip_longest(*iterables)
78+
l = list(zipped)
79+
if len(l) == 0:
80+
return []
81+
if func is None:
82+
result = l
83+
else:
84+
result = list(starmap(func, l))
85+
86+
# Inspect to see whether it's a simple sequence of tuples
87+
try:
88+
if max([len(item) for item in result]) == 1:
89+
return list(chain.from_iterable(result))
90+
# return list(flatmap(func, result))
91+
except TypeError as e:
92+
# Simple objects like ints have no len()
93+
pass
94+
return result
95+
96+
############################
97+
### For reference, the source code for Py2.7 map function:
98+
# static PyObject *
99+
# builtin_map(PyObject *self, PyObject *args)
100+
# {
101+
# typedef struct {
102+
# PyObject *it; /* the iterator object */
103+
# int saw_StopIteration; /* bool: did the iterator end? */
104+
# } sequence;
105+
#
106+
# PyObject *func, *result;
107+
# sequence *seqs = NULL, *sqp;
108+
# Py_ssize_t n, len;
109+
# register int i, j;
110+
#
111+
# n = PyTuple_Size(args);
112+
# if (n < 2) {
113+
# PyErr_SetString(PyExc_TypeError,
114+
# "map() requires at least two args");
115+
# return NULL;
116+
# }
117+
#
118+
# func = PyTuple_GetItem(args, 0);
119+
# n--;
120+
#
121+
# if (func == Py_None) {
122+
# if (PyErr_WarnPy3k("map(None, ...) not supported in 3.x; "
123+
# "use list(...)", 1) < 0)
124+
# return NULL;
125+
# if (n == 1) {
126+
# /* map(None, S) is the same as list(S). */
127+
# return PySequence_List(PyTuple_GetItem(args, 1));
128+
# }
129+
# }
130+
#
131+
# /* Get space for sequence descriptors. Must NULL out the iterator
132+
# * pointers so that jumping to Fail_2 later doesn't see trash.
133+
# */
134+
# if ((seqs = PyMem_NEW(sequence, n)) == NULL) {
135+
# PyErr_NoMemory();
136+
# return NULL;
137+
# }
138+
# for (i = 0; i < n; ++i) {
139+
# seqs[i].it = (PyObject*)NULL;
140+
# seqs[i].saw_StopIteration = 0;
141+
# }
142+
#
143+
# /* Do a first pass to obtain iterators for the arguments, and set len
144+
# * to the largest of their lengths.
145+
# */
146+
# len = 0;
147+
# for (i = 0, sqp = seqs; i < n; ++i, ++sqp) {
148+
# PyObject *curseq;
149+
# Py_ssize_t curlen;
150+
#
151+
# /* Get iterator. */
152+
# curseq = PyTuple_GetItem(args, i+1);
153+
# sqp->it = PyObject_GetIter(curseq);
154+
# if (sqp->it == NULL) {
155+
# static char errmsg[] =
156+
# "argument %d to map() must support iteration";
157+
# char errbuf[sizeof(errmsg) + 25];
158+
# PyOS_snprintf(errbuf, sizeof(errbuf), errmsg, i+2);
159+
# PyErr_SetString(PyExc_TypeError, errbuf);
160+
# goto Fail_2;
161+
# }
162+
#
163+
# /* Update len. */
164+
# curlen = _PyObject_LengthHint(curseq, 8);
165+
# if (curlen > len)
166+
# len = curlen;
167+
# }
168+
#
169+
# /* Get space for the result list. */
170+
# if ((result = (PyObject *) PyList_New(len)) == NULL)
171+
# goto Fail_2;
172+
#
173+
# /* Iterate over the sequences until all have stopped. */
174+
# for (i = 0; ; ++i) {
175+
# PyObject *alist, *item=NULL, *value;
176+
# int numactive = 0;
177+
#
178+
# if (func == Py_None && n == 1)
179+
# alist = NULL;
180+
# else if ((alist = PyTuple_New(n)) == NULL)
181+
# goto Fail_1;
182+
#
183+
# for (j = 0, sqp = seqs; j < n; ++j, ++sqp) {
184+
# if (sqp->saw_StopIteration) {
185+
# Py_INCREF(Py_None);
186+
# item = Py_None;
187+
# }
188+
# else {
189+
# item = PyIter_Next(sqp->it);
190+
# if (item)
191+
# ++numactive;
192+
# else {
193+
# if (PyErr_Occurred()) {
194+
# Py_XDECREF(alist);
195+
# goto Fail_1;
196+
# }
197+
# Py_INCREF(Py_None);
198+
# item = Py_None;
199+
# sqp->saw_StopIteration = 1;
200+
# }
201+
# }
202+
# if (alist)
203+
# PyTuple_SET_ITEM(alist, j, item);
204+
# else
205+
# break;
206+
# }
207+
#
208+
# if (!alist)
209+
# alist = item;
210+
#
211+
# if (numactive == 0) {
212+
# Py_DECREF(alist);
213+
# break;
214+
# }
215+
#
216+
# if (func == Py_None)
217+
# value = alist;
218+
# else {
219+
# value = PyEval_CallObject(func, alist);
220+
# Py_DECREF(alist);
221+
# if (value == NULL)
222+
# goto Fail_1;
223+
# }
224+
# if (i >= len) {
225+
# int status = PyList_Append(result, value);
226+
# Py_DECREF(value);
227+
# if (status < 0)
228+
# goto Fail_1;
229+
# }
230+
# else if (PyList_SetItem(result, i, value) < 0)
231+
# goto Fail_1;
232+
# }
233+
#
234+
# if (i < len && PyList_SetSlice(result, i, len, NULL) < 0)
235+
# goto Fail_1;
236+
#
237+
# goto Succeed;
238+
#
239+
# Fail_1:
240+
# Py_DECREF(result);
241+
# Fail_2:
242+
# result = NULL;
243+
# Succeed:
244+
# assert(seqs);
245+
# for (i = 0; i < n; ++i)
246+
# Py_XDECREF(seqs[i].it);
247+
# PyMem_DEL(seqs);
248+
# return result;
249+
# }
34250

35251
def oldrange(*args, **kwargs):
36252
return list(builtins.range(*args, **kwargs))
37253

38-
# def reduce(*args, **kwargs):
39-
# return list(reduce(*args, **kwargs))
40-
41254
def oldzip(*args, **kwargs):
42255
return list(builtins.zip(*args, **kwargs))
43256

44257
filter = oldfilter
45258
map = oldmap
46259
range = oldrange
260+
from functools import reduce
47261
zip = oldzip
48-
__all__ = ['filter', 'map', 'range', 'zip']
262+
__all__ = ['filter', 'map', 'range', 'reduce', 'zip']
49263

50264
else:
51265
import __builtin__
52266
# Python 2-builtin ranges produce lists
53267
filter = __builtin__.filter
54268
map = __builtin__.map
55-
# reduce = __builtin__.reduce
56269
range = __builtin__.range
270+
reduce = __builtin__.reduce
57271
zip = __builtin__.zip
58272
__all__ = []
59273

past/builtins/types/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
basestring = __builtin__.basestring
66
dict = __builtin__.dict
77
str = __builtin__.str
8+
long = __builtin__.long
9+
unicode = __builtin__.unicode
810
__all__ = []
911
else:
1012
from .basestring import basestring
1113
from .olddict import olddict as dict
1214
from .oldstr import oldstr as str
15+
long = int
16+
unicode = str
1317
# from .unicode import unicode
14-
__all__ = ['basestring', 'dict', 'str']
18+
__all__ = ['basestring', 'dict', 'str', 'long', 'unicode']
1519

0 commit comments

Comments
 (0)