##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Support for reading and generating publication metadata files.
Such files include the PKG-INFO files generated by `distutils` as well
as the PUBLICATION.cfg files used by **zpkg**.
:var PUBLICATION_CONF: The default name of the file containing
publication data as used by **zpkg**.
"""
from distutils.dist import DistributionMetadata
from distutils.util import rfc822_escape
from email.Parser import HeaderParser
from StringIO import StringIO
PUBLICATION_CONF = "PUBLICATION.cfg"
# XXX The dump() and dumps() methods are very similar to the
# DistributionMetadata.write_pkg_info() method, but don't constrain
# where the data is written. Much of this can be discarded if
# portions of the PEP 262 patch (http://www.python.org/sf/562100) are
# accepted.
def dump(metadata, f):
"""Write package metadata to a file in PKG-INFO format.
:param metadata: Metadata object to serialize.
:param f: Open file object to write to.
"""
metadata_version = "1.0"
if (metadata.maintainer or metadata.maintainer_email
or metadata.url or metadata.get_classifiers()):
metadata_version = "1.1"
print >>f, "Metadata-Version:", metadata_version
print >>f, "Name:", metadata.get_name()
if metadata.version:
print >>f, "Version:", metadata.get_version()
if metadata.description:
print >>f, "Summary:", metadata.get_description()
if metadata.url:
print >>f, "Home-page:", metadata.get_url()
if metadata.author:
print >>f, "Author:", metadata.get_author()
if metadata.author_email:
print >>f, "Author-email:", metadata.get_author_email()
if metadata.maintainer:
print >>f, "Maintainer:", metadata.get_maintainer()
if metadata.maintainer_email:
print >>f, "Maintainer-email:", metadata.get_maintainer_email()
if metadata.license:
print >>f, "License:", metadata.get_license()
if metadata.url:
print >>f, "Download-URL:", metadata.url
if metadata.long_description:
long_desc = rfc822_escape(metadata.get_long_description())
print >>f, "Description:", long_desc
keywords = metadata.get_keywords()
if keywords:
print >>f, "Keywords:", ", ".join(keywords)
for platform in metadata.get_platforms():
print >>f, "Platform:", platform
for classifier in metadata.get_classifiers():
print >>f, "Classifier:", classifier
def dumps(metadata):
"""Return package metadata serialized in PKG-INFO format.
:return: String containing the serialized metadata.
:rtype: str
:param metadata: Metadata object to serialize.
"""
sio = StringIO()
dump(metadata, sio)
return sio.getvalue()
def load(f, versioninfo=False, metadata=None):
"""Parse a PKG-INFO file and return a DistributionMetadata instance.
:return: Populated metadata object.
:rtype: `DistributionMetadata`
:param versioninfo: Flag indicating whether version-specific
information should be included.
:param metadata: Metadata object which should be populated from
the publication data. If omitted, a fresh
`DistributionMetadata` instance will be used.
"""
parser = HeaderParser()
msg = parser.parse(f)
return _loadmsg(msg, versioninfo, metadata)
def loads(text, versioninfo=False, metadata=None):
"""Parse PKG-INFO source text and return a DistributionMetadata instance.
:return: Populated metadata object.
:rtype: `DistributionMetadata`
:param versioninfo: Flag indicating whether version-specific
information should be included.
:param metadata: Metadata object which should be populated from
the publication data. If omitted, a fresh
`DistributionMetadata` instance will be used.
"""
parser = HeaderParser()
msg = parser.parsestr(text)
return _loadmsg(msg, versioninfo, metadata)
def _loadmsg(msg, versioninfo, metadata=None):
if metadata is None:
metadata = DistributionMetadata()
if versioninfo:
metadata.version = _get_single_header(msg, "Version")
metadata.download_url = _get_single_header(msg, "Download-URL")
metadata.name = _get_single_header(msg, "Name")
metadata.author = _get_single_header(msg, "Author")
metadata.author_email = _get_single_header(msg, "Author-email")
metadata.maintainer = _get_single_header(msg, "Maintainer")
metadata.maintainer_email = _get_single_header(msg, "Maintainer-email")
metadata.url = _get_single_header(msg, "Home-page")
metadata.license = _get_single_header(msg, "License")
metadata.description = _get_single_header(msg, "Summary")
metadata.long_description = _get_single_header(msg, "Description")
keywords = _get_single_header(msg, "Keywords", "")
keywords = [s.strip() for s in keywords.split(",") if s.strip()]
metadata.keywords = keywords or None
platforms = msg.get_all("Platform")
if platforms:
metadata.platforms = platforms
classifiers = msg.get_all("Classifier")
if classifiers:
metadata.classifiers = classifiers
return metadata
def _get_single_header(msg, name, default=None):
"""Return the value for a header that only occurs once in the input.
:raises ValueError: If the header occurs more than once.
"""
headers = msg.get_all(name)
if headers and len(headers) > 1:
raise ValueError("header %r can only be given once" % name)
if headers:
v = headers[0]
if v == "UNKNOWN":
return None
else:
return v
else:
return default
ALPHA = "Development Status :: 3 - Alpha"
BETA = "Development Status :: 4 - Beta"
STABLE = "Development Status :: 5 - Production/Stable"
def set_development_status(metadata, status):
if not metadata.classifiers:
metadata.classifiers = [status]
return
for i in range(len(metadata.classifiers)):
classifier = metadata.classifiers[i]
parts = [s.strip() for s in classifier.lower().split("::")]
if parts[0] == "development status":
metadata.classifiers[i] = status
break
else:
metadata.classifiers.append(status)
syntax highlighted by Code2HTML, v. 0.9.1