Commit 6f0132db authored by Bryton Lacquement's avatar Bryton Lacquement 🚪

fixes: major refactoring to eliminate boilerplate code

parent 6c078a2e
......@@ -2,14 +2,28 @@ from collections import defaultdict
from lib2to3.fixer_base import BaseFix as lib2to3_BaseFix
import lib2to3.fixer_util
from my2to3.trace import create_table, get_data, tracing_functions
class BaseFix(lib2to3_BaseFix):
def start_tree(self, tree, filename):
super(BaseFix, self).start_tree(tree, filename)
pass
class BaseStaticTraceFix(BaseFix):
def __init__(self, *args, **kwargs):
super(BaseStaticTraceFix, self).__init__(*args, **kwargs)
# Note: id is used to differentiate the divisions of the same line.
shared_columns = "filename", "lineno", "id"
self.insert_trace = create_table(self.basename + "_trace", *(shared_columns + self.traced_information))
self.insert_modified = create_table(self.basename + "_modified", *shared_columns)
def start_tree(self, *args, **kwargs):
super(BaseStaticTraceFix, self).start_tree(*args, **kwargs)
self.ids = defaultdict(int)
def traced_call(self, name, insert_function, node, children):
def traced_call(self, node, children):
# Important: every node inside "children" should be cloned before this
# function is called.
......@@ -22,7 +36,7 @@ class BaseFix(lib2to3_BaseFix):
id_ = self.ids[lineno]
new_node = lib2to3.fixer_util.Call(
lib2to3.fixer_util.Name(name),
lib2to3.fixer_util.Name(self.basename + "_traced"),
args=[
lib2to3.fixer_util.String('"%s"' % filename),
lib2to3.fixer_util.Comma(),
......@@ -35,6 +49,50 @@ class BaseFix(lib2to3_BaseFix):
self.ids[lineno] += 1
insert_function(filename, lineno, id_)
self.insert_modified(filename, lineno, id_)
return new_node
class BaseDynamicTraceFix(BaseStaticTraceFix):
def __init__(self, *args, **kwargs):
super(BaseDynamicTraceFix, self).__init__(*args, **kwargs)
def f(filename, lineno, id_, *args, **kwargs):
result, values = self._dynamic_trace(*args, **kwargs)
self.insert_trace(filename, lineno, id_, *values)
return result
f.__name__ = self.basename + "_traced"
tracing_functions.append(f)
class BaseSupportFix(BaseFix):
def __init__(self, *args, **kwargs):
super(BaseSupportFix, self).__init__(*args, **kwargs)
# Note: id is used to differentiate the divisions of the same line.
self.insert_support = create_table(self.basename + "_support", "filename", "lineno", "id", "status")
def analyze_data(self, filename, lineno, id_):
class_name = "Fix%sTrace" % self.basename.capitalize()
traced_information = getattr(
__import__("my2to3.fixes.fix_%s_trace" % self.basename, fromlist=[class_name]),
class_name
).traced_information
data = get_data(
self.basename + "_trace",
traced_information,
dict(filename=filename, lineno=lineno, id=id_)
)
if data:
try:
results, status = self._analyze_data()
except Exception:
results, status = False, "unknown"
else:
results, status = False, "no data"
self.insert_support(filename[1:-1], lineno, id_, status)
return results
......@@ -3,24 +3,19 @@ from lib2to3.pygram import python_symbols as syms
from lib2to3.pytree import Leaf, Node
import os
from . import BaseFix
from my2to3.trace import create_table, get_data
from . import BaseSupportFix
from my2to3.trace import create_table
from my2to3.util import add_future, data2types
# id is used to differentiate the divisions of the same line.
insert_support = create_table("division_support", "filename", "lineno", "id", "status")
def analyze_data(data):
if not data:
return False, "no data"
class FixDivisionSupport(BaseSupportFix):
"""Rewrites division_traced(n, a, b) into Py2/Py3-compatible division
"""
basename = "division"
try:
@staticmethod
def _analyze_data(data):
types = data2types(data)
except Exception as e:
# Probably, one type is not a builtins
return False, "unknown"
if len(types) == 1:
dividend, divisor = types[0]
......@@ -29,11 +24,6 @@ def analyze_data(data):
return False, "manual"
class FixDivisionSupport(BaseFix):
"""Rewrites division_traced(n, a, b) into Py2/Py3-compatible division
"""
# Inspired by https://github.com/python/cpython/blob/e42b705188271da108de42b55d9344642170aa2b/Lib/lib2to3/fixes/fix_xrange.py#L14
PATTERN = """
power<'division_traced' trailer< '(' args=any ')' >
......@@ -42,15 +32,7 @@ class FixDivisionSupport(BaseFix):
def transform(self, node, results):
args = results['args']
filename, lineno, id_ = [l.value for l in args.children[:-4:2]]
should_change, status = analyze_data(
get_data(
"division_trace",
["dividend_type", "divisor_type"],
dict(filename=filename, lineno=lineno, id=id_)
)
)
insert_support(filename[1:-1], lineno, id_, status)
should_change = self.analyze_data(*[l.value for l in args.children[:-4:2]])
if should_change:
add_future(node, 'division')
operator = Leaf(lib2to3.pgen2.token.DOUBLESLASH, "//")
......
......@@ -4,30 +4,22 @@ import lib2to3.pgen2
from lib2to3.pygram import python_symbols as syms
from lib2to3.pytree import Node
from . import BaseFix
from my2to3.trace import create_table, register_tracing_function
from . import BaseDynamicTraceFix
from my2to3.util import parse_type
insert_trace = create_table("division_trace", "filename", "lineno", "id", "dividend_type", "divisor_type")
insert_modified = create_table("division_modified", "filename", "lineno", "id")
@register_tracing_function
def division_traced(filename, lineno, id_, dividend, divisor):
insert_trace(
filename,
lineno,
id_,
parse_type(type(dividend)),
parse_type(type(divisor))
)
return dividend / divisor
class FixDivisionTrace(BaseFix):
class FixDivisionTrace(BaseDynamicTraceFix):
"""Rewrites a / b into division_traced(id, a, b)
"""
basename = "division"
traced_information = "dividend_type", "divisor_type"
@staticmethod
def _dynamic_trace(dividend, divisor):
return (
dividend / divisor,
(parse_type(type(dividend)), parse_type(type(divisor)))
)
def match(self, node):
if node.type == syms.term:
......@@ -64,7 +56,7 @@ class FixDivisionTrace(BaseFix):
comma.prefix = children[1].prefix
children[1] = comma
previous_node = self.traced_call("division_traced", insert_modified, node, children)
previous_node = self.traced_call(node, children)
else:
# It's not a division operation
previous_node = Node(syms.term, children)
......
from lib2to3.pygram import python_symbols as syms
from . import BaseFix
from my2to3.trace import create_table
from . import BaseStaticTraceFix
insert_trace = create_table("nested_except_trace", "filename", "lineno_parent", "lineno_child")
class FixNestedExceptTrace(BaseFix):
class FixNestedExceptTrace(BaseStaticTraceFix):
"""This fixer detects scope bugs which can occur due to nested except clauses
which use the same variable name.
......@@ -33,6 +29,9 @@ class FixNestedExceptTrace(BaseFix):
print(e)
raise
"""
basename = "nested_except"
traced_information = "lineno_parent", "lineno_child"
# https://github.com/python/cpython/blob/3549ca313a6103a3adb281ef3a849298b7d7f72c/Lib/lib2to3/fixes/fix_except.py#L39-L45
PATTERN = """
try_stmt< 'try' ':' (simple_stmt | suite)
......@@ -59,6 +58,6 @@ class FixNestedExceptTrace(BaseFix):
for child in self.list_except_clause_children(body):
try:
if except_clause.children[3] == child.children[3]:
insert_trace(self.filename, except_clause.get_lineno(), child.get_lineno())
self.insert_trace(self.filename, None, None, except_clause.get_lineno(), child.get_lineno())
except Exception:
pass
......@@ -4,30 +4,23 @@ import lib2to3.pgen2
from lib2to3.pygram import python_symbols as syms
from lib2to3.pytree import Node
from . import BaseFix
from my2to3.trace import create_table, register_tracing_function
from . import BaseDynamicTraceFix
from my2to3.util import parse_type
insert_trace = create_table("round_trace", "filename", "lineno", "id", "number", "ndigits")
insert_modified = create_table("round_modified", "filename", "lineno", "id")
@register_tracing_function
def round_traced(filename, lineno, id_, number, ndigits=0):
insert_trace(
filename,
lineno,
id_,
number,
ndigits
)
return round(number, ndigits)
class FixRoundTrace(BaseFix):
class FixRoundTrace(BaseDynamicTraceFix):
"""Rewrites round(a[, b]) into round_traced(id, a[, b])
"""
basename = "round"
traced_information = "number", "ndigits"
@staticmethod
def _dynamic_trace(number, ndigits=0):
return (
round(number, ndigits),
(parse_type(type(number)), parse_type(type(ndigits)))
)
# Inspired by https://github.com/python/cpython/blob/e42b705188271da108de42b55d9344642170aa2b/Lib/lib2to3/fixes/fix_xrange.py#L14
PATTERN = """
power<'round' trailer< '(' args=any ')' >
......@@ -45,4 +38,4 @@ class FixRoundTrace(BaseFix):
# e.g. round(1,23, 4)
children = [leaf.clone() for leaf in args.children]
node.replace(self.traced_call("division_traced", insert_modified, node, children))
node.replace(self.traced_call(node, children))
......@@ -30,7 +30,7 @@ class testFixNestedExceptTrace(FixerTestCase):
3
"""
self.unchanged(a)
self.assertDataEqual("nested_except_trace", [(u'<string>', 4, 7)])
self.assertDataEqual("nested_except_trace", [(u'<string>', None, None, 4, 7)])
def test_except_2(self):
a = """
......@@ -59,7 +59,7 @@ class testFixNestedExceptTrace(FixerTestCase):
4
"""
self.unchanged(a)
self.assertDataEqual("nested_except_trace", [(u'<string>', 7, 10), (u'<string>', 4, 7), (u'<string>', 4, 10)])
self.assertDataEqual("nested_except_trace", [(u'<string>', None, None, 7, 10), (u'<string>', None, None, 4, 7), (u'<string>', None, None, 4, 10)])
def test_else(self):
a = """
......
......@@ -8,9 +8,6 @@ conn = sqlite3.connect(database)
tracing_functions = []
def register_tracing_function(f):
tracing_functions.append(f)
return f
def create_table(table, *columns):
......@@ -115,11 +112,9 @@ class ModuleImporter:
def patch_imports(whitelist):
sys.meta_path.append(ModuleImporter(whitelist))
# XXX: This makes sure that "my2to3.trace.tracing_functions" is correctly
# populated. Let's find a better way.
apply_fixers("", "dummy")
for f in tracing_functions:
setattr(__builtin__, f.__name__, f)
for fixer in get_fixers():
# Import each fixer, to make sure that "my2to3.trace.tracing_functions" is
# correctly populated.
__import__(fixer)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment