summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorÉric Araujo <merwok@netwok.org>2011-10-19 19:32:39 (GMT)
committerÉric Araujo <merwok@netwok.org>2011-10-19 19:32:39 (GMT)
commit3bb8be6d78130dfcf49c4860f0009300508ff92b (patch)
tree90596cfdab8b6969ee3ae7f6b4abeace9a8d2e06
parent1a129c882cbe2f3b51babc047b08d9266634de2b (diff)
parent784cd4cc543f3685bee3b5e65e78ad5b68d77e04 (diff)
downloadcpython-3bb8be6d78130dfcf49c4860f0009300508ff92b.zip
cpython-3bb8be6d78130dfcf49c4860f0009300508ff92b.tar.gz
cpython-3bb8be6d78130dfcf49c4860f0009300508ff92b.tar.bz2
Branch merge
-rw-r--r--Doc/packaging/setupcfg.rst39
-rw-r--r--Lib/packaging/command/build_py.py6
-rw-r--r--Lib/packaging/command/install_lib.py6
-rw-r--r--Lib/packaging/metadata.py7
-rw-r--r--Lib/packaging/tests/__main__.py1
-rw-r--r--Lib/packaging/tests/support.py39
-rw-r--r--Lib/packaging/tests/test_command_build_py.py35
-rw-r--r--Lib/packaging/tests/test_command_check.py45
-rw-r--r--Lib/packaging/tests/test_command_clean.py2
-rw-r--r--Lib/packaging/tests/test_command_cmd.py3
-rw-r--r--Lib/packaging/tests/test_command_install_data.py5
-rw-r--r--Lib/packaging/tests/test_command_install_dist.py21
-rw-r--r--Lib/packaging/tests/test_command_sdist.py12
-rw-r--r--Lib/packaging/tests/test_command_test.py6
-rw-r--r--Lib/packaging/tests/test_command_upload.py2
-rw-r--r--Lib/packaging/tests/test_command_upload_docs.py15
-rw-r--r--Lib/packaging/tests/test_config.py8
-rw-r--r--Lib/packaging/tests/test_create.py3
-rw-r--r--Lib/packaging/tests/test_dist.py16
-rw-r--r--Lib/packaging/tests/test_manifest.py7
-rw-r--r--Lib/packaging/tests/test_metadata.py5
-rw-r--r--Lib/packaging/tests/test_mixin2to3.py1
-rw-r--r--Lib/packaging/tests/test_uninstall.py3
-rw-r--r--Lib/packaging/tests/test_util.py100
-rw-r--r--Lib/packaging/tests/test_version.py4
-rw-r--r--Lib/packaging/util.py21
-rw-r--r--Misc/NEWS2
-rw-r--r--setup.py3
28 files changed, 253 insertions, 164 deletions
diff --git a/Doc/packaging/setupcfg.rst b/Doc/packaging/setupcfg.rst
index 1a394b5..9b09a4c 100644
--- a/Doc/packaging/setupcfg.rst
+++ b/Doc/packaging/setupcfg.rst
@@ -19,6 +19,8 @@ information that's already documented for Python in the
:local:
+.. _setupcfg-syntax:
+
Syntax
======
@@ -117,6 +119,8 @@ from most specialized to most common.
file**. This will be useful to let users publish a single file.
+.. _setupcfg-sections:
+
Description of sections and fields
==================================
@@ -149,6 +153,8 @@ command sections
on the command line.
+.. _setupcfg-section-global:
+
Global options
--------------
@@ -194,6 +200,9 @@ setup_hooks
setup_hooks = _setuphooks.customize_config
+
+.. _setupcfg-section-metadata:
+
Metadata
--------
@@ -318,6 +327,8 @@ You should not give any explicit value for metadata-version: it will be guessed
from the fields present in the file.
+.. _setupcfg-section-files:
+
Files
-----
@@ -325,7 +336,8 @@ This section describes the files included in the project.
packages_root
the root directory containing all packages and modules
- (default: current directory). *optional*
+ (default: current directory, i.e. the project's top-level
+ directory where :file:`setup.cfg` lives). *optional*
packages
a list of packages the project includes *optional*, *multi*
@@ -337,8 +349,8 @@ scripts
a list of scripts the project includes *optional*, *multi*
extra_files
- a list of patterns to include extra files *optional*,
- *multi*
+ a list of patterns for additional files to include in source distributions
+ (see :ref:`packaging-manifest`) *optional*, *multi*
Example::
@@ -747,8 +759,10 @@ We use brace expansion syntax to place all the shell and batch scripts into
{scripts} category.
-Extension sections
-------------------
+.. _setupcfg-section-extensions:
+
+Extension modules sections
+--------------------------
If a project includes extension modules written in C or C++, each one of them
needs to have its options defined in a dedicated section. Here's an example::
@@ -779,8 +793,10 @@ addition, multi-line values accept environment markers on each line, after a
``--``.
-Command sections
-----------------
+.. _setupcfg-section-commands:
+
+Commands sections
+-----------------
To pass options to commands without having to type them on the command line
for each invocation, you can write them in the :file:`setup.cfg` file, in a
@@ -803,6 +819,11 @@ section named after the command. Example::
Option values given in the configuration file can be overriden on the command
line. See :ref:`packaging-setup-config` for more information.
+These sections are also used to define :ref:`command hooks
+<packaging-command-hooks>`.
+
+
+.. _setupcfg-extensibility:
Extensibility
=============
@@ -817,6 +838,8 @@ An extension field starts with ``X-``. Example::
X-Debian-Name = python-distribute
+.. _setupcfg-changes:
+
Changes in the specification
============================
@@ -852,6 +875,8 @@ A tool written to produce 1.x should have these properties:
- May write optional fields.
+.. _setupcfg-acks:
+
Acknowledgments
===============
diff --git a/Lib/packaging/command/build_py.py b/Lib/packaging/command/build_py.py
index 0eafffa..e5b10b0 100644
--- a/Lib/packaging/command/build_py.py
+++ b/Lib/packaging/command/build_py.py
@@ -1,6 +1,7 @@
"""Build pure Python modules (just copy to build directory)."""
import os
+import imp
import sys
from glob import glob
@@ -330,9 +331,10 @@ class build_py(Command, Mixin2to3):
outputs.append(filename)
if include_bytecode:
if self.compile:
- outputs.append(filename + "c")
+ outputs.append(imp.cache_from_source(filename))
if self.optimize > 0:
- outputs.append(filename + "o")
+ outputs.append(imp.cache_from_source(filename,
+ debug_override=False))
outputs += [
os.path.join(build_dir, filename)
diff --git a/Lib/packaging/command/install_lib.py b/Lib/packaging/command/install_lib.py
index 5e81b41..558966d 100644
--- a/Lib/packaging/command/install_lib.py
+++ b/Lib/packaging/command/install_lib.py
@@ -1,6 +1,7 @@
"""Install all modules (extensions and pure Python)."""
import os
+import imp
import sys
import logging
@@ -172,9 +173,10 @@ class install_lib(Command):
if ext != PYTHON_SOURCE_EXTENSION:
continue
if self.compile:
- bytecode_files.append(py_file + "c")
+ bytecode_files.append(imp.cache_from_source(py_file))
if self.optimize > 0:
- bytecode_files.append(py_file + "o")
+ bytecode_files.append(imp.cache_from_source(
+ py_file, debug_override=False))
return bytecode_files
diff --git a/Lib/packaging/metadata.py b/Lib/packaging/metadata.py
index 9d3f8ab..2d0ffa4 100644
--- a/Lib/packaging/metadata.py
+++ b/Lib/packaging/metadata.py
@@ -185,6 +185,7 @@ _MISSING = object()
_FILESAFE = re.compile('[^A-Za-z0-9.]+')
+
class Metadata:
"""The metadata of a release.
@@ -228,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/Lib/packaging/tests/__main__.py b/Lib/packaging/tests/__main__.py
index 51daba4..00f323e 100644
--- a/Lib/packaging/tests/__main__.py
+++ b/Lib/packaging/tests/__main__.py
@@ -3,7 +3,6 @@
# Ripped from importlib tests, thanks Brett!
import os
-import sys
import unittest
from test.support import run_unittest, reap_children, reap_threads
diff --git a/Lib/packaging/tests/support.py b/Lib/packaging/tests/support.py
index cfcfe01..6e26ea4 100644
--- a/Lib/packaging/tests/support.py
+++ b/Lib/packaging/tests/support.py
@@ -82,10 +82,13 @@ class LoggingCatcher:
configured to record all messages logged to the 'packaging' 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):
@@ -109,25 +112,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
diff --git a/Lib/packaging/tests/test_command_build_py.py b/Lib/packaging/tests/test_command_build_py.py
index a978c91..9d519e3 100644
--- a/Lib/packaging/tests/test_command_build_py.py
+++ b/Lib/packaging/tests/test_command_build_py.py
@@ -102,6 +102,40 @@ class BuildPyTestCase(support.TempdirManager,
os.chdir(cwd)
sys.stdout = old_stdout
+ @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
+ def test_byte_compile(self):
+ project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
+ os.chdir(project_dir)
+ self.write_file('boiledeggs.py', 'import antigravity')
+ cmd = build_py(dist)
+ cmd.compile = True
+ cmd.build_lib = 'here'
+ cmd.finalize_options()
+ cmd.run()
+
+ found = os.listdir(cmd.build_lib)
+ self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
+ found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
+ self.assertEqual(found, ['boiledeggs.%s.pyc' % imp.get_tag()])
+
+ @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
+ 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()
+
+ found = os.listdir(cmd.build_lib)
+ 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_dont_write_bytecode(self):
# makes sure byte_compile is not used
pkg_dir, dist = self.create_dist()
@@ -118,6 +152,7 @@ class BuildPyTestCase(support.TempdirManager,
self.assertIn('byte-compiling is disabled', self.get_logs()[0])
+
def test_suite():
return unittest.makeSuite(BuildPyTestCase)
diff --git a/Lib/packaging/tests/test_command_check.py b/Lib/packaging/tests/test_command_check.py
index 3a4ab42..0b91050 100644
--- a/Lib/packaging/tests/test_command_check.py
+++ b/Lib/packaging/tests/test_command_check.py
@@ -1,6 +1,5 @@
"""Tests for distutils.command.check."""
-import logging
from packaging.command.check import check
from packaging.metadata import _HAS_DOCUTILS
from packaging.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/Lib/packaging/tests/test_command_clean.py b/Lib/packaging/tests/test_command_clean.py
index 8d29e4d..ab944ed 100644
--- a/Lib/packaging/tests/test_command_clean.py
+++ b/Lib/packaging/tests/test_command_clean.py
@@ -23,7 +23,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
diff --git a/Lib/packaging/tests/test_command_cmd.py b/Lib/packaging/tests/test_command_cmd.py
index 8ac9dce..6d00ec3 100644
--- a/Lib/packaging/tests/test_command_cmd.py
+++ b/Lib/packaging/tests/test_command_cmd.py
@@ -1,5 +1,6 @@
"""Tests for distutils.cmd."""
import os
+import logging
from packaging.command.cmd import Command
from packaging.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/Lib/packaging/tests/test_command_install_data.py b/Lib/packaging/tests/test_command_install_data.py
index 44a14d2..7f4af41 100644
--- a/Lib/packaging/tests/test_command_install_data.py
+++ b/Lib/packaging/tests/test_command_install_data.py
@@ -117,6 +117,11 @@ class InstallDataTestCase(support.TempdirManager,
dist.command_obj['install_distinfo'] = cmd
cmd.run()
+ # first a few sanity checks
+ self.assertEqual(os.listdir(scripts_dir), ['spamd'])
+ self.assertEqual(os.listdir(install_dir), ['Spamlib-0.1.dist-info'])
+
+ # now the real test
fn = os.path.join(install_dir, 'Spamlib-0.1.dist-info', 'RESOURCES')
with open(fn, encoding='utf-8') as fp:
content = fp.read().strip()
diff --git a/Lib/packaging/tests/test_command_install_dist.py b/Lib/packaging/tests/test_command_install_dist.py
index 808b568..3345d2e 100644
--- a/Lib/packaging/tests/test_command_install_dist.py
+++ b/Lib/packaging/tests/test_command_install_dist.py
@@ -1,6 +1,7 @@
"""Tests for packaging.command.install."""
import os
+import imp
import sys
from sysconfig import (get_scheme_names, get_config_vars,
_SCHEMES, get_config_var, get_path)
@@ -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
@@ -196,8 +198,9 @@ class InstallTestCase(support.TempdirManager,
content = f.read()
found = [os.path.basename(line) for line in content.splitlines()]
- expected = ['hello', 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
- self.assertEqual(found, expected)
+ expected = ['hello.py', 'hello.%s.pyc' % imp.get_tag(), '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/Lib/packaging/tests/test_command_sdist.py b/Lib/packaging/tests/test_command_sdist.py
index ddc6bf7..7ea138c 100644
--- a/Lib/packaging/tests/test_command_sdist.py
+++ b/Lib/packaging/tests/test_command_sdist.py
@@ -2,7 +2,6 @@
import os
import zipfile
import tarfile
-import logging
from packaging.tests.support import requires_zlib
@@ -221,7 +220,7 @@ class SDistTestCase(support.TempdirManager,
# with the check subcommand
cmd.ensure_finalized()
cmd.run()
- warnings = self.get_logs(logging.WARN)
+ warnings = self.get_logs()
self.assertEqual(len(warnings), 4)
# trying with a complete set of metadata
@@ -230,13 +229,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)
diff --git a/Lib/packaging/tests/test_command_test.py b/Lib/packaging/tests/test_command_test.py
index f780723..7aa1f79 100644
--- a/Lib/packaging/tests/test_command_test.py
+++ b/Lib/packaging/tests/test_command_test.py
@@ -2,7 +2,6 @@ import os
import re
import sys
import shutil
-import logging
import unittest as ut1
import packaging.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/Lib/packaging/tests/test_command_upload.py b/Lib/packaging/tests/test_command_upload.py
index 82d7360..1f68c1d 100644
--- a/Lib/packaging/tests/test_command_upload.py
+++ b/Lib/packaging/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/Lib/packaging/tests/test_command_upload_docs.py b/Lib/packaging/tests/test_command_upload_docs.py
index f443727..803e733 100644
--- a/Lib/packaging/tests/test_command_upload_docs.py
+++ b/Lib/packaging/tests/test_command_upload_docs.py
@@ -1,6 +1,7 @@
"""Tests for packaging.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/Lib/packaging/tests/test_config.py b/Lib/packaging/tests/test_config.py
index bcb55fb..e45fc11 100644
--- a/Lib/packaging/tests/test_config.py
+++ b/Lib/packaging/tests/test_config.py
@@ -1,7 +1,6 @@
"""Tests for packaging.config."""
import os
import sys
-import logging
from io import StringIO
from packaging import command
@@ -375,15 +374,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_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 +395,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])
diff --git a/Lib/packaging/tests/test_create.py b/Lib/packaging/tests/test_create.py
index b896619..16dbfb7 100644
--- a/Lib/packaging/tests/test_create.py
+++ b/Lib/packaging/tests/test_create.py
@@ -80,8 +80,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()
diff --git a/Lib/packaging/tests/test_dist.py b/Lib/packaging/tests/test_dist.py
index f912c6f..3089f36 100644
--- a/Lib/packaging/tests/test_dist.py
+++ b/Lib/packaging/tests/test_dist.py
@@ -1,13 +1,12 @@
"""Tests for packaging.dist."""
import os
import sys
-import logging
import textwrap
import packaging.dist
from packaging.dist import Distribution
-from packaging.command import set_command
+from packaging.command import set_command, _COMMANDS
from packaging.command.cmd import Command
from packaging.errors import PackagingModuleError, PackagingOptionError
from packaging.tests import captured_stdout
@@ -29,6 +28,9 @@ class test_dist(Command):
def finalize_options(self):
pass
+ def run(self):
+ pass
+
class DistributionTestCase(support.TempdirManager,
support.LoggingCatcher,
@@ -39,12 +41,18 @@ 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:]
+ self._commands = _COMMANDS.copy()
def tearDown(self):
sys.argv = self.argv[0]
sys.argv[:] = self.argv[1]
+ # XXX maybe we need a public API to remove commands
+ _COMMANDS.clear()
+ _COMMANDS.update(self._commands)
super(DistributionTestCase, self).tearDown()
@unittest.skip('needs to be updated')
@@ -74,7 +82,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 +93,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):
diff --git a/Lib/packaging/tests/test_manifest.py b/Lib/packaging/tests/test_manifest.py
index 1c7aa93..5f89331 100644
--- a/Lib/packaging/tests/test_manifest.py
+++ b/Lib/packaging/tests/test_manifest.py
@@ -1,7 +1,6 @@
"""Tests for packaging.manifest."""
import os
import re
-import logging
from io import StringIO
from packaging.errors import PackagingTemplateError
from packaging.manifest import Manifest, _translate_pattern, _glob_to_re
@@ -37,10 +36,10 @@ class ManifestTestCase(support.TempdirManager,
super(ManifestTestCase, self).tearDown()
def assertNoWarnings(self):
- self.assertEqual(self.get_logs(logging.WARNING), [])
+ self.assertEqual(self.get_logs(), [])
def assertWarnings(self):
- self.assertGreater(len(self.get_logs(logging.WARNING)), 0)
+ self.assertNotEqual(self.get_logs(), [])
def test_manifest_reader(self):
tmpdir = self.mkdtemp()
@@ -51,7 +50,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))
diff --git a/Lib/packaging/tests/test_metadata.py b/Lib/packaging/tests/test_metadata.py
index 6b7dd38..54a7af3 100644
--- a/Lib/packaging/tests/test_metadata.py
+++ b/Lib/packaging/tests/test_metadata.py
@@ -1,7 +1,6 @@
"""Tests for packaging.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/Lib/packaging/tests/test_mixin2to3.py b/Lib/packaging/tests/test_mixin2to3.py
index 14a7487..000a992 100644
--- a/Lib/packaging/tests/test_mixin2to3.py
+++ b/Lib/packaging/tests/test_mixin2to3.py
@@ -1,4 +1,3 @@
-import sys
import textwrap
from packaging.tests import unittest, support
diff --git a/Lib/packaging/tests/test_uninstall.py b/Lib/packaging/tests/test_uninstall.py
index 8f094f4..2168b6f 100644
--- a/Lib/packaging/tests/test_uninstall.py
+++ b/Lib/packaging/tests/test_uninstall.py
@@ -61,8 +61,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'), '#')
diff --git a/Lib/packaging/tests/test_util.py b/Lib/packaging/tests/test_util.py
index 3d71a96..92aa72a 100644
--- a/Lib/packaging/tests/test_util.py
+++ b/Lib/packaging/tests/test_util.py
@@ -4,6 +4,7 @@ import sys
import time
import logging
import tempfile
+import textwrap
import subprocess
from io import StringIO
@@ -355,55 +356,64 @@ 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("packaging.tests.test_util.UtilTestCase").__name__)
- self.assertEqual(
- UtilTestCase.test_resolve_name.__name__,
- resolve_name("packaging.tests.test_util.UtilTestCase."
- "test_resolve_name").__name__)
-
- self.assertRaises(ImportError, resolve_name,
- "packaging.tests.test_util.UtilTestCaseNot")
- self.assertRaises(ImportError, resolve_name,
- "packaging.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')
def test_run_2to3_on_code(self):
content = "print 'test'"
diff --git a/Lib/packaging/tests/test_version.py b/Lib/packaging/tests/test_version.py
index 2c86111..c863362 100644
--- a/Lib/packaging/tests/test_version.py
+++ b/Lib/packaging/tests/test_version.py
@@ -1,6 +1,5 @@
"""Tests for packaging.version."""
import doctest
-import os
from packaging.version import NormalizedVersion as V
from packaging.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/Lib/packaging/util.py b/Lib/packaging/util.py
index f8a8058..2af1149 100644
--- a/Lib/packaging/util.py
+++ b/Lib/packaging/util.py
@@ -630,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:
diff --git a/Misc/NEWS b/Misc/NEWS
index 178a0b3..8774334 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -1455,7 +1455,7 @@ Tests
the amount of time needed to run the tests. "make test" and "make quicktest"
now include some resource-intensive tests, but no longer run the test suite
twice to check for bugs in .pyc generation. Tools/scripts/run_test.py provides
- as an easy platform-independent way to run test suite with sensible defaults.
+ an easy platform-independent way to run test suite with sensible defaults.
- Issue #12331: The test suite for the packaging module can now run from an
installed Python.
diff --git a/setup.py b/setup.py
index 542dc49..76566bd 100644
--- a/setup.py
+++ b/setup.py
@@ -1380,8 +1380,7 @@ class PyBuildExt(build_ext):
# End multiprocessing
# Platform-specific libraries
- if any(platform.startswith(prefix)
- for prefix in ("linux", "freebsd", "gnukfreebsd")):
+ if platform.startswith(('linux', 'freebsd', 'gnukfreebsd')):
exts.append( Extension('ossaudiodev', ['ossaudiodev.c']) )
else:
missing.append('ossaudiodev')