diff -r d8502fee4638 Doc/packaging/setupcfg.rst --- a/Doc/packaging/setupcfg.rst Mon Jun 06 17:13:08 2011 +0200 +++ b/Doc/packaging/setupcfg.rst Tue Jun 07 15:46:07 2011 -0400 @@ -153,7 +153,7 @@ commands - Defined Packaging command. A command is defined by its fully + Defines a Packaging command. A command is defined by its fully qualified name. *optional*, *multi* Examples:: @@ -164,7 +164,7 @@ package.setup.BdistDeb compilers - Defined Packaging compiler. A compiler is defined by its fully + Defines a Packaging compiler. A compiler is defined by its fully qualified name. *optional*, *multi* Example:: @@ -174,9 +174,11 @@ hotcompiler.SmartCCompiler setup_hook - defines a callable that will be called right after the + 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* + in form of a mapping and can make some changes to it. If more than one + callables are specified, they are called in the order listed. + *optional*, *multi* Example:: diff -r d8502fee4638 Lib/packaging/config.py --- a/Lib/packaging/config.py Mon Jun 06 17:13:08 2011 +0200 +++ b/Lib/packaging/config.py Tue Jun 07 15:46:07 2011 -0400 @@ -9,7 +9,8 @@ from packaging import logger from packaging.errors import PackagingOptionError from packaging.compiler.extension import Extension -from packaging.util import check_environ, iglob, resolve_name, strtobool +from packaging.util import check_environ, iglob, resolve_name, strtobool, \ + split_multiline from packaging.compiler import set_compiler from packaging.command import set_command from packaging.markers import interpret @@ -64,13 +65,15 @@ """ def __init__(self, dist): self.dist = dist - self.setup_hook = None + self.setup_hooks = None - def run_hook(self, config): - if self.setup_hook is None: + def run_hooks(self, config): + if self.setup_hooks is None: return + # the hook gets only the config - self.setup_hook(config) + for hook in self.setup_hooks: + hook(config) def find_config_files(self): """Find as many configuration files as should be processed for this @@ -124,12 +127,6 @@ # XXX return value - def _multiline(self, value): - value = [v for v in - [v.strip() for v in value.split('\n')] - if v != ''] - return value - def _read_setup_cfg(self, parser, cfg_filename): cfg_directory = os.path.dirname(os.path.abspath(cfg_filename)) content = {} @@ -140,13 +137,19 @@ if 'global' in content: if 'setup_hook' in content['global']: setup_hook = content['global']['setup_hook'] + setup_hooks = split_multiline(setup_hook) + + if setup_hooks: + self.setup_hooks = [] + try: - self.setup_hook = resolve_name(setup_hook) + for hook in setup_hooks: + self.setup_hooks.append(resolve_name(hook)) except ImportError as e: logger.warning('could not import setup_hook: %s', e.args[0]) else: - self.run_hook(content) + self.run_hooks(content) metadata = self.dist.metadata @@ -155,7 +158,7 @@ for key, value in content['metadata'].items(): key = key.replace('_', '-') if metadata.is_multi_field(key): - value = self._multiline(value) + value = split_multiline(value) if key == 'project-url': value = [(label.strip(), url.strip()) @@ -192,7 +195,7 @@ files = content['files'] self.dist.package_dir = files.pop('packages_root', None) - files = dict((key, self._multiline(value)) for key, value in + files = dict((key, split_multiline(value)) for key, value in files.items()) self.dist.packages = [] @@ -310,7 +313,7 @@ opt = opt.replace('-', '_') if opt == 'sub_commands': - val = self._multiline(val) + val = split_multiline(val) if isinstance(val, str): val = [val] @@ -348,14 +351,14 @@ raise PackagingOptionError(msg) def _load_compilers(self, compilers): - compilers = self._multiline(compilers) + compilers = split_multiline(compilers) if isinstance(compilers, str): compilers = [compilers] for compiler in compilers: set_compiler(compiler.strip()) def _load_commands(self, commands): - commands = self._multiline(commands) + commands = split_multiline(commands) if isinstance(commands, str): commands = [commands] for command in commands: diff -r d8502fee4638 Lib/packaging/tests/test_util.py --- a/Lib/packaging/tests/test_util.py Mon Jun 06 17:13:08 2011 +0200 +++ b/Lib/packaging/tests/test_util.py Tue Jun 07 15:46:07 2011 -0400 @@ -92,9 +92,13 @@ def setUp(self): super(UtilTestCase, self).setUp() - self.tmp_dir = self.mkdtemp() - self.rc = os.path.join(self.tmp_dir, '.pypirc') - os.environ['HOME'] = self.tmp_dir + + self.addCleanup(os.chdir, os.getcwd()) + tempdir = self.mkdtemp() + os.chdir(tempdir) + self.tempdir = tempdir + self.rc = os.path.join(self.tempdir, '.pypirc') + os.environ['HOME'] = self.tempdir # saving the environment self.name = os.name self.platform = sys.platform @@ -492,6 +496,40 @@ self.assertEqual(content, WANTED) + def test_cfg_to_args(self): + from distutils2.dist import Distribution + + opts = {'description-file': 'README', 'extra-files': ''} + self.write_file('setup.cfg', SETUP_CFG % opts) + self.write_file('README', 'loooong description') + + args = cfg_to_args() + dist = Distribution() + dist.parse_config_files() + + metadata = dist.metadata + + # Just compare what's in the Distribution to the setup() args + self.assertEqual(args['name'], metadata['Name']) + # + .dev1 because the test SETUP_CFG also tests a hook function in + # test_config.py for appending to the version string. + self.assertEqual(args['version'] + '.dev1', metadata['Version']) + self.assertEqual(args['author'], metadata['Author']) + self.assertEqual(args['author_email'], metadata['Author-Email']) + self.assertEqual(args['maintainer'], metadata['Maintainer']) + self.assertEqual(args['maintainer_email'], metadata['Maintainer-Email']) + self.assertEqual(args['description'], metadata['Summary']) + self.assertEqual(args['long_description'], metadata['Description']) + self.assertEqual(args['classifiers'], metadata['Classifier']) + self.assertEqual(args['requires'], metadata['Requires-Dist']) + self.assertEqual(args['provides'], metadata['Provides-Dist']) + + self.assertEqual(args['package_dir'].get(''), dist.package_dir) + self.assertEqual(args['packages'], dist.packages) + self.assertEqual(args['scripts'], dist.scripts) + self.assertEqual(args['py_modules'], dist.py_modules) + + class GlobTestCaseBase(support.TempdirManager, support.LoggingCatcher, unittest.TestCase): diff -r d8502fee4638 Lib/packaging/util.py --- a/Lib/packaging/util.py Mon Jun 06 17:13:08 2011 +0200 +++ b/Lib/packaging/util.py Tue Jun 07 15:46:07 2011 -0400 @@ -250,6 +250,14 @@ return words +def split_multiline(self, value): + """Split a multiline string into an array, excluding blank lines.""" + + return [v for v in + [v.strip() for v in value.split('\n')] + if v != ''] + + def execute(func, args, msg=None, verbose=0, dry_run=False): """Perform some action that affects the outside world. @@ -1010,16 +1018,20 @@ "requires": ("metadata", "requires_dist"), "provides": ("metadata", "provides_dist"), # ** "obsoletes": ("metadata", "obsoletes_dist"), # ** + "package_dir": ("files", 'packages_root'), "packages": ("files",), "scripts": ("files",), "py_modules": ("files", "modules"), # ** } MULTI_FIELDS = ("classifiers", + "platforms", "requires", - "platforms", + "provides", + "obsoletes", "packages", - "scripts") + "scripts", + "py_modules") def has_get_option(config, section, option): if config.has_option(section, option): @@ -1031,9 +1043,9 @@ # The real code starts here config = RawConfigParser() - if not os.path.exists(file): + if not os.path.exists(path): raise PackagingFileError("file '%s' does not exist" % - os.path.abspath(file)) + os.path.abspath(path)) config.read(path) kwargs = {} @@ -1051,16 +1063,22 @@ if not in_cfg_value: # There is no such option in the setup.cfg if arg == "long_description": - filename = has_get_option(config, section, "description_file") - if filename: - with open(filename) as fp: - in_cfg_value = fp.read() + filenames = has_get_option(config, section, "description_file") + if filenames: + filenames = split_multiline(filenames) + in_cfg_value = '' + for filename in filenames: + with open(filename) as fp: + in_cfg_value += fp.read().strip() + '\n\n' else: continue + if arg == 'package_dir' and in_cfg_value: + in_cfg_value = {'': in_cfg_value} + if arg in MULTI_FIELDS: # support multiline options - in_cfg_value = in_cfg_value.strip().split('\n') + in_cfg_value = split_multiline(in_cfg_value) kwargs[arg] = in_cfg_value