[Python-checkins] distutils2: Merge with merge with main branche

tarek.ziade python-checkins at python.org
Wed Feb 16 22:23:57 CET 2011


tarek.ziade pushed 25439a8e604b to distutils2:

http://hg.python.org/distutils2/rev/25439a8e604b
changeset:   1046:25439a8e604b
parent:      1045:7b7827c8e4dd
parent:      1044:cc4a40ea2743
user:        FELD Boris <lothiraldan at gmail.com>
date:        Sat Jan 29 15:40:33 2011 +0100
summary:
  Merge with merge with main branche

files:
  docs/source/setupcfg.rst

diff --git a/distutils2/command/build_py.py b/distutils2/command/build_py.py
--- a/distutils2/command/build_py.py
+++ b/distutils2/command/build_py.py
@@ -66,10 +66,9 @@
         self.packages = self.distribution.packages
         self.py_modules = self.distribution.py_modules
         self.package_data = self.distribution.package_data
-        self.package_dir = {}
-        if self.distribution.package_dir:
-            for name, path in self.distribution.package_dir.iteritems():
-                self.package_dir[name] = convert_path(path)
+        self.package_dir = None
+        if self.distribution.package_dir is not None:
+            self.package_dir = convert_path(self.distribution.package_dir)
         self.data_files = self.get_data_files()
 
         # Ick, copied straight from install_lib.py (fancy_getopt needs a
@@ -179,41 +178,14 @@
         """Return the directory, relative to the top of the source
            distribution, where package 'package' should be found
            (at least according to the 'package_dir' option, if any)."""
+        path = package.split('.')
+        if self.package_dir is not None:
+            path.insert(0, self.package_dir)
 
-        path = package.split('.')
+        if len(path) > 0:
+            return os.path.join(*path)
 
-        if not self.package_dir:
-            if path:
-                return os.path.join(*path)
-            else:
-                return ''
-        else:
-            tail = []
-            while path:
-                try:
-                    pdir = self.package_dir['.'.join(path)]
-                except KeyError:
-                    tail.insert(0, path[-1])
-                    del path[-1]
-                else:
-                    tail.insert(0, pdir)
-                    return os.path.join(*tail)
-            else:
-                # Oops, got all the way through 'path' without finding a
-                # match in package_dir.  If package_dir defines a directory
-                # for the root (nameless) package, then fallback on it;
-                # otherwise, we might as well have not consulted
-                # package_dir at all, as we just use the directory implied
-                # by 'tail' (which should be the same as the original value
-                # of 'path' at this point).
-                pdir = self.package_dir.get('')
-                if pdir is not None:
-                    tail.insert(0, pdir)
-
-                if tail:
-                    return os.path.join(*tail)
-                else:
-                    return ''
+        return ''
 
     def check_package(self, package, package_dir):
         """Helper function for `find_package_modules()` and `find_modules()'.
diff --git a/distutils2/command/check.py b/distutils2/command/check.py
--- a/distutils2/command/check.py
+++ b/distutils2/command/check.py
@@ -57,7 +57,7 @@
 
         Warns if any are missing.
         """
-        missing, __ = self.distribution.metadata.check()
+        missing, __ = self.distribution.metadata.check(strict=True)
         if missing != []:
             self.warn("missing required metadata: %s"  % ', '.join(missing))
 
diff --git a/distutils2/command/sdist.py b/distutils2/command/sdist.py
--- a/distutils2/command/sdist.py
+++ b/distutils2/command/sdist.py
@@ -18,7 +18,8 @@
 from distutils2.command import get_command_names
 from distutils2.command.cmd import Command
 from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError,
-                               DistutilsTemplateError, DistutilsModuleError)
+                               DistutilsTemplateError, DistutilsModuleError,
+                               DistutilsFileError)
 from distutils2.manifest import Manifest
 from distutils2 import logger
 from distutils2.util import convert_path, resolve_name
@@ -214,8 +215,6 @@
 
     def add_defaults(self):
         """Add all the default files to self.filelist:
-          - README or README.txt
-          - test/test*.py
           - all pure Python modules mentioned in setup script
           - all files pointed by package_data (build_py)
           - all files defined in data_files.
@@ -225,32 +224,6 @@
         Warns if (README or README.txt) or setup.py are missing; everything
         else is optional.
         """
-        standards = [('README', 'README.txt')]
-        for fn in standards:
-            if isinstance(fn, tuple):
-                alts = fn
-                got_it = 0
-                for fn in alts:
-                    if os.path.exists(fn):
-                        got_it = 1
-                        self.filelist.append(fn)
-                        break
-
-                if not got_it:
-                    self.warn("standard file not found: should have one of " +
-                              string.join(alts, ', '))
-            else:
-                if os.path.exists(fn):
-                    self.filelist.append(fn)
-                else:
-                    self.warn("standard file '%s' not found" % fn)
-
-        optional = ['test/test*.py', 'setup.cfg']
-        for pattern in optional:
-            files = filter(os.path.isfile, glob(pattern))
-            if files:
-                self.filelist.extend(files)
-
         for cmd_name in get_command_names():
             try:
                 cmd_obj = self.get_finalized_command(cmd_name)
