[Python-checkins] python/nondist/sandbox/setuptools pkg_resources.py, 1.8, 1.9

pje at users.sourceforge.net pje at users.sourceforge.net
Sun Apr 3 20:52:23 CEST 2005


Update of /cvsroot/python/python/nondist/sandbox/setuptools
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14433

Modified Files:
	pkg_resources.py 
Log Message:
Added "AvailableDistributions" class that finds and indexes usable
distributions; this replaces the previous "iter_distributions()" API.
Added basic platform support to Distribution and AvailableDistributions so
that platform-independent distros as well as local platform-compatible
distros are acceptable.  The actual platform scheme is currently delegated
to distutils.util.get_platform(), but needs to be replaced with a better
scheme of some kind, especially for OS X.


Index: pkg_resources.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- pkg_resources.py	3 Apr 2005 01:21:08 -0000	1.8
+++ pkg_resources.py	3 Apr 2005 18:52:21 -0000	1.9
@@ -15,13 +15,30 @@
 """
 __all__ = [
     'register_loader_type', 'get_provider', 'IResourceProvider',
-    'ResourceManager', 'iter_distributions', 'require', 'resource_string',
+    'ResourceManager', 'AvailableDistributions', 'require', 'resource_string',
     'resource_stream', 'resource_filename', 'set_extraction_path',
     'cleanup_resources', 'parse_requirements', 'parse_version',
+    'compatible_platforms', 'get_platform',
     'Distribution', # 'glob_resources'
 ]
 
 import sys, os, zipimport, time, re
+
+def _sort_dists(dists):
+    tmp = [(dist.version,dist) for dist in dists]
+    tmp.sort()
+    tmp.reverse()
+    dists[:] = [d for v,d in tmp]
+
+
+
+
+
+
+
+
+
+
 _provider_factories = {}
 
 def register_loader_type(loader_type, provider_factory):
@@ -39,6 +56,30 @@
     loader = getattr(module, '__loader__', None)
     return _find_adapter(_provider_factories, loader)(module)
 
+def get_platform():
+    """Return this platform's string for platform-specific distributions
+
+    XXX Currently this is the same as ``distutils.util.get_platform()``, but it
+    needs some hacks for Linux and Mac OS X.
+    """
+    from distutils.util import get_platform
+    return get_platform()
+
+def compatible_platforms(provided,required):
+    """Can code for the `provided` platform run on the `required` platform?
+
+    Returns true if either platform is ``None``, or the platforms are equal.
+
+    XXX Needs compatibility checks for Linux and Mac OS X.
+    """
+    if provided is None or required is None or provided==required:
+        return True     # easy case
+
+    # XXX all the tricky cases go here
+    
+    return False
+
+
 class IResourceProvider:
 
     """An object that provides access to package resources"""
@@ -80,46 +121,128 @@
 
 
 
-class ResourceManager:
-    """Manage resource extraction and packages"""
+class AvailableDistributions(object):
+    """Searchable snapshot of distributions on a search path"""
 
-    extraction_path = None
+    def __init__(self, search_path=None, platform=get_platform()):
+        """Snapshot distributions available on a search path
 
-    def __init__(self):
-        self.cached_files = []
+        `search_path` should be a sequence of ``sys.path`` items.  If not
+        supplied, ``sys.path`` is used.
 
-    def resource_exists(self, package_name, resource_name):
-        """Does the named resource exist in the named package?"""
-        return get_provider(package_name).has_resource(self, resource_name)
+        The `platform` is an optional string specifying the name of the
+        platform that platform-specific distributions must be compatible
+        with.  If not specified, it defaults to the current platform
+        (as defined by the result of ``get_platform()`` when ``pkg_resources``
+        was first imported.)
 
-    def resource_filename(self, package_name, resource_name):
-        """Return a true filesystem path for specified resource"""
-        return get_provider(package_name).get_resource_filename(self,resource_name)
+        You may explicitly set `platform` to ``None`` if you wish to map *all*
+        distributions, not just those compatible with the running platform.
+        """
 
-    def resource_stream(self, package_name, resource_name):
-        """Return a readable file-like object for specified resource"""
-        return get_provider(package_name).get_resource_stream(self,resource_name)
+        self._distmap = {}
+        self._cache = {}
+        self.scan(search_path,platform)
 
-    def resource_string(self, package_name, resource_name):
-        """Return specified resource as a string"""
-        return get_provider(package_name).get_resource_string(self,resource_name)
+    def __iter__(self):
+        """Iterate over distribution keys"""
+        return iter(self._distmap.keys())
 
+    def __contains__(self,name):
+        """Has a distribution named `name` ever been added to this map?"""
+        return name.lower() in self._distmap
 
+    def __len__(self):
+        return len(self._distmap)
 
+    def get(self,key,default=None):
+        """Return ``self[key]`` if `key` in self, otherwise return `default`"""
+        if key in self:
+            return self[key]
+        else:
+            return default
 
+    def scan(self, search_path=None, platform=get_platform()):
+        """Scan `search_path` for distributions usable on `platform`
 
+        Any distributions found are added to the distribution map.
+        `search_path` should be a sequence of ``sys.path`` items.  If not
+        supplied, ``sys.path`` is used.  `platform` is an optional string
+        specifying the name of the platform that platform-specific
+        distributions must be compatible with.  If unspecified, it defaults to
+        the current platform.
 
+        You may explicitly set `platform` to ``None`` if you wish to map *all*
+        distributions, not just those compatible with the running platform.
+        """
+        if search_path is None:
+            search_path = sys.path
+        add = self.add
+        for item in search_path:
+            source = get_dist_source(item)
+            for dist in source.iter_distributions(requirement):
+                if compatible_platforms(dist.platform, platform):
+                    add(dist)
 
+    def __getitem__(self,key):
+        """Return a newest-to-oldest list of distributions for the given key
 
+        The returned list may be modified in-place, e.g. for narrowing down
+        usable distributions.
+        """
+        try:
+            return self._cache[key]
+        except KeyError:
+            key = key.lower()
+            if key not in self._distmap:
+                raise
 
+        if key not in self._cache:
+            dists = self._cache[key] = self._distmap[key]
+            _sort_dists(dists)
 
+        return self._cache[key]
 
+    def add(self,dist):
+        """Add `dist` to the distribution map"""
+        self._distmap.setdefault(dist.key,[]).append(dist)
+        if dist.key in self._cache:
+            _sort_dists(self._cache[dist.key])
 
+    def remove(self,dist):
+        """Remove `dist` from the distribution map"""
+        self._distmap[dist.key].remove(dist)
 
 
+class ResourceManager:
+    """Manage resource extraction and packages"""
 
+    extraction_path = None
 
+    def __init__(self):
+        self.cached_files = []
 
+    def resource_exists(self, package_name, resource_name):
+        """Does the named resource exist in the named package?"""
+        return get_provider(package_name).has_resource(self, resource_name)
+
+    def resource_filename(self, package_name, resource_name):
+        """Return a true filesystem path for specified resource"""
+        return get_provider(package_name).get_resource_filename(
+            self,resource_name
+        )
+
+    def resource_stream(self, package_name, resource_name):
+        """Return a readable file-like object for specified resource"""
+        return get_provider(package_name).get_resource_stream(
+            self, resource_name
+        )
+
+    def resource_string(self, package_name, resource_name):
+        """Return specified resource as a string"""
+        return get_provider(package_name).get_resource_string(
+            self, resource_name
+        )
 
     def get_cache_path(self, archive_name, names=()):
         """Return absolute location in cache for `archive_name` and `names`
@@ -203,47 +326,6 @@
 
 
 
