[Python-checkins] distutils2: merged upstream

tarek.ziade python-checkins at python.org
Sun Jul 4 11:48:38 CEST 2010


tarek.ziade pushed be8f96b7c538 to distutils2:

http://hg.python.org/distutils2/rev/be8f96b7c538
changeset:   259:be8f96b7c538
parent:      258:0b00b7e6e0b8
parent:      133:d519643faee6
user:        Konrad Delong <konryd at gmail.com>
date:        Sun May 09 13:02:57 2010 -0400
summary:     merged upstream
files:       

diff --git a/.hgtags b/.hgtags
new file mode 100644
--- /dev/null
+++ b/.hgtags
@@ -0,0 +1,1 @@
+5847fb2b7549fb301882c1054eb8b3d0893e3570 1.0a1
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -2,9 +2,16 @@
 CHANGES
 =======
 
-1.0a1
------
+1.0a2 - ?
+--------
 
-- Initial import from the stdlib
+-
 
+1.0a1 - 2010-05-06
+------------------
 
+- Initial import from the stdlib [tarek]
+- Added support for PEP 376 in distutils2.version [tarek]
+- Added support for PEP 345 in distutils2.metadata [tarek]
+- Added distutils2.util.find_package [tarek]
+
diff --git a/src/README.txt b/src/README.txt
--- a/src/README.txt
+++ b/src/README.txt
@@ -1,5 +1,15 @@
-======
-README
-======
+==========
+Distutils2
+==========
 
+Welcome to Distutils2 !
 
+Distutils2 is the new version of Distutils. It's not backward compatible with
+Distutils but provides more features, and implement most new packaging
+standards.
+
+See the documentation at http://packages.python.org/Distutils2 for more info.
+
+**Beware that Distutils2 is its in early stage and should not be used in
+production. Its API is subject to changes**
+
diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py
--- a/src/distutils2/__init__.py
+++ b/src/distutils2/__init__.py
@@ -10,7 +10,7 @@
 __all__ = ['__version__', 'setup']
 
 __revision__ = "$Id: __init__.py 78020 2010-02-06 16:37:32Z benjamin.peterson $"
-__version__ = "1.0a1"
+__version__ = "1.0a2"
 
 from distutils2.core import setup
 
diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py
--- a/src/distutils2/metadata.py
+++ b/src/distutils2/metadata.py
@@ -103,20 +103,44 @@
             if marker in keys:
                 return True
         return False
+
     keys = fields.keys()
-    is_1_1 = _has_marker(keys, _314_MARKERS)
-    is_1_2 = _has_marker(keys, _345_MARKERS)
+    possible_versions = ['1.0', '1.1', '1.2']
+
+
+    # first let's try to see if a field is not part of one of the version
+    for key in keys:
+        if key not in _241_FIELDS and '1.0' in possible_versions:
+            possible_versions.remove('1.0')
+        if key not in _314_FIELDS and '1.1' in possible_versions:
+            possible_versions.remove('1.1')
+        if key not in _345_FIELDS and '1.2' in possible_versions:
+            possible_versions.remove('1.2')
+
+    # possible_version contains qualified versions
+    if len(possible_versions) == 1:
+        return possible_versions[0]   # found !
+    elif len(possible_versions) == 0:
+        raise MetadataConflictError('Unknown metadata set')
+
+    # let's see if one unique marker is found
+    is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS)
+    is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS)
     if is_1_1 and is_1_2:
-        raise MetadataConflictError('You used both 1.1 and 1.2 fields')
+        raise MetadataConflictError('You used incompatible 1.1 and 1.2 fields')
 
     # we have the choice, either 1.0, or 1.2
     #   - 1.0 has a broken Summary field but work with all tools
     #   - 1.1 is to avoid
     #   - 1.2 fixes Summary but is not spreaded yet
     if not is_1_1 and not is_1_2:
-        return PKG_INFO_PREFERRED_VERSION
+        # we couldn't find any specific marker
+        if PKG_INFO_PREFERRED_VERSION in possible_versions:
+            return PKG_INFO_PREFERRED_VERSION
     if is_1_1:
         return '1.1'