@@ -319,6 +292,12 @@
             logger.warn("no files to distribute -- empty manifest?")
         else:
             logger.info(msg)
+
+        for file in self.distribution.metadata.requires_files:
+            if file not in files:
+                msg = "'%s' must be included explicitly in 'extra_files'" % file
+                raise DistutilsFileError(msg)
+
         for file in files:
             if not os.path.isfile(file):
                 logger.warn("'%s' not a regular file -- skipping" % file)
@@ -376,4 +355,3 @@
         # Now create them
         for dir in need_dirs:
             self.mkpath(dir, mode, verbose=verbose, dry_run=dry_run)
-
diff --git a/distutils2/config.py b/distutils2/config.py
--- a/distutils2/config.py
+++ b/distutils2/config.py
@@ -79,10 +79,9 @@
         return value
 
     def _multiline(self, value):
-        if '\n' in value:
-            value = [v for v in
-                        [v.strip() for v in value.split('\n')]
-                        if v != '']
+        value = [v for v in
+                [v.strip() for v in value.split('\n')]
+                if v != '']
         return value
 
     def _read_setup_cfg(self, parser, filename):
@@ -103,7 +102,9 @@
         if 'metadata' in content:
             for key, value in content['metadata'].iteritems():
                 key = key.replace('_', '-')
-                value = self._multiline(value)
+                if metadata.is_multi_field(key):
+                    value = self._multiline(value)
+
                 if key == 'project-url':
                     value = [(label.strip(), url.strip())
                              for label, url in
@@ -115,30 +116,45 @@
                                "mutually exclusive")
                         raise DistutilsOptionError(msg)
 
-                    f = open(value)    # will raise if file not found
-                    try:
-                        value = f.read()
-                    finally:
-                        f.close()
+                    if isinstance(value, list):
+                        filenames = value
+                    else:
+                        filenames = value.split()
+
+                    # concatenate each files
+                    value = ''
+                    for filename in filenames:
+                        f = open(filename)    # will raise if file not found
+                        try:
+                            value += f.read().strip() + '\n'
+                        finally:
+                            f.close()
+                        # add filename as a required file
+                        if filename not in metadata.requires_files:
+                            metadata.requires_files.append(filename)
+                    value = value.strip()
                     key = 'description'
 
                 if metadata.is_metadata_field(key):
                     metadata[key] = self._convert_metadata(key, value)
 
+
         if 'files' in content:
-            files = dict([(key, self._multiline(value))
+            def _convert(key, value):
+                if key not in ('packages_root',):
+                    value = self._multiline(value)
+                return value
+
+            files = dict([(key, _convert(key, value))
                           for key, value in content['files'].iteritems()])
             self.dist.packages = []
-            self.dist.package_dir = {}
+            self.dist.package_dir = pkg_dir = files.get('packages_root')
 
             packages = files.get('packages', [])
             if isinstance(packages, str):
                 packages = [packages]
 
             for package in packages:
-                if ':' in package:
-                    dir_, package = package.split(':')
-                    self.dist.package_dir[package] = dir_
                 self.dist.packages.append(package)
 
             self.dist.py_modules = files.get('modules', [])
diff --git a/distutils2/errors.py b/distutils2/errors.py
--- a/distutils2/errors.py
+++ b/distutils2/errors.py
@@ -110,6 +110,10 @@
     """Attempt to process an unknown file type."""
 
 
+class MetadataMissingError(DistutilsError):
+    """A required metadata is missing"""
+
+
 class MetadataConflictError(DistutilsError):
     """Attempt to read or write metadata fields that are conflictual."""
 
diff --git a/distutils2/install.py b/distutils2/install.py
--- a/distutils2/install.py
+++ b/distutils2/install.py
@@ -208,6 +208,13 @@
             infos[key].extend(new_infos[key])
 
 
+def remove(project_name):
+    """Removes a single project from the installation"""
+    pass
+
+
+
+
 def main(**attrs):
     if 'script_args' not in attrs:
         import sys
diff --git a/distutils2/metadata.py b/distutils2/metadata.py
--- a/distutils2/metadata.py
+++ b/distutils2/metadata.py
@@ -14,7 +14,8 @@
 from distutils2 import logger
 from distutils2.version import (is_valid_predicate, is_valid_version,
                                 is_valid_versions)
-from distutils2.errors import (MetadataConflictError,
+from distutils2.errors import (MetadataMissingError,
+                               MetadataConflictError,
                                MetadataUnrecognizedVersionError)
 
 try:
@@ -77,12 +78,13 @@
                 'Obsoletes-Dist', 'Requires-External', 'Maintainer',
                 'Maintainer-email', 'Project-URL')
 
+_345_REQUIRED = ('Name', 'Version')
+
 _ALL_FIELDS = set()
 _ALL_FIELDS.update(_241_FIELDS)
 _ALL_FIELDS.update(_314_FIELDS)
 _ALL_FIELDS.update(_345_FIELDS)
 
-
 def _version2fieldlist(version):
     if version == '1.0':
         return _241_FIELDS
@@ -172,14 +174,19 @@
 _LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes',
         'Requires', 'Provides', 'Obsoletes-Dist',
         'Provides-Dist', 'Requires-Dist', 'Requires-External',
-        'Project-URL')
+        'Project-URL', 'Supported-Platform')
 _LISTTUPLEFIELDS = ('Project-URL',)
 
 _ELEMENTSFIELD = ('Keywords',)
 
 _UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description')
 
