Commit 681001e1 authored by Georg Brandl's avatar Georg Brandl

Generate pydoc's topic help from the reST docs via Sphinx'

new text writer.
parent 0b9aaa98
...@@ -98,6 +98,11 @@ doctest: build ...@@ -98,6 +98,11 @@ doctest: build
@echo "Testing of doctests in the sources finished, look at the " \ @echo "Testing of doctests in the sources finished, look at the " \
"results in build/doctest/output.txt" "results in build/doctest/output.txt"
pydoc-topics: BUILDER = pydoc-topics
pydoc-topics: build
@echo "Building finished; now copy build/pydoc-topics/pydoc_topics.py " \
"into the Lib/ directory"
clean: clean:
-rm -rf build/* -rm -rf build/*
-rm -rf tools/sphinx -rm -rf tools/sphinx
...@@ -64,6 +64,11 @@ Available make targets are: ...@@ -64,6 +64,11 @@ Available make targets are:
* "coverage", which builds a coverage overview for standard library modules * "coverage", which builds a coverage overview for standard library modules
and C API. and C API.
* "pydoc-topics", which builds a Python module containing a dictionary
with plain text documentation for the labels defined in
`tools/sphinxext/pyspecific.py` -- pydoc needs these to show topic
and keyword help.
A "make update" updates the Subversion checkouts in `tools/`. A "make update" updates the Subversion checkouts in `tools/`.
......
...@@ -20,5 +20,71 @@ def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): ...@@ -20,5 +20,71 @@ def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
return [refnode], [] return [refnode], []
# Support for building "topic help" for pydoc
pydoc_topic_labels = [
'assert', 'assignment', 'atom-identifiers', 'atom-literals',
'attribute-access', 'attribute-references', 'augassign', 'binary',
'bitwise', 'bltin-code-objects', 'bltin-ellipsis-object',
'bltin-file-objects', 'bltin-null-object', 'bltin-type-objects', 'booleans',
'break', 'callable-types', 'calls', 'class', 'coercion-rules',
'comparisons', 'compound', 'context-managers', 'continue', 'conversions',
'customization', 'debugger', 'del', 'dict', 'dynamic-features', 'else',
'exceptions', 'exec', 'execmodel', 'exprlists', 'floating', 'for',
'formatstrings', 'function', 'global', 'id-classes', 'identifiers', 'if',
'imaginary', 'import', 'in', 'integers', 'lambda', 'lists', 'naming',
'numbers', 'numeric-types', 'objects', 'operator-summary', 'pass', 'power',
'print', 'raise', 'return', 'sequence-methods', 'sequence-types',
'shifting', 'slicings', 'specialattrs', 'specialnames',
'string-conversions', 'string-methods', 'strings', 'subscriptions', 'truth',
'try', 'types', 'typesfunctions', 'typesmapping', 'typesmethods',
'typesmodules', 'typesseq', 'typesseq-mutable', 'unary', 'while', 'with',
'yield'
]
from os import path
from time import asctime
from pprint import pformat
from docutils.io import StringOutput
from docutils.utils import new_document
from sphinx.builder import Builder
from sphinx.textwriter import TextWriter
class PydocTopicsBuilder(Builder):
name = 'pydoc-topics'
def init(self):
self.topics = {}
def get_outdated_docs(self):
return 'all pydoc topics'
def get_target_uri(self, docname, typ=None):
return '' # no URIs
def write(self, *ignored):
writer = TextWriter(self)
for label in self.status_iterator(pydoc_topic_labels, 'building topics... '):
if label not in self.env.labels:
self.warn('label %r not in documentation' % label)
continue
docname, labelid, sectname = self.env.labels[label]
doctree = self.env.get_and_resolve_doctree(docname, self)
document = new_document('<section node>')
document.append(doctree.ids[labelid])
destination = StringOutput(encoding='utf-8')
writer.write(document, destination)
self.topics[label] = writer.output
def finish(self):
f = open(path.join(self.outdir, 'pydoc_topics.py'), 'w')
try:
f.write('# Autogenerated by Sphinx on %s\n' % asctime())
f.write('topics = ' + pformat(self.topics) + '\n')
finally:
f.close()
def setup(app): def setup(app):
app.add_role('issue', issue_role) app.add_role('issue', issue_role)
app.add_builder(PydocTopicsBuilder)
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: Latin-1 -*- # -*- coding: latin-1 -*-
"""Generate Python documentation in HTML or text for interactive use. """Generate Python documentation in HTML or text for interactive use.
In the Python interpreter, do "from pydoc import help" to provide online In the Python interpreter, do "from pydoc import help" to provide online
...@@ -1523,142 +1523,149 @@ def writedocs(dir, pkgpath='', done=None): ...@@ -1523,142 +1523,149 @@ def writedocs(dir, pkgpath='', done=None):
return return
class Helper: class Helper:
# These dictionaries map a topic name to either an alias, or a tuple
# (label, seealso-items). The "label" is the label of the corresponding
# section in the .rst file under Doc/ and an index into the dictionary
# in pydoc_topics.py.
#
# CAUTION: if you change one of these dictionaries, be sure to adapt the
# list of needed labels in Doc/tools/sphinxext/pyspecific.py and
# regenerate the pydoc_topics.py file by running
# make pydoc-topics
# in Doc/ and copying the output file into the Lib/ directory.
keywords = { keywords = {
'and': 'BOOLEAN', 'and': 'BOOLEAN',
'as': 'with', 'as': 'with',
'assert': ('ref/assert', ''), 'assert': ('assert', ''),
'break': ('ref/break', 'while for'), 'break': ('break', 'while for'),
'class': ('ref/class', 'CLASSES SPECIALMETHODS'), 'class': ('class', 'CLASSES SPECIALMETHODS'),
'continue': ('ref/continue', 'while for'), 'continue': ('continue', 'while for'),
'def': ('ref/function', ''), 'def': ('function', ''),
'del': ('ref/del', 'BASICMETHODS'), 'del': ('del', 'BASICMETHODS'),
'elif': 'if', 'elif': 'if',
'else': ('ref/if', 'while for'), 'else': ('else', 'while for'),
'except': 'try', 'except': 'except',
'exec': ('ref/exec', ''), 'exec': ('exec', ''),
'finally': 'try', 'finally': 'finally',
'for': ('ref/for', 'break continue while'), 'for': ('for', 'break continue while'),
'from': 'import', 'from': 'from',
'global': ('ref/global', 'NAMESPACES'), 'global': ('global', 'NAMESPACES'),
'if': ('ref/if', 'TRUTHVALUE'), 'if': ('if', 'TRUTHVALUE'),
'import': ('ref/import', 'MODULES'), 'import': ('import', 'MODULES'),
'in': ('ref/comparisons', 'SEQUENCEMETHODS2'), 'in': ('in', 'SEQUENCEMETHODS2'),
'is': 'COMPARISON', 'is': 'COMPARISON',
'lambda': ('ref/lambdas', 'FUNCTIONS'), 'lambda': ('lambda', 'FUNCTIONS'),
'not': 'BOOLEAN', 'not': 'BOOLEAN',
'or': 'BOOLEAN', 'or': 'BOOLEAN',
'pass': ('ref/pass', ''), 'pass': ('pass', ''),
'print': ('ref/print', ''), 'print': ('print', ''),
'raise': ('ref/raise', 'EXCEPTIONS'), 'raise': ('raise', 'EXCEPTIONS'),
'return': ('ref/return', 'FUNCTIONS'), 'return': ('return', 'FUNCTIONS'),
'try': ('ref/try', 'EXCEPTIONS'), 'try': ('try', 'EXCEPTIONS'),
'while': ('ref/while', 'break continue if TRUTHVALUE'), 'while': ('while', 'break continue if TRUTHVALUE'),
'with': ('ref/with', 'CONTEXTMANAGERS EXCEPTIONS yield'), 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'),
'yield': ('ref/yield', ''), 'yield': ('yield', ''),
} }
topics = { topics = {
'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'), 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS '
'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'), 'FUNCTIONS CLASSES MODULES FILES inspect'),
'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'), 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING '
'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'), 'TYPES'),
'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'), 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'),
'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'), 'FORMATTING': ('formatstrings', 'OPERATORS'),
'INTEGER': ('ref/integers', 'int range'), 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS '
'FLOAT': ('ref/floating', 'float math'), 'FORMATTING TYPES'),
'COMPLEX': ('ref/imaginary', 'complex cmath'), 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'),
'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'), 'INTEGER': ('integers', 'int range'),
'FLOAT': ('floating', 'float math'),
'COMPLEX': ('imaginary', 'complex cmath'),
'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
'MAPPINGS': 'DICTIONARIES', 'MAPPINGS': 'DICTIONARIES',
'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'), 'FUNCTIONS': ('typesfunctions', 'def TYPES'),
'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'), 'METHODS': ('typesmethods', 'class def CLASSES TYPES'),
'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'), 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'),
'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'), 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'),
'FRAMEOBJECTS': 'TYPES', 'FRAMEOBJECTS': 'TYPES',
'TRACEBACKS': 'TYPES', 'TRACEBACKS': 'TYPES',
'NONE': ('lib/bltin-null-object', ''), 'NONE': ('bltin-null-object', ''),
'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'), 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'),
'FILES': ('lib/bltin-file-objects', ''), 'FILES': ('bltin-file-objects', ''),
'SPECIALATTRIBUTES': ('lib/specialattrs', ''), 'SPECIALATTRIBUTES': ('specialattrs', ''),
'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'), 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'),
'MODULES': ('lib/typesmodules', 'import'), 'MODULES': ('typesmodules', 'import'),
'PACKAGES': 'import', 'PACKAGES': 'import',
'EXPRESSIONS': ('ref/summary', 'lambda or and not in is BOOLEAN COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES LISTS DICTIONARIES BACKQUOTES'), 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN '
'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER '
'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES '
'LISTS DICTIONARIES BACKQUOTES'),
'OPERATORS': 'EXPRESSIONS', 'OPERATORS': 'EXPRESSIONS',
'PRECEDENCE': 'EXPRESSIONS', 'PRECEDENCE': 'EXPRESSIONS',
'OBJECTS': ('ref/objects', 'TYPES'), 'OBJECTS': ('objects', 'TYPES'),
'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'), 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS '
'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'), 'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS '
'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'), 'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'),
'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'), 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'), 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'),
'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'), 'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 '
'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'), 'SPECIALMETHODS'),
'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), 'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 '
'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'), 'SPECIALMETHODS'),
'DYNAMICFEATURES': ('ref/dynamic-features', ''), 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'),
'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT '
'SPECIALMETHODS'),
'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
'DYNAMICFEATURES': ('dynamic-features', ''),
'SCOPING': 'NAMESPACES', 'SCOPING': 'NAMESPACES',
'FRAMES': 'NAMESPACES', 'FRAMES': 'NAMESPACES',
'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'), 'EXCEPTIONS': ('exceptions', 'try except finally raise'),
'COERCIONS': ('ref/coercion-rules','CONVERSIONS'), 'COERCIONS': ('coercion-rules','CONVERSIONS'),
'CONVERSIONS': ('ref/conversions', 'COERCIONS'), 'CONVERSIONS': ('conversions', 'COERCIONS'),
'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'), 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'),
'SPECIALIDENTIFIERS': ('ref/id-classes', ''), 'SPECIALIDENTIFIERS': ('id-classes', ''),
'PRIVATENAMES': ('ref/atom-identifiers', ''), 'PRIVATENAMES': ('atom-identifiers', ''),
'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'), 'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS '
'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
'TUPLES': 'SEQUENCES', 'TUPLES': 'SEQUENCES',
'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'), 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'),
'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'), 'LISTS': ('typesseq-mutable', 'LISTLITERALS'),
'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'), 'LISTLITERALS': ('lists', 'LISTS LITERALS'),
'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'), 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'),
'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'), 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'),
'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'), 'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'),
'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'), 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr '
'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'), 'ATTRIBUTEMETHODS'),
'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'), 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'),
'CALLS': ('ref/calls', 'EXPRESSIONS'), 'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'),
'POWER': ('ref/power', 'EXPRESSIONS'), 'CALLS': ('calls', 'EXPRESSIONS'),
'UNARY': ('ref/unary', 'EXPRESSIONS'), 'POWER': ('power', 'EXPRESSIONS'),
'BINARY': ('ref/binary', 'EXPRESSIONS'), 'UNARY': ('unary', 'EXPRESSIONS'),
'SHIFTING': ('ref/shifting', 'EXPRESSIONS'), 'BINARY': ('binary', 'EXPRESSIONS'),
'BITWISE': ('ref/bitwise', 'EXPRESSIONS'), 'SHIFTING': ('shifting', 'EXPRESSIONS'),
'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'), 'BITWISE': ('bitwise', 'EXPRESSIONS'),
'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'), 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'),
'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'),
'ASSERTION': 'assert', 'ASSERTION': 'assert',
'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'), 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'),
'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'), 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'),
'DELETION': 'del', 'DELETION': 'del',
'PRINTING': 'print', 'PRINTING': 'print',
'RETURNING': 'return', 'RETURNING': 'return',
'IMPORTING': 'import', 'IMPORTING': 'import',
'CONDITIONAL': 'if', 'CONDITIONAL': 'if',
'LOOPING': ('ref/compound', 'for while break continue'), 'LOOPING': ('compound', 'for while break continue'),
'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'), 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'),
'DEBUGGING': ('lib/module-pdb', 'pdb'), 'DEBUGGING': ('debugger', 'pdb'),
'CONTEXTMANAGERS': ('ref/context-managers', 'with'), 'CONTEXTMANAGERS': ('context-managers', 'with'),
} }
def __init__(self, input, output): def __init__(self, input, output):
self.input = input self.input = input
self.output = output self.output = output
self.docdir = None
execdir = os.path.dirname(sys.executable)
homedir = os.environ.get('PYTHONHOME')
join = os.path.join
for dir in [os.environ.get('PYTHONDOCS'),
homedir and os.path.join(homedir, 'doc'),
join(execdir, 'doc'), # for Windows
join(sys.prefix, 'doc/python-docs-' + split(sys.version)[0]),
join(sys.prefix, 'doc/python-' + split(sys.version)[0]),
join(sys.prefix, 'doc/python-docs-' + sys.version[:3]),
join(sys.prefix, 'doc/python-' + sys.version[:3]),
join(sys.prefix, 'Resources/English.lproj/Documentation')]:
if dir and os.path.isdir(join(dir, 'lib')):
self.docdir = dir
break
if dir and os.path.isdir(join(dir, 'html', 'lib')):
self.docdir = join(dir, 'html')
break
def __repr__(self): def __repr__(self):
if inspect.stack()[1][3] == '?': if inspect.stack()[1][3] == '?':
...@@ -1761,14 +1768,12 @@ Here is a list of available topics. Enter any topic name to get more help. ...@@ -1761,14 +1768,12 @@ Here is a list of available topics. Enter any topic name to get more help.
self.list(self.topics.keys()) self.list(self.topics.keys())
def showtopic(self, topic): def showtopic(self, topic):
if not self.docdir: try:
import pydoc_topics
except ImportError:
self.output.write(''' self.output.write('''
Sorry, topic and keyword documentation is not available because the Python Sorry, topic and keyword documentation is not available because the
HTML documentation files could not be found. If you have installed them, module "pydoc_topics" could not be found.
please set the environment variable PYTHONDOCS to indicate their location.
On the Microsoft Windows operating system, the files can be built by
running "hh -decompile . PythonNN.chm" in the C:\PythonNN\Doc> directory.
''') ''')
return return
target = self.topics.get(topic, self.keywords.get(topic)) target = self.topics.get(topic, self.keywords.get(topic))
...@@ -1778,31 +1783,15 @@ running "hh -decompile . PythonNN.chm" in the C:\PythonNN\Doc> directory. ...@@ -1778,31 +1783,15 @@ running "hh -decompile . PythonNN.chm" in the C:\PythonNN\Doc> directory.
if type(target) is type(''): if type(target) is type(''):
return self.showtopic(target) return self.showtopic(target)
filename, xrefs = target label, xrefs = target
filename = self.docdir + '/' + filename + '.html'
try: try:
file = open(filename) doc = pydoc_topics.topics[label]
except: except KeyError:
self.output.write('could not read docs from %s\n' % filename) self.output.write('no documentation found for %s\n' % repr(topic))
return return
pager(strip(doc) + '\n')
divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
file.close()
import htmllib, formatter, StringIO
buffer = StringIO.StringIO()
parser = htmllib.HTMLParser(
formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
parser.start_table = parser.do_p
parser.end_table = lambda parser=parser: parser.do_p({})
parser.start_tr = parser.do_br
parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
parser.feed(document)
buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
pager(' ' + strip(buffer) + '\n')
if xrefs: if xrefs:
import StringIO, formatter
buffer = StringIO.StringIO() buffer = StringIO.StringIO()
formatter.DumbWriter(buffer).send_flowing_data( formatter.DumbWriter(buffer).send_flowing_data(
'Related help topics: ' + join(split(xrefs), ', ') + '\n') 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment