Merged revisions 64808,65013,65076,65100-65101,65119,65121-65123 via svnmerge from

svn+ssh://pythondev@svn.python.org/doctools/branches/0.4.x

........
  r64808 | georg.brandl | 2008-07-08 21:39:33 +0200 (Tue, 08 Jul 2008) | 2 lines

  Allow relocation of source and doctree dir.
........
  r65013 | georg.brandl | 2008-07-16 15:25:30 +0200 (Wed, 16 Jul 2008) | 2 lines

  Remove curious quote.
........
  r65076 | georg.brandl | 2008-07-17 22:43:01 +0200 (Thu, 17 Jul 2008) | 2 lines

  Add a test for sphinx.quickstart.
........
  r65100 | georg.brandl | 2008-07-18 14:41:54 +0200 (Fri, 18 Jul 2008) | 2 lines

  Fix phony targets.
........
  r65101 | georg.brandl | 2008-07-18 14:55:03 +0200 (Fri, 18 Jul 2008) | 2 lines

  Fix problems in "make check".
........
  r65119 | georg.brandl | 2008-07-18 23:06:42 +0200 (Fri, 18 Jul 2008) | 2 lines

  Emit a more precise error message in autodoc.
........
  r65121 | georg.brandl | 2008-07-18 23:41:35 +0200 (Fri, 18 Jul 2008) | 2 lines

  Warn if a toctree-included document doesn't contain a title.
........
  r65122 | georg.brandl | 2008-07-18 23:51:28 +0200 (Fri, 18 Jul 2008) | 2 lines

  Don't use \samp{} for code with whitespaces, only for :samp:`code`.
........
  r65123 | georg.brandl | 2008-07-19 00:49:46 +0200 (Sat, 19 Jul 2008) | 2 lines

  Put inheritance info always on its own line.
........
This commit is contained in:
Georg Brandl 2008-07-18 22:55:36 +00:00
parent 7cf390aa9b
commit ae2570c269
17 changed files with 203 additions and 34 deletions

View File

@ -2,7 +2,7 @@ PYTHON ?= python
export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx
.PHONY: all check clean clean-pyc pylint reindent testserver .PHONY: all check clean clean-pyc clean-patchfiles pylint reindent test
all: clean-pyc check all: clean-pyc check

4
TODO
View File

@ -1,6 +1,10 @@
Sphinx TODO Sphinx TODO
=========== ===========
- specify node visit functions when adding nodes to app
- allow extensions to add static files
- decide which static files to include
- verbose option
- remove redundant <ul>s in tocs - remove redundant <ul>s in tocs
- autoattribute in autodoc - autoattribute in autodoc
- range and object options for literalinclude - range and object options for literalinclude

View File

@ -29,7 +29,7 @@ def usage(argv, msg=None):
print >>sys.stderr print >>sys.stderr
print >>sys.stderr, """\ print >>sys.stderr, """\
Sphinx v%s Sphinx v%s
Usage: %s [options] sourcedir outdir [filenames...]" Usage: %s [options] sourcedir outdir [filenames...]
Options: -b <builder> -- builder to use; default is html Options: -b <builder> -- builder to use; default is html
-a -- write all files; default is to only write new and changed files -a -- write all files; default is to only write new and changed files
-E -- don't use a saved environment, always read all files -E -- don't use a saved environment, always read all files

View File

@ -211,7 +211,7 @@ class Builder(object):
warnings = [] warnings = []
self.env.set_warnfunc(warnings.append) self.env.set_warnfunc(warnings.append)
self.info(bold('updating environment: '), nonl=1) self.info(bold('updating environment: '), nonl=1)
iterator = self.env.update(self.config, self.app) iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app)
# the first item in the iterator is a summary message # the first item in the iterator is a summary message
self.info(iterator.next()) self.info(iterator.next())
for docname in self.status_iterator(iterator, 'reading... ', purple): for docname in self.status_iterator(iterator, 'reading... ', purple):

View File

