summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/setuptools.txt24
-rwxr-xr-xsetup.py1
-rw-r--r--setuptools/__init__.py10
-rw-r--r--setuptools/command/build_py.py60
-rw-r--r--setuptools/dist.py5
5 files changed, 98 insertions, 2 deletions
diff --git a/docs/setuptools.txt b/docs/setuptools.txt
index 7de0ab08..1c73d4a9 100644
--- a/docs/setuptools.txt
+++ b/docs/setuptools.txt
@@ -404,6 +404,10 @@ unless you need the associated ``setuptools`` feature.
mess with it. For more details on how this argument works, see the section
below on `Automatic Resource Extraction`_.
+``convert_doctests_2to3``
+ List of doctest source files that need to be converted with 2to3. See
+ `Converting with 2to3`_ below for more details.
+
Using ``find_packages()``
-------------------------
@@ -446,6 +450,26 @@ argument in your setup script. Especially since it frees you from having to
remember to modify your setup script whenever your project grows additional
top-level packages or subpackages.
+Converting with 2to3
+--------------------
+
+When run under Python 3.x, setuptools will automatically run 2to3 on
+all Python source files, if ``setuptools.run_2to3`` is set to True; by
+default, this variable is False. It will also convert doctests inside
+all Python source files, unless ``setuptools.run_2to3_on_doctests`` is
+False; by default, this setting is True. If additional files
+containing doctests need to be converted, the
+``convert_doctests_2to3``setup option should provide a list of all
+such files.
+
+By default, this conversion uses all fixers in the ``lib2to3.fixes``
+package. To use additional fixes, the list
+``setuptools.lib2to3_fixer_packages`` must be extended with names
+of packages containing fixes. If certain fixes are to be suppressed,
+this again can be overridden with the list
+``setuptools.commands.build_py.build_py.fixers``, which then contains
+the list of all fixer class names.
+
Automatic Script Creation
=========================
diff --git a/setup.py b/setup.py
index b7fb986b..0bc261a9 100755
--- a/setup.py
+++ b/setup.py
@@ -97,6 +97,7 @@ dist = setup(
"include_package_data = setuptools.dist:assert_bool",
"dependency_links = setuptools.dist:assert_string_list",
"test_loader = setuptools.dist:check_importable",
+ "convert_doctests_2to3= setuptools.dist:assert_string_list"
],
"egg_info.writers": [
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index aaf634da..8c3eeb6d 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -24,6 +24,16 @@ _distribute = True
bootstrap_install_from = None
+# Should we run 2to3 on all Python files, in Python 3.x?
+# Default: no; assume that a distribution installed for 3.x is already
+# written in 3.x
+run_2to3 = False
+# If we run 2to3 on .py files, should we also convert docstrings?
+# Default: yes; assume that we can detect doctests reliably
+run_2to3_on_doctests = True
+# Package names for fixer packages
+lib2to3_fixer_packages = ['lib2to3.fixes']
+
def find_packages(where='.', exclude=()):
"""Return a list all Python packages found within directory 'where'
diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py
index 3fce7693..2413b420 100644
--- a/setuptools/command/build_py.py
+++ b/setuptools/command/build_py.py
@@ -3,7 +3,47 @@ from distutils.command.build_py import build_py as _build_py
from distutils.util import convert_path
from glob import glob
-class build_py(_build_py):
+try:
+ from distutils.util import Mixin2to3 as _Mixin2to3
+ # add support for converting doctests that is missing in 3.1 distutils
+ from distutils import log
+ from lib2to3.refactor import RefactoringTool, get_fixers_from_package
+ import setuptools
+ class DistutilsRefactoringTool(RefactoringTool):
+ def log_error(self, msg, *args, **kw):
+ log.error(msg, *args)
+
+ def log_message(self, msg, *args):
+ log.info(msg, *args)
+
+ def log_debug(self, msg, *args):
+ log.debug(msg, *args)
+
+ class Mixin2to3(_Mixin2to3):
+ def run_2to3(self, files, doctests = False):
+ if not setuptools.run_2to3:
+ return
+ if not files:
+ return
+ log.info("Fixing "+" ".join(files))
+ if not self.fixer_names:
+ self.fixer_names = []
+ for p in setuptools.lib2to3_fixer_packages:
+ self.fixer_names.extend(get_fixers_from_package(p))
+ if doctests:
+ if setuptools.run_2to3_on_doctests:
+ r = DistutilsRefactoringTool(self.fixer_names)
+ r.refactor(files, write=True, doctests_only=True)
+ else:
+ _Mixin2to3.run_2to3(self, files)
+
+except ImportError:
+ class Mixin2to3:
+ def run_2to3(self, files, doctests=True):
+ # Nothing done in 2.x
+ pass
+
+class build_py(_build_py, Mixin2to3):
"""Enhanced 'build_py' command that includes data files with packages
The data files are specified via a 'package_data' argument to 'setup()'.
@@ -17,6 +57,8 @@ class build_py(_build_py):
self.package_data = self.distribution.package_data
self.exclude_package_data = self.distribution.exclude_package_data or {}
if 'data_files' in self.__dict__: del self.__dict__['data_files']
+ self.__updated_files = []
+ self.__doctests_2to3 = []
def run(self):
"""Build modules, packages, and copy data files to build directory"""
@@ -30,6 +72,10 @@ class build_py(_build_py):
self.build_packages()
self.build_package_data()
+ self.run_2to3(self.__updated_files, False)
+ self.run_2to3(self.__updated_files, True)
+ self.run_2to3(self.__doctests_2to3, True)
+
# Only compile actual .py files, using our base class' idea of what our
# output files are.
self.byte_compile(_build_py.get_outputs(self, include_bytecode=0))
@@ -39,6 +85,12 @@ class build_py(_build_py):
self.data_files = files = self._get_data_files(); return files
return _build_py.__getattr__(self,attr)
+ def build_module(self, module, module_file, package):
+ outfile, copied = _build_py.build_module(self, module, module_file, package)
+ if copied:
+ self.__updated_files.append(outfile)
+ return outfile, copied
+
def _get_data_files(self):
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
self.analyze_manifest()
@@ -77,7 +129,11 @@ class build_py(_build_py):
for filename in filenames:
target = os.path.join(build_dir, filename)
self.mkpath(os.path.dirname(target))
- self.copy_file(os.path.join(src_dir, filename), target)
+ srcfile = os.path.join(src_dir, filename)
+ outf, copied = self.copy_file(srcfile, target)
+ srcfile = os.path.abspath(srcfile)
+ if copied and srcfile in self.distribution.convert_doctests_2to3:
+ self.__doctests_2to3.append(outf)
def analyze_manifest(self):
diff --git a/setuptools/dist.py b/setuptools/dist.py
index c2e57f4b..f295ca4e 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -255,6 +255,11 @@ class Distribution(_Distribution):
if value is not None:
ep.require(installer=self.fetch_build_egg)
ep.load()(self, ep.name, value)
+ if self.convert_doctests_2to3:
+ # XXX may convert to set here when we can rely on set being builtin
+ self.convert_doctests_2to3 = [os.path.abspath(p) for p in self.convert_doctests_2to3]
+ else:
+ self.convert_doctests_2to3 = []
def fetch_build_egg(self, req):
"""Fetch an egg needed for building"""