8000 Backport `count()` with step param for Py2.6 (issue #152) · thecodingchicken/python-future@5363a9f · GitHub
[go: up one dir, main page]

Skip to content

Commit 5363a9f

Browse files
committed
Backport count() with step param for Py2.6 (issue PythonCharmers#152)
1 parent 661a1c5 commit 5363a9f

File tree

7 files changed

+135
-35
lines changed

7 files changed

+135
-35
lines changed

docs/standard_library_imports.rst

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ As of version 0.14, the ``future`` package comes with top-level packages for
1515
Python 2.x that provide access to the reorganized standard library modules
1616
under their Python 3.x names.
1717

18-
Direct imports are the preferred mechanism for accesing the renamed standard library
19-
modules in Python 2/3 compatible code. For example, the following clean Python
20-
3 code runs unchanged on Python 2 after installing ``future``::
18+
Direct imports are the preferred mechanism for accesing the renamed standard
19+
library modules in Python 2/3 compatible code. For example, the following clean
20+
Python 3 code runs unchanged on Python 2 after installing ``future``::
2121

2222
>>> # Alias for future.builtins on Py2:
2323
>>> from builtins import str, open, range, dict
@@ -28,8 +28,8 @@ modules in Python 2/3 compatible code. For example, the following clean Python
2828
>>> import tkinter.dialog
2929
>>> etc.
3030

31-
Notice that this code actually runs on Python 3 without the presence of the ``future``
32-
package.
31+
Notice that this code actually runs on Python 3 without the presence of the
32+
``future`` package.
3333

3434
Of the 44 modules that were refactored with PEP 3108 (standard library
3535
reorganization), 30 are supported with direct imports in the above manner. The
@@ -92,21 +92,21 @@ package makes the Python 3.x APIs available on Python 2.x as follows::
9292
from future.standard_library import install_aliases
9393
install_aliases()
9494

95-
from collections import Counter, OrderedDict # backported to Py2.6
9695
from collections import UserDict, UserList, UserString
9796

9897
import dbm
9998
import dbm.dumb
100-
import dbm.gnu # requires Python dbm support
101-
import dbm.ndbm # requires Python dbm support
99+
import dbm.gnu # requires Python dbm support
100+
import dbm.ndbm # requires Python dbm support
102101

103102
from itertools import filterfalse, zip_longest
104103

105-
from subprocess import check_output # backported to Py2.6
106104
from subprocess import getoutput, getstatusoutput
107105

108106
from sys import intern
109107

108+
from math import ceil # now returns an int
109+
110110
import test.support
111111

112112
import urllib.error
@@ -116,6 +116,22 @@ package makes the Python 3.x APIs available on Python 2.x as follows::
116116
import urllib.robotparser
117117

118118

119+
Backports also exist of the following features from Python 2.7+ for Python 2.6:
120+
121+
- collections.Counter
122+
- collections.OrderedDict
123+
- itertools.count (with an optional step parameter)
124+
125+
These can be imported on Python 2.6 as follows::
126+
127+
from future.standard_library import install_aliases
128+
install_aliases()
129+
130+
from collections import Counter, OrderedDict
131+
from itertools import count
132+
from subprocess import check_output
133+
134+
119135
External standard-library backports
120136
-----------------------------------
121137

docs/whatsnew.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ What's New
88
What's new in version 0.14.4 (2015-06-03)
99
=========================================
1010

11-
This is primarily a bug-fix release. It adds two new minor features and fixes several small bugs.
11+
This is primarily a bug-fix release. It adds some minor new features and
12+
fixes several small bugs.
1213

1314
Minor features:
1415

1516
- ``tkinter.ttk`` support (issue #151)
1617
- ``collections.ChainMap`` backport (issue #150)
18+
- ``itertools.count`` backport for Py2.6 (issue #152)
1719

1820
Bug fixes:
1921

src/future/backports/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@
66

77
if sys.version_info[0] == 3:
88
import_top_level_modules()
9+
10+
from .misc import ceil, OrderedDict, Counter, check_output, count

src/future/backports/misc.py

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
"""
2-
Miscellaneous function (re)definitions from the Py3.3 standard library for
3-
Python 2.6/2.7.
2+
Miscellaneous function (re)definitions from the Py3.3+ standard library
3+
for Python 2.6/2.7.
44
5-
math.ceil
6-
7-
collections.OrderedDict (for Python 2.6)
8-
collections.Counter (for Python 2.6)
5+
- math.ceil (for Python 2.7)
6+
- collections.OrderedDict (for Python 2.6)
7+
- collections.Counter (for Python 2.6)
8+
- itertools.count (for Python 2.6, with step parameter)
99
"""
1010

11-
from math import ceil as oldceil
11+
from math import ceil
1212
import subprocess
1313

1414
from future.utils import iteritems, itervalues, PY26
1515

1616

17-
def ceil(x):
17+
def _ceil(x):
1818
"""
1919
Return the ceiling of x as an int.
2020
This is the smallest integral value >= x.
2121
"""
22-
return int(oldceil(x))
22+
return int(ceil(x))
2323

2424

2525
# OrderedDict Shim from Raymond Hettinger, python core dev
@@ -482,16 +482,13 @@ def __and__(self, other):
482482
result[elem] = newcount
483483
return result
484484

485-
try:
486-
from collections import OrderedDict, Counter
487-
except ImportError:
488-
# Python 2.6 doesn't have these:
489-
OrderedDict = _OrderedDict
490-
Counter = _Counter
491485

486+
def _check_output(*popenargs, **kwargs):
487+
"""
488+
For Python 2.6 compatibility: see
489+
http://stackoverflow.com/questions/4814970/
490+
"""
492491

493-
# For Python 2.6 compatibility: see http://stackoverflow.com/questions/4814970/
494-
def check_output(*popenargs, **kwargs):
495492
if 'stdout' in kwargs:
496493
raise ValueError('stdout argument not allowed, it will be overridden.')
497494
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
@@ -503,3 +500,27 @@ def check_output(*popenargs, **kwargs):
503500
cmd = popenargs[0]
504501
raise subprocess.CalledProcessError(retcode, cmd)
505502
return output
503+
504+
505+
def _count(start=0, step=1):
506+
"""
507+
``itertools.count`` in Py 2.6 doesn't accept a step
508+
parameter. This is an enhanced version of ``itertools.count``
509+
for Py2.6 equivalent to ``itertools.count`` in Python 2.7+.
510+
"""
511+
while True:
512+
yield start
513+
start += step
514+
515+
516+
if not PY26:
517+
from math import ceil
518+
from collections import OrderedDict, Counter
519+
from subprocess import check_output
520+
from itertools import count
521+
else:
522+
ceil = _ceil
523+
OrderedDict = _OrderedDict
524+
Counter = _Counter
525+
check_output = _check_output
526+
count = _count

src/future/standard_library/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@
196196
('math', 'ceil', 'future.backports.misc', 'ceil'),
197197
('collections', 'OrderedDict', 'future.backports.misc', 'OrderedDict'),
198198
('collections', 'Counter', 'future.backports.misc', 'Counter'),
199+
('itertools', 'count', 'future.backports.misc', 'count'),
199200

200201
# This is no use, since "import urllib.request" etc. still fails:
201202
# ('urllib', 'error', 'future.moves.urllib', 'error'),

src/future/types/newrange.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
from collections import Sequence, Iterator
2222
from itertools import islice
2323

24+
from future.backports.misc import count # with step parameter on Py2.6
25+
# For backward compatibility with python-future versions < 0.14.4:
26+
from future.backports.misc import _count
27+
2428

2529
class newrange(Sequence):
2630
"""
@@ -142,7 +146,7 @@ class range_iterator(Iterator):
142146
"""An iterator for a :class:`range`.
143147
"""
144148
def __init__(self, range_):
145-
self._stepper = islice(_count(range_.start, range_.step), len(range_))
149+
self._stepper = islice(count(range_.start, range_.step), len(range_))
146150

147151
def __iter__(self):
148152
return self
@@ -151,11 +155,4 @@ def next(self):
151155
return next(self._stepper)
152156

153157

154-
# itertools.count in Py 2.6 doesn't accept a step parameter
155-
def _count(start=0, step=1):
156-
while True:
157-
yield start
158-
start += step
159-
160-
161158
__all__ = ['newrange']

tests/test_future/test_backports.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Tests for various backported functions and classes in ``future.backports``
4+
"""
5+
6+
from __future__ import absolute_import, unicode_literals, print_function
7+
8+
from future.backports.misc import count, _count
9+
from future.utils import PY26
10+
from future.tests.base import unittest, skip26
11+
12+
13+
class CountTest(unittest.TestCase):
14+
"""Test the count function."""
15+
16+
def _test_count_func(self, func):
17+
self.assertEqual(next(func(1)), 1)
18+
self.assertEqual(next(func(start=1)), 1)
19+
20+
c = func()
21+
self.assertEqual(next(c), 0)
22+
self.assertEqual(next(c), 1)
23+
self.assertEqual(next(c), 2)
24+
c = func(1, 1)
25+
self.assertEqual(next(c), 1)
26+
self.assertEqual(next(c), 2)
27+
c = func(step=1)
28+
self.assertEqual(next(c), 0)
29+
self.assertEqual(next(c), 1)
30+
c = func(start=1, step=1)
31+
self.assertEqual(next(c), 1)
32+
self.assertEqual(next(c), 2)
33+
34+
c = func(-1)
35+
self.assertEqual(next(c), -1)
36+
self.assertEqual(next(c), 0)
37+
self.assertEqual(next(c), 1)
38+
c = func(1, -1)
39+
self.assertEqual(next(c), 1)
40+
self.assertEqual(next(c), 0)
41+
self.assertEqual(next(c), -1)
42+
c = func(-1, -1)
43+
self.assertEqual( 9A01 next(c), -1)
44+
self.assertEqual(next(c), -2)
45+
self.assertEqual(next(c), -3)
46+
47+
def test_count(self):
48+
"""Test the count function."""
49+
self._test_count_func(count)
50+
51+
def test_own_count(self):
52+
"""Test own count implementation."""
53+
if PY26:
54+
self.assertIs(count, _count)
55+
else:
56+
self.assertNotEqual(count, _count)
57+
self._test_count_func(_count)
58+
59+
60+
if __name__ == '__main__':
61+
unittest.main()

0 commit comments

Comments
 (0)
0