@ -396,7 +396,7 @@ class BuildEnvironment:
return added, changed, removed return added, changed, removed
def update(self, config, app=None): def update(self, config, srcdir, doctreedir, app=None):
"""(Re-)read all files new or changed since last update. Yields a summary """(Re-)read all files new or changed since last update. Yields a summary
and then docnames as it processes them. Store all environment docnames and then docnames as it processes them. Store all environment docnames
in the canonical format (ie using SEP as a separator in place of in the canonical format (ie using SEP as a separator in place of
@ -416,6 +416,9 @@ class BuildEnvironment:
break break
else: else:
msg = '' msg = ''
# the source and doctree directories may have been relocated
self.srcdir = srcdir
self.doctreedir = doctreedir
self.find_files(config) self.find_files(config)
added, changed, removed = self.get_outdated_files(config_changed) added, changed, removed = self.get_outdated_files(config_changed)
msg += '%s added, %s changed, %s removed' % (len(added), len(changed), msg += '%s added, %s changed, %s removed' % (len(added), len(changed),
@ -807,10 +810,15 @@ class BuildEnvironment:
for includefile in includefiles: for includefile in includefiles:
try: try:
toc = self.tocs[includefile].deepcopy() toc = self.tocs[includefile].deepcopy()
if not toc.children:
# empty toc means: no titles will show up in the toctree
self.warn(docname, 'toctree contains reference to document '
'%r that doesn\'t have a title: no link will be '
'generated' % includefile)
except KeyError: except KeyError:
# this is raised if the included file does not exist # this is raised if the included file does not exist
self.warn(docname, 'toctree contains ref to nonexisting ' self.warn(docname, 'toctree contains reference to nonexisting '
'file %r' % includefile) 'document %r' % includefile)
else: else:
# if titles_only is given, only keep the main title and # if titles_only is given, only keep the main title and
# sub-toctrees # sub-toctrees

View File

@ -355,10 +355,11 @@ def generate_rst(what, name, members, options, add_content, document, lineno,
modfile = None # e.g. for builtin and C modules modfile = None # e.g. for builtin and C modules
for part in objpath: for part in objpath:
todoc = getattr(todoc, part) todoc = getattr(todoc, part)
except (ImportError, AttributeError): except (ImportError, AttributeError), err:
warnings.append(document.reporter.warning( warnings.append(document.reporter.warning(
'autodoc can\'t import/find %s %r, check your spelling ' 'autodoc can\'t import/find %s %r, it reported error: "%s",'
'and sys.path' % (what, str(fullname)), line=lineno)) 'please check your spelling and sys.path' %
(what, str(fullname), err), line=lineno))
return warnings, result return warnings, result
# check __module__ of object if wanted (for members not given explicitly) # check __module__ of object if wanted (for members not given explicitly)
@ -416,6 +417,7 @@ def generate_rst(what, name, members, options, add_content, document, lineno,
u':class:`%s.%s`' % (b.__module__, b.__name__) u':class:`%s.%s`' % (b.__module__, b.__name__)
for b in todoc.__bases__] for b in todoc.__bases__]
result.append(indent + u' Bases: %s' % ', '.join(bases), '<autodoc>') result.append(indent + u' Bases: %s' % ', '.join(bases), '<autodoc>')
result.append(u'', '<autodoc>')
# the module directive doesn't have content # the module directive doesn't have content
if what != 'module': if what != 'module':

View File

@ -873,7 +873,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
content = self.encode(node.astext().strip()) content = self.encode(node.astext().strip())
if self.in_title: if self.in_title:
self.body.append(r'\texttt{%s}' % content) self.body.append(r'\texttt{%s}' % content)
elif re.search('[ \t\n]', content): elif node.has_key('role') and node['role'] == 'samp':
self.body.append(r'\samp{%s}' % content) self.body.append(r'\samp{%s}' % content)
else: else:
self.body.append(r'\code{%s}' % content) self.body.append(r'\code{%s}' % content)

View File

@ -16,6 +16,8 @@ from sphinx.util import make_filename
from sphinx.util.console import purple, bold, red, nocolor from sphinx.util.console import purple, bold, red, nocolor
PROMPT_PREFIX = '> '
QUICKSTART_CONF = '''\ QUICKSTART_CONF = '''\
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
@ -176,7 +178,8 @@ htmlhelp_basename = '%(project_fn)sdoc'
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class [howto/manual]). # (source start file, target name, title, author, document class [howto/manual]).
latex_documents = [ latex_documents = [
('%(master)s', '%(project_fn)s.tex', '%(project)s Documentation', '%(author)s', 'manual'), ('%(master)s', '%(project_fn)s.tex', '%(project)s Documentation',
'%(author)s', 'manual'),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@ -231,7 +234,8 @@ PAPER =
# Internal variables. # Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d %(rbuilddir)s/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) %(rsrcdir)s ALLSPHINXOPTS = -d %(rbuilddir)s/doctrees $(PAPEROPT_$(PAPER)) \
$(SPHINXOPTS) %(rsrcdir)s
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck .PHONY: help clean html web pickle htmlhelp latex changes linkcheck
@ -333,9 +337,9 @@ def ok(x):
def do_prompt(d, key, text, default=None, validator=nonempty): def do_prompt(d, key, text, default=None, validator=nonempty):
while True: while True:
if default: if default:
prompt = purple('> %s [%s]: ' % (text, default)) prompt = purple(PROMPT_PREFIX + '%s [%s]: ' % (text, default))
else: else:
prompt = purple('> ' + text + ': ') prompt = purple(PROMPT_PREFIX + text + ': ')
x = raw_input(prompt) x = raw_input(prompt)
if default and not x: if default and not x:
x = default x = default
@ -364,7 +368,7 @@ Enter the root path for documentation.'''
You have two options for placing the build directory for Sphinx output. You have two options for placing the build directory for Sphinx output.
Either, you use a directory ".build" within the root path, or you separate Either, you use a directory ".build" within the root path, or you separate
"source" and "build" directories within the root path.''' "source" and "build" directories within the root path.'''
do_prompt(d, 'sep', 'Separate source and build directories (y/n)', 'n', do_prompt(d, 'sep', 'Separate source and build directories (y/N)', 'n',
boolean) boolean)
print ''' print '''
Inside the root directory, two more directories will be created; ".templates" Inside the root directory, two more directories will be created; ".templates"
@ -399,14 +403,14 @@ document is a custom template, you can also set this to another filename.'''
print ''' print '''
Please indicate if you want to use one of the following Sphinx extensions:''' Please indicate if you want to use one of the following Sphinx extensions:'''
do_prompt(d, 'ext_autodoc', 'autodoc: automatically insert docstrings ' do_prompt(d, 'ext_autodoc', 'autodoc: automatically insert docstrings '
'from modules (y/n)', 'n', boolean) 'from modules (y/N)', 'n', boolean)
do_prompt(d, 'ext_doctest', 'doctest: automatically test code snippets ' do_prompt(d, 'ext_doctest', 'doctest: automatically test code snippets '
'in doctest blocks (y/n)', 'n', boolean) 'in doctest blocks (y/N)', 'n', boolean)
print ''' print '''
If you are under Unix, a Makefile can be generated for you so that you If you are under Unix, a Makefile can be generated for you so that you
only have to run e.g. `make html' instead of invoking sphinx-build only have to run e.g. `make html' instead of invoking sphinx-build
directly.''' directly.'''
do_prompt(d, 'makefile', 'Create Makefile? (y/n)', do_prompt(d, 'makefile', 'Create Makefile? (Y/n)',
os.name == 'posix' and 'y' or 'n', boolean) os.name == 'posix' and 'y' or 'n', boolean)
d['project_fn'] = make_filename(d['project']) d['project_fn'] = make_filename(d['project'])

View File

@ -195,17 +195,17 @@ _litvar_re = re.compile('{([^}]+)}')
def emph_literal_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): def emph_literal_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
text = utils.unescape(text) text = utils.unescape(text)
retnodes = []
pos = 0 pos = 0
retnode = nodes.literal(role=typ)
for m in _litvar_re.finditer(text): for m in _litvar_re.finditer(text):
if m.start() > pos: if m.start() > pos:
txt = text[pos:m.start()] txt = text[pos:m.start()]
retnodes.append(nodes.literal(txt, txt)) retnode += nodes.Text(txt, txt)
retnodes.append(nodes.emphasis('', '', nodes.literal(m.group(1), m.group(1)))) retnode += nodes.emphasis(m.group(1), m.group(1))
pos = m.end() pos = m.end()
if pos < len(text): if pos < len(text):
retnodes.append(nodes.literal(text[pos:], text[pos:])) retnode += nodes.Text(text[pos:], text[pos:])
return retnodes, [] return [retnode], []
specific_docroles = { specific_docroles = {

View File

@ -250,7 +250,7 @@ def patfilter(names, pat):
Adapted from fnmatch module. Adapted from fnmatch module.
""" """
result = [] result = []
if not pat in _pat_cache: if pat not in _pat_cache:
_pat_cache[pat] = re.compile(_translate_pattern(pat)) _pat_cache[pat] = re.compile(_translate_pattern(pat))
match = _pat_cache[pat].match match = _pat_cache[pat].match
return filter(match, names) return filter(match, names)

View File

@ -38,6 +38,9 @@ def print_and_backspace(text, func):
def nocolor(): def nocolor():
codes.clear() codes.clear()
def coloron():
codes.update(_orig_codes)
def colorize(name, text): def colorize(name, text):
return codes.get(name, '') + text + codes.get('reset', '') return codes.get(name, '') + text + codes.get('reset', '')
@ -73,5 +76,7 @@ for i, (dark, light) in enumerate(_colors):
codes[dark] = '\x1b[%im' % (i+30) codes[dark] = '\x1b[%im' % (i+30)
codes[light] = '\x1b[%i;01m' % (i+30) codes[light] = '\x1b[%i;01m' % (i+30)
_orig_codes = codes.copy()
for _name in codes: for _name in codes:
create_color_func(_name) create_color_func(_name)

144
tests/test_quickstart.py Normal file
View File

@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
"""
test_quickstart
~~~~~~~~~~~~~~~
Test the sphinx.quickstart module.
:copyright: 2008 by Georg Brandl.
:license: BSD.
"""
import sys
import time
import __builtin__
from util import *
from sphinx import quickstart as qs
from sphinx.util.console import nocolor, coloron
def setup_module():
nocolor()
def mock_raw_input(answers, needanswer=False):
called = set()
def raw_input(prompt):
if prompt in called:
raise AssertionError('answer for %r missing and no default '
'present' % prompt)
called.add(prompt)
for question in answers:
if prompt.startswith(qs.PROMPT_PREFIX + question):
return answers[question]
if needanswer:
raise AssertionError('answer for %r missing' % prompt)
return ''
return raw_input
def teardown_module():
qs.raw_input = __builtin__.raw_input
coloron()
def test_do_prompt():
d = {}
answers = {
'Q2': 'v2',
'Q3': 'v3',
'Q4': 'yes',
'Q5': 'no',
'Q6': 'foo',
}
qs.raw_input = mock_raw_input(answers)
try:
qs.do_prompt(d, 'k1', 'Q1')
except AssertionError:
assert 'k1' not in d
else:
assert False, 'AssertionError not raised'
qs.do_prompt(d, 'k1', 'Q1', default='v1')
assert d['k1'] == 'v1'
qs.do_prompt(d, 'k3', 'Q3', default='v3_default')
assert d['k3'] == 'v3'
qs.do_prompt(d, 'k2', 'Q2')
assert d['k2'] == 'v2'
qs.do_prompt(d, 'k4', 'Q4', validator=qs.boolean)
assert d['k4'] == 'yes'
qs.do_prompt(d, 'k5', 'Q5', validator=qs.boolean)
assert d['k5'] == 'no'
raises(AssertionError, qs.do_prompt, d, 'k6', 'Q6', validator=qs.boolean)
@with_tempdir
def test_quickstart_defaults(tempdir):
answers = {
'Root path': tempdir,
'Project name': 'Sphinx Test',
'Author name': 'Georg Brandl',
'Project version': '0.1',
}
qs.raw_input = mock_raw_input(answers)
qs.inner_main([])
conffile = tempdir / 'conf.py'
assert conffile.isfile()
ns = {}
execfile(conffile, ns)
assert ns['extensions'] == []
assert ns['templates_path'] == ['.templates']
assert ns['source_suffix'] == '.rst'
assert ns['master_doc'] == 'index'
assert ns['project'] == 'Sphinx Test'
assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y')
assert ns['version'] == '0.1'
assert ns['release'] == '0.1'
assert ns['html_static_path'] == ['.static']
assert ns['latex_documents'] == [
('index', 'SphinxTest.tex', 'Sphinx Test Documentation',
'Georg Brandl', 'manual')]
assert (tempdir / '.static').isdir()
assert (tempdir / '.templates').isdir()
assert (tempdir / 'index.rst').isfile()
assert (tempdir / 'Makefile').isfile()
@with_tempdir
def test_quickstart_all_answers(tempdir):
answers = {
'Root path': tempdir,
'Separate source and build': 'y',
'Name prefix for templates': '_',
'Project name': 'Sphinx Test',
'Author name': 'Georg Brandl',
'Project version': '0.1',
'Project release': '0.1.1',
'Source file suffix': '.txt',
'Name of your master document': 'contents',
'autodoc': 'y',
'doctest': 'yes',
'Create Makefile': 'no',
}
qs.raw_input = mock_raw_input(answers, needanswer=True)
qs.inner_main([])
conffile = tempdir / 'source' / 'conf.py'
assert conffile.isfile()
ns = {}
execfile(conffile, ns)
assert ns['extensions'] == ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
assert ns['templates_path'] == ['_templates']
assert ns['source_suffix'] == '.txt'
assert ns['master_doc'] == 'contents'
assert ns['project'] == 'Sphinx Test'
assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y')
assert ns['version'] == '0.1'
assert ns['release'] == '0.1.1'
assert ns['html_static_path'] == ['_static']
assert ns['latex_documents'] == [
('contents', 'SphinxTest.tex', 'Sphinx Test Documentation',
'Georg Brandl', 'manual')]
assert (tempdir / 'build').isdir()
assert (tempdir / 'source' / '_static').isdir()
assert (tempdir / 'source' / '_templates').isdir()
assert (tempdir / 'source' / 'contents.txt').isfile()

View File

@ -19,7 +19,7 @@ from path import path
__all__ = [ __all__ = [
'raises', 'raises_msg', 'raises', 'raises_msg',
'ErrorOutput', 'TestApp', 'ErrorOutput', 'TestApp',
'with_tempdir', 'write_file', 'path', 'with_tempdir', 'write_file',
] ]

View File

@ -7,7 +7,7 @@
Make sure each Python file has a correct file header Make sure each Python file has a correct file header
including copyright and license information. including copyright and license information.
:copyright: 2006-2007 by Georg Brandl. :copyright: 2006-2008 by Georg Brandl.
:license: GNU GPL, see LICENSE for more details. :license: GNU GPL, see LICENSE for more details.
""" """
@ -57,15 +57,17 @@ def check_style_and_encoding(fn, lines):
for lno, line in enumerate(lines): for lno, line in enumerate(lines):
if len(line) > 90: if len(line) > 90:
yield lno+1, "line too long" yield lno+1, "line too long"
if lno < 2:
co = coding_re.search(line)
if co:
encoding = co.group(1)
if line.strip().startswith('#'):
continue
m = not_ix_re.search(line) m = not_ix_re.search(line)
if m: if m:
yield lno+1, '"' + m.group() + '"' yield lno+1, '"' + m.group() + '"'
if is_const_re.search(line): if is_const_re.search(line):
yield lno+1, 'using == None/True/False' yield lno+1, 'using == None/True/False'
if lno < 2:
co = coding_re.search(line)
if co:
encoding = co.group(1)
try: try:
line.decode(encoding) line.decode(encoding)
except UnicodeDecodeError, err: except UnicodeDecodeError, err: