Skip to content

Commit 8b78b5e

Browse files
authored
Merge pull request #368 from jmadler/3step
Support complex statements in fix_divsion_safe
2 parents 0995e9a + 4f8b461 commit 8b78b5e

File tree

3 files changed

+73
-23
lines changed

3 files changed

+73
-23
lines changed

src/libfuturize/fixer_util.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -507,13 +507,14 @@ def wrap_in_fn_call(fn_name, args, prefix=None):
507507
508508
>>> wrap_in_fn_call("olddiv", (arg1, arg2))
509509
olddiv(arg1, arg2)
510+
511+
>>> wrap_in_fn_call("olddiv", [arg1, comma, arg2, comma, arg3])
512+
olddiv(arg1, arg2, arg3)
510513
"""
511514
assert len(args) > 0
512-
if len(args) == 1:
513-
newargs = args
514-
elif len(args) == 2:
515+
if len(args) == 2:
515516
expr1, expr2 = args
516517
newargs = [expr1, Comma(), expr2]
517518
else:
518-
assert NotImplementedError('write me')
519+
newargs = args
519520
return Call(Name(fn_name), newargs, prefix=prefix)

src/libfuturize/fixes/fix_division_safe.py

+36-19
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import re
1717
import lib2to3.pytree as pytree
18-
from lib2to3.fixer_util import Leaf, Node
18+
from lib2to3.fixer_util import Leaf, Node, Comma
1919
from lib2to3 import fixer_base
2020
from lib2to3.fixer_util import syms, does_tree_import
2121
from libfuturize.fixer_util import (token, future_import, touch_import_top,
@@ -33,8 +33,14 @@ def match_division(node):
3333

3434
const_re = re.compile('^[0-9]*[.][0-9]*$')
3535

36+
def is_floaty(node, div_idx):
37+
return _is_floaty(node.children[0:div_idx]) or _is_floaty(node.children[div_idx+1:])
38+
3639

3740
def _is_floaty(expr):
41+
if isinstance(expr, list):
42+
expr = expr[0]
43+
3844
if isinstance(expr, Leaf):
3945
# If it's a leaf, let's see if it's a numeric constant containing a '.'
4046
return const_re.match(expr.value)
@@ -44,6 +50,24 @@ def _is_floaty(expr):
4450
return expr.children[0].value == u'float'
4551
return False
4652

53+
def find_division(node):
54+
for i, child in enumerate(node.children):
55+
if match_division(child):
56+
return i
57+
return False
58+
59+
def clone_div_operands(node, div_idx):
60+
children = []
61+
for i, child in enumerate(node.children):
62+
if i == div_idx:
63+
children.append(Comma())
64+
else:
65+
children.append(child.clone())
66+
67+
# Strip any leading space for the first number:
68+
children[0].prefix = u''
69+
70+
return children
4771

4872
class FixDivisionSafe(fixer_base.BaseFix):
4973
# BM_compatible = True
@@ -60,33 +84,26 @@ def start_tree(self, tree, name):
6084
Skip this fixer if "__future__.division" is already imported.
6185
"""
6286
super(FixDivisionSafe, self).start_tree(tree, name)
63-
self.skip = "division" in tree.future_features
87+
self.skip = "division" not in tree.future_features
6488

6589
def match(self, node):
6690
u"""
6791
Since the tree needs to be fixed once and only once if and only if it
6892
matches, we can start discarding matches after the first.
6993
"""
70-
if (node.type == self.syms.term and
71-
len(node.children) == 3 and
72-
match_division(node.children[1])):
73-
expr1, expr2 = node.children[0], node.children[2]
74-
return expr1, expr2
75-
else:
76-
return False
94+
if node.type == self.syms.term:
95+
div_idx = find_division(node)
96+
if div_idx is not False:
97+
# if expr1 or expr2 are obviously floats, we don't need to wrap in
98+
# old_div, as the behavior of division between any number and a float
99+
# should be the same in 2 or 3
100+
if not is_floaty(node, div_idx):
101+
return clone_div_operands(node, div_idx)
102+
return False
77103

78104
def transform(self, node, results):
79105
if self.skip:
80106
return
81107
future_import(u"division", node)
82-
83-
expr1, expr2 = results[0].clone(), results[1].clone()
84-
# Strip any leading space for the first number:
85-
expr1.prefix = u''
86-
# if expr1 or expr2 are obviously floats, we don't need to wrap in
87-
# old_div, as the behavior of division between any number and a float
88-
# should be the same in 2 or 3
89-
if _is_floaty(expr1) or _is_floaty(expr2):
90-
return
91108
touch_import_top(u'past.utils', u'old_div', node)
92-
return wrap_in_fn_call("old_div", (expr1, expr2), prefix=node.prefix)
109+
return wrap_in_fn_call("old_div", results, prefix=node.prefix)

tests/test_future/test_futurize.py

+32
Original file line numberDiff line numberDiff line change
@@ -1181,18 +1181,50 @@ def test_safe_division(self):
11811181
after futurization.
11821182
"""
11831183
before = """
1184+
import random
11841185
x = 3 / 2
11851186
y = 3. / 2
1187+
foo = range(100)
11861188
assert x == 1 and isinstance(x, int)
11871189
assert y == 1.5 and isinstance(y, float)
1190+
a = 1 + foo[len(foo) / 2]
1191+
b = 1 + foo[len(foo) * 3 / 4]
1192+
assert a == 50
1193+
assert b == 75
1194+
r = random.randint(0, 1000) * 1.0 / 1000
1195+
output = { "SUCCESS": 5, "TOTAL": 10 }
1196+
output["SUCCESS"] * 100 / output["TOTAL"]
1197+
obj = foo
1198+
val = float(obj.numer) / obj.denom * 1e-9
1199+
mount.bytes_free * mount.free_size / bytes_in_gb
1200+
obj.total_count() * threshold / 100
1201+
100 * abs(obj.width - original_width) / float(max(obj.width, original_width))
1202+
100 * abs(obj.width - original_width) / max(obj.width, original_width)
1203+
float(target_width) * float(original_height) / float(original_width)
11881204
"""
11891205
after = """
11901206
from __future__ import division
11911207
from past.utils import old_div
1208+
import random
11921209
x = old_div(3, 2)
11931210
y = 3. / 2
1211+
foo = range(100)
11941212
assert x == 1 and isinstance(x, int)
11951213
assert y == 1.5 and isinstance(y, float)
1214+
a = 1 + foo[old_div(len(foo), 2)]
1215+
b = 1 + foo[old_div(len(foo) * 3, 4)]
1216+
assert a == 50
1217+
assert b == 75
1218+
r = old_div(random.randint(0, 1000) * 1.0, 1000)
1219+
output = { "SUCCESS": 5, "TOTAL": 10 }
1220+
output["SUCCESS"] * 100 / output["TOTAL"]
1221+
obj = foo
1222+
val = float(obj.numer) / obj.denom * 1e-9
1223+
old_div(mount.bytes_free * mount.free_size, bytes_in_gb)
1224+
old_div(obj.total_count() * threshold, 100)
1225+
100 * abs(obj.width - original_width) / float(max(obj.width, original_width))
1226+
100 * abs(obj.width - original_width), max(obj.width, original_width))
1227+
float(target_width) * float(original_height) / float(original_width)
11961228
"""
11971229
self.convert_check(before, after)
11981230

0 commit comments

Comments
 (0)