[Zope-CVS] CVS: Packages/zpkgtools/zpkgtools - cvsloader.py:1.1
dependencies.py:1.1 include.py:1.1 pep262.py:1.1
publication.py:1.1 setup.py:1.1
Fred L. Drake, Jr.
fred at zope.com
Fri Mar 5 16:39:23 EST 2004
Update of /cvs-repository/Packages/zpkgtools/zpkgtools
In directory cvs.zope.org:/tmp/cvs-serv10053/zpkgtools
Added Files:
cvsloader.py dependencies.py include.py pep262.py
publication.py setup.py
Log Message:
first steps for the release assembler
=== Added File Packages/zpkgtools/zpkgtools/cvsloader.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Data loader that looks in CVS for content."""
import copy
import os
import posixpath
import re
import shutil
import tempfile
class CvsLoadingError(Exception):
"""Raised when there was some error loading from CVS."""
def __init__(self, cvsurl, exitcode):
self.cvsurl = cvsurl
self.exitcode = exitcode
Exception.__init__(self, ("could not load from %s (cvs exit code %d)"
% (cvsurl.getUrl(), exitcode)))
_cvs_url_match = re.compile(
"""
cvs://(?P<host>[^/]*)
/(?P<cvsroot>[^:]*)
(:(?P<path>[^:]*)
(:(?P<tag>[^:]*))?)?$
""",
re.IGNORECASE | re.VERBOSE).match
_repository_url_match = re.compile(
"""
repository:(?P<path>[^:]*)
(:(?P<tag>[^:]*))?$
""",
re.IGNORECASE | re.VERBOSE).match
def parse(cvsurl):
m = _cvs_url_match(cvsurl)
if m is None:
m = _repository_url_match(cvsurl)
if m is None:
raise ValueError("not a valid CVS url")
return RepositoryUrl(m.group("path"), m.group("tag"))
host = m.group("host")
cvsroot = "/" + m.group("cvsroot")
path = m.group("path")
tag = m.group("tag") or ""
username = None
password = None
type = None
if "@" in host:
userinfo, host = host.split("@", 1)
if ":" in userinfo:
username, password = userinfo.split(":", 1)
else:
username = userinfo
if ":" in host:
host, type = host.split(":", 1)
return CvsUrl(type, host, cvsroot, path, tag, username, password)
def fromPath(path):
path = os.path.normpath(path)
if os.path.isdir(path):
dirname = path
basename = ""
else:
dirname, basename = os.path.split(path)
cvsdir = os.path.join(dirname, "CVS")
tagfile = os.path.join(cvsdir, "Tag")
if os.path.isfile(tagfile):
tag = _read_one_line(tagfile)[1:]
else:
tag = None
if basename:
# The tag may be overridden for specific files; check:
entries = os.path.join(cvsdir, "Entries")
if os.path.isfile(entries):
entries = open(entries, "rU")
for line in entries:
parts = line.split("/")
if os.path.normcase(parts[1]) == os.path.normcase(basename):
if len(parts) >= 6:
tag = parts[5][1:].rstrip() or tag
break
modpath = _read_one_line(os.path.join(cvsdir, "Repository"))
repo = _read_one_line(os.path.join(cvsdir, "Root"))
host = ""
type = username = None
if repo[:1] == ":":
parts = repo.split(":")
type = parts[1]
host = parts[2]
cvsroot = parts[3]
elif ":" in repo:
host, cvsroot = repo.split(":", 1)
if "@" in host:
username, host = host.split("@")
return CvsUrl(type, host, cvsroot,
posixpath.join(modpath, basename),
tag, username)
def _read_one_line(filename):
f = open(filename, "rU")
try:
line = f.readline()
finally:
f.close()
return line.rstrip()
class CvsUrl:
def __init__(self, type, host, cvsroot, path,
tag=None, username=None, password=None):
assert cvsroot.startswith("/")
self.type = type or None
self.host = host or None
self.cvsroot = cvsroot
self.path = path
self.tag = tag or None
self.username = username or None
self.password = password or None
def getCvsRoot(self):
s = ""
if self.type:
s = ":%s:" % self.type
if self.username:
s = "%s%s@" % (s, self.username)
if self.host:
s = "%s%s:" % (s, self.host)
return s + self.cvsroot
def getUrl(self):
host = self.host or ""
if self.type:
host = "%s:%s" % (host, self.type)
if self.username:
username = self.username
if self.password:
username = "%s:%s" % (username, self.password)
host = "%s@%s" % (username, host)
url = "cvs://%s%s:%s" % (host, self.cvsroot, self.path)
if self.tag:
url = "%s:%s" % (url, self.tag)
return url
class RepositoryUrl:
def __init__(self, path, tag=None):
self.path = path or None
self.tag = tag or None
def getUrl(self):
url = "repository:" + self.path
if self.tag:
url = "%s:%s" % (url, self.tag)
return url
def join(self, cvsurl):
cvsurl = copy.copy(cvsurl)
if self.path:
path = posixpath.normpath(self.path)
if path[:1] == "/":
newpath = path[1:]
else:
newpath = posixpath.join(cvsurl.path, self.path)
else:
newpath = cvsurl.path
cvsurl.path = posixpath.normpath(newpath)
if self.tag:
cvsurl.tag = self.tag
return cvsurl
class CvsLoader:
def __init__(self, cvsurl, tag=None):
self.cvsurl = cvsurl
self.tag = tag or None
self.workdirs = []
def cleanup(self):
"""Remove all checkouts that are present."""
while self.workdirs:
d = self.workdirs.pop()
shutil.rmtree(d)
def load(self, url):
"""Load resource from URL into a temporary location.
Returns the location of the resource once loaded.
"""
if isinstance(url, basestring):
try:
url = parse(url)
except ValueError:
raise TypeError(
"load() requires a cvs or repository URL; received %r"
% url)
if isinstance(url, RepositoryUrl):
cvsurl = url.join(self.cvsurl)
elif isinstance(url, CvsUrl):
cvsurl = copy.copy(url)
else:
raise TypeError("load() requires a cvs or repository URL")
if not cvsurl.tag:
cvsurl.tag = self.tag
workdir = tempfile.mkdtemp(prefix="cvsloader-")
cvsroot = cvsurl.getCvsRoot()
tag = cvsurl.tag or "HEAD"
path = cvsurl.path or "."
rc = self.runCvsExport(cvsroot, workdir, tag, path)
if rc:
# some error occurred; haven't figured out what, and don't
# really care; details went to standard error:
shutil.rmtree(workdir)
raise CvsLoadingError(cvsurl, rc)
self.workdirs.append(workdir)
if path == ".":
return workdir
elif self.isFileResource(cvsurl):
basename = posixpath.basename(path)
return os.path.join(workdir, basename, basename)
else:
basename = posixpath.basename(path)
return os.path.join(workdir, basename)
def runCvsExport(self, cvsroot, workdir, tag, path):
# cvs -f -Q -z6 -d CVSROOT export -kk -d WORKDIR -r TAG PATH
# separated out from load() to ease testing of load()
# XXX not sure of a good way to test this method!
wf = posixpath.basename(path)
pwd = os.getcwd()
os.chdir(workdir)
try:
rc = os.spawnlp(os.P_WAIT, "cvs",
"cvs", "-f", "-Q", "-z6", "-d", cvsroot,
"export", "-kk", "-d", wf, "-r", tag, path)
finally:
os.chdir(pwd)
return rc
# XXX CVS does some weird things with export; not sure how much
# they mean yet. Note that there's no way to tell if the resource
# is a file or directory from the cvs: URL.
#
# - If the directory named with -d already exists, a CVS/
# directory is created within that and is populated, regardless
# of whether the requested resource is a file or directory.
#
# - If the directory does not already exist it is created, and no
# CVS/ directory is created within that.
#
# - If the requested resource is a file, it is created within the
# new directory, otherwise the directory is populated with the
# contents of the directory in the repository.
#
# "cvs rlog -R" gives a list of ,v files for the selected
# resource. If there's more than one, it's a directory.
# Otherwise, it's a file if the path matches the repository root +
# the path from the cvs: URL.
def isFileResource(self, cvsurl):
if isinstance(cvsurl, RepositoryUrl):
cvsurl = cvsurl.join(self.cvsurl)
if not cvsurl.path:
# The whole repository is always a directory
return False
f = self.openCvsRLog(cvsurl.getCvsRoot(), cvsurl.path)
line1 = f.readline().rstrip()
line2 = f.readline()
f.close()
if line2:
# more than one line; must be a directory
return False
module, base = posixpath.split(cvsurl.path)
comma_v = posixpath.join(cvsurl.cvsroot, cvsurl.path) + ",v"
comma_v_attic = posixpath.join(
cvsurl.cvsroot, module, "Attic", base) + ",v"
return line1 in (comma_v, comma_v_attic)
# separate this out to ease testing
def openCvsRLog(self, cvsroot, path):
return os.popen(
"cvs -f -d '%s' rlog -R -l '%s'" % (cvsroot, path), "r")
=== Added File Packages/zpkgtools/zpkgtools/dependencies.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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 handling dependency information."""
import re
import sets
_ident = "[a-zA-Z_][a-zA-Z_0-9]*"
_module_match = re.compile(r"%s(\.%s)*$" % (_ident, _ident)).match
def isModuleName(string):
return _module_match(string) is not None
def load(f):
deps = DependencyInfo()
deps.load(f)
return deps
class DependencyInfo(object):
"""Dependency information."""
def __init__(self):
self.modules = sets.Set()
self.others = sets.Set()
def load(self, f):
while True:
line = f.readline().strip()
if not line:
return
if line[0] == "#":
continue
if isModuleName(line):
self.modules.add(line)
else:
self.others.add(line)
=== Added File Packages/zpkgtools/zpkgtools/include.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Processor for inclusions when building a release."""
import glob
import os
import posixpath
import shutil
import urllib
import urllib2
from zpkgtools import cvsloader
# Names that are exluded from globbing results:
EXCLUDE_NAMES = ["CVS", ".cvsignore", "RCS", "SCCS", ".svn"]
class InclusionError(Exception):
pass
class InclusionSpecificationError(ValueError, InclusionError):
def __init__(self, message, filename, lineno):
self.filename = filename
self.lineno = lineno
ValueError.__init__(self, message)
class InclusionProcessor:
"""Handler for processing inclusion specifications.
Methods are provided for both reading specifications and creating
the output tree.
The following attributes are filled in by loadSpecification().
These are exposed for use from the unit tests.
excludes
Iterable containing the absolute path names of the files in the
source tree that should not be part of the destination.
includes
Mapping from relative path (relative to the destination) to
either an absolute path in the source directory or a URL.
"""
def __init__(self, source, destination, specfile=None):
if not os.path.exists(source):
raise InclusionError("source directory does not exist: %r"
% source)
self.source = os.path.abspath(source)
self.destination = os.path.abspath(destination)
prefix = os.path.commonprefix([self.source, self.destination])
if prefix == self.source:
raise InclusionError("destination directory may not be"
" contained in the source directory")
elif prefix == self.destination:
raise InclusionError("source directory may not be"
" contained in the destination directory")
self.excludes = {}
self.includes = {}
f = None
if specfile is None:
# Read soruce/INCLUDES.txt, if it exists.
specfile = os.path.join(source, "INCLUDES.txt")
if os.path.exists(specfile):
f = open(specfile, "rU")
contextdir = self.source
else:
# Read the specified file, without testing for existance.
f = open(specfile, "rU")
contextdir = os.path.dirname(os.path.abspath(specfile))
if f is not None:
try:
self.loadSpecification(f, specfile)
finally:
f.close()
if os.path.isdir(os.path.join(contextdir, "CVS")):
self.cvsurl = cvsloader.fromPath(contextdir)
else:
self.cvsurl = None
self.cvs_loader = None
def loadSpecification(self, f, filename):
lineno = 0
for line in f:
lineno += 1
line = line.strip()
if line[:1] in ("", "#"):
continue
parts = line.split(None, 1)
if len(parts) != 2:
raise InclusionSpecificationError(
"inclusion specifications require"
" both target and source parts",
filename, lineno)
dest, src = parts
dest = self.normalizePath(dest, "destination", filename, lineno)
src = self.normalizePathOrURL(src, "source", filename, lineno)
if src == "-":
path = os.path.join(self.source, dest)
expansions = self.glob(path)
if not expansions:
raise InclusionSpecificationError(
"exclusion %r doesn't match any files" % dest,
filename, lineno)
for fn in expansions:
self.excludes[fn] = fn
else:
self.includes[dest] = src
def glob(self, pattern):
return [n for n in glob.glob(pattern)
if os.path.basename(n) not in EXCLUDE_NAMES]
def filterNames(self, names):
return [n for n in names
if n not in EXCLUDE_NAMES]
def normalizePath(self, path, type, filename, lineno):
if ":" in path:
scheme, rest = urllib.splittype(path)
if len(scheme) == 1:
# looks like a drive letter for Windows; scream,
# 'cause that's not allowable:
raise InclusionSpecificationError(
"drive letters are not allowed in inclusions: %r"
% path,
filename, lineno)
np = posixpath.normpath(path)
if posixpath.isabs(np) or np[:1] == ".":
raise InclusionSpecificationError(
"%s path must not be absolute or refer to a location"
" not contained in the source directory"
% path,
filename, lineno)
return np.replace("/", os.sep)
def normalizePathOrURL(self, path, type, filename, lineno):
if ":" in path:
scheme, rest = urllib.splittype(path)
if len(scheme) != 1:
# should normalize the URL, but skip that for now
return path
return self.normalizePath(path, type, filename, lineno)
def createDistributionTree(self):
"""Create the output tree according to the loaded specification.
The destination directory will be created if it doesn't
already exist.
"""
self.copyTree(self.source, self.destination)
self.addIncludes()
def copyTree(self, source, destination):
"""Populate the destination tree from the source tree.
Files and directories will be created with the same permission
bits and stat info as the source tree.
Entries identified as exclusions will not be copied at all.
"""
if not os.path.exists(destination):
os.mkdir(destination)
prefix = os.path.join(source, "")
for dirname, dirs, files in os.walk(source):
dirs[:] = self.filterNames(dirs)
files = self.filterNames(files)
# remove excluded directories:
for dir in dirs[:]:
fullpath = os.path.join(dirname, dir)
if fullpath in self.excludes:
dirs.remove(dir)
# reldir is the name of the directory to write to,
# relative to destination. It will be '' at the top
# level.
reldir = dirname[len(prefix):]
if reldir:
destdir = os.path.join(destination, reldir)
else:
destdir = destination
for file in files:
srcname = os.path.join(dirname, file)
if srcname in self.excludes:
continue
destname = os.path.join(destdir, file)
# Copy file data, permission bits, and stat info;
# owner/group are not copied.
shutil.copy2(srcname, destname)
for dir in dirs:
srcname = os.path.join(dirname, dir)
destname = os.path.join(destdir, dir)
# Create the directory, copying over the permission
# bits and stat info.
os.mkdir(destname)
shutil.copymode(srcname, destname)
shutil.copystat(srcname, destname)
def addIncludes(self):
"""Add files and directories based on the specification."""
for relpath, source in self.includes.iteritems():
self.addSingleInclude(relpath, source)
def addSingleInclude(self, relpath, source):
dirname, basename = os.path.split(relpath)
if dirname:
destdir = os.path.join(self.destination, dirname)
if not os.path.exists(destdir):
os.makedirs(destdir)
else:
# Known to exist, so no need to create it.
destdir = self.destination
# This is what we want to create:
destination = os.path.join(destdir, basename)
try:
cvsurl = cvsloader.parse(source)
except ValueError:
# not a cvs: or repository: URL
type, rest = urllib.splittype(source)
if type:
# some sort of URL
self.includeFromUrl(source, destination)
else:
# local path
self.includeFromLocalTree(os.path.join(self.source, source),
destination)
else:
self.includeFromCvs(cvsurl, destination)
def includeFromLocalTree(self, source, destination):
# Check for file-ness here since copyTree() doesn't handle
# individual files at all.
if os.path.isfile(source):
shutil.copy2(source, destination)
else:
self.copyTree(source, destination)
def includeFromUrl(self, source, destination):
# XXX treat FTP URLs specially to get permission bits and directories?
inf = urllib2.urlopen(source)
try:
outf = open(destination, "w")
try:
shutil.copyfileobj(inf, outf)
finally:
outf.close()
finally:
inf.close()
def includeFromCvs(self, cvsurl, destination):
loader = self.getLoader(cvsurl)
source = loader.load(cvsurl)
if os.path.isfile(source):
shutil.copy2(source, destination)
else:
self.copyTree(source, destination)
def getLoader(self, cvsurl):
if self.cvs_loader is None and self.cvsurl is not None:
self.cvs_loader = cvsloader.CvsLoader(self.cvsurl)
if self.cvs_loader is None:
# We can create a temporary loader from a cvs: URL if we need to:
if isinstance(cvsurl, cvsloader.CvsUrl):
loader = cvsloader.CvsLoader(cvsurl)
else:
# We don't have a cvs: URL, and repository: URLs are
# always relative to a cvs: URL, so we can't proceed:
raise InclusionError(
"cannot load URL %s without base repository information"
% cvsurl.getUrl())
else:
loader = self.cvs_loader
return loader
=== Added File Packages/zpkgtools/zpkgtools/pep262.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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 writing PEP 262 package metadata files."""
import sets
from distutils.dist import DistributionMetadata
from zpkgtools import dependencies
from zpkgtools import publication
class PackageData(dependencies.DependencyInfo, DistributionMetadata):
def __init__(self):
super(PackageData, self).__init__()
self.files = {}
self.provides = sets.Set()
def load(self, f):
partsline = f.readline()
parts = partsline.split()
for part in parts:
if part == "PKG-INFO":
publication.load(f, metadata=self, versioninfo=True)
elif part == "FILES":
self.loadFiles(f)
elif part == "REQUIRES":
super(PackageData, self).load(f)
elif part == "PROVIDES":
self.loadProvides(f)
else:
# unsupported section; skip to next blank line
for line in f:
if not line.strip():
break
def loadFiles(self, f):
while True:
line = f.readline().strip()
if not line:
return
parts = line.split("\t")
while len(parts) < 6:
parts.append(None)
path, size, perms, owner, group, digest = parts[:6]
try:
size = int(size)
except (TypeError, ValueError):
# Ugh! but we don't want to lose the info, so just keep it.
pass
if perms == "unknown":
perms = None
self.files[path] = FileEntry(path, size, perms,
owner, group, digest)
def loadProvides(self, f):
while True:
line = f.readline().strip()
if not line:
return
self.provides.add(line)
class FileEntry(object):
__slots__ = "path", "size", "permissions", "owner", "group", "digest"
def __init__(self, path, size, permissions, owner, group, digest):
self.path = path
self.size = size
self.permissions = permissions
self.owner = owner
self.group = group
self.digest = digest
=== Added File Packages/zpkgtools/zpkgtools/publication.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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 PKG-INFO files.
$Id: publication.py,v 1.1 2004/03/05 21:39:19 fdrake Exp $
"""
from distutils.dist import DistributionMetadata
from distutils.util import rfc822_escape
from email.Parser import Parser
from StringIO import StringIO
# 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."""
print >>f, "Metadata-Version: 1.0"
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."""
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.
Unsupported metadata formats cause a ValueError to be raised.
"""
parser = Parser()
msg = parser.parse(f, headersonly=True)
return _loadmsg(msg, versioninfo, metadata)
def loads(text, versioninfo=False, metadata=None):
"""Parse PKG-INFO source text and return a DistributionMetadata instance.
Unsupported metadata formats cause a ValueError to be raised.
"""
parser = Parser()
msg = parser.parsestr(text, headersonly=True)
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.
If the header occurs more than once, ValueError is raised.
"""
headers = msg.get_all(name)
if headers and len(headers) > 1:
raise ValueError("header %r can only be given once" % name)
if headers:
return headers[0]
else:
return default
=== Added File Packages/zpkgtools/zpkgtools/setup.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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.
#
##############################################################################
"""Generator for distutils setup.py files."""
import os.path
from StringIO import StringIO
# These are both attributes of the publication data object and keyword
# arguments to setup().
STRING_ATTRIBUTES = [
"name",
"version",
"license",
"author",
"author_email",
"maintainer",
"maintainer_email",
"description",
"long_description",
"download_url",
]
LIST_ATTRIBUTES = [
"keywords",
"classifiers",
]
def generate(directory, publication, version, packageinfo=None):
setup_py = os.path.join(directory, "setup.py")
f = open(setup_py, "w")
try:
generate_py(f, publication, version, packageinfo)
finally:
f.close()
# We don't always need to generate a setup.cfg, so use a StringIO
# as an intermediate:
f = StringIO()
generate_cfg(f, publication, version, packageinfo)
text = f.getvalue()
if text.strip():
setup_cfg = os.path.join(directory, "setup.cfg")
f = open(setup_cfg, "w")
try:
f.write(CONFIG_HEADER)
f.write(text)
finally:
f.close()
def generate_py(f, publication, version, packageinfo):
"""Generate the setup.py for a release."""
print >>f, HEADER
print >>f, "setup(version=%r," % version
for name in STRING_ATTRIBUTES:
dumpString(f, publication, name)
if publication.platforms:
print >>f, " platforms=%r," % ", ".join(publication.platforms)
for name in LIST_ATTRIBUTES:
dumpList(f, publication, name)
print >>f, " )"
def generate_cfg(f, publication, version, packageinfo):
"""Generate the setup.cfg for a release."""
# For now, do nothing.
def dumpString(f, publication, name):
value = getattr(publication, name)
if value is not None:
if "\n" in value:
# deal with multiline values
pass
else:
print >>f, " %s=%r," % (name, value)
def dumpList(f, publication, name):
value = getattr(publication, name)
if value:
print >>f, " %s=[" % name
for v in value:
print >>f, " %r," % v
print >>f, " ],"
HEADER = """\
#! /usr/bin/env python
#
# THIS IS A GENERATED FILE. DO NOT EDIT THIS DIRECTLY.
from distutils.core import setup
from distutils.core import Extension
"""
CONFIG_HEADER = """\
# THIS IS A GENERATED FILE. DO NOT EDIT THIS DIRECTLY.
"""
More information about the Zope-CVS
mailing list