summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author?ric Araujo <merwok@netwok.org>2011-11-12 07:33:45 +0100
committer?ric Araujo <merwok@netwok.org>2011-11-12 07:33:45 +0100
commited71facf9298a2d12dc631f86e4ecdd33a117380 (patch)
treee31993c8b5ca3878fc6142db3fb2fea458806827
parentc00356888d4cce280d33dd86c7349bc9279a430d (diff)
parente65890db19e3b798752544632abd303625c4a7f7 (diff)
downloaddisutils2-ed71facf9298a2d12dc631f86e4ecdd33a117380.tar.gz
Ye olde merge.
I broke test_mixin2to3 somehow; distutils2-default is okay and packaging too, so I don?t see an obvious reason right now, I?ll investigate later.
-rw-r--r--CHANGES.txt9
-rw-r--r--CONTRIBUTORS.txt1
-rw-r--r--distutils2/command/bdist.py2
-rw-r--r--distutils2/command/bdist_dumb.py4
-rw-r--r--distutils2/command/bdist_msi.py8
-rw-r--r--distutils2/command/bdist_wininst.py6
-rw-r--r--distutils2/command/build.py2
-rw-r--r--distutils2/command/build_py.py77
-rw-r--r--distutils2/command/cmd.py24
-rw-r--r--distutils2/command/install_dist.py4
-rw-r--r--distutils2/command/install_distinfo.py25
-rw-r--r--distutils2/command/install_lib.py72
-rw-r--r--distutils2/command/register.py2
-rw-r--r--distutils2/command/test.py2
-rw-r--r--distutils2/compat.py17
-rw-r--r--distutils2/compiler/bcppcompiler.py2
-rw-r--r--distutils2/compiler/cygwinccompiler.py25
-rw-r--r--distutils2/compiler/msvc9compiler.py2
-rw-r--r--distutils2/compiler/msvccompiler.py2
-rw-r--r--distutils2/create.py34
-rw-r--r--distutils2/depgraph.py5
-rw-r--r--distutils2/dist.py25
-rw-r--r--distutils2/errors.py4
-rw-r--r--distutils2/install.py9
-rw-r--r--distutils2/manifest.py4
-rw-r--r--distutils2/metadata.py12
-rw-r--r--distutils2/run.py5
-rw-r--r--distutils2/tests/__main__.py1
-rw-r--r--distutils2/tests/pypi_server.py12
-rw-r--r--distutils2/tests/support.py83
-rw-r--r--distutils2/tests/test_command_bdist_dumb.py23
-rw-r--r--distutils2/tests/test_command_build_ext.py33
-rw-r--r--distutils2/tests/test_command_build_py.py96
-rw-r--r--distutils2/tests/test_command_check.py45
-rw-r--r--distutils2/tests/test_command_clean.py9
-rw-r--r--distutils2/tests/test_command_cmd.py3
-rw-r--r--distutils2/tests/test_command_install_data.py12
-rw-r--r--distutils2/tests/test_command_install_dist.py25
-rw-r--r--distutils2/tests/test_command_install_distinfo.py11
-rw-r--r--distutils2/tests/test_command_install_lib.py72
-rw-r--r--distutils2/tests/test_command_register.py28
-rw-r--r--distutils2/tests/test_command_sdist.py72
-rw-r--r--distutils2/tests/test_command_test.py6
-rw-r--r--distutils2/tests/test_command_upload.py2
-rw-r--r--distutils2/tests/test_command_upload_docs.py15
-rw-r--r--distutils2/tests/test_config.py32
-rw-r--r--distutils2/tests/test_create.py34
-rw-r--r--distutils2/tests/test_dist.py31
-rw-r--r--distutils2/tests/test_manifest.py204
-rw-r--r--distutils2/tests/test_metadata.py5
-rw-r--r--distutils2/tests/test_mixin2to3.py2
-rw-r--r--distutils2/tests/test_pypi_simple.py2
-rw-r--r--distutils2/tests/test_run.py4
-rw-r--r--distutils2/tests/test_uninstall.py17
-rw-r--r--distutils2/tests/test_util.py165
-rw-r--r--distutils2/tests/test_version.py4
-rw-r--r--distutils2/util.py64
57 files changed, 822 insertions, 674 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 5d42e27..d29edc3 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -148,6 +148,15 @@ CONTRIBUTORS.txt for full names. Bug numbers refer to http://bugs.python.org/.
- #13170: Revert one of Jeremy's changes to config to fix a bug, kludge around
shlex not supporting unicode in 2.x, fix wrong shutil import [david, éric]
- #13205: Fix and improve generated setup scripts [david, éric]
+- #12386: Fix writing of the RESOURCES file [éric]
+- #11751: Improve test coverage for manifest [justin]
+- Byte compilation is now isolated from the calling Python -B or -O options
+ [éric]
+- The signature of tests.support.LoggingCatcher.get_logs changed, see
+ docstring [éric]
+- Rename get_reinitialized_command back to reinitialize_command [éric]
+- Rename install_distinfo's option from distinfo-dir to the more usual
+ install_dir [éric]
1.0a3 - 2010-10-08
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 9e6ef84..a777fd6 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -46,6 +46,7 @@ Thanks to:
- Alain Leufroy
- Martin von Löwis
- Hugo Lopes Tavares
+- Justin Love
- Simon Mathieu
- Carl Meyer
- Alexis Métaireau
diff --git a/distutils2/command/bdist.py b/distutils2/command/bdist.py
index 0a39fb1..583041e 100644
--- a/distutils2/command/bdist.py
+++ b/distutils2/command/bdist.py
@@ -126,7 +126,7 @@ class bdist(Command):
# Reinitialize and run each command.
for i in range(len(self.formats)):
cmd_name = commands[i]
- sub_cmd = self.get_reinitialized_command(cmd_name)
+ sub_cmd = self.reinitialize_command(cmd_name)
sub_cmd.format = self.formats[i]
# passing the owner and group names for tar archiving
diff --git a/distutils2/command/bdist_dumb.py b/distutils2/command/bdist_dumb.py
index 677deb9..dfe80b9 100644
--- a/distutils2/command/bdist_dumb.py
+++ b/distutils2/command/bdist_dumb.py
@@ -80,8 +80,8 @@ class bdist_dumb(Command):
if not self.skip_build:
self.run_command('build')
- install = self.get_reinitialized_command('install_dist',
- reinit_subcommands=True)
+ install = self.reinitialize_command('install_dist',
+ reinit_subcommands=True)
install.root = self.bdist_dir
install.skip_build = self.skip_build
install.warn_dir = False
diff --git a/distutils2/command/bdist_msi.py b/distutils2/command/bdist_msi.py
index 40e733f..704e695 100644
--- a/distutils2/command/bdist_msi.py
+++ b/distutils2/command/bdist_msi.py
@@ -35,7 +35,7 @@ class PyDialog(Dialog):
def __init__(self, *args, **kw):
"""Dialog(database, name, x, y, w, h, attributes, title, first,
default, cancel, bitmap=true)"""
- Dialog.__init__(self, *args)
+ super(PyDialog, self).__init__(*args)
ruler = self.h - 36
#if kw.get("bitmap", True):
# self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
@@ -183,13 +183,13 @@ class bdist_msi(Command):
if not self.skip_build:
self.run_command('build')
- install = self.get_reinitialized_command('install_dist',
- reinit_subcommands=True)
+ install = self.reinitialize_command('install_dist',
+ reinit_subcommands=True)
install.prefix = self.bdist_dir
install.skip_build = self.skip_build
install.warn_dir = False
- install_lib = self.get_reinitialized_command('install_lib')
+ install_lib = self.reinitialize_command('install_lib')
# we do not want to include pyc or pyo files
install_lib.compile = False
install_lib.optimize = 0
diff --git a/distutils2/command/bdist_wininst.py b/distutils2/command/bdist_wininst.py
index e4cde7a..fcd0955 100644
--- a/distutils2/command/bdist_wininst.py
+++ b/distutils2/command/bdist_wininst.py
@@ -2,7 +2,6 @@
import sys
import os
-import codecs
from shutil import rmtree
@@ -117,14 +116,13 @@ class bdist_wininst(Command):
if not self.skip_build:
self.run_command('build')
- install = self.get_reinitialized_command('install',
- reinit_subcommands=True)
+ install = self.reinitialize_command('install', reinit_subcommands=True)
install.root = self.bdist_dir
install.skip_build = self.skip_build
install.warn_dir = False
install.plat_name = self.plat_name
- install_lib = self.get_reinitialized_command('install_lib')
+ install_lib = self.reinitialize_command('install_lib')
# we do not want to include pyc or pyo files
install_lib.compile = False
install_lib.optimize = 0
diff --git a/distutils2/command/build.py b/distutils2/command/build.py
index b2e5bf8..708b848 100644
--- a/distutils2/command/build.py
+++ b/distutils2/command/build.py
@@ -41,7 +41,7 @@ class build(Command):
('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"),
]
diff --git a/distutils2/command/build_py.py b/distutils2/command/build_py.py
index e27e271..f9b9bcd 100644
--- a/distutils2/command/build_py.py
+++ b/distutils2/command/build_py.py
@@ -1,22 +1,25 @@
"""Build pure Python modules (just copy to build directory)."""
import os
-import sys
from glob import glob
from distutils2 import logger
-from distutils2.command.cmd import Command
-from distutils2.errors import PackagingOptionError, PackagingFileError
from distutils2.util import convert_path
-from distutils2.compat import Mixin2to3
+from distutils2.compat import Mixin2to3, cache_from_source
+from distutils2.errors import PackagingOptionError, PackagingFileError
+from distutils2.command.cmd import Command
# 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 @@ class build_py(Command, Mixin2to3):
('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 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
# 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 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
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):
@@ -330,9 +335,9 @@ class build_py(Command, Mixin2to3):
outputs.append(filename)
if include_bytecode:
if self.compile:
- outputs.append(filename + "c")
- if self.optimize > 0:
- outputs.append(filename + "o")
+ outputs.append(cache_from_source(filename, True))
+ if self.optimize:
+ outputs.append(cache_from_source(filename, False))
outputs += [
os.path.join(build_dir, filename)
@@ -359,7 +364,6 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
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 @@ class build_py(Command, Mixin2to3):
for package_, module, module_file in modules:
assert package == package_
self.build_module(module, module_file, package)
-
- def byte_compile(self, files):
- if sys.dont_write_bytecode:
- 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
index 6d76e7f..956623d 100644
--- a/distutils2/command/cmd.py
+++ b/distutils2/command/cmd.py
@@ -10,7 +10,7 @@ from distutils2._backport.shutil import copyfile, move, make_archive
class Command:
"""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
@@ -318,8 +318,8 @@ class Command:
cmd_obj.ensure_finalized()
return cmd_obj
- def get_reinitialized_command(self, command, reinit_subcommands=False):
- return self.distribution.get_reinitialized_command(
+ def reinitialize_command(self, command, reinit_subcommands=False):
+ return self.distribution.reinitialize_command(
command, reinit_subcommands)
def run_command(self, command):
@@ -386,7 +386,6 @@ class Command:
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 @@ class Command:
# 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_dist.py b/distutils2/command/install_dist.py
index 159ccc2..8d8b675 100644
--- a/distutils2/command/install_dist.py
+++ b/distutils2/command/install_dist.py
@@ -55,9 +55,7 @@ class install_dist(Command):
('install-data=', None,
"installation directory for data files"),
- # Byte-compilation options -- see install_lib.py for details, as
- # these are duplicated from there (but only install_lib does
- # anything with them).
+ # Byte-compilation options -- see install_lib for details
('compile', 'c', "compile .py to .pyc [default]"),
('no-compile', None, "don't compile .py files"),
('optimize=', 'O',
diff --git a/distutils2/command/install_distinfo.py b/distutils2/command/install_distinfo.py
index 167844c..d495bc2 100644
--- a/distutils2/command/install_distinfo.py
+++ b/distutils2/command/install_distinfo.py
@@ -16,8 +16,8 @@ class install_distinfo(Command):
description = 'create a .dist-info directory for the distribution'
user_options = [
- ('distinfo-dir=', None,
- "directory where the the .dist-info directory will be installed"),
+ ('install-dir=', None,
+ "directory where the the .dist-info directory will be created"),
('installer=', None,
"the name of the installer"),
('requested', None,
@@ -35,7 +35,7 @@ class install_distinfo(Command):
negative_opt = {'no-requested': 'requested'}
def initialize_options(self):
- self.distinfo_dir = None
+ self.install_dir = None
self.installer = None
self.requested = None
self.no_record = None
@@ -46,8 +46,7 @@ class install_distinfo(Command):
self.set_undefined_options('install_dist',
'installer', 'requested', 'no_record')
- self.set_undefined_options('install_lib',
- ('install_dir', 'distinfo_dir'))
+ self.set_undefined_options('install_lib', 'install_dir')
if self.installer is None:
# FIXME distutils or packaging or distutils2?
@@ -64,26 +63,26 @@ class install_distinfo(Command):
basename = metadata.get_fullname(filesafe=True) + ".dist-info"
- self.distinfo_dir = os.path.join(self.distinfo_dir, basename)
+ self.install_dir = os.path.join(self.install_dir, basename)
def run(self):
- target = self.distinfo_dir
+ target = self.install_dir
if os.path.isdir(target) and not os.path.islink(target):
if not self.dry_run:
rmtree(target)
elif os.path.exists(target):
- self.execute(os.unlink, (self.distinfo_dir,),
+ self.execute(os.unlink, (self.install_dir,),
"removing " + target)
self.execute(os.makedirs, (target,), "creating " + target)
- metadata_path = os.path.join(self.distinfo_dir, 'METADATA')
+ metadata_path = os.path.join(self.install_dir, 'METADATA')
self.execute(self.distribution.metadata.write, (metadata_path,),
"creating " + metadata_path)
self.outfiles.append(metadata_path)
- installer_path = os.path.join(self.distinfo_dir, 'INSTALLER')
+ installer_path = os.path.join(self.install_dir, 'INSTALLER')
logger.info('creating %s', installer_path)
if not self.dry_run:
with open(installer_path, 'w') as f:
@@ -91,7 +90,7 @@ class install_distinfo(Command):
self.outfiles.append(installer_path)
if self.requested:
- requested_path = os.path.join(self.distinfo_dir, 'REQUESTED')
+ requested_path = os.path.join(self.install_dir, 'REQUESTED')
logger.info('creating %s', requested_path)
if not self.dry_run:
open(requested_path, 'wb').close()
@@ -100,7 +99,7 @@ class install_distinfo(Command):
if not self.no_resources:
install_data = self.get_finalized_command('install_data')
if install_data.get_resources_out() != []:
- resources_path = os.path.join(self.distinfo_dir,
+ resources_path = os.path.join(self.install_dir,
'RESOURCES')
logger.info('creating %s', resources_path)
if not self.dry_run:
@@ -114,7 +113,7 @@ class install_distinfo(Command):
self.outfiles.append(resources_path)
if not self.no_record:
- record_path = os.path.join(self.distinfo_dir, 'RECORD')
+ record_path = os.path.join(self.install_dir, 'RECORD')
logger.info('creating %s', record_path)
if not self.dry_run:
with open(record_path, 'w', encoding='utf-8') as f:
diff --git a/distutils2/command/install_lib.py b/distutils2/command/install_lib.py
index ea53eab..8ee1fd5 100644
--- a/distutils2/command/install_lib.py
+++ b/distutils2/command/install_lib.py
@@ -1,34 +1,26 @@
"""Install all modules (extensions and pure Python)."""
import os
-import sys
-import logging
from distutils2 import logger
+from distutils2.compat import cache_from_source
from distutils2.command.cmd import Command
from distutils2.errors import PackagingOptionError
# 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 +28,7 @@ class install_lib(Command):
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 +39,8 @@ class install_lib(Command):
]
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 +58,8 @@ class install_lib(Command):
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 +83,14 @@ class install_lib(Command):
# 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 +112,6 @@ class install_lib(Command):
return
return outfiles
- def byte_compile(self, files):
- if sys.dont_write_bytecode:
- # 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):
@@ -172,13 +139,12 @@ class install_lib(Command):
if ext != PYTHON_SOURCE_EXTENSION:
continue
if self.compile:
- bytecode_files.append(py_file + "c")
- if self.optimize > 0:
- bytecode_files.append(py_file + "o")
+ bytecode_files.append(cache_from_source(py_file, True))
+ if self.optimize:
+ bytecode_files.append(cache_from_source(py_file, False))
return bytecode_files
-
# -- External interface --------------------------------------------
# (called by outsiders)
diff --git a/distutils2/command/register.py b/distutils2/command/register.py
index 79ec7b3..091918c 100644
--- a/distutils2/command/register.py
+++ b/distutils2/command/register.py
@@ -178,7 +178,7 @@ Your selection [default 1]: ''')
'will be faster.\n(the login will be stored in %s)',
get_pypirc_path())
choice = 'X'
- while choice.lower() not in 'yn':
+ while choice.lower() not in ('y', 'n'):
choice = input('Save your login (y/N)?')
if not choice:
choice = 'n'
diff --git a/distutils2/command/test.py b/distutils2/command/test.py
index da5d141..09af3b6 100644
--- a/distutils2/command/test.py
+++ b/distutils2/command/test.py
@@ -56,7 +56,7 @@ class test(Command):
prev_syspath = sys.path[:]
try:
# build release
- build = self.get_reinitialized_command('build')
+ build = self.reinitialize_command('build')
self.run_command('build')
sys.path.insert(0, build.build_lib)
diff --git a/distutils2/compat.py b/distutils2/compat.py
index f7ac7a7..3927ea0 100644
--- a/distutils2/compat.py
+++ b/distutils2/compat.py
@@ -68,3 +68,20 @@ def fsencode(filename):
else:
raise TypeError("expect bytes or str, not %s" %
type(filename).__name__)
+
+
+try:
+ callable = callable
+except NameError:
+ from collections import Callable
+
+ def callable(obj):
+ return isinstance(obj, Callable)
+
+
+try:
+ from imp import cache_from_source
+except ImportError:
+ def cache_from_source(py_file, debug=__debug__):
+ ext = debug and 'c' or 'o'
+ return py_file + ext
diff --git a/distutils2/compiler/bcppcompiler.py b/distutils2/compiler/bcppcompiler.py
index 972c0f4..98855f8 100644
--- a/distutils2/compiler/bcppcompiler.py
+++ b/distutils2/compiler/bcppcompiler.py
@@ -48,7 +48,7 @@ class BCPPCompiler(CCompiler) :
def __init__(self, verbose=0, dry_run=False, force=False):
- CCompiler.__init__(self, verbose, dry_run, force)
+ super(BCPPCompiler, self).__init__(verbose, dry_run, force)
# These executables are assumed to all be in the path.
# Borland doesn't seem to use any special registry settings to
diff --git a/distutils2/compiler/cygwinccompiler.py b/distutils2/compiler/cygwinccompiler.py
index 4b0b786..720d1d6 100644
--- a/distutils2/compiler/cygwinccompiler.py
+++ b/distutils2/compiler/cygwinccompiler.py
@@ -93,8 +93,7 @@ class CygwinCCompiler(UnixCCompiler):
exe_extension = ".exe"
def __init__(self, verbose=0, dry_run=False, force=False):
-
- UnixCCompiler.__init__(self, verbose, dry_run, force)
+ super(CygwinCCompiler, self).__init__(verbose, dry_run, force)
status, details = check_config_h()
logger.debug("Python's GCC status: %s (details: %s)", status, details)
@@ -234,12 +233,11 @@ class CygwinCCompiler(UnixCCompiler):
if not debug:
extra_preargs.append("-s")
- UnixCCompiler.link(self, target_desc, objects, output_filename,
- output_dir, libraries, library_dirs,
- runtime_library_dirs,
- None, # export_symbols, we do this in our def-file
- debug, extra_preargs, extra_postargs, build_temp,
- target_lang)
+ super(CygwinCCompiler, self).link(
+ target_desc, objects, output_filename, output_dir, libraries,
+ library_dirs, runtime_library_dirs,
+ None, # export_symbols, we do this in our def-file
+ debug, extra_preargs, extra_postargs, build_temp, target_lang)
# -- Miscellaneous methods -----------------------------------------
@@ -255,14 +253,14 @@ class CygwinCCompiler(UnixCCompiler):
if ext not in (self.src_extensions + ['.rc','.res']):
raise UnknownFileError("unknown file type '%s' (from '%s')" % (ext, src_name))
if strip_dir:
- base = os.path.basename (base)
+ base = os.path.basename(base)
if ext in ('.res', '.rc'):
# these need to be compiled to object files
- obj_names.append (os.path.join(output_dir,
+ obj_names.append(os.path.join(output_dir,
base + ext + self.obj_extension))
else:
- obj_names.append (os.path.join(output_dir,
- base + self.obj_extension))
+ obj_names.append(os.path.join(output_dir,
+ base + self.obj_extension))
return obj_names
# the same as cygwin plus some additional parameters
@@ -273,8 +271,7 @@ class Mingw32CCompiler(CygwinCCompiler):
description = 'MinGW32 compiler'
def __init__(self, verbose=0, dry_run=False, force=False):
-
- CygwinCCompiler.__init__ (self, verbose, dry_run, force)
+ super(Mingw32CCompiler, self).__init__(verbose, dry_run, force)
# ld_version >= "2.13" support -shared so use it instead of
# -mdll -static
diff --git a/distutils2/compiler/msvc9compiler.py b/distutils2/compiler/msvc9compiler.py
index fb53d7d..0a86782 100644
--- a/distutils2/compiler/msvc9compiler.py
+++ b/distutils2/compiler/msvc9compiler.py
@@ -310,7 +310,7 @@ class MSVCCompiler(CCompiler) :
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=False, force=False):
- CCompiler.__init__(self, verbose, dry_run, force)
+ super(MSVCCompiler, self).__init__(verbose, dry_run, force)
self.__version = VERSION
self.__root = r"Software\Microsoft\VisualStudio"
# self.__macros = MACROS
diff --git a/distutils2/compiler/msvccompiler.py b/distutils2/compiler/msvccompiler.py
index 7a0d313..459cb02 100644
--- a/distutils2/compiler/msvccompiler.py
+++ b/distutils2/compiler/msvccompiler.py
@@ -237,7 +237,7 @@ class MSVCCompiler(CCompiler):
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=False, force=False):
- CCompiler.__init__(self, verbose, dry_run, force)
+ super(MSVCCompiler, self).__init__(verbose, dry_run, force)
self.__version = get_build_version()
self.__arch = get_build_architecture()
if self.__arch == "Intel":
diff --git a/distutils2/create.py b/distutils2/create.py
index 139cf6a..36b6b82 100644
--- a/distutils2/create.py
+++ b/distutils2/create.py
@@ -29,6 +29,7 @@ from textwrap import dedent
from tokenize import detect_encoding
from configparser import RawConfigParser
+from distutils2 import logger
# importing this with an underscore as it should be replaced by the
# dict form or another structures for all purposes
from distutils2._trove import all_classifiers as _CLASSIFIERS_LIST
@@ -125,7 +126,7 @@ def ask_yn(question, default=None, helptext=None):
if answer and answer[0].lower() in ('y', 'n'):
return answer[0].lower()
- print('\nERROR: You must select "Y" or "N".\n')
+ logger.error('You must select "Y" or "N".')
# XXX use util.ask
@@ -148,10 +149,7 @@ def ask(question, default=None, helptext=None, required=True,
helptext = helptext.strip("\n")
while True:
- sys.stdout.write(prompt)
- sys.stdout.flush()
-
- line = sys.stdin.readline().strip()
+ line = input(prompt).strip()
if line == '?':
print('=' * 70)
print(helptext)
@@ -272,9 +270,10 @@ class MainProgram:
def _write_cfg(self):
if os.path.exists(_FILENAME):
if os.path.exists('%s.old' % _FILENAME):
- print("ERROR: %(name)s.old backup exists, please check that "
- "current %(name)s is correct and remove %(name)s.old" %
- {'name': _FILENAME})
+ message = ("ERROR: %(name)s.old backup exists, please check "
+ "that current %(name)s is correct and remove "
+ "%(name)s.old" % {'name': _FILENAME})
+ logger.error(message)
return
shutil.move(_FILENAME, '%s.old' % _FILENAME)
@@ -321,7 +320,7 @@ class MainProgram:
fp.write('\n')
os.chmod(_FILENAME, 0o644)
- print('Wrote "%s".' % _FILENAME)
+ logger.info('Wrote "%s".' % _FILENAME)
def convert_py_to_cfg(self):
"""Generate a setup.cfg from an existing setup.py.
@@ -351,7 +350,6 @@ class MainProgram:
('long_description', 'description'),
('url', 'home_page'),
('platforms', 'platform'),
- # backport only for 2.5+
('provides', 'provides-dist'),
('obsoletes', 'obsoletes-dist'),
('requires', 'requires-dist'))
@@ -615,8 +613,8 @@ class MainProgram:
break
if len(found_list) == 0:
- print('ERROR: Could not find a matching license for "%s"' %
- license)
+ logger.error('Could not find a matching license for "%s"' %
+ license)
continue
question = 'Matching licenses:\n\n'
@@ -637,8 +635,8 @@ class MainProgram:
try:
index = found_list[int(choice) - 1]
except ValueError:
- print("ERROR: Invalid selection, type a number from the list "
- "above.")
+ logger.error(
+ "Invalid selection, type a number from the list above.")
classifiers.add(_CLASSIFIERS_LIST[index])
@@ -661,8 +659,8 @@ class MainProgram:
classifiers.add(key)
return
except (IndexError, ValueError):
- print("ERROR: Invalid selection, type a single digit "
- "number.")
+ logger.error(
+ "Invalid selection, type a single digit number.")
def main():
@@ -676,7 +674,3 @@ def main():
# program.write_setup_script()
# distutils2.util.cfg_to_args()
program()
-
-
-if __name__ == '__main__':
- main()
diff --git a/distutils2/depgraph.py b/distutils2/depgraph.py
index 5fc9bc2..f03ca58 100644
--- a/distutils2/depgraph.py
+++ b/distutils2/depgraph.py
@@ -224,6 +224,7 @@ def dependent_dists(dists, dist):
def main():
+ # XXX move to run._graph
from distutils2.database import get_distributions
tempout = StringIO()
try:
@@ -267,7 +268,3 @@ def main():
else:
print('Supported option: -d [filename]')
sys.exit(1)
-
-
-if __name__ == '__main__':
- main()
diff --git a/distutils2/dist.py b/distutils2/dist.py
index e2a4266..a17c950 100644
--- a/distutils2/dist.py
+++ b/distutils2/dist.py
@@ -5,9 +5,10 @@ import re
from distutils2 import logger
from distutils2.util import strtobool, resolve_name
+from distutils2.compat import callable
+from distutils2.config import Config
from distutils2.errors import (PackagingOptionError, PackagingArgError,
PackagingModuleError, PackagingClassError)
-from distutils2.config import Config
from distutils2.command import get_command_class, STANDARD_COMMANDS
from distutils2.command.cmd import Command
from distutils2.metadata import Metadata
@@ -69,7 +70,7 @@ Common commands: (see '--help-commands' for more)
('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"),
]
display_option_names = [x[0].replace('-', '_') for x in display_options]
@@ -409,13 +410,12 @@ Common commands: (see '--help-commands' for more)
for help_option, short, desc, func in cmd_class.help_options:
if hasattr(opts, help_option.replace('-', '_')):
help_option_found = True
- if hasattr(func, '__call__'):
- func()
- else:
+ if not callable(func):
raise PackagingClassError(
"invalid help function %r for help option %r: "
"must be a callable object (function, etc.)"
% (func, help_option))
+ func()
if help_option_found:
return
@@ -636,9 +636,9 @@ Common commands: (see '--help-commands' for more)
except ValueError as msg:
raise PackagingOptionError(msg)
- def get_reinitialized_command(self, command, reinit_subcommands=False):
+ def reinitialize_command(self, command, reinit_subcommands=False):
"""Reinitializes a command to the state it was in when first
- returned by 'get_command_obj()': ie., initialized but not yet
+ returned by 'get_command_obj()': i.e., initialized but not yet
finalized. This provides the opportunity to sneak option
values in programmatically, overriding or supplementing
user-supplied values from the config files and command line.
@@ -650,10 +650,11 @@ Common commands: (see '--help-commands' for more)
'reinit_subcommands' is true, also reinitializes the command's
sub-commands, as declared by the 'sub_commands' class attribute (if
it has one). See the "install_dist" command for an example. Only
- reinitializes the sub-commands that actually matter, ie. those
- whose test predicates return true.
+ reinitializes the sub-commands that actually matter, i.e. those
+ whose test predicate return true.
- Returns the reinitialized command object.
+ Returns the reinitialized command object. It will be the same
+ object as the one stored in the self.command_obj attribute.
"""
if not isinstance(command, Command):
command_name = command
@@ -671,7 +672,7 @@ Common commands: (see '--help-commands' for more)
if reinit_subcommands:
for sub in command.get_sub_commands():
- self.get_reinitialized_command(sub, reinit_subcommands)
+ self.reinitialize_command(sub, reinit_subcommands)
return command
@@ -733,7 +734,7 @@ Common commands: (see '--help-commands' for more)
else:
hook_obj = hook
- if not hasattr(hook_obj, '__call__'):
+ if not callable(hook_obj):
raise PackagingOptionError('hook %r is not callable' % hook)
logger.info('running %s %s for command %s',
diff --git a/distutils2/errors.py b/distutils2/errors.py
index cf58d09..e99116c 100644
--- a/distutils2/errors.py
+++ b/distutils2/errors.py
@@ -72,10 +72,6 @@ class PackagingTemplateError(PackagingError):
"""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/install.py b/distutils2/install.py
index 832f5db..01f9090 100644
--- a/distutils2/install.py
+++ b/distutils2/install.py
@@ -528,12 +528,3 @@ def install(project):
logger.info('%r conflicts with %s', project, ','.join(projects))
return True
-
-
-def _main(**attrs):
- if 'script_args' not in attrs:
- attrs['requirements'] = sys.argv[1]
- get_infos(**attrs)
-
-if __name__ == '__main__':
- _main()
diff --git a/distutils2/manifest.py b/distutils2/manifest.py
index 64e8ebf..5c6c511 100644
--- a/distutils2/manifest.py
+++ b/distutils2/manifest.py
@@ -147,7 +147,9 @@ class Manifest:
def _parse_template_line(self, line):
words = line.split()
- if len(words) == 1:
+ if len(words) == 1 and words[0] not in (
+ 'include', 'exclude', 'global-include', 'global-exclude',
+ 'recursive-include', 'recursive-exclude', 'graft', 'prune'):
# no action given, let's use the default 'include'
words.insert(0, 'include')
diff --git a/distutils2/metadata.py b/distutils2/metadata.py
index 8db86e2..a08322a 100644
--- a/distutils2/metadata.py
+++ b/distutils2/metadata.py
@@ -28,8 +28,9 @@ try:
def __init__(self, source, report_level, halt_level, stream=None,
debug=0, encoding='ascii', error_handler='replace'):
self.messages = []
- Reporter.__init__(self, source, report_level, halt_level, stream,
- debug, encoding, error_handler)
+ super(SilentReporter, self).__init__(
+ source, report_level, halt_level, stream,
+ debug, encoding, error_handler)
def system_message(self, level, message, *children, **kwargs):
self.messages.append((level, message, children, kwargs))
@@ -184,6 +185,7 @@ _MISSING = object()
_FILESAFE = re.compile('[^A-Za-z0-9.]+')
+
class Metadata:
"""The metadata of a release.
@@ -227,10 +229,8 @@ class Metadata:
def __delitem__(self, name):
field_name = self._convert_name(name)
- try:
- del self._fields[field_name]
- except KeyError:
- raise KeyError(name)
+ # we let a KeyError propagate
+ del self._fields[field_name]
self._set_best_version()
def __contains__(self, name):
diff --git a/distutils2/run.py b/distutils2/run.py
index bebf070..d2f1eb6 100644
--- a/distutils2/run.py
+++ b/distutils2/run.py
@@ -9,6 +9,7 @@ import logging
from distutils2 import logger
from distutils2.dist import Distribution
from distutils2.util import _is_archive_file, generate_setup_py
+from distutils2.compat import callable
from distutils2.command import get_command_class, STANDARD_COMMANDS
from distutils2.install import install, install_local_project, remove
from distutils2.database import get_distribution, get_distributions
@@ -368,7 +369,7 @@ actions = [
('list', 'List installed projects', _list),
('graph', 'Display a graph', _graph),
('create', 'Create a project', _create),
- ('generate-setup', 'Generate a backward-comptatible setup.py', _generate),
+ ('generate-setup', 'Generate a backward-compatible setup.py', _generate),
]
@@ -500,7 +501,7 @@ class Dispatcher:
for help_option, short, desc, func in cmd_class.help_options:
if hasattr(opts, help_option.replace('-', '_')):
help_option_found = True
- if hasattr(func, '__call__'):
+ if callable(func):
func()
else:
raise PackagingClassError(
diff --git a/distutils2/tests/__main__.py b/distutils2/tests/__main__.py
index 3609c64..6ee5434 100644
--- a/distutils2/tests/__main__.py
+++ b/distutils2/tests/__main__.py
@@ -3,7 +3,6 @@
# Ripped from importlib tests, thanks Brett!
import os
-import sys
from test.support import reap_children, reap_threads, run_unittest
from distutils2.tests import unittest
diff --git a/distutils2/tests/pypi_server.py b/distutils2/tests/pypi_server.py
index 710e055..6e77e40 100644
--- a/distutils2/tests/pypi_server.py
+++ b/distutils2/tests/pypi_server.py
@@ -33,7 +33,6 @@ import os
import queue
import select
import threading
-import socketserver
from functools import wraps
from http.server import HTTPServer, SimpleHTTPRequestHandler
from xmlrpc.server import SimpleXMLRPCServer
@@ -103,7 +102,7 @@ class PyPIServer(threading.Thread):
"""
# we want to launch the server in a new dedicated thread, to not freeze
# tests.
- threading.Thread.__init__(self)
+ super(PyPIServer, self).__init__()
self._run = True
self._serve_xmlrpc = serve_xmlrpc
if static_filesystem_paths is None:
@@ -270,7 +269,7 @@ class PyPIRequestHandler(SimpleHTTPRequestHandler):
class PyPIXMLRPCServer(SimpleXMLRPCServer):
def server_bind(self):
"""Override server_bind to store the server name."""
- socketserver.TCPServer.server_bind(self)
+ super(PyPIXMLRPCServer, self).server_bind()
host, port = self.socket.getsockname()[:2]
self.server_port = port
@@ -371,12 +370,13 @@ class MockDist:
'requires_python': self.requires_python,
'classifiers': [],
'name': self.name,
- 'licence': self.licence,
+ 'licence': self.licence, # XXX licence or license?
'summary': self.summary,
'home_page': self.homepage,
'stable_version': self.stable_version,
- 'provides_dist': self.provides_dist or "%s (%s)" % (self.name,
- self.version),
+ # FIXME doesn't that reproduce the bug from 6527d3106e9f?
+ 'provides_dist': (self.provides_dist or
+ "%s (%s)" % (self.name, self.version)),
'requires': self.requires,
'cheesecake_installability_id': self.cheesecake_installability_id,
}
diff --git a/distutils2/tests/support.py b/distutils2/tests/support.py
index 961780d..cacda42 100644
--- a/distutils2/tests/support.py
+++ b/distutils2/tests/support.py
@@ -36,7 +36,6 @@ import os
import re
import sys
import errno
-import codecs
import shutil
import logging
import logging.handlers
@@ -49,6 +48,9 @@ except ImportError:
zlib = None
from distutils2.dist import Distribution
+from distutils2.util import resolve_name
+from distutils2.command import set_command, _COMMANDS
+
from distutils2.tests import unittest
from distutils2._backport import sysconfig
@@ -57,11 +59,12 @@ __all__ = [
# TestCase mixins
'LoggingCatcher', 'TempdirManager', 'EnvironRestorer',
# mocks
- 'DummyCommand', 'TestDistribution',
+ 'DummyCommand', 'TestDistribution', 'Inputs',
# misc. functions and decorators
- 'fake_dec', 'create_distribution', 'copy_xxmodule_c', 'fixup_build_ext',
+ 'fake_dec', 'create_distribution', 'use_command',
+ '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',
]
@@ -73,7 +76,7 @@ class _TestHandler(logging.handlers.BufferingHandler):
# stolen and adapted from test.support
def __init__(self):
- logging.handlers.BufferingHandler.__init__(self, 0)
+ super(_TestHandler, self).__init__(0)
self.setLevel(logging.DEBUG)
def shouldFlush(self):
@@ -90,10 +93,13 @@ class LoggingCatcher:
configured to record all messages logged to the 'distutils2' logger.
Use get_logs to retrieve messages and self.loghandler.flush to discard
- them. get_logs automatically flushes the logs; if you test code that
- generates logging messages but don't use get_logs, you have to flush
- manually before doing other checks on logging message, otherwise you
- will get irrelevant results. See example in test_command_check.
+ them. get_logs automatically flushes the logs, unless you pass
+ *flush=False*, for example to make multiple calls to the method with
+ different level arguments. If your test calls some code that generates
+ logging message and then you don't call get_logs, you will need to flush
+ manually before testing other code in the same test_* method, otherwise
+ get_logs in the next lines will see messages from the previous lines.
+ See example in test_command_check.
"""
def setUp(self):
@@ -117,25 +123,23 @@ class LoggingCatcher:
logger2to3.setLevel(self._old_levels[1])
super(LoggingCatcher, self).tearDown()
- def get_logs(self, *levels):
- """Return all log messages with level in *levels*.
+ def get_logs(self, level=logging.WARNING, flush=True):
+ """Return all log messages with given level.
- Without explicit levels given, returns all messages. *levels* defaults
- to all levels. For log calls with arguments (i.e.
- logger.info('bla bla %r', arg)), the messages will be formatted before
- being returned (e.g. "bla bla 'thing'").
+ *level* defaults to logging.WARNING.
- Returns a list. Automatically flushes the loghandler after being
- called.
+ For log calls with arguments (i.e. logger.info('bla bla %r', arg)),
+ the messages will be formatted before being returned (e.g. "bla bla
+ 'thing'").
- Example: self.get_logs(logging.WARN, logging.DEBUG).
+ Returns a list. Automatically flushes the loghandler after being
+ called, unless *flush* is False (this is useful to get e.g. all
+ warnings then all info messages).
"""
- if not levels:
- messages = [log.getMessage() for log in self.loghandler.buffer]
- else:
- messages = [log.getMessage() for log in self.loghandler.buffer
- if log.levelno in levels]
- self.loghandler.flush()
+ messages = [log.getMessage() for log in self.loghandler.buffer
+ if log.levelno == level]
+ if flush:
+ self.loghandler.flush()
return messages
@@ -254,7 +258,7 @@ class DummyCommand:
Useful for mocking one dependency command in the tests for another
command, see e.g. the dummy build command in test_build_scripts.
"""
- # XXX does not work with dist.get_reinitialized_command, which typechecks
+ # XXX does not work with dist.reinitialize_command, which typechecks
# and wants a finalized attribute
def __init__(self, **kwargs):
@@ -277,6 +281,22 @@ class TestDistribution(Distribution):
return self._config_files
+class Inputs:
+ """Fakes user inputs."""
+ # TODO document usage
+ # TODO use context manager or something for auto cleanup
+
+ def __init__(self, *answers):
+ self.answers = answers
+ self.index = 0
+
+ def __call__(self, prompt=''):
+ try:
+ return self.answers[self.index]
+ finally:
+ self.index += 1
+
+
def create_distribution(configfiles=()):
"""Prepares a distribution with given config files parsed."""
d = TestDistribution()
@@ -287,6 +307,15 @@ def create_distribution(configfiles=()):
return d
+def use_command(testcase, fullname):
+ """Register command at *fullname* for the duration of a test."""
+ set_command(fullname)
+ # XXX maybe set_command should return the class object
+ name = resolve_name(fullname).get_command_name()
+ # XXX maybe we need a public API to remove commands
+ testcase.addCleanup(_COMMANDS.__delitem__, name)
+
+
def fake_dec(*args, **kw):
"""Fake decorator"""
def _wrap(func):
@@ -369,6 +398,10 @@ except ImportError:
'requires test.support.skip_unless_symlink')
+skip_2to3_optimize = unittest.skipIf(sys.flags.optimize,
+ "2to3 doesn't work under -O")
+
+
requires_zlib = unittest.skipUnless(zlib, 'requires zlib')
diff --git a/distutils2/tests/test_command_bdist_dumb.py b/distutils2/tests/test_command_bdist_dumb.py
index a31d14e..f77e279 100644
--- a/distutils2/tests/test_command_bdist_dumb.py
+++ b/distutils2/tests/test_command_bdist_dumb.py
@@ -1,6 +1,9 @@
"""Tests for distutils.command.bdist_dumb."""
import os
+import imp
+import sys
+import zipfile
import distutils2.util
from distutils2.dist import Distribution
@@ -49,15 +52,27 @@ class BuildDumbTestCase(support.TempdirManager,
# see what we have
dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
- base = "%s.%s" % (dist.get_fullname(), cmd.plat_name)
+ base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name)
if os.name == 'os2':
base = base.replace(':', '-')
- wanted = ['%s.zip' % base]
- self.assertEqual(dist_created, wanted)
+ self.assertEqual(dist_created, [base])
# now let's check what we have in the zip file
- # XXX to be done
+ fp = zipfile.ZipFile(os.path.join('dist', base))
+ try:
+ contents = fp.namelist()
+ finally:
+ fp.close
+
+ if sys.version_info[1] == 1:
+ pyc = 'foo.pyc'
+ else:
+ pyc = 'foo.%s.pyc' % imp.get_tag()
+ contents = sorted(os.path.basename(fn) for fn in contents)
+ wanted = ['foo.py', pyc,
+ 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
+ self.assertEqual(contents, sorted(wanted))
def test_finalize_options(self):
pkg_dir, dist = self.create_dist()
diff --git a/distutils2/tests/test_command_build_ext.py b/distutils2/tests/test_command_build_ext.py
index 06b5992..b9f8cc2 100644
--- a/distutils2/tests/test_command_build_ext.py
+++ b/distutils2/tests/test_command_build_ext.py
@@ -1,9 +1,7 @@
import os
import sys
import site
-import shutil
import textwrap
-from io import StringIO
from distutils2.dist import Distribution
from distutils2.errors import (UnknownFileError, CompileError,
PackagingPlatformError)
@@ -11,7 +9,7 @@ from distutils2.command.build_ext import build_ext
from distutils2.compiler.extension import Extension
from distutils2._backport import sysconfig
-from distutils2.tests import support, unittest, verbose
+from distutils2.tests import support, unittest
from distutils2.tests.support import assert_python_ok
@@ -38,18 +36,10 @@ class BuildExtTestCase(support.TempdirManager,
support.fixup_build_ext(cmd)
cmd.build_lib = self.tmp_dir
cmd.build_temp = self.tmp_dir
+ cmd.ensure_finalized()
+ cmd.run()
- old_stdout = sys.stdout
- if not verbose:
- # silence compiler output
- sys.stdout = StringIO()
- try:
- cmd.ensure_finalized()
- cmd.run()
- finally:
- sys.stdout = old_stdout
-
- code = """if 1:
+ code = textwrap.dedent("""\
import sys
sys.path.insert(0, %r)
@@ -64,7 +54,8 @@ class BuildExtTestCase(support.TempdirManager,
doc = 'This is a template module just for instruction.'
assert xx.__doc__ == doc
assert isinstance(xx.Null(), xx.Null)
- assert isinstance(xx.Str(), xx.Str)"""
+ assert isinstance(xx.Str(), xx.Str)
+ """)
code = code % self.tmp_dir
assert_python_ok('-c', code)
@@ -389,16 +380,8 @@ class BuildExtTestCase(support.TempdirManager,
cmd.build_temp = self.tmp_dir
try:
- old_stdout = sys.stdout
- if not verbose:
- # silence compiler output
- sys.stdout = StringIO()
- try:
- cmd.ensure_finalized()
- cmd.run()
- finally:
- sys.stdout = old_stdout
-
+ cmd.ensure_finalized()
+ cmd.run()
except CompileError:
self.fail("Wrong deployment target during compilation")
diff --git a/distutils2/tests/test_command_build_py.py b/distutils2/tests/test_command_build_py.py
index fa152ec..4b6c6aa 100644
--- a/distutils2/tests/test_command_build_py.py
+++ b/distutils2/tests/test_command_build_py.py
@@ -55,30 +55,20 @@ class BuildPyTestCase(support.TempdirManager,
# 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)
- pycache_dir = os.path.join(pkgdest, "__pycache__")
self.assertIn("__init__.py", files)
self.assertIn("README.txt", files)
- if sys.dont_write_bytecode:
- if sys.version_info[1] == 1:
- self.assertNotIn("__init__.pyc", files)
- else:
- self.assertFalse(os.path.exists(pycache_dir))
+ if sys.version_info[1] == 1:
+ self.assertIn("__init__.pyc", files)
else:
- # XXX even with -O, distutils2 writes pyc, not pyo; bug?
- if sys.version_info[1] == 1:
- self.assertIn("__init__.pyc", files)
- else:
- pyc_files = os.listdir(pycache_dir)
- self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files)
+ pycache_dir = os.path.join(pkgdest, "__pycache__")
+ pyc_files = os.listdir(pycache_dir)
+ self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files)
def test_empty_package_dir(self):
# See SF 1668596/1720897.
- cwd = os.getcwd()
-
# create the distribution files.
sources = self.mkdtemp()
pkg = os.path.join(sources, 'pkg')
@@ -89,40 +79,66 @@ class BuildPyTestCase(support.TempdirManager,
open(os.path.join(testdir, "testfile"), "wb").close()
os.chdir(sources)
- old_stdout = sys.stdout
- #sys.stdout = StringIO.StringIO()
+ dist = Distribution({"packages": ["pkg"],
+ "package_dir": sources,
+ "package_data": {"pkg": ["doc/*"]}})
+ dist.script_args = ["build"]
+ dist.parse_command_line()
try:
- dist = Distribution({"packages": ["pkg"],
- "package_dir": sources,
- "package_data": {"pkg": ["doc/*"]}})
- dist.script_args = ["build"]
- dist.parse_command_line()
-
- try:
- dist.run_commands()
- except PackagingFileError:
- self.fail("failed package_data test when package_dir is ''")
- finally:
- # Restore state.
- os.chdir(cwd)
- sys.stdout = old_stdout
+ dist.run_commands()
+ except PackagingFileError:
+ self.fail("failed package_data test when package_dir is ''")
+
+ 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()
- def test_dont_write_bytecode(self):
- # makes sure byte_compile is not used
- pkg_dir, dist = self.create_dist()
+ found = os.listdir(cmd.build_lib)
+ if sys.version_info[1] == 1:
+ self.assertEqual(sorted(found),
+ ['boiledeggs.py', 'boiledeggs.pyc'])
+ else:
+ self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
+ found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
+ self.assertEqual(found, ['boiledeggs.%s.pyc' % imp.get_tag()])
+
+ 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)
+ if sys.version_info[1] == 1:
+ self.assertEqual(sorted(found), ['boiledeggs.py', 'boiledeggs.pyc',
+ 'boiledeggs.pyo'])
+ else:
+ self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
+ found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
+ self.assertEqual(sorted(found),
+ ['boiledeggs.%s.pyc' % imp.get_tag(),
+ 'boiledeggs.%s.pyo' % imp.get_tag()])
+
+ 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_check.py b/distutils2/tests/test_command_check.py
index 638bdaf..e4413b3 100644
--- a/distutils2/tests/test_command_check.py
+++ b/distutils2/tests/test_command_check.py
@@ -1,6 +1,5 @@
"""Tests for distutils.command.check."""
-import logging
from distutils2.command.check import check
from distutils2.metadata import _HAS_DOCUTILS
from distutils2.errors import PackagingSetupError, MetadataMissingError
@@ -27,11 +26,11 @@ class CheckTestCase(support.LoggingCatcher,
# let's run the command with no metadata at all
# by default, check is checking the metadata
# should have some warnings
- cmd = self._run()
+ self._run()
# trick: using assertNotEqual with an empty list will give us a more
# useful error message than assertGreater(.., 0) when the code change
# and the test fails
- self.assertNotEqual([], self.get_logs(logging.WARNING))
+ self.assertNotEqual(self.get_logs(), [])
# now let's add the required fields
# and run it again, to make sure we don't get
@@ -40,8 +39,8 @@ class CheckTestCase(support.LoggingCatcher,
'author_email': 'xxx',
'name': 'xxx', 'version': '4.2',
}
- cmd = self._run(metadata)
- self.assertEqual([], self.get_logs(logging.WARNING))
+ self._run(metadata)
+ self.assertEqual(self.get_logs(), [])
# now with the strict mode, we should
# get an error if there are missing metadata
@@ -53,8 +52,8 @@ class CheckTestCase(support.LoggingCatcher,
self.loghandler.flush()
# and of course, no error when all metadata fields are present
- cmd = self._run(metadata, strict=True)
- self.assertEqual([], self.get_logs(logging.WARNING))
+ self._run(metadata, strict=True)
+ self.assertEqual(self.get_logs(), [])
# now a test with non-ASCII characters
metadata = {'home_page': 'xxx', 'author': '\u00c9ric',
@@ -62,15 +61,15 @@ class CheckTestCase(support.LoggingCatcher,
'version': '1.2',
'summary': 'Something about esszet \u00df',
'description': 'More things about esszet \u00df'}
- cmd = self._run(metadata)
- self.assertEqual([], self.get_logs(logging.WARNING))
+ self._run(metadata)
+ self.assertEqual(self.get_logs(), [])
def test_check_metadata_1_2(self):
# let's run the command with no metadata at all
# by default, check is checking the metadata
# should have some warnings
- cmd = self._run()
- self.assertNotEqual([], self.get_logs(logging.WARNING))
+ self._run()
+ self.assertNotEqual(self.get_logs(), [])
# now let's add the required fields and run it again, to make sure we
# don't get any warning anymore let's use requires_python as a marker
@@ -80,8 +79,8 @@ class CheckTestCase(support.LoggingCatcher,
'name': 'xxx', 'version': '4.2',
'requires_python': '2.4',
}
- cmd = self._run(metadata)
- self.assertEqual([], self.get_logs(logging.WARNING))
+ self._run(metadata)
+ self.assertEqual(self.get_logs(), [])
# now with the strict mode, we should
# get an error if there are missing metadata
@@ -99,8 +98,8 @@ class CheckTestCase(support.LoggingCatcher,
# now with correct version format again
metadata['version'] = '4.2'
- cmd = self._run(metadata, strict=True)
- self.assertEqual([], self.get_logs(logging.WARNING))
+ self._run(metadata, strict=True)
+ self.assertEqual(self.get_logs(), [])
@unittest.skipUnless(_HAS_DOCUTILS, "requires docutils")
def test_check_restructuredtext(self):
@@ -109,9 +108,7 @@ class CheckTestCase(support.LoggingCatcher,
pkg_info, dist = self.create_dist(description=broken_rest)
cmd = check(dist)
cmd.check_restructuredtext()
- self.assertEqual(len(self.get_logs(logging.WARNING)), 1)
- # clear warnings from the previous call
- self.loghandler.flush()
+ self.assertEqual(len(self.get_logs()), 1)
# let's see if we have an error with strict=1
metadata = {'home_page': 'xxx', 'author': 'xxx',
@@ -126,7 +123,7 @@ class CheckTestCase(support.LoggingCatcher,
dist = self.create_dist(description='title\n=====\n\ntest \u00df')[1]
cmd = check(dist)
cmd.check_restructuredtext()
- self.assertEqual([], self.get_logs(logging.WARNING))
+ self.assertEqual(self.get_logs(), [])
def test_check_all(self):
self.assertRaises(PackagingSetupError, self._run,
@@ -143,18 +140,18 @@ class CheckTestCase(support.LoggingCatcher,
}
cmd = check(dist)
cmd.check_hooks_resolvable()
- self.assertEqual(len(self.get_logs(logging.WARNING)), 1)
+ self.assertEqual(len(self.get_logs()), 1)
def test_warn(self):
_, dist = self.create_dist()
cmd = check(dist)
- self.assertEqual([], self.get_logs())
+ self.assertEqual(self.get_logs(), [])
cmd.warn('hello')
- self.assertEqual(['check: hello'], self.get_logs())
+ self.assertEqual(self.get_logs(), ['check: hello'])
cmd.warn('hello %s', 'world')
- self.assertEqual(['check: hello world'], self.get_logs())
+ self.assertEqual(self.get_logs(), ['check: hello world'])
cmd.warn('hello %s %s', 'beautiful', 'world')
- self.assertEqual(['check: hello beautiful world'], self.get_logs())
+ self.assertEqual(self.get_logs(), ['check: hello beautiful world'])
def test_suite():
diff --git a/distutils2/tests/test_command_clean.py b/distutils2/tests/test_command_clean.py
index 7b8effa..910628a 100644
--- a/distutils2/tests/test_command_clean.py
+++ b/distutils2/tests/test_command_clean.py
@@ -5,7 +5,8 @@ from distutils2.command.clean import clean
from distutils2.tests import unittest, support
-class cleanTestCase(support.TempdirManager, support.LoggingCatcher,
+class CleanTestCase(support.TempdirManager,
+ support.LoggingCatcher,
unittest.TestCase):
def test_simple_run(self):
@@ -23,7 +24,7 @@ class cleanTestCase(support.TempdirManager, support.LoggingCatcher,
if name == 'build_base':
continue
for f in ('one', 'two', 'three'):
- self.write_file(os.path.join(path, f))
+ self.write_file((path, f))
# let's run the command
cmd.all = True
@@ -36,13 +37,11 @@ class cleanTestCase(support.TempdirManager, support.LoggingCatcher,
'%r was not removed' % path)
# let's run the command again (should spit warnings but succeed)
- cmd.all = True
- cmd.ensure_finalized()
cmd.run()
def test_suite():
- return unittest.makeSuite(cleanTestCase)
+ return unittest.makeSuite(CleanTestCase)
if __name__ == "__main__":
unittest.main(defaultTest="test_suite")
diff --git a/distutils2/tests/test_command_cmd.py b/distutils2/tests/test_command_cmd.py
index 1b0f622..c29f0de 100644
--- a/distutils2/tests/test_command_cmd.py
+++ b/distutils2/tests/test_command_cmd.py
@@ -1,5 +1,6 @@
"""Tests for distutils.cmd."""
import os
+import logging
from distutils2.command.cmd import Command
from distutils2.dist import Distribution
@@ -43,7 +44,7 @@ class CommandTestCase(support.LoggingCatcher,
wanted = ["command options for 'MyCmd':", ' option1 = 1',
' option2 = 1']
- msgs = self.get_logs()
+ msgs = self.get_logs(logging.INFO)
self.assertEqual(msgs, wanted)
def test_ensure_string(self):
diff --git a/distutils2/tests/test_command_install_data.py b/distutils2/tests/test_command_install_data.py
index ba93cce..c169b87 100644
--- a/distutils2/tests/test_command_install_data.py
+++ b/distutils2/tests/test_command_install_data.py
@@ -62,6 +62,7 @@ class InstallDataTestCase(support.TempdirManager,
# let's try with warn_dir one
cmd.warn_dir = True
+ cmd.finalized = False
cmd.ensure_finalized()
cmd.run()
@@ -80,6 +81,7 @@ class InstallDataTestCase(support.TempdirManager,
cmd.data_files = {one: '{inst}/one', two: '{inst2}/two',
three: '{inst3}/three'}
+ cmd.finalized = False
cmd.ensure_finalized()
cmd.run()
@@ -125,22 +127,16 @@ class InstallDataTestCase(support.TempdirManager,
# now the real test
fn = os.path.join(install_dir, 'Spamlib-0.1.dist-info', 'RESOURCES')
- fp = open(fn)
- try:
+ with open(fn, encoding='utf-8') as fp:
content = fp.read().strip()
- finally:
- fp.close()
expected = 'spamd,%s' % os.path.join(scripts_dir, 'spamd')
self.assertEqual(content, expected)
# just to be sure, we also test that get_file works here, even though
# packaging.database has its own test file
- fp = distutils2.database.get_file('Spamlib', 'spamd')
- try:
+ with distutils2.database.get_file('Spamlib', 'spamd') as fp:
content = fp.read()
- finally:
- fp.close()
self.assertEqual('# Python script', content)
diff --git a/distutils2/tests/test_command_install_dist.py b/distutils2/tests/test_command_install_dist.py
index c477299..597e10a 100644
--- a/distutils2/tests/test_command_install_dist.py
+++ b/distutils2/tests/test_command_install_dist.py
@@ -1,6 +1,7 @@
"""Tests for distutils2.command.install."""
import os
+import imp
import sys
from distutils2.command.build_ext import build_ext
@@ -92,21 +93,20 @@ class InstallTestCase(support.TempdirManager,
self.old_expand = os.path.expanduser
os.path.expanduser = _expanduser
- try:
- # this is the actual test
- self._test_user_site()
- finally:
+ def cleanup():
_CONFIG_VARS['userbase'] = self.old_user_base
_SCHEMES.set(scheme, 'purelib', self.old_user_site)
os.path.expanduser = self.old_expand
- def _test_user_site(self):
+ self.addCleanup(cleanup)
+
schemes = get_scheme_names()
for key in ('nt_user', 'posix_user', 'os2_home'):
self.assertIn(key, schemes)
dist = Distribution({'name': 'xx'})
cmd = install_dist(dist)
+
# making sure the user option is there
options = [name for name, short, lable in
cmd.user_options]
@@ -181,9 +181,11 @@ class InstallTestCase(support.TempdirManager,
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
@@ -195,9 +197,14 @@ class InstallTestCase(support.TempdirManager,
with open(cmd.record) as f:
content = f.read()
+ if sys.version_info[1] == 1:
+ pyc = 'hello.pyc'
+ else:
+ pyc = 'hello.%s.pyc' % imp.get_tag()
found = [os.path.basename(line) for line in content.splitlines()]
- expected = ['hello', 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
- self.assertEqual(found, expected)
+ expected = ['hello.py', 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_distinfo.py b/distutils2/tests/test_command_install_distinfo.py
index 9898c2f..3985e0b 100644
--- a/distutils2/tests/test_command_install_distinfo.py
+++ b/distutils2/tests/test_command_install_distinfo.py
@@ -49,7 +49,7 @@ class InstallDistinfoTestCase(support.TempdirManager,
cmd = install_distinfo(dist)
dist.command_obj['install_distinfo'] = cmd
- cmd.distinfo_dir = install_dir
+ cmd.install_dir = install_dir
cmd.ensure_finalized()
cmd.run()
@@ -60,6 +60,7 @@ class InstallDistinfoTestCase(support.TempdirManager,
['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER'])
with open(os.path.join(dist_info, 'INSTALLER')) as fp:
self.assertEqual(fp.read(), 'distutils')
+
with open(os.path.join(dist_info, 'REQUESTED')) as fp:
self.assertEqual(fp.read(), '')
meta_path = os.path.join(dist_info, 'METADATA')
@@ -76,7 +77,7 @@ class InstallDistinfoTestCase(support.TempdirManager,
cmd = install_distinfo(dist)
dist.command_obj['install_distinfo'] = cmd
- cmd.distinfo_dir = install_dir
+ cmd.install_dir = install_dir
cmd.installer = 'bacon-python'
cmd.ensure_finalized()
cmd.run()
@@ -96,7 +97,7 @@ class InstallDistinfoTestCase(support.TempdirManager,
cmd = install_distinfo(dist)
dist.command_obj['install_distinfo'] = cmd
- cmd.distinfo_dir = install_dir
+ cmd.install_dir = install_dir
cmd.requested = False
cmd.ensure_finalized()
cmd.run()
@@ -116,7 +117,7 @@ class InstallDistinfoTestCase(support.TempdirManager,
cmd = install_distinfo(dist)
dist.command_obj['install_distinfo'] = cmd
- cmd.distinfo_dir = install_dir
+ cmd.install_dir = install_dir
cmd.no_record = True
cmd.ensure_finalized()
cmd.run()
@@ -214,7 +215,7 @@ class InstallDistinfoTestCase(support.TempdirManager,
cmd = install_distinfo(dist)
dist.command_obj['install_distinfo'] = cmd
- cmd.distinfo_dir = install_dir
+ cmd.install_dir = install_dir
cmd.ensure_finalized()
cmd.run()
diff --git a/distutils2/tests/test_command_install_lib.py b/distutils2/tests/test_command_install_lib.py
index 78b539c..160e62e 100644
--- a/distutils2/tests/test_command_install_lib.py
+++ b/distutils2/tests/test_command_install_lib.py
@@ -17,7 +17,7 @@ class InstallLibTestCase(support.TempdirManager,
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()
@@ -34,75 +34,77 @@ class InstallLibTestCase(support.TempdirManager,
cmd.finalize_options()
self.assertEqual(cmd.optimize, 2)
- @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
def test_byte_compile(self):
- pkg_dir, dist = self.create_dist()
- os.chdir(pkg_dir)
+ 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])
if sys.version_info[1] == 1:
pyc_file = 'foo.pyc'
pyo_file = 'foo.pyo'
else:
- pyc_file = imp.cache_from_source('foo.py')
- pyo_file = imp.cache_from_source('foo.py', debug_override=False)
+ pyc_file = imp.cache_from_source('foo.py', True)
+ pyo_file = imp.cache_from_source('foo.py', False)
self.assertTrue(os.path.exists(pyc_file))
self.assertTrue(os.path.exists(pyo_file))
+ 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]
-
- # get_input should return 2 elements
- self.assertEqual(len(cmd.get_inputs()), 2)
-
- 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([])
+ cmd.distribution.packages = ['spam']
- 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_command_register.py b/distutils2/tests/test_command_register.py
index 2cc9f70..4e6c1de 100644
--- a/distutils2/tests/test_command_register.py
+++ b/distutils2/tests/test_command_register.py
@@ -12,6 +12,7 @@ except ImportError:
DOCUTILS_SUPPORT = False
from distutils2.tests import unittest, support
+from distutils2.tests.support import Inputs
from distutils2.command import register as register_module
from distutils2.command.register import register
from distutils2.errors import PackagingSetupError
@@ -38,19 +39,6 @@ password:password
"""
-class Inputs:
- """Fakes user inputs."""
- def __init__(self, *answers):
- self.answers = answers
- self.index = 0
-
- def __call__(self, prompt=''):
- try:
- return self.answers[self.index]
- finally:
- self.index += 1
-
-
class FakeOpener:
"""Fakes a PyPI server"""
def __init__(self):
@@ -143,6 +131,7 @@ class RegisterTestCase(support.TempdirManager,
register_module.input = _no_way
cmd.show_response = True
+ cmd.finalized = False
cmd.ensure_finalized()
cmd.run()
@@ -200,12 +189,10 @@ class RegisterTestCase(support.TempdirManager,
@unittest.skipUnless(DOCUTILS_SUPPORT, 'needs docutils')
def test_strict(self):
- # testing the script option
- # when on, the register command stops if
- # the metadata is incomplete or if
- # long_description is not reSt compliant
+ # testing the strict option: when on, the register command stops if the
+ # metadata is incomplete or if description contains bad reST
- # empty metadata
+ # empty metadata # XXX this is not really empty..
cmd = self._get_cmd({'name': 'xxx', 'version': 'xxx'})
cmd.ensure_finalized()
cmd.strict = True
@@ -213,16 +200,15 @@ class RegisterTestCase(support.TempdirManager,
register_module.input = inputs
self.assertRaises(PackagingSetupError, cmd.run)
- # metadata is OK but long_description is broken
+ # metadata is OK but description is broken
metadata = {'home_page': 'xxx', 'author': 'xxx',
'author_email': 'éxéxé',
- 'name': 'xxx', 'version': 'xxx',
+ 'name': 'xxx', 'version': '4.2',
'description': 'title\n==\n\ntext'}
cmd = self._get_cmd(metadata)
cmd.ensure_finalized()
cmd.strict = True
-
self.assertRaises(PackagingSetupError, cmd.run)
# now something that works
diff --git a/distutils2/tests/test_command_sdist.py b/distutils2/tests/test_command_sdist.py
index 9bf1fcf..0df3728 100644
--- a/distutils2/tests/test_command_sdist.py
+++ b/distutils2/tests/test_command_sdist.py
@@ -1,9 +1,6 @@
"""Tests for distutils2.command.sdist."""
import os
import zipfile
-import logging
-
-from distutils2.tests.support import requires_zlib
try:
import grp
@@ -13,17 +10,17 @@ except ImportError:
UID_GID_SUPPORT = False
from os.path import join
-from distutils2.tests import captured_stdout
-from distutils2.command.sdist import sdist
-from distutils2.command.sdist import show_formats
from distutils2.dist import Distribution
-from distutils2.tests import unittest
-from distutils2.errors import PackagingOptionError
from distutils2.util import find_executable
-from distutils2.tests import support
+from distutils2.errors import PackagingOptionError
+from distutils2.command.sdist import sdist, show_formats
from distutils2._backport import tarfile
from distutils2._backport.shutil import get_archive_formats
+from distutils2.tests import support, unittest
+from distutils2.tests import captured_stdout
+from distutils2.tests.support import requires_zlib
+
MANIFEST = """\
# file GENERATED by distutils2, do NOT edit
@@ -89,7 +86,6 @@ class SDistTestCase(support.TempdirManager,
# creating VCS directories with some files in them
os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
-
self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx')
os.mkdir(join(self.tmp_dir, 'somecode', '.hg'))
@@ -147,7 +143,7 @@ class SDistTestCase(support.TempdirManager,
# now trying a tar then a gztar
cmd.formats = ['tar', 'gztar']
-
+ cmd.finalized = False
cmd.ensure_finalized()
cmd.run()
@@ -223,12 +219,14 @@ class SDistTestCase(support.TempdirManager,
# testing the `check-metadata` option
dist, cmd = self.get_cmd(metadata={'name': 'xxx', 'version': 'xxx'})
- # this should raise some warnings
- # with the check subcommand
+ # this should cause the check subcommand to log two warnings:
+ # version is invalid, home-page and author are missing
cmd.ensure_finalized()
cmd.run()
- warnings = self.get_logs(logging.WARN)
- self.assertEqual(len(warnings), 4)
+ warnings = self.get_logs()
+ check_warnings = [msg for msg in warnings if
+ not msg.startswith('sdist:')]
+ self.assertEqual(len(check_warnings), 2, warnings)
# trying with a complete set of metadata
self.loghandler.flush()
@@ -236,13 +234,10 @@ class SDistTestCase(support.TempdirManager,
cmd.ensure_finalized()
cmd.metadata_check = False
cmd.run()
- warnings = self.get_logs(logging.WARN)
- # removing manifest generated warnings
- warnings = [warn for warn in warnings if
- not warn.endswith('-- skipping')]
- # the remaining warnings are about the use of the default file list and
- # the absence of setup.cfg
+ warnings = self.get_logs()
self.assertEqual(len(warnings), 2)
+ self.assertIn('using default file list', warnings[0])
+ self.assertIn("'setup.cfg' file not found", warnings[1])
def test_show_formats(self):
__, stdout = captured_stdout(show_formats)
@@ -254,7 +249,6 @@ class SDistTestCase(support.TempdirManager,
self.assertEqual(len(output), num_formats)
def test_finalize_options(self):
-
dist, cmd = self.get_cmd()
cmd.finalize_options()
@@ -274,6 +268,18 @@ class SDistTestCase(support.TempdirManager,
self.assertRaises(PackagingOptionError, cmd.finalize_options)
@requires_zlib
+ def test_template(self):
+ dist, cmd = self.get_cmd()
+ dist.extra_files = ['include yeah']
+ cmd.ensure_finalized()
+ self.write_file((self.tmp_dir, 'yeah'), 'xxx')
+ cmd.run()
+ with open(cmd.manifest) as f:
+ content = f.read()
+
+ self.assertIn('yeah', content)
+
+ @requires_zlib
@unittest.skipUnless(UID_GID_SUPPORT, "requires grp and pwd support")
@unittest.skipIf(find_executable('tar') is None or
find_executable('gzip') is None,
@@ -291,13 +297,10 @@ class SDistTestCase(support.TempdirManager,
# making sure we have the good rights
archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
- archive = tarfile.open(archive_name)
- try:
+ with tarfile.open(archive_name) as archive:
for member in archive.getmembers():
self.assertEqual(member.uid, 0)
self.assertEqual(member.gid, 0)
- finally:
- archive.close()
# building a sdist again
dist, cmd = self.get_cmd()
@@ -309,15 +312,12 @@ class SDistTestCase(support.TempdirManager,
# making sure we have the good rights
archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
- archive = tarfile.open(archive_name)
- try:
+ with tarfile.open(archive_name) as archive:
# note that we are not testing the group ownership here
# because, depending on the platforms and the container
# rights (see #7408)
for member in archive.getmembers():
self.assertEqual(member.uid, os.getuid())
- finally:
- archive.close()
@requires_zlib
def test_get_file_list(self):
@@ -383,18 +383,6 @@ class SDistTestCase(support.TempdirManager,
self.assertEqual(manifest, ['README.manual'])
@requires_zlib
- def test_template(self):
- dist, cmd = self.get_cmd()
- dist.extra_files = ['include yeah']
- cmd.ensure_finalized()
- self.write_file((self.tmp_dir, 'yeah'), 'xxx')
- cmd.run()
- with open(cmd.manifest) as f:
- content = f.read()
-
- self.assertIn('yeah', content)
-
- @requires_zlib
def test_manifest_builder(self):
dist, cmd = self.get_cmd()
cmd.manifest_builders = 'distutils2.tests.test_command_sdist.builder'
diff --git a/distutils2/tests/test_command_test.py b/distutils2/tests/test_command_test.py
index 9fb6fb9..f2dcd22 100644
--- a/distutils2/tests/test_command_test.py
+++ b/distutils2/tests/test_command_test.py
@@ -2,7 +2,6 @@ import os
import re
import sys
import shutil
-import logging
import unittest as ut1
import distutils2.database
@@ -140,7 +139,8 @@ class TestTest(TempdirManager,
cmd.run()
self.assertEqual(['build has run'], record)
- def _test_works_with_2to3(self):
+ @unittest.skip('needs to be written')
+ def test_works_with_2to3(self):
pass
def test_checks_requires(self):
@@ -149,7 +149,7 @@ class TestTest(TempdirManager,
phony_project = 'ohno_ohno-impossible_1234-name_stop-that!'
cmd.tests_require = [phony_project]
cmd.ensure_finalized()
- logs = self.get_logs(logging.WARNING)
+ logs = self.get_logs()
self.assertIn(phony_project, logs[-1])
def prepare_a_module(self):
diff --git a/distutils2/tests/test_command_upload.py b/distutils2/tests/test_command_upload.py
index 5c58879..61c6654 100644
--- a/distutils2/tests/test_command_upload.py
+++ b/distutils2/tests/test_command_upload.py
@@ -129,7 +129,7 @@ class UploadTestCase(support.TempdirManager, support.EnvironRestorer,
dist_files = [(command, pyversion, filename)]
docs_path = os.path.join(self.tmp_dir, "build", "docs")
os.makedirs(docs_path)
- self.write_file(os.path.join(docs_path, "index.html"), "yellow")
+ self.write_file((docs_path, "index.html"), "yellow")
self.write_file(self.rc, PYPIRC)
# let's run it
diff --git a/distutils2/tests/test_command_upload_docs.py b/distutils2/tests/test_command_upload_docs.py
index cf69af9..95eec0c 100644
--- a/distutils2/tests/test_command_upload_docs.py
+++ b/distutils2/tests/test_command_upload_docs.py
@@ -1,6 +1,7 @@
"""Tests for distutils2.command.upload_docs."""
import os
import shutil
+import logging
import zipfile
try:
import _ssl
@@ -70,9 +71,8 @@ class UploadDocsTestCase(support.TempdirManager,
if sample_dir is None:
sample_dir = self.mkdtemp()
os.mkdir(os.path.join(sample_dir, "docs"))
- self.write_file(os.path.join(sample_dir, "docs", "index.html"),
- "Ce mortel ennui")
- self.write_file(os.path.join(sample_dir, "index.html"), "Oh la la")
+ self.write_file((sample_dir, "docs", "index.html"), "Ce mortel ennui")
+ self.write_file((sample_dir, "index.html"), "Oh la la")
return sample_dir
def test_zip_dir(self):
@@ -141,13 +141,16 @@ class UploadDocsTestCase(support.TempdirManager,
self.pypi.default_response_status = '403 Forbidden'
self.prepare_command()
self.cmd.run()
- self.assertIn('Upload failed (403): Forbidden', self.get_logs()[-1])
+ errors = self.get_logs(logging.ERROR)
+ self.assertEqual(len(errors), 1)
+ self.assertIn('Upload failed (403): Forbidden', errors[0])
self.pypi.default_response_status = '301 Moved Permanently'
self.pypi.default_response_headers.append(
("Location", "brand_new_location"))
self.cmd.run()
- self.assertIn('brand_new_location', self.get_logs()[-1])
+ lastlog = self.get_logs(logging.INFO)[-1]
+ self.assertIn('brand_new_location', lastlog)
def test_reads_pypirc_data(self):
self.write_file(self.rc, PYPIRC % self.pypi.full_address)
@@ -171,7 +174,7 @@ class UploadDocsTestCase(support.TempdirManager,
self.prepare_command()
self.cmd.show_response = True
self.cmd.run()
- record = self.get_logs()[-1]
+ record = self.get_logs(logging.INFO)[-1]
self.assertTrue(record, "should report the response")
self.assertIn(self.pypi.default_response_data, record)
diff --git a/distutils2/tests/test_config.py b/distutils2/tests/test_config.py
index 7c913ee..dc61daa 100644
--- a/distutils2/tests/test_config.py
+++ b/distutils2/tests/test_config.py
@@ -1,8 +1,6 @@
"""Tests for distutils2.config."""
import os
import sys
-import logging
-from io import StringIO
from distutils2 import command
from distutils2.dist import Distribution
@@ -184,13 +182,14 @@ class FooBarBazTest:
def __init__(self, dist):
self.distribution = dist
+ self._record = []
@classmethod
def get_command_name(cls):
return 'foo'
def run(self):
- self.distribution.foo_was_here = True
+ self._record.append('foo has run')
def nothing(self):
pass
@@ -210,21 +209,11 @@ class ConfigTestCase(support.TempdirManager,
def setUp(self):
super(ConfigTestCase, self).setUp()
- self.addCleanup(setattr, sys, 'stdout', sys.stdout)
- self.addCleanup(setattr, sys, 'stderr', sys.stderr)
- sys.stdout = StringIO()
- sys.stderr = StringIO()
-
- self.addCleanup(os.chdir, os.getcwd())
tempdir = self.mkdtemp()
self.working_dir = os.getcwd()
os.chdir(tempdir)
self.tempdir = tempdir
- def tearDown(self):
- os.chdir(self.working_dir)
- super(ConfigTestCase, self).tearDown()
-
def write_setup(self, kwargs=None):
opts = {'description-file': 'README', 'extra-files': '',
'setup-hooks': 'distutils2.tests.test_config.version_hook'}
@@ -375,15 +364,14 @@ class ConfigTestCase(support.TempdirManager,
self.write_file('README', 'yeah')
self.write_file('hooks.py', HOOKS_MODULE)
self.get_dist()
- logs = self.get_logs(logging.WARNING)
- self.assertEqual(['logging_hook called'], logs)
+ self.assertEqual(['logging_hook called'], self.get_logs())
self.assertIn('hooks', sys.modules)
def test_missing_setup_hook_warns(self):
- self.write_setup({'setup-hooks': 'this.does._not.exist'})
+ self.write_setup({'setup-hooks': 'does._not.exist'})
self.write_file('README', 'yeah')
self.get_dist()
- logs = self.get_logs(logging.WARNING)
+ logs = self.get_logs()
self.assertEqual(1, len(logs))
self.assertIn('cannot find setup hook', logs[0])
@@ -397,7 +385,7 @@ class ConfigTestCase(support.TempdirManager,
dist = self.get_dist()
self.assertEqual(['haven', 'first', 'third'], dist.py_modules)
- logs = self.get_logs(logging.WARNING)
+ logs = self.get_logs()
self.assertEqual(1, len(logs))
self.assertIn('cannot find setup hook', logs[0])
@@ -493,10 +481,12 @@ class ConfigTestCase(support.TempdirManager,
self.write_file((pkg, '__init__.py'), '#')
# try to run the install command to see if foo is called
+ self.addCleanup(command._COMMANDS.__delitem__, 'foo')
dist = self.get_dist()
- self.assertIn('foo', command.get_command_names())
- self.assertEqual('FooBarBazTest',
- dist.get_command_obj('foo').__class__.__name__)
+ dist.run_command('install_dist')
+ cmd = dist.get_command_obj('foo')
+ self.assertEqual(cmd.__class__.__name__, 'FooBarBazTest')
+ self.assertEqual(cmd._record, ['foo has run'])
def test_suite():
diff --git a/distutils2/tests/test_create.py b/distutils2/tests/test_create.py
index 7a0aa0d..83d2f50 100644
--- a/distutils2/tests/test_create.py
+++ b/distutils2/tests/test_create.py
@@ -1,16 +1,18 @@
"""Tests for distutils2.create."""
import os
import sys
-from io import StringIO
from textwrap import dedent
+from distutils2 import create
from distutils2.create import MainProgram, ask_yn, ask, main
from distutils2._backport import sysconfig
from distutils2.tests import support, unittest
+from distutils2.tests.support import Inputs
class CreateTestCase(support.TempdirManager,
support.EnvironRestorer,
+ support.LoggingCatcher,
unittest.TestCase):
maxDiff = None
@@ -18,11 +20,6 @@ class CreateTestCase(support.TempdirManager,
def setUp(self):
super(CreateTestCase, self).setUp()
- self._stdin = sys.stdin # TODO use Inputs
- self._stdout = sys.stdout
- sys.stdin = StringIO()
- sys.stdout = StringIO()
- self._cwd = os.getcwd()
self.wdir = self.mkdtemp()
os.chdir(self.wdir)
# patch sysconfig
@@ -32,29 +29,24 @@ class CreateTestCase(support.TempdirManager,
'doc': sys.prefix + '/share/doc/pyxfoil', }
def tearDown(self):
- sys.stdin = self._stdin
- sys.stdout = self._stdout
- os.chdir(self._cwd)
sysconfig.get_paths = self._old_get_paths
+ if hasattr(create, 'input'):
+ del create.input
super(CreateTestCase, self).tearDown()
def test_ask_yn(self):
- sys.stdin.write('y\n')
- sys.stdin.seek(0)
+ create.input = Inputs('y')
self.assertEqual('y', ask_yn('is this a test'))
def test_ask(self):
- sys.stdin.write('a\n')
- sys.stdin.write('b\n')
- sys.stdin.seek(0)
+ create.input = Inputs('a', 'b')
self.assertEqual('a', ask('is this a test'))
self.assertEqual('b', ask(str(list(range(0, 70))), default='c',
lengthy=True))
def test_set_multi(self):
mainprogram = MainProgram()
- sys.stdin.write('aaaaa\n')
- sys.stdin.seek(0)
+ create.input = Inputs('aaaaa')
mainprogram.data['author'] = []
mainprogram._set_multi('_set_multi test', 'author')
self.assertEqual(['aaaaa'], mainprogram.data['author'])
@@ -80,8 +72,7 @@ class CreateTestCase(support.TempdirManager,
os.mkdir(os.path.join(tempdir, dir_))
for file_ in files:
- path = os.path.join(tempdir, file_)
- self.write_file(path, 'xxx')
+ self.write_file((tempdir, file_), 'xxx')
mainprogram._find_files()
mainprogram.data['packages'].sort()
@@ -131,8 +122,7 @@ class CreateTestCase(support.TempdirManager,
scripts=['my_script', 'bin/run'],
)
"""), encoding='utf-8')
- sys.stdin.write('y\n')
- sys.stdin.seek(0)
+ create.input = Inputs('y')
main()
path = os.path.join(self.wdir, 'setup.cfg')
@@ -207,9 +197,7 @@ My super Death-scription
barbar is now in the public domain,
ho, baby!
'''))
- sys.stdin.write('y\n')
- sys.stdin.seek(0)
- # FIXME Out of memory error.
+ create.input = Inputs('y')
main()
path = os.path.join(self.wdir, 'setup.cfg')
diff --git a/distutils2/tests/test_dist.py b/distutils2/tests/test_dist.py
index 22fd37b..1e9eb9c 100644
--- a/distutils2/tests/test_dist.py
+++ b/distutils2/tests/test_dist.py
@@ -1,33 +1,37 @@
"""Tests for distutils2.dist."""
import os
import sys
-import logging
import textwrap
import distutils2.dist
from distutils2.dist import Distribution
-from distutils2.command import set_command
from distutils2.command.cmd import Command
from distutils2.errors import PackagingModuleError, PackagingOptionError
from distutils2.tests import captured_stdout
from distutils2.tests import support, unittest
-from distutils2.tests.support import create_distribution
+from distutils2.tests.support import create_distribution, use_command
from distutils2.tests.support import unload
class test_dist(Command):
- """Sample distutils2 extension command."""
+ """Custom command used for testing."""
user_options = [
- ("sample-option=", "S", "help text"),
+ ('sample-option=', 'S',
+ "help text"),
]
def initialize_options(self):
self.sample_option = None
+ self._record = []
def finalize_options(self):
- pass
+ if self.sample_option is None:
+ self.sample_option = 'default value'
+
+ def run(self):
+ self._record.append('test_dist has run')
class DistributionTestCase(support.TempdirManager,
@@ -39,6 +43,8 @@ class DistributionTestCase(support.TempdirManager,
def setUp(self):
super(DistributionTestCase, self).setUp()
+ # XXX this is ugly, we should fix the functions to accept args
+ # (defaulting to sys.argv)
self.argv = sys.argv, sys.argv[:]
del sys.argv[1:]
@@ -74,7 +80,7 @@ class DistributionTestCase(support.TempdirManager,
'version': '1.2',
'home_page': 'xxxx',
'badoptname': 'xxx'})
- logs = self.get_logs(logging.WARNING)
+ logs = self.get_logs()
self.assertEqual(len(logs), 1)
self.assertIn('unknown argument', logs[0])
@@ -85,7 +91,7 @@ class DistributionTestCase(support.TempdirManager,
'version': '1.2', 'home_page': 'xxxx',
'options': {}})
- self.assertEqual([], self.get_logs(logging.WARNING))
+ self.assertEqual(self.get_logs(), [])
self.assertNotIn('options', dir(dist))
def test_non_empty_options(self):
@@ -173,7 +179,8 @@ class DistributionTestCase(support.TempdirManager,
self.write_file((temp_home, "config2.cfg"),
'[test_dist]\npre-hook.b = type')
- set_command('distutils2.tests.test_dist.test_dist')
+ use_command(self, 'distutils2.tests.test_dist.test_dist')
+
dist = create_distribution(config_files)
cmd = dist.get_command_obj("test_dist")
self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'})
@@ -201,7 +208,7 @@ class DistributionTestCase(support.TempdirManager,
record.append('post-%s' % cmd.get_command_name())
'''))
- set_command('distutils2.tests.test_dist.test_dist')
+ use_command(self, 'distutils2.tests.test_dist.test_dist')
d = create_distribution([config_file])
cmd = d.get_command_obj("test_dist")
@@ -228,7 +235,7 @@ class DistributionTestCase(support.TempdirManager,
[test_dist]
pre-hook.test = nonexistent.dotted.name'''))
- set_command('distutils2.tests.test_dist.test_dist')
+ use_command(self, 'distutils2.tests.test_dist.test_dist')
d = create_distribution([config_file])
cmd = d.get_command_obj("test_dist")
cmd.ensure_finalized()
@@ -243,7 +250,7 @@ class DistributionTestCase(support.TempdirManager,
[test_dist]
pre-hook.test = distutils2.tests.test_dist.__doc__'''))
- set_command('distutils2.tests.test_dist.test_dist')
+ use_command(self, 'distutils2.tests.test_dist.test_dist')
d = create_distribution([config_file])
cmd = d.get_command_obj("test_dist")
cmd.ensure_finalized()
diff --git a/distutils2/tests/test_manifest.py b/distutils2/tests/test_manifest.py
index 24f0510..e829fa4 100644
--- a/distutils2/tests/test_manifest.py
+++ b/distutils2/tests/test_manifest.py
@@ -1,8 +1,9 @@
"""Tests for distutils2.manifest."""
import os
-import logging
+import re
from io import StringIO
-from distutils2.manifest import Manifest
+from distutils2.errors import PackagingTemplateError
+from distutils2.manifest import Manifest, _translate_pattern, _glob_to_re
from distutils2.tests import unittest, support
@@ -26,13 +27,11 @@ class ManifestTestCase(support.TempdirManager,
support.LoggingCatcher,
unittest.TestCase):
- def setUp(self):
- super(ManifestTestCase, self).setUp()
- self.cwd = os.getcwd()
+ def assertNoWarnings(self):
+ self.assertEqual(self.get_logs(), [])
- def tearDown(self):
- os.chdir(self.cwd)
- super(ManifestTestCase, self).tearDown()
+ def assertWarnings(self):
+ self.assertNotEqual(self.get_logs(), [])
def test_manifest_reader(self):
tmpdir = self.mkdtemp()
@@ -43,7 +42,7 @@ class ManifestTestCase(support.TempdirManager,
manifest = Manifest()
manifest.read_template(MANIFEST)
- warnings = self.get_logs(logging.WARNING)
+ warnings = self.get_logs()
# the manifest should have been read and 3 warnings issued
# (we didn't provide the files)
self.assertEqual(3, len(warnings))
@@ -69,6 +68,193 @@ class ManifestTestCase(support.TempdirManager,
manifest.read_template(content)
self.assertEqual(['README', 'file1'], manifest.files)
+ def test_glob_to_re(self):
+ # simple cases
+ self.assertEqual(_glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)')
+ self.assertEqual(_glob_to_re('foo?'), 'foo[^/]\\Z(?ms)')
+ self.assertEqual(_glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)')
+
+ # special cases
+ self.assertEqual(_glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)')
+ self.assertEqual(_glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)')
+ self.assertEqual(_glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)')
+ self.assertEqual(_glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)')
+
+ def test_remove_duplicates(self):
+ manifest = Manifest()
+ manifest.files = ['a', 'b', 'a', 'g', 'c', 'g']
+ # files must be sorted beforehand
+ manifest.sort()
+ manifest.remove_duplicates()
+ self.assertEqual(manifest.files, ['a', 'b', 'c', 'g'])
+
+ def test_translate_pattern(self):
+ # blackbox test of a private function
+
+ # not regex
+ pattern = _translate_pattern('a', anchor=True, is_regex=False)
+ self.assertTrue(hasattr(pattern, 'search'))
+
+ # is a regex
+ regex = re.compile('a')
+ pattern = _translate_pattern(regex, anchor=True, is_regex=True)
+ self.assertEqual(pattern, regex)
+
+ # plain string flagged as regex
+ pattern = _translate_pattern('a', anchor=True, is_regex=True)
+ self.assertTrue(hasattr(pattern, 'search'))
+
+ # glob support
+ pattern = _translate_pattern('*.py', anchor=True, is_regex=False)
+ self.assertTrue(pattern.search('filelist.py'))
+
+ def test_exclude_pattern(self):
+ # return False if no match
+ manifest = Manifest()
+ self.assertFalse(manifest.exclude_pattern('*.py'))
+
+ # return True if files match
+ manifest = Manifest()
+ manifest.files = ['a.py', 'b.py']
+ self.assertTrue(manifest.exclude_pattern('*.py'))
+
+ # test excludes
+ manifest = Manifest()
+ manifest.files = ['a.py', 'a.txt']
+ manifest.exclude_pattern('*.py')
+ self.assertEqual(manifest.files, ['a.txt'])
+
+ def test_include_pattern(self):
+ # return False if no match
+ manifest = Manifest()
+ manifest.allfiles = []
+ self.assertFalse(manifest._include_pattern('*.py'))
+
+ # return True if files match
+ manifest = Manifest()
+ manifest.allfiles = ['a.py', 'b.txt']
+ self.assertTrue(manifest._include_pattern('*.py'))
+
+ # test * matches all files
+ manifest = Manifest()
+ self.assertIsNone(manifest.allfiles)
+ manifest.allfiles = ['a.py', 'b.txt']
+ manifest._include_pattern('*')
+ self.assertEqual(manifest.allfiles, ['a.py', 'b.txt'])
+
+ def test_process_template(self):
+ # invalid lines
+ manifest = Manifest()
+ for action in ('include', 'exclude', 'global-include',
+ 'global-exclude', 'recursive-include',
+ 'recursive-exclude', 'graft', 'prune'):
+ self.assertRaises(PackagingTemplateError,
+ manifest._process_template_line, action)
+
+ # implicit include
+ manifest = Manifest()
+ manifest.allfiles = ['a.py', 'b.txt', 'd/c.py']
+
+ manifest._process_template_line('*.py')
+ self.assertEqual(manifest.files, ['a.py'])
+ self.assertNoWarnings()
+
+ # include
+ manifest = Manifest()
+ manifest.allfiles = ['a.py', 'b.txt', 'd/c.py']
+
+ manifest._process_template_line('include *.py')
+ self.assertEqual(manifest.files, ['a.py'])
+ self.assertNoWarnings()
+
+ manifest._process_template_line('include *.rb')
+ self.assertEqual(manifest.files, ['a.py'])
+ self.assertWarnings()
+
+ # exclude
+ manifest = Manifest()
+ manifest.files = ['a.py', 'b.txt', 'd/c.py']
+
+ manifest._process_template_line('exclude *.py')
+ self.assertEqual(manifest.files, ['b.txt', 'd/c.py'])
+ self.assertNoWarnings()
+
+ manifest._process_template_line('exclude *.rb')
+ self.assertEqual(manifest.files, ['b.txt', 'd/c.py'])
+ self.assertWarnings()
+
+ # global-include
+ manifest = Manifest()
+ manifest.allfiles = ['a.py', 'b.txt', 'd/c.py']
+
+ manifest._process_template_line('global-include *.py')
+ self.assertEqual(manifest.files, ['a.py', 'd/c.py'])
+ self.assertNoWarnings()
+
+ manifest._process_template_line('global-include *.rb')
+ self.assertEqual(manifest.files, ['a.py', 'd/c.py'])
+ self.assertWarnings()
+
+ # global-exclude
+ manifest = Manifest()
+ manifest.files = ['a.py', 'b.txt', 'd/c.py']
+
+ manifest._process_template_line('global-exclude *.py')
+ self.assertEqual(manifest.files, ['b.txt'])
+ self.assertNoWarnings()
+
+ manifest._process_template_line('global-exclude *.rb')
+ self.assertEqual(manifest.files, ['b.txt'])
+ self.assertWarnings()
+
+ # recursive-include
+ manifest = Manifest()
+ manifest.allfiles = ['a.py', 'd/b.py', 'd/c.txt', 'd/d/e.py']
+
+ manifest._process_template_line('recursive-include d *.py')
+ self.assertEqual(manifest.files, ['d/b.py', 'd/d/e.py'])
+ self.assertNoWarnings()
+
+ manifest._process_template_line('recursive-include e *.py')
+ self.assertEqual(manifest.files, ['d/b.py', 'd/d/e.py'])
+ self.assertWarnings()
+
+ # recursive-exclude
+ manifest = Manifest()
+ manifest.files = ['a.py', 'd/b.py', 'd/c.txt', 'd/d/e.py']
+
+ manifest._process_template_line('recursive-exclude d *.py')
+ self.assertEqual(manifest.files, ['a.py', 'd/c.txt'])
+ self.assertNoWarnings()
+
+ manifest._process_template_line('recursive-exclude e *.py')
+ self.assertEqual(manifest.files, ['a.py', 'd/c.txt'])
+ self.assertWarnings()
+
+ # graft
+ manifest = Manifest()
+ manifest.allfiles = ['a.py', 'd/b.py', 'd/d/e.py', 'f/f.py']
+
+ manifest._process_template_line('graft d')
+ self.assertEqual(manifest.files, ['d/b.py', 'd/d/e.py'])
+ self.assertNoWarnings()
+
+ manifest._process_template_line('graft e')
+ self.assertEqual(manifest.files, ['d/b.py', 'd/d/e.py'])
+ self.assertWarnings()
+
+ # prune
+ manifest = Manifest()
+ manifest.files = ['a.py', 'd/b.py', 'd/d/e.py', 'f/f.py']
+
+ manifest._process_template_line('prune d')
+ self.assertEqual(manifest.files, ['a.py', 'f/f.py'])
+ self.assertNoWarnings()
+
+ manifest._process_template_line('prune e')
+ self.assertEqual(manifest.files, ['a.py', 'f/f.py'])
+ self.assertWarnings()
+
def test_suite():
return unittest.makeSuite(ManifestTestCase)
diff --git a/distutils2/tests/test_metadata.py b/distutils2/tests/test_metadata.py
index 82f5212..e19af0d 100644
--- a/distutils2/tests/test_metadata.py
+++ b/distutils2/tests/test_metadata.py
@@ -1,7 +1,6 @@
"""Tests for distutils2.metadata."""
import os
import sys
-import logging
from textwrap import dedent
from io import StringIO
@@ -302,7 +301,7 @@ class MetadataTestCase(LoggingCatcher,
'name': 'xxx',
'version': 'xxx',
'home_page': 'xxxx'})
- logs = self.get_logs(logging.WARNING)
+ logs = self.get_logs()
self.assertEqual(1, len(logs))
self.assertIn('not a valid version', logs[0])
@@ -418,7 +417,7 @@ class MetadataTestCase(LoggingCatcher,
# XXX check PEP and see if 3 == 3.0
metadata['Requires-Python'] = '>=2.6, <3.0'
metadata['Requires-Dist'] = ['Foo (>=2.6, <3.0)']
- self.assertEqual([], self.get_logs(logging.WARNING))
+ self.assertEqual(self.get_logs(), [])
@unittest.skip('needs to be implemented')
def test_requires_illegal(self):
diff --git a/distutils2/tests/test_mixin2to3.py b/distutils2/tests/test_mixin2to3.py
index 4d1584b..76e14d6 100644
--- a/distutils2/tests/test_mixin2to3.py
+++ b/distutils2/tests/test_mixin2to3.py
@@ -1,4 +1,3 @@
-import sys
import textwrap
from distutils2.tests import unittest, support
@@ -9,6 +8,7 @@ class Mixin2to3TestCase(support.TempdirManager,
support.LoggingCatcher,
unittest.TestCase):
+ @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_pypi_simple.py b/distutils2/tests/test_pypi_simple.py
index c587701..70c3ba3 100644
--- a/distutils2/tests/test_pypi_simple.py
+++ b/distutils2/tests/test_pypi_simple.py
@@ -87,7 +87,7 @@ class SimpleCrawlerTestCase(TempdirManager,
try:
crawler._open_url(url)
except Exception as v:
- if sys.version_info[:2] < (3, 3):
+ if sys.version_info[:2] < (3, 2, 3):
wanted = 'nonnumeric port'
else:
wanted = 'Download error'
diff --git a/distutils2/tests/test_run.py b/distutils2/tests/test_run.py
index 8645f63..7433731 100644
--- a/distutils2/tests/test_run.py
+++ b/distutils2/tests/test_run.py
@@ -33,11 +33,9 @@ class RunTestCase(support.TempdirManager,
def setUp(self):
super(RunTestCase, self).setUp()
- self.old_stdout = sys.stdout
self.old_argv = sys.argv, sys.argv[:]
def tearDown(self):
- sys.stdout = self.old_stdout
sys.argv = self.old_argv[0]
sys.argv[:] = self.old_argv[1]
super(RunTestCase, self).tearDown()
@@ -67,7 +65,7 @@ class RunTestCase(support.TempdirManager,
pythonpath = os.environ.get('PYTHONPATH')
d2parent = os.path.dirname(os.path.dirname(__file__))
if pythonpath is not None:
- pythonpath = os.pathsep.join((pythonpath, d2parent))
+ pythonpath = os.pathsep.join((pythonpath, d2parent))
else:
pythonpath = d2parent
diff --git a/distutils2/tests/test_uninstall.py b/distutils2/tests/test_uninstall.py
index fc664a3..4b66f03 100644
--- a/distutils2/tests/test_uninstall.py
+++ b/distutils2/tests/test_uninstall.py
@@ -1,6 +1,5 @@
"""Tests for the distutils2.uninstall module."""
import os
-import sys
import logging
import distutils2.util
@@ -31,19 +30,10 @@ class UninstallTestCase(support.TempdirManager,
def setUp(self):
super(UninstallTestCase, self).setUp()
- self.addCleanup(setattr, sys, 'stdout', sys.stdout)
- self.addCleanup(setattr, sys, 'stderr', sys.stderr)
- self.addCleanup(os.chdir, os.getcwd())
self.addCleanup(enable_cache)
- self.root_dir = self.mkdtemp()
- self.cwd = os.getcwd()
+ self.addCleanup(distutils2.util._path_created.clear)
disable_cache()
- def tearDown(self):
- os.chdir(self.cwd)
- distutils2.util._path_created.clear()
- super(UninstallTestCase, self).tearDown()
-
def get_path(self, dist, name):
# the dist argument must contain an install_dist command correctly
# initialized with a prefix option and finalized befored this method
@@ -61,8 +51,7 @@ class UninstallTestCase(support.TempdirManager,
kw['pkg'] = pkg
pkg_dir = os.path.join(project_dir, pkg)
- os.mkdir(pkg_dir)
- os.mkdir(os.path.join(pkg_dir, 'sub'))
+ os.makedirs(os.path.join(pkg_dir, 'sub'))
self.write_file((project_dir, 'setup.cfg'), SETUP_CFG % kw)
self.write_file((pkg_dir, '__init__.py'), '#')
@@ -85,7 +74,7 @@ class UninstallTestCase(support.TempdirManager,
dist.parse_config_files()
dist.finalize_options()
dist.run_command('install_dist',
- {'prefix': ('command line', self.root_dir)})
+ {'prefix': ('command line', self.mkdtemp())})
site_packages = self.get_path(dist, 'purelib')
return dist, site_packages
diff --git a/distutils2/tests/test_util.py b/distutils2/tests/test_util.py
index f3432ab..0edb402 100644
--- a/distutils2/tests/test_util.py
+++ b/distutils2/tests/test_util.py
@@ -10,7 +10,7 @@ import subprocess
from io import StringIO
from distutils2.errors import (
- PackagingPlatformError, PackagingByteCompileError, PackagingFileError,
+ PackagingPlatformError, PackagingFileError,
PackagingExecError, InstallationException)
from distutils2 import util
from distutils2.dist import Distribution
@@ -138,15 +138,8 @@ class UtilTestCase(support.EnvironRestorer,
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
@@ -161,17 +154,24 @@ class UtilTestCase(support.EnvironRestorer,
os.uname = self.uname
else:
del os.uname
- 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 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 _get_uname(self):
- return self._uname
+ def unmock_popen(self):
+ util.find_executable = self.old_find_executable
+ subprocess.Popen = self.old_popen
+ sys.stdout = self.old_stdout
+ sys.stderr = self.old_stderr
def test_convert_path(self):
# linux/mac
@@ -283,6 +283,7 @@ class UtilTestCase(support.EnvironRestorer,
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))
@@ -323,15 +324,12 @@ class UtilTestCase(support.EnvironRestorer,
res = get_compiler_versions()
self.assertEqual(res[2], None)
- 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')
@@ -359,56 +357,66 @@ class UtilTestCase(support.EnvironRestorer,
#
root = self.mkdtemp()
pkg1 = os.path.join(root, 'pkg1')
- os.mkdir(pkg1)
- self.write_file(os.path.join(pkg1, '__init__.py'))
- os.mkdir(os.path.join(pkg1, 'pkg2'))
- self.write_file(os.path.join(pkg1, 'pkg2', '__init__.py'))
- os.mkdir(os.path.join(pkg1, 'pkg3'))
- self.write_file(os.path.join(pkg1, 'pkg3', '__init__.py'))
- os.mkdir(os.path.join(pkg1, 'pkg3', 'pkg6'))
- self.write_file(os.path.join(pkg1, 'pkg3', 'pkg6', '__init__.py'))
- os.mkdir(os.path.join(pkg1, 'pkg4'))
- os.mkdir(os.path.join(pkg1, 'pkg4', 'pkg8'))
- self.write_file(os.path.join(pkg1, 'pkg4', 'pkg8', '__init__.py'))
- pkg5 = os.path.join(root, 'pkg5')
- os.mkdir(pkg5)
- self.write_file(os.path.join(pkg5, '__init__.py'))
+ os.makedirs(os.path.join(pkg1, 'pkg2'))
+ os.makedirs(os.path.join(pkg1, 'pkg3', 'pkg6'))
+ os.makedirs(os.path.join(pkg1, 'pkg4', 'pkg8'))
+ os.makedirs(os.path.join(root, 'pkg5'))
+ self.write_file((pkg1, '__init__.py'))
+ self.write_file((pkg1, 'pkg2', '__init__.py'))
+ self.write_file((pkg1, 'pkg3', '__init__.py'))
+ self.write_file((pkg1, 'pkg3', 'pkg6', '__init__.py'))
+ self.write_file((pkg1, 'pkg4', 'pkg8', '__init__.py'))
+ self.write_file((root, 'pkg5', '__init__.py'))
res = find_packages([root], ['pkg1.pkg2'])
- self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3',
- 'pkg1.pkg3.pkg6']))
+ self.assertEqual(sorted(res),
+ ['pkg1', 'pkg1.pkg3', 'pkg1.pkg3.pkg6', 'pkg5'])
def test_resolve_name(self):
- self.assertIs(str, resolve_name('builtins.str'))
- self.assertEqual(
- UtilTestCase.__name__,
- resolve_name("distutils2.tests.test_util.UtilTestCase").__name__)
- self.assertEqual(
- UtilTestCase.test_resolve_name.__name__,
- resolve_name("distutils2.tests.test_util.UtilTestCase."
- "test_resolve_name").__name__)
-
- self.assertRaises(ImportError, resolve_name,
- "distutils2.tests.test_util.UtilTestCaseNot")
- self.assertRaises(ImportError, resolve_name,
- "distutils2.tests.test_util.UtilTestCase."
- "nonexistent_attribute")
-
- def test_import_nested_first_time(self):
- tmp_dir = self.mkdtemp()
- os.makedirs(os.path.join(tmp_dir, 'a', 'b'))
- self.write_file(os.path.join(tmp_dir, 'a', '__init__.py'), '')
- self.write_file(os.path.join(tmp_dir, 'a', 'b', '__init__.py'), '')
- self.write_file(os.path.join(tmp_dir, 'a', 'b', 'c.py'),
- 'class Foo: pass')
-
- try:
- sys.path.append(tmp_dir)
- resolve_name("a.b.c.Foo")
- # assert nothing raised
- finally:
- sys.path.remove(tmp_dir)
-
+ # test raw module name
+ tmpdir = self.mkdtemp()
+ sys.path.append(tmpdir)
+ self.addCleanup(sys.path.remove, tmpdir)
+ self.write_file((tmpdir, 'hello.py'), '')
+
+ os.makedirs(os.path.join(tmpdir, 'a', 'b'))
+ self.write_file((tmpdir, 'a', '__init__.py'), '')
+ self.write_file((tmpdir, 'a', 'b', '__init__.py'), '')
+ self.write_file((tmpdir, 'a', 'b', 'c.py'), 'class Foo: pass')
+ self.write_file((tmpdir, 'a', 'b', 'd.py'), textwrap.dedent("""\
+ class FooBar:
+ class Bar:
+ def baz(self):
+ pass
+ """))
+
+ # check Python, C and built-in module
+ self.assertEqual(resolve_name('hello').__name__, 'hello')
+ self.assertEqual(resolve_name('_csv').__name__, '_csv')
+ self.assertEqual(resolve_name('sys').__name__, 'sys')
+
+ # test module.attr
+ self.assertIs(resolve_name('builtins.str'), str)
+ self.assertIsNone(resolve_name('hello.__doc__'))
+ self.assertEqual(resolve_name('a.b.c.Foo').__name__, 'Foo')
+ self.assertEqual(resolve_name('a.b.d.FooBar.Bar.baz').__name__, 'baz')
+
+ # error if module not found
+ self.assertRaises(ImportError, resolve_name, 'nonexistent')
+ self.assertRaises(ImportError, resolve_name, 'non.existent')
+ self.assertRaises(ImportError, resolve_name, 'a.no')
+ self.assertRaises(ImportError, resolve_name, 'a.b.no')
+ self.assertRaises(ImportError, resolve_name, 'a.b.no.no')
+ self.assertRaises(ImportError, resolve_name, 'inva-lid')
+
+ # looking up built-in names is not supported
+ self.assertRaises(ImportError, resolve_name, 'str')
+
+ # error if module found but not attr
+ self.assertRaises(ImportError, resolve_name, 'a.b.Spam')
+ self.assertRaises(ImportError, resolve_name, 'a.b.c.Spam')
+
+ @support.skip_2to3_optimize
def test_run_2to3_on_code(self):
content = "print 'test'"
converted_content = "print('test')"
@@ -422,6 +430,7 @@ class UtilTestCase(support.EnvironRestorer,
file_handle.close()
self.assertEqual(new_content, converted_content)
+ @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"
@@ -439,8 +448,6 @@ class UtilTestCase(support.EnvironRestorer,
@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
@@ -538,8 +545,6 @@ class UtilTestCase(support.EnvironRestorer,
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]
@@ -599,14 +604,6 @@ class GlobTestCaseBase(support.TempdirManager,
class GlobTestCase(GlobTestCaseBase):
- def setUp(self):
- super(GlobTestCase, self).setUp()
- self.cwd = os.getcwd()
-
- def tearDown(self):
- os.chdir(self.cwd)
- super(GlobTestCase, self).tearDown()
-
def assertGlobMatch(self, glob, spec):
tempdir = self.build_files_tree(spec)
expected = self.clean_tree(spec)
diff --git a/distutils2/tests/test_version.py b/distutils2/tests/test_version.py
index 1928bbd..ab4ab58 100644
--- a/distutils2/tests/test_version.py
+++ b/distutils2/tests/test_version.py
@@ -1,6 +1,5 @@
"""Tests for distutils2.version."""
import doctest
-import os
from distutils2.version import NormalizedVersion as V
from distutils2.version import HugeMajorVersionNumError, IrrationalVersionError
@@ -46,7 +45,6 @@ class VersionTestCase(unittest.TestCase):
def test_from_parts(self):
for v, s in self.versions:
- parts = v.parts
v2 = V.from_parts(*v.parts)
self.assertEqual(v, v2)
self.assertEqual(str(v), str(v2))
@@ -192,7 +190,7 @@ class VersionTestCase(unittest.TestCase):
'Hey (>=2.5,<2.7)')
for predicate in predicates:
- v = VersionPredicate(predicate)
+ VersionPredicate(predicate)
self.assertTrue(VersionPredicate('Hey (>=2.5,<2.7)').match('2.6'))
self.assertTrue(VersionPredicate('Ho').match('2.6'))
diff --git a/distutils2/util.py b/distutils2/util.py
index a5111b2..0f723a2 100644
--- a/distutils2/util.py
+++ b/distutils2/util.py
@@ -18,9 +18,10 @@ from inspect import getsource
from configparser import RawConfigParser
from distutils2 import logger
+from distutils2.compat import cache_from_source
from distutils2.errors import (PackagingPlatformError, PackagingFileError,
- PackagingByteCompileError, PackagingExecError,
- InstallationException, PackagingInternalError)
+ PackagingExecError, InstallationException,
+ PackagingInternalError)
from distutils2._backport import sysconfig
__all__ = [
@@ -296,7 +297,7 @@ def strtobool(val):
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 (Python 3.1) or in a __pycache__
subdirectory (3.2 and newer).
@@ -306,6 +307,9 @@ def byte_compile(py_files, optimize=0, force=False, prefix=None,
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.
@@ -327,10 +331,7 @@ def byte_compile(py_files, optimize=0, force=False, prefix=None,
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 sys.dont_write_bytecode:
- 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
@@ -384,15 +385,11 @@ files = [
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))
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)
@@ -418,17 +415,12 @@ byte_compile(files, optimize=%r, force=%r,
# Terminology from the py_compile module:
# cfile - byte-compiled file
# dfile - purported source filename (same as 'file' by default)
- if sys.version_info[1] == 1:
- cfile = file + (__debug__ and "c" or "o")
- else:
- # comply with PEP 3147 in 3.2+
- if optimize >= 0:
- cfile = imp.cache_from_source(file,
- debug_override=not optimize)
- else:
- cfile = imp.cache_from_source(file)
-
+ # The second argument to cache_from_source forces the extension to
+ # be .pyc (if true) or .pyo (if false); without it, the extension
+ # would depend on the calling Python's -O option
+ cfile = cache_from_source(file, not optimize)
dfile = file
+
if prefix:
if file[:len(prefix)] != prefix:
raise ValueError("invalid prefix: filename %r doesn't "
@@ -638,22 +630,35 @@ def find_packages(paths=(os.curdir,), exclude=()):
def resolve_name(name):
"""Resolve a name like ``module.object`` to an object and return it.
- Raise ImportError if the module or name is not found.
+ This functions supports packages and attributes without depth limitation:
+ ``package.package.module.class.class.function.attr`` is valid input.
+ However, looking up builtins is not directly supported: use
+ ``builtins.name``.
+
+ Raises ImportError if importing the module fails or if one requested
+ attribute is not found.
"""
+ if '.' not in name:
+ # shortcut
+ __import__(name)
+ return sys.modules[name]
+
+ # FIXME clean up this code!
parts = name.split('.')
cursor = len(parts)
module_name = parts[:cursor]
+ ret = ''
while cursor > 0:
try:
ret = __import__('.'.join(module_name))
break
except ImportError:
- if cursor == 0:
- raise
cursor -= 1
module_name = parts[:cursor]
- ret = ''
+
+ if ret == '':
+ raise ImportError(parts[0])
for part in parts[1:]:
try:
@@ -1331,6 +1336,8 @@ def get_install_method(path):
def copy_tree(src, dst, preserve_mode=True, preserve_times=True,
preserve_symlinks=False, update=False, verbose=True,
dry_run=False):
+ # FIXME use of this function is why we get spurious logging message on
+ # stdout when tests run; kill and replace by shutil!
from distutils.file_util import copy_file
if not dry_run and not os.path.isdir(src):
@@ -1444,8 +1451,7 @@ def encode_multipart(fields, files, boundary=None):
Returns (content_type: bytes, body: bytes) ready for http.client.HTTP.
"""
- # Taken from
- # http://code.activestate.com/recipes/146306-http-client-to-post-using-multipartform-data/
+ # Taken from http://code.activestate.com/recipes/146306
if boundary is None:
boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'