-_MISSING = object()
+class NoDefault(object):
+    """Marker object used for clean representation"""
+    def __repr__(self):
+        return '<NoDefault>'
+
+_MISSING = NoDefault()
 
 class DistributionMetadata(object):
     """The metadata of a release.
@@ -200,6 +207,7 @@
         self._fields = {}
         self.display_warnings = display_warnings
         self.version = None
+        self.requires_files = []
         self.docutils_support = _HAS_DOCUTILS
         self.platform_dependent = platform_dependent
         self.execution_context = execution_context
@@ -292,13 +300,20 @@
     # Public API
     #
     def get_fullname(self):
+        """Return the distribution name with version"""
         return '%s-%s' % (self['Name'], self['Version'])
 
     def is_metadata_field(self, name):
+        """return True if name is a valid metadata key"""
         name = self._convert_name(name)
         return name in _ALL_FIELDS
 
+    def is_multi_field(self, name):
+        name = self._convert_name(name)
+        return name in _LISTFIELDS
+
     def read(self, filepath):
+        """Read the metadata values from a file path."""
         self.read_file(open(filepath))
 
     def read_file(self, fileob):
@@ -451,11 +466,21 @@
             return None
         return value
 
-    def check(self):
-        """Check if the metadata is compliant."""
+    def check(self, strict=False):
+        """Check if the metadata is compliant. If strict is False then raise if
+        no Name or Version are provided"""
         # XXX should check the versions (if the file was loaded)
         missing, warnings = [], []
-        for attr in ('Name', 'Version', 'Home-page'):
+
+        for attr in ('Name', 'Version'):
+            if attr not in self:
+                missing.append(attr)
+
+        if strict and missing != []:
+            msg = "missing required metadata: %s"  % ', '.join(missing)
+            raise MetadataMissingError(msg)
+
+        for attr in ('Home-page',):
             if attr not in self:
                 missing.append(attr)
 
@@ -483,12 +508,15 @@
         return missing, warnings
 
     def keys(self):
+        """Dict like api"""
         return _version2fieldlist(self.version)
 
     def values(self):
+        """Dict like api"""
         return [self[key] for key in self.keys()]
 
     def items(self):
+        """Dict like api"""
         return [(key, self[key]) for key in self.keys()]
 
 
diff --git a/distutils2/run.py b/distutils2/run.py
--- a/distutils2/run.py
+++ b/distutils2/run.py
@@ -109,6 +109,7 @@
 
         except (DistutilsError,
                 CCompilerError), msg:
+            raise
             raise SystemExit, "error: " + str(msg)
 
     return dist
@@ -124,6 +125,10 @@
                   action="store_true", dest="version", default=False,
                   help="Prints out the version of Distutils2 and exits.")
 
+    parser.add_option("-m", "--metadata",
+                  action="append", dest="metadata", default=[],
+                  help="List METADATA metadata or 'all' for all metadatas.")
+
     parser.add_option("-s", "--search",
                   action="store", dest="search", default=None,
                   help="Search for installed distributions.")
@@ -141,6 +146,31 @@
         print('Distutils2 %s' % __version__)
 #        sys.exit(0)
 
+    if len(options.metadata):
+        from distutils2.dist import Distribution
+        dist = Distribution()
+        dist.parse_config_files()
+        metadata = dist.metadata
+
+        if 'all' in options.metadata:
+            keys = metadata.keys()
+        else:
+            keys = options.metadata
+            if len(keys) == 1:
+                print metadata[keys[0]]
+                sys.exit(0)
+
+        for key in keys:
+            if key in metadata:
+                print(metadata._convert_name(key)+':')
+                value = metadata[key]
+                if isinstance(value, list):
+                    for v in value:
+                        print('    '+v)
+                else:
+                    print('    '+value.replace('\n', '\n    '))
+        sys.exit(0)
+
     if options.search is not None:
         search = options.search.lower()
         for dist in get_distributions(use_egg_info=True):
diff --git a/distutils2/tests/test_command_build_ext.py b/distutils2/tests/test_command_build_ext.py
--- a/distutils2/tests/test_command_build_ext.py
+++ b/distutils2/tests/test_command_build_ext.py
@@ -289,7 +289,7 @@
 
         # inplace = 0, cmd.package = 'bar'
         build_py = cmd.get_finalized_command('build_py')
-        build_py.package_dir = {'': 'bar'}
+        build_py.package_dir = 'bar'
         path = cmd.get_ext_fullpath('foo')
         # checking that the last directory is the build_dir
         path = os.path.split(path)[0]
@@ -318,7 +318,7 @@
         dist = Distribution()
         cmd = build_ext(dist)
         cmd.inplace = 1
-        cmd.distribution.package_dir = {'': 'src'}
+        cmd.distribution.package_dir = 'src'
         cmd.distribution.packages = ['lxml', 'lxml.html']
         curdir = os.getcwd()
         wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
@@ -334,7 +334,7 @@
 
         # building twisted.runner.portmap not inplace
         build_py = cmd.get_finalized_command('build_py')
-        build_py.package_dir = {}
+        build_py.package_dir = None
         cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
         path = cmd.get_ext_fullpath('twisted.runner.portmap')
         wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
diff --git a/distutils2/tests/test_command_build_py.py b/distutils2/tests/test_command_build_py.py
--- a/distutils2/tests/test_command_build_py.py
+++ b/distutils2/tests/test_command_build_py.py
@@ -17,12 +17,14 @@
 
     def test_package_data(self):
         sources = self.mkdtemp()
-        f = open(os.path.join(sources, "__init__.py"), "w")
+        pkg_dir = os.path.join(sources, 'pkg')
+        os.mkdir(pkg_dir)
+        f = open(os.path.join(pkg_dir, "__init__.py"), "w")
         try:
             f.write("# Pretend this is a package.")
         finally:
             f.close()
-        f = open(os.path.join(sources, "README.txt"), "w")
+        f = open(os.path.join(pkg_dir, "README.txt"), "w")
         try:
             f.write("Info about this package")
         finally:
@@ -31,8 +33,9 @@
         destination = self.mkdtemp()
 
         dist = Distribution({"packages": ["pkg"],
-                             "package_dir": {"pkg": sources}})
+                             "package_dir": sources})
         # script_name need not exist, it just need to be initialized
+
         dist.script_name = os.path.join(sources, "setup.py")
         dist.command_obj["build"] = support.DummyCommand(
             force=0,
@@ -42,7 +45,7 @@
             use_2to3=False)
         dist.packages = ["pkg"]
         dist.package_data = {"pkg": ["README.txt"]}
-        dist.package_dir = {"pkg": sources}
+        dist.package_dir = sources
 
         cmd = build_py(dist)
         cmd.compile = 1
@@ -68,19 +71,20 @@
 
         # create the distribution files.
         sources = self.mkdtemp()
-        open(os.path.join(sources, "__init__.py"), "w").close()
-
-        testdir = os.path.join(sources, "doc")
+        pkg = os.path.join(sources, 'pkg')
+        os.mkdir(pkg)
+        open(os.path.join(pkg, "__init__.py"), "w").close()
+        testdir = os.path.join(pkg, "doc")
         os.mkdir(testdir)
         open(os.path.join(testdir, "testfile"), "w").close()
 
         os.chdir(sources)
         old_stdout = sys.stdout
-        sys.stdout = StringIO.StringIO()
+        #sys.stdout = StringIO.StringIO()
 
         try:
             dist = Distribution({"packages": ["pkg"],
-                                 "package_dir": {"pkg": ""},
+                                 "package_dir": sources,
                                  "package_data": {"pkg": ["doc/*"]}})
             # script_name need not exist, it just need to be initialized
             dist.script_name = os.path.join(sources, "setup.py")
@@ -89,7 +93,7 @@
 
             try:
                 dist.run_commands()
-            except DistutilsFileError:
+            except DistutilsFileError, e:
                 self.fail("failed package_data test when package_dir is ''")
         finally:
             # Restore state.
diff --git a/distutils2/tests/test_command_check.py b/distutils2/tests/test_command_check.py
--- a/distutils2/tests/test_command_check.py
+++ b/distutils2/tests/test_command_check.py
@@ -4,6 +4,7 @@
 from distutils2.metadata import _HAS_DOCUTILS
 from distutils2.tests import unittest, support
 from distutils2.errors import DistutilsSetupError
+from distutils2.errors import MetadataMissingError
 
 class CheckTestCase(support.LoggingCatcher,
                     support.TempdirManager,
@@ -11,7 +12,7 @@
 
     def _run(self, metadata=None, **options):
         if metadata is None:
-            metadata = {}
+            metadata = {'name':'xxx', 'version':'xxx'}
         pkg_info, dist = self.create_dist(**metadata)
         cmd = check(dist)
         cmd.initialize_options()
@@ -40,7 +41,8 @@
 
         # now with the strict mode, we should
         # get an error if there are missing metadata
-        self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1})
+        self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
+        self.assertRaises(DistutilsSetupError, self._run, {'name':'xxx', 'version':'xxx'}, **{'strict': 1})
 
         # and of course, no error when all metadata fields are present
         cmd = self._run(metadata, strict=1)
@@ -63,6 +65,9 @@
     def test_check_all(self):
 
         self.assertRaises(DistutilsSetupError, self._run,
+                          {'name':'xxx', 'version':'xxx'}, **{'strict': 1,
+                                 'all': 1})
+        self.assertRaises(MetadataMissingError, self._run,
                           {}, **{'strict': 1,
                                  'all': 1})
 
diff --git a/distutils2/tests/test_command_register.py b/distutils2/tests/test_command_register.py
--- a/distutils2/tests/test_command_register.py
+++ b/distutils2/tests/test_command_register.py
@@ -195,7 +195,7 @@
         # long_description is not reSt compliant
 
         # empty metadata
-        cmd = self._get_cmd({})
+        cmd = self._get_cmd({'name': 'xxx', 'version': 'xxx'})
         cmd.ensure_finalized()
         cmd.strict = 1
         inputs = RawInputs('1', 'tarek', 'y')
diff --git a/distutils2/tests/test_command_sdist.py b/distutils2/tests/test_command_sdist.py
--- a/distutils2/tests/test_command_sdist.py
+++ b/distutils2/tests/test_command_sdist.py
@@ -45,7 +45,6 @@
 
 MANIFEST = """\
 # file GENERATED by distutils, do NOT edit
