[Python-checkins] cpython: Allow multiple setup hooks in packaging’s setup.cfg files (#12240).

eric.araujo python-checkins at python.org
Sat Jun 11 20:01:44 CEST 2011


http://hg.python.org/cpython/rev/5f0cd4844061
changeset:   70792:5f0cd4844061
user:        Éric Araujo <merwok at netwok.org>
date:        Sat Jun 11 00:33:38 2011 +0200
summary:
  Allow multiple setup hooks in packaging’s setup.cfg files (#12240).

Original patch by Erik Bray.

files:
  Doc/packaging/setupcfg.rst         |  14 ++++--
  Lib/packaging/config.py            |  37 +++++++++--------
  Lib/packaging/tests/test_config.py |  36 ++++++++++++++---
  Lib/packaging/tests/test_util.py   |   2 +-
  Misc/NEWS                          |   3 +
  5 files changed, 61 insertions(+), 31 deletions(-)


diff --git a/Doc/packaging/setupcfg.rst b/Doc/packaging/setupcfg.rst
--- a/Doc/packaging/setupcfg.rst
+++ b/Doc/packaging/setupcfg.rst
@@ -176,15 +176,19 @@
       compilers =
           hotcompiler.SmartCCompiler
 
-setup_hook
-   defines a callable that will be called right after the
-   :file:`setup.cfg` file is read. The callable receives the configuration
-   in form of a mapping and can make some changes to it. *optional*
+setup_hooks
+   Defines a list of callables to be called right after the :file:`setup.cfg`
+   file is read, before any other processing.  The callables are executed in the
+   order they're found in the file; if one of them cannot be found, tools should
+   not stop, but for example produce a warning and continue with the next line.
+   Each callable receives the configuration as a dictionary (keys are
+   :file:`setup.cfg` sections, values are dictionaries of fields) and can make
+   any changes to it.  *optional*, *multi*
 
    Example::
 
       [global]
-      setup_hook = package.setup.customize_dist
+      setup_hooks = package.setup.customize_dist
 
 
 Metadata
diff --git a/Lib/packaging/config.py b/Lib/packaging/config.py
--- a/Lib/packaging/config.py
+++ b/Lib/packaging/config.py
@@ -61,17 +61,15 @@
 
 
 class Config:
-    """Reads configuration files and work with the Distribution instance
-    """
+    """Class used to work with configuration files"""
     def __init__(self, dist):
         self.dist = dist
-        self.setup_hook = None
+        self.setup_hooks = []
 
-    def run_hook(self, config):
-        if self.setup_hook is None:
-            return
-        # the hook gets only the config
-        self.setup_hook(config)
+    def run_hooks(self, config):
+        """Run setup hooks in the order defined in the spec."""
+        for hook in self.setup_hooks:
+            hook(config)
 
     def find_config_files(self):
         """Find as many configuration files as should be processed for this
@@ -131,17 +129,20 @@
         for section in parser.sections():
             content[section] = dict(parser.items(section))
 
-        # global:setup_hook is called *first*
+        # global setup hooks are called first
         if 'global' in content:
-            if 'setup_hook' in content['global']:
-                setup_hook = content['global']['setup_hook']
-                try:
-                    self.setup_hook = resolve_name(setup_hook)
-                except ImportError as e:
-                    logger.warning('could not import setup_hook: %s',
-                            e.args[0])
-                else:
-                    self.run_hook(content)
+            if 'setup_hooks' in content['global']:
+                setup_hooks = split_multiline(content['global']['setup_hooks'])
+
+                for line in setup_hooks:
+                    try:
+                        hook = resolve_name(line)
+                    except ImportError as e:
+                        logger.warning('cannot find setup hook: %s', e.args[0])
+                    else:
+                        self.setup_hooks.append(hook)
+
+                self.run_hooks(content)
 
         metadata = self.dist.metadata
 
diff --git a/Lib/packaging/tests/test_config.py b/Lib/packaging/tests/test_config.py
--- a/Lib/packaging/tests/test_config.py
+++ b/Lib/packaging/tests/test_config.py
@@ -90,7 +90,7 @@
 compilers =
     packaging.tests.test_config.DCompiler
 
-setup_hook = %(setup-hook)s
+setup_hooks = %(setup-hooks)s
 
 
 
@@ -135,8 +135,16 @@
         pass
 
 
-def hook(content):
-    content['metadata']['version'] += '.dev1'
+def version_hook(config):
+    config['metadata']['version'] += '.dev1'
+
+
+def first_hook(config):
+    config['files']['modules'] += '\n first'
+
+
+def third_hook(config):
+    config['files']['modules'] += '\n third'
 
 
 class FooBarBazTest:
@@ -186,7 +194,7 @@
 
     def write_setup(self, kwargs=None):
         opts = {'description-file': 'README', 'extra-files': '',
-                'setup-hook': 'packaging.tests.test_config.hook'}
+                'setup-hooks': 'packaging.tests.test_config.version_hook'}
         if kwargs:
             opts.update(kwargs)
         self.write_file('setup.cfg', SETUP_CFG % opts, encoding='utf-8')
@@ -318,13 +326,27 @@
         self.assertEqual(ext.extra_compile_args, cargs)
         self.assertEqual(ext.language, 'cxx')
 
-    def test_missing_setuphook_warns(self):
-        self.write_setup({'setup-hook': 'this.does._not.exist'})
+    def test_missing_setup_hook_warns(self):
+        self.write_setup({'setup-hooks': 'this.does._not.exist'})
         self.write_file('README', 'yeah')
         dist = self.get_dist()
         logs = self.get_logs(logging.WARNING)
         self.assertEqual(1, len(logs))
-        self.assertIn('could not import setup_hook', logs[0])
+        self.assertIn('cannot find setup hook', logs[0])
+
+    def test_multiple_setup_hooks(self):
+        self.write_setup({
+            'setup-hooks': '\n  packaging.tests.test_config.first_hook'
+                           '\n  packaging.tests.test_config.missing_hook'
+                           '\n  packaging.tests.test_config.third_hook'
+        })
+        self.write_file('README', 'yeah')
+        dist = self.get_dist()
+
+        self.assertEqual(['haven', 'first', 'third'], dist.py_modules)
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('cannot find setup hook', logs[0])
 
     def test_metadata_requires_description_files_missing(self):
         self.write_setup({'description-file': 'README README2'})
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
@@ -495,7 +495,7 @@
 
     def test_cfg_to_args(self):
         opts = {'description-file': 'README', 'extra-files': '',
-                'setup-hook': 'packaging.tests.test_config.hook'}
+                'setup-hooks': 'packaging.tests.test_config.version_hook'}
         self.write_file('setup.cfg', SETUP_CFG % opts)
         self.write_file('README', 'loooong description')
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -187,6 +187,9 @@
 Library
 -------
 
+- Issue #12240: Allow multiple setup hooks in packaging's setup.cfg files.
+  Original patch by Erik Bray.
+
 - Issue #11595: Fix assorted bugs in packaging.util.cfg_to_args, a
   compatibility helper for the distutils-packaging transition.  Original patch
   by Erik Bray.

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


More information about the Python-checkins mailing list