[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