-def iter_distributions(requirement=None, path=None):
-    """Iterate over distributions in `path` matching `requirement`
-
-    The `path` is a sequence of ``sys.path`` items.  If not supplied,
-    ``sys.path`` is used.
-
-    The `requirement` is an optional string specifying the name of the
-    desired distribution.
-    """
-    if path is None:
-        path = sys.path
-
-    if requirement is not None:
-        requirements = list(parse_requirements(requirement))
-        try:
-            requirement, = requirements
-        except ValueError:
-            raise ValueError("Must specify exactly one requirement")
-
-    for item in path:
-        source = get_dist_source(item)
-        for dist in source.iter_distributions(requirement):
-            yield dist
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 def require(*requirements):
     """Ensure that distributions matching `requirements` are on ``sys.path``
 
@@ -251,14 +333,8 @@
     thereof, specifying the distributions and versions required.
     XXX THIS IS DRAFT CODE FOR DESIGN PURPOSES ONLY RIGHT NOW
     """
-    all_distros = {}
+    all_distros = AvailableDistributions()
     installed = {}
-    for dist in iter_distributions():
-        key = dist.name.lower()
-        all_distros.setdefault(key,[]).append(dist)
-        if dist.installed():
-            installed[key] = dist   # XXX what if more than one on path?
-
     all_requirements = {}
 
     def _require(requirements,source=None):
@@ -282,9 +358,15 @@
             pass
             # find "best" distro for key and install it
             # after _require()-ing its requirements
-        
+
     _require(requirements)
-        
+
+
+
+
+
+
+
 class DefaultProvider:
     """Provides access to package resources in the filesystem"""
 
@@ -544,7 +626,7 @@
     dropped, but dashes are retained.  Trailing zeros between alpha segments
     or dashes are suppressed, so that e.g. 2.4.0 is considered the same as 2.4.
     Alphanumeric parts are lower-cased.
-   
+
     The algorithm assumes that strings like '-' and any alpha string > "final"
     represents a "patch level".  So, "2.4-1" is assumed to be a branch or patch
     of "2.4", and therefore "2.4.1" is considered newer than "2.4-1".
@@ -556,7 +638,7 @@
     Finally, to handle miscellaneous cases, the strings "pre", "preview", and
     "rc" are treated as if they were "c", i.e. as though they were release
     candidates, and therefore are not as new as a version string that does not
-    contain them.    
+    contain them.
     """
     parts = []
     for part in _parse_version_parts(s.lower()):
@@ -574,17 +656,18 @@
 
 class Distribution(object):
     """Wrap an actual or potential sys.path entry w/metadata"""
-    
+
     def __init__(self,
         path_str, metadata=None, name=None, version=None,
-        py_version=sys.version[:3]
+        py_version=sys.version[:3], platform=None
     ):
         if name:
-            self.name = name
+            self.name = name.replace('_','-')
         if version:
-            self.version = version
+            self.version = version.replace('_','-')
 
         self.py_version = py_version
+        self.platform = platform
         self.path = path_str
         self.normalized_path = os.path.normpath(os.path.normcase(path_str))
 
@@ -612,7 +695,6 @@
 
 
 
-
     #@classmethod
     def from_filename(cls,filename,metadata=None):
         name,version,py_version,platform = [None]*4
@@ -623,11 +705,9 @@
                 name,version,py_version,platform = match.group(
                     'name','ver','pyver','plat'
                 )
-                name = name.replace('_','-')
-                if version and '_' in version:
-                    version = version.replace('_','-')
         return cls(
-            filename,metadata,name=name,version=version,py_version=py_version
+            filename, metadata, name=name, version=version,
+            py_version=py_version, platform=platform
         )
     from_filename = classmethod(from_filename)
 
@@ -653,7 +733,9 @@
             return pv
 
     parsed_version = property(parsed_version)
-    
+
+
+
 def parse_requirements(strs):
     """Yield ``Requirement`` objects for each specification in `strs`
 
@@ -695,7 +777,6 @@
 
 
 
-
 def _get_mro(cls):
     """Get an mro for a type or classic class"""
     if not isinstance(cls,type):



More information about the Python-checkins mailing list