diff --git a/Doc/ACKS.txt b/Doc/ACKS.txt index 246816cb806..d79d5cc8b53 100644 --- a/Doc/ACKS.txt +++ b/Doc/ACKS.txt @@ -183,6 +183,7 @@ docs@python.org), and we'll be glad to correct the problem. * Joakim Sernbrant * Justin Sheehy * Charlie Shepherd + * Yue Shuaijie * Michael Simcich * Ionel Simionescu * Michael Sloan diff --git a/Doc/distutils/apiref.rst b/Doc/distutils/apiref.rst index 63eca411e57..eae9078843d 100644 --- a/Doc/distutils/apiref.rst +++ b/Doc/distutils/apiref.rst @@ -31,8 +31,9 @@ setup script). Indirectly provides the :class:`distutils.dist.Distribution` and +====================+================================+=============================================================+ | *name* | The name of the package | a string | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *version* | The version number of the | See :mod:`distutils.version` | - | | package | | + | *version* | The version number of the | a string | + | | package; see | | + | | :mod:`distutils.version` | | +--------------------+--------------------------------+-------------------------------------------------------------+ | *description* | A single line describing the | a string | | | package | | @@ -49,14 +50,14 @@ setup script). Indirectly provides the :class:`distutils.dist.Distribution` and | | maintainer, if different from | | | | the author | | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *maintainer_email* | The email address of the | | + | *maintainer_email* | The email address of the | a string | | | current maintainer, if | | | | different from the author | | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *url* | A URL for the package | a URL | + | *url* | A URL for the package | a string | | | (homepage) | | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *download_url* | A URL to download the package | a URL | + | *download_url* | A URL to download the package | a string | +--------------------+--------------------------------+-------------------------------------------------------------+ | *packages* | A list of Python packages that | a list of strings | | | distutils will manipulate | | @@ -68,14 +69,13 @@ setup script). Indirectly provides the :class:`distutils.dist.Distribution` and | | files to be built and | | | | installed | | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *ext_modules* | A list of Python extensions to | A list of instances of | + | *ext_modules* | A list of Python extensions to | a list of instances of | | | be built | :class:`distutils.core.Extension` | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *classifiers* | A list of categories for the | The list of available | - | | package | categorizations is available on `PyPI | - | | | `_. | + | *classifiers* | A list of categories for the | a list of strings; valid classifiers are listed on `PyPI | + | | package | `_. | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *distclass* | the :class:`Distribution` | A subclass of | + | *distclass* | the :class:`Distribution` | a subclass of | | | class to use | :class:`distutils.core.Distribution` | +--------------------+--------------------------------+-------------------------------------------------------------+ | *script_name* | The name of the setup.py | a string | @@ -85,15 +85,15 @@ setup script). Indirectly provides the :class:`distutils.dist.Distribution` and | *script_args* | Arguments to supply to the | a list of strings | | | setup script | | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *options* | default options for the setup | a string | + | *options* | default options for the setup | a dictionary | | | script | | +--------------------+--------------------------------+-------------------------------------------------------------+ | *license* | The license for the package | a string | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *keywords* | Descriptive meta-data, see | | + | *keywords* | Descriptive meta-data, see | a list of strings or a comma-separated string | | | :pep:`314` | | +--------------------+--------------------------------+-------------------------------------------------------------+ - | *platforms* | | | + | *platforms* | | a list of strings or a comma-separated string | +--------------------+--------------------------------+-------------------------------------------------------------+ | *cmdclass* | A mapping of command names to | a dictionary | | | :class:`Command` subclasses | | @@ -165,13 +165,13 @@ the full reference. +------------------------+--------------------------------+---------------------------+ | argument name | value | type | +========================+================================+===========================+ - | *name* | the full name of the | string | + | *name* | the full name of the | a string | | | extension, including any | | | | packages --- ie. *not* a | | | | filename or pathname, but | | | | Python dotted name | | +------------------------+--------------------------------+---------------------------+ - | *sources* | list of source filenames, | string | + | *sources* | list of source filenames, | a list of strings | | | relative to the distribution | | | | root (where the setup script | | | | lives), in Unix form (slash- | | @@ -184,12 +184,12 @@ the full reference. | | as source for a Python | | | | extension. | | +------------------------+--------------------------------+---------------------------+ - | *include_dirs* | list of directories to search | string | + | *include_dirs* | list of directories to search | a list of strings | | | for C/C++ header files (in | | | | Unix form for portability) | | +------------------------+--------------------------------+---------------------------+ - | *define_macros* | list of macros to define; each | (string, string) tuple or | - | | macro is defined using a | (name, ``None``) | + | *define_macros* | list of macros to define; each | a list of tuples | + | | macro is defined using a | | | | 2-tuple ``(name, value)``, | | | | where *value* is | | | | either the string to define it | | @@ -200,31 +200,31 @@ the full reference. | | on Unix C compiler command | | | | line) | | +------------------------+--------------------------------+---------------------------+ - | *undef_macros* | list of macros to undefine | string | + | *undef_macros* | list of macros to undefine | a list of strings | | | explicitly | | +------------------------+--------------------------------+---------------------------+ - | *library_dirs* | list of directories to search | string | + | *library_dirs* | list of directories to search | a list of strings | | | for C/C++ libraries at link | | | | time | | +------------------------+--------------------------------+---------------------------+ - | *libraries* | list of library names (not | string | + | *libraries* | list of library names (not | a list of strings | | | filenames or paths) to link | | | | against | | +------------------------+--------------------------------+---------------------------+ - | *runtime_library_dirs* | list of directories to search | string | + | *runtime_library_dirs* | list of directories to search | a list of strings | | | for C/C++ libraries at run | | | | time (for shared extensions, | | | | this is when the extension is | | | | loaded) | | +------------------------+--------------------------------+---------------------------+ - | *extra_objects* | list of extra files to link | string | + | *extra_objects* | list of extra files to link | a list of strings | | | with (eg. object files not | | | | implied by 'sources', static | | | | library that must be | | | | explicitly specified, binary | | | | resource files, etc.) | | +------------------------+--------------------------------+---------------------------+ - | *extra_compile_args* | any extra platform- and | string | + | *extra_compile_args* | any extra platform- and | a list of strings | | | compiler-specific information | | | | to use when compiling the | | | | source files in 'sources'. For | | @@ -235,7 +235,7 @@ the full reference. | | for other platforms it could | | | | be anything. | | +------------------------+--------------------------------+---------------------------+ - | *extra_link_args* | any extra platform- and | string | + | *extra_link_args* | any extra platform- and | a list of strings | | | compiler-specific information | | | | to use when linking object | | | | files together to create the | | @@ -244,7 +244,7 @@ the full reference. | | Similar interpretation as for | | | | 'extra_compile_args'. | | +------------------------+--------------------------------+---------------------------+ - | *export_symbols* | list of symbols to be exported | string | + | *export_symbols* | list of symbols to be exported | a list of strings | | | from a shared extension. Not | | | | used on all platforms, and not | | | | generally necessary for Python | | @@ -252,15 +252,20 @@ the full reference. | | export exactly one symbol: | | | | ``init`` + extension_name. | | +------------------------+--------------------------------+---------------------------+ - | *depends* | list of files that the | string | + | *depends* | list of files that the | a list of strings | | | extension depends on | | +------------------------+--------------------------------+---------------------------+ - | *language* | extension language (i.e. | string | + | *language* | extension language (i.e. | a string | | | ``'c'``, ``'c++'``, | | | | ``'objc'``). Will be detected | | | | from the source extensions if | | | | not provided. | | +------------------------+--------------------------------+---------------------------+ + | *optional* | specifies that a build failure | a boolean | + | | in the extension should not | | + | | abort the build process, but | | + | | simply skip the extension. | | + +------------------------+--------------------------------+---------------------------+ .. class:: Distribution diff --git a/Lib/distutils/tests/support.py b/Lib/distutils/tests/support.py index 783318e8d37..788acdaa3e6 100644 --- a/Lib/distutils/tests/support.py +++ b/Lib/distutils/tests/support.py @@ -1,7 +1,10 @@ """Support code for distutils test cases.""" import os +import sys import shutil import tempfile +import unittest +import sysconfig from copy import deepcopy import warnings @@ -9,6 +12,7 @@ from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution + def capture_warnings(func): def _capture_warnings(*args, **kw): with warnings.catch_warnings(): @@ -16,6 +20,7 @@ def capture_warnings(func): return func(*args, **kw) return _capture_warnings + class LoggingSilencer(object): def setUp(self): @@ -49,6 +54,7 @@ class LoggingSilencer(object): def clear_logs(self): self.logs = [] + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. @@ -105,6 +111,7 @@ class TempdirManager(object): return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" @@ -115,6 +122,7 @@ class DummyCommand: def ensure_finalized(self): pass + class EnvironGuard(object): def setUp(self): @@ -131,3 +139,71 @@ class EnvironGuard(object): del os.environ[key] super(EnvironGuard, self).tearDown() + + +def copy_xxmodule_c(directory): + """Helper for tests that need the xxmodule.c source file. + + Example use: + + def test_compile(self): + copy_xxmodule_c(self.tmpdir) + self.assertIn('xxmodule.c', os.listdir(self.tmpdir)) + + If the source file can be found, it will be copied to *directory*. If not, + the test will be skipped. Errors during copy are not caught. + """ + filename = _get_xxmodule_path() + if filename is None: + raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' + 'the python build dir)') + shutil.copy(filename, directory) + + +def _get_xxmodule_path(): + srcdir = sysconfig.get_config_var('srcdir') + candidates = [ + # use installed copy if available + os.path.join(os.path.dirname(__file__), 'xxmodule.c'), + # otherwise try using copy from build directory + os.path.join(srcdir, 'Modules', 'xxmodule.c'), + # srcdir mysteriously can be $srcdir/Lib/distutils/tests when + # this file is run from its parent directory, so walk up the + # tree to find the real srcdir + os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), + ] + for path in candidates: + if os.path.exists(path): + return path + + +def fixup_build_ext(cmd): + """Function needed to make build_ext tests pass. + + When Python was build with --enable-shared on Unix, -L. is not good + enough to find the libpython.so. This is because regrtest runs + it under a tempdir, not in the top level where the .so lives. By the + time we've gotten here, Python's already been chdir'd to the tempdir. + + When Python was built with in debug mode on Windows, build_ext commands + need their debug attribute set, and it is not done automatically for + some reason. + + This function handles both of these things. Example use: + + cmd = build_ext(dist) + support.fixup_build_ext(cmd) + cmd.ensure_finalized() + """ + if os.name == 'nt': + cmd.debug = sys.executable.endswith('_d.exe') + elif sysconfig.get_config_var('Py_ENABLE_SHARED'): + # To further add to the shared builds fun on Unix, we can't just add + # library_dirs to the Extension() instance because that doesn't get + # plumbed through to the final compiler command. + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py index ced1329efda..2fa63d300bb 100644 --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -1,7 +1,5 @@ import sys import os -import tempfile -import shutil from StringIO import StringIO import textwrap @@ -19,76 +17,40 @@ from test import test_support # Don't load the xx module more than once. ALREADY_TESTED = False -def _get_source_filename(): - # use installed copy if available - tests_f = os.path.join(os.path.dirname(__file__), 'xxmodule.c') - if os.path.exists(tests_f): - return tests_f - # otherwise try using copy from build directory - srcdir = sysconfig.get_config_var('srcdir') - if srcdir is None: - return os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') - return os.path.join(srcdir, 'Modules', 'xxmodule.c') - -_XX_MODULE_PATH = _get_source_filename() class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): def setUp(self): - # Create a simple test environment - # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() - self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") - if os.path.exists(_XX_MODULE_PATH): - self.sys_path = sys.path[:] - sys.path.append(self.tmp_dir) - shutil.copy(_XX_MODULE_PATH, self.tmp_dir) + self.tmp_dir = self.mkdtemp() + self.xx_created = False + sys.path.append(self.tmp_dir) + self.addCleanup(sys.path.remove, self.tmp_dir) + if sys.version > "2.6": + import site + self.old_user_base = site.USER_BASE + site.USER_BASE = self.mkdtemp() + from distutils.command import build_ext + build_ext.USER_BASE = site.USER_BASE def tearDown(self): - # Get everything back to normal - if os.path.exists(_XX_MODULE_PATH): + if self.xx_created: test_support.unload('xx') - sys.path[:] = self.sys_path # XXX on Windows the test leaves a directory # with xx module in TEMP - shutil.rmtree(self.tmp_dir, os.name == 'nt' or - sys.platform == 'cygwin') super(BuildExtTestCase, self).tearDown() - def _fixup_command(self, cmd): - # When Python was build with --enable-shared, -L. is not good enough - # to find the libpython.so. This is because regrtest runs it - # under a tempdir, not in the top level where the .so lives. By the - # time we've gotten here, Python's already been chdir'd to the - # tempdir. - # - # To further add to the fun, we can't just add library_dirs to the - # Extension() instance because that doesn't get plumbed through to the - # final compiler command. - if (sysconfig.get_config_var('Py_ENABLE_SHARED') and - not sys.platform.startswith('win')): - runshared = sysconfig.get_config_var('RUNSHARED') - if runshared is None: - cmd.library_dirs = ['.'] - else: - name, equals, value = runshared.partition('=') - cmd.library_dirs = value.split(os.pathsep) - - @unittest.skipIf(not os.path.exists(_XX_MODULE_PATH), - 'xxmodule.c not found') def test_build_ext(self): global ALREADY_TESTED + support.copy_xxmodule_c(self.tmp_dir) + self.xx_created = True xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) - self._fixup_command(cmd) - if os.name == "nt": - # On Windows, we must build a debug version iff running - # a debug build of Python - cmd.debug = sys.executable.endswith("_d.exe") + support.fixup_build_ext(cmd) cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir @@ -141,6 +103,36 @@ class BuildExtTestCase(support.TempdirManager, # make sure we get some library dirs under solaris self.assertTrue(len(cmd.library_dirs) > 0) + def test_user_site(self): + # site.USER_SITE was introduced in 2.6 + if sys.version < '2.6': + return + + import site + dist = Distribution({'name': 'xx'}) + cmd = build_ext(dist) + + # making sure the user option is there + options = [name for name, short, label in + cmd.user_options] + self.assertIn('user', options) + + # setting a value + cmd.user = 1 + + # setting user based lib and include + lib = os.path.join(site.USER_BASE, 'lib') + incl = os.path.join(site.USER_BASE, 'include') + os.mkdir(lib) + os.mkdir(incl) + + cmd.ensure_finalized() + + # see if include_dirs and library_dirs were set + self.assertIn(lib, cmd.library_dirs) + self.assertIn(lib, cmd.rpath) + self.assertIn(incl, cmd.include_dirs) + def test_finalize_options(self): # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. @@ -149,7 +141,6 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.finalize_options() - from distutils import sysconfig py_include = sysconfig.get_python_inc() self.assertTrue(py_include in cmd.include_dirs) @@ -277,13 +268,10 @@ class BuildExtTestCase(support.TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) - self._fixup_command(cmd) + support.fixup_build_ext(cmd) cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) - if os.name == "nt": - cmd.debug = sys.executable.endswith("_d.exe") - cmd.build_lib = os.path.join(self.tmp_dir, 'build') cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') @@ -509,10 +497,8 @@ class BuildExtTestCase(support.TempdirManager, cmd.build_temp = self.tmp_dir try: - old_stdout = sys.stdout cmd.ensure_finalized() cmd.run() - except CompileError: self.fail("Wrong deployment target during compilation") diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py index 4f976f34e67..ebfb04f4463 100644 --- a/Lib/distutils/tests/test_install.py +++ b/Lib/distutils/tests/test_install.py @@ -1,17 +1,33 @@ """Tests for distutils.command.install.""" import os +import sys import unittest +import site -from test.test_support import run_unittest +from test.test_support import captured_stdout, run_unittest +from distutils import sysconfig from distutils.command.install import install +from distutils.command import install as install_module +from distutils.command.build_ext import build_ext +from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution +from distutils.errors import DistutilsOptionError +from distutils.extension import Extension from distutils.tests import support -class InstallTestCase(support.TempdirManager, unittest.TestCase): +def _make_ext_name(modname): + if os.name == 'nt' and sys.executable.endswith('_d.exe'): + modname += '_d' + return modname + sysconfig.get_config_var('SO') + + +class InstallTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_home_installation_scheme(self): # This ensure two things: @@ -49,6 +65,181 @@ class InstallTestCase(support.TempdirManager, unittest.TestCase): check_path(cmd.install_scripts, os.path.join(destination, "bin")) check_path(cmd.install_data, destination) + def test_user_site(self): + # site.USER_SITE was introduced in 2.6 + if sys.version < '2.6': + return + + # preparing the environment for the test + self.old_user_base = site.USER_BASE + self.old_user_site = site.USER_SITE + self.tmpdir = self.mkdtemp() + self.user_base = os.path.join(self.tmpdir, 'B') + self.user_site = os.path.join(self.tmpdir, 'S') + site.USER_BASE = self.user_base + site.USER_SITE = self.user_site + install_module.USER_BASE = self.user_base + install_module.USER_SITE = self.user_site + + def _expanduser(path): + return self.tmpdir + self.old_expand = os.path.expanduser + os.path.expanduser = _expanduser + + try: + # this is the actual test + self._test_user_site() + finally: + site.USER_BASE = self.old_user_base + site.USER_SITE = self.old_user_site + install_module.USER_BASE = self.old_user_base + install_module.USER_SITE = self.old_user_site + os.path.expanduser = self.old_expand + + def _test_user_site(self): + for key in ('nt_user', 'unix_user', 'os2_home'): + self.assertTrue(key in INSTALL_SCHEMES) + + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # making sure the user option is there + options = [name for name, short, lable in + cmd.user_options] + self.assertTrue('user' in options) + + # setting a value + cmd.user = 1 + + # user base and site shouldn't be created yet + self.assertTrue(not os.path.exists(self.user_base)) + self.assertTrue(not os.path.exists(self.user_site)) + + # let's run finalize + cmd.ensure_finalized() + + # now they should + self.assertTrue(os.path.exists(self.user_base)) + self.assertTrue(os.path.exists(self.user_site)) + + self.assertTrue('userbase' in cmd.config_vars) + self.assertTrue('usersite' in cmd.config_vars) + + def test_handle_extra_path(self): + dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) + cmd = install(dist) + + # two elements + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, ['path', 'dirs']) + self.assertEqual(cmd.extra_dirs, 'dirs') + self.assertEqual(cmd.path_file, 'path') + + # one element + cmd.extra_path = ['path'] + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, ['path']) + self.assertEqual(cmd.extra_dirs, 'path') + self.assertEqual(cmd.path_file, 'path') + + # none + dist.extra_path = cmd.extra_path = None + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, None) + self.assertEqual(cmd.extra_dirs, '') + self.assertEqual(cmd.path_file, None) + + # three elements (no way !) + cmd.extra_path = 'path,dirs,again' + self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) + + def test_finalize_options(self): + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # must supply either prefix/exec-prefix/home or + # install-base/install-platbase -- not both + cmd.prefix = 'prefix' + cmd.install_base = 'base' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # must supply either home or prefix/exec-prefix -- not both + cmd.install_base = None + cmd.home = 'home' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # can't combine user with with prefix/exec_prefix/home or + # install_(plat)base + cmd.prefix = None + cmd.user = 'user' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + def test_record(self): + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(scripts=['hello']) + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(project_dir) + self.write_file('hello', "print('o hai')") + + cmd = install(dist) + dist.command_obj['install'] = cmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'RECORD') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['hello', + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + + def test_record_extensions(self): + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(ext_modules=[ + Extension('xx', ['xxmodule.c'])]) + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(project_dir) + support.copy_xxmodule_c(project_dir) + + buildextcmd = build_ext(dist) + support.fixup_build_ext(buildextcmd) + buildextcmd.ensure_finalized() + + cmd = install(dist) + dist.command_obj['install'] = cmd + dist.command_obj['build_ext'] = buildextcmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'RECORD') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = [_make_ext_name('xx'), + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + + def test_debug_mode(self): + # this covers the code called when DEBUG is set + old_logs_len = len(self.logs) + install_module.DEBUG = True + try: + with captured_stdout(): + self.test_record() + finally: + install_module.DEBUG = False + self.assertTrue(len(self.logs) > old_logs_len) def test_suite(): return unittest.makeSuite(InstallTestCase) diff --git a/Lib/distutils/tests/test_sdist.py b/Lib/distutils/tests/test_sdist.py index 42faa5d16ba..2f09c9bea18 100644 --- a/Lib/distutils/tests/test_sdist.py +++ b/Lib/distutils/tests/test_sdist.py @@ -423,6 +423,7 @@ class SDistTestCase(PyPIRCCommandTestCase): def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() + cmd.formats = ['gztar'] cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') self.write_file((self.tmp_dir, 'README.manual'), diff --git a/Lib/shutil.py b/Lib/shutil.py index 59a38fef76e..be83251104e 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -25,7 +25,8 @@ except ImportError: __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", "copytree", "move", "rmtree", "Error", "SpecialFileError", "ExecError", "make_archive", "get_archive_formats", - "register_archive_format", "unregister_archive_format"] + "register_archive_format", "unregister_archive_format", + "ignore_patterns"] class Error(EnvironmentError): pass