+
+    # default marker when 1.0 is disqualified
     return '1.2'
 
 _ATTR2FIELD = {'metadata_version': 'Metadata-Version',
@@ -269,6 +293,8 @@
             if field in _LISTFIELDS:
                 # we can have multiple lines
                 values = msg.get_all(field)
+                if field in _LISTTUPLEFIELDS and values is not None:
+                    values = [tuple(value.split(',')) for value in values]
                 self.set(field, values)
             else:
                 # single line
@@ -299,6 +325,9 @@
                     values = values.replace('\n', '\n       |')
                 values = [values]
 
+            if field in _LISTTUPLEFIELDS:
+                values = [','.join(value) for value in values]
+
             for value in values:
                 self._write_field(fileobject, field, value)
 
diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py
--- a/src/distutils2/tests/test_metadata.py
+++ b/src/distutils2/tests/test_metadata.py
@@ -4,7 +4,8 @@
 import sys
 from StringIO import StringIO
 
-from distutils2.metadata import DistributionMetadata, _interpret
+from distutils2.metadata import (DistributionMetadata, _interpret,
+                                 PKG_INFO_PREFERRED_VERSION)
 
 class DistributionMetadataTestCase(unittest2.TestCase):
 
@@ -200,6 +201,31 @@
         self.assertEquals(missing, ['Name', 'Home-page'])
         self.assertEquals(len(warnings), 2)
 
+    def test_best_choice(self):
+        metadata = DistributionMetadata()
+        metadata['Version'] = '1.0'
+        self.assertEquals(metadata.version, PKG_INFO_PREFERRED_VERSION)
+        metadata['Classifier'] = ['ok']
+        self.assertEquals(metadata.version, '1.2')
+
+    def test_project_urls(self):
+        # project-url is a bit specific, make sure we write it
+        # properly in PKG-INFO
+        metadata = DistributionMetadata()
+        metadata['Version'] = '1.0'
+        metadata['Project-Url'] = [('one', 'http://ok')]
+        self.assertEquals(metadata['Project-Url'], [('one', 'http://ok')])
+        file_ = StringIO()
+        metadata.write_file(file_)
+        file_.seek(0)
+        res = file_.read().split('\n')
+        self.assertIn('Project-URL: one,http://ok', res)
+
+        file_.seek(0)
+        metadata = DistributionMetadata()
+        metadata.read_file(file_)
+        self.assertEquals(metadata['Project-Url'], [('one', 'http://ok')])
+
 
 def test_suite():
     return unittest2.makeSuite(DistributionMetadataTestCase)
diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py
--- a/src/distutils2/tests/test_util.py
+++ b/src/distutils2/tests/test_util.py
@@ -14,7 +14,7 @@
                             check_environ, split_quoted, strtobool,
                             rfc822_escape, get_compiler_versions,
                             _find_exe_version, _MAC_OS_X_LD_VERSION,
-                            byte_compile)
+                            byte_compile, find_packages)
 from distutils2 import util
 from distutils2.tests import support
 
@@ -32,7 +32,9 @@
             self.stdout = StringIO(exes[self.cmd])
             self.stderr = StringIO()
 
-class UtilTestCase(support.EnvironGuard, unittest2.TestCase):
+class UtilTestCase(support.EnvironGuard,
+                   support.TempdirManager,
+                   unittest2.TestCase):
 
     def setUp(self):
         super(UtilTestCase, self).setUp()
@@ -257,6 +259,44 @@
         self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx')
 
 
+    def test_find_packages(self):
+        # let's create a structure we want to scan:
+        #
+        #   pkg1
+        #     __init__
+        #     pkg2
+        #       __init__
+        #     pkg3
+        #       __init__
+        #       pkg6
+        #           __init__
+        #     pkg4    <--- not a pkg
+        #       pkg8
+        #          __init__
+        #   pkg5
+        #     __init__
+        #
+        root = self.mkdtemp()
+        pkg1 = os.path.join(root, 'pkg1')
+        os.mkdir(pkg1)
+        self.write_file(os.path.join(pkg1, '__init__.py'))
+        os.mkdir(os.path.join(pkg1, 'pkg2'))
+        self.write_file(os.path.join(pkg1, 'pkg2', '__init__.py'))
+        os.mkdir(os.path.join(pkg1, 'pkg3'))
+        self.write_file(os.path.join(pkg1, 'pkg3', '__init__.py'))
+        os.mkdir(os.path.join(pkg1, 'pkg3', 'pkg6'))
+        self.write_file(os.path.join(pkg1, 'pkg3', 'pkg6', '__init__.py'))
+        os.mkdir(os.path.join(pkg1, 'pkg4'))
+        os.mkdir(os.path.join(pkg1, 'pkg4', 'pkg8'))
+        self.write_file(os.path.join(pkg1, 'pkg4', 'pkg8', '__init__.py'))
+        pkg5 = os.path.join(root, 'pkg5')
+        os.mkdir(pkg5)
+        self.write_file(os.path.join(pkg5, '__init__.py'))
+
+        res = find_packages([root], ['pkg1.pkg2'])
+        self.assertEquals(res, ['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6'])
+
+
 def test_suite():
     return unittest2.makeSuite(UtilTestCase)
 
