Commit 3bb8be6d authored by Éric Araujo's avatar Éric Araujo

Branch merge

parents 1a129c88 784cd4cc
...@@ -19,6 +19,8 @@ information that's already documented for Python in the ...@@ -19,6 +19,8 @@ information that's already documented for Python in the
:local: :local:
.. _setupcfg-syntax:
Syntax Syntax
====== ======
...@@ -117,6 +119,8 @@ from most specialized to most common. ...@@ -117,6 +119,8 @@ from most specialized to most common.
file**. This will be useful to let users publish a single file. file**. This will be useful to let users publish a single file.
.. _setupcfg-sections:
Description of sections and fields Description of sections and fields
================================== ==================================
...@@ -149,6 +153,8 @@ command sections ...@@ -149,6 +153,8 @@ command sections
on the command line. on the command line.
.. _setupcfg-section-global:
Global options Global options
-------------- --------------
...@@ -194,6 +200,9 @@ setup_hooks ...@@ -194,6 +200,9 @@ setup_hooks
setup_hooks = _setuphooks.customize_config setup_hooks = _setuphooks.customize_config
.. _setupcfg-section-metadata:
Metadata Metadata
-------- --------
...@@ -318,6 +327,8 @@ You should not give any explicit value for metadata-version: it will be guessed ...@@ -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. from the fields present in the file.
.. _setupcfg-section-files:
Files Files
----- -----
...@@ -325,7 +336,8 @@ This section describes the files included in the project. ...@@ -325,7 +336,8 @@ This section describes the files included in the project.
packages_root packages_root
the root directory containing all packages and modules 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 packages
a list of packages the project includes *optional*, *multi* a list of packages the project includes *optional*, *multi*
...@@ -337,8 +349,8 @@ scripts ...@@ -337,8 +349,8 @@ scripts
a list of scripts the project includes *optional*, *multi* a list of scripts the project includes *optional*, *multi*
extra_files extra_files
a list of patterns to include extra files *optional*, a list of patterns for additional files to include in source distributions
*multi* (see :ref:`packaging-manifest`) *optional*, *multi*
Example:: Example::
...@@ -747,8 +759,10 @@ We use brace expansion syntax to place all the shell and batch scripts into ...@@ -747,8 +759,10 @@ We use brace expansion syntax to place all the shell and batch scripts into
{scripts} category. {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 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:: 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 ...@@ -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 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 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:: ...@@ -803,6 +819,11 @@ section named after the command. Example::
Option values given in the configuration file can be overriden on the command Option values given in the configuration file can be overriden on the command
line. See :ref:`packaging-setup-config` for more information. 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 Extensibility
============= =============
...@@ -817,6 +838,8 @@ An extension field starts with ``X-``. Example:: ...@@ -817,6 +838,8 @@ An extension field starts with ``X-``. Example::
X-Debian-Name = python-distribute X-Debian-Name = python-distribute
.. _setupcfg-changes:
Changes in the specification Changes in the specification
============================ ============================
...@@ -852,6 +875,8 @@ A tool written to produce 1.x should have these properties: ...@@ -852,6 +875,8 @@ A tool written to produce 1.x should have these properties:
- May write optional fields. - May write optional fields.
.. _setupcfg-acks:
Acknowledgments Acknowledgments
=============== ===============
......
"""Build pure Python modules (just copy to build directory).""" """Build pure Python modules (just copy to build directory)."""
import os import os
import imp
import sys import sys
from glob import glob from glob import glob
...@@ -330,9 +331,10 @@ class build_py(Command, Mixin2to3): ...@@ -330,9 +331,10 @@ class build_py(Command, Mixin2to3):
outputs.append(filename) outputs.append(filename)
if include_bytecode: if include_bytecode:
if self.compile: if self.compile:
outputs.append(filename + "c") outputs.append(imp.cache_from_source(filename))
if self.optimize > 0: if self.optimize > 0:
outputs.append(filename + "o") outputs.append(imp.cache_from_source(filename,
debug_override=False))
outputs += [ outputs += [
os.path.join(build_dir, filename) os.path.join(build_dir, filename)
......
"""Install all modules (extensions and pure Python).""" """Install all modules (extensions and pure Python)."""
import os import os
import imp
import sys import sys
import logging import logging
...@@ -172,9 +173,10 @@ class install_lib(Command): ...@@ -172,9 +173,10 @@ class install_lib(Command):
if ext != PYTHON_SOURCE_EXTENSION: if ext != PYTHON_SOURCE_EXTENSION:
continue continue
if self.compile: if self.compile:
bytecode_files.append(py_file + "c") bytecode_files.append(imp.cache_from_source(py_file))
if self.optimize > 0: 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 return bytecode_files
......
...@@ -185,6 +185,7 @@ _MISSING = object() ...@@ -185,6 +185,7 @@ _MISSING = object()
_FILESAFE = re.compile('[^A-Za-z0-9.]+') _FILESAFE = re.compile('[^A-Za-z0-9.]+')
class Metadata: class Metadata:
"""The metadata of a release. """The metadata of a release.
...@@ -228,10 +229,8 @@ class Metadata: ...@@ -228,10 +229,8 @@ class Metadata:
def __delitem__(self, name): def __delitem__(self, name):
field_name = self._convert_name(name) field_name = self._convert_name(name)
try: # we let a KeyError propagate
del self._fields[field_name] del self._fields[field_name]
except KeyError:
raise KeyError(name)
self._set_best_version() self._set_best_version()
def __contains__(self, name): def __contains__(self, name):
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
# Ripped from importlib tests, thanks Brett! # Ripped from importlib tests, thanks Brett!
import os import os
import sys
import unittest import unittest
from test.support import run_unittest, reap_children, reap_threads from test.support import run_unittest, reap_children, reap_threads
......
...@@ -82,10 +82,13 @@ class LoggingCatcher: ...@@ -82,10 +82,13 @@ class LoggingCatcher:
configured to record all messages logged to the 'packaging' logger. configured to record all messages logged to the 'packaging' logger.
Use get_logs to retrieve messages and self.loghandler.flush to discard Use get_logs to retrieve messages and self.loghandler.flush to discard
them. get_logs automatically flushes the logs; if you test code that them. get_logs automatically flushes the logs, unless you pass
generates logging messages but don't use get_logs, you have to flush *flush=False*, for example to make multiple calls to the method with
manually before doing other checks on logging message, otherwise you different level arguments. If your test calls some code that generates
will get irrelevant results. See example in test_command_check. 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): def setUp(self):
...@@ -109,25 +112,23 @@ class LoggingCatcher: ...@@ -109,25 +112,23 @@ class LoggingCatcher:
logger2to3.setLevel(self._old_levels[1]) logger2to3.setLevel(self._old_levels[1])
super(LoggingCatcher, self).tearDown() super(LoggingCatcher, self).tearDown()
def get_logs(self, *levels): def get_logs(self, level=logging.WARNING, flush=True):
"""Return all log messages with level in *levels*. """Return all log messages with given level.
Without explicit levels given, returns all messages. *levels* defaults *level* defaults to logging.WARNING.
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'").
Returns a list. Automatically flushes the loghandler after being For log calls with arguments (i.e. logger.info('bla bla %r', arg)),
called. 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
messages = [log.getMessage() for log in self.loghandler.buffer] if log.levelno == level]
else: if flush:
messages = [log.getMessage() for log in self.loghandler.buffer self.loghandler.flush()
if log.levelno in levels]
self.loghandler.flush()
return messages return messages
......
...@@ -102,6 +102,40 @@ class BuildPyTestCase(support.TempdirManager, ...@@ -102,6 +102,40 @@ class BuildPyTestCase(support.TempdirManager,
os.chdir(cwd) os.chdir(cwd)
sys.stdout = old_stdout 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): def test_dont_write_bytecode(self):
# makes sure byte_compile is not used # makes sure byte_compile is not used
pkg_dir, dist = self.create_dist() pkg_dir, dist = self.create_dist()
...@@ -118,6 +152,7 @@ class BuildPyTestCase(support.TempdirManager, ...@@ -118,6 +152,7 @@ class BuildPyTestCase(support.TempdirManager,
self.assertIn('byte-compiling is disabled', self.get_logs()[0]) self.assertIn('byte-compiling is disabled', self.get_logs()[0])
def test_suite(): def test_suite():
return unittest.makeSuite(BuildPyTestCase) return unittest.makeSuite(BuildPyTestCase)
......
"""Tests for distutils.command.check.""" """Tests for distutils.command.check."""
import logging
from packaging.command.check import check from packaging.command.check import check
from packaging.metadata import _HAS_DOCUTILS from packaging.metadata import _HAS_DOCUTILS
from packaging.errors import PackagingSetupError, MetadataMissingError from packaging.errors import PackagingSetupError, MetadataMissingError
...@@ -27,11 +26,11 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -27,11 +26,11 @@ class CheckTestCase(support.LoggingCatcher,
# let's run the command with no metadata at all # let's run the command with no metadata at all
# by default, check is checking the metadata # by default, check is checking the metadata
# should have some warnings # should have some warnings
cmd = self._run() self._run()
# trick: using assertNotEqual with an empty list will give us a more # trick: using assertNotEqual with an empty list will give us a more
# useful error message than assertGreater(.., 0) when the code change # useful error message than assertGreater(.., 0) when the code change
# and the test fails # and the test fails
self.assertNotEqual([], self.get_logs(logging.WARNING)) self.assertNotEqual(self.get_logs(), [])
# now let's add the required fields # now let's add the required fields
# and run it again, to make sure we don't get # and run it again, to make sure we don't get
...@@ -40,8 +39,8 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -40,8 +39,8 @@ class CheckTestCase(support.LoggingCatcher,
'author_email': 'xxx', 'author_email': 'xxx',
'name': 'xxx', 'version': '4.2', 'name': 'xxx', 'version': '4.2',
} }
cmd = self._run(metadata) self._run(metadata)
self.assertEqual([], self.get_logs(logging.WARNING)) self.assertEqual(self.get_logs(), [])
# now with the strict mode, we should # now with the strict mode, we should
# get an error if there are missing metadata # get an error if there are missing metadata
...@@ -53,8 +52,8 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -53,8 +52,8 @@ class CheckTestCase(support.LoggingCatcher,
self.loghandler.flush() self.loghandler.flush()
# and of course, no error when all metadata fields are present # and of course, no error when all metadata fields are present
cmd = self._run(metadata, strict=True) self._run(metadata, strict=True)
self.assertEqual([], self.get_logs(logging.WARNING)) self.assertEqual(self.get_logs(), [])
# now a test with non-ASCII characters # now a test with non-ASCII characters
metadata = {'home_page': 'xxx', 'author': '\u00c9ric', metadata = {'home_page': 'xxx', 'author': '\u00c9ric',
...@@ -62,15 +61,15 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -62,15 +61,15 @@ class CheckTestCase(support.LoggingCatcher,
'version': '1.2', 'version': '1.2',
'summary': 'Something about esszet \u00df', 'summary': 'Something about esszet \u00df',
'description': 'More things about esszet \u00df'} 'description': 'More things about esszet \u00df'}
cmd = self._run(metadata) self._run(metadata)
self.assertEqual([], self.get_logs(logging.WARNING)) self.assertEqual(self.get_logs(), [])
def test_check_metadata_1_2(self): def test_check_metadata_1_2(self):
# let's run the command with no metadata at all # let's run the command with no metadata at all
# by default, check is checking the metadata # by default, check is checking the metadata
# should have some warnings # should have some warnings
cmd = self._run() self._run()
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 # 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 # don't get any warning anymore let's use requires_python as a marker
...@@ -80,8 +79,8 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -80,8 +79,8 @@ class CheckTestCase(support.LoggingCatcher,
'name': 'xxx', 'version': '4.2', 'name': 'xxx', 'version': '4.2',
'requires_python': '2.4', 'requires_python': '2.4',
} }
cmd = self._run(metadata) self._run(metadata)
self.assertEqual([], self.get_logs(logging.WARNING)) self.assertEqual(self.get_logs(), [])
# now with the strict mode, we should # now with the strict mode, we should
# get an error if there are missing metadata # get an error if there are missing metadata
...@@ -99,8 +98,8 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -99,8 +98,8 @@ class CheckTestCase(support.LoggingCatcher,
# now with correct version format again # now with correct version format again
metadata['version'] = '4.2' metadata['version'] = '4.2'
cmd = self._run(metadata, strict=True) self._run(metadata, strict=True)
self.assertEqual([], self.get_logs(logging.WARNING)) self.assertEqual(self.get_logs(), [])
@unittest.skipUnless(_HAS_DOCUTILS, "requires docutils") @unittest.skipUnless(_HAS_DOCUTILS, "requires docutils")
def test_check_restructuredtext(self): def test_check_restructuredtext(self):
...@@ -109,9 +108,7 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -109,9 +108,7 @@ class CheckTestCase(support.LoggingCatcher,
pkg_info, dist = self.create_dist(description=broken_rest) pkg_info, dist = self.create_dist(description=broken_rest)
cmd = check(dist) cmd = check(dist)
cmd.check_restructuredtext() cmd.check_restructuredtext()
self.assertEqual(len(self.get_logs(logging.WARNING)), 1) self.assertEqual(len(self.get_logs()), 1)
# clear warnings from the previous call
self.loghandler.flush()
# let's see if we have an error with strict=1 # let's see if we have an error with strict=1
metadata = {'home_page': 'xxx', 'author': 'xxx', metadata = {'home_page': 'xxx', 'author': 'xxx',
...@@ -126,7 +123,7 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -126,7 +123,7 @@ class CheckTestCase(support.LoggingCatcher,
dist = self.create_dist(description='title\n=====\n\ntest \u00df')[1] dist = self.create_dist(description='title\n=====\n\ntest \u00df')[1]
cmd = check(dist) cmd = check(dist)
cmd.check_restructuredtext() cmd.check_restructuredtext()
self.assertEqual([], self.get_logs(logging.WARNING)) self.assertEqual(self.get_logs(), [])
def test_check_all(self): def test_check_all(self):
self.assertRaises(PackagingSetupError, self._run, self.assertRaises(PackagingSetupError, self._run,
...@@ -143,18 +140,18 @@ class CheckTestCase(support.LoggingCatcher, ...@@ -143,18 +140,18 @@ class CheckTestCase(support.LoggingCatcher,
} }
cmd = check(dist) cmd = check(dist)
cmd.check_hooks_resolvable() cmd.check_hooks_resolvable()
self.assertEqual(len(self.get_logs(logging.WARNING)), 1) self.assertEqual(len(self.get_logs()), 1)
def test_warn(self): def test_warn(self):
_, dist = self.create_dist() _, dist = self.create_dist()
cmd = check(dist) cmd = check(dist)
self.assertEqual([], self.get_logs()) self.assertEqual(self.get_logs(), [])
cmd.warn('hello') cmd.warn('hello')
self.assertEqual(['check: hello'], self.get_logs()) self.assertEqual(self.get_logs(), ['check: hello'])
cmd.warn('hello %s', 'world') 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') 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(): def test_suite():
......
...@@ -23,7 +23,7 @@ class cleanTestCase(support.TempdirManager, support.LoggingCatcher, ...@@ -23,7 +23,7 @@ class cleanTestCase(support.TempdirManager, support.LoggingCatcher,
if name == 'build_base': if name == 'build_base':
continue continue
for f in ('one', 'two', 'three'): for f in ('one', 'two', 'three'):
self.write_file(os.path.join(path, f)) self.write_file((path, f))
# let's run the command # let's run the command
cmd.all = True cmd.all = True
......
"""Tests for distutils.cmd.""" """Tests for distutils.cmd."""
import os import os
import logging
from packaging.command.cmd import Command from packaging.command.cmd import Command
from packaging.dist import Distribution from packaging.dist import Distribution
...@@ -43,7 +44,7 @@ class CommandTestCase(support.LoggingCatcher, ...@@ -43,7 +44,7 @@ class CommandTestCase(support.LoggingCatcher,
wanted = ["command options for 'MyCmd':", ' option1 = 1', wanted = ["command options for 'MyCmd':", ' option1 = 1',
' option2 = 1'] ' option2 = 1']
msgs = self.get_logs() msgs = self.get_logs(logging.INFO)
self.assertEqual(msgs, wanted) self.assertEqual(msgs, wanted)
def test_ensure_string(self): def test_ensure_string(self):
......
...@@ -117,6 +117,11 @@ class InstallDataTestCase(support.TempdirManager, ...@@ -117,6 +117,11 @@ class InstallDataTestCase(support.TempdirManager,
dist.command_obj['install_distinfo'] = cmd dist.command_obj['install_distinfo'] = cmd
cmd.run() 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') fn = os.path.join(install_dir, 'Spamlib-0.1.dist-info', 'RESOURCES')
with open(fn, encoding='utf-8') as fp: with open(fn, encoding='utf-8') as fp:
content = fp.read().strip() content = fp.read().strip()
......
"""Tests for packaging.command.install.""" """Tests for packaging.command.install."""
import os import os
import imp
import sys import sys
from sysconfig import (get_scheme_names, get_config_vars, from sysconfig import (get_scheme_names, get_config_vars,
_SCHEMES, get_config_var, get_path) _SCHEMES, get_config_var, get_path)
...@@ -92,21 +93,20 @@ class InstallTestCase(support.TempdirManager, ...@@ -92,21 +93,20 @@ class InstallTestCase(support.TempdirManager,
self.old_expand = os.path.expanduser self.old_expand = os.path.expanduser
os.path.expanduser = _expanduser os.path.expanduser = _expanduser
try: def cleanup():
# this is the actual test
self._test_user_site()
finally:
_CONFIG_VARS['userbase'] = self.old_user_base _CONFIG_VARS['userbase'] = self.old_user_base
_SCHEMES.set(scheme, 'purelib', self.old_user_site) _SCHEMES.set(scheme, 'purelib', self.old_user_site)
os.path.expanduser = self.old_expand os.path.expanduser = self.old_expand
def _test_user_site(self): self.addCleanup(cleanup)
schemes = get_scheme_names() schemes = get_scheme_names()
for key in ('nt_user', 'posix_user', 'os2_home'): for key in ('nt_user', 'posix_user', 'os2_home'):
self.assertIn(key, schemes) self.assertIn(key, schemes)
dist = Distribution({'name': 'xx'}) dist = Distribution({'name': 'xx'})
cmd = install_dist(dist) cmd = install_dist(dist)
# making sure the user option is there # making sure the user option is there
options = [name for name, short, lable in options = [name for name, short, lable in
cmd.user_options] cmd.user_options]
...@@ -181,9 +181,11 @@ class InstallTestCase(support.TempdirManager, ...@@ -181,9 +181,11 @@ class InstallTestCase(support.TempdirManager,
def test_old_record(self): def test_old_record(self):
# test pre-PEP 376 --record option (outside dist-info dir) # test pre-PEP 376 --record option (outside dist-info dir)
install_dir = self.mkdtemp() 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) 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) cmd = install_dist(dist)
dist.command_obj['install_dist'] = cmd dist.command_obj['install_dist'] = cmd
...@@ -196,8 +198,9 @@ class InstallTestCase(support.TempdirManager, ...@@ -196,8 +198,9 @@ class InstallTestCase(support.TempdirManager,
content = f.read() content = f.read()
found = [os.path.basename(line) for line in content.splitlines()] found = [os.path.basename(line) for line in content.splitlines()]
expected = ['hello', 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD'] expected = ['hello.py', 'hello.%s.pyc' % imp.get_tag(), 'sayhi',
self.assertEqual(found, expected) 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
self.assertEqual(sorted(found), sorted(expected))
# XXX test that fancy_getopt is okay with options named # XXX test that fancy_getopt is okay with options named
# record and no-record but unrelated # record and no-record but unrelated
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import os import os
import zipfile import zipfile
import tarfile import tarfile
import logging
from packaging.tests.support import requires_zlib from packaging.tests.support import requires_zlib
...@@ -221,7 +220,7 @@ class SDistTestCase(support.TempdirManager, ...@@ -221,7 +220,7 @@ class SDistTestCase(support.TempdirManager,
# with the check subcommand # with the check subcommand
cmd.ensure_finalized() cmd.ensure_finalized()
cmd.run() cmd.run()
warnings = self.get_logs(logging.WARN) warnings = self.get_logs()
self.assertEqual(len(warnings), 4) self.assertEqual(len(warnings), 4)
# trying with a complete set of metadata # trying with a complete set of metadata
...@@ -230,13 +229,10 @@ class SDistTestCase(support.TempdirManager, ...@@ -230,13 +229,10 @@ class SDistTestCase(support.TempdirManager,
cmd.ensure_finalized() cmd.ensure_finalized()
cmd.metadata_check = False cmd.metadata_check = False
cmd.run() cmd.run()
warnings = self.get_logs(logging.WARN) warnings = self.get_logs()
# 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
self.assertEqual(len(warnings), 2) 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): def test_show_formats(self):
__, stdout = captured_stdout(show_formats) __, stdout = captured_stdout(show_formats)
......
...@@ -2,7 +2,6 @@ import os ...@@ -2,7 +2,6 @@ import os
import re import re
import sys import sys
import shutil import shutil
import logging
import unittest as ut1 import unittest as ut1
import packaging.database import packaging.database
...@@ -140,7 +139,8 @@ class TestTest(TempdirManager, ...@@ -140,7 +139,8 @@ class TestTest(TempdirManager,
cmd.run() cmd.run()
self.assertEqual(['build has run'], record) 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 pass
def test_checks_requires(self): def test_checks_requires(self):
...@@ -149,7 +149,7 @@ class TestTest(TempdirManager, ...@@ -149,7 +149,7 @@ class TestTest(TempdirManager,
phony_project = 'ohno_ohno-impossible_1234-name_stop-that!' phony_project = 'ohno_ohno-impossible_1234-name_stop-that!'
cmd.tests_require = [phony_project] cmd.tests_require = [phony_project]
cmd.ensure_finalized() cmd.ensure_finalized()
logs = self.get_logs(logging.WARNING) logs = self.get_logs()
self.assertIn(phony_project, logs[-1]) self.assertIn(phony_project, logs[-1])
def prepare_a_module(self): def prepare_a_module(self):
......
...@@ -129,7 +129,7 @@ class UploadTestCase(support.TempdirManager, support.EnvironRestorer, ...@@ -129,7 +129,7 @@ class UploadTestCase(support.TempdirManager, support.EnvironRestorer,
dist_files = [(command, pyversion, filename)] dist_files = [(command, pyversion, filename)]
docs_path = os.path.join(self.tmp_dir, "build", "docs") docs_path = os.path.join(self.tmp_dir, "build", "docs")
os.makedirs(docs_path) 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) self.write_file(self.rc, PYPIRC)
# let's run it # let's run it
......
"""Tests for packaging.command.upload_docs.""" """Tests for packaging.command.upload_docs."""
import os import os
import shutil import shutil
import logging
import zipfile import zipfile
try: try:
import _ssl import _ssl
...@@ -70,9 +71,8 @@ class UploadDocsTestCase(support.TempdirManager, ...@@ -70,9 +71,8 @@ class UploadDocsTestCase(support.TempdirManager,
if sample_dir is None: if sample_dir is None:
sample_dir = self.mkdtemp() sample_dir = self.mkdtemp()
os.mkdir(os.path.join(sample_dir, "docs")) os.mkdir(os.path.join(sample_dir, "docs"))
self.write_file(os.path.join(sample_dir, "docs", "index.html"), self.write_file((sample_dir, "docs", "index.html"), "Ce mortel ennui")
"Ce mortel ennui") self.write_file((sample_dir, "index.html"), "Oh la la")
self.write_file(os.path.join(sample_dir, "index.html"), "Oh la la")
return sample_dir return sample_dir
def test_zip_dir(self): def test_zip_dir(self):
...@@ -141,13 +141,16 @@ class UploadDocsTestCase(support.TempdirManager, ...@@ -141,13 +141,16 @@ class UploadDocsTestCase(support.TempdirManager,
self.pypi.default_response_status = '403 Forbidden' self.pypi.default_response_status = '403 Forbidden'
self.prepare_command() self.prepare_command()
self.cmd.run() 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_status = '301 Moved Permanently'
self.pypi.default_response_headers.append( self.pypi.default_response_headers.append(
("Location", "brand_new_location")) ("Location", "brand_new_location"))
self.cmd.run() 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): def test_reads_pypirc_data(self):
self.write_file(self.rc, PYPIRC % self.pypi.full_address) self.write_file(self.rc, PYPIRC % self.pypi.full_address)
...@@ -171,7 +174,7 @@ class UploadDocsTestCase(support.TempdirManager, ...@@ -171,7 +174,7 @@ class UploadDocsTestCase(support.TempdirManager,
self.prepare_command() self.prepare_command()
self.cmd.show_response = True self.cmd.show_response = True
self.cmd.run() self.cmd.run()
record = self.get_logs()[-1] record = self.get_logs(logging.INFO)[-1]
self.assertTrue(record, "should report the response") self.assertTrue(record, "should report the response")
self.assertIn(self.pypi.default_response_data, record) self.assertIn(self.pypi.default_response_data, record)
......
"""Tests for packaging.config.""" """Tests for packaging.config."""
import os import os
import sys import sys
import logging
from io import StringIO from io import StringIO
from packaging import command from packaging import command
...@@ -375,15 +374,14 @@ class ConfigTestCase(support.TempdirManager, ...@@ -375,15 +374,14 @@ class ConfigTestCase(support.TempdirManager,
self.write_file('README', 'yeah') self.write_file('README', 'yeah')
self.write_file('hooks.py', HOOKS_MODULE) self.write_file('hooks.py', HOOKS_MODULE)
self.get_dist() self.get_dist()
logs = self.get_logs(logging.WARNING) self.assertEqual(['logging_hook called'], self.get_logs())
self.assertEqual(['logging_hook called'], logs)
self.assertIn('hooks', sys.modules) self.assertIn('hooks', sys.modules)
def test_missing_setup_hook_warns(self): def test_missing_setup_hook_warns(self):
self.write_setup({'setup-hooks': 'this.does._not.exist'}) self.write_setup({'setup-hooks': 'this.does._not.exist'})
self.write_file('README', 'yeah') self.write_file('README', 'yeah')
self.get_dist() self.get_dist()
logs = self.get_logs(logging.WARNING) logs = self.get_logs()
self.assertEqual(1, len(logs)) self.assertEqual(1, len(logs))
self.assertIn('cannot find setup hook', logs[0]) self.assertIn('cannot find setup hook', logs[0])
...@@ -397,7 +395,7 @@ class ConfigTestCase(support.TempdirManager, ...@@ -397,7 +395,7 @@ class ConfigTestCase(support.TempdirManager,
dist = self.get_dist() dist = self.get_dist()
self.assertEqual(['haven', 'first', 'third'], dist.py_modules) self.assertEqual(['haven', 'first', 'third'], dist.py_modules)
logs = self.get_logs(logging.WARNING) logs = self.get_logs()
self.assertEqual(1, len(logs)) self.assertEqual(1, len(logs))
self.assertIn('cannot find setup hook', logs[0]) self.assertIn('cannot find setup hook', logs[0])
......
...@@ -80,8 +80,7 @@ class CreateTestCase(support.TempdirManager, ...@@ -80,8 +80,7 @@ class CreateTestCase(support.TempdirManager,
os.mkdir(os.path.join(tempdir, dir_)) os.mkdir(os.path.join(tempdir, dir_))
for file_ in files: for file_ in files:
path = os.path.join(tempdir, file_) self.write_file((tempdir, file_), 'xxx')
self.write_file(path, 'xxx')
mainprogram._find_files() mainprogram._find_files()
mainprogram.data['packages'].sort() mainprogram.data['packages'].sort()
......
"""Tests for packaging.dist.""" """Tests for packaging.dist."""
import os import os
import sys import sys
import logging
import textwrap import textwrap
import packaging.dist import packaging.dist
from packaging.dist import Distribution 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.command.cmd import Command
from packaging.errors import PackagingModuleError, PackagingOptionError from packaging.errors import PackagingModuleError, PackagingOptionError
from packaging.tests import captured_stdout from packaging.tests import captured_stdout
...@@ -29,6 +28,9 @@ class test_dist(Command): ...@@ -29,6 +28,9 @@ class test_dist(Command):
def finalize_options(self): def finalize_options(self):
pass pass
def run(self):
pass
class DistributionTestCase(support.TempdirManager, class DistributionTestCase(support.TempdirManager,
support.LoggingCatcher, support.LoggingCatcher,
...@@ -39,12 +41,18 @@ class DistributionTestCase(support.TempdirManager, ...@@ -39,12 +41,18 @@ class DistributionTestCase(support.TempdirManager,
def setUp(self): def setUp(self):
super(DistributionTestCase, self).setUp() 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[:] self.argv = sys.argv, sys.argv[:]
del sys.argv[1:] del sys.argv[1:]
self._commands = _COMMANDS.copy()
def tearDown(self): def tearDown(self):
sys.argv = self.argv[0] sys.argv = self.argv[0]
sys.argv[:] = self.argv[1] 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() super(DistributionTestCase, self).tearDown()
@unittest.skip('needs to be updated') @unittest.skip('needs to be updated')
...@@ -74,7 +82,7 @@ class DistributionTestCase(support.TempdirManager, ...@@ -74,7 +82,7 @@ class DistributionTestCase(support.TempdirManager,
'version': '1.2', 'version': '1.2',
'home_page': 'xxxx', 'home_page': 'xxxx',
'badoptname': 'xxx'}) 'badoptname': 'xxx'})
logs = self.get_logs(logging.WARNING) logs = self.get_logs()
self.assertEqual(len(logs), 1) self.assertEqual(len(logs), 1)
self.assertIn('unknown argument', logs[0]) self.assertIn('unknown argument', logs[0])
...@@ -85,7 +93,7 @@ class DistributionTestCase(support.TempdirManager, ...@@ -85,7 +93,7 @@ class DistributionTestCase(support.TempdirManager,
'version': '1.2', 'home_page': 'xxxx', 'version': '1.2', 'home_page': 'xxxx',
'options': {}}) 'options': {}})
self.assertEqual([], self.get_logs(logging.WARNING)) self.assertEqual(self.get_logs(), [])
self.assertNotIn('options', dir(dist)) self.assertNotIn('options', dir(dist))
def test_non_empty_options(self): def test_non_empty_options(self):
......
"""Tests for packaging.manifest.""" """Tests for packaging.manifest."""
import os import os
import re import re
import logging
from io import StringIO from io import StringIO
from packaging.errors import PackagingTemplateError from packaging.errors import PackagingTemplateError
from packaging.manifest import Manifest, _translate_pattern, _glob_to_re from packaging.manifest import Manifest, _translate_pattern, _glob_to_re
...@@ -37,10 +36,10 @@ class ManifestTestCase(support.TempdirManager, ...@@ -37,10 +36,10 @@ class ManifestTestCase(support.TempdirManager,
super(ManifestTestCase, self).tearDown() super(ManifestTestCase, self).tearDown()
def assertNoWarnings(self): def assertNoWarnings(self):
self.assertEqual(self.get_logs(logging.WARNING), []) self.assertEqual(self.get_logs(), [])
def assertWarnings(self): def assertWarnings(self):
self.assertGreater(len(self.get_logs(logging.WARNING)), 0) self.assertNotEqual(self.get_logs(), [])
def test_manifest_reader(self): def test_manifest_reader(self):
tmpdir = self.mkdtemp() tmpdir = self.mkdtemp()
...@@ -51,7 +50,7 @@ class ManifestTestCase(support.TempdirManager, ...@@ -51,7 +50,7 @@ class ManifestTestCase(support.TempdirManager,
manifest = Manifest() manifest = Manifest()
manifest.read_template(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 # the manifest should have been read and 3 warnings issued
# (we didn't provide the files) # (we didn't provide the files)
self.assertEqual(3, len(warnings)) self.assertEqual(3, len(warnings))
......
"""Tests for packaging.metadata.""" """Tests for packaging.metadata."""
import os import os
import sys import sys
import logging
from textwrap import dedent from textwrap import dedent
from io import StringIO from io import StringIO
...@@ -302,7 +301,7 @@ class MetadataTestCase(LoggingCatcher, ...@@ -302,7 +301,7 @@ class MetadataTestCase(LoggingCatcher,
'name': 'xxx', 'name': 'xxx',
'version': 'xxx', 'version': 'xxx',
'home_page': 'xxxx'}) 'home_page': 'xxxx'})
logs = self.get_logs(logging.WARNING) logs = self.get_logs()
self.assertEqual(1, len(logs)) self.assertEqual(1, len(logs))
self.assertIn('not a valid version', logs[0]) self.assertIn('not a valid version', logs[0])
...@@ -418,7 +417,7 @@ class MetadataTestCase(LoggingCatcher, ...@@ -418,7 +417,7 @@ class MetadataTestCase(LoggingCatcher,
# XXX check PEP and see if 3 == 3.0 # XXX check PEP and see if 3 == 3.0
metadata['Requires-Python'] = '>=2.6, <3.0' metadata['Requires-Python'] = '>=2.6, <3.0'
metadata['Requires-Dist'] = ['Foo (>=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') @unittest.skip('needs to be implemented')
def test_requires_illegal(self): def test_requires_illegal(self):
......
import sys
import textwrap import textwrap
from packaging.tests import unittest, support from packaging.tests import unittest, support
......
...@@ -61,8 +61,7 @@ class UninstallTestCase(support.TempdirManager, ...@@ -61,8 +61,7 @@ class UninstallTestCase(support.TempdirManager,
kw['pkg'] = pkg kw['pkg'] = pkg
pkg_dir = os.path.join(project_dir, pkg) pkg_dir = os.path.join(project_dir, pkg)
os.mkdir(pkg_dir) os.makedirs(os.path.join(pkg_dir, 'sub'))
os.mkdir(os.path.join(pkg_dir, 'sub'))
self.write_file((project_dir, 'setup.cfg'), SETUP_CFG % kw) self.write_file((project_dir, 'setup.cfg'), SETUP_CFG % kw)
self.write_file((pkg_dir, '__init__.py'), '#') self.write_file((pkg_dir, '__init__.py'), '#')
......
...@@ -4,6 +4,7 @@ import sys ...@@ -4,6 +4,7 @@ import sys
import time import time
import logging import logging
import tempfile import tempfile
import textwrap
import subprocess import subprocess
from io import StringIO from io import StringIO
...@@ -355,55 +356,64 @@ class UtilTestCase(support.EnvironRestorer, ...@@ -355,55 +356,64 @@ class UtilTestCase(support.EnvironRestorer,
# #
root = self.mkdtemp() root = self.mkdtemp()
pkg1 = os.path.join(root, 'pkg1') pkg1 = os.path.join(root, 'pkg1')
os.mkdir(pkg1) os.makedirs(os.path.join(pkg1, 'pkg2'))
self.write_file(os.path.join(pkg1, '__init__.py')) os.makedirs(os.path.join(pkg1, 'pkg3', 'pkg6'))
os.mkdir(os.path.join(pkg1, 'pkg2')) os.makedirs(os.path.join(pkg1, 'pkg4', 'pkg8'))
self.write_file(os.path.join(pkg1, 'pkg2', '__init__.py')) os.makedirs(os.path.join(root, 'pkg5'))
os.mkdir(os.path.join(pkg1, 'pkg3')) self.write_file((pkg1, '__init__.py'))
self.write_file(os.path.join(pkg1, 'pkg3', '__init__.py')) self.write_file((pkg1, 'pkg2', '__init__.py'))
os.mkdir(os.path.join(pkg1, 'pkg3', 'pkg6')) self.write_file((pkg1, 'pkg3', '__init__.py'))
self.write_file(os.path.join(pkg1, 'pkg3', 'pkg6', '__init__.py')) self.write_file((pkg1, 'pkg3', 'pkg6', '__init__.py'))
os.mkdir(os.path.join(pkg1, 'pkg4')) self.write_file((pkg1, 'pkg4', 'pkg8', '__init__.py'))
os.mkdir(os.path.join(pkg1, 'pkg4', 'pkg8')) self.write_file((root, 'pkg5', '__init__.py'))
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'))
res = find_packages([root], ['pkg1.pkg2']) res = find_packages([root], ['pkg1.pkg2'])
self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', self.assertEqual(sorted(res),
'pkg1.pkg3.pkg6'])) ['pkg1', 'pkg1.pkg3', 'pkg1.pkg3.pkg6', 'pkg5'])
def test_resolve_name(self): def test_resolve_name(self):
self.assertIs(str, resolve_name('builtins.str')) # test raw module name
self.assertEqual( tmpdir = self.mkdtemp()
UtilTestCase.__name__, sys.path.append(tmpdir)
resolve_name("packaging.tests.test_util.UtilTestCase").__name__) self.addCleanup(sys.path.remove, tmpdir)
self.assertEqual( self.write_file((tmpdir, 'hello.py'), '')
UtilTestCase.test_resolve_name.__name__,
resolve_name("packaging.tests.test_util.UtilTestCase." os.makedirs(os.path.join(tmpdir, 'a', 'b'))
"test_resolve_name").__name__) self.write_file((tmpdir, 'a', '__init__.py'), '')
self.write_file((tmpdir, 'a', 'b', '__init__.py'), '')
self.assertRaises(ImportError, resolve_name, self.write_file((tmpdir, 'a', 'b', 'c.py'), 'class Foo: pass')
"packaging.tests.test_util.UtilTestCaseNot") self.write_file((tmpdir, 'a', 'b', 'd.py'), textwrap.dedent("""\
self.assertRaises(ImportError, resolve_name, class FooBar:
"packaging.tests.test_util.UtilTestCase." class Bar:
"nonexistent_attribute") def baz(self):
pass
def test_import_nested_first_time(self): """))
tmp_dir = self.mkdtemp()
os.makedirs(os.path.join(tmp_dir, 'a', 'b')) # check Python, C and built-in module
self.write_file(os.path.join(tmp_dir, 'a', '__init__.py'), '') self.assertEqual(resolve_name('hello').__name__, 'hello')
self.write_file(os.path.join(tmp_dir, 'a', 'b', '__init__.py'), '') self.assertEqual(resolve_name('_csv').__name__, '_csv')
self.write_file(os.path.join(tmp_dir, 'a', 'b', 'c.py'), self.assertEqual(resolve_name('sys').__name__, 'sys')
'class Foo: pass')
# test module.attr
try: self.assertIs(resolve_name('builtins.str'), str)
sys.path.append(tmp_dir) self.assertIsNone(resolve_name('hello.__doc__'))
resolve_name("a.b.c.Foo") self.assertEqual(resolve_name('a.b.c.Foo').__name__, 'Foo')
# assert nothing raised self.assertEqual(resolve_name('a.b.d.FooBar.Bar.baz').__name__, 'baz')
finally:
sys.path.remove(tmp_dir) # 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): def test_run_2to3_on_code(self):
content = "print 'test'" content = "print 'test'"
......
"""Tests for packaging.version.""" """Tests for packaging.version."""
import doctest import doctest
import os
from packaging.version import NormalizedVersion as V from packaging.version import NormalizedVersion as V
from packaging.version import HugeMajorVersionNumError, IrrationalVersionError from packaging.version import HugeMajorVersionNumError, IrrationalVersionError
...@@ -46,7 +45,6 @@ class VersionTestCase(unittest.TestCase): ...@@ -46,7 +45,6 @@ class VersionTestCase(unittest.TestCase):
def test_from_parts(self): def test_from_parts(self):
for v, s in self.versions: for v, s in self.versions:
parts = v.parts
v2 = V.from_parts(*v.parts) v2 = V.from_parts(*v.parts)
self.assertEqual(v, v2) self.assertEqual(v, v2)
self.assertEqual(str(v), str(v2)) self.assertEqual(str(v), str(v2))
...@@ -192,7 +190,7 @@ class VersionTestCase(unittest.TestCase): ...@@ -192,7 +190,7 @@ class VersionTestCase(unittest.TestCase):
'Hey (>=2.5,<2.7)') 'Hey (>=2.5,<2.7)')
for predicate in predicates: for predicate in predicates:
v = VersionPredicate(predicate) VersionPredicate(predicate)
self.assertTrue(VersionPredicate('Hey (>=2.5,<2.7)').match('2.6')) self.assertTrue(VersionPredicate('Hey (>=2.5,<2.7)').match('2.6'))
self.assertTrue(VersionPredicate('Ho').match('2.6')) self.assertTrue(VersionPredicate('Ho').match('2.6'))
......
...@@ -630,22 +630,35 @@ def find_packages(paths=(os.curdir,), exclude=()): ...@@ -630,22 +630,35 @@ def find_packages(paths=(os.curdir,), exclude=()):
def resolve_name(name): def resolve_name(name):
"""Resolve a name like ``module.object`` to an object and return it. """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('.') parts = name.split('.')
cursor = len(parts) cursor = len(parts)
module_name = parts[:cursor] module_name = parts[:cursor]
ret = ''
while cursor > 0: while cursor > 0:
try: try:
ret = __import__('.'.join(module_name)) ret = __import__('.'.join(module_name))
break break
except ImportError: except ImportError:
if cursor == 0:
raise
cursor -= 1 cursor -= 1
module_name = parts[:cursor] module_name = parts[:cursor]
ret = ''
if ret == '':
raise ImportError(parts[0])
for part in parts[1:]: for part in parts[1:]:
try: try:
......
...@@ -1455,7 +1455,7 @@ Tests ...@@ -1455,7 +1455,7 @@ Tests
the amount of time needed to run the tests. "make test" and "make quicktest" 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 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 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 - Issue #12331: The test suite for the packaging module can now run from an
installed Python. installed Python.
......
...@@ -1380,8 +1380,7 @@ class PyBuildExt(build_ext): ...@@ -1380,8 +1380,7 @@ class PyBuildExt(build_ext):
# End multiprocessing # End multiprocessing
# Platform-specific libraries # Platform-specific libraries
if any(platform.startswith(prefix) if platform.startswith(('linux', 'freebsd', 'gnukfreebsd')):
for prefix in ("linux", "freebsd", "gnukfreebsd")):
exts.append( Extension('ossaudiodev', ['ossaudiodev.c']) ) exts.append( Extension('ossaudiodev', ['ossaudiodev.c']) )
else: else:
missing.append('ossaudiodev') missing.append('ossaudiodev')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment