[Python-checkins] distutils2: Added support for zipped eggs

tarek.ziade python-checkins at python.org
Sun Jun 20 23:04:52 CEST 2010


tarek.ziade pushed ce6942d3c4f2 to distutils2:

http://hg.python.org/distutils2/rev/ce6942d3c4f2
changeset:   199:ce6942d3c4f2
parent:      187:48f3b9a445da
user:        Josip Djolonga
date:        Sat Jun 05 16:59:49 2010 +0200
summary:     Added support for zipped eggs
files:       src/distutils2/_backport/pkgutil.py, src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO, src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA, src/distutils2/depgraph.py, src/distutils2/metadata.py

diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py
--- a/src/distutils2/_backport/pkgutil.py
+++ b/src/distutils2/_backport/pkgutil.py
@@ -12,6 +12,13 @@
 from distutils2.errors import DistutilsError
 from distutils2.metadata import DistributionMetadata
 from distutils2.version import suggest_normalized_version, VersionPredicate
+import zipimport
+try:
+    import cStringIO as StringIO
+except ImportError:
+    import StringIO
+import re
+import warnings
 
 __all__ = [
     'get_importer', 'iter_importers', 'get_loader', 'find_loader',
@@ -726,12 +733,96 @@
     metadata = None
     """A :class:`distutils2.metadata.DistributionMetadata` instance loaded with
     the distribution's METADATA file."""
+    _REQUIREMENT = re.compile( \
+        r'(?P<name>[-A-Za-z0-9_.]+)\s*' \
+        r'(?P<first>(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)?\s*' \
+        r'(?P<rest>(?:\s*,\s*(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)*)\s*' \
+        r'(?P<extras>\[.*\])?')
 
     def __init__(self, path):
-        if os.path.isdir(path):
-            path = os.path.join(path, 'PKG-INFO')
-        self.metadata = DistributionMetadata(path=path)
-        self.name = self.metadata['name']
+
+        # reused from Distribute's pkg_resources
+        def yield_lines(strs):
+            """Yield non-empty/non-comment lines of a ``basestring`` or sequence"""
+            if isinstance(strs, basestring):
+                for s in strs.splitlines():
+                    s = s.strip()
+                    if s and not s.startswith('#'): # skip blank lines/comments
+                        yield s
+                    else:
+                        for ss in strs:
+                            for s in yield_lines(ss):
+                                yield s
+
+        requires = None
+        if path.endswith('.egg'):
+            if os.path.isdir(path):
+                path = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+                self.metadata = DistributionMetadata(path=path)
+                try:
+                    req_path = os.path.join(path, 'EGG_INFO', 'requires.txt')
+                    requires = open(req_path, 'r').read()
+                except IOError:
+                    requires = None
+            else:
+                zipf = zipimport.zipimporter(path)
+                fileobj = StringIO.StringIO(zipf.get_data('EGG-INFO/PKG-INFO'))
+                self.metadata = DistributionMetadata(fileobj=fileobj)
+                try:
+                    requires = zipf.get_data('EGG-INFO/requires.txt')
+                except IOError:
+                    requires = None
+        elif path.endswith('.egg-info'):
+            if os.path.isdir(path):
+                path = os.path.join(path, 'PKG-INFO')
+                try:
+                    req_f = open(os.path.join(path, 'requires.txt'), 'r')
+                    requires = req_f.read()
+                except IOError:
+                    requires = None
+            self.metadata = DistributionMetadata(path=path)
+            self.name = self.metadata['name']
+        else:
+            raise ValueError('The path must end with .egg-info or .egg')
+
+        provides = "%s (%s)" % (self.metadata['name'],
+                                self.metadata['version'])
+        if self.metadata['Metadata-Version'] == '1.2':
+            self.metadata['Provides-Dist'] += (provides,)
+        else:
+            self.metadata['Provides'] += (provides,)
+        reqs = []
+        if requires is not None:
+            for line in yield_lines(requires):
+                if line[0] == '[':
+                    warnings.warn('distutils2 does not support extensions in requires.txt')
+                    break
+                else:
+                    match = _REQUIREMENT.match(line.strip())
+                    if not match:
+                        raise ValueError('Distribution %s has ill formed '
+                                         'requires.txt file (%s)' %
+                                         (self.name, line))
+                    else:
+                        if match.group('extra'):
+                            s = 'Distribution %s uses extra requirements which'\
+                                ' are not supported in distutils' % (self.name)
+                            warnings.warn(s)
+                        name = match.group('name')
+                        version = None
+                        if match.group('first'):
+                            version = match.group('first')
+                            if match.group('rest'):
+                                version += match.group('rest')
+                            version = version.replace(' ', '') # trim spaces
+                        if version is None:
+                            reqs.append(name)
+                        else:
+                            reqs.append('%s (%s)' % (name, version))
+            if self.metadata['Metadata-Version'] == '1.2':
+                self.metadata['Requires-Dist'] += reqs
+            else:
+                self.metadata['Requires'] += reqs
 
     def get_installed_files(self, local=False):
         return []
@@ -792,7 +883,8 @@
             if dir.endswith('.dist-info'):
                 dist = Distribution(os.path.join(realpath, dir))
                 yield dist
-            elif use_egg_info and dir.endswith('.egg-info'):
+            elif use_egg_info and (dir.endswith('.egg-info') or
+                                   dir.endswith('.egg')):
                 dist = EggInfoDistribution(os.path.join(realpath, dir))
                 yield dist
 
@@ -887,7 +979,7 @@
         provided = dist.metadata['Provides-Dist'] + dist.metadata['Provides']
 
         for p in provided:
-            p_components = p.split(' ', 1)
+            p_components = p.rsplit(' ', 1)
             if len(p_components) == 1 or predicate is None:
                 if name == p_components[0]:
                     yield dist
@@ -896,7 +988,7 @@
                 p_name, p_ver = p_components
                 if len(p_ver) < 2 or p_ver[0] != '(' or p_ver[-1] != ')':
                     raise DistutilsError(('Distribution %s has invalid ' +
-                                          'provides field') % (dist.name,))
+                                          'provides field: %s') % (dist.name,p))
                 p_ver = p_ver[1:-1] # trim off the parenthesis
                 if p_name == name and predicate.match(p_ver):
                     yield dist
diff --git a/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO b/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
--- a/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
+++ b/src/distutils2/_backport/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
@@ -2,4 +2,5 @@
 Name: bacon
 Version: 0.1
 Provides-Dist: truffles (2.0)
+Provides-Dist: bacon (0.1)
 Obsoletes-Dist: truffles (>=0.9,<=1.5)
diff --git a/src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA b/src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
--- a/src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
+++ b/src/distutils2/_backport/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
@@ -2,4 +2,6 @@
 Name: towel-stuff
 Version: 0.1
 Provides-Dist: truffles (1.1.2)
+Provides-Dist: towel-stuff (0.1)
 Obsoletes-Dist: truffles (!=0.8,<1.0)
+Requires-Dist: bacon (<=0.2)
diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py
--- a/src/distutils2/depgraph.py
+++ b/src/distutils2/depgraph.py
@@ -60,40 +60,41 @@
         """
         self.missing[distribution].append(requirement)
 
-    def to_dot(self, f, skip_disconnected=True):
-        """
-        Writes a DOT output for the graph to the provided *file*.
-        If *skip_disconnected* is set to ``True``, then all distributions
-        that are not dependent on any other distributions are skipped.
 
-        :type f: ``file``
-        ;type skip_disconnected: ``bool``
-        """
-        if not isinstance(f, file):
-            raise TypeError('the argument has to be of type file')
+def graph_to_dot(graph, f, skip_disconnected=True):
+    """
+    Writes a DOT output for the graph to the provided *file*.
+    If *skip_disconnected* is set to ``True``, then all distributions
+    that are not dependent on any other distributions are skipped.
 
-        disconnected = []
+    :type f: ``file``
+    ;type skip_disconnected: ``bool``
+    """
+    if not isinstance(f, file):
+        raise TypeError('the argument has to be of type file')
 
-        f.write("digraph dependencies {\n")
-        for dist, adjs in self.adjacency_list.iteritems():
-            if len(adjs) == 0 and not skip_disconnected:
-                disconnected.append(dist)
-            for (other, label) in adjs:
-                if not label is None:
-                    f.write('"%s" -> "%s" [label="%s"]\n' %
-                                                (dist.name, other.name, label))
-                else:
-                    f.write('"%s" -> "%s"\n' % (dist.name, other.name))
-        if not skip_disconnected and len(disconnected) > 0:
-            f.write('subgraph disconnected {\n')
-            f.write('label = "Disconnected"\n')
-            f.write('bgcolor = red\n')
+    disconnected = []
 
-            for dist in disconnected:
-                f.write('"%s"' % dist.name)
-                f.write('\n')
-            f.write('}\n')
+    f.write("digraph dependencies {\n")
+    for dist, adjs in graph.adjacency_list.iteritems():
+        if len(adjs) == 0 and not skip_disconnected:
+            disconnected.append(dist)
+        for (other, label) in adjs:
+            if not label is None:
+                f.write('"%s" -> "%s" [label="%s"]\n' %
+                                            (dist.name, other.name, label))
+            else:
+                f.write('"%s" -> "%s"\n' % (dist.name, other.name))
+    if not skip_disconnected and len(disconnected) > 0:
+        f.write('subgraph disconnected {\n')
+        f.write('label = "Disconnected"\n')
+        f.write('bgcolor = red\n')
+
+        for dist in disconnected:
+            f.write('"%s"' % dist.name)
+            f.write('\n')
         f.write('}\n')
+    f.write('}\n')
 
 
 def generate_graph(dists):
diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py
--- a/src/distutils2/metadata.py
+++ b/src/distutils2/metadata.py
@@ -186,13 +186,15 @@
     """Distribution meta-data class (1.0 or 1.2).
     """
     def __init__(self, path=None, platform_dependant=False,
-                 execution_context=None):
+                 execution_context=None, fileobj=None):
         self._fields = {}
         self.version = None
         self.docutils_support = _HAS_DOCUTILS
         self.platform_dependant = platform_dependant
         if path is not None:
             self.read(path)
+        elif fileobj is not None:
+            self.read_file(fileobj)
         self.execution_context = execution_context
 
     def _set_best_version(self):

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


More information about the Python-checkins mailing list