-README
 inroot.txt
 data%(sep)sdata.dt
 scripts%(sep)sscript.py
@@ -141,7 +140,7 @@
             zip_file.close()
 
         # making sure everything has been pruned correctly
-        self.assertEqual(len(content), 3)
+        self.assertEqual(len(content), 2)
 
     @unittest.skipUnless(zlib, "requires zlib")
     def test_make_distribution(self):
@@ -236,7 +235,7 @@
             zip_file.close()
 
         # making sure everything was added
-        self.assertEqual(len(content), 10)
+        self.assertEqual(len(content), 9)
 
         # checking the MANIFEST
         manifest = open(join(self.tmp_dir, 'MANIFEST')).read()
@@ -245,7 +244,7 @@
     @unittest.skipUnless(zlib, "requires zlib")
     def test_metadata_check_option(self):
         # testing the `check-metadata` option
-        dist, cmd = self.get_cmd(metadata={})
+        dist, cmd = self.get_cmd(metadata={'name':'xxx', 'version':'xxx'})
 
         # this should raise some warnings !
         # with the `check` subcommand
@@ -362,8 +361,7 @@
                         if line.strip() != '']
         finally:
             f.close()
-
-        self.assertEqual(len(manifest), 4)
+        self.assertEqual(len(manifest), 3)
 
         # adding a file
         self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
@@ -383,7 +381,7 @@
             f.close()
 
         # do we have the new file in MANIFEST ?
-        self.assertEqual(len(manifest2), 5)
+        self.assertEqual(len(manifest2), 4)
         self.assertIn('doc2.txt', manifest2[-1])
 
     def test_manifest_marker(self):
diff --git a/distutils2/tests/test_config.py b/distutils2/tests/test_config.py
--- a/distutils2/tests/test_config.py
+++ b/distutils2/tests/test_config.py
@@ -5,6 +5,8 @@
 from StringIO import StringIO
 
 from distutils2.tests import unittest, support, run_unittest
+from distutils2.command.sdist import sdist
+from distutils2.errors import DistutilsFileError
 
 
 SETUP_CFG = """
@@ -16,7 +18,7 @@
 maintainer = Éric Araujo
 maintainer_email = merwok at netwok.org
 summary = A sample project demonstrating distutils2 packaging
-description-file = README
+description-file = %(description-file)s
 keywords = distutils2, packaging, sample project
 
 classifier =
@@ -47,9 +49,11 @@
   Fork in progress, http://bitbucket.org/Merwok/sample-distutils2-project
 
 [files]
+packages_root = src
+
 packages = one
-           src:two
-           src2:three
+           two
+           three
 
 modules = haven
 
@@ -66,6 +70,8 @@
   config = cfg/data.cfg
   /etc/init.d = init-script
 
+extra_files = %(extra-files)s
+
 # Replaces MANIFEST.in
 sdist_extra =
   include THANKS HACKING
@@ -130,22 +136,33 @@
         self.addCleanup(setattr, sys, 'stderr', sys.stderr)
         self.addCleanup(os.chdir, os.getcwd())
 
-    def test_config(self):
-        tempdir = self.mkdtemp()
-        os.chdir(tempdir)
-        self.write_file('setup.cfg', SETUP_CFG)
-        self.write_file('README', 'yeah')
+    def write_setup(self, kwargs=None):
+        opts = {'description-file': 'README', 'extra-files':''}
+        if kwargs:
+            opts.update(kwargs)
+        self.write_file('setup.cfg', SETUP_CFG % opts)
 
-        # try to load the metadata now
+
+    def run_setup(self, *args):
+        # run setup with args
         sys.stdout = StringIO()
-        sys.argv[:] = ['setup.py', '--version']
+        sys.argv[:] = [''] + list(args)
         old_sys = sys.argv[:]
-
         try:
             from distutils2.run import commands_main
             dist = commands_main()
         finally:
             sys.argv[:] = old_sys
+        return dist
+
+    def test_config(self):
+        tempdir = self.mkdtemp()
+        os.chdir(tempdir)
+        self.write_setup()
+        self.write_file('README', 'yeah')
+
+        # try to load the metadata now
+        dist = self.run_setup('--version')
 
         # sanity check
         self.assertEqual(sys.stdout.getvalue(), '0.6.4.dev1' + os.linesep)
@@ -184,7 +201,6 @@
                  'http://bitbucket.org/Merwok/sample-distutils2-project')]
         self.assertEqual(dist.metadata['Project-Url'], urls)
 
-
         self.assertEqual(dist.packages, ['one', 'two', 'three'])
         self.assertEqual(dist.py_modules, ['haven'])
         self.assertEqual(dist.package_data, {'cheese': 'data/templates/*'})
@@ -192,7 +208,8 @@
             [('bitmaps ', ['bm/b1.gif', 'bm/b2.gif']),
              ('config ', ['cfg/data.cfg']),
              ('/etc/init.d ', ['init-script'])])
-        self.assertEqual(dist.package_dir['two'], 'src')
+
+        self.assertEqual(dist.package_dir, 'src')
 
         # Make sure we get the foo command loaded.  We use a string comparison
         # instead of assertIsInstance because the class is not the same when
@@ -213,10 +230,94 @@
         d = new_compiler(compiler='d')
         self.assertEqual(d.description, 'D Compiler')
 
+
+    def test_multiple_description_file(self):
+        tempdir = self.mkdtemp()
+        os.chdir(tempdir)
+
+        self.write_setup({'description-file': 'README  CHANGES'})
+        self.write_file('README', 'yeah')
+        self.write_file('CHANGES', 'changelog2')
+        dist = self.run_setup('--version')
+        self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
+
+    def test_multiline_description_file(self):
+        tempdir = self.mkdtemp()
+        os.chdir(tempdir)
+
+        self.write_setup({'description-file': 'README\n  CHANGES'})
+        self.write_file('README', 'yeah')
+        self.write_file('CHANGES', 'changelog')
+        dist = self.run_setup('--version')
+        self.assertEqual(dist.metadata['description'], 'yeah\nchangelog')
+        self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
+
+    def test_metadata_requires_description_files_missing(self):
+        tempdir = self.mkdtemp()
+        os.chdir(tempdir)
+        self.write_setup({'description-file': 'README\n  README2'})
+        self.write_file('README', 'yeah')
+        self.write_file('README2', 'yeah')
+        self.write_file('haven.py', '#')
+        self.write_file('script1.py', '#')
+        os.mkdir('scripts')
+        self.write_file(os.path.join('scripts', 'find-coconuts'), '#')
+        os.mkdir('bin')
+        self.write_file(os.path.join('bin', 'taunt'), '#')
+
+        os.mkdir('src')
+        for pkg in ('one', 'two', 'three'):
+            pkg = os.path.join('src', pkg)
+            os.mkdir(pkg)
+            self.write_file(os.path.join(pkg, '__init__.py'), '#')
+
+        dist = self.run_setup('--version')
+        cmd = sdist(dist)
+        cmd.finalize_options()
+        cmd.get_file_list()
+        self.assertRaises(DistutilsFileError, cmd.make_distribution)
+
+    def test_metadata_requires_description_files(self):
+        tempdir = self.mkdtemp()
+        os.chdir(tempdir)
+        self.write_setup({'description-file': 'README\n  README2',
+                          'extra-files':'\n  README2'})
+        self.write_file('README', 'yeah')
+        self.write_file('README2', 'yeah')
+        self.write_file('haven.py', '#')
+        self.write_file('script1.py', '#')
+        os.mkdir('scripts')
+        self.write_file(os.path.join('scripts', 'find-coconuts'), '#')
+        os.mkdir('bin')
+        self.write_file(os.path.join('bin', 'taunt'), '#')
+
+        os.mkdir('src')
+        for pkg in ('one', 'two', 'three'):
+            pkg = os.path.join('src', pkg)
+            os.mkdir(pkg)
+            self.write_file(os.path.join(pkg, '__init__.py'), '#')
+
+        dist = self.run_setup('--description')
+        self.assertIn('yeah\nyeah\n', sys.stdout.getvalue())
+
+        cmd = sdist(dist)
+        cmd.finalize_options()
+        cmd.get_file_list()
+        self.assertRaises(DistutilsFileError, cmd.make_distribution)
+
+        self.write_setup({'description-file': 'README\n  README2',
+                          'extra-files': '\n  README2\n    README'})
+        dist = self.run_setup('--description')
+        cmd = sdist(dist)
+        cmd.finalize_options()
+        cmd.get_file_list()
+        cmd.make_distribution()
+        self.assertIn('README\nREADME2\n', open('MANIFEST').read())
+
     def test_sub_commands(self):
         tempdir = self.mkdtemp()
         os.chdir(tempdir)
-        self.write_file('setup.cfg', SETUP_CFG)
+        self.write_setup()
         self.write_file('README', 'yeah')
         self.write_file('haven.py', '#')
         self.write_file('script1.py', '#')
@@ -224,20 +325,15 @@
         self.write_file(os.path.join('scripts', 'find-coconuts'), '#')
         os.mkdir('bin')
         self.write_file(os.path.join('bin', 'taunt'), '#')
+        os.mkdir('src')
 
-        for pkg in ('one', 'src', 'src2'):
+        for pkg in ('one', 'two', 'three'):
+            pkg = os.path.join('src', pkg)
             os.mkdir(pkg)
             self.write_file(os.path.join(pkg, '__init__.py'), '#')
 
         # try to run the install command to see if foo is called
-        sys.stdout = sys.stderr = StringIO()
-        sys.argv[:] = ['', 'install_dist']
-        old_sys = sys.argv[:]
-        try:
-            from distutils2.run import main
-            dist = main()
-        finally:
-            sys.argv[:] = old_sys
+        dist = self.run_setup('install_dist')
 
         self.assertEqual(dist.foo_was_here, 1)
 
diff --git a/distutils2/util.py b/distutils2/util.py
--- a/distutils2/util.py
+++ b/distutils2/util.py
@@ -15,6 +15,7 @@
 from copy import copy
 from fnmatch import fnmatchcase
 from ConfigParser import RawConfigParser
+from inspect import getsource
 
 from distutils2.errors import (DistutilsPlatformError, DistutilsFileError,
                                DistutilsByteCompileError, DistutilsExecError)
@@ -1127,3 +1128,117 @@
         """ Issues a call to util.run_2to3. """
         return run_2to3(files, doctests_only, self.fixer_names,
                         self.options, self.explicit)
+
+
+def generate_distutils_kwargs_from_setup_cfg(file='setup.cfg'):
+    """ Distutils2 to distutils1 compatibility util.
+
+        This method uses an existing setup.cfg to generate a dictionnary of
+        keywords that can be used by distutils.core.setup(kwargs**).
+
+        :param file:
+            The setup.cfg path.
+        :raises DistutilsFileError:
+            When the setup.cfg file is not found.
+
+    """
+    # We need to declare the following constants here so that it's easier to
+    # generate the setup.py afterwards, using inspect.getsource.
+    D1_D2_SETUP_ARGS = {
+        # D1 name             : (D2_section, D2_name)
+        "name"                : ("metadata",),
+        "version"             : ("metadata",),
+        "author"              : ("metadata",),
+        "author_email"        : ("metadata",),
+        "maintainer"          : ("metadata",),
+        "maintainer_email"    : ("metadata",),
+        "url"                 : ("metadata", "home_page"),
+        "description"         : ("metadata", "summary"),
+        "long_description"    : ("metadata", "description"),
+        "download-url"        : ("metadata",),
+        "classifiers"         : ("metadata", "classifier"),
+        "platforms"           : ("metadata", "platform"), # Needs testing
+        "license"             : ("metadata",),
+        "requires"            : ("metadata", "requires_dist"),
+        "provides"            : ("metadata", "provides_dist"), # Needs testing
+        "obsoletes"           : ("metadata", "obsoletes_dist"), # Needs testing
+    
+        "packages"            : ("files",),
+        "scripts"             : ("files",),
+        "py_modules"          : ("files", "modules"), # Needs testing
+    }
+
+    MULTI_FIELDS = ("classifiers",
+                    "requires",
+                    "platforms",
+                    "packages",
+                    "scripts")
+
+    def has_get_option(config, section, option):
+        if config.has_option(section, option):
+            return config.get(section, option)
+        elif config.has_option(section, option.replace('_', '-')):
+            return config.get(section, option.replace('_', '-'))
+        else:
+            return False
+
+    # The method source code really starts here.
+    config = RawConfigParser()
+    if not os.path.exists(file):
+        raise DistutilsFileError("file '%s' does not exist" %
+                                 os.path.abspath(file))
+    config.read(file)
+
+    kwargs = {}
+    for arg in D1_D2_SETUP_ARGS:
+        if len(D1_D2_SETUP_ARGS[arg]) == 2:
+            # The distutils field name is different than distutils2's.
+            section, option = D1_D2_SETUP_ARGS[arg]
+
+        elif len(D1_D2_SETUP_ARGS[arg]) == 1:
+            # The distutils field name is the same thant distutils2's.
+            section = D1_D2_SETUP_ARGS[arg][0]
+            option = arg
+
+        in_cfg_value = has_get_option(config, section, option)
+        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")
+                print "We have a filename", filename
+                if filename:
+                    in_cfg_value = open(filename).read()
+            else:
+                continue
+
+        if arg in MULTI_FIELDS:
+            # Special behaviour when we have a multi line option
+            if "\n" in in_cfg_value:
+                in_cfg_value = in_cfg_value.strip().split('\n')
+            else:
+                in_cfg_value = list((in_cfg_value,))
+
+        kwargs[arg] = in_cfg_value
+
+    return kwargs
+
+
+def generate_distutils_setup_py():
+    """ Generate a distutils compatible setup.py using an existing setup.cfg.
+
+        :raises DistutilsFileError:
+            When a setup.py already exists.
+    """
+    if os.path.exists("setup.py"):
+        raise DistutilsFileError("A pre existing setup.py file exists")
+
+    handle = open("setup.py", "w")
+    handle.write("# Distutils script using distutils2 setup.cfg to call the\n")
+    handle.write("# distutils.core.setup() with the right args.\n\n\n")
+    handle.write("import os\n")
+    handle.write("from distutils.core import setup\n")
+    handle.write("from ConfigParser import RawConfigParser\n\n")
+    handle.write(getsource(generate_distutils_kwargs_from_setup_cfg))
+    handle.write("\n\nkwargs = generate_distutils_kwargs_from_setup_cfg()\n")
+    handle.write("setup(**kwargs)")
+    handle.close()
diff --git a/docs/source/distutils/apiref.rst b/docs/source/distutils/apiref.rst
--- a/docs/source/distutils/apiref.rst
+++ b/docs/source/distutils/apiref.rst
@@ -1055,6 +1055,13 @@
    Create a file called *filename* and write *contents* (a sequence of strings
    without line terminators) to it.
 
+:mod:`distutils2.metadata` --- Metadata handling
+================================================================
+
+.. module:: distutils2.metadata
+
+.. autoclass:: distutils2.metadata.DistributionMetadata
+   :members:
 
 :mod:`distutils2.util` --- Miscellaneous other utility functions
 ================================================================
diff --git a/docs/source/distutils/examples.rst b/docs/source/distutils/examples.rst
--- a/docs/source/distutils/examples.rst
+++ b/docs/source/distutils/examples.rst
@@ -301,7 +301,7 @@
 :class:`distutils2.dist.DistributionMetadata` class and its
 :func:`read_pkg_file` method::
 
-    >>> from distutils2.dist import DistributionMetadata
+    >>> from distutils2.metadata import DistributionMetadata
     >>> metadata = DistributionMetadata()
     >>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))
     >>> metadata.name
diff --git a/docs/source/library/distutils2.metadata.rst b/docs/source/library/distutils2.metadata.rst
--- a/docs/source/library/distutils2.metadata.rst
+++ b/docs/source/library/distutils2.metadata.rst
@@ -2,7 +2,9 @@
 Metadata
 ========
 
-Distutils2 provides a :class:`DistributionMetadata` class that can read and
+.. module:: distutils2.metadata
+
+Distutils2 provides a :class:`~distutils2.metadata.DistributionMetadata` class that can read and
 write metadata files. This class is compatible with all metadata versions:
 
 * 1.0: :PEP:`241`
@@ -17,7 +19,7 @@
 Reading metadata
 ================
 
-The :class:`DistributionMetadata` class can be instantiated with the path of
+The :class:`~distutils2.metadata.DistributionMetadata` class can be instantiated with the path of
 the metadata file, and provides a dict-like interface to the values::
 
     >>> from distutils2.metadata import DistributionMetadata
@@ -33,7 +35,7 @@
 
 The fields that supports environment markers can be automatically ignored if
 the object is instantiated using the ``platform_dependent`` option.
-:class:`DistributionMetadata` will interpret in the case the markers and will
+:class:`~distutils2.metadata.DistributionMetadata` will interpret in the case the markers and will
 automatically remove the fields that are not compliant with the running
 environment. Here's an example under Mac OS X. The win32 dependency
 we saw earlier is ignored::
diff --git a/docs/source/setupcfg.rst b/docs/source/setupcfg.rst
--- a/docs/source/setupcfg.rst
+++ b/docs/source/setupcfg.rst
@@ -128,6 +128,8 @@
 
 This section describes the files included in the project.
 
+- **packages_root**: the root directory containing all packages. If not provided
+  Distutils2 will use the current directory.  *\*optional*
 - **packages**: a list of packages the project includes *\*optional* *\*multi*
 - **modules**: a list of packages the project includes *\*optional* *\*multi*
 - **scripts**: a list of scripts the project includes *\*optional* *\*multi*
@@ -136,6 +138,7 @@
 Example::
 
     [files]
+    packages_root = src
     packages =
             pypi2rpm
             pypi2rpm.command
diff --git a/patch b/patch
new file mode 100644
--- /dev/null
+++ b/patch
@@ -0,0 +1,23 @@
+diff -r 5603e1bc5442 distutils2/util.py
+--- a/distutils2/util.py    Fri Jan 28 18:42:44 2011 +0100
++++ b/distutils2/util.py    Sat Jan 29 02:39:55 2011 +0100
+@@ -1203,12 +1203,13 @@
+         in_cfg_value = has_get_option(config, section, option)
+         if not in_cfg_value:
+             # There is no such option in the setup.cfg
+-            continue
+-
+-        if arg == "long_description":
+-            filename = has_get_option("description_file")
+-            if filename:
+-                in_cfg_value = open(filename).read()
++            if arg == "long_description":
++                filename = has_get_option(config, section, "description_file")
++                print "We have a filename", filename
++                if filename:
++                    in_cfg_value = open(filename).read()
++            else:
++                continue
+ 
+         if arg in MULTI_FIELDS:
+             # Special behaviour when we have a multi line option
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -7,11 +7,15 @@
 from distutils2 import __version__ as VERSION
 from distutils2.util import find_packages
 from distutils import log
-from distutils.core import setup, Extension
 from distutils.ccompiler import new_compiler
 from distutils.command.sdist import sdist
 from distutils.command.install import install
 
+try:
+    from setuptools import setup, Extension
+except ImportError:
+    from distutils.core import setup, Extension
+
 f = open('README.txt')
 try:
     README = f.read()

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


More information about the Python-checkins mailing list