[Python-checkins] distutils2: use_2to3 option added. tests added.
tarek.ziade
python-checkins at python.org
Thu Aug 19 08:34:13 CEST 2010
tarek.ziade pushed 991755cb2e16 to distutils2:
http://hg.python.org/distutils2/rev/991755cb2e16
changeset: 560:991755cb2e16
parent: 350:8b73f08d1d8d
user: Zubin Mithra <zubin.mithra at gmail.com>
date: Thu Jul 08 15:06:21 2010 +0530
summary: use_2to3 option added. tests added.
files: src/distutils2/command/build_py.py, src/distutils2/core.py, src/distutils2/dist.py, src/distutils2/tests/fixer/__init__.py, src/distutils2/tests/fixer/fix_idioms.py, src/distutils2/tests/test_Mixin2to3.py, src/distutils2/tests/test_util.py, src/distutils2/util.py
diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py
--- a/src/distutils2/command/build_py.py
+++ b/src/distutils2/command/build_py.py
@@ -19,6 +19,7 @@
try:
from distutils2.util import Mixin2to3 as _Mixin2to3
+ from distutils2 import run_2to3_on_doctests
from lib2to3.refactor import get_fixers_from_package
_CONVERT = True
_KLASS = _Mixin2to3
@@ -33,13 +34,16 @@
yet does nothing in particular with it.
"""
if _CONVERT:
- def _run_2to3(self, files, doctests=[]):
+ def _run_2to3(self, files, doctests=[], fixers=[]):
""" Takes a list of files and doctests, and performs conversion
on those.
- First, the files which contain the code(`files`) are converted.
- Second, the doctests in `files` are converted.
- Thirdly, the doctests in `doctests` are converted.
"""
+ # if additional fixers are present, use them
+ if fixers:
+ self.fixer_names = fixers
# Convert the ".py" files.
logging.info("Converting Python code")
@@ -56,12 +60,12 @@
# containing doctests. It is set as
# distutils2.run_2to3_on_doctests
- if doctests != [] and distutils2.run_2to3_on_doctests:
+ if doctests != [] and run_2to3_on_doctests:
logging.info("Converting text files which contain doctests")
_KLASS.run_2to3(self, doctests, doctests_only=True)
else:
# If run on Python 2.x, there is nothing to do.
- def _run_2to3(self, files, doctests=[]):
+ def _run_2to3(self, files, doctests=[], fixers=[]):
pass
class build_py(Command, Mixin2to3):
@@ -147,7 +151,8 @@
self.build_package_data()
if self.distribution.use_2to3 and self_updated_files:
- self.run_2to3(self._updated_files, self._doctests_2to3)
+ self.run_2to3(self._updated_files, self._doctests_2to3,
+ self.distribution.use_2to3_fixers)
self.byte_compile(self.get_outputs(include_bytecode=0))
diff --git a/src/distutils2/core.py b/src/distutils2/core.py
--- a/src/distutils2/core.py
+++ b/src/distutils2/core.py
@@ -49,8 +49,8 @@
'maintainer', 'maintainer_email', 'url', 'license',
'description', 'long_description', 'keywords',
'platforms', 'classifiers', 'download_url',
- 'requires', 'provides', 'obsoletes', 'use_2to3',
- 'convert_2to3_doctests',
+ 'requires', 'provides', 'obsoletes', 'use_2to3_fixers',
+ 'convert_2to3_doctests', 'use_2to3_fixers'
)
# Legal keyword arguments for the Extension constructor
diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py
--- a/src/distutils2/dist.py
+++ b/src/distutils2/dist.py
@@ -116,7 +116,9 @@
('use-2to3', None,
"use 2to3 to make source python 3.x compatible"),
('convert-2to3-doctests', None,
- "use 2to3 to convert doctests in seperate text files"),
+ "use 2to3 to convert doctests in seperate text files"),
+ ('use-2to3-fixers', None,
+ "list additional fixers opted for during 2to3 conversion")
]
display_option_names = map(lambda x: translate_longopt(x[0]),
display_options)
@@ -212,6 +214,7 @@
self.password = ''
self.use_2to3 = False
self.convert_2to3_doctests = []
+ self.use_2to3_fixers = []
# And now initialize bookkeeping stuff that can't be supplied by
# the caller at all. 'command_obj' maps command names to
diff --git a/src/distutils2/tests/fixer/__init__.py b/src/distutils2/tests/fixer/__init__.py
new file mode 100644
diff --git a/src/distutils2/tests/fixer/fix_idioms.py b/src/distutils2/tests/fixer/fix_idioms.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/tests/fixer/fix_idioms.py
@@ -0,0 +1,134 @@
+"""Adjust some old Python 2 idioms to their modern counterparts.
+
+* Change some type comparisons to isinstance() calls:
+ type(x) == T -> isinstance(x, T)
+ type(x) is T -> isinstance(x, T)
+ type(x) != T -> not isinstance(x, T)
+ type(x) is not T -> not isinstance(x, T)
+
+* Change "while 1:" into "while True:".
+
+* Change both
+
+ v = list(EXPR)
+ v.sort()
+ foo(v)
+
+and the more general
+
+ v = EXPR
+ v.sort()
+ foo(v)
+
+into
+
+ v = sorted(EXPR)
+ foo(v)
+"""
+# Author: Jacques Frechet, Collin Winter
+
+# Local imports
+from lib2to3 import fixer_base
+from lib2to3.fixer_util import Call, Comma, Name, Node, syms
+
+CMP = "(n='!=' | '==' | 'is' | n=comp_op< 'is' 'not' >)"
+TYPE = "power< 'type' trailer< '(' x=any ')' > >"
+
+class FixIdioms(fixer_base.BaseFix):
+
+ explicit = False # The user must ask for this fixer
+
+ PATTERN = r"""
+ isinstance=comparison< %s %s T=any >
+ |
+ isinstance=comparison< T=any %s %s >
+ |
+ while_stmt< 'while' while='1' ':' any+ >
+ |
+ sorted=any<
+ any*
+ simple_stmt<
+ expr_stmt< id1=any '='
+ power< list='list' trailer< '(' (not arglist<any+>) any ')' > >
+ >
+ '\n'
+ >
+ sort=
+ simple_stmt<
+ power< id2=any
+ trailer< '.' 'sort' > trailer< '(' ')' >
+ >
+ '\n'
+ >
+ next=any*
+ >
+ |
+ sorted=any<
+ any*
+ simple_stmt< expr_stmt< id1=any '=' expr=any > '\n' >
+ sort=
+ simple_stmt<
+ power< id2=any
+ trailer< '.' 'sort' > trailer< '(' ')' >
+ >
+ '\n'
+ >
+ next=any*
+ >
+ """ % (TYPE, CMP, CMP, TYPE)
+
+ def match(self, node):
+ r = super(FixIdioms, self).match(node)
+ # If we've matched one of the sort/sorted subpatterns above, we
+ # want to reject matches where the initial assignment and the
+ # subsequent .sort() call involve different identifiers.
+ if r and "sorted" in r:
+ if r["id1"] == r["id2"]:
+ return r
+ return None
+ return r
+
+ def transform(self, node, results):
+ if "isinstance" in results:
+ return self.transform_isinstance(node, results)
+ elif "while" in results:
+ return self.transform_while(node, results)
+ elif "sorted" in results:
+ return self.transform_sort(node, results)
+ else:
+ raise RuntimeError("Invalid match")
+
+ def transform_isinstance(self, node, results):
+ x = results["x"].clone() # The thing inside of type()
+ T = results["T"].clone() # The type being compared against
+ x.set_prefix("")
+ T.set_prefix(" ")
+ test = Call(Name("isinstance"), [x, Comma(), T])
+ if "n" in results:
+ test.set_prefix(" ")
+ test = Node(syms.not_test, [Name("not"), test])
+ test.set_prefix(node.get_prefix())
+ return test
+
+ def transform_while(self, node, results):
+ one = results["while"]
+ one.replace(Name("True", prefix=one.get_prefix()))
+
+ def transform_sort(self, node, results):
+ sort_stmt = results["sort"]
+ next_stmt = results["next"]
+ list_call = results.get("list")
+ simple_expr = results.get("expr")
+
+ if list_call:
+ list_call.replace(Name("sorted", prefix=list_call.get_prefix()))
+ elif simple_expr:
+ new = simple_expr.clone()
+ new.set_prefix("")
+ simple_expr.replace(Call(Name("sorted"), [new],
+ prefix=simple_expr.get_prefix()))
+ else:
+ raise RuntimeError("should not have reached here")
+ sort_stmt.remove()
+ if next_stmt:
+ next_stmt[0].set_prefix(sort_stmt.get_prefix())
diff --git a/src/distutils2/tests/test_Mixin2to3.py b/src/distutils2/tests/test_Mixin2to3.py
--- a/src/distutils2/tests/test_Mixin2to3.py
+++ b/src/distutils2/tests/test_Mixin2to3.py
@@ -47,6 +47,24 @@
self.assertEquals(new_doctest_content, converted_doctest_content)
+ @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6')
+ def test_additional_fixers(self):
+ # used to check if use_2to3_fixers works
+ from distutils2.tests import fixer
+ code_content = "type(x) is T"
+ code_handle = self.mktempfile()
+ code_name = code_handle.name
+
+ code_handle.write(code_content)
+ code_handle.flush()
+
+ mixin2to3 = Mixin2to3()
+
+ mixin2to3._run_2to3([code_name], None, ['distutils2.tests.fixer'])
+ converted_code_content = "isinstance(x, T)"
+ new_code_content = "".join(open(code_name).readlines())
+ self.assertEquals(new_code_content, converted_code_content)
+
def test_suite():
return unittest.makeSuite(Mixin2to3TestCase)
diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py
--- a/src/distutils2/tests/test_util.py
+++ b/src/distutils2/tests/test_util.py
@@ -327,6 +327,21 @@
file_handle.close()
self.assertEquals(new_content, converted_content)
+ @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more')
+ def test_use_additional_fixers(self):
+ # to check if additional fixers work
+ content = "type(x) is T"
+ converted_content = "isinstance(x, T)"
+ file_handle = self.mktempfile()
+ file_name = file_handle.name
+ file_handle.write(content)
+ file_handle.flush()
+ file_handle.seek(0)
+ from distutils2.util import run_2to3
+ run_2to3([file_name], None, ['distutils2.tests.fixer'])
+ new_content = "".join(file_handle.read())
+ file_handle.close()
+ self.assertEquals(new_content, converted_content)
def test_suite():
return unittest.makeSuite(UtilTestCase)
diff --git a/src/distutils2/util.py b/src/distutils2/util.py
--- a/src/distutils2/util.py
+++ b/src/distutils2/util.py
@@ -644,7 +644,7 @@
# utility functions for 2to3 support
-def run_2to3(files, doctests_only=False, fixer_names=None, options=None,
+def run_2to3(files=[], doctests_only=False, fixer_names=[], options=None,
explicit=None):
""" Wrapper function around the refactor() class which
performs the conversions on a list of python files.
@@ -655,13 +655,23 @@
return
# Make this class local, to delay import of 2to3
- from lib2to3.refactor import get_fixers_from_package
- from distutils2.converter.refactor import DistutilsRefactoringTool
+ from lib2to3.refactor import get_fixers_from_package, RefactoringTool
+ #from distutils2.converter.refactor import DistutilsRefactoringTool
- if fixer_names is None:
- fixer_names = get_fixers_from_package('lib2to3.fixes')
+ fixers = []
+
+ # select only the 'default fixers' from the lib2to3 package
+ for fixer in get_fixers_from_package('lib2to3.fixes'):
+ if fixers not in ('lib2to3.fixes.fix_buffer', 'lib2to3.fixes.fix_idioms',
+ 'lib2to3.fixes.fix_numliterals', 'lib2to3.fixes.fix_set_literal',
+ 'lib2to3.fixes.fix_ws_comma'):
+ fixers.append(fixer)
- r = DistutilsRefactoringTool(fixer_names, options=options)
+ if fixer_names is not None:
+ for fixer_package in fixer_names:
+ fixers.extend(get_fixers_from_package(fixer_package))
+
+ r = RefactoringTool(fixers, options=options)
if doctests_only:
r.refactor(files, doctests_only=True, write=True)
else:
@@ -676,7 +686,7 @@
"""
# provide list of fixers to run.
# defaults to all from lib2to3.fixers
- fixer_names = None
+ fixer_names = []
# options dictionary
options = None
@@ -687,4 +697,4 @@
def run_2to3(self, files, doctests_only=False):
""" Issues a call to util.run_2to3. """
return run_2to3(files, doctests_only, self.fixer_names,
- self.options, self.explicit)
+ self.options, self.explicit)
\ No newline at end of file
--
Repository URL: http://hg.python.org/distutils2
More information about the Python-checkins
mailing list