Skip to content

Commit f4ed99c

Browse files
committed
Add safe fixer for division: 3 / 5 -> old_div(3, 5)
Also move ``old_div`` to ``past.utils``
1 parent d8c5431 commit f4ed99c

File tree

5 files changed

+57
-17
lines changed

5 files changed

+57
-17
lines changed

future/tests/test_futurize.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ def test_shebang_blank_with_future_division_import(self):
4949
"""
5050
after = """
5151
#!/usr/bin/env python
52-
from __future__ import division
5352
53+
from __future__ import division
54+
from past.utils import old_div
5455
import math
55-
1 / 5
56+
old_div(1, 5)
5657
"""
5758
self.convert_check(before, after)
5859

@@ -515,7 +516,6 @@ def test_literal_prefixes_are_not_stripped(self):
515516
'''
516517
self.unchanged(code)
517518

518-
@unittest.expectedFailure
519519
def test_division(self):
520520
"""
521521
TODO: implement this!
@@ -524,7 +524,7 @@ def test_division(self):
524524
x = 1 / 2
525525
"""
526526
after = """
527-
from future.utils import old_div
527+
from past.utils import old_div
528528
x = old_div(1, 2)
529529
"""
530530
self.convert_check(before, after, stages=[1, 2])
@@ -1038,9 +1038,9 @@ def test_safe_division(self):
10381038
"""
10391039
after = """
10401040
from __future__ import division
1041-
from future.utils import old_div
1041+
from past.utils import old_div
10421042
x = old_div(3, 2)
1043-
y = old_div(3. / 2)
1043+
y = old_div(3., 2)
10441044
assert x == 1 and isinstance(x, int)
10451045
assert y == 1.5 and isinstance(y, float)
10461046
"""
@@ -1055,6 +1055,8 @@ def test_safe_division_overloaded(self):
10551055
before = """
10561056
class Path(str):
10571057
def __div__(self, other):
1058+
return self.__truediv__(other)
1059+
def __truediv__(self, other):
10581060
return Path(str(self) + '/' + str(other))
10591061
path1 = Path('home')
10601062
path2 = Path('user')
@@ -1064,13 +1066,15 @@ def __div__(self, other):
10641066
"""
10651067
after = """
10661068
from __future__ import division
1067-
from future.utils import old_div
1069+
from past.utils import old_div
10681070
class Path(str):
1071+
def __div__(self, other):
1072+
return self.__truediv__(other)
10691073
def __truediv__(self, other):
10701074
return Path(str(self) + '/' + str(other))
10711075
path1 = Path('home')
10721076
path2 = Path('user')
1073-
z = old_div(path1 / path2)
1077+
z = old_div(path1, path2)
10741078
assert isinstance(z, Path)
10751079
assert str(z) == 'home/user'
10761080
"""

future/utils/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,8 +617,11 @@ def exec_(code, globs=None, locs=None):
617617
exec("""exec code in globs, locs""")
618618

619619

620+
# Defined here too for backward compatibility:
620621
def old_div(a, b):
621622
"""
623+
DEPRECATED: import ``old_div`` from ``past.utils`` instead.
624+
622625
Equivalent to ``a / b`` on Python 2 without ``from __future__ import
623626
division``.
624627

libfuturize/fixes/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070

7171
libfuturize_fix_names_stage1 = set([
7272
'libfuturize.fixes.fix_absolute_import',
73-
'libfuturize.fixes.fix_division',
7473
'libfuturize.fixes.fix_next_call', # obj.next() -> next(obj). Unlike
7574
# lib2to3.fixes.fix_next, doesn't change
7675
# the ``next`` method to ``__next__``.
@@ -82,6 +81,7 @@
8281
libfuturize_fix_names_stage2 = set([
8382
# 'libfuturize.fixes.fix_add__future__imports_except_unicode_literals', # just in case
8483
'libfuturize.fixes.fix_cmp',
84+
'libfuturize.fixes.fix_division_safe',
8585
'libfuturize.fixes.fix_execfile',
8686
'libfuturize.fixes.fix_future_builtins',
8787
'libfuturize.fixes.fix_future_standard_library',

libfuturize/fixes/fix_division_safe.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
from __future__ import division
77
88
at the top and changes any old-style divisions to be calls to
9-
future.utils.old_div so the code runs as before on Py2.6/2.7 and has the same
9+
past.utils.old_div so the code runs as before on Py2.6/2.7 and has the same
1010
behaviour on Py3.
1111
"""
1212

1313
from lib2to3 import fixer_base
14-
from lib2to3.fixer_util import touch_import
15-
from libfuturize.fixer_util import token, future_import
14+
from lib2to3.fixer_util import (Node, Call, Name, parenthesize,
15+
syms, LParen, RParen, Comma, Number)
16+
from libfuturize.fixer_util import token, future_import, touch_import_top
1617

1718
def match_division(node):
1819
u"""
@@ -23,18 +24,37 @@ def match_division(node):
2324
return node.type == slash and not node.next_sibling.type == slash and \
2425
not node.prev_sibling.type == slash
2526

27+
28+
def parenthesize_two(expr1, expr2):
29+
return Node(syms.atom, [LParen(), expr1, Comma(), expr2, RParen()])
30+
31+
2632
class FixDivisionSafe(fixer_base.BaseFix):
33+
# BM_compatible = True
2734
run_order = 4 # this seems to be ignored?
2835

36+
_accept_type = token.SLASH
37+
38+
PATTERN = """
39+
term<(not('/') any)+ '/' ((not('/') any))>
40+
"""
41+
2942
def match(self, node):
3043
u"""
3144
Since the tree needs to be fixed once and only once if and only if it
3245
matches, then we can start discarding matches after we make the first.
3346
"""
34-
return match_division(node)
47+
if (node.type == self.syms.term and
48+
len(node.children) == 3 and
49+
match_division(node.children[1])):
50+
expr1, expr2 = node.children[0], node.children[2]
51+
return expr1, expr2
3552

3653
def transform(self, node, results):
37-
import pdb
38-
pdb.set_trace()
3954
future_import(u"division", node)
40-
touch_import(u'future.utils', u'old_div', node)
55+
touch_import_top(u'past.utils', u'old_div', node)
56+
expr1, expr2 = results[0].clone(), results[1].clone()
57+
# Strip any leading space for the first number:
58+
expr1.prefix = u''
59+
return Call(Name("old_div"), [expr1, Comma(), expr2], prefix=node.prefix)
60+

past/utils/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sys
2+
import numbers
23

34
PY3 = sys.version_info[0] == 3
45
PY2 = sys.version_info[0] == 2
@@ -64,6 +65,18 @@ def native(obj):
6465
return obj
6566

6667

68+
def old_div(a, b):
69+
"""
70+
Equivalent to ``a / b`` on Python 2 without ``from __future__ import
71+
division``.
72+
73+
TODO: generalize this to other objects (like arrays etc.)
74+
"""
75+
if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral):
76+
return a // b
77+
else:
78+
return a / b
79+
6780

68-
__all__ = ['PY3', 'PY2', 'PYPY', 'with_metaclass', 'native']
81+
__all__ = ['PY3', 'PY2', 'PYPY', 'with_metaclass', 'native', 'old_div']
6982

0 commit comments

Comments
 (0)