aboutsummaryrefslogtreecommitdiffstats
path: root/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/python/_release.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/python/_release.py')
-rwxr-xr-xlib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/python/_release.py1371
1 files changed, 0 insertions, 1371 deletions
diff --git a/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/python/_release.py b/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/python/_release.py
deleted file mode 100755
index 78d84f6a..00000000
--- a/lib/python2.7/site-packages/Twisted-12.2.0-py2.7-linux-x86_64.egg/twisted/python/_release.py
+++ /dev/null
@@ -1,1371 +0,0 @@
-# -*- test-case-name: twisted.python.test.test_release -*-
-# Copyright (c) Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-"""
-Twisted's automated release system.
-
-This module is only for use within Twisted's release system. If you are anyone
-else, do not use it. The interface and behaviour will change without notice.
-
-Only Linux is supported by this code. It should not be used by any tools
-which must run on multiple platforms (eg the setup.py script).
-"""
-
-import textwrap
-from datetime import date
-import re
-import sys
-import os
-from tempfile import mkdtemp
-import tarfile
-
-from subprocess import PIPE, STDOUT, Popen
-
-from twisted.python.versions import Version
-from twisted.python.filepath import FilePath
-from twisted.python.dist import twisted_subprojects
-from twisted.python.compat import execfile
-
-# This import is an example of why you shouldn't use this module unless you're
-# radix
-try:
- from twisted.lore.scripts import lore
-except ImportError:
- pass
-
-# The offset between a year and the corresponding major version number.
-VERSION_OFFSET = 2000
-
-
-def runCommand(args):
- """
- Execute a vector of arguments.
-
- @type args: C{list} of C{str}
- @param args: A list of arguments, the first of which will be used as the
- executable to run.
-
- @rtype: C{str}
- @return: All of the standard output.
-
- @raise CommandFailed: when the program exited with a non-0 exit code.
- """
- process = Popen(args, stdout=PIPE, stderr=STDOUT)
- stdout = process.stdout.read()
- exitCode = process.wait()
- if exitCode < 0:
- raise CommandFailed(None, -exitCode, stdout)
- elif exitCode > 0:
- raise CommandFailed(exitCode, None, stdout)
- return stdout
-
-
-class CommandFailed(Exception):
- """
- Raised when a child process exits unsuccessfully.
-
- @type exitStatus: C{int}
- @ivar exitStatus: The exit status for the child process.
-
- @type exitSignal: C{int}
- @ivar exitSignal: The exit signal for the child process.
-
- @type output: C{str}
- @ivar output: The bytes read from stdout and stderr of the child process.
- """
- def __init__(self, exitStatus, exitSignal, output):
- Exception.__init__(self, exitStatus, exitSignal, output)
- self.exitStatus = exitStatus
- self.exitSignal = exitSignal
- self.output = output
-
-
-
-def _changeVersionInFile(old, new, filename):
- """
- Replace the C{old} version number with the C{new} one in the given
- C{filename}.
- """
- replaceInFile(filename, {old.base(): new.base()})
-
-
-
-def getNextVersion(version, now=None):
- """
- Calculate the version number for a new release of Twisted based on
- the previous version number.
-
- @param version: The previous version number.
- @param now: (optional) The current date.
- """
- # XXX: This has no way of incrementing the patch number. Currently, we
- # don't need it. See bug 2915. Jonathan Lange, 2007-11-20.
- if now is None:
- now = date.today()
- major = now.year - VERSION_OFFSET
- if major != version.major:
- minor = 0
- else:
- minor = version.minor + 1
- return Version(version.package, major, minor, 0)
-
-
-def changeAllProjectVersions(root, versionTemplate, today=None):
- """
- Change the version of all projects (including core and all subprojects).
-
- If the current version of a project is pre-release, then also change the
- versions in the current NEWS entries for that project.
-
- @type root: L{FilePath}
- @param root: The root of the Twisted source tree.
- @type versionTemplate: L{Version}
- @param versionTemplate: The version of all projects. The name will be
- replaced for each respective project.
- @type today: C{str}
- @param today: A YYYY-MM-DD formatted string. If not provided, defaults to
- the current day, according to the system clock.
- """
- if not today:
- today = date.today().strftime('%Y-%m-%d')
- for project in findTwistedProjects(root):
- if project.directory.basename() == "twisted":
- packageName = "twisted"
- else:
- packageName = "twisted." + project.directory.basename()
- oldVersion = project.getVersion()
- newVersion = Version(packageName, versionTemplate.major,
- versionTemplate.minor, versionTemplate.micro,
- prerelease=versionTemplate.prerelease)
-
- if oldVersion.prerelease:
- builder = NewsBuilder()
- builder._changeNewsVersion(
- root.child("NEWS"), builder._getNewsName(project),
- oldVersion, newVersion, today)
- builder._changeNewsVersion(
- project.directory.child("topfiles").child("NEWS"),
- builder._getNewsName(project), oldVersion, newVersion,
- today)
-
- # The placement of the top-level README with respect to other files (eg
- # _version.py) is sufficiently different from the others that we just
- # have to handle it specially.
- if packageName == "twisted":
- _changeVersionInFile(
- oldVersion, newVersion, root.child('README').path)
-
- project.updateVersion(newVersion)
-
-
-
-
-class Project(object):
- """
- A representation of a project that has a version.
-
- @ivar directory: A L{twisted.python.filepath.FilePath} pointing to the base
- directory of a Twisted-style Python package. The package should contain
- a C{_version.py} file and a C{topfiles} directory that contains a
- C{README} file.
- """
-
- def __init__(self, directory):
- self.directory = directory
-
-
- def __repr__(self):
- return '%s(%r)' % (
- self.__class__.__name__, self.directory)
-
-
- def getVersion(self):
- """
- @return: A L{Version} specifying the version number of the project
- based on live python modules.
- """
- namespace = {}
- execfile(self.directory.child("_version.py").path, namespace)
- return namespace["version"]
-
-
- def updateVersion(self, version):
- """
- Replace the existing version numbers in _version.py and README files
- with the specified version.
- """
- oldVersion = self.getVersion()
- replaceProjectVersion(self.directory.child("_version.py").path,
- version)
- _changeVersionInFile(
- oldVersion, version,
- self.directory.child("topfiles").child("README").path)
-
-
-
-def findTwistedProjects(baseDirectory):
- """
- Find all Twisted-style projects beneath a base directory.
-
- @param baseDirectory: A L{twisted.python.filepath.FilePath} to look inside.
- @return: A list of L{Project}.
- """
- projects = []
- for filePath in baseDirectory.walk():
- if filePath.basename() == 'topfiles':
- projectDirectory = filePath.parent()
- projects.append(Project(projectDirectory))
- return projects
-
-
-
-def updateTwistedVersionInformation(baseDirectory, now):
- """
- Update the version information for Twisted and all subprojects to the
- date-based version number.
-
- @param baseDirectory: Where to look for Twisted. If None, the function
- infers the information from C{twisted.__file__}.
- @param now: The current date (as L{datetime.date}). If None, it defaults
- to today.
- """
- for project in findTwistedProjects(baseDirectory):
- project.updateVersion(getNextVersion(project.getVersion(), now=now))
-
-
-def generateVersionFileData(version):
- """
- Generate the data to be placed into a _version.py file.
-
- @param version: A version object.
- """
- if version.prerelease is not None:
- prerelease = ", prerelease=%r" % (version.prerelease,)
- else:
- prerelease = ""
- data = '''\
-# This is an auto-generated file. Do not edit it.
-from twisted.python import versions
-version = versions.Version(%r, %s, %s, %s%s)
-''' % (version.package, version.major, version.minor, version.micro, prerelease)
- return data
-
-
-def replaceProjectVersion(filename, newversion):
- """
- Write version specification code into the given filename, which
- sets the version to the given version number.
-
- @param filename: A filename which is most likely a "_version.py"
- under some Twisted project.
- @param newversion: A version object.
- """
- # XXX - this should be moved to Project and renamed to writeVersionFile.
- # jml, 2007-11-15.
- f = open(filename, 'w')
- f.write(generateVersionFileData(newversion))
- f.close()
-
-
-
-def replaceInFile(filename, oldToNew):
- """
- I replace the text `oldstr' with `newstr' in `filename' using science.
- """
- os.rename(filename, filename+'.bak')
- f = open(filename+'.bak')
- d = f.read()
- f.close()
- for k,v in oldToNew.items():
- d = d.replace(k, v)
- f = open(filename + '.new', 'w')
- f.write(d)
- f.close()
- os.rename(filename+'.new', filename)
- os.unlink(filename+'.bak')
-
-
-
-class NoDocumentsFound(Exception):
- """
- Raised when no input documents are found.
- """
-
-
-
-class LoreBuilderMixin(object):
- """
- Base class for builders which invoke lore.
- """
- def lore(self, arguments):
- """
- Run lore with the given arguments.
-
- @param arguments: A C{list} of C{str} giving command line arguments to
- lore which should be used.
- """
- options = lore.Options()
- options.parseOptions(["--null"] + arguments)
- lore.runGivenOptions(options)
-
-
-
-class DocBuilder(LoreBuilderMixin):
- """
- Generate HTML documentation for projects.
- """
-
- def build(self, version, resourceDir, docDir, template, apiBaseURL=None,
- deleteInput=False):
- """
- Build the documentation in C{docDir} with Lore.
-
- Input files ending in .xhtml will be considered. Output will written as
- .html files.
-
- @param version: the version of the documentation to pass to lore.
- @type version: C{str}
-
- @param resourceDir: The directory which contains the toplevel index and
- stylesheet file for this section of documentation.
- @type resourceDir: L{twisted.python.filepath.FilePath}
-
- @param docDir: The directory of the documentation.
- @type docDir: L{twisted.python.filepath.FilePath}
-
- @param template: The template used to generate the documentation.
- @type template: L{twisted.python.filepath.FilePath}
-
- @type apiBaseURL: C{str} or C{NoneType}
- @param apiBaseURL: A format string which will be interpolated with the
- fully-qualified Python name for each API link. For example, to
- generate the Twisted 8.0.0 documentation, pass
- C{"http://twistedmatrix.com/documents/8.0.0/api/%s.html"}.
-
- @param deleteInput: If True, the input documents will be deleted after
- their output is generated.
- @type deleteInput: C{bool}
-
- @raise NoDocumentsFound: When there are no .xhtml files in the given
- C{docDir}.
- """
- linkrel = self.getLinkrel(resourceDir, docDir)
- inputFiles = docDir.globChildren("*.xhtml")
- filenames = [x.path for x in inputFiles]
- if not filenames:
- raise NoDocumentsFound("No input documents found in %s" % (docDir,))
- if apiBaseURL is not None:
- arguments = ["--config", "baseurl=" + apiBaseURL]
- else:
- arguments = []
- arguments.extend(["--config", "template=%s" % (template.path,),
- "--config", "ext=.html",
- "--config", "version=%s" % (version,),
- "--linkrel", linkrel] + filenames)
- self.lore(arguments)
- if deleteInput:
- for inputFile in inputFiles:
- inputFile.remove()
-
-
- def getLinkrel(self, resourceDir, docDir):
- """
- Calculate a value appropriate for Lore's --linkrel option.
-
- Lore's --linkrel option defines how to 'find' documents that are
- linked to from TEMPLATE files (NOT document bodies). That is, it's a
- prefix for links ('a' and 'link') in the template.
-
- @param resourceDir: The directory which contains the toplevel index and
- stylesheet file for this section of documentation.
- @type resourceDir: L{twisted.python.filepath.FilePath}
-
- @param docDir: The directory containing documents that must link to
- C{resourceDir}.
- @type docDir: L{twisted.python.filepath.FilePath}
- """
- if resourceDir != docDir:
- return '/'.join(filePathDelta(docDir, resourceDir)) + "/"
- else:
- return ""
-
-
-
-class ManBuilder(LoreBuilderMixin):
- """
- Generate man pages of the different existing scripts.
- """
-
- def build(self, manDir):
- """
- Generate Lore input files from the man pages in C{manDir}.
-
- Input files ending in .1 will be considered. Output will written as
- -man.xhtml files.
-
- @param manDir: The directory of the man pages.
- @type manDir: L{twisted.python.filepath.FilePath}
-
- @raise NoDocumentsFound: When there are no .1 files in the given
- C{manDir}.
- """
- inputFiles = manDir.globChildren("*.1")
- filenames = [x.path for x in inputFiles]
- if not filenames:
- raise NoDocumentsFound("No manual pages found in %s" % (manDir,))
- arguments = ["--input", "man",
- "--output", "lore",
- "--config", "ext=-man.xhtml"] + filenames
- self.lore(arguments)
-
-
-
-class APIBuilder(object):
- """
- Generate API documentation from source files using
- U{pydoctor<http://codespeak.net/~mwh/pydoctor/>}. This requires
- pydoctor to be installed and usable (which means you won't be able to
- use it with Python 2.3).
- """
- def build(self, projectName, projectURL, sourceURL, packagePath,
- outputPath):
- """
- Call pydoctor's entry point with options which will generate HTML
- documentation for the specified package's API.
-
- @type projectName: C{str}
- @param projectName: The name of the package for which to generate
- documentation.
-
- @type projectURL: C{str}
- @param projectURL: The location (probably an HTTP URL) of the project
- on the web.
-
- @type sourceURL: C{str}
- @param sourceURL: The location (probably an HTTP URL) of the root of
- the source browser for the project.
-
- @type packagePath: L{FilePath}
- @param packagePath: The path to the top-level of the package named by
- C{projectName}.
-
- @type outputPath: L{FilePath}
- @param outputPath: An existing directory to which the generated API
- documentation will be written.
- """
- from pydoctor.driver import main
- main(
- ["--project-name", projectName,
- "--project-url", projectURL,
- "--system-class", "pydoctor.twistedmodel.TwistedSystem",
- "--project-base-dir", packagePath.parent().path,
- "--html-viewsource-base", sourceURL,
- "--add-package", packagePath.path,
- "--html-output", outputPath.path,
- "--html-write-function-pages", "--quiet", "--make-html"])
-
-
-
-class BookBuilder(LoreBuilderMixin):
- """
- Generate the LaTeX and PDF documentation.
-
- The book is built by assembling a number of LaTeX documents. Only the
- overall document which describes how to assemble the documents is stored
- in LaTeX in the source. The rest of the documentation is generated from
- Lore input files. These are primarily XHTML files (of the particular
- Lore subset), but man pages are stored in GROFF format. BookBuilder
- expects all of its input to be Lore XHTML format, so L{ManBuilder}
- should be invoked first if the man pages are to be included in the
- result (this is determined by the book LaTeX definition file).
- Therefore, a sample usage of BookBuilder may look something like this::
-
- man = ManBuilder()
- man.build(FilePath("doc/core/man"))
- book = BookBuilder()
- book.build(
- FilePath('doc/core/howto'),
- [FilePath('doc/core/howto'), FilePath('doc/core/howto/tutorial'),
- FilePath('doc/core/man'), FilePath('doc/core/specifications')],
- FilePath('doc/core/howto/book.tex'), FilePath('/tmp/book.pdf'))
- """
- def run(self, command):
- """
- Execute a command in a child process and return the output.
-
- @type command: C{str}
- @param command: The shell command to run.
-
- @raise CommandFailed: If the child process exits with an error.
- """
- return runCommand(command)
-
-
- def buildTeX(self, howtoDir):
- """
- Build LaTeX files for lore input files in the given directory.
-
- Input files ending in .xhtml will be considered. Output will written as
- .tex files.
-
- @type howtoDir: L{FilePath}
- @param howtoDir: A directory containing lore input files.
-
- @raise ValueError: If C{howtoDir} does not exist.
- """
- if not howtoDir.exists():
- raise ValueError("%r does not exist." % (howtoDir.path,))
- self.lore(
- ["--output", "latex",
- "--config", "section"] +
- [child.path for child in howtoDir.globChildren("*.xhtml")])
-
-
- def buildPDF(self, bookPath, inputDirectory, outputPath):
- """
- Build a PDF from the given a LaTeX book document.
-
- @type bookPath: L{FilePath}
- @param bookPath: The location of a LaTeX document defining a book.
-
- @type inputDirectory: L{FilePath}
- @param inputDirectory: The directory which the inputs of the book are
- relative to.
-
- @type outputPath: L{FilePath}
- @param outputPath: The location to which to write the resulting book.
- """
- if not bookPath.basename().endswith(".tex"):
- raise ValueError("Book filename must end with .tex")
-
- workPath = FilePath(mkdtemp())
- try:
- startDir = os.getcwd()
- try:
- os.chdir(inputDirectory.path)
-
- texToDVI = [
- "latex", "-interaction=nonstopmode",
- "-output-directory=" + workPath.path,
- bookPath.path]
-
- # What I tell you three times is true!
- # The first two invocations of latex on the book file allows it
- # correctly create page numbers for in-text references. Why this is
- # the case, I could not tell you. -exarkun
- for i in range(3):
- self.run(texToDVI)
-
- bookBaseWithoutExtension = bookPath.basename()[:-4]
- dviPath = workPath.child(bookBaseWithoutExtension + ".dvi")
- psPath = workPath.child(bookBaseWithoutExtension + ".ps")
- pdfPath = workPath.child(bookBaseWithoutExtension + ".pdf")
- self.run([
- "dvips", "-o", psPath.path, "-t", "letter", "-Ppdf",
- dviPath.path])
- self.run(["ps2pdf13", psPath.path, pdfPath.path])
- pdfPath.moveTo(outputPath)
- workPath.remove()
- finally:
- os.chdir(startDir)
- except:
- workPath.moveTo(bookPath.parent().child(workPath.basename()))
- raise
-
-
- def build(self, baseDirectory, inputDirectories, bookPath, outputPath):
- """
- Build a PDF book from the given TeX book definition and directories
- containing lore inputs.
-
- @type baseDirectory: L{FilePath}
- @param baseDirectory: The directory which the inputs of the book are
- relative to.
-
- @type inputDirectories: C{list} of L{FilePath}
- @param inputDirectories: The paths which contain lore inputs to be
- converted to LaTeX.
-
- @type bookPath: L{FilePath}
- @param bookPath: The location of a LaTeX document defining a book.
-
- @type outputPath: L{FilePath}
- @param outputPath: The location to which to write the resulting book.
- """
- for inputDir in inputDirectories:
- self.buildTeX(inputDir)
- self.buildPDF(bookPath, baseDirectory, outputPath)
- for inputDirectory in inputDirectories:
- for child in inputDirectory.children():
- if child.splitext()[1] == ".tex" and child != bookPath:
- child.remove()
-
-
-
-class NewsBuilder(object):
- """
- Generate the new section of a NEWS file.
-
- The C{_FEATURE}, C{_BUGFIX}, C{_DOC}, C{_REMOVAL}, and C{_MISC}
- attributes of this class are symbolic names for the news entry types
- which are supported. Conveniently, they each also take on the value of
- the file name extension which indicates a news entry of that type.
-
- @cvar _headings: A C{dict} mapping one of the news entry types to the
- heading to write out for that type of news entry.
-
- @cvar _NO_CHANGES: A C{str} giving the text which appears when there are
- no significant changes in a release.
-
- @cvar _TICKET_HINT: A C{str} giving the text which appears at the top of
- each news file and which should be kept at the top, not shifted down
- with all the other content. Put another way, this is the text after
- which the new news text is inserted.
- """
-
- _FEATURE = ".feature"
- _BUGFIX = ".bugfix"
- _DOC = ".doc"
- _REMOVAL = ".removal"
- _MISC = ".misc"
-
- _headings = {
- _FEATURE: "Features",
- _BUGFIX: "Bugfixes",
- _DOC: "Improved Documentation",
- _REMOVAL: "Deprecations and Removals",
- _MISC: "Other",
- }
-
- _NO_CHANGES = "No significant changes have been made for this release.\n"
-
- _TICKET_HINT = (
- 'Ticket numbers in this file can be looked up by visiting\n'
- 'http://twistedmatrix.com/trac/ticket/<number>\n'
- '\n')
-
- def _today(self):
- """
- Return today's date as a string in YYYY-MM-DD format.
- """
- return date.today().strftime('%Y-%m-%d')
-
-
- def _findChanges(self, path, ticketType):
- """
- Load all the feature ticket summaries.
-
- @param path: A L{FilePath} the direct children of which to search
- for news entries.
-
- @param ticketType: The type of news entries to search for. One of
- L{NewsBuilder._FEATURE}, L{NewsBuilder._BUGFIX},
- L{NewsBuilder._REMOVAL}, or L{NewsBuilder._MISC}.
-
- @return: A C{list} of two-tuples. The first element is the ticket
- number as an C{int}. The second element of each tuple is the
- description of the feature.
- """
- results = []
- for child in path.children():
- base, ext = os.path.splitext(child.basename())
- if ext == ticketType:
- results.append((
- int(base),
- ' '.join(child.getContent().splitlines())))
- results.sort()
- return results
-
-
- def _formatHeader(self, header):
- """
- Format a header for a NEWS file.
-
- A header is a title with '=' signs underlining it.
-
- @param header: The header string to format.
- @type header: C{str}
- @return: A C{str} containing C{header}.
- """
- return header + '\n' + '=' * len(header) + '\n\n'
-
-
- def _writeHeader(self, fileObj, header):
- """
- Write a version header to the given file.
-
- @param fileObj: A file-like object to which to write the header.
- @param header: The header to write to the file.
- @type header: C{str}
- """
- fileObj.write(self._formatHeader(header))
-
-
- def _writeSection(self, fileObj, header, tickets):
- """
- Write out one section (features, bug fixes, etc) to the given file.
-
- @param fileObj: A file-like object to which to write the news section.
-
- @param header: The header for the section to write.
- @type header: C{str}
-
- @param tickets: A C{list} of ticket information of the sort returned
- by L{NewsBuilder._findChanges}.
- """
- if not tickets:
- return
-
- reverse = {}
- for (ticket, description) in tickets:
- reverse.setdefault(description, []).append(ticket)
- for description in reverse:
- reverse[description].sort()
- reverse = reverse.items()
- reverse.sort(key=lambda (descr, tickets): tickets[0])
-
- fileObj.write(header + '\n' + '-' * len(header) + '\n')
- for (description, relatedTickets) in reverse:
- ticketList = ', '.join([
- '#' + str(ticket) for ticket in relatedTickets])
- entry = ' - %s (%s)' % (description, ticketList)
- entry = textwrap.fill(entry, subsequent_indent=' ')
- fileObj.write(entry + '\n')
- fileObj.write('\n')
-
-
- def _writeMisc(self, fileObj, header, tickets):
- """
- Write out a miscellaneous-changes section to the given file.
-
- @param fileObj: A file-like object to which to write the news section.
-
- @param header: The header for the section to write.
- @type header: C{str}
-
- @param tickets: A C{list} of ticket information of the sort returned
- by L{NewsBuilder._findChanges}.
- """
- if not tickets:
- return
-
- fileObj.write(header + '\n' + '-' * len(header) + '\n')
- formattedTickets = []
- for (ticket, ignored) in tickets:
- formattedTickets.append('#' + str(ticket))
- entry = ' - ' + ', '.join(formattedTickets)
- entry = textwrap.fill(entry, subsequent_indent=' ')
- fileObj.write(entry + '\n\n')
-
-
- def build(self, path, output, header):
- """
- Load all of the change information from the given directory and write
- it out to the given output file.
-
- @param path: A directory (probably a I{topfiles} directory) containing
- change information in the form of <ticket>.<change type> files.
- @type path: L{FilePath}
-
- @param output: The NEWS file to which the results will be prepended.
- @type output: L{FilePath}
-
- @param header: The top-level header to use when writing the news.
- @type header: L{str}
- """
- changes = []
- for part in (self._FEATURE, self._BUGFIX, self._DOC, self._REMOVAL):
- tickets = self._findChanges(path, part)
- if tickets:
- changes.append((part, tickets))
- misc = self._findChanges(path, self._MISC)
-
- oldNews = output.getContent()
- newNews = output.sibling('NEWS.new').open('w')
- if oldNews.startswith(self._TICKET_HINT):
- newNews.write(self._TICKET_HINT)
- oldNews = oldNews[len(self._TICKET_HINT):]
-
- self._writeHeader(newNews, header)
- if changes:
- for (part, tickets) in changes:
- self._writeSection(newNews, self._headings.get(part), tickets)
- else:
- newNews.write(self._NO_CHANGES)
- newNews.write('\n')
- self._writeMisc(newNews, self._headings.get(self._MISC), misc)
- newNews.write('\n')
- newNews.write(oldNews)
- newNews.close()
- output.sibling('NEWS.new').moveTo(output)
-
-
- def _getNewsName(self, project):
- """
- Return the name of C{project} that should appear in NEWS.
-
- @param project: A L{Project}
- @return: The name of C{project}.
- """
- name = project.directory.basename().title()
- if name == 'Twisted':
- name = 'Core'
- return name
-
-
- def _iterProjects(self, baseDirectory):
- """
- Iterate through the Twisted projects in C{baseDirectory}, yielding
- everything we need to know to build news for them.
-
- Yields C{topfiles}, C{news}, C{name}, C{version} for each sub-project
- in reverse-alphabetical order. C{topfile} is the L{FilePath} for the
- topfiles directory, C{news} is the L{FilePath} for the NEWS file,
- C{name} is the nice name of the project (as should appear in the NEWS
- file), C{version} is the current version string for that project.
-
- @param baseDirectory: A L{FilePath} representing the root directory
- beneath which to find Twisted projects for which to generate
- news (see L{findTwistedProjects}).
- @type baseDirectory: L{FilePath}
- """
- # Get all the subprojects to generate news for
- projects = findTwistedProjects(baseDirectory)
- # And order them alphabetically for ease of reading
- projects.sort(key=lambda proj: proj.directory.path)
- # And generate them backwards since we write news by prepending to
- # files.
- projects.reverse()
-
- for aggregateNews in [False, True]:
- for project in projects:
- topfiles = project.directory.child("topfiles")
- if aggregateNews:
- news = baseDirectory.child("NEWS")
- else:
- news = topfiles.child("NEWS")
- name = self._getNewsName(project)
- version = project.getVersion()
- yield topfiles, news, name, version
-
-
- def buildAll(self, baseDirectory):
- """
- Find all of the Twisted subprojects beneath C{baseDirectory} and update
- their news files from the ticket change description files in their
- I{topfiles} directories and update the news file in C{baseDirectory}
- with all of the news.
-
- @param baseDirectory: A L{FilePath} representing the root directory
- beneath which to find Twisted projects for which to generate
- news (see L{findTwistedProjects}).
- """
- today = self._today()
- for topfiles, news, name, version in self._iterProjects(baseDirectory):
- self.build(
- topfiles, news,
- "Twisted %s %s (%s)" % (name, version.base(), today))
-
-
- def _changeNewsVersion(self, news, name, oldVersion, newVersion, today):
- """
- Change all references to the current version number in a NEWS file to
- refer to C{newVersion} instead.
-
- @param news: The NEWS file to change.
- @type news: L{FilePath}
- @param name: The name of the project to change.
- @type name: C{str}
- @param oldVersion: The old version of the project.
- @type oldVersion: L{Version}
- @param newVersion: The new version of the project.
- @type newVersion: L{Version}
- @param today: A YYYY-MM-DD string representing today's date.
- @type today: C{str}
- """
- newHeader = self._formatHeader(
- "Twisted %s %s (%s)" % (name, newVersion.base(), today))
- expectedHeaderRegex = re.compile(
- r"Twisted %s %s \(\d{4}-\d\d-\d\d\)\n=+\n\n" % (
- re.escape(name), re.escape(oldVersion.base())))
- oldNews = news.getContent()
- match = expectedHeaderRegex.search(oldNews)
- if match:
- oldHeader = match.group()
- replaceInFile(news.path, {oldHeader: newHeader})
-
-
- def main(self, args):
- """
- Build all news files.
-
- @param args: The command line arguments to process. This must contain
- one string, the path to the base of the Twisted checkout for which
- to build the news.
- @type args: C{list} of C{str}
- """
- if len(args) != 1:
- sys.exit("Must specify one argument: the path to the Twisted checkout")
- self.buildAll(FilePath(args[0]))
-
-
-
-def filePathDelta(origin, destination):
- """
- Return a list of strings that represent C{destination} as a path relative
- to C{origin}.
-
- It is assumed that both paths represent directories, not files. That is to
- say, the delta of L{twisted.python.filepath.FilePath} /foo/bar to
- L{twisted.python.filepath.FilePath} /foo/baz will be C{../baz},
- not C{baz}.
-
- @type origin: L{twisted.python.filepath.FilePath}
- @param origin: The origin of the relative path.
-
- @type destination: L{twisted.python.filepath.FilePath}
- @param destination: The destination of the relative path.
- """
- commonItems = 0
- path1 = origin.path.split(os.sep)
- path2 = destination.path.split(os.sep)
- for elem1, elem2 in zip(path1, path2):
- if elem1 == elem2:
- commonItems += 1
- else:
- break
- path = [".."] * (len(path1) - commonItems)
- return path + path2[commonItems:]
-
-
-
-class DistributionBuilder(object):
- """
- A builder of Twisted distributions.
-
- This knows how to build tarballs for Twisted and all of its subprojects.
- """
- from twisted.python.dist import twisted_subprojects as subprojects
-
- def __init__(self, rootDirectory, outputDirectory, templatePath=None,
- apiBaseURL=None):
- """
- Create a distribution builder.
-
- @param rootDirectory: root of a Twisted export which will populate
- subsequent tarballs.
- @type rootDirectory: L{FilePath}.
-
- @param outputDirectory: The directory in which to create the tarballs.
- @type outputDirectory: L{FilePath}
-
- @param templatePath: Path to the template file that is used for the
- howto documentation.
- @type templatePath: L{FilePath}
-
- @type apiBaseURL: C{str} or C{NoneType}
- @param apiBaseURL: A format string which will be interpolated with the
- fully-qualified Python name for each API link. For example, to
- generate the Twisted 8.0.0 documentation, pass
- C{"http://twistedmatrix.com/documents/8.0.0/api/%s.html"}.
- """
- self.rootDirectory = rootDirectory
- self.outputDirectory = outputDirectory
- self.templatePath = templatePath
- self.apiBaseURL = apiBaseURL
- self.manBuilder = ManBuilder()
- self.docBuilder = DocBuilder()
-
-
- def _buildDocInDir(self, path, version, howtoPath):
- """
- Generate documentation in the given path, building man pages first if
- necessary and swallowing errors (so that directories without lore
- documentation in them are ignored).
-
- @param path: The path containing documentation to build.
- @type path: L{FilePath}
- @param version: The version of the project to include in all generated
- pages.
- @type version: C{str}
- @param howtoPath: The "resource path" as L{DocBuilder} describes it.
- @type howtoPath: L{FilePath}
- """
- if self.templatePath is None:
- self.templatePath = self.rootDirectory.descendant(
- ["doc", "core", "howto", "template.tpl"])
- if path.basename() == "man":
- self.manBuilder.build(path)
- if path.isdir():
- try:
- self.docBuilder.build(version, howtoPath, path,
- self.templatePath, self.apiBaseURL, True)
- except NoDocumentsFound:
- pass
-
-
- def buildTwisted(self, version):
- """
- Build the main Twisted distribution in C{Twisted-<version>.tar.bz2}.
-
- bin/admin is excluded.
-
- @type version: C{str}
- @param version: The version of Twisted to build.
-
- @return: The tarball file.
- @rtype: L{FilePath}.
- """
- releaseName = "Twisted-%s" % (version,)
- buildPath = lambda *args: '/'.join((releaseName,) + args)
-
- outputFile = self.outputDirectory.child(releaseName + ".tar.bz2")
- tarball = tarfile.TarFile.open(outputFile.path, 'w:bz2')
-
- docPath = self.rootDirectory.child("doc")
-
- # Generate docs!
- if docPath.isdir():
- for subProjectDir in docPath.children():
- if subProjectDir.isdir():
- for child in subProjectDir.walk():
- self._buildDocInDir(child, version,
- subProjectDir.child("howto"))
-
- for binthing in self.rootDirectory.child("bin").children():
- # bin/admin should not be included.
- if binthing.basename() != "admin":
- tarball.add(binthing.path,
- buildPath("bin", binthing.basename()))
-
- for submodule in self.rootDirectory.child("twisted").children():
- if submodule.basename() == "plugins":
- for plugin in submodule.children():
- tarball.add(plugin.path, buildPath("twisted", "plugins",
- plugin.basename()))
- else:
- tarball.add(submodule.path, buildPath("twisted",
- submodule.basename()))
-
- for docDir in self.rootDirectory.child("doc").children():
- if docDir.basename() != "historic":
- tarball.add(docDir.path, buildPath("doc", docDir.basename()))
-
- for toplevel in self.rootDirectory.children():
- if not toplevel.isdir():
- tarball.add(toplevel.path, buildPath(toplevel.basename()))
-
- tarball.close()
-
- return outputFile
-
-
- def buildCore(self, version):
- """
- Build a core distribution in C{TwistedCore-<version>.tar.bz2}.
-
- This is very similar to L{buildSubProject}, but core tarballs and the
- input are laid out slightly differently.
-
- - scripts are in the top level of the C{bin} directory.
- - code is included directly from the C{twisted} directory, excluding
- subprojects.
- - all plugins except the subproject plugins are included.
-
- @type version: C{str}
- @param version: The version of Twisted to build.
-
- @return: The tarball file.
- @rtype: L{FilePath}.
- """
- releaseName = "TwistedCore-%s" % (version,)
- outputFile = self.outputDirectory.child(releaseName + ".tar.bz2")
- buildPath = lambda *args: '/'.join((releaseName,) + args)
- tarball = self._createBasicSubprojectTarball(
- "core", version, outputFile)
-
- # Include the bin directory for the subproject.
- for path in self.rootDirectory.child("bin").children():
- if not path.isdir():
- tarball.add(path.path, buildPath("bin", path.basename()))
-
- # Include all files within twisted/ that aren't part of a subproject.
- for path in self.rootDirectory.child("twisted").children():
- if path.basename() == "plugins":
- for plugin in path.children():
- for subproject in self.subprojects:
- if plugin.basename() == "twisted_%s.py" % (subproject,):
- break
- else:
- tarball.add(plugin.path,
- buildPath("twisted", "plugins",
- plugin.basename()))
- elif not path.basename() in self.subprojects + ["topfiles"]:
- tarball.add(path.path, buildPath("twisted", path.basename()))
-
- tarball.add(self.rootDirectory.child("twisted").child("topfiles").path,
- releaseName)
- tarball.close()
-
- return outputFile
-
-
- def buildSubProject(self, projectName, version):
- """
- Build a subproject distribution in
- C{Twisted<Projectname>-<version>.tar.bz2}.
-
- @type projectName: C{str}
- @param projectName: The lowercase name of the subproject to build.
- @type version: C{str}
- @param version: The version of Twisted to build.
-
- @return: The tarball file.
- @rtype: L{FilePath}.
- """
- releaseName = "Twisted%s-%s" % (projectName.capitalize(), version)
- outputFile = self.outputDirectory.child(releaseName + ".tar.bz2")
- buildPath = lambda *args: '/'.join((releaseName,) + args)
- subProjectDir = self.rootDirectory.child("twisted").child(projectName)
-
- tarball = self._createBasicSubprojectTarball(projectName, version,
- outputFile)
-
- tarball.add(subProjectDir.child("topfiles").path, releaseName)
-
- # Include all files in the subproject package except for topfiles.
- for child in subProjectDir.children():
- name = child.basename()
- if name != "topfiles":
- tarball.add(
- child.path,
- buildPath("twisted", projectName, name))
-
- pluginsDir = self.rootDirectory.child("twisted").child("plugins")
- # Include the plugin for the subproject.
- pluginFileName = "twisted_%s.py" % (projectName,)
- pluginFile = pluginsDir.child(pluginFileName)
- if pluginFile.exists():
- tarball.add(pluginFile.path,
- buildPath("twisted", "plugins", pluginFileName))
-
- # Include the bin directory for the subproject.
- binPath = self.rootDirectory.child("bin").child(projectName)
- if binPath.isdir():
- tarball.add(binPath.path, buildPath("bin"))
- tarball.close()
-
- return outputFile
-
-
- def _createBasicSubprojectTarball(self, projectName, version, outputFile):
- """
- Helper method to create and fill a tarball with things common between
- subprojects and core.
-
- @param projectName: The subproject's name.
- @type projectName: C{str}
- @param version: The version of the release.
- @type version: C{str}
- @param outputFile: The location of the tar file to create.
- @type outputFile: L{FilePath}
- """
- releaseName = "Twisted%s-%s" % (projectName.capitalize(), version)
- buildPath = lambda *args: '/'.join((releaseName,) + args)
-
- tarball = tarfile.TarFile.open(outputFile.path, 'w:bz2')
-
- tarball.add(self.rootDirectory.child("LICENSE").path,
- buildPath("LICENSE"))
-
- docPath = self.rootDirectory.child("doc").child(projectName)
-
- if docPath.isdir():
- for child in docPath.walk():
- self._buildDocInDir(child, version, docPath.child("howto"))
- tarball.add(docPath.path, buildPath("doc"))
-
- return tarball
-
-
-
-class UncleanWorkingDirectory(Exception):
- """
- Raised when the working directory of an SVN checkout is unclean.
- """
-
-
-
-class NotWorkingDirectory(Exception):
- """
- Raised when a directory does not appear to be an SVN working directory.
- """
-
-
-
-def buildAllTarballs(checkout, destination, templatePath=None):
- """
- Build complete tarballs (including documentation) for Twisted and all
- subprojects.
-
- This should be called after the version numbers have been updated and
- NEWS files created.
-
- @type checkout: L{FilePath}
- @param checkout: The SVN working copy from which a pristine source tree
- will be exported.
- @type destination: L{FilePath}
- @param destination: The directory in which tarballs will be placed.
- @type templatePath: L{FilePath}
- @param templatePath: Location of the template file that is used for the
- howto documentation.
-
- @raise UncleanWorkingDirectory: If there are modifications to the
- working directory of C{checkout}.
- @raise NotWorkingDirectory: If the C{checkout} path is not an SVN checkout.
- """
- if not checkout.child(".svn").exists():
- raise NotWorkingDirectory(
- "%s does not appear to be an SVN working directory."
- % (checkout.path,))
- if runCommand(["svn", "st", checkout.path]).strip():
- raise UncleanWorkingDirectory(
- "There are local modifications to the SVN checkout in %s."
- % (checkout.path,))
-
- workPath = FilePath(mkdtemp())
- export = workPath.child("export")
- runCommand(["svn", "export", checkout.path, export.path])
- twistedPath = export.child("twisted")
- version = Project(twistedPath).getVersion()
- versionString = version.base()
-
- apiBaseURL = "http://twistedmatrix.com/documents/%s/api/%%s.html" % (
- versionString)
- if not destination.exists():
- destination.createDirectory()
- db = DistributionBuilder(export, destination, templatePath=templatePath,
- apiBaseURL=apiBaseURL)
-
- db.buildCore(versionString)
- for subproject in twisted_subprojects:
- if twistedPath.child(subproject).exists():
- db.buildSubProject(subproject, versionString)
-
- db.buildTwisted(versionString)
- workPath.remove()
-
-
-class ChangeVersionsScript(object):
- """
- A thing for changing version numbers. See L{main}.
- """
- changeAllProjectVersions = staticmethod(changeAllProjectVersions)
-
- def main(self, args):
- """
- Given a list of command-line arguments, change all the Twisted versions
- in the current directory.
-
- @type args: list of str
- @param args: List of command line arguments. This should only
- contain the version number.
- """
- version_format = (
- "Version should be in a form kind of like '1.2.3[pre4]'")
- if len(args) != 1:
- sys.exit("Must specify exactly one argument to change-versions")
- version = args[0]
- try:
- major, minor, micro_and_pre = version.split(".")
- except ValueError:
- raise SystemExit(version_format)
- if "pre" in micro_and_pre:
- micro, pre = micro_and_pre.split("pre")
- else:
- micro = micro_and_pre
- pre = None
- try:
- major = int(major)
- minor = int(minor)
- micro = int(micro)
- if pre is not None:
- pre = int(pre)
- except ValueError:
- raise SystemExit(version_format)
- version_template = Version("Whatever",
- major, minor, micro, prerelease=pre)
- self.changeAllProjectVersions(FilePath("."), version_template)
-
-
-
-class BuildTarballsScript(object):
- """
- A thing for building release tarballs. See L{main}.
- """
- buildAllTarballs = staticmethod(buildAllTarballs)
-
- def main(self, args):
- """
- Build all release tarballs.
-
- @type args: list of C{str}
- @param args: The command line arguments to process. This must contain
- at least two strings: the checkout directory and the destination
- directory. An optional third string can be specified for the website
- template file, used for building the howto documentation. If this
- string isn't specified, the default template included in twisted
- will be used.
- """
- if len(args) < 2 or len(args) > 3:
- sys.exit("Must specify at least two arguments: "
- "Twisted checkout and destination path. The optional third "
- "argument is the website template path.")
- if len(args) == 2:
- self.buildAllTarballs(FilePath(args[0]), FilePath(args[1]))
- elif len(args) == 3:
- self.buildAllTarballs(FilePath(args[0]), FilePath(args[1]),
- FilePath(args[2]))
-
-
-
-class BuildAPIDocsScript(object):
- """
- A thing for building API documentation. See L{main}.
- """
-
- def buildAPIDocs(self, projectRoot, output):
- """
- Build the API documentation of Twisted, with our project policy.
-
- @param projectRoot: A L{FilePath} representing the root of the Twisted
- checkout.
- @param output: A L{FilePath} pointing to the desired output directory.
- """
- version = Project(projectRoot.child("twisted")).getVersion()
- versionString = version.base()
- sourceURL = ("http://twistedmatrix.com/trac/browser/tags/releases/"
- "twisted-%s" % (versionString,))
- apiBuilder = APIBuilder()
- apiBuilder.build(
- "Twisted",
- "http://twistedmatrix.com/",
- sourceURL,
- projectRoot.child("twisted"),
- output)
-
-
- def main(self, args):
- """
- Build API documentation.
-
- @type args: list of str
- @param args: The command line arguments to process. This must contain
- two strings: the path to the root of the Twisted checkout, and a
- path to an output directory.
- """
- if len(args) != 2:
- sys.exit("Must specify two arguments: "
- "Twisted checkout and destination path")
- self.buildAPIDocs(FilePath(args[0]), FilePath(args[1]))