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
******
- section numbers
- split the general index
- HTML section numbers?
- split the general index?
- add OpenSearch
- "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
- link to keywords
- source file cross-references?
Web App
*******
- fix /download
- discuss and debug comments system
- prepare for databases other than sqlite for comments
- 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
-N -- do not do colored output
-q -- no output on stdout, just warnings on stderr
-P -- run Pdb on exception
modi:
* without -a and without filenames, write new and changed files.
* with -a, write all files.
@@ -43,7 +44,7 @@ modi:
def main(argv):
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])
if not path.isdir(srcdirname):
print >>sys.stderr, 'Error: Cannot find source directory.'
@@ -69,7 +70,7 @@ def main(argv):
return 1
builder = all_files = None
opt_help = freshenv = False
opt_help = freshenv = use_pdb = False
status = sys.stdout
options = {}
confoverrides = {}
@@ -111,6 +112,8 @@ def main(argv):
freshenv = True
elif opt == '-q':
status = StringIO()
elif opt == '-P':
use_pdb = True
if not sys.stdout.isatty() or sys.platform == 'win32':
# Windows' cmd box doesn't understand ANSI sequences
@@ -128,17 +131,23 @@ def main(argv):
print ' * %s: %s' % (optname, description)
return 0
builderobj = builderobj(srcdirname, outdirname, doctreedir, options,
status_stream=status,
warning_stream=sys.stderr,
confoverrides=confoverrides,
freshenv=freshenv)
if all_files:
builderobj.build_all()
elif filenames:
builderobj.build_specific(filenames)
else:
builderobj.build_update()
try:
builderobj = builderobj(srcdirname, outdirname, doctreedir, options,
status_stream=status,
warning_stream=sys.stderr,
confoverrides=confoverrides,
freshenv=freshenv)
if all_files:
builderobj.build_all()
elif filenames:
builderobj.build_specific(filenames)
else:
builderobj.build_update()
except:
if not use_pdb:
raise
import pdb
pdb.post_mortem(sys.exc_info()[2])
if __name__ == '__main__':

View File

@@ -19,6 +19,7 @@ import shutil
import cPickle as pickle
import cStringIO as StringIO
from os import path
from cgi import escape
from docutils.io import StringOutput, FileOutput, DocTreeInput
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 .latexwriter import LaTeXWriter
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 . import addnodes
@@ -194,66 +195,74 @@ class Builder(object):
def build_update(self):
"""Only rebuild files changed or added since last build."""
self.load_env()
to_build = list(self.get_outdated_files())
to_build = self.get_outdated_files()
if not to_build:
self.msg('no target files are out of date, exiting.')
return
self.build(to_build,
summary='targets for %d source files that are '
'out of date' % len(to_build))
if isinstance(to_build, str):
self.build([], to_build)
else:
to_build = list(to_build)
self.build(to_build,
summary='targets for %d source files that are '
'out of date' % len(to_build))
def build(self, filenames, summary=None):
if summary:
self.msg('building [%s]:' % self.name, nonl=1)
self.msg(summary, nobold=1)
updated_filenames = []
# while reading, collect all warnings from docutils
with collect_env_warnings(self):
self.msg('reading, updating environment:', nonl=1)
iterator = self.env.update(self.config)
self.msg(iterator.next(), nobold=1)
self.msg(iterator.next(), nonl=1, nobold=1)
for filename in iterator:
if not updated_filenames:
self.msg('')
updated_filenames.append(filename)
self.msg(purple(filename), nonl=1, nobold=1)
self.msg()
# save the environment
self.msg('pickling the env...', nonl=True)
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
self.msg('done', nobold=True)
if updated_filenames:
# save the environment
self.msg('pickling the env...', nonl=True)
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
self.msg('done', nobold=True)
# global actions
self.msg('checking consistency...')
self.env.check_consistency()
# global actions
self.msg('checking consistency...')
self.env.check_consistency()
# another indirection to support methods which don't build files
# individually
self.write(filenames)
self.write(filenames, updated_filenames)
# finish (write style files etc.)
self.msg('finishing...')
self.finish()
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.env.create_index(self)
if 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)
self.prepare_writing(filenames)
# write target files
with collect_env_warnings(self):
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):
doctree = self.env.get_and_resolve_doctree(filename, self)
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 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))
shutil.copyfile(path.join(self.srcdir, os_path(filename)), source_name)
@@ -652,8 +662,7 @@ class LaTeXBuilder(Builder):
self.filenames = []
def get_outdated_files(self):
# XXX always rebuild everything for now
return ['dummy']
return 'all documents' # for now
def get_target_uri(self, source_filename, typ=None):
if typ == 'token':
@@ -677,9 +686,7 @@ class LaTeXBuilder(Builder):
and not fn.endswith('index.rst')]:
yield (howto, 'howto-'+howto[6:-4]+'.tex', 'howto')
def write(self, filenames):
# "filenames" is ignored here...
def write(self, *ignored):
# first, assemble the "special" docs that are in every PDF
specials = []
for fname in ["glossary", "about", "license", "copyright"]:
@@ -695,8 +702,8 @@ class LaTeXBuilder(Builder):
destination_path=path.join(self.outdir, targetname),
encoding='utf-8')
print "processing", targetname + "...",
doctree = self.assemble_doctree(sourcename,
specials=(docclass == 'manual') and specials or [])
doctree = self.assemble_doctree(
sourcename, specials=(docclass == 'manual') and specials or [])
print "writing...",
doctree.settings = docsettings
doctree.settings.filename = sourcename
@@ -743,9 +750,119 @@ class LaTeXBuilder(Builder):
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 = {
'html': StandaloneHTMLBuilder,
'web': WebHTMLBuilder,
'htmlhelp': HTMLHelpBuilder,
'latex': LaTeXBuilder,
'changes': ChangesBuilder,
}

