[Python-checkins] distutils2: Improve byte-compilation to be independent of -O or -B.
eric.araujo
python-checkins at python.org
Mon Nov 14 15:24:07 CET 2011
http://hg.python.org/distutils2/rev/7c0a88497b5c
changeset: 1237:7c0a88497b5c
user: Éric Araujo <merwok at netwok.org>
date: Sat Nov 12 02:58:14 2011 +0100
summary:
Improve byte-compilation to be independent of -O or -B.
All code (util.byte_compile, build_py, install_lib) can now create .pyc
and/or.pyo files according to options given by users, without
interference from the calling Python’s own optimize mode or from the
sys.dont_write_bytecode switch.
The rationale is that packaging gives control over the creation of
.pyc/.pyo files to the user with its own explicit option, and the
behavior should not be changed if the calling Python happens to run with
-B or -O for whatever reason.
This is actually a bug fix, not an improvement: Digging into the early
history of distutils shows that the original author wanted this behavior
(see for example comments in build_py in r12940).
files:
CHANGES.txt | 2 +
distutils2/command/build_py.py | 67 +++-----
distutils2/command/cmd.py | 20 ++-
distutils2/command/install_lib.py | 67 ++------
distutils2/errors.py | 4 -
distutils2/tests/support.py | 6 +-
distutils2/tests/test_command_build_py.py | 50 ++++--
distutils2/tests/test_command_install_dist.py | 11 +-
distutils2/tests/test_command_install_lib.py | 78 +++++----
distutils2/tests/test_mixin2to3.py | 1 +
distutils2/tests/test_util.py | 58 +++---
distutils2/util.py | 25 +-
12 files changed, 184 insertions(+), 205 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -149,6 +149,8 @@
shlex not supporting unicode in 2.x, fix wrong shutil import [david, éric]
- #13205: Fix and improve generated setup scripts [david, éric]
- #11751: Improve test coverage for manifest [justin]
+- Byte compilation is now isolated from the calling Python -B or -O options
+ [éric]
1.0a3 - 2010-10-08
diff --git a/distutils2/command/build_py.py b/distutils2/command/build_py.py
--- a/distutils2/command/build_py.py
+++ b/distutils2/command/build_py.py
@@ -1,7 +1,6 @@
"""Build pure Python modules (just copy to build directory)."""
import os
-import sys
from glob import glob
from distutils2 import logger
@@ -13,10 +12,14 @@
# marking public APIs
__all__ = ['build_py']
+
class build_py(Command, Mixin2to3):
description = "build pure Python modules (copy to build directory)"
+ # The options for controlling byte compilation are two independent sets;
+ # more info in install_lib or the reST docs
+
user_options = [
('build-lib=', 'd', "directory to build (copy) to"),
('compile', 'c', "compile .py to .pyc"),
@@ -28,13 +31,14 @@
('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 separate text files"),
('use-2to3-fixers', None,
"list additional fixers opted for during 2to3 conversion"),
]
boolean_options = ['compile', 'force']
- negative_opt = {'no-compile' : 'compile'}
+
+ negative_opt = {'no-compile': 'compile'}
def initialize_options(self):
self.build_lib = None
@@ -108,14 +112,15 @@
self.run_2to3(self._updated_files, self._doctests_2to3,
self.use_2to3_fixers)
- self.byte_compile(self.get_outputs(include_bytecode=False))
+ self.byte_compile(self.get_outputs(include_bytecode=False),
+ prefix=self.build_lib)
# -- Top-level worker functions ------------------------------------
def get_data_files(self):
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples.
- Helper function for `finalize_options()`.
+ Helper function for finalize_options.
"""
data = []
if not self.packages:
@@ -130,7 +135,7 @@
# Length of path to strip from found files
plen = 0
if src_dir:
- plen = len(src_dir)+1
+ plen = len(src_dir) + 1
# Strip directory from globbed filenames
filenames = [
@@ -142,7 +147,7 @@
def find_data_files(self, package, src_dir):
"""Return filenames for package's data files in 'src_dir'.
- Helper function for `get_data_files()`.
+ Helper function for get_data_files.
"""
globs = (self.package_data.get('', [])
+ self.package_data.get(package, []))
@@ -157,7 +162,7 @@
def build_package_data(self):
"""Copy data files into build directory.
- Helper function for `run()`.
+ Helper function for run.
"""
# FIXME add tests for this method
for package, src_dir, build_dir, filenames in self.data_files:
@@ -167,16 +172,17 @@
self.mkpath(os.path.dirname(target))
outf, copied = self.copy_file(srcfile,
target, preserve_mode=False)
- if copied and srcfile in self.distribution.convert_2to3.doctests:
+ doctests = self.distribution.convert_2to3_doctests
+ if copied and srcfile in doctests:
self._doctests_2to3.append(outf)
# XXX - this should be moved to the Distribution class as it is not
# only needed for build_py. It also has no dependencies on this class.
def get_package_dir(self, package):
"""Return the directory, relative to the top of the source
- distribution, where package 'package' should be found
- (at least according to the 'package_dir' option, if any)."""
-
+ distribution, where package 'package' should be found
+ (at least according to the 'package_dir' option, if any).
+ """
path = package.split('.')
if self.package_dir is not None:
path.insert(0, self.package_dir)
@@ -187,8 +193,7 @@
return ''
def check_package(self, package, package_dir):
- """Helper function for `find_package_modules()` and `find_modules()'.
- """
+ """Helper function for find_package_modules and find_modules."""
# Empty dir name means current directory, which we can probably
# assume exists. Also, os.path.exists and isdir don't know about
# my "empty string means current dir" convention, so we have to
@@ -208,8 +213,8 @@
if os.path.isfile(init_py):
return init_py
else:
- logger.warning(("package init file '%s' not found " +
- "(or not a regular file)"), init_py)
+ logger.warning("package init file %r not found "
+ "(or not a regular file)", init_py)
# Either not in a package at all (__init__.py not expected), or
# __init__.py doesn't exist -- so don't return the filename.
@@ -217,7 +222,7 @@
def check_module(self, module, module_file):
if not os.path.isfile(module_file):
- logger.warning("file %s (for module %s) not found",
+ logger.warning("file %r (for module %r) not found",
module_file, module)
return False
else:
@@ -238,7 +243,7 @@
module = os.path.splitext(os.path.basename(f))[0]
modules.append((package, module, f))
else:
- logger.debug("excluding %s", setup_script)
+ logger.debug("excluding %r", setup_script)
return modules
def find_modules(self):
@@ -331,7 +336,7 @@
if include_bytecode:
if self.compile:
outputs.append(filename + "c")
- if self.optimize > 0:
+ if self.optimize:
outputs.append(filename + "o")
outputs += [
@@ -359,7 +364,6 @@
def build_modules(self):
modules = self.find_modules()
for package, module, module_file in modules:
-
# Now "build" the module -- ie. copy the source file to
# self.build_lib (the build directory for Python source).
# (Actually, it gets copied to the directory for this package
@@ -368,7 +372,6 @@
def build_packages(self):
for package in self.packages:
-
# Get list of (package, module, module_file) tuples based on
# scanning the package directory. 'package' is only included
# in the tuple so that 'find_modules()' and
@@ -386,25 +389,3 @@
for package_, module, module_file in modules:
assert package == package_
self.build_module(module, module_file, package)
-
- def byte_compile(self, files):
- if getattr(sys, 'dont_write_bytecode', False):
- logger.warning('%s: byte-compiling is disabled, skipping.',
- self.get_command_name())
- return
-
- from distutils2.util import byte_compile # FIXME use compileall
- prefix = self.build_lib
- if prefix[-1] != os.sep:
- prefix = prefix + os.sep
-
- # XXX this code is essentially the same as the 'byte_compile()
- # method of the "install_lib" command, except for the determination
- # of the 'prefix' string. Hmmm.
-
- if self.compile:
- byte_compile(files, optimize=0,
- force=self.force, prefix=prefix, dry_run=self.dry_run)
- if self.optimize > 0:
- byte_compile(files, optimize=self.optimize,
- force=self.force, prefix=prefix, dry_run=self.dry_run)
diff --git a/distutils2/command/cmd.py b/distutils2/command/cmd.py
--- a/distutils2/command/cmd.py
+++ b/distutils2/command/cmd.py
@@ -10,7 +10,7 @@
class Command(object):
"""Abstract base class for defining command classes, the "worker bees"
- of the Packaging. A useful analogy for command classes is to think of
+ of Packaging. A useful analogy for command classes is to think of
them as subroutines with local variables called "options". The options
are "declared" in 'initialize_options()' and "defined" (given their
final values, aka "finalized") in 'finalize_options()', both of which
@@ -386,7 +386,6 @@
if self.dry_run:
return # see if we want to display something
-
return util.copy_tree(infile, outfile, preserve_mode, preserve_times,
preserve_symlinks, not self.force, dry_run=self.dry_run)
@@ -439,3 +438,20 @@
# Otherwise, print the "skip" message
else:
logger.debug(skip_msg)
+
+ def byte_compile(self, files, prefix=None):
+ """Byte-compile files to pyc and/or pyo files.
+
+ This method requires that the calling class define compile and
+ optimize options, like build_py and install_lib. It also
+ automatically respects the force and dry-run options.
+
+ prefix, if given, is a string that will be stripped off the
+ filenames encoded in bytecode files.
+ """
+ if self.compile:
+ util.byte_compile(files, optimize=False, prefix=prefix,
+ force=self.force, dry_run=self.dry_run)
+ if self.optimize:
+ util.byte_compile(files, optimize=self.optimize, prefix=prefix,
+ force=self.force, dry_run=self.dry_run)
diff --git a/distutils2/command/install_lib.py b/distutils2/command/install_lib.py
--- a/distutils2/command/install_lib.py
+++ b/distutils2/command/install_lib.py
@@ -1,8 +1,6 @@
"""Install all modules (extensions and pure Python)."""
import os
-import sys
-import logging
from distutils2 import logger
from distutils2.command.cmd import Command
@@ -10,25 +8,18 @@
# Extension for Python source files.
+# XXX dead code? most of the codebase checks for literal '.py'
if hasattr(os, 'extsep'):
PYTHON_SOURCE_EXTENSION = os.extsep + "py"
else:
PYTHON_SOURCE_EXTENSION = ".py"
+
class install_lib(Command):
description = "install all modules (extensions and pure Python)"
- # The byte-compilation options are a tad confusing. Here are the
- # possible scenarios:
- # 1) no compilation at all (--no-compile --no-optimize)
- # 2) compile .pyc only (--compile --no-optimize; default)
- # 3) compile .pyc and "level 1" .pyo (--compile --optimize)
- # 4) compile "level 1" .pyo only (--no-compile --optimize)
- # 5) compile .pyc and "level 2" .pyo (--compile --optimize-more)
- # 6) compile "level 2" .pyo only (--no-compile --optimize-more)
- #
- # The UI for this is two option, 'compile' and 'optimize'.
+ # The options for controlling byte compilation are two independent sets:
# 'compile' is strictly boolean, and only decides whether to
# generate .pyc files. 'optimize' is three-way (0, 1, or 2), and
# decides both whether to generate .pyo files and what level of
@@ -36,7 +27,7 @@
user_options = [
('install-dir=', 'd', "directory to install to"),
- ('build-dir=','b', "build directory (where to install from)"),
+ ('build-dir=', 'b', "build directory (where to install from)"),
('force', 'f', "force installation (overwrite existing files)"),
('compile', 'c', "compile .py to .pyc [default]"),
('no-compile', None, "don't compile .py files"),
@@ -47,7 +38,8 @@
]
boolean_options = ['force', 'compile', 'skip-build']
- negative_opt = {'no-compile' : 'compile'}
+
+ negative_opt = {'no-compile': 'compile'}
def initialize_options(self):
# let the 'install_dist' command dictate our installation directory
@@ -65,7 +57,8 @@
self.set_undefined_options('install_dist',
('build_lib', 'build_dir'),
('install_lib', 'install_dir'),
- 'force', 'compile', 'optimize', 'skip_build')
+ 'force', 'compile', 'optimize',
+ 'skip_build')
if self.compile is None:
self.compile = True
@@ -89,9 +82,14 @@
# having a build directory!)
outfiles = self.install()
- # (Optionally) compile .py to .pyc
+ # (Optionally) compile .py to .pyc and/or .pyo
if outfiles is not None and self.distribution.has_pure_modules():
- self.byte_compile(outfiles)
+ # XXX comment from distutils: "This [prefix stripping] is far from
+ # complete, but it should at least generate usable bytecode in RPM
+ # distributions." -> need to find exact requirements for
+ # byte-compiled files and fix it
+ install_root = self.get_finalized_command('install_dist').root
+ self.byte_compile(outfiles, prefix=install_root)
# -- Top-level worker functions ------------------------------------
# (called from 'run()')
@@ -113,38 +111,6 @@
return
return outfiles
- def byte_compile(self, files):
- if getattr(sys, 'dont_write_bytecode', False):
- # XXX do we want this? because a Python runs without bytecode
- # doesn't mean that the *dists should not contain bytecode
- #--or does it?
- logger.warning('%s: byte-compiling is disabled, skipping.',
- self.get_command_name())
- return
-
- from distutils2.util import byte_compile # FIXME use compileall
-
- # Get the "--root" directory supplied to the "install_dist" command,
- # and use it as a prefix to strip off the purported filename
- # encoded in bytecode files. This is far from complete, but it
- # should at least generate usable bytecode in RPM distributions.
- install_root = self.get_finalized_command('install_dist').root
-
- # Temporary kludge until we remove the verbose arguments and use
- # logging everywhere
- verbose = logger.getEffectiveLevel() >= logging.DEBUG
-
- if self.compile:
- byte_compile(files, optimize=0,
- force=self.force, prefix=install_root,
- dry_run=self.dry_run)
- if self.optimize > 0:
- byte_compile(files, optimize=self.optimize,
- force=self.force, prefix=install_root,
- verbose=verbose,
- dry_run=self.dry_run)
-
-
# -- Utility methods -----------------------------------------------
def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir):
@@ -173,12 +139,11 @@
continue
if self.compile:
bytecode_files.append(py_file + "c")
- if self.optimize > 0:
+ if self.optimize:
bytecode_files.append(py_file + "o")
return bytecode_files
-
# -- External interface --------------------------------------------
# (called by outsiders)
diff --git a/distutils2/errors.py b/distutils2/errors.py
--- a/distutils2/errors.py
+++ b/distutils2/errors.py
@@ -72,10 +72,6 @@
"""Syntax error in a file list template."""
-class PackagingByteCompileError(PackagingError):
- """Byte compile error."""
-
-
class PackagingPyPIError(PackagingError):
"""Any problem occuring during using the indexes."""
diff --git a/distutils2/tests/support.py b/distutils2/tests/support.py
--- a/distutils2/tests/support.py
+++ b/distutils2/tests/support.py
@@ -61,7 +61,7 @@
# misc. functions and decorators
'fake_dec', 'create_distribution', 'copy_xxmodule_c', 'fixup_build_ext',
# imported from this module for backport purposes
- 'unittest', 'requires_zlib', 'skip_unless_symlink',
+ 'unittest', 'requires_zlib', 'skip_2to3_optimize', 'skip_unless_symlink',
]
@@ -362,6 +362,10 @@
'requires test.test_support.skip_unless_symlink')
+skip_2to3_optimize = unittest.skipUnless(__debug__,
+ "2to3 doesn't work under -O")
+
+
requires_zlib = unittest.skipUnless(zlib, 'requires zlib')
diff --git a/distutils2/tests/test_command_build_py.py b/distutils2/tests/test_command_build_py.py
--- a/distutils2/tests/test_command_build_py.py
+++ b/distutils2/tests/test_command_build_py.py
@@ -54,17 +54,12 @@
# This makes sure the list of outputs includes byte-compiled
# files for Python modules but not for package data files
# (there shouldn't *be* byte-code files for those!).
- #
self.assertEqual(len(cmd.get_outputs()), 3)
pkgdest = os.path.join(destination, "pkg")
files = os.listdir(pkgdest)
self.assertIn("__init__.py", files)
self.assertIn("README.txt", files)
- # XXX even with -O, distutils writes pyc, not pyo; bug?
- if getattr(sys , 'dont_write_bytecode', False):
- self.assertNotIn("__init__.pyc", files)
- else:
- self.assertIn("__init__.pyc", files)
+ self.assertIn("__init__.pyc", files)
def test_empty_package_dir(self):
# See SF 1668596/1720897.
@@ -99,23 +94,44 @@
os.chdir(cwd)
sys.stdout = old_stdout
- @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
- 'sys.dont_write_bytecode not supported')
- def test_dont_write_bytecode(self):
- # makes sure byte_compile is not used
- pkg_dir, dist = self.create_dist()
+ def test_byte_compile(self):
+ project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
+ os.chdir(project_dir)
+ self.write_file('boiledeggs.py', 'import antigravity')
+ cmd = build_py(dist)
+ cmd.compile = True
+ cmd.build_lib = 'here'
+ cmd.finalize_options()
+ cmd.run()
+
+ found = os.listdir(cmd.build_lib)
+ self.assertEqual(sorted(found), ['boiledeggs.py', 'boiledeggs.pyc'])
+
+ def test_byte_compile_optimized(self):
+ project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
+ os.chdir(project_dir)
+ self.write_file('boiledeggs.py', 'import antigravity')
cmd = build_py(dist)
cmd.compile = True
cmd.optimize = 1
+ cmd.build_lib = 'here'
+ cmd.finalize_options()
+ cmd.run()
- old_dont_write_bytecode = sys.dont_write_bytecode
+ found = os.listdir(cmd.build_lib)
+ self.assertEqual(sorted(found),
+ ['boiledeggs.py', 'boiledeggs.pyc', 'boiledeggs.pyo'])
+
+ @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
+ 'sys.dont_write_bytecode not supported')
+ def test_byte_compile_under_B(self):
+ # make sure byte compilation works under -B (dont_write_bytecode)
+ self.addCleanup(setattr, sys, 'dont_write_bytecode',
+ sys.dont_write_bytecode)
sys.dont_write_bytecode = True
- try:
- cmd.byte_compile([])
- finally:
- sys.dont_write_bytecode = old_dont_write_bytecode
+ self.test_byte_compile()
+ self.test_byte_compile_optimized()
- self.assertIn('byte-compiling is disabled', self.get_logs()[0])
def test_suite():
return unittest.makeSuite(BuildPyTestCase)
diff --git a/distutils2/tests/test_command_install_dist.py b/distutils2/tests/test_command_install_dist.py
--- a/distutils2/tests/test_command_install_dist.py
+++ b/distutils2/tests/test_command_install_dist.py
@@ -182,9 +182,11 @@
def test_old_record(self):
# test pre-PEP 376 --record option (outside dist-info dir)
install_dir = self.mkdtemp()
- project_dir, dist = self.create_dist(scripts=['hello'])
+ project_dir, dist = self.create_dist(py_modules=['hello'],
+ scripts=['sayhi'])
os.chdir(project_dir)
- self.write_file('hello', "print 'o hai'")
+ self.write_file('hello.py', "def main(): print 'o hai'")
+ self.write_file('sayhi', 'from hello import main; main()')
cmd = install_dist(dist)
dist.command_obj['install_dist'] = cmd
@@ -200,8 +202,9 @@
f.close()
found = [os.path.basename(line) for line in content.splitlines()]
- expected = ['hello', 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
- self.assertEqual(found, expected)
+ expected = ['hello.py', 'hello.pyc', 'sayhi',
+ 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
+ self.assertEqual(sorted(found), sorted(expected))
# XXX test that fancy_getopt is okay with options named
# record and no-record but unrelated
diff --git a/distutils2/tests/test_command_install_lib.py b/distutils2/tests/test_command_install_lib.py
--- a/distutils2/tests/test_command_install_lib.py
+++ b/distutils2/tests/test_command_install_lib.py
@@ -1,6 +1,6 @@
"""Tests for distutils2.command.install_data."""
+import os
import sys
-import os
from distutils2.tests import unittest, support
from distutils2.command.install_lib import install_lib
@@ -16,7 +16,7 @@
restore_environ = ['PYTHONPATH']
def test_finalize_options(self):
- pkg_dir, dist = self.create_dist()
+ dist = self.create_dist()[1]
cmd = install_lib(dist)
cmd.finalize_options()
@@ -33,71 +33,73 @@
cmd.finalize_options()
self.assertEqual(cmd.optimize, 2)
- @unittest.skipIf(getattr(sys, 'dont_write_bytecode', False),
- 'byte-compile disabled')
def test_byte_compile(self):
- pkg_dir, dist = self.create_dist()
+ project_dir, dist = self.create_dist()
+ os.chdir(project_dir)
cmd = install_lib(dist)
cmd.compile = True
cmd.optimize = 1
- f = os.path.join(pkg_dir, 'foo.py')
+ f = os.path.join(project_dir, 'foo.py')
self.write_file(f, '# python file')
cmd.byte_compile([f])
- self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc')))
- self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo')))
+ self.assertTrue(os.path.exists(f + 'c'))
+ self.assertTrue(os.path.exists(f + 'o'))
+
+ @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
+ 'sys.dont_write_bytecode not supported')
+ def test_byte_compile_under_B(self):
+ # make sure byte compilation works under -B (dont_write_bytecode)
+ self.addCleanup(setattr, sys, 'dont_write_bytecode',
+ sys.dont_write_bytecode)
+ sys.dont_write_bytecode = True
+ self.test_byte_compile()
def test_get_outputs(self):
- pkg_dir, dist = self.create_dist()
+ project_dir, dist = self.create_dist()
+ os.chdir(project_dir)
+ os.mkdir('spam')
cmd = install_lib(dist)
# setting up a dist environment
cmd.compile = True
cmd.optimize = 1
- cmd.install_dir = pkg_dir
- f = os.path.join(pkg_dir, '__init__.py')
+ cmd.install_dir = self.mkdtemp()
+ f = os.path.join(project_dir, 'spam', '__init__.py')
self.write_file(f, '# python package')
cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
- cmd.distribution.packages = [pkg_dir]
+ cmd.distribution.packages = ['spam']
- # make sure the build_lib is set the temp dir
- build_dir = os.path.split(pkg_dir)[0]
+ # make sure the build_lib is set the temp dir # XXX what? this is not
+ # needed in the same distutils test and should work without manual
+ # intervention
+ build_dir = os.path.split(project_dir)[0]
cmd.get_finalized_command('build_py').build_lib = build_dir
- # get_output should return 4 elements
- self.assertEqual(len(cmd.get_outputs()), 4)
+ # get_outputs should return 4 elements: spam/__init__.py, .pyc and
+ # .pyo, foo.so / foo.pyd
+ outputs = cmd.get_outputs()
+ self.assertEqual(len(outputs), 4, outputs)
def test_get_inputs(self):
- pkg_dir, dist = self.create_dist()
+ project_dir, dist = self.create_dist()
+ os.chdir(project_dir)
+ os.mkdir('spam')
cmd = install_lib(dist)
# setting up a dist environment
cmd.compile = True
cmd.optimize = 1
- cmd.install_dir = pkg_dir
- f = os.path.join(pkg_dir, '__init__.py')
+ cmd.install_dir = self.mkdtemp()
+ f = os.path.join(project_dir, 'spam', '__init__.py')
self.write_file(f, '# python package')
cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
- cmd.distribution.packages = [pkg_dir]
+ cmd.distribution.packages = ['spam']
- # get_input should return 2 elements
- self.assertEqual(len(cmd.get_inputs()), 2)
-
- @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
- 'sys.dont_write_bytecode not supported')
- def test_dont_write_bytecode(self):
- # makes sure byte_compile is not used
- pkg_dir, dist = self.create_dist()
- cmd = install_lib(dist)
- cmd.compile = True
- cmd.optimize = 1
-
- self.addCleanup(setattr, sys, 'dont_write_bytecode',
- sys.dont_write_bytecode)
- sys.dont_write_bytecode = True
- cmd.byte_compile([])
-
- self.assertIn('byte-compiling is disabled', self.get_logs()[0])
+ # get_inputs should return 2 elements: spam/__init__.py and
+ # foo.so / foo.pyd
+ inputs = cmd.get_inputs()
+ self.assertEqual(len(inputs), 2, inputs)
def test_suite():
diff --git a/distutils2/tests/test_mixin2to3.py b/distutils2/tests/test_mixin2to3.py
--- a/distutils2/tests/test_mixin2to3.py
+++ b/distutils2/tests/test_mixin2to3.py
@@ -10,6 +10,7 @@
unittest.TestCase):
@unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ @support.skip_2to3_optimize
def test_convert_code_only(self):
# used to check if code gets converted properly.
code = "print 'test'"
diff --git a/distutils2/tests/test_util.py b/distutils2/tests/test_util.py
--- a/distutils2/tests/test_util.py
+++ b/distutils2/tests/test_util.py
@@ -9,7 +9,7 @@
from StringIO import StringIO
from distutils2.errors import (
- PackagingPlatformError, PackagingByteCompileError, PackagingFileError,
+ PackagingPlatformError, PackagingFileError,
PackagingExecError, InstallationException)
from distutils2 import util
from distutils2.dist import Distribution
@@ -137,15 +137,8 @@
self._uname = None
os.uname = self._get_uname
- # patching POpen
- self.old_find_executable = util.find_executable
- util.find_executable = self._find_executable
- self._exes = {}
- self.old_popen = subprocess.Popen
- self.old_stdout = sys.stdout
- self.old_stderr = sys.stderr
- FakePopen.test_class = self
- subprocess.Popen = FakePopen
+ def _get_uname(self):
+ return self._uname
def tearDown(self):
# getting back the environment
@@ -160,17 +153,24 @@
os.uname = self.uname
else:
del os.uname
+ super(UtilTestCase, self).tearDown()
+
+ def mock_popen(self):
+ self.old_find_executable = util.find_executable
+ util.find_executable = self._find_executable
+ self._exes = {}
+ self.old_popen = subprocess.Popen
+ self.old_stdout = sys.stdout
+ self.old_stderr = sys.stderr
+ FakePopen.test_class = self
+ subprocess.Popen = FakePopen
+ self.addCleanup(self.unmock_popen)
+
+ def unmock_popen(self):
util.find_executable = self.old_find_executable
subprocess.Popen = self.old_popen
- sys.old_stdout = self.old_stdout
- sys.old_stderr = self.old_stderr
- super(UtilTestCase, self).tearDown()
-
- def _set_uname(self, uname):
- self._uname = uname
-
- def _get_uname(self):
- return self._uname
+ sys.stdout = self.old_stdout
+ sys.stderr = self.old_stderr
def test_convert_path(self):
# linux/mac
@@ -282,6 +282,7 @@
return None
def test_get_compiler_versions(self):
+ self.mock_popen()
# get_versions calls distutils.spawn.find_executable on
# 'gcc', 'ld' and 'dllwrap'
self.assertEqual(get_compiler_versions(), (None, None, None))
@@ -324,15 +325,12 @@
@unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
'sys.dont_write_bytecode not supported')
- def test_dont_write_bytecode(self):
- # makes sure byte_compile raise a PackagingError
- # if sys.dont_write_bytecode is True
- old_dont_write_bytecode = sys.dont_write_bytecode
+ def test_byte_compile_under_B(self):
+ # make sure byte compilation works under -B (dont_write_bytecode)
+ self.addCleanup(setattr, sys, 'dont_write_bytecode',
+ sys.dont_write_bytecode)
sys.dont_write_bytecode = True
- try:
- self.assertRaises(PackagingByteCompileError, byte_compile, [])
- finally:
- sys.dont_write_bytecode = old_dont_write_bytecode
+ byte_compile([])
def test_newer(self):
self.assertRaises(PackagingFileError, util.newer, 'xxx', 'xxx')
@@ -420,6 +418,7 @@
self.assertRaises(ImportError, resolve_name, 'a.b.c.Spam')
@unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ @support.skip_2to3_optimize
def test_run_2to3_on_code(self):
content = "print 'test'"
converted_content = "print('test')"
@@ -434,6 +433,7 @@
self.assertEqual(new_content, converted_content)
@unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ @support.skip_2to3_optimize
def test_run_2to3_on_doctests(self):
# to check if text files containing doctests only get converted.
content = ">>> print 'test'\ntest\n"
@@ -451,8 +451,6 @@
@unittest.skipUnless(os.name in ('nt', 'posix'),
'runs only under posix or nt')
def test_spawn(self):
- # no patching of Popen here
- subprocess.Popen = self.old_popen
tmpdir = self.mkdtemp()
# creating something executable
@@ -549,8 +547,6 @@
self.assertEqual(args['py_modules'], dist.py_modules)
def test_generate_setup_py(self):
- # undo subprocess.Popen monkey-patching before using assert_python_*
- subprocess.Popen = self.old_popen
os.chdir(self.mkdtemp())
self.write_file('setup.cfg', textwrap.dedent("""\
[metadata]
diff --git a/distutils2/util.py b/distutils2/util.py
--- a/distutils2/util.py
+++ b/distutils2/util.py
@@ -24,8 +24,8 @@
from distutils2 import logger
from distutils2.errors import (PackagingPlatformError, PackagingFileError,
- PackagingByteCompileError, PackagingExecError,
- InstallationException, PackagingInternalError)
+ PackagingExecError, InstallationException,
+ PackagingInternalError)
from distutils2._backport import sysconfig
__all__ = [
@@ -301,7 +301,7 @@
def byte_compile(py_files, optimize=0, force=False, prefix=None,
- base_dir=None, verbose=0, dry_run=False, direct=None):
+ base_dir=None, dry_run=False, direct=None):
"""Byte-compile a collection of Python source files to either .pyc
or .pyo files in the same directory.
@@ -310,6 +310,9 @@
0 - don't optimize (generate .pyc)
1 - normal optimization (like "python -O")
2 - extra optimization (like "python -OO")
+ This function is independent from the running Python's -O or -B options;
+ it is fully controlled by the parameters passed in.
+
If 'force' is true, all files are recompiled regardless of
timestamps.
@@ -331,10 +334,7 @@
generated in indirect mode; unless you know what you're doing, leave
it set to None.
"""
- # nothing is done if sys.dont_write_bytecode is True
- # FIXME this should not raise an error
- if getattr(sys, 'dont_write_bytecode', False):
- raise PackagingByteCompileError('byte-compiling is disabled.')
+ # FIXME use compileall + remove direct/indirect shenanigans
# First, if the caller didn't force us into direct or indirect mode,
# figure out which mode we should be in. We take a conservative
@@ -388,17 +388,13 @@
script.write("""
byte_compile(files, optimize=%r, force=%r,
prefix=%r, base_dir=%r,
- verbose=%r, dry_run=False,
+ dry_run=False,
direct=True)
-""" % (optimize, force, prefix, base_dir, verbose))
+""" % (optimize, force, prefix, base_dir))
finally:
script.close()
cmd = [sys.executable, script_name]
- if optimize == 1:
- cmd.insert(1, "-O")
- elif optimize == 2:
- cmd.insert(1, "-OO")
env = os.environ.copy()
env['PYTHONPATH'] = os.path.pathsep.join(sys.path)
@@ -424,8 +420,9 @@
# Terminology from the py_compile module:
# cfile - byte-compiled file
# dfile - purported source filename (same as 'file' by default)
- cfile = file + (__debug__ and "c" or "o")
+ cfile = file + (optimize and 'o' or 'c')
dfile = file
+
if prefix:
if file[:len(prefix)] != prefix:
raise ValueError("invalid prefix: filename %r doesn't "
--
Repository URL: http://hg.python.org/distutils2
More information about the Python-checkins
mailing list