diff options
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) |
commit | 3bb8be6d78130dfcf49c4860f0009300508ff92b (patch) | |
tree | 90596cfdab8b6969ee3ae7f6b4abeace9a8d2e06 | |
parent | 1a129c882cbe2f3b51babc047b08d9266634de2b (diff) | |
parent | 784cd4cc543f3685bee3b5e65e78ad5b68d77e04 (diff) | |
download | cpython-3bb8be6d78130dfcf49c4860f0009300508ff92b.zip cpython-3bb8be6d78130dfcf49c4860f0009300508ff92b.tar.gz cpython-3bb8be6d78130dfcf49c4860f0009300508ff92b.tar.bz2 |
Branch merge
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: @@ -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. @@ -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') |