View File

@@ -428,7 +428,7 @@ def version_directive(name, arguments, options, content, lineno,
else:
ret = [node]
env = state.document.settings.env
env.note_versionchange(node['type'], node['version'], node)
env.note_versionchange(node['type'], node['version'], node, lineno)
return ret
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
# to properly invalidate pickle files.
ENV_VERSION = 13
ENV_VERSION = 14
def walk_depth(node, depth, maxdepth):
@@ -222,7 +222,7 @@ class BuildEnvironment:
self.indexentries = {} # filename -> list of
# (type, string, target, aliasname)
self.versionchanges = {} # version -> list of
# (type, filename, module, descname, content)
# (type, filename, lineno, module, descname, content)
# These are set while parsing a file
self.filename = None # current file name
@@ -383,7 +383,8 @@ class BuildEnvironment:
if save_parsed:
# 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)
if not path.isdir(dirname):
os.makedirs(dirname)
@@ -528,9 +529,9 @@ class BuildEnvironment:
self.indexentries.setdefault(self.filename, []).append(
(type, string, targetid, aliasname))
def note_versionchange(self, type, version, node):
def note_versionchange(self, type, version, node, lineno):
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 ------------------------------
@@ -603,6 +604,8 @@ class BuildEnvironment:
try:
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, ('','',''))
if not filename:
newnode = doctree.reporter.system_message(
@@ -620,6 +623,20 @@ class BuildEnvironment:
newnode['refuri'] = builder.get_relative_uri(
docfilename, filename) + '#' + labelid
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'):
filename, labelid = self.reftargets.get((typ, target), ('', ''))
if not filename:
@@ -656,10 +673,12 @@ class BuildEnvironment:
synopsis, (' (deprecated)' if deprecated else ''))
newnode.append(contnode)
else:
# "descrefs"
modname = node['modname']
clsname = node['classname']
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:
newnode = contnode
else:

View File

