Checkin my holiday work:

- Add "changes" builder to quickly get an overview over all "versionadded/changed/deprecated" directives for a certain version
- Cross-reference keywords
- Fix some problems in the webapp and the latex writer
This commit is contained in:
Georg Brandl
2007-12-29 10:58:10 +00:00
parent e77b466281
commit 350143494d
20 changed files with 302 additions and 180 deletions

8
TODO
View File

@@ -4,18 +4,18 @@ Global TODO
Sphinx Sphinx
****** ******
- section numbers - HTML section numbers?
- split the general index - split the general index?
- add OpenSearch - add OpenSearch
- "seealso" links to external examples, see http://svn.python.org/projects/sandbox/trunk/seealso/ and http://effbot.org/zone/idea-seealso.htm - "seealso" links to external examples, see http://svn.python.org/projects/sandbox/trunk/seealso/ and http://effbot.org/zone/idea-seealso.htm
- write a "printable" builder (export to latex, most probably)
- "often used" combo box in sidebar - "often used" combo box in sidebar
- link to keywords
- source file cross-references? - source file cross-references?
Web App Web App
******* *******
- fix /download
- discuss and debug comments system - discuss and debug comments system
- prepare for databases other than sqlite for comments - prepare for databases other than sqlite for comments
- add search via Xapian or Nucular (Python indexer - nucular.sf.net) - add search via Xapian or Nucular (Python indexer - nucular.sf.net)

View File

@@ -35,6 +35,7 @@ options: -b <builder> -- builder to use (one of %s)
-D <setting=value> -- override a setting in sourcedir/conf.py -D <setting=value> -- override a setting in sourcedir/conf.py
-N -- do not do colored output -N -- do not do colored output
-q -- no output on stdout, just warnings on stderr -q -- no output on stdout, just warnings on stderr
-P -- run Pdb on exception
modi: modi:
* without -a and without filenames, write new and changed files. * without -a and without filenames, write new and changed files.
* with -a, write all files. * with -a, write all files.
@@ -43,7 +44,7 @@ modi:
def main(argv): def main(argv):
try: try:
opts, args = getopt.getopt(argv[1:], 'ab:d:O:D:NEq') opts, args = getopt.getopt(argv[1:], 'ab:d:O:D:NEqP')
srcdirname = path.abspath(args[0]) srcdirname = path.abspath(args[0])
if not path.isdir(srcdirname): if not path.isdir(srcdirname):
print >>sys.stderr, 'Error: Cannot find source directory.' print >>sys.stderr, 'Error: Cannot find source directory.'
@@ -69,7 +70,7 @@ def main(argv):
return 1 return 1
builder = all_files = None builder = all_files = None
opt_help = freshenv = False opt_help = freshenv = use_pdb = False
status = sys.stdout status = sys.stdout
options = {} options = {}
confoverrides = {} confoverrides = {}
@@ -111,6 +112,8 @@ def main(argv):
freshenv = True freshenv = True
elif opt == '-q': elif opt == '-q':
status = StringIO() status = StringIO()
elif opt == '-P':
use_pdb = True
if not sys.stdout.isatty() or sys.platform == 'win32': if not sys.stdout.isatty() or sys.platform == 'win32':
# Windows' cmd box doesn't understand ANSI sequences # Windows' cmd box doesn't understand ANSI sequences
@@ -128,6 +131,7 @@ def main(argv):
print ' * %s: %s' % (optname, description) print ' * %s: %s' % (optname, description)
return 0 return 0
try:
builderobj = builderobj(srcdirname, outdirname, doctreedir, options, builderobj = builderobj(srcdirname, outdirname, doctreedir, options,
status_stream=status, status_stream=status,
warning_stream=sys.stderr, warning_stream=sys.stderr,
@@ -139,6 +143,11 @@ def main(argv):
builderobj.build_specific(filenames) builderobj.build_specific(filenames)
else: else:
builderobj.build_update() builderobj.build_update()
except:
if not use_pdb:
raise
import pdb
pdb.post_mortem(sys.exc_info()[2])
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -19,6 +19,7 @@ import shutil
import cPickle as pickle import cPickle as pickle
import cStringIO as StringIO import cStringIO as StringIO
from os import path from os import path
from cgi import escape
from docutils.io import StringOutput, FileOutput, DocTreeInput from docutils.io import StringOutput, FileOutput, DocTreeInput
from docutils.core import publish_parts from docutils.core import publish_parts
@@ -33,7 +34,7 @@ from .patchlevel import get_version_info, get_sys_version_info
from .htmlwriter import HTMLWriter from .htmlwriter import HTMLWriter
from .latexwriter import LaTeXWriter from .latexwriter import LaTeXWriter
from .environment import BuildEnvironment, NoUri from .environment import BuildEnvironment, NoUri
from .highlighting import pygments, get_stylesheet from .highlighting import pygments, highlight_block, get_stylesheet
from .util.console import bold, purple, green from .util.console import bold, purple, green
from . import addnodes from . import addnodes
@@ -194,10 +195,14 @@ class Builder(object):
def build_update(self): def build_update(self):
"""Only rebuild files changed or added since last build.""" """Only rebuild files changed or added since last build."""
self.load_env() self.load_env()
to_build = list(self.get_outdated_files()) to_build = self.get_outdated_files()
if not to_build: if not to_build:
self.msg('no target files are out of date, exiting.') self.msg('no target files are out of date, exiting.')
return return
if isinstance(to_build, str):
self.build([], to_build)
else:
to_build = list(to_build)
self.build(to_build, self.build(to_build,
summary='targets for %d source files that are ' summary='targets for %d source files that are '
'out of date' % len(to_build)) 'out of date' % len(to_build))
@@ -207,15 +212,20 @@ class Builder(object):
self.msg('building [%s]:' % self.name, nonl=1) self.msg('building [%s]:' % self.name, nonl=1)
self.msg(summary, nobold=1) self.msg(summary, nobold=1)
updated_filenames = []
# while reading, collect all warnings from docutils # while reading, collect all warnings from docutils
with collect_env_warnings(self): with collect_env_warnings(self):
self.msg('reading, updating environment:', nonl=1) self.msg('reading, updating environment:', nonl=1)
iterator = self.env.update(self.config) iterator = self.env.update(self.config)
self.msg(iterator.next(), nobold=1) self.msg(iterator.next(), nonl=1, nobold=1)
for filename in iterator: for filename in iterator:
if not updated_filenames:
self.msg('')
updated_filenames.append(filename)
self.msg(purple(filename), nonl=1, nobold=1) self.msg(purple(filename), nonl=1, nobold=1)
self.msg() self.msg()
if updated_filenames:
# save the environment # save the environment
self.msg('pickling the env...', nonl=True) self.msg('pickling the env...', nonl=True)
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME)) self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
@@ -227,33 +237,32 @@ class Builder(object):
# another indirection to support methods which don't build files # another indirection to support methods which don't build files
# individually # individually
self.write(filenames) self.write(filenames, updated_filenames)
# finish (write style files etc.) # finish (write style files etc.)
self.msg('finishing...') self.msg('finishing...')
self.finish() self.finish()
self.msg('done!') self.msg('done!')
def write(self, filenames): def write(self, build_filenames, updated_filenames):
if build_filenames is None: # build_all
build_filenames = self.env.all_files
filenames = set(build_filenames) | set(updated_filenames)
# add all toctree-containing files that may have changed
for filename in list(filenames):
for tocfilename in self.env.files_to_rebuild.get(filename, []):
filenames.add(tocfilename)
filenames.add('contents.rst')
self.msg('creating index...') self.msg('creating index...')
self.env.create_index(self) self.env.create_index(self)
if filenames: self.prepare_writing(filenames)
# add all TOC files that may have changed
filenames_set = set(filenames)
for filename in filenames:
for tocfilename in self.env.files_to_rebuild.get(filename, []):
filenames_set.add(tocfilename)
filenames_set.add('contents.rst')
else:
# build all
filenames_set = set(self.env.all_files)
self.prepare_writing(filenames_set)
# write target files # write target files
with collect_env_warnings(self): with collect_env_warnings(self):
self.msg('writing output...') self.msg('writing output...')
for filename in status_iterator(sorted(filenames_set), green, for filename in status_iterator(sorted(filenames), green,
stream=self.status_stream): stream=self.status_stream):
doctree = self.env.get_and_resolve_doctree(filename, self) doctree = self.env.get_and_resolve_doctree(filename, self)
self.write_file(filename, doctree) self.write_file(filename, doctree)
@@ -595,7 +604,8 @@ class WebHTMLBuilder(StandaloneHTMLBuilder):
# if there is a source file, copy the source file for the "show source" link # if there is a source file, copy the source file for the "show source" link
if context.get('sourcename'): if context.get('sourcename'):
source_name = path.join(self.outdir, 'sources', os_path(context['sourcename'])) source_name = path.join(self.outdir, 'sources',
os_path(context['sourcename']))
ensuredir(path.dirname(source_name)) ensuredir(path.dirname(source_name))
shutil.copyfile(path.join(self.srcdir, os_path(filename)), source_name) shutil.copyfile(path.join(self.srcdir, os_path(filename)), source_name)
@@ -652,8 +662,7 @@ class LaTeXBuilder(Builder):
self.filenames = [] self.filenames = []
def get_outdated_files(self): def get_outdated_files(self):
# XXX always rebuild everything for now return 'all documents' # for now
return ['dummy']
def get_target_uri(self, source_filename, typ=None): def get_target_uri(self, source_filename, typ=None):
if typ == 'token': if typ == 'token':
@@ -677,9 +686,7 @@ class LaTeXBuilder(Builder):
and not fn.endswith('index.rst')]: and not fn.endswith('index.rst')]:
yield (howto, 'howto-'+howto[6:-4]+'.tex', 'howto') yield (howto, 'howto-'+howto[6:-4]+'.tex', 'howto')
def write(self, filenames): def write(self, *ignored):
# "filenames" is ignored here...
# first, assemble the "special" docs that are in every PDF # first, assemble the "special" docs that are in every PDF
specials = [] specials = []
for fname in ["glossary", "about", "license", "copyright"]: for fname in ["glossary", "about", "license", "copyright"]:
@@ -695,8 +702,8 @@ class LaTeXBuilder(Builder):
destination_path=path.join(self.outdir, targetname), destination_path=path.join(self.outdir, targetname),
encoding='utf-8') encoding='utf-8')
print "processing", targetname + "...", print "processing", targetname + "...",
doctree = self.assemble_doctree(sourcename, doctree = self.assemble_doctree(
specials=(docclass == 'manual') and specials or []) sourcename, specials=(docclass == 'manual') and specials or [])
print "writing...", print "writing...",
doctree.settings = docsettings doctree.settings = docsettings
doctree.settings.filename = sourcename doctree.settings.filename = sourcename
@@ -743,9 +750,119 @@ class LaTeXBuilder(Builder):
path.join(self.outdir, filename)) path.join(self.outdir, filename))
class ChangesBuilder(Builder):
"""
Write a summary with all versionadded/changed directives.
"""
name = 'changes'
def init(self):
from ._jinja import Environment, FileSystemLoader
templates_path = path.join(path.dirname(__file__), 'templates')
jinja_env = Environment(loader=FileSystemLoader(templates_path),
# disable traceback, more likely that something in the
# application is broken than in the templates
friendly_traceback=False)
self.ftemplate = jinja_env.get_template('versionchanges_frameset.html')
self.vtemplate = jinja_env.get_template('versionchanges.html')
self.stemplate = jinja_env.get_template('rstsource.html')
def get_outdated_files(self):
return self.outdir
typemap = {
'versionadded': 'added',
'versionchanged': 'changed',
'deprecated': 'deprecated',
}
def write(self, *ignored):
ver = self.config['version']
libchanges = {}
apichanges = []
otherchanges = {}
self.msg('writing summary file...')
for type, filename, lineno, module, descname, content in \
self.env.versionchanges[ver]:
ttext = self.typemap[type]
context = content.replace('\n', ' ')
if descname and filename.startswith('c-api'):
if not descname:
continue
if context:
entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context)
else:
entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
apichanges.append((entry, filename, lineno))
elif descname or module:
if not module:
module = 'Builtins'
if not descname:
descname = 'Module level'
if context:
entry = '<b>%s</b>: <i>%s:</i> %s' % (descname, ttext, context)
else:
entry = '<b>%s</b>: <i>%s</i>.' % (descname, ttext)
libchanges.setdefault(module, []).append((entry, filename, lineno))
else:
if not context:
continue
entry = '<i>%s:</i> %s' % (ttext.capitalize(), context)
title = self.env.titles[filename].astext()
otherchanges.setdefault((filename, title), []).append(
(entry, filename, lineno))
ctx = {
'version': ver,
'libchanges': sorted(libchanges.iteritems()),
'apichanges': sorted(apichanges),
'otherchanges': sorted(otherchanges.iteritems()),
}
with open(path.join(self.outdir, 'index.html'), 'w') as f:
f.write(self.ftemplate.render(ctx))
with open(path.join(self.outdir, 'changes.html'), 'w') as f:
f.write(self.vtemplate.render(ctx))
hltext = ['.. versionadded:: %s' % ver,
'.. versionchanged:: %s' % ver,
'.. deprecated:: %s' % ver]
def hl(no, line):
line = '<a name="L%s"> </a>' % no + escape(line)
for x in hltext:
if x in line:
line = '<span class="hl">%s</span>' % line
break
return line
self.msg('copying source files...')
for filename in self.env.all_files:
with open(path.join(self.srcdir, os_path(filename))) as f:
lines = f.readlines()
targetfn = path.join(self.outdir, 'rst', os_path(filename)) + '.html'
ensuredir(path.dirname(targetfn))
with codecs.open(targetfn, 'w', 'utf8') as f:
text = ''.join(hl(i+1, line) for (i, line) in enumerate(lines))
ctx = {'filename': filename, 'text': text}
f.write(self.stemplate.render(ctx))
shutil.copyfile(path.join(path.dirname(__file__), 'style', 'default.css'),
path.join(self.outdir, 'default.css'))
def hl(self, text, ver):
text = escape(text)
for directive in ['versionchanged', 'versionadded', 'deprecated']:
text = text.replace('.. %s:: %s' % (directive, ver),
'<b>.. %s:: %s</b>' % (directive, ver))
return text
def finish(self):
pass
builders = { builders = {
'html': StandaloneHTMLBuilder, 'html': StandaloneHTMLBuilder,
'web': WebHTMLBuilder, 'web': WebHTMLBuilder,
'htmlhelp': HTMLHelpBuilder, 'htmlhelp': HTMLHelpBuilder,
'latex': LaTeXBuilder, 'latex': LaTeXBuilder,
'changes': ChangesBuilder,
} }

View File

@@ -428,7 +428,7 @@ def version_directive(name, arguments, options, content, lineno,
else: else:
ret = [node] ret = [node]
env = state.document.settings.env env = state.document.settings.env
env.note_versionchange(node['type'], node['version'], node) env.note_versionchange(node['type'], node['version'], node, lineno)
return ret return ret
version_directive.arguments = (1, 1, 1) version_directive.arguments = (1, 1, 1)

View File

@@ -52,7 +52,7 @@ default_settings = {
# This is increased every time a new environment attribute is added # This is increased every time a new environment attribute is added
# to properly invalidate pickle files. # to properly invalidate pickle files.
ENV_VERSION = 13 ENV_VERSION = 14
def walk_depth(node, depth, maxdepth): def walk_depth(node, depth, maxdepth):
@@ -222,7 +222,7 @@ class BuildEnvironment:
self.indexentries = {} # filename -> list of self.indexentries = {} # filename -> list of
# (type, string, target, aliasname) # (type, string, target, aliasname)
self.versionchanges = {} # version -> list of self.versionchanges = {} # version -> list of
# (type, filename, module, descname, content) # (type, filename, lineno, module, descname, content)
# These are set while parsing a file # These are set while parsing a file
self.filename = None # current file name self.filename = None # current file name
@@ -383,7 +383,8 @@ class BuildEnvironment:
if save_parsed: if save_parsed:
# save the parsed doctree # save the parsed doctree
doctree_filename = path.join(self.doctreedir, os_path(filename)[:-3] + 'doctree') doctree_filename = path.join(self.doctreedir,
os_path(filename)[:-3] + 'doctree')
dirname = path.dirname(doctree_filename) dirname = path.dirname(doctree_filename)
if not path.isdir(dirname): if not path.isdir(dirname):
os.makedirs(dirname) os.makedirs(dirname)
@@ -528,9 +529,9 @@ class BuildEnvironment:
self.indexentries.setdefault(self.filename, []).append( self.indexentries.setdefault(self.filename, []).append(
(type, string, targetid, aliasname)) (type, string, targetid, aliasname))
def note_versionchange(self, type, version, node): def note_versionchange(self, type, version, node, lineno):
self.versionchanges.setdefault(version, []).append( self.versionchanges.setdefault(version, []).append(
(type, self.filename, self.currmodule, self.currdesc, node.deepcopy())) (type, self.filename, lineno, self.currmodule, self.currdesc, node.astext()))
# ------- # -------
# --------- RESOLVING REFERENCES AND TOCTREES ------------------------------ # --------- RESOLVING REFERENCES AND TOCTREES ------------------------------
@@ -603,6 +604,8 @@ class BuildEnvironment:
try: try:
if typ == 'ref': if typ == 'ref':
# reference to the named label; the final node will contain the
# section name after the label
filename, labelid, sectname = self.labels.get(target, ('','','')) filename, labelid, sectname = self.labels.get(target, ('','',''))
if not filename: if not filename:
newnode = doctree.reporter.system_message( newnode = doctree.reporter.system_message(
@@ -620,6 +623,20 @@ class BuildEnvironment:
newnode['refuri'] = builder.get_relative_uri( newnode['refuri'] = builder.get_relative_uri(
docfilename, filename) + '#' + labelid docfilename, filename) + '#' + labelid
newnode.append(innernode) newnode.append(innernode)
elif typ == 'keyword':
# keywords are referenced by named labels
filename, labelid, _ = self.labels.get(target, ('','',''))
if not filename:
self._warnfunc('%s: unknown keyword: %s' % (docfilename, target))
newnode = contnode
else:
newnode = nodes.reference('', '')
if filename == docfilename:
newnode['refid'] = labelid
else:
newnode['refuri'] = builder.get_relative_uri(
docfilename, filename) + '#' + labelid
newnode.append(contnode)
elif typ in ('token', 'term', 'envvar', 'option'): elif typ in ('token', 'term', 'envvar', 'option'):
filename, labelid = self.reftargets.get((typ, target), ('', '')) filename, labelid = self.reftargets.get((typ, target), ('', ''))
if not filename: if not filename:
@@ -656,10 +673,12 @@ class BuildEnvironment:
synopsis, (' (deprecated)' if deprecated else '')) synopsis, (' (deprecated)' if deprecated else ''))
newnode.append(contnode) newnode.append(contnode)
else: else:
# "descrefs"
modname = node['modname'] modname = node['modname']
clsname = node['classname'] clsname = node['classname']
searchorder = 1 if node.hasattr('refspecific') else 0 searchorder = 1 if node.hasattr('refspecific') else 0
name, desc = self.find_desc(modname, clsname, target, typ, searchorder) name, desc = self.find_desc(modname, clsname,
target, typ, searchorder)
if not desc: if not desc:
newnode = contnode newnode = contnode
else: else:

View File

@@ -25,6 +25,7 @@ from . import highlighting
HEADER = r'''%% Generated by Sphinx. HEADER = r'''%% Generated by Sphinx.
\documentclass[%(papersize)s,%(pointsize)s]{%(docclass)s} \documentclass[%(papersize)s,%(pointsize)s]{%(docclass)s}
\usepackage[utf8]{inputenc} \usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[colorlinks,breaklinks]{hyperref} \usepackage[colorlinks,breaklinks]{hyperref}
\title{%(title)s} \title{%(title)s}
\date{%(date)s} \date{%(date)s}
@@ -408,7 +409,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# this is a list in the source, but should be rendered as a # this is a list in the source, but should be rendered as a
# comma-separated list here # comma-separated list here
self.body.append('\n\n') self.body.append('\n\n')
self.body.append(', '.join(n.astext() for n in node.children[0].children)) self.body.append(', '.join(n.astext() for n in node.children[0].children) + '.')
self.body.append('\n\n') self.body.append('\n\n')
raise nodes.SkipNode raise nodes.SkipNode

View File

@@ -23,7 +23,6 @@ generic_docroles = {
'dfn' : nodes.emphasis, 'dfn' : nodes.emphasis,
'guilabel' : nodes.strong, 'guilabel' : nodes.strong,
'kbd' : nodes.literal, 'kbd' : nodes.literal,
'keyword' : nodes.literal,
'mailheader' : addnodes.literal_emphasis, 'mailheader' : addnodes.literal_emphasis,
'makevar' : nodes.Text, 'makevar' : nodes.Text,
'manpage' : addnodes.literal_emphasis, 'manpage' : addnodes.literal_emphasis,
@@ -172,6 +171,7 @@ specific_docroles = {
'mod' : xfileref_role, 'mod' : xfileref_role,
'keyword': xfileref_role,
'ref': xfileref_role, 'ref': xfileref_role,
'token' : xfileref_role, 'token' : xfileref_role,
'term': xfileref_role, 'term': xfileref_role,

View File

@@ -10,9 +10,10 @@
""" """
import re import re
import pickle import pickle
from collections import defaultdict from collections import defaultdict
from docutils.nodes import Text, NodeVisitor from docutils.nodes import Text, NodeVisitor
from .util.stemmer import PorterStemmer from .util.stemmer import PorterStemmer
from .util.json import dump_json, load_json from .util.json import dump_json, load_json

View File

@@ -154,7 +154,6 @@ div.sidebar input {
div.modulecloud { div.modulecloud {
margin: -5px 10px 5px 10px; margin: -5px 10px 5px 10px;
padding: 10px; padding: 10px;
font-size: 110%;
line-height: 160%; line-height: 160%;
border: 1px solid #cbe7e5; border: 1px solid #cbe7e5;
background-color: #f2fbfd; background-color: #f2fbfd;
@@ -708,6 +707,7 @@ pre {
tt { tt {
background-color: #ecf0f3; background-color: #ecf0f3;
padding: 0 1px 0 1px; padding: 0 1px 0 1px;
font-size: 0.95em;
} }
tt.descname { tt.descname {

View File

@@ -0,0 +1,15 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>{{ filename }} &mdash; Python Documentation</title>
<style type="text/css">
.hl { background-color: yellow }
</style>
</head>
<body style="font-size: 90%">
<pre>
{{ text }}
</pre>
</body>
</html>

View File

@@ -54,6 +54,9 @@
<input type="hidden" name="check_keywords" value="yes"> <input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default"> <input type="hidden" name="area" value="default">
</form> </form>
{% if builder == 'web' %}
<p style="font-size: 90%">Enter a module, class or function name.</p>
{% endif %}
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@@ -0,0 +1,33 @@
{% macro entries changes %}
<ul>{% for entry, filename, lineno in changes %}
<li><a href="rst/{{ filename }}.html#L{{ lineno-10 }}" target="src">{{ entry }}</a></li>
{% endfor %}</ul>
{% endmacro -%}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" href="default.css">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Changes in Version {{ version }} &mdash; Python Documentation</title>
</head>
<body>
<div class="document">
<div class="body">
<h1>Automatically generated list of changes in version {{ version }}</h1>
<h2>Library changes</h2>
{% for modname, changes in libchanges %}
<h4>{{ modname }}</h4>
{{ entries(changes) }}
{% endfor %}
<h2>C API changes</h2>
{{ entries(apichanges) }}
<h2>Other changes</h2>
{% for (fn, title), changes in otherchanges %}
<h4>{{ title }} <span style="font-size: 50%">({{ fn }})</span></h4>
{{ entries(changes) }}
{% endfor %}
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,11 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<title>Changes in Version {{ version }} &mdash; Python Documentation</title>
</head>
<frameset cols="45%,*">
<frame name="main" src="changes.html">
<frame name="src" src="about:blank">
</frameset>
</html>

View File

@@ -1,5 +1,5 @@
% %
% howto.cls for the Python documentation % howto.cls for Sphinx
% %
\NeedsTeXFormat{LaTeX2e}[1995/12/01] \NeedsTeXFormat{LaTeX2e}[1995/12/01]
@@ -34,7 +34,7 @@
% This gives us all the Python-specific markup that we really want. % This gives us all the Python-specific markup that we really want.
% This should come last. Do not change this. % This should come last. Do not change this.
% %
\RequirePackage{python} \RequirePackage{sphinx}
% This comes after python.sty because it otherwise defines its own % This comes after python.sty because it otherwise defines its own
% "seealso" command. % "seealso" command.

View File

@@ -1,5 +1,5 @@
% %
% manual.cls for the Python documentation % manual.cls for Sphinx
% %
\NeedsTeXFormat{LaTeX2e}[1995/12/01] \NeedsTeXFormat{LaTeX2e}[1995/12/01]
@@ -43,9 +43,9 @@
% This gives us all the Python-specific markup that we really want. % This gives us all the Python-specific markup that we really want.
% This should come last. Do not change this. % This should come last. Do not change this.
% %
\RequirePackage{python} \RequirePackage{sphinx}
% This comes after python.sty because it otherwise defines its own % This comes after sphinx.sty because it otherwise defines its own
% "seealso" command. % "seealso" command.
\RequirePackage{makeidx} \RequirePackage{makeidx}

View File

@@ -1,10 +1,13 @@
% %
% python.sty for the Python docummentation [works only with Latex2e] % sphinx.sty
%
% Adapted from the old python.sty, mostly written by Fred Drake,
% by Georg Brandl.
% %
\NeedsTeXFormat{LaTeX2e}[1995/12/01] \NeedsTeXFormat{LaTeX2e}[1995/12/01]
\ProvidesPackage{python} \ProvidesPackage{sphinx}
[1998/01/11 LaTeX package (Python markup)] [2007/12/30 LaTeX package (Sphinx markup)]
\RequirePackage{textcomp} \RequirePackage{textcomp}
\RequirePackage{longtable} \RequirePackage{longtable}
@@ -310,17 +313,6 @@
\newcommand{\refexmodindex}[1]{\py@refmodule{#1}{extension }} \newcommand{\refexmodindex}[1]{\py@refmodule{#1}{extension }}
\newcommand{\refstmodindex}[1]{\py@refmodule{#1}{standard }} \newcommand{\refstmodindex}[1]{\py@refmodule{#1}{standard }}
% Refer to a module's documentation using a hyperlink of the module's
% name, at least if we're building PDF:
\ifpdf
\newcommand{\refmodule}[2][\py@modulebadkey]{%
\ifx\py@modulebadkey#1\def\py@modulekey{#2}\else\def\py@modulekey{#1}\fi%
\py@linkToName{label-module-\py@modulekey}{\module{#2}}%
}
\else
\newcommand{\refmodule}[2][\py@modulebadkey]{\module{#2}}
\fi
% support for the module index % support for the module index
\newif\ifpy@UseModuleIndex \newif\ifpy@UseModuleIndex
\py@UseModuleIndexfalse \py@UseModuleIndexfalse
@@ -839,9 +831,7 @@
% Also for consistency: spell Python "Python", not "python"! % Also for consistency: spell Python "Python", not "python"!
% code is the most difficult one... \newcommand{\code}[1]{\texttt{#1}}
\newcommand{\code}[1]{\textrm{\@vobeyspaces\@noligs\def\{{\char`\{}\def\}{\char`\}}\def\~{\char`\~}\def\^{\char`\^}\def\e{\char`\\}\def\${\char`\$}\def\#{\char`\#}\def\&{\char`\&}\def\%{\char`\%}%
\texttt{#1}}}
\newcommand{\bfcode}[1]{\code{\bfseries#1}} % bold-faced code font \newcommand{\bfcode}[1]{\code{\bfseries#1}} % bold-faced code font
\newcommand{\csimplemacro}[1]{\code{#1}} \newcommand{\csimplemacro}[1]{\code{#1}}
@@ -861,28 +851,6 @@
\newcommand{\file}[1]{`\filenq{#1}'} \newcommand{\file}[1]{`\filenq{#1}'}
\newcommand{\filenq}[1]{{\py@smallsize\textsf{\let\e=\textbackslash#1}}} \newcommand{\filenq}[1]{{\py@smallsize\textsf{\let\e=\textbackslash#1}}}
% Use this def/redef approach for \url{} since hyperref defined this already,
% but only if we actually used hyperref:
%\ifpdf
% \newcommand{\url}[1]{{%
% \py@pdfstartlink%
% attr{ /Border [0 0 0] }%
% user{%
% /Subtype/Link%
% /A<<%
% /Type/Action%
% /S/URI%
% /URI(#1)%
% >>%
% }%
% \py@LinkColor% color of the link text
% \py@smallsize\sf #1%
% \py@NormalColor% Turn it back off; these are declarative
% \pdfendlink}% and don't appear bound to the current
% }% formatting "box".
%\else
% \newcommand{\url}[1]{\mbox{\py@smallsize\textsf{#1}}}
%\fi
\newcommand{\email}[1]{{\py@smallsize\textsf{#1}}} \newcommand{\email}[1]{{\py@smallsize\textsf{#1}}}
\newcommand{\newsgroup}[1]{{\py@smallsize\textsf{#1}}} \newcommand{\newsgroup}[1]{{\py@smallsize\textsf{#1}}}
@@ -891,14 +859,6 @@
\let\moreargs=\py@moreargs% \let\moreargs=\py@moreargs%
\var{#1}}}} \var{#1}}}}
% I'd really like to get rid of this!
\newif\iftexi\texifalse
% This is used to get l2h to put the copyright and abstract on
% a separate HTML page.
\newif\ifhtml\htmlfalse
% These should be used for all references to identifiers which are % These should be used for all references to identifiers which are
% used to refer to instances of specific language constructs. See the % used to refer to instances of specific language constructs. See the
% names for specific semantic assignments. % names for specific semantic assignments.
@@ -947,29 +907,6 @@
% Note that \longprogramopt provides the '--'! % Note that \longprogramopt provides the '--'!
\newcommand{\longprogramopt}[1]{\strong{-{}-#1}} \newcommand{\longprogramopt}[1]{\strong{-{}-#1}}
% \ulink{link text}{URL}
\ifpdf
\newcommand{\ulink}[2]{{%
% For PDF, we *should* only generate a link when the URL is absolute.
\py@pdfstartlink%
attr{ /Border [0 0 0] }%
user{%
/Subtype/Link%
/A<<%
/Type/Action%
/S/URI%
/URI(#2)%
>>%
}%
\py@LinkColor% color of the link text
#1%
\py@NormalColor% Turn it back off; these are declarative
\pdfendlink}% and don't appear bound to the current
}% formatting "box".
\else
\newcommand{\ulink}[2]{#1}
\fi
% cited titles: \citetitle{Title of Work} % cited titles: \citetitle{Title of Work}
% online: \citetitle[url-to-resource]{Title of Work} % online: \citetitle[url-to-resource]{Title of Work}
\ifpdf \ifpdf
@@ -1160,27 +1097,6 @@
\end{center}% \end{center}%
} }
% XXX Don't think we can use this yet, though it cleans up some
% tedious markup. There's no equivalent for the HTML transform yet,
% and that needs to exist. I don't know how to write it.
%
% This should really have something that makes it easier to bind a
% table's ``Notes'' column and an associated tablenotes environment,
% and generates the right magic for getting the numbers right in the
% table.
%
% So this is quite incomplete.
%
\newcounter{py@tablenotescounter}
\newenvironment{tablenotes}{%
\noindent Notes:
\par
\setcounter{py@tablenotescounter}{0}
\begin{list}{(\arabic{py@tablenotescounter})}%
{\usecounter{py@tablenotescounter}}
}{\end{list}}
% Cross-referencing (AMK, new impl. FLD) % Cross-referencing (AMK, new impl. FLD)
% Sample usage: % Sample usage:
% \begin{seealso} % \begin{seealso}

View File

@@ -88,7 +88,7 @@ class AdminPanel(object):
Log the user out. Log the user out.
""" """
req.logout() req.logout()
return RedirectResponse('admin/login/') return RedirectResponse('@admin/login/')
def do_change_password(self, req): def do_change_password(self, req):
""" """

View File

@@ -43,13 +43,16 @@ class AntiSpam(object):
else: else:
lines = [l.strip() for l in data.splitlines() lines = [l.strip() for l in data.splitlines()
if not l.startswith('#')] if not l.startswith('#')]
f = file(bad_content_file, 'w') with file(bad_content_file, 'w') as f:
f.write('\n'.join(lines)) f.write('\n'.join(lines))
last_change = int(time.time()) last_change = int(time.time())
if lines is None: if lines is None:
try:
with file(bad_content_file) as f: with file(bad_content_file) as f:
lines = [l.strip() for l in f] lines = [l.strip() for l in f]
except:
lines = []
self.rules = [re.compile(rule) for rule in lines if rule] self.rules = [re.compile(rule) for rule in lines if rule]
def is_spam(self, fields): def is_spam(self, fields):

View File

@@ -28,8 +28,7 @@ from collections import defaultdict
from .feed import Feed from .feed import Feed
from .mail import Email from .mail import Email
from .util import render_template, render_simple_template, get_target_uri, \ from .util import render_template, get_target_uri, blackhole_dict, striptags
blackhole_dict, striptags
from .admin import AdminPanel from .admin import AdminPanel
from .userdb import UserDatabase from .userdb import UserDatabase
from .robots import robots_txt from .robots import robots_txt
@@ -39,9 +38,9 @@ from .database import connect, set_connection, Comment
from .wsgiutil import Request, Response, RedirectResponse, \ from .wsgiutil import Request, Response, RedirectResponse, \
JSONResponse, SharedDataMiddleware, NotFound, get_base_uri JSONResponse, SharedDataMiddleware, NotFound, get_base_uri
from ..util import relative_uri, shorten_result from ..util import relative_uri
from ..search import SearchFrontend from ..search import SearchFrontend
from ..writer import HTMLWriter from ..htmlwriter import HTMLWriter
from ..builder import LAST_BUILD_FILENAME, ENV_PICKLE_FILENAME from ..builder import LAST_BUILD_FILENAME, ENV_PICKLE_FILENAME
from docutils.io import StringOutput from docutils.io import StringOutput
@@ -88,6 +87,7 @@ comments_methods = {
class MockBuilder(object): class MockBuilder(object):
def get_relative_uri(self, from_, to): def get_relative_uri(self, from_, to):
return '' return ''
name = 'web'
NoCache = object() NoCache = object()
@@ -139,8 +139,7 @@ class DocumentationApplication(object):
def load_env(self, new_mtime): def load_env(self, new_mtime):
env_lock.acquire() with env_lock:
try:
if self.buildmtime == new_mtime: if self.buildmtime == new_mtime:
# happens if another thread already reloaded the env # happens if another thread already reloaded the env
return return
@@ -153,8 +152,6 @@ class DocumentationApplication(object):
self.search_frontend = SearchFrontend(pickle.load(f)) self.search_frontend = SearchFrontend(pickle.load(f))
self.buildmtime = new_mtime self.buildmtime = new_mtime
self.cache.clear() self.cache.clear()
finally:
env_lock.release()
def search(self, req): def search(self, req):
@@ -209,9 +206,11 @@ class DocumentationApplication(object):
warning_stream = StringIO.StringIO() warning_stream = StringIO.StringIO()
env2 = copy.deepcopy(self.env) env2 = copy.deepcopy(self.env)
destination = StringOutput(encoding='utf-8') destination = StringOutput(encoding='utf-8')
writer = HTMLWriter(env2.config) builder = MockBuilder()
builder.config = env2.config
writer = HTMLWriter(builder)
doctree = env2.read_file(page_id, pathname, save_parsed=False) doctree = env2.read_file(page_id, pathname, save_parsed=False)
doctree = env2.get_and_resolve_doctree(page_id, MockBuilder(), doctree) doctree = env2.get_and_resolve_doctree(page_id, builder, doctree)
doctree.settings = OptionParser(defaults=env2.settings, doctree.settings = OptionParser(defaults=env2.settings,
components=(writer,)).get_default_values() components=(writer,)).get_default_values()
doctree.reporter = Reporter(page_id, 2, 4, stream=warning_stream) doctree.reporter = Reporter(page_id, 2, 4, stream=warning_stream)

View File

@@ -71,11 +71,6 @@ def render_template(req, template_name, *contexts):
return tmpl.render(context) return tmpl.render(context)
def render_simple_template(template_name, context):
tmpl = jinja_env.get_template(template_name)
return tmpl.render(context)
class lazy_property(object): class lazy_property(object):
""" """
Descriptor implementing a "lazy property", i.e. the function Descriptor implementing a "lazy property", i.e. the function