[Python-checkins] cpython: Fix missing imports in setup scripts generated by packaging (#13205).

eric.araujo python-checkins at python.org
Fri Oct 21 15:52:23 CEST 2011


http://hg.python.org/cpython/rev/21c33aa2178b
changeset:   73040:21c33aa2178b
parent:      73008:5419bdf37fee
user:        Éric Araujo <merwok at netwok.org>
date:        Fri Oct 21 06:27:06 2011 +0200
summary:
  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.

files:
  Lib/packaging/tests/test_util.py |  34 ++++++++++-
  Lib/packaging/util.py            |  56 +++++++++++++++----
  Misc/ACKS                        |   1 +
  3 files changed, 74 insertions(+), 17 deletions(-)


diff --git a/Lib/packaging/tests/test_util.py b/Lib/packaging/tests/test_util.py
--- a/Lib/packaging/tests/test_util.py
+++ b/Lib/packaging/tests/test_util.py
@@ -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 --git a/Lib/packaging/util.py b/Lib/packaging/util.py
--- a/Lib/packaging/util.py
+++ b/Lib/packaging/util.py
@@ -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 --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -56,6 +56,7 @@
 Chris Barker
 Nick Barnes
 Quentin Barnes
+David Barnett
 Richard Barran
 Cesar Eduardo Barros
 Des Barry

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list