diff --git a/src/distutils2/util.py b/src/distutils2/util.py
--- a/src/distutils2/util.py
+++ b/src/distutils2/util.py
@@ -7,6 +7,7 @@
 __revision__ = "$Id: util.py 77761 2010-01-26 22:46:15Z tarek.ziade $"
 
 import sys, os, string, re
+from fnmatch import fnmatchcase
 
 from distutils2.errors import (DistutilsPlatformError, DistutilsFileError,
                                DistutilsByteCompileError)
@@ -543,3 +544,70 @@
         f.write(line + "\n")
     f.close()
 
+def _is_package(path):
+    """Returns True if path is a package (a dir with an __init__ file."""
+    if not os.path.isdir(path):
+        return False
+    return os.path.isfile(os.path.join(path, '__init__.py'))
+
+def _under(path, root):
+    path = path.split(os.sep)
+    root = root.split(os.sep)
+    if len(root) > len(path):
+        return False
+    for pos, part in enumerate(root):
+        if path[pos] != part:
+            return False
+    return True
+
+def _package_name(root_path, path):
+    """Returns a dotted package name, given a subpath."""
+    if not _under(path, root_path):
+        raise ValueError('"%s" is not a subpath of "%s"' % (path, root_path))
+    return path[len(root_path) + 1:].replace(os.sep, '.')
+
+def find_packages(paths=('.',), exclude=()):
+    """Return a list all Python packages found recursively within
+    directories 'paths'
+
+    'paths' should be supplied as a sequence of "cross-platform"
+    (i.e. URL-style) path; it will be converted to the appropriate local
+    path syntax.
+
+    'exclude' is a sequence of package names to exclude; '*' can be used as
+    a wildcard in the names, such that 'foo.*' will exclude all subpackages
+    of 'foo' (but not 'foo' itself).
+    """
+    packages = []
+    discarded = []
+    def _discarded(path):
+        for discard in discarded:
+            if _under(path, discard):
+                return True
+        return False
+
+    for path in paths:
+        path = convert_path(path)
+        for root, dirs, files in os.walk(path):
+            for dir_ in dirs:
+                fullpath = os.path.join(root, dir_)
+                if _discarded(fullpath):
+                    continue
+                # we work only with Python packages
+                if not _is_package(fullpath):
+                    discarded.append(fullpath)
+                    continue
+                # see if it's excluded
+                excluded = False
+                package_name = _package_name(path, fullpath)
+                for pattern in exclude:
+                    if fnmatchcase(package_name, pattern):
+                        excluded = True
+                        break
+                if excluded:
+                    continue
+
+                # adding it to the list
+                packages.append(package_name)
+    return packages
+
diff --git a/src/setup.py b/src/setup.py
--- a/src/setup.py
+++ b/src/setup.py
@@ -8,6 +8,7 @@
 from distutils2.command.sdist import sdist
 from distutils2.command.install import install
 from distutils2 import __version__ as VERSION
+from distutils2.util import find_packages
 
 f = open('README.txt')
 try:
@@ -67,20 +68,36 @@
 if sys.version < '2.6':
     setup_kwargs['scripts'] = ['distutils2/mkpkg.py']
 
+_CLASSIFIERS = """\
+Development Status :: 3 - Alpha
+Intended Audience :: Developers
+License :: OSI Approved :: Python Software Foundation License
+Operating System :: OS Independent
+Programming Language :: Python
+Topic :: Software Development :: Libraries :: Python Modules
+Topic :: System :: Archiving :: Packaging
+Topic :: System :: Systems Administration
+Topic :: Utilities"""
+
 setup (name="Distutils2",
        version=VERSION,
-       description="Python Distribution Utilities",
+       summary="Python Distribution Utilities",
+       keywords=['packaging', 'distutils'],
        author="Tarek Ziade",
        author_email="tarek at ziade.org",
-       url="http://www.python.org/sigs/distutils-sig",
+       home_page="http://bitbucket.org/tarek/distutils2/wiki/Home",
        license="PSF",
-       long_description=README,
-       packages=['distutils2',
-                 'distutils2.command',
-                 'distutils2.tests',
-                 'distutils2._backport'],
+       description=README,
+       classifier=_CLASSIFIERS.split('\n'),
+       packages=find_packages(),
        cmdclass={'sdist': sdist_hg, 'install': install_hg},
        package_data={'distutils2._backport': ['sysconfig.cfg']},
+       project_url=[('Mailing-list',
+                    'http://mail.python.org/mailman/listinfo/distutils-sig/'),
+                    ('Documentation',
+                     'http://packages.python.org/Distutils2'),
+                    ('Repository', 'http://hg.python.org/distutils2'),
+                    ('Bug tracker', 'http://bugs.python.org')],
        **setup_kwargs
        )
 

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


More information about the Python-checkins mailing list