(original) (raw)

changeset: 73040:21c33aa2178b parent: 73008:5419bdf37fee user: Éric Araujo merwok@netwok.org date: Fri Oct 21 06:27:06 2011 +0200 files: Lib/packaging/tests/test_util.py Lib/packaging/util.py Misc/ACKS description: Fix missing imports in setup scripts generated by packaging (#13205). I’ve made more edits than the bug report suggested to make sure the generated setup script is compatible with many Python versions; a comment in the source explains that in detail. The cfg_to_args function uses old 2.x idioms like codecs.open and RawConfigParser.readfp because I want the setup.py generated by packaging and distutils2 to be the same. Most users won’t see the deprecation warning and I ignore it in the test suite. Thanks to David Barnett for the report and original patch. diff -r 5419bdf37fee -r 21c33aa2178b Lib/packaging/tests/test_util.py --- a/Lib/packaging/tests/test_util.py Wed Oct 19 21:32:39 2011 +0200 +++ b/Lib/packaging/tests/test_util.py Fri Oct 21 06:27:06 2011 +0200 @@ -5,11 +5,10 @@ import logging import tempfile import textwrap +import warnings import subprocess from io import StringIO -from packaging.tests import support, unittest -from packaging.tests.test_config import SETUP_CFG from packaging.errors import ( PackagingPlatformError, PackagingByteCompileError, PackagingFileError, PackagingExecError, InstallationException) @@ -20,7 +19,11 @@ get_compiler_versions, _MAC_OS_X_LD_VERSION, byte_compile, find_packages, spawn, get_pypirc_path, generate_pypirc, read_pypirc, resolve_name, iglob, RICH_GLOB, egginfo_to_distinfo, is_setuptools, is_distutils, is_packaging, - get_install_method, cfg_to_args, encode_multipart) + get_install_method, cfg_to_args, generate_setup_py, encode_multipart) + +from packaging.tests import support, unittest +from packaging.tests.test_config import SETUP_CFG +from test.script_helper import assert_python_ok, assert_python_failure PYPIRC = """\ @@ -513,7 +516,9 @@ self.write_file('setup.cfg', SETUP_CFG % opts, encoding='utf-8') self.write_file('README', 'loooong description') - args = cfg_to_args() + with warnings.catch_warnings(): + warnings.simplefilter('ignore', DeprecationWarning) + args = cfg_to_args() # use Distribution to get the contents of the setup.cfg file dist = Distribution() dist.parse_config_files() @@ -539,6 +544,26 @@ self.assertEqual(args['scripts'], dist.scripts) self.assertEqual(args['py_modules'], dist.py_modules) + def test_generate_setup_py(self): + # undo subprocess.Popen monkey-patching before using assert_python_* + subprocess.Popen = self.old_popen + os.chdir(self.mkdtemp()) + self.write_file('setup.cfg', textwrap.dedent("""\ + [metadata] + name = SPAM + classifier = Programming Language :: Python + """)) + generate_setup_py() + self.assertTrue(os.path.exists('setup.py'), 'setup.py not created') + rc, out, err = assert_python_ok('setup.py', '--name') + self.assertEqual(out, b'SPAM\n') + self.assertEqual(err, b'') + + # a generated setup.py should complain if no setup.cfg is present + os.unlink('setup.cfg') + rc, out, err = assert_python_failure('setup.py', '--name') + self.assertIn(b'setup.cfg', err) + def test_encode_multipart(self): fields = [('username', 'wok'), ('password', 'secret')] files = [('picture', 'wok.png', b'PNG89')] @@ -590,7 +615,6 @@ super(GlobTestCase, self).tearDown() def assertGlobMatch(self, glob, spec): - """""" tempdir = self.build_files_tree(spec) expected = self.clean_tree(spec) os.chdir(tempdir) diff -r 5419bdf37fee -r 21c33aa2178b Lib/packaging/util.py --- a/Lib/packaging/util.py Wed Oct 19 21:32:39 2011 +0200 +++ b/Lib/packaging/util.py Fri Oct 21 06:27:06 2011 +0200 @@ -6,6 +6,7 @@ import imp import sys import errno +import codecs import shutil import string import hashlib @@ -417,7 +418,8 @@ # cfile - byte-compiled file # dfile - purported source filename (same as 'file' by default) if optimize >= 0: - cfile = imp.cache_from_source(file, debug_override=not optimize) + cfile = imp.cache_from_source(file, + debug_override=not optimize) else: cfile = imp.cache_from_source(file) dfile = file @@ -931,6 +933,24 @@ yield file +# HOWTO change cfg_to_args +# +# This function has two major constraints: It is copied by inspect.getsource +# in generate_setup_py; it is used in generated setup.py which may be run by +# any Python version supported by distutils2 (2.4-3.3). +# +# * Keep objects like D1_D2_SETUP_ARGS static, i.e. in the function body +# instead of global. +# * If you use a function from another module, update the imports in +# SETUP_TEMPLATE. Use only modules, classes and functions compatible with +# all versions: codecs.open instead of open, RawConfigParser.readfp instead +# of read, standard exceptions instead of Packaging*Error, etc. +# * If you use a function from this module, update the template and +# generate_setup_py. +# +# test_util tests this function and the generated setup.py, but does not test +# that it's compatible with all Python versions. + def cfg_to_args(path='setup.cfg'): """Compatibility helper to use setup.cfg in setup.py. @@ -941,8 +961,6 @@ *file* is the path to the setup.cfg file. If it doesn't exist, PackagingFileError is raised. """ - # We need to declare the following constants here so that it's easier to - # generate the setup.py afterwards, using inspect.getsource. # XXX ** == needs testing D1_D2_SETUP_ARGS = {"name": ("metadata",), @@ -986,10 +1004,11 @@ # The real code starts here config = RawConfigParser() - if not os.path.exists(path): - raise PackagingFileError("file '%s' does not exist" % - os.path.abspath(path)) - config.read(path, encoding='utf-8') + f = codecs.open(path, encoding='utf-8') + try: + config.readfp(f) + finally: + f.close() kwargs = {} for arg in D1_D2_SETUP_ARGS: @@ -1011,8 +1030,11 @@ filenames = split_multiline(filenames) in_cfg_value = [] for filename in filenames: - with open(filename) as fp: + fp = codecs.open(filename, encoding='utf-8') + try: in_cfg_value.append(fp.read()) + finally: + fp.close() in_cfg_value = '\n\n'.join(in_cfg_value) else: continue @@ -1029,13 +1051,19 @@ return kwargs -_SETUP_TMPL = """\ +SETUP_TEMPLATE = """\ # This script was automatically generated by packaging import os +import codecs from distutils.core import setup -from ConfigParser import RawConfigParser +try: + from ConfigParser import RawConfigParser +except ImportError: + from configparser import RawConfigParser -%(func)s +%(split_multiline)s + +%(cfg_to_args)s setup(**cfg_to_args()) """ @@ -1049,8 +1077,10 @@ if os.path.exists("setup.py"): raise PackagingFileError("a setup.py file already exists") + source = SETUP_TEMPLATE % {'split_multiline': getsource(split_multiline), + 'cfg_to_args': getsource(cfg_to_args)} with open("setup.py", "w", encoding='utf-8') as fp: - fp.write(_SETUP_TMPL % {'func': getsource(cfg_to_args)}) + fp.write(source) # Taken from the pip project @@ -1307,6 +1337,8 @@ def copy_tree(src, dst, preserve_mode=True, preserve_times=True, preserve_symlinks=False, update=False, verbose=True, dry_run=False): + # FIXME use of this function is why we get spurious logging message on + # stdout when tests run; kill and replace by shuil! from distutils.file_util import copy_file if not dry_run and not os.path.isdir(src): diff -r 5419bdf37fee -r 21c33aa2178b Misc/ACKS --- a/Misc/ACKS Wed Oct 19 21:32:39 2011 +0200 +++ b/Misc/ACKS Fri Oct 21 06:27:06 2011 +0200 @@ -56,6 +56,7 @@ Chris Barker Nick Barnes Quentin Barnes +David Barnett Richard Barran Cesar Eduardo Barros Des Barry /merwok@netwok.org