Update of /cvsroot/python/python/nondist/sandbox/setuptools In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13209 Modified Files: pkg_resources.py Log Message: Distribution metadata parsing: distribution objects can now extract their version from PKG-INFO and their dependencies from depends.txt, including optional dependencies. Index: pkg_resources.py =================================================================== RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/pkg_resources.py,v retrieving revision 1.11 retrieving revision 1.12 diff -u -d -r1.11 -r1.12 --- pkg_resources.py 22 May 2005 18:16:59 -0000 1.11 +++ pkg_resources.py 22 May 2005 19:40:22 -0000 1.12 @@ -14,23 +14,19 @@ method. """ __all__ = [ - 'register_loader_type', 'get_provider', 'IResourceProvider', + 'register_loader_type', 'get_provider', 'IResourceProvider', 'ResourceManager', 'AvailableDistributions', 'require', 'resource_string', 'resource_stream', 'resource_filename', 'set_extraction_path', 'cleanup_resources', 'parse_requirements', 'parse_version', - 'compatible_platforms', 'get_platform', + 'compatible_platforms', 'get_platform', 'IMetadataProvider', 'ResolutionError', 'VersionConflict', 'DistributionNotFound', - 'Distribution', 'Requirement', # 'glob_resources' + 'InvalidOption', 'Distribution', 'Requirement', 'yield_lines', + 'split_sections', # 'glob_resources' ] import sys, os, zipimport, time, re -def _sort_dists(dists): - tmp = [(dist.version,dist) for dist in dists] - tmp.sort() - dists[::-1] = [d for v,d in tmp] - -class ResolutionError(ImportError): +class ResolutionError(Exception): """Abstract base for dependency resolution errors""" class VersionConflict(ResolutionError): @@ -39,6 +35,10 @@ class DistributionNotFound(ResolutionError): """A requested distribution was not found""" +class InvalidOption(ResolutionError): + """Invalid or unrecognized option name for a distribution""" + + _provider_factories = {} def register_loader_type(loader_type, provider_factory): @@ -80,7 +80,22 @@ return False -class IResourceProvider: +class IMetadataProvider: + + def has_metadata(name): + """Does the package's distribution contain the named metadata?""" + + def get_metadata(name): + """The named metadata resource as a string""" + + def get_metadata_lines(name): + """Yield named metadata resource as list of non-blank non-comment lines + + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted. + """ + +class IResourceProvider(IMetadataProvider): """An object that provides access to package resources""" @@ -102,25 +117,10 @@ def has_resource(resource_name): """Does the package contain the named resource?""" - def has_metadata(name): - """Does the package's distribution contain the named metadata?""" - - def get_metadata(name): - """The named metadata resource as a string""" - - def get_metadata_lines(name): - """Yield named metadata resource as list of non-blank non-comment lines - - Leading and trailing whitespace is stripped from each line, and lines - with ``#`` as the first non-blank character are omitted. - """ - # XXX list_resources? glob_resources? - - class AvailableDistributions(object): """Searchable snapshot of distributions on a search path""" @@ -417,7 +417,6 @@ XXX This doesn't work yet, because: * get_distro_source() isn't implemented - * Distribution.depends() isn't implemented * Distribution.install_on() isn't implemented * Requirement.options isn't implemented * AvailableDistributions.resolve() is untested @@ -449,6 +448,7 @@ + class DefaultProvider: """Provides access to package resources in the filesystem""" @@ -746,11 +746,12 @@ if name: self.name = name.replace('_','-') if version: - self.version = version.replace('_','-') + self._version = version.replace('_','-') self.py_version = py_version self.platform = platform self.path = path_str + self.metadata = metadata def installed_on(self,path=None): """Is this distro installed on `path`? (defaults to ``sys.path``)""" @@ -776,7 +777,6 @@ - # These properties have to be lazy so that we don't have to load any # metadata until/unless it's actually needed. (i.e., some distributions # may not know their name or version without loading PKG-INFO) @@ -800,17 +800,58 @@ parsed_version = property(parsed_version) + #@property + def version(self): + try: + return self._version + except AttributeError: + for line in self.metadata.get_metadata_lines('PKG-INFO'): + if line.lower().startswith('version:'): + self._version = line.split(':',1)[1].strip() + return self._version + else: + raise AttributeError( + "Missing Version: header in PKG-INFO", self + ) + version = property(version) + #@property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + dm = self.__dep_map = {None: []} + if self.metadata.has_metadata('depends.txt'): + for section,contents in split_sections( + self.metadata.get_metadata_lines('depends.txt') + ): + dm[section] = list(parse_requirements(contents)) + return dm + _dep_map = property(_dep_map) + def depends(self,options=()): + """List of Requirements needed for this distro if `options` are used""" + dm = self._dep_map + deps = [] + deps.extend(dm.get(None,())) + for opt in options: + try: + deps.extend(dm[opt.lower()]) + except KeyError: + raise InvalidOption("No such option", self, opt) + return deps - +def _sort_dists(dists): + tmp = [(dist.version,dist) for dist in dists] + tmp.sort() + dists[::-1] = [d for v,d in tmp] @@ -941,6 +982,33 @@ os.makedirs(dirname) +def split_sections(s): + """Split a string or iterable thereof into (section,content) pairs + + Each ``section`` is a lowercase version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if content: + yield section, content + section = line[1:-1].strip().lower() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + if content: + yield section, content + + # Set up global resource manager _manager = ResourceManager() @@ -952,3 +1020,6 @@ _initialize(globals()) + + +
participants (1)
-
pje@users.sourceforge.net