@@ -25,6 +25,7 @@ from . import highlighting
HEADER = r'''%% Generated by Sphinx.
\documentclass[%(papersize)s,%(pointsize)s]{%(docclass)s}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[colorlinks,breaklinks]{hyperref}
\title{%(title)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
# comma-separated list here
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')
raise nodes.SkipNode

View File

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

View File

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

View File

@@ -154,7 +154,6 @@ div.sidebar input {
div.modulecloud {
margin: -5px 10px 5px 10px;
padding: 10px;
font-size: 110%;
line-height: 160%;
border: 1px solid #cbe7e5;
background-color: #f2fbfd;
@@ -708,6 +707,7 @@ pre {
tt {
background-color: #ecf0f3;
padding: 0 1px 0 1px;
font-size: 0.95em;
}
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="area" value="default">
</form>
{% if builder == 'web' %}
<p style="font-size: 90%">Enter a module, class or function name.</p>
{% endif %}
{% endif %}
</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]
@@ -34,7 +34,7 @@
% This gives us all the Python-specific markup that we really want.
% This should come last. Do not change this.
%
\RequirePackage{python}
\RequirePackage{sphinx}
% This comes after python.sty because it otherwise defines its own
% "seealso" command.

View File

@@ -1,5 +1,5 @@
%
% manual.cls for the Python documentation
% manual.cls for Sphinx
%
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
@@ -43,9 +43,9 @@
% This gives us all the Python-specific markup that we really want.
% 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.
\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]
\ProvidesPackage{python}
[1998/01/11 LaTeX package (Python markup)]
\ProvidesPackage{sphinx}
[2007/12/30 LaTeX package (Sphinx markup)]
\RequirePackage{textcomp}
\RequirePackage{longtable}
@@ -310,17 +313,6 @@
\newcommand{\refexmodindex}[1]{\py@refmodule{#1}{extension }}
\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
\newif\ifpy@UseModuleIndex
\py@UseModuleIndexfalse
@@ -839,9 +831,7 @@
% Also for consistency: spell Python "Python", not "python"!
% code is the most difficult one...
\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{\code}[1]{\texttt{#1}}
\newcommand{\bfcode}[1]{\code{\bfseries#1}} % bold-faced code font
\newcommand{\csimplemacro}[1]{\code{#1}}
@@ -861,28 +851,6 @@
\newcommand{\file}[1]{`\filenq{#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{\newsgroup}[1]{{\py@smallsize\textsf{#1}}}
@@ -891,14 +859,6 @@
\let\moreargs=\py@moreargs%
\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
% used to refer to instances of specific language constructs. See the
% names for specific semantic assignments.
@@ -947,29 +907,6 @@
% Note that \longprogramopt provides the '--'!
\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}
% online: \citetitle[url-to-resource]{Title of Work}
\ifpdf
@@ -1160,27 +1097,6 @@
\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)
% Sample usage:
% \begin{seealso}

View File

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

View File

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

View File

@@ -28,8 +28,7 @@ from collections import defaultdict
from .feed import Feed
from .mail import Email
from .util import render_template, render_simple_template, get_target_uri, \
blackhole_dict, striptags
from .util import render_template, get_target_uri, blackhole_dict, striptags
from .admin import AdminPanel
from .userdb import UserDatabase
from .robots import robots_txt
@@ -39,9 +38,9 @@ from .database import connect, set_connection, Comment
from .wsgiutil import Request, Response, RedirectResponse, \
JSONResponse, SharedDataMiddleware, NotFound, get_base_uri
from ..util import relative_uri, shorten_result
from ..util import relative_uri
from ..search import SearchFrontend
from ..writer import HTMLWriter
from ..htmlwriter import HTMLWriter
from ..builder import LAST_BUILD_FILENAME, ENV_PICKLE_FILENAME
from docutils.io import StringOutput
@@ -88,6 +87,7 @@ comments_methods = {
class MockBuilder(object):
def get_relative_uri(self, from_, to):
return ''
name = 'web'
NoCache = object()
@@ -139,8 +139,7 @@ class DocumentationApplication(object):
def load_env(self, new_mtime):
env_lock.acquire()
try:
with env_lock:
if self.buildmtime == new_mtime:
# happens if another thread already reloaded the env
return
@@ -153,8 +152,6 @@ class DocumentationApplication(object):
self.search_frontend = SearchFrontend(pickle.load(f))
self.buildmtime = new_mtime
self.cache.clear()
finally:
env_lock.release()
def search(self, req):
@@ -209,9 +206,11 @@ class DocumentationApplication(object):
warning_stream = StringIO.StringIO()
env2 = copy.deepcopy(self.env)
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.get_and_resolve_doctree(page_id, MockBuilder(), doctree)
doctree = env2.get_and_resolve_doctree(page_id, builder, doctree)
doctree.settings = OptionParser(defaults=env2.settings,
components=(writer,)).get_default_values()
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)
def render_simple_template(template_name, context):
tmpl = jinja_env.get_template(template_name)
return tmpl.render(context)
class lazy_property(object):
"""
Descriptor implementing a "lazy property", i.e. the function