From 961b2fb2253039778e47d35ec4dadcb3a77b64b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tuomas=20R=C3=A4s=C3=A4nen?= Date: Tue, 5 May 2009 01:03:05 +0300 Subject: [PATCH 001/744] Fixed long lines and trailing whitespaces. --- sphinx/writers/html.py | 9 ++++----- tests/coverage.py | 31 ++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 79b2fcc55..313eb4182 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -470,12 +470,11 @@ class HTMLTranslator(BaseTranslator): if close_tag.startswith('\u00B6' % - _('Permalink to this headline')) + 'Permalink to this headline') elif close_tag.startswith('\u00B6' % - _('Permalink to this headline')) - + self.body.append(u'\u00B6' % + 'Permalink to this headline') BaseTranslator.depart_title(self, node) def unknown_visit(self, node): diff --git a/tests/coverage.py b/tests/coverage.py index 117fd4f29..95f6f8440 100755 --- a/tests/coverage.py +++ b/tests/coverage.py @@ -362,8 +362,9 @@ class coverage: settings[o[2:]] = 1 elif o[2:] + '=' in long_opts: settings[o[2:]+'='] = a - else: #pragma: no cover - pass # Can't get here, because getopt won't return anything unknown. + else: #pragma: no cover + # Can't get here, because getopt won't return anything unknown. + pass if settings.get('help'): help_fn() @@ -645,20 +646,23 @@ class coverage: def find_docstring_pass_pair(self, tree, spots): for i in range(1, len(tree)): - if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]): + if (self.is_string_constant(tree[i]) + and self.is_pass_stmt(tree[i+1])): first_line = self.first_line_of_tree(tree[i]) last_line = self.last_line_of_tree(tree[i+1]) self.record_multiline(spots, first_line, last_line) def is_string_constant(self, tree): try: - return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt + return (tree[0] == symbol.stmt + and tree[1][1][1][0] == symbol.expr_stmt) except: return False def is_pass_stmt(self, tree): try: - return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt + return (tree[0] == symbol.stmt + and tree[1][1][1][0] == symbol.pass_stmt) except: return False @@ -761,7 +765,7 @@ class coverage: pairs = [] while i < len(statements) and j < len(lines): if statements[i] == lines[j]: - if start == None: + if start is None: start = lines[j] end = lines[j] j = j + 1 @@ -831,7 +835,8 @@ class coverage: def morf_name_compare(self, x, y): return cmp(self.morf_name(x), self.morf_name(y)) - def report(self, morfs, show_missing=1, ignore_errors=0, file=None, omit_prefixes=[]): + def report(self, morfs, show_missing=1, ignore_errors=0, file=None, + omit_prefixes=[]): if not isinstance(morfs, types.ListType): morfs = [morfs] # On windows, the shell doesn't expand wildcards. Do it here. @@ -898,19 +903,23 @@ class coverage: blank_re = re.compile(r"\s*(#|$)") else_re = re.compile(r"\s*else\s*:\s*(#|$)") - def annotate(self, morfs, directory=None, ignore_errors=0, omit_prefixes=[]): + def annotate(self, morfs, directory=None, ignore_errors=0, + omit_prefixes=[]): morfs = self.filter_by_prefix(morfs, omit_prefixes) for morf in morfs: try: - filename, statements, excluded, missing, _ = self.analysis2(morf) - self.annotate_file(filename, statements, excluded, missing, directory) + (filename, statements, excluded, + missing, _) = self.analysis2(morf) + self.annotate_file(filename, statements, excluded, missing, + directory) except KeyboardInterrupt: raise except: if not ignore_errors: raise - def annotate_file(self, filename, statements, excluded, missing, directory=None): + def annotate_file(self, filename, statements, excluded, missing, + directory=None): source = open(filename, 'r') if directory: dest_file = os.path.join(directory, From 9eb45eec96918f383bf373f4901133c26e574ea0 Mon Sep 17 00:00:00 2001 From: David Winslow Date: Mon, 29 Jun 2009 19:22:56 -0400 Subject: [PATCH 002/744] Use system line separators instead of a literal newline. --- sphinx/writers/text.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index ceca5c8a0..909c71ce8 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -10,6 +10,7 @@ """ import re +import os import textwrap from docutils import nodes, writers @@ -97,7 +98,7 @@ class TextTranslator(nodes.NodeVisitor): self.new_state(0) def depart_document(self, node): self.end_state() - self.body = '\n'.join(line and (' '*indent + line) + self.body = os.linesep.join(line and (' '*indent + line) for indent, lines in self.states[0] for line in lines) # XXX header/footer? @@ -231,7 +232,7 @@ class TextTranslator(nodes.NodeVisitor): def visit_desc_content(self, node): self.new_state() - self.add_text('\n') + self.add_text(os.linesep) def depart_desc_content(self, node): self.end_state() @@ -257,7 +258,7 @@ class TextTranslator(nodes.NodeVisitor): lastname = production['tokenname'] else: self.add_text('%s ' % (' '*len(lastname))) - self.add_text(production.astext() + '\n') + self.add_text(production.astext() + os.linesep) self.end_state(wrap=False) raise nodes.SkipNode @@ -357,7 +358,7 @@ class TextTranslator(nodes.NodeVisitor): 'not implemented.') self.new_state(0) def depart_entry(self, node): - text = '\n'.join('\n'.join(x[1]) for x in self.states.pop()) + text = os.linesep.join(os.linesep.join(x[1]) for x in self.states.pop()) self.stateindent.pop() self.table[-1].append(text) @@ -393,7 +394,7 @@ class TextTranslator(nodes.NodeVisitor): for width in realwidths: out.append(char * (width+2)) out.append('+') - self.add_text(''.join(out) + '\n') + self.add_text(''.join(out) + os.linesep) def writerow(row): lines = map(None, *row) @@ -405,7 +406,7 @@ class TextTranslator(nodes.NodeVisitor): else: out.append(' ' * (realwidths[i] + 2)) out.append('|') - self.add_text(''.join(out) + '\n') + self.add_text(''.join(out) + os.linesep) for i, row in enumerate(fmted_rows): if separator and i == separator: From 12ecec3f5753881d7bd0509f057dd97353ae38cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 29 Apr 2010 18:06:22 +0200 Subject: [PATCH 003/744] Show python 3.x incompatibilities which cannot be trivially fixed --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7057a7152..682f03666 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PYTHON ?= python +PYTHON ?= python -3 export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx From e97122236d9e908ae6b9806858ad09c1d70cbcc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 29 Apr 2010 18:08:44 +0200 Subject: [PATCH 004/744] Replace .has_key() calls with the in-operator --- sphinx/writers/latex.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 5674b388c..360cf40c9 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -314,7 +314,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # ... and all others are the appendices self.body.append(u'\n\\appendix\n') self.first_document = -1 - if node.has_key('docname'): + if 'docname' in node: self.body.append(self.hypertarget(':doc')) # "- 1" because the level is increased before the title is visited self.sectionlevel = self.top_sectionlevel - 1 @@ -694,7 +694,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.rowcount += 1 def visit_entry(self, node): - if node.has_key('morerows') or node.has_key('morecols'): + if 'morerows' in node or 'morecols' in node: raise UnsupportedError('%s:%s: column or row spanning cells are ' 'not yet implemented.' % (self.curfilestack[-1], node.line or '')) @@ -751,7 +751,7 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_term(self, node): ctx = '}] \\leavevmode' - if node.has_key('ids') and node['ids']: + if node.get('ids'): ctx += self.hypertarget(node['ids'][0]) self.body.append('\\item[{') self.context.append(ctx) @@ -833,20 +833,20 @@ class LaTeXTranslator(nodes.NodeVisitor): post = [] include_graphics_options = [] is_inline = self.is_inline(node) - if attrs.has_key('scale'): + if 'scale' in attrs: # Could also be done with ``scale`` option to # ``\includegraphics``; doing it this way for consistency. pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,)) post.append('}') - if attrs.has_key('width'): + if 'width' in attrs: w = self.latex_image_length(attrs['width']) if w: include_graphics_options.append('width=%s' % w) - if attrs.has_key('height'): + if 'height' in attrs: h = self.latex_image_length(attrs['height']) if h: include_graphics_options.append('height=%s' % h) - if attrs.has_key('align'): + if 'align' in attrs: align_prepost = { # By default latex aligns the top of an image. (1, 'top'): ('', ''), @@ -887,13 +887,13 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def visit_figure(self, node): - if node.has_key('width') and node.get('align', '') in ('left', 'right'): + if 'width' in node and node.get('align', '') in ('left', 'right'): self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' % (node['align'] == 'right' and 'r' or 'l', node['width'])) self.context.append('\\end{wrapfigure}\n') else: - if (not node.attributes.has_key('align') or + if (not 'align' in node.attributes or node.attributes['align'] == 'center'): # centering does not add vertical space like center. align = '\n\\centering' @@ -1154,7 +1154,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.no_contractions -= 1 if self.in_title: self.body.append(r'\texttt{%s}' % content) - elif node.has_key('role') and node['role'] == 'samp': + elif node.get('role') == 'samp': self.body.append(r'\samp{%s}' % content) else: self.body.append(r'\code{%s}' % content) @@ -1183,10 +1183,10 @@ class LaTeXTranslator(nodes.NodeVisitor): code = self.verbatim.rstrip('\n') lang = self.hlsettingstack[-1][0] linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1 - if node.has_key('language'): + if 'language' in node: # code-block directives lang = node['language'] - if node.has_key('linenos'): + if 'linenos' in node: linenos = node['linenos'] hlcode = self.highlighter.highlight_block(code, lang, linenos) # workaround for Unicode issue From 4adbc983503072918b058d6bfa6bca4fd64b86aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 29 Apr 2010 19:42:17 +0200 Subject: [PATCH 005/744] Removed map(None, ...) usage --- sphinx/writers/text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 98528d5ba..b28b23792 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -390,7 +390,7 @@ class TextTranslator(nodes.NodeVisitor): self.add_text(''.join(out) + '\n') def writerow(row): - lines = map(None, *row) + lines = zip(*row) for line in lines: out = ['|'] for i, cell in enumerate(line): From 21376f21e7cee76761fccef6874c88c1b3f79e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 29 Apr 2010 20:34:08 +0200 Subject: [PATCH 006/744] Don't use execfile() anymore --- sphinx/config.py | 6 +++++- tests/test_quickstart.py | 12 ++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 12c2a04ba..f76d330ac 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -165,7 +165,11 @@ class Config(object): try: try: os.chdir(dirname) - execfile(config['__file__'], config) + try: + f = open(config_file, 'U') + exec f in config + finally: + f.close() except SyntaxError, err: raise ConfigError('There is a syntax error in your ' 'configuration file: ' + str(err)) diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index cb40d27cf..34c54f95a 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -85,7 +85,11 @@ def test_quickstart_defaults(tempdir): conffile = tempdir / 'conf.py' assert conffile.isfile() ns = {} - execfile(conffile, ns) + try: + f = open(conffile, 'U') + exec f in ns + finally: + f.close() assert ns['extensions'] == [] assert ns['templates_path'] == ['_templates'] assert ns['source_suffix'] == '.rst' @@ -138,7 +142,11 @@ def test_quickstart_all_answers(tempdir): conffile = tempdir / 'source' / 'conf.py' assert conffile.isfile() ns = {} - execfile(conffile, ns) + try: + f = open(conffile, 'U') + exec f in ns + finally: + f.close() assert ns['extensions'] == ['sphinx.ext.autodoc', 'sphinx.ext.doctest'] assert ns['templates_path'] == ['.templates'] assert ns['source_suffix'] == '.txt' From 47557af776de7772da88a5d3ceaad62bf9edaef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Fri, 30 Apr 2010 12:32:42 +0200 Subject: [PATCH 007/744] Make sphinx.domains.cpp.DefExpr unhashable --- sphinx/domains/cpp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 4dac89253..90c3533ef 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -132,6 +132,8 @@ class DefExpr(object): def __ne__(self, other): return not self.__eq__(other) + __hash__ = None + def clone(self): """Close a definition expression node""" return deepcopy(self) From f6bf9b13ff40ae8dfbc9afe19db0da3fcbac8f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 1 May 2010 19:17:52 +0200 Subject: [PATCH 008/744] Fixed issue #1 --- sphinx/environment.py | 3 ++- sphinx/pycode/pgen2/tokenize.py | 4 +++- utils/reindent.py | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 5edcb4d90..fa8460cb3 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1488,8 +1488,9 @@ class BuildEnvironment: i += 1 # group the entries by letter - def keyfunc2((k, v), letters=string.ascii_uppercase + '_'): + def keyfunc2(item, letters=string.ascii_uppercase + '_'): # hack: mutating the subitems dicts to a list in the keyfunc + k, v = item v[1] = sorted((si, se) for (si, (se, void)) in v[1].iteritems()) # now calculate the key letter = k[0].upper() diff --git a/sphinx/pycode/pgen2/tokenize.py b/sphinx/pycode/pgen2/tokenize.py index 4489db898..7ad9f012c 100644 --- a/sphinx/pycode/pgen2/tokenize.py +++ b/sphinx/pycode/pgen2/tokenize.py @@ -143,7 +143,9 @@ class TokenError(Exception): pass class StopTokenizing(Exception): pass -def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing +def printtoken(type, token, scell, ecell, line): # for testing + srow, scol = scell + erow, ecol = ecell print "%d,%d-%d,%d:\t%s\t%s" % \ (srow, scol, erow, ecol, tok_name[type], repr(token)) diff --git a/utils/reindent.py b/utils/reindent.py index c499f671e..bcb6b4343 100755 --- a/utils/reindent.py +++ b/utils/reindent.py @@ -244,12 +244,13 @@ class Reindenter: return line # Line-eater for tokenize. - def tokeneater(self, type, token, (sline, scol), end, line, + def tokeneater(self, type, token, scell, end, line, INDENT=tokenize.INDENT, DEDENT=tokenize.DEDENT, NEWLINE=tokenize.NEWLINE, COMMENT=tokenize.COMMENT, NL=tokenize.NL): + sline, scol = scell if type == NEWLINE: # A program statement, or ENDMARKER, will eventually follow, From eef0b0821d01c3eb4d26eb3a4f8a185b29df8c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 1 May 2010 19:18:31 +0200 Subject: [PATCH 009/744] Make sphinx.pycode.nodes.BaseNode unhashable --- sphinx/pycode/nodes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/pycode/nodes.py b/sphinx/pycode/nodes.py index e71846779..fc6eb93aa 100644 --- a/sphinx/pycode/nodes.py +++ b/sphinx/pycode/nodes.py @@ -29,6 +29,8 @@ class BaseNode(object): return NotImplemented return not self._eq(other) + __hash__ = None + def get_prev_sibling(self): """Return previous child in parent's children, or None.""" if self.parent is None: From 6651f67602a51795447fee8f6f8ac517f26a8f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 1 May 2010 19:19:24 +0200 Subject: [PATCH 010/744] Removed pre-2.3 workaround for booleans --- tests/path.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/path.py b/tests/path.py index ceb895f50..20deb0489 100644 --- a/tests/path.py +++ b/tests/path.py @@ -56,12 +56,6 @@ try: except AttributeError: pass -# Pre-2.3 workaround for booleans -try: - True, False -except NameError: - True, False = 1, 0 - # Pre-2.3 workaround for basestring. try: basestring From 4391e63b07c02d317617fc061256fd77eb0cb9b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 1 May 2010 20:26:05 +0200 Subject: [PATCH 011/744] Move open() calls out of the try block --- sphinx/config.py | 2 +- tests/test_quickstart.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index f76d330ac..c22a5ee74 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -165,8 +165,8 @@ class Config(object): try: try: os.chdir(dirname) + f = open(config_file, 'U') try: - f = open(config_file, 'U') exec f in config finally: f.close() diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 34c54f95a..71ca95a4d 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -85,8 +85,8 @@ def test_quickstart_defaults(tempdir): conffile = tempdir / 'conf.py' assert conffile.isfile() ns = {} + f = open(conffile, 'U') try: - f = open(conffile, 'U') exec f in ns finally: f.close() @@ -142,8 +142,8 @@ def test_quickstart_all_answers(tempdir): conffile = tempdir / 'source' / 'conf.py' assert conffile.isfile() ns = {} + f = open(conffile, 'U') try: - f = open(conffile, 'U') exec f in ns finally: f.close() From df236468781b3ef8dc33f31896fe64b7958587a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 6 May 2010 16:05:37 +0200 Subject: [PATCH 012/744] Added a file containing the changes made during GSoC. --- CHANGES.DasIch | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 CHANGES.DasIch diff --git a/CHANGES.DasIch b/CHANGES.DasIch new file mode 100644 index 000000000..856a5d87b --- /dev/null +++ b/CHANGES.DasIch @@ -0,0 +1,19 @@ +Changes +======= + +This file contains changes made by Daniel Neuhäuser, during the Google Summer +of Code 2010, to port Sphinx to Python 3.x. Changes are ordered descending by +date. + +May 1: - Removed deprecated tuple parameter unpacking. + - Removed a pre-2.3 workaround for booleans because this creates a + deprecation warning for 3.x, in which you can't assign values to + booleans. + - Moved :func:`open()` calls out of the try-blocks, which fixes revision + c577c25bd44b. + +April 30: Made :cls:`sphinx.domains.cpp.DefExpr` unhashable as described by the + documentation because classes in 3.x don't inherit ``__hash__`` if + they implement ``__eq__``. + +April 29: Removed several deprecated function/method calls. From 89cb0714b1041b77ceae5a777a18dd780c032bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 May 2010 20:34:19 +0200 Subject: [PATCH 013/744] Removed ez_setup which doesn't work with python3 and added use_2to3 for distribute --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 183fcceb1..2494851f5 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ A development egg can be found `here requires = ['Pygments>=0.8', 'Jinja2>=2.2', 'docutils>=0.5'] if sys.version_info < (2, 4): - print 'ERROR: Sphinx requires at least Python 2.4 to run.' + print('ERROR: Sphinx requires at least Python 2.4 to run.') sys.exit(1) if sys.version_info < (2, 5): @@ -198,4 +198,5 @@ setup( }, install_requires=requires, cmdclass=cmdclass, + use_2to3=True, ) From c09c74c3dfbbf166d60c80ce6fcb0a85f4df63a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 May 2010 20:53:49 +0200 Subject: [PATCH 014/744] Check for unicode before trying to decode input from raw_input. Also use codecs.open when writing non-binary files. sphinx-quickstart now works. --- sphinx/quickstart.py | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 884caca75..5820996f1 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -11,6 +11,7 @@ import sys, os, time from os import path +from codecs import open TERM_ENCODING = getattr(sys.stdin, 'encoding', None) @@ -659,17 +660,20 @@ def do_prompt(d, key, text, default=None, validator=nonempty): x = raw_input(prompt) if default and not x: x = default - if x.decode('ascii', 'replace').encode('ascii', 'replace') != x: - if TERM_ENCODING: - x = x.decode(TERM_ENCODING) - else: - print turquoise('* Note: non-ASCII characters entered ' - 'and terminal encoding unknown -- assuming ' - 'UTF-8 or Latin-1.') - try: - x = x.decode('utf-8') - except UnicodeDecodeError: - x = x.decode('latin1') + # in 3.x raw_input returns a unicode string, those have no decode + # method + if not isinstance(x, unicode): + if x.decode('ascii', 'replace').encode('ascii', 'replace') != x: + if TERM_ENCODING: + x = x.decode(TERM_ENCODING) + else: + print turquoise('* Note: non-ASCII characters entered ' + 'and terminal encoding unknown -- assuming ' + 'UTF-8 or Latin-1.') + try: + x = x.decode('utf-8') + except UnicodeDecodeError: + x = x.decode('latin1') try: x = validator(x) except ValidationError, err: @@ -834,28 +838,28 @@ directly.''' if d['ext_intersphinx']: conf_text += INTERSPHINX_CONFIG - f = open(path.join(srcdir, 'conf.py'), 'w') - f.write(conf_text.encode('utf-8')) + f = open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8') + f.write(conf_text) f.close() masterfile = path.join(srcdir, d['master'] + d['suffix']) - f = open(masterfile, 'w') - f.write((MASTER_FILE % d).encode('utf-8')) + f = open(masterfile, 'w', encoding='utf-8') + f.write(MASTER_FILE % d) f.close() if d['makefile']: d['rsrcdir'] = d['sep'] and 'source' or '.' d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build' # use binary mode, to avoid writing \r\n on Windows - f = open(path.join(d['path'], 'Makefile'), 'wb') - f.write((MAKEFILE % d).encode('utf-8')) + f = open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8') + f.write(MAKEFILE % d) f.close() if d['batchfile']: d['rsrcdir'] = d['sep'] and 'source' or '.' d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build' - f = open(path.join(d['path'], 'make.bat'), 'w') - f.write((BATCHFILE % d).encode('utf-8')) + f = open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8') + f.write(BATCHFILE % d) f.close() print From 66244b8432b9a594848d29e6d43f354e643f7706 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:03:42 +0200 Subject: [PATCH 015/744] Use codecs.open(). --- sphinx/quickstart.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 5820996f1..fe2b43a33 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -660,9 +660,8 @@ def do_prompt(d, key, text, default=None, validator=nonempty): x = raw_input(prompt) if default and not x: x = default - # in 3.x raw_input returns a unicode string, those have no decode - # method if not isinstance(x, unicode): + # for Python 2.x, try to get a Unicode string out of it if x.decode('ascii', 'replace').encode('ascii', 'replace') != x: if TERM_ENCODING: x = x.decode(TERM_ENCODING) @@ -838,12 +837,12 @@ directly.''' if d['ext_intersphinx']: conf_text += INTERSPHINX_CONFIG - f = open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8') + f = codecs.open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8') f.write(conf_text) f.close() masterfile = path.join(srcdir, d['master'] + d['suffix']) - f = open(masterfile, 'w', encoding='utf-8') + f = codecs.open(masterfile, 'w', encoding='utf-8') f.write(MASTER_FILE % d) f.close() @@ -851,14 +850,14 @@ directly.''' d['rsrcdir'] = d['sep'] and 'source' or '.' d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build' # use binary mode, to avoid writing \r\n on Windows - f = open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8') + f = codecs.open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8') f.write(MAKEFILE % d) f.close() if d['batchfile']: d['rsrcdir'] = d['sep'] and 'source' or '.' d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build' - f = open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8') + f = codecs.open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8') f.write(BATCHFILE % d) f.close() From 9c29a8cd9c7cd42a84e4243fa8cc20631c466c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 May 2010 21:47:52 +0200 Subject: [PATCH 016/744] Encode even bytestrings containing ascii tests, they are unicode in python3 --- sphinx/util/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 8d1298cd3..c1e8d25c9 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -168,11 +168,14 @@ def save_traceback(): """ exc = traceback.format_exc() fd, path = tempfile.mkstemp('.log', 'sphinx-err-') - os.write(fd, '# Sphinx version: %s\n' % sphinx.__version__) - os.write(fd, '# Docutils version: %s %s\n' % (docutils.__version__, - docutils.__version_details__)) - os.write(fd, '# Jinja2 version: %s\n' % jinja2.__version__) - os.write(fd, exc) + os.write(fd, + (('# Sphinx version: %s\n' + '# Docutils version: %s %s\n' + '# Jinja2 version: %s\n') % (sphinx.__version__, + docutils.__version__, + docutils.__version_details__, + jinja2.__version__)).encode('utf-8')) + os.write(fd, exc.encode('utf-8')) os.close(fd) return path From b81d428b89137a3f763bc2fd95de9a55bcc2e29a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:08:00 +0200 Subject: [PATCH 017/744] Take string constant out of function. --- sphinx/util/__init__.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index c1e8d25c9..2ef420ed1 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -162,19 +162,22 @@ def copy_static_entry(source, targetdir, builder, context={}, shutil.copytree(source, target) +_DEBUG_HEADER = '''\ +# Sphinx version: %s +# Docutils version: %s %s +# Jinja2 version: %s +''' + def save_traceback(): """ Save the current exception's traceback in a temporary file. """ exc = traceback.format_exc() fd, path = tempfile.mkstemp('.log', 'sphinx-err-') - os.write(fd, - (('# Sphinx version: %s\n' - '# Docutils version: %s %s\n' - '# Jinja2 version: %s\n') % (sphinx.__version__, - docutils.__version__, - docutils.__version_details__, - jinja2.__version__)).encode('utf-8')) + os.write(fd, (_DEBUG_HEADER % + (sphinx.__version__, + docutils.__version__, docutils.__version_details__, + jinja2.__version__)).encode('utf-8')) os.write(fd, exc.encode('utf-8')) os.close(fd) return path From 25b16f89e08be50c08fcc0f29ade8172d33cb347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 May 2010 22:00:15 +0200 Subject: [PATCH 018/744] Use code objects for exec statements instead of files --- sphinx/config.py | 3 ++- tests/test_quickstart.py | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index c22a5ee74..2ec769871 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -167,9 +167,10 @@ class Config(object): os.chdir(dirname) f = open(config_file, 'U') try: - exec f in config + code = compile(f.read(), config_file, 'exec') finally: f.close() + exec code in config except SyntaxError, err: raise ConfigError('There is a syntax error in your ' 'configuration file: ' + str(err)) diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 71ca95a4d..8acff5884 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -87,9 +87,10 @@ def test_quickstart_defaults(tempdir): ns = {} f = open(conffile, 'U') try: - exec f in ns + code = compile(f.read(), conffile, 'exec') finally: f.close() + exec code in ns assert ns['extensions'] == [] assert ns['templates_path'] == ['_templates'] assert ns['source_suffix'] == '.rst' @@ -144,9 +145,10 @@ def test_quickstart_all_answers(tempdir): ns = {} f = open(conffile, 'U') try: - exec f in ns + code = compile(f.read(), conffile, 'exec') finally: f.close() + exec code in ns assert ns['extensions'] == ['sphinx.ext.autodoc', 'sphinx.ext.doctest'] assert ns['templates_path'] == ['.templates'] assert ns['source_suffix'] == '.txt' From 8e9709290f5b38e108b975abc3bbc399950bbce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 May 2010 22:28:28 +0200 Subject: [PATCH 019/744] Removing unnecessary ez_setup.py --- ez_setup.py | 276 ---------------------------------------------------- 1 file changed, 276 deletions(-) delete mode 100644 ez_setup.py diff --git a/ez_setup.py b/ez_setup.py deleted file mode 100644 index d24e845e5..000000000 --- a/ez_setup.py +++ /dev/null @@ -1,276 +0,0 @@ -#!python -"""Bootstrap setuptools installation - -If you want to use setuptools in your package's setup.py, just include this -file in the same directory with it, and add this to the top of your setup.py:: - - from ez_setup import use_setuptools - use_setuptools() - -If you want to require a specific version of setuptools, set a download -mirror, or use an alternate download directory, you can do so by supplying -the appropriate options to ``use_setuptools()``. - -This file can also be run as a script to install or upgrade setuptools. -""" -import sys -DEFAULT_VERSION = "0.6c9" -DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] - -md5_data = { - 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', - 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', - 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', - 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', - 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', - 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', - 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', - 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', - 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', - 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', - 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', - 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', - 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', - 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', - 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', - 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', - 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', - 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', - 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', - 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', - 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', - 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', - 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', - 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', - 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', - 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', - 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', - 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', - 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', - 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', - 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', - 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', - 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', - 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', -} - -import sys, os -try: from hashlib import md5 -except ImportError: from md5 import md5 - -def _validate_md5(egg_name, data): - if egg_name in md5_data: - digest = md5(data).hexdigest() - if digest != md5_data[egg_name]: - print >>sys.stderr, ( - "md5 validation of %s failed! (Possible download problem?)" - % egg_name - ) - sys.exit(2) - return data - -def use_setuptools( - version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, - download_delay=15 -): - """Automatically find/download setuptools and make it available on sys.path - - `version` should be a valid setuptools version number that is available - as an egg for download under the `download_base` URL (which should end with - a '/'). `to_dir` is the directory where setuptools will be downloaded, if - it is not already available. If `download_delay` is specified, it should - be the number of seconds that will be paused before initiating a download, - should one be required. If an older version of setuptools is installed, - this routine will print a message to ``sys.stderr`` and raise SystemExit in - an attempt to abort the calling script. - """ - was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules - def do_download(): - egg = download_setuptools(version, download_base, to_dir, download_delay) - sys.path.insert(0, egg) - import setuptools; setuptools.bootstrap_install_from = egg - try: - import pkg_resources - except ImportError: - return do_download() - try: - pkg_resources.require("setuptools>="+version); return - except pkg_resources.VersionConflict, e: - if was_imported: - print >>sys.stderr, ( - "The required version of setuptools (>=%s) is not available, and\n" - "can't be installed while this script is running. Please install\n" - " a more recent version first, using 'easy_install -U setuptools'." - "\n\n(Currently using %r)" - ) % (version, e.args[0]) - sys.exit(2) - else: - del pkg_resources, sys.modules['pkg_resources'] # reload ok - return do_download() - except pkg_resources.DistributionNotFound: - return do_download() - -def download_setuptools( - version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, - delay = 15 -): - """Download setuptools from a specified location and return its filename - - `version` should be a valid setuptools version number that is available - as an egg for download under the `download_base` URL (which should end - with a '/'). `to_dir` is the directory where the egg will be downloaded. - `delay` is the number of seconds to pause before an actual download attempt. - """ - import urllib2, shutil - egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) - url = download_base + egg_name - saveto = os.path.join(to_dir, egg_name) - src = dst = None - if not os.path.exists(saveto): # Avoid repeated downloads - try: - from distutils import log - if delay: - log.warn(""" ---------------------------------------------------------------------------- -This script requires setuptools version %s to run (even to display -help). I will attempt to download it for you (from -%s), but -you may need to enable firewall access for this script first. -I will start the download in %d seconds. - -(Note: if this machine does not have network access, please obtain the file - - %s - -and place it in this directory before rerunning this script.) ----------------------------------------------------------------------------""", - version, download_base, delay, url - ); from time import sleep; sleep(delay) - log.warn("Downloading %s", url) - src = urllib2.urlopen(url) - # Read/write all in one block, so we don't create a corrupt file - # if the download is interrupted. - data = _validate_md5(egg_name, src.read()) - dst = open(saveto,"wb"); dst.write(data) - finally: - if src: src.close() - if dst: dst.close() - return os.path.realpath(saveto) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def main(argv, version=DEFAULT_VERSION): - """Install or upgrade setuptools and EasyInstall""" - try: - import setuptools - except ImportError: - egg = None - try: - egg = download_setuptools(version, delay=0) - sys.path.insert(0,egg) - from setuptools.command.easy_install import main - return main(list(argv)+[egg]) # we're done here - finally: - if egg and os.path.exists(egg): - os.unlink(egg) - else: - if setuptools.__version__ == '0.0.1': - print >>sys.stderr, ( - "You have an obsolete version of setuptools installed. Please\n" - "remove it from your system entirely before rerunning this script." - ) - sys.exit(2) - - req = "setuptools>="+version - import pkg_resources - try: - pkg_resources.require(req) - except pkg_resources.VersionConflict: - try: - from setuptools.command.easy_install import main - except ImportError: - from easy_install import main - main(list(argv)+[download_setuptools(delay=0)]) - sys.exit(0) # try to force an exit - else: - if argv: - from setuptools.command.easy_install import main - main(argv) - else: - print "Setuptools version",version,"or greater has been installed." - print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' - -def update_md5(filenames): - """Update our built-in md5 registry""" - - import re - - for name in filenames: - base = os.path.basename(name) - f = open(name,'rb') - md5_data[base] = md5(f.read()).hexdigest() - f.close() - - data = [" %r: %r,\n" % it for it in md5_data.items()] - data.sort() - repl = "".join(data) - - import inspect - srcfile = inspect.getsourcefile(sys.modules[__name__]) - f = open(srcfile, 'rb'); src = f.read(); f.close() - - match = re.search("\nmd5_data = {\n([^}]+)}", src) - if not match: - print >>sys.stderr, "Internal error!" - sys.exit(2) - - src = src[:match.start(1)] + repl + src[match.end(1):] - f = open(srcfile,'w') - f.write(src) - f.close() - - -if __name__=='__main__': - if len(sys.argv)>2 and sys.argv[1]=='--md5update': - update_md5(sys.argv[2:]) - else: - main(sys.argv[1:]) - - - - - - From 4f13ff1ab7424d79dd9ed708315c6cc1d1a2c566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 May 2010 22:33:36 +0200 Subject: [PATCH 020/744] Added a distribute_setup to replace ez_setup --- distribute_setup.py | 481 ++++++++++++++++++++++++++++++++++++++++++++ setup.py | 4 +- 2 files changed, 483 insertions(+), 2 deletions(-) create mode 100644 distribute_setup.py diff --git a/distribute_setup.py b/distribute_setup.py new file mode 100644 index 000000000..4f7bd08c0 --- /dev/null +++ b/distribute_setup.py @@ -0,0 +1,481 @@ +#!python +"""Bootstrap distribute installation + +If you want to use setuptools in your package's setup.py, just include this +file in the same directory with it, and add this to the top of your setup.py:: + + from distribute_setup import use_setuptools + use_setuptools() + +If you want to require a specific version of setuptools, set a download +mirror, or use an alternate download directory, you can do so by supplying +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import os +import sys +import time +import fnmatch +import tempfile +import tarfile +from distutils import log + +try: + from site import USER_SITE +except ImportError: + USER_SITE = None + +try: + import subprocess + + def _python_cmd(*args): + args = (sys.executable,) + args + return subprocess.call(args) == 0 + +except ImportError: + # will be used for python 2.3 + def _python_cmd(*args): + args = (sys.executable,) + args + # quoting arguments if windows + if sys.platform == 'win32': + def quote(arg): + if ' ' in arg: + return '"%s"' % arg + return arg + args = [quote(arg) for arg in args] + return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 + +DEFAULT_VERSION = "0.6.12" +DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" +SETUPTOOLS_FAKED_VERSION = "0.6c11" + +SETUPTOOLS_PKG_INFO = """\ +Metadata-Version: 1.0 +Name: setuptools +Version: %s +Summary: xxxx +Home-page: xxx +Author: xxx +Author-email: xxx +License: xxx +Description: xxx +""" % SETUPTOOLS_FAKED_VERSION + + +def _install(tarball): + # extracting the tarball + tmpdir = tempfile.mkdtemp() + log.warn('Extracting in %s', tmpdir) + old_wd = os.getcwd() + try: + os.chdir(tmpdir) + tar = tarfile.open(tarball) + _extractall(tar) + tar.close() + + # going in the directory + subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) + os.chdir(subdir) + log.warn('Now working in %s', subdir) + + # installing + log.warn('Installing Distribute') + if not _python_cmd('setup.py', 'install'): + log.warn('Something went wrong during the installation.') + log.warn('See the error message above.') + finally: + os.chdir(old_wd) + + +def _build_egg(egg, tarball, to_dir): + # extracting the tarball + tmpdir = tempfile.mkdtemp() + log.warn('Extracting in %s', tmpdir) + old_wd = os.getcwd() + try: + os.chdir(tmpdir) + tar = tarfile.open(tarball) + _extractall(tar) + tar.close() + + # going in the directory + subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) + os.chdir(subdir) + log.warn('Now working in %s', subdir) + + # building an egg + log.warn('Building a Distribute egg in %s', to_dir) + _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) + + finally: + os.chdir(old_wd) + # returning the result + log.warn(egg) + if not os.path.exists(egg): + raise IOError('Could not build the egg.') + + +def _do_download(version, download_base, to_dir, download_delay): + egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' + % (version, sys.version_info[0], sys.version_info[1])) + if not os.path.exists(egg): + tarball = download_setuptools(version, download_base, + to_dir, download_delay) + _build_egg(egg, tarball, to_dir) + sys.path.insert(0, egg) + import setuptools + setuptools.bootstrap_install_from = egg + + +def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, + to_dir=os.curdir, download_delay=15, no_fake=True): + # making sure we use the absolute path + to_dir = os.path.abspath(to_dir) + was_imported = 'pkg_resources' in sys.modules or \ + 'setuptools' in sys.modules + try: + try: + import pkg_resources + if not hasattr(pkg_resources, '_distribute'): + if not no_fake: + _fake_setuptools() + raise ImportError + except ImportError: + return _do_download(version, download_base, to_dir, download_delay) + try: + pkg_resources.require("distribute>="+version) + return + except pkg_resources.VersionConflict: + e = sys.exc_info()[1] + if was_imported: + sys.stderr.write( + "The required version of distribute (>=%s) is not available,\n" + "and can't be installed while this script is running. Please\n" + "install a more recent version first, using\n" + "'easy_install -U distribute'." + "\n\n(Currently using %r)\n" % (version, e.args[0])) + sys.exit(2) + else: + del pkg_resources, sys.modules['pkg_resources'] # reload ok + return _do_download(version, download_base, to_dir, + download_delay) + except pkg_resources.DistributionNotFound: + return _do_download(version, download_base, to_dir, + download_delay) + finally: + if not no_fake: + _create_fake_setuptools_pkg_info(to_dir) + +def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, + to_dir=os.curdir, delay=15): + """Download distribute from a specified location and return its filename + + `version` should be a valid distribute version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download + attempt. + """ + # making sure we use the absolute path + to_dir = os.path.abspath(to_dir) + try: + from urllib.request import urlopen + except ImportError: + from urllib2 import urlopen + tgz_name = "distribute-%s.tar.gz" % version + url = download_base + tgz_name + saveto = os.path.join(to_dir, tgz_name) + src = dst = None + if not os.path.exists(saveto): # Avoid repeated downloads + try: + log.warn("Downloading %s", url) + src = urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = src.read() + dst = open(saveto, "wb") + dst.write(data) + finally: + if src: + src.close() + if dst: + dst.close() + return os.path.realpath(saveto) + +def _no_sandbox(function): + def __no_sandbox(*args, **kw): + try: + from setuptools.sandbox import DirectorySandbox + if not hasattr(DirectorySandbox, '_old'): + def violation(*args): + pass + DirectorySandbox._old = DirectorySandbox._violation + DirectorySandbox._violation = violation + patched = True + else: + patched = False + except ImportError: + patched = False + + try: + return function(*args, **kw) + finally: + if patched: + DirectorySandbox._violation = DirectorySandbox._old + del DirectorySandbox._old + + return __no_sandbox + +@_no_sandbox +def _patch_file(path, content): + """Will backup the file then patch it""" + existing_content = open(path).read() + if existing_content == content: + # already patched + log.warn('Already patched.') + return False + log.warn('Patching...') + _rename_path(path) + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() + return True + + +def _same_content(path, content): + return open(path).read() == content + +def _rename_path(path): + new_name = path + '.OLD.%s' % time.time() + log.warn('Renaming %s into %s', path, new_name) + os.rename(path, new_name) + return new_name + +@_no_sandbox +def _remove_flat_installation(placeholder): + if not os.path.isdir(placeholder): + log.warn('Unkown installation at %s', placeholder) + return False + found = False + for file in os.listdir(placeholder): + if fnmatch.fnmatch(file, 'setuptools*.egg-info'): + found = True + break + if not found: + log.warn('Could not locate setuptools*.egg-info') + return + + log.warn('Removing elements out of the way...') + pkg_info = os.path.join(placeholder, file) + if os.path.isdir(pkg_info): + patched = _patch_egg_dir(pkg_info) + else: + patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) + + if not patched: + log.warn('%s already patched.', pkg_info) + return False + # now let's move the files out of the way + for element in ('setuptools', 'pkg_resources.py', 'site.py'): + element = os.path.join(placeholder, element) + if os.path.exists(element): + _rename_path(element) + else: + log.warn('Could not find the %s element of the ' + 'Setuptools distribution', element) + return True + + +def _after_install(dist): + log.warn('After install bootstrap.') + placeholder = dist.get_command_obj('install').install_purelib + _create_fake_setuptools_pkg_info(placeholder) + +@_no_sandbox +def _create_fake_setuptools_pkg_info(placeholder): + if not placeholder or not os.path.exists(placeholder): + log.warn('Could not find the install location') + return + pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) + setuptools_file = 'setuptools-%s-py%s.egg-info' % \ + (SETUPTOOLS_FAKED_VERSION, pyver) + pkg_info = os.path.join(placeholder, setuptools_file) + if os.path.exists(pkg_info): + log.warn('%s already exists', pkg_info) + return + + log.warn('Creating %s', pkg_info) + f = open(pkg_info, 'w') + try: + f.write(SETUPTOOLS_PKG_INFO) + finally: + f.close() + + pth_file = os.path.join(placeholder, 'setuptools.pth') + log.warn('Creating %s', pth_file) + f = open(pth_file, 'w') + try: + f.write(os.path.join(os.curdir, setuptools_file)) + finally: + f.close() + +@_no_sandbox +def _patch_egg_dir(path): + # let's check if it's already patched + pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') + if os.path.exists(pkg_info): + if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): + log.warn('%s already patched.', pkg_info) + return False + _rename_path(path) + os.mkdir(path) + os.mkdir(os.path.join(path, 'EGG-INFO')) + pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') + f = open(pkg_info, 'w') + try: + f.write(SETUPTOOLS_PKG_INFO) + finally: + f.close() + return True + + +def _before_install(): + log.warn('Before install bootstrap.') + _fake_setuptools() + + +def _under_prefix(location): + if 'install' not in sys.argv: + return True + args = sys.argv[sys.argv.index('install')+1:] + for index, arg in enumerate(args): + for option in ('--root', '--prefix'): + if arg.startswith('%s=' % option): + top_dir = arg.split('root=')[-1] + return location.startswith(top_dir) + elif arg == option: + if len(args) > index: + top_dir = args[index+1] + return location.startswith(top_dir) + elif option == '--user' and USER_SITE is not None: + return location.startswith(USER_SITE) + return True + + +def _fake_setuptools(): + log.warn('Scanning installed packages') + try: + import pkg_resources + except ImportError: + # we're cool + log.warn('Setuptools or Distribute does not seem to be installed.') + return + ws = pkg_resources.working_set + try: + setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', + replacement=False)) + except TypeError: + # old distribute API + setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) + + if setuptools_dist is None: + log.warn('No setuptools distribution found') + return + # detecting if it was already faked + setuptools_location = setuptools_dist.location + log.warn('Setuptools installation detected at %s', setuptools_location) + + # if --root or --preix was provided, and if + # setuptools is not located in them, we don't patch it + if not _under_prefix(setuptools_location): + log.warn('Not patching, --root or --prefix is installing Distribute' + ' in another location') + return + + # let's see if its an egg + if not setuptools_location.endswith('.egg'): + log.warn('Non-egg installation') + res = _remove_flat_installation(setuptools_location) + if not res: + return + else: + log.warn('Egg installation') + pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') + if (os.path.exists(pkg_info) and + _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): + log.warn('Already patched.') + return + log.warn('Patching...') + # let's create a fake egg replacing setuptools one + res = _patch_egg_dir(setuptools_location) + if not res: + return + log.warn('Patched done.') + _relaunch() + + +def _relaunch(): + log.warn('Relaunching...') + # we have to relaunch the process + args = [sys.executable] + sys.argv + sys.exit(subprocess.call(args)) + + +def _extractall(self, path=".", members=None): + """Extract all members from the archive to the current working + directory and set owner, modification time and permissions on + directories afterwards. `path' specifies a different directory + to extract to. `members' is optional and must be a subset of the + list returned by getmembers(). + """ + import copy + import operator + from tarfile import ExtractError + directories = [] + + if members is None: + members = self + + for tarinfo in members: + if tarinfo.isdir(): + # Extract directories with a safe mode. + directories.append(tarinfo) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 448 # decimal for oct 0700 + self.extract(tarinfo, path) + + # Reverse sort directories. + if sys.version_info < (2, 4): + def sorter(dir1, dir2): + return cmp(dir1.name, dir2.name) + directories.sort(sorter) + directories.reverse() + else: + directories.sort(key=operator.attrgetter('name'), reverse=True) + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError: + e = sys.exc_info()[1] + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + +def main(argv, version=DEFAULT_VERSION): + """Install or upgrade setuptools and EasyInstall""" + tarball = download_setuptools() + _install(tarball) + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/setup.py b/setup.py index 2494851f5..8d06f5691 100644 --- a/setup.py +++ b/setup.py @@ -2,8 +2,8 @@ try: from setuptools import setup, find_packages except ImportError: - import ez_setup - ez_setup.use_setuptools() + import distribute_setup + distribute_setup.use_setuptools() from setuptools import setup, find_packages import os From d6e5dfacd48c2d8bdc92a30b9bbeb3c015a03f12 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:13:57 +0200 Subject: [PATCH 021/744] Add a constant for class types, which lacks types.ClassType in Py3k. --- sphinx/application.py | 3 --- sphinx/environment.py | 4 ++-- sphinx/ext/autodoc.py | 13 ++++--------- sphinx/util/nodes.py | 4 ++-- sphinx/util/pycompat.py | 12 ++++++++++++ 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/sphinx/application.py b/sphinx/application.py index 97778d3fb..b3d2aebc4 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -37,9 +37,6 @@ from sphinx.util.osutil import ENOENT from sphinx.util.console import bold -# Directive is either new-style or old-style -clstypes = (type, types.ClassType) - # List of all known core events. Maps name to arguments description. events = { 'builder-inited': '', diff --git a/sphinx/environment.py b/sphinx/environment.py index fa8460cb3..21994a746 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -39,7 +39,7 @@ from sphinx.util import url_re, get_matching_docs, docname_join, \ from sphinx.util.nodes import clean_astext, make_refnode from sphinx.util.osutil import movefile, SEP, ustrftime from sphinx.util.matching import compile_matchers -from sphinx.util.pycompat import all +from sphinx.util.pycompat import all, class_types from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _ @@ -251,7 +251,7 @@ class BuildEnvironment: if key.startswith('_') or \ isinstance(val, types.ModuleType) or \ isinstance(val, types.FunctionType) or \ - isinstance(val, (type, types.ClassType)): + isinstance(val, class_types): del self.config[key] try: pickle.dump(self, picklefile, pickle.HIGHEST_PROTOCOL) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index adf08bcde..8a827a91f 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -14,7 +14,7 @@ import re import sys import inspect -from types import FunctionType, BuiltinFunctionType, MethodType, ClassType +from types import FunctionType, BuiltinFunctionType, MethodType from docutils import nodes from docutils.utils import assemble_option_dict @@ -27,15 +27,10 @@ from sphinx.application import ExtensionError from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.compat import Directive from sphinx.util.inspect import isdescriptor, safe_getmembers, safe_getattr +from sphinx.util.pycompat import base_exception, class_types from sphinx.util.docstrings import prepare_docstring -try: - base_exception = BaseException -except NameError: - base_exception = Exception - - #: extended signature RE: with explicit module name separated by :: py_ext_sig_re = re.compile( r'''^ ([\w.]+::)? # explicit module name @@ -866,7 +861,7 @@ class ClassDocumenter(ModuleLevelDocumenter): @classmethod def can_document_member(cls, member, membername, isattr, parent): - return isinstance(member, (type, ClassType)) + return isinstance(member, class_types) def import_object(self): ret = ModuleLevelDocumenter.import_object(self) @@ -972,7 +967,7 @@ class ExceptionDocumenter(ClassDocumenter): @classmethod def can_document_member(cls, member, membername, isattr, parent): - return isinstance(member, (type, ClassType)) and \ + return isinstance(member, class_types) and \ issubclass(member, base_exception) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 97b585696..aab8f0142 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -10,11 +10,11 @@ """ import re -import types from docutils import nodes from sphinx import addnodes +from sphinx.util.pycompat import class_types # \x00 means the "<" was backslash-escaped @@ -115,7 +115,7 @@ def _new_traverse(self, condition=None, if include_self and descend and not siblings and not ascend: if condition is None: return self._all_traverse([]) - elif isinstance(condition, (types.ClassType, type)): + elif isinstance(condition, class_types): return self._fast_traverse(condition, []) return self._old_traverse(condition, include_self, descend, siblings, ascend) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index bdd9507df..7bf768fac 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -13,6 +13,18 @@ import sys import codecs import encodings +try: + from types import ClassType + class_types = (type, ClassType) +except ImportError: + # Python 3 + class_types = (type,) + +try: + base_exception = BaseException +except NameError: + base_exception = Exception + try: any = any From 28349ed8569665196b4b2363ac23e13a22006f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 May 2010 23:23:56 +0200 Subject: [PATCH 022/744] Use 'U' if file is not present (we run under 3.x) --- tests/path.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/path.py b/tests/path.py index 20deb0489..7b89c0cd3 100644 --- a/tests/path.py +++ b/tests/path.py @@ -64,8 +64,13 @@ except NameError: # Universal newline support _textmode = 'r' -if hasattr(file, 'newlines'): +try: + file +except NameError: _textmode = 'U' +else: + if hasattr(file, 'newlines'): + _textmode = 'U' class TreeWalkWarning(Warning): From 84cba9c39c0b36c6837d8cc7ce19bbad05be7ff1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:18:57 +0200 Subject: [PATCH 023/744] Fix wrong qualified name. --- sphinx/quickstart.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index fe2b43a33..a63907c7a 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -837,12 +837,12 @@ directly.''' if d['ext_intersphinx']: conf_text += INTERSPHINX_CONFIG - f = codecs.open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8') + f = open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8') f.write(conf_text) f.close() masterfile = path.join(srcdir, d['master'] + d['suffix']) - f = codecs.open(masterfile, 'w', encoding='utf-8') + f = open(masterfile, 'w', encoding='utf-8') f.write(MASTER_FILE % d) f.close() @@ -850,14 +850,14 @@ directly.''' d['rsrcdir'] = d['sep'] and 'source' or '.' d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build' # use binary mode, to avoid writing \r\n on Windows - f = codecs.open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8') + f = open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8') f.write(MAKEFILE % d) f.close() if d['batchfile']: d['rsrcdir'] = d['sep'] and 'source' or '.' d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build' - f = codecs.open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8') + f = open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8') f.write(BATCHFILE % d) f.close() From 420adbce2f75071b556e0d206b9f1887309c44c4 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:19:17 +0200 Subject: [PATCH 024/744] Make it easier for the test suite to override raw_input for test_quickstart. --- sphinx/quickstart.py | 5 ++++- tests/test_quickstart.py | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index a63907c7a..892bd641d 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -21,6 +21,9 @@ from sphinx.util.console import purple, bold, red, turquoise, \ nocolor, color_terminal from sphinx.util import texescape +# function to get input from terminal -- overridden by the test suite +term_input = raw_input + PROMPT_PREFIX = '> ' @@ -657,7 +660,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty): prompt = purple(PROMPT_PREFIX + '%s [%s]: ' % (text, default)) else: prompt = purple(PROMPT_PREFIX + text + ': ') - x = raw_input(prompt) + x = term_input(prompt) if default and not x: x = default if not isinstance(x, unicode): diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 8acff5884..d0403d3b2 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -37,7 +37,7 @@ def mock_raw_input(answers, needanswer=False): return raw_input def teardown_module(): - qs.raw_input = __builtin__.raw_input + qs.term_input = raw_input qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None) coloron() @@ -51,7 +51,7 @@ def test_do_prompt(): 'Q5': 'no', 'Q6': 'foo', } - qs.raw_input = mock_raw_input(answers) + qs.term_input = mock_raw_input(answers) try: qs.do_prompt(d, 'k1', 'Q1') except AssertionError: @@ -79,7 +79,7 @@ def test_quickstart_defaults(tempdir): 'Author name': 'Georg Brandl', 'Project version': '0.1', } - qs.raw_input = mock_raw_input(answers) + qs.term_input = mock_raw_input(answers) qs.inner_main([]) conffile = tempdir / 'conf.py' @@ -136,7 +136,7 @@ def test_quickstart_all_answers(tempdir): 'Create Windows command file': 'no', 'Do you want to use the epub builder': 'yes', } - qs.raw_input = mock_raw_input(answers, needanswer=True) + qs.term_input = mock_raw_input(answers, needanswer=True) qs.TERM_ENCODING = 'utf-8' qs.inner_main([]) From bcbce5955fe77854bf2a6781fab99fa2e23ab311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 9 May 2010 00:38:16 +0200 Subject: [PATCH 025/744] Changed tests/run.py so that it's possible to run the testsuite on python3 more easiely --- Makefile | 2 +- tests/run.py | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 682f03666..593c7ad49 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PYTHON ?= python -3 +PYTHON ?= python3 export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx diff --git a/tests/run.py b/tests/run.py index 0cb41442c..5fd30d62b 100755 --- a/tests/run.py +++ b/tests/run.py @@ -11,7 +11,16 @@ """ import sys -from os import path +from os import path, chdir + +if sys.version_info >= (3,): + print('Copying and converting sources to build/lib/test...') + from distutils.util import copydir_run_2to3 + testroot = path.dirname(__file__) or '.' + newroot = path.join(testroot, path.pardir, 'build', 'lib', 'test') + copydir_run_2to3(testroot, newroot) + # switch to the converted dir so nose tests the right tests + chdir(newroot) # always test the sphinx package from this directory sys.path.insert(0, path.join(path.dirname(__file__), path.pardir)) @@ -19,8 +28,8 @@ sys.path.insert(0, path.join(path.dirname(__file__), path.pardir)) try: import nose except ImportError: - print "The nose package is needed to run the Sphinx test suite." + print("The nose package is needed to run the Sphinx test suite.") sys.exit(1) -print "Running Sphinx test suite..." +print("Running Sphinx test suite...") nose.main() From 5243c56d7abe5a4e2a2c5499f29fae8318c665f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 9 May 2010 00:54:14 +0200 Subject: [PATCH 026/744] Fixed DefExpr.__str__ --- sphinx/domains/cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 90c3533ef..8df89459b 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -110,7 +110,7 @@ class DefinitionError(Exception): return self.description def __str__(self): - return unicode(self.encode('utf-8')) + return unicode(self).encode('utf-8') class DefExpr(object): From e20b61b06e5b029d1b12f8fff392ebf4ca851574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 9 May 2010 14:57:18 +0200 Subject: [PATCH 027/744] Rename __unicode__ to __str__ --- custom_fixers/__init__.py | 0 custom_fixers/fix_alt_unicode.py | 12 ++++++++++++ setup.py | 1 + 3 files changed, 13 insertions(+) create mode 100644 custom_fixers/__init__.py create mode 100644 custom_fixers/fix_alt_unicode.py diff --git a/custom_fixers/__init__.py b/custom_fixers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/custom_fixers/fix_alt_unicode.py b/custom_fixers/fix_alt_unicode.py new file mode 100644 index 000000000..55175e90f --- /dev/null +++ b/custom_fixers/fix_alt_unicode.py @@ -0,0 +1,12 @@ +from lib2to3.fixer_base import BaseFix +from lib2to3.fixer_util import Name + +class FixAltUnicode(BaseFix): + PATTERN = """ + func=funcdef< 'def' name='__unicode__' + parameters< '(' NAME ')' > any+ > + """ + + def transform(self, node, results): + name = results['name'] + name.replace(Name('__str__', prefix=name.prefix)) diff --git a/setup.py b/setup.py index 8d06f5691..fe4066b80 100644 --- a/setup.py +++ b/setup.py @@ -199,4 +199,5 @@ setup( install_requires=requires, cmdclass=cmdclass, use_2to3=True, + use_2to3_fixers=['custom_fixers'], ) From 22a01fab9b7eb02266816c8001ba2db4ecb1573d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 10 May 2010 00:59:50 +0200 Subject: [PATCH 028/744] Added information about the i did during the weekend to the changes file --- CHANGES.DasIch | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.DasIch b/CHANGES.DasIch index 856a5d87b..711f9a534 100644 --- a/CHANGES.DasIch +++ b/CHANGES.DasIch @@ -5,6 +5,17 @@ This file contains changes made by Daniel Neuhäuser, during the Google Summer of Code 2010, to port Sphinx to Python 3.x. Changes are ordered descending by date. +May 10: Fixed a couple of tests and made several small changes. + +May 9: - Removed ez_setup.py which does not work with Python 3.x. and replaced + it with distribute_setup.py + - Use distribute (at least on 3.x) in order to run 2to3 automatically. + - Reverted some of the changes made in revision bac40c7c924c which + caused errors. + - Modified tests/run.py to test against the build created by + setup.py build in order to run the test suite with 3.x + - Several small changes to fix 3.x compatibilty. + May 1: - Removed deprecated tuple parameter unpacking. - Removed a pre-2.3 workaround for booleans because this creates a deprecation warning for 3.x, in which you can't assign values to From 36338fe9eaf559cbf7ede349c1bd6b5a650e3e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 14:00:50 +0200 Subject: [PATCH 029/744] Added a clean-backupfiles target to the makefile --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 593c7ad49..aa2795147 100644 --- a/Makefile +++ b/Makefile @@ -12,17 +12,20 @@ check: -i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py \ -i env -i .tox . -clean: clean-pyc clean-patchfiles +clean: clean-pyc clean-patchfiles clean-backupfiles clean-pyc: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + clean-patchfiles: find . -name '*.orig' -exec rm -f {} + find . -name '*.rej' -exec rm -f {} + +clean-backupfiles: + find . -name '*~' -exec rm -f {} + + find . -name '*.bak' -exec rm -f {} + + pylint: @pylint --rcfile utils/pylintrc sphinx From df444e822cc68e1e4717089c9fbb66bb110113ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 16:20:44 +0200 Subject: [PATCH 030/744] Add setup_distribute.py to MANIFEST.in and remove ez_setup.py --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 25cbc334f..5e3104a82 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,7 +7,7 @@ include TODO include babel.cfg include Makefile -include ez_setup.py +include setup_distribute.py include sphinx-autogen.py include sphinx-build.py include sphinx-quickstart.py From 471da5aa21c6be081a68d7fc255456d81c5ee02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 16:54:15 +0200 Subject: [PATCH 031/744] Added a build target to the Makefile which we need for python3 tests --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index aa2795147..72e047729 100644 --- a/Makefile +++ b/Makefile @@ -32,8 +32,11 @@ pylint: reindent: @$(PYTHON) utils/reindent.py -r -B . -test: +test: build @cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST) -covertest: +covertest: build @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage --cover-package=sphinx $(TEST) + +build: + @$(PYTHON) setup.py build From d8425102e27723839fc2c5078ab96c7752de1207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 17:50:13 +0200 Subject: [PATCH 032/744] Switched check_sources.py from getopt to optparse --- utils/check_sources.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/utils/check_sources.py b/utils/check_sources.py index 0571ab1e8..1b30f2dc5 100755 --- a/utils/check_sources.py +++ b/utils/check_sources.py @@ -12,8 +12,8 @@ """ import sys, os, re -import getopt import cStringIO +from optparse import OptionParser from os.path import join, splitext, abspath @@ -165,34 +165,32 @@ def check_xhtml(fn, lines): def main(argv): - try: - gopts, args = getopt.getopt(argv[1:], "vi:") - except getopt.GetoptError: - print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0] - return 2 - opts = {} - for opt, val in gopts: - if opt == '-i': - val = abspath(val) - opts.setdefault(opt, []).append(val) + parser = OptionParser(usage='Usage: %prog [-v] [-i ignorepath]* [path]') + parser.add_option('-v', '--verbose', dest='verbose', default=False, + action='store_true') + parser.add_option('-i', '--ignore-path', dest='ignored_paths', + default=[], action='append') + options, args = parser.parse_args(argv[1:]) if len(args) == 0: path = '.' elif len(args) == 1: path = args[0] else: - print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0] - return 2 + print args + parser.error('No more then one path supported') - verbose = '-v' in opts + verbose = options.verbose + ignored_paths = set(abspath(p) for p in options.ignored_paths) num = 0 out = cStringIO.StringIO() for root, dirs, files in os.walk(path): - if '.svn' in dirs: - dirs.remove('.svn') - if '-i' in opts and abspath(root) in opts['-i']: + for vcs_dir in ['.svn', '.hg', '.git']: + if vcs_dir in dirs: + dirs.remove(vcs_dir) + if abspath(root) in ignored_paths: del dirs[:] continue in_check_pkg = root.startswith('./sphinx') @@ -201,7 +199,7 @@ def main(argv): fn = join(root, fn) if fn[:2] == './': fn = fn[2:] - if '-i' in opts and abspath(fn) in opts['-i']: + if abspath(fn) in ignored_paths: continue ext = splitext(fn)[1] From ad29ab1b860d13fcd0dacd2395f8f3dea5c2370a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 19:45:23 +0200 Subject: [PATCH 033/744] Fixed file opening --- utils/check_sources.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utils/check_sources.py b/utils/check_sources.py index 1b30f2dc5..8eeadbf4b 100755 --- a/utils/check_sources.py +++ b/utils/check_sources.py @@ -212,7 +212,10 @@ def main(argv): try: f = open(fn, 'r') - lines = list(f) + try: + lines = list(f) + finally: + f.close() except (IOError, OSError), err: print "%s: cannot open: %s" % (fn, err) num += 1 From 592a978c25d94579142c50c4be240d5856124891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 22:19:38 +0200 Subject: [PATCH 034/744] Scripts in utils are now automatically converted. They may not work at the moment though --- Makefile | 14 ++++++++++---- utils/convert.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 utils/convert.py diff --git a/Makefile b/Makefile index 72e047729..53424b5d6 100644 --- a/Makefile +++ b/Makefile @@ -2,17 +2,17 @@ PYTHON ?= python3 export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx -.PHONY: all check clean clean-pyc clean-patchfiles pylint reindent test +.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint reindent test all: clean-pyc check test -check: +check: convert-utils @$(PYTHON) utils/check_sources.py -i build -i dist -i sphinx/style/jquery.js \ -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py -i .ropeproject \ -i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py \ -i env -i .tox . -clean: clean-pyc clean-patchfiles clean-backupfiles +clean: clean-pyc clean-patchfiles clean-backupfiles clean-generated clean-pyc: find . -name '*.pyc' -exec rm -f {} + @@ -26,10 +26,13 @@ clean-backupfiles: find . -name '*~' -exec rm -f {} + find . -name '*.bak' -exec rm -f {} + +clean-generated: + rm utils/*3.py* + pylint: @pylint --rcfile utils/pylintrc sphinx -reindent: +reindent: convert-utils @$(PYTHON) utils/reindent.py -r -B . test: build @@ -40,3 +43,6 @@ covertest: build build: @$(PYTHON) setup.py build + +convert-utils: + @python3 utils/convert.py -i utils/convert.py utils/ diff --git a/utils/convert.py b/utils/convert.py new file mode 100644 index 000000000..65b4d3bdf --- /dev/null +++ b/utils/convert.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# coding: utf-8 +""" + Converts files with 2to3 + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Creates a Python 3 version of each file. + + The Python3 version of a file foo.py will be called foo3.py. + + :copyright: Copyright 2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import os +import sys +from glob import iglob +from optparse import OptionParser +from shutil import copy +from distutils.util import run_2to3 + +def main(argv): + parser = OptionParser(usage='%prog [path]') + parser.add_option('-i', '--ignorepath', dest='ignored_paths', + action='append', default=[]) + options, args = parser.parse_args(argv) + + ignored_paths = set(options.ignored_paths) + + path = os.path.abspath(args[0]) if args else os.getcwd() + convertables = [] + for filename in iglob(os.path.join(path, '*.py')): + if filename in ignored_paths: + continue + basename, ext = os.path.splitext(filename) + if basename.endswith('3'): + continue + filename3 = basename + '3' + ext + copy(filename, filename3) + convertables.append(filename3) + run_2to3(convertables) + +if __name__ == "__main__": + main(sys.argv[1:]) From 315ad84fce2beab11457d23b53b48b41fb23332b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:28:34 +0200 Subject: [PATCH 035/744] Ignore failures in removing converted utils. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 53424b5d6..4c0cdef87 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ clean-backupfiles: find . -name '*.bak' -exec rm -f {} + clean-generated: - rm utils/*3.py* + -rm utils/*3.py* pylint: @pylint --rcfile utils/pylintrc sphinx From e1b7381a94592d04717290f842f49a7aa08e33c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 22:24:20 +0200 Subject: [PATCH 036/744] convert.py now properly ignores paths --- utils/convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/convert.py b/utils/convert.py index 65b4d3bdf..f025c49a0 100644 --- a/utils/convert.py +++ b/utils/convert.py @@ -24,7 +24,7 @@ def main(argv): action='append', default=[]) options, args = parser.parse_args(argv) - ignored_paths = set(options.ignored_paths) + ignored_paths = {os.path.abspath(p) for p in options.ignored_paths} path = os.path.abspath(args[0]) if args else os.getcwd() convertables = [] From 87d35afcfccd9c42faaf32c424e8de19f0e7b627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 22:27:05 +0200 Subject: [PATCH 037/744] Ignore generated files --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index 70ea36a52..40c00aac7 100644 --- a/.hgignore +++ b/.hgignore @@ -15,3 +15,4 @@ ^env/ \.DS_Store$ ~$ +^utils/.*3\.py$ From e8d98966dec388bce4e9076de0ea03454053e678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 23:51:25 +0200 Subject: [PATCH 038/744] Automatically use converted scripts in the makefile --- Makefile | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 4c0cdef87..2672aad4b 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,22 @@ -PYTHON ?= python3 +PYTHON ?= python export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx .PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint reindent test +DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js -i sphinx/pycode/pgen2 \ + -i sphinx/util/smartypants.py -i .ropeproject -i doc/_build -i tests/path.py \ + -i tests/coverage.py -i env -i utils/convert.py -i utils/reindent3.py \ + -i utils/check_sources3.py + all: clean-pyc check test check: convert-utils - @$(PYTHON) utils/check_sources.py -i build -i dist -i sphinx/style/jquery.js \ - -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py -i .ropeproject \ - -i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py \ - -i env -i .tox . +ifeq ($(PYTHON), python3) + @$(PYTHON) utils/check_sources3.py $(DONT_CHECK) . +else + @$(PYTHON) utils/check_sources.py $(DONT_CHECK) . +endif clean: clean-pyc clean-patchfiles clean-backupfiles clean-generated @@ -33,7 +39,11 @@ pylint: @pylint --rcfile utils/pylintrc sphinx reindent: convert-utils +ifeq ($(PYTHON), python3) + @$(PYTHON) utils/reindent3.py -r -B . +else @$(PYTHON) utils/reindent.py -r -B . +endif test: build @cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST) From 46e5c123d5125524d2563a641189820601004dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 23:56:44 +0200 Subject: [PATCH 039/744] Keep under 80 chars per line in the Makefile --- Makefile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 2672aad4b..cf4f292f7 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,14 @@ PYTHON ?= python export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx -.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint reindent test +.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint \ + reindent test -DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js -i sphinx/pycode/pgen2 \ - -i sphinx/util/smartypants.py -i .ropeproject -i doc/_build -i tests/path.py \ - -i tests/coverage.py -i env -i utils/convert.py -i utils/reindent3.py \ - -i utils/check_sources3.py +DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \ + -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \ + -i .ropeproject -i doc/_build -i tests/path.py \ + -i tests/coverage.py -i env -i utils/convert.py \ + -i utils/reindent3.py -i utils/check_sources3.py -i .tox all: clean-pyc check test @@ -49,7 +51,8 @@ test: build @cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST) covertest: build - @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage --cover-package=sphinx $(TEST) + @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage \ + --cover-package=sphinx $(TEST) build: @$(PYTHON) setup.py build From acd1eaf9d7f6316e3f53e8d96de1d54f624c00cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 17 May 2010 00:44:44 +0200 Subject: [PATCH 040/744] check_sources.py is now ported to 3.x --- Makefile | 2 +- utils/check_sources.py | 72 +++++++++++++++++++++++------------------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index cf4f292f7..209302302 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PYTHON ?= python +PYTHON ?= python3 export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx diff --git a/utils/check_sources.py b/utils/check_sources.py index 8eeadbf4b..c412742b7 100755 --- a/utils/check_sources.py +++ b/utils/check_sources.py @@ -16,6 +16,12 @@ import cStringIO from optparse import OptionParser from os.path import join, splitext, abspath +if sys.version_info >= (3, 0): + def b(s): + return s.encode('utf-8') +else: + b = str + checkers = {} @@ -30,26 +36,26 @@ def checker(*suffixes, **kwds): name_mail_re = r'[\w ]+(<.*?>)?' -copyright_re = re.compile(r'^ :copyright: Copyright 200\d(-20\d\d)? ' - r'by %s(, %s)*[,.]$' % - (name_mail_re, name_mail_re)) -license_re = re.compile(r" :license: (.*?).\n") -copyright_2_re = re.compile(r'^ %s(, %s)*[,.]$' % - (name_mail_re, name_mail_re)) -coding_re = re.compile(r'coding[:=]\s*([-\w.]+)') -not_ix_re = re.compile(r'\bnot\s+\S+?\s+i[sn]\s\S+') -is_const_re = re.compile(r'if.*?==\s+(None|False|True)\b') +copyright_re = re.compile(b(r'^ :copyright: Copyright 200\d(-20\d\d)? ' + r'by %s(, %s)*[,.]$' % + (name_mail_re, name_mail_re))) +license_re = re.compile(b(r" :license: (.*?).\n")) +copyright_2_re = re.compile(b(r'^ %s(, %s)*[,.]$' % + (name_mail_re, name_mail_re))) +coding_re = re.compile(b(r'coding[:=]\s*([-\w.]+)')) +not_ix_re = re.compile(b(r'\bnot\s+\S+?\s+i[sn]\s\S+')) +is_const_re = re.compile(b(r'if.*?==\s+(None|False|True)\b')) -misspellings = ["developement", "adress", "verificate", # ALLOW-MISSPELLING - "informations"] # ALLOW-MISSPELLING +misspellings = [b("developement"), b("adress"), # ALLOW-MISSPELLING + b("verificate"), b("informations")] # ALLOW-MISSPELLING - -@checker('.py') -def check_syntax(fn, lines): - try: - compile(''.join(lines), fn, "exec") - except SyntaxError, err: - yield 0, "not compilable: %s" % err +if sys.version_info < (3, 0): + @checker('.py') + def check_syntax(fn, lines): + try: + compile(b('').join(lines), fn, "exec") + except SyntaxError, err: + yield 0, "not compilable: %s" % err @checker('.py') @@ -61,8 +67,8 @@ def check_style_and_encoding(fn, lines): if lno < 2: co = coding_re.search(line) if co: - encoding = co.group(1) - if line.strip().startswith('#'): + encoding = co.group(1).decode('ascii') + if line.strip().startswith(b('#')): continue #m = not_ix_re.search(line) #if m: @@ -82,7 +88,7 @@ def check_style_and_encoding(fn, lines): def check_fileheader(fn, lines): # line number correction c = 1 - if lines[0:1] == ['#!/usr/bin/env python\n']: + if lines[0:1] == [b('#!/usr/bin/env python\n')]: lines = lines[1:] c = 2 @@ -91,38 +97,38 @@ def check_fileheader(fn, lines): for lno, l in enumerate(lines): llist.append(l) if lno == 0: - if l == '# -*- coding: rot13 -*-\n': + if l == b('# -*- coding: rot13 -*-\n'): # special-case pony package return - elif l != '# -*- coding: utf-8 -*-\n': + elif l != b('# -*- coding: utf-8 -*-\n'): yield 1, "missing coding declaration" elif lno == 1: - if l != '"""\n' and l != 'r"""\n': + if l != b('"""\n') and l != b('r"""\n'): yield 2, 'missing docstring begin (""")' else: docopen = True elif docopen: - if l == '"""\n': + if l == b('"""\n'): # end of docstring if lno <= 4: yield lno+c, "missing module name in docstring" break - if l != "\n" and l[:4] != ' ' and docopen: + if l != b("\n") and l[:4] != b(' ') and docopen: yield lno+c, "missing correct docstring indentation" if lno == 2: # if not in package, don't check the module name modname = fn[:-3].replace('/', '.').replace('.__init__', '') while modname: - if l.lower()[4:-1] == modname: + if l.lower()[4:-1] == b(modname): break modname = '.'.join(modname.split('.')[1:]) else: yield 3, "wrong module name in docstring heading" modnamelen = len(l.strip()) elif lno == 3: - if l.strip() != modnamelen * "~": + if l.strip() != modnamelen * b("~"): yield 4, "wrong module name underline, should be ~~~...~" else: @@ -145,16 +151,16 @@ def check_fileheader(fn, lines): @checker('.py', '.html', '.rst') def check_whitespace_and_spelling(fn, lines): for lno, line in enumerate(lines): - if "\t" in line: + if b("\t") in line: yield lno+1, "OMG TABS!!!1 " - if line[:-1].rstrip(' \t') != line[:-1]: + if line[:-1].rstrip(b(' \t')) != line[:-1]: yield lno+1, "trailing whitespace" for word in misspellings: - if word in line and 'ALLOW-MISSPELLING' not in line: + if word in line and b('ALLOW-MISSPELLING') not in line: yield lno+1, '"%s" used' % word -bad_tags = ('', '', '', '
', '', '', '', '
', ' Date: Mon, 17 May 2010 01:11:22 +0200 Subject: [PATCH 041/744] ported utils/reindent.py to python 3.x --- utils/reindent.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/utils/reindent.py b/utils/reindent.py index bcb6b4343..63ff7ef73 100755 --- a/utils/reindent.py +++ b/utils/reindent.py @@ -47,6 +47,17 @@ recurse = 0 dryrun = 0 no_backup = 0 +if sys.version_info >= (3, 0): + def tokens(readline, tokeneater): + for token in tokenize.tokenize(readline): + tokeneater(*token) + + def b(s): + return s.encode('utf-8') +else: + tokens = tokenize.tokenize + b = str + def usage(msg=None): if msg is not None: print >> sys.stderr, msg @@ -106,7 +117,7 @@ def check(file): if verbose: print "checking", file, "...", try: - f = open(file) + f = open(file, 'rb') except IOError, msg: errprint("%s: I/O Error: %s" % (file, str(msg))) return @@ -129,7 +140,7 @@ def check(file): os.rename(file, bak) if verbose: print "renamed", file, "to", bak - f = open(file, "w") + f = open(file, "wb") r.write(f) f.close() if verbose: @@ -151,7 +162,7 @@ class Reindenter: # File lines, rstripped & tab-expanded. Dummy at start is so # that we can use tokenize's 1-based line numbering easily. # Note that a line is all-blank iff it's "\n". - self.lines = [line.rstrip('\n \t').expandtabs() + "\n" + self.lines = [line.rstrip(b('\n \t')).expandtabs() + b("\n") for line in self.raw] self.lines.insert(0, None) self.index = 1 # index into self.lines of next line @@ -163,10 +174,10 @@ class Reindenter: self.stats = [] def run(self): - tokenize.tokenize(self.getline, self.tokeneater) + tokens(self.getline, self.tokeneater) # Remove trailing empty lines. lines = self.lines - while lines and lines[-1] == "\n": + while lines and lines[-1] == b("\n"): lines.pop() # Sentinel. stats = self.stats @@ -222,10 +233,10 @@ class Reindenter: else: for line in lines[thisstmt:nextstmt]: if diff > 0: - if line == "\n": + if line == b("\n"): after.append(line) else: - after.append(" " * diff + line) + after.append(b(" ") * diff + line) else: remove = min(getlspace(line), -diff) after.append(line[remove:]) @@ -237,7 +248,7 @@ class Reindenter: # Line-getter for tokenize. def getline(self): if self.index >= len(self.lines): - line = "" + line = b("") else: line = self.lines[self.index] self.index += 1 @@ -286,7 +297,7 @@ class Reindenter: # Count number of leading blanks. def getlspace(line): i, n = 0, len(line) - while i < n and line[i] == " ": + while i < n and line[i] == b(" "): i += 1 return i From 936d68fc8364c2274e1416a951bf2af9b54cbd3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 17 May 2010 01:28:50 +0200 Subject: [PATCH 042/744] Ignore errors when removing generated files --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 209302302..c1b266dcb 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ clean-backupfiles: find . -name '*.bak' -exec rm -f {} + clean-generated: - -rm utils/*3.py* + rm -f utils/*3.py* pylint: @pylint --rcfile utils/pylintrc sphinx From 765871c86c1ca26f013b6fc489c24fa6dba96e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 17 May 2010 02:23:59 +0200 Subject: [PATCH 043/744] Added latest changes to the Changes.DasIch file --- CHANGES.DasIch | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.DasIch b/CHANGES.DasIch index 711f9a534..3f7167263 100644 --- a/CHANGES.DasIch +++ b/CHANGES.DasIch @@ -5,6 +5,12 @@ This file contains changes made by Daniel Neuhäuser, during the Google Summer of Code 2010, to port Sphinx to Python 3.x. Changes are ordered descending by date. +May 16: - Added utils/convert.py which converts entire directories of python + files with 2to3 and names the converted files foo3.py. + - Modified the Makefile so that in case Python 3 is used the scripts in + utils get converted with utils/convert.py and are used instead of the + Python 2 scripts. + May 10: Fixed a couple of tests and made several small changes. May 9: - Removed ez_setup.py which does not work with Python 3.x. and replaced From ad9055868ee81971bf9a7aa0cd2c6d24a6e55522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 17 May 2010 02:26:31 +0200 Subject: [PATCH 044/744] Added latest reindent.py file --- Makefile | 4 +- utils/reindent.py | 96 +++++++++++++++++++++++++---------------------- 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index c1b266dcb..ecc7722c2 100644 --- a/Makefile +++ b/Makefile @@ -42,9 +42,9 @@ pylint: reindent: convert-utils ifeq ($(PYTHON), python3) - @$(PYTHON) utils/reindent3.py -r -B . + @$(PYTHON) utils/reindent3.py -r -n . else - @$(PYTHON) utils/reindent.py -r -B . + @$(PYTHON) utils/reindent.py -r -n . endif test: build diff --git a/utils/reindent.py b/utils/reindent.py index 63ff7ef73..59828fd86 100755 --- a/utils/reindent.py +++ b/utils/reindent.py @@ -1,16 +1,14 @@ #! /usr/bin/env python # Released to the public domain, by Tim Peters, 03 October 2000. -# -B option added by Georg Brandl, 2006. """reindent [-d][-r][-v] [ path ... ] --d (--dryrun) Dry run. Analyze, but don't make any changes to files. --r (--recurse) Recurse. Search for all .py files in subdirectories too. --B (--no-backup) Don't write .bak backup files. --v (--verbose) Verbose. Print informative msgs; else only names of \ -changed files. --h (--help) Help. Print this usage information and exit. +-d (--dryrun) Dry run. Analyze, but don't make any changes to, files. +-r (--recurse) Recurse. Search for all .py files in subdirectories too. +-n (--nobackup) No backup. Does not make a ".bak" file before reindenting. +-v (--verbose) Verbose. Print informative msgs; else no output. +-h (--help) Help. Print this usage information and exit. Change Python (.py) files to use 4-space indents and no hard tab characters. Also trim excess spaces and tabs from ends of lines, and remove empty lines @@ -34,29 +32,30 @@ resulting .py file won't change it again). The hard part of reindenting is figuring out what to do with comment lines. So long as the input files get a clean bill of health from tabnanny.py, reindent should do a good job. + +The backup file is a copy of the one that is being reindented. The ".bak" +file is generated with shutil.copy(), but some corner cases regarding +user/group and permissions could leave the backup file more readable that +you'd prefer. You can always use the --nobackup option to prevent this. """ __version__ = "1" import tokenize -import os +import os, shutil import sys -verbose = 0 -recurse = 0 -dryrun = 0 -no_backup = 0 - if sys.version_info >= (3, 0): def tokens(readline, tokeneater): for token in tokenize.tokenize(readline): - tokeneater(*token) - - def b(s): - return s.encode('utf-8') + yield tokeneater(*token) else: tokens = tokenize.tokenize - b = str + +verbose = 0 +recurse = 0 +dryrun = 0 +makebackup = True def usage(msg=None): if msg is not None: @@ -72,12 +71,10 @@ def errprint(*args): def main(): import getopt - global verbose, recurse, dryrun, no_backup - + global verbose, recurse, dryrun, makebackup try: - opts, args = getopt.getopt(sys.argv[1:], "drvhB", - ["dryrun", "recurse", "verbose", "help", - "no-backup"]) + opts, args = getopt.getopt(sys.argv[1:], "drnvh", + ["dryrun", "recurse", "nobackup", "verbose", "help"]) except getopt.error, msg: usage(msg) return @@ -86,10 +83,10 @@ def main(): dryrun += 1 elif o in ('-r', '--recurse'): recurse += 1 + elif o in ('-n', '--nobackup'): + makebackup = False elif o in ('-v', '--verbose'): verbose += 1 - elif o in ('-B', '--no-backup'): - no_backup += 1 elif o in ('-h', '--help'): usage() return @@ -109,7 +106,8 @@ def check(file): for name in names: fullname = os.path.join(file, name) if ((recurse and os.path.isdir(fullname) and - not os.path.islink(fullname)) + not os.path.islink(fullname) and + not os.path.split(fullname)[1].startswith(".")) or name.lower().endswith(".py")): check(fullname) return @@ -117,7 +115,7 @@ def check(file): if verbose: print "checking", file, "...", try: - f = open(file, 'rb') + f = open(file) except IOError, msg: errprint("%s: I/O Error: %s" % (file, str(msg))) return @@ -129,26 +127,35 @@ def check(file): print "changed." if dryrun: print "But this is a dry run, so leaving it alone." - else: - print "reindented", file, \ - (dryrun and "(dry run => not really)" or "") if not dryrun: - if not no_backup: - bak = file + ".bak" - if os.path.exists(bak): - os.remove(bak) - os.rename(file, bak) + bak = file + ".bak" + if makebackup: + shutil.copyfile(file, bak) if verbose: - print "renamed", file, "to", bak - f = open(file, "wb") + print "backed up", file, "to", bak + f = open(file, "w") r.write(f) f.close() if verbose: print "wrote new", file + return True else: if verbose: print "unchanged." + return False +def _rstrip(line, JUNK='\n \t'): + """Return line stripped of trailing spaces, tabs, newlines. + + Note that line.rstrip() instead also strips sundry control characters, + but at least one known Emacs user expects to keep junk like that, not + mentioning Barry by name or anything . + """ + + i = len(line) + while i > 0 and line[i-1] in JUNK: + i -= 1 + return line[:i] class Reindenter: @@ -162,7 +169,7 @@ class Reindenter: # File lines, rstripped & tab-expanded. Dummy at start is so # that we can use tokenize's 1-based line numbering easily. # Note that a line is all-blank iff it's "\n". - self.lines = [line.rstrip(b('\n \t')).expandtabs() + b("\n") + self.lines = [_rstrip(line).expandtabs() + "\n" for line in self.raw] self.lines.insert(0, None) self.index = 1 # index into self.lines of next line @@ -177,7 +184,7 @@ class Reindenter: tokens(self.getline, self.tokeneater) # Remove trailing empty lines. lines = self.lines - while lines and lines[-1] == b("\n"): + while lines and lines[-1] == "\n": lines.pop() # Sentinel. stats = self.stats @@ -233,10 +240,10 @@ class Reindenter: else: for line in lines[thisstmt:nextstmt]: if diff > 0: - if line == b("\n"): + if line == "\n": after.append(line) else: - after.append(b(" ") * diff + line) + after.append(" " * diff + line) else: remove = min(getlspace(line), -diff) after.append(line[remove:]) @@ -248,20 +255,19 @@ class Reindenter: # Line-getter for tokenize. def getline(self): if self.index >= len(self.lines): - line = b("") + line = "" else: line = self.lines[self.index] self.index += 1 return line # Line-eater for tokenize. - def tokeneater(self, type, token, scell, end, line, + def tokeneater(self, type, token, (sline, scol), end, line, INDENT=tokenize.INDENT, DEDENT=tokenize.DEDENT, NEWLINE=tokenize.NEWLINE, COMMENT=tokenize.COMMENT, NL=tokenize.NL): - sline, scol = scell if type == NEWLINE: # A program statement, or ENDMARKER, will eventually follow, @@ -297,7 +303,7 @@ class Reindenter: # Count number of leading blanks. def getlspace(line): i, n = 0, len(line) - while i < n and line[i] == b(" "): + while i < n and line[i] == " ": i += 1 return i From 614a7f048fb51de38b1a55460714670c3f6de901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 23 May 2010 01:34:52 +0200 Subject: [PATCH 045/744] make now works without python3 --- Makefile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index ecc7722c2..21a87e367 100644 --- a/Makefile +++ b/Makefile @@ -11,12 +11,13 @@ DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \ -i tests/coverage.py -i env -i utils/convert.py \ -i utils/reindent3.py -i utils/check_sources3.py -i .tox -all: clean-pyc check test +all: clean-pyc clean-backupfiles check test -check: convert-utils ifeq ($(PYTHON), python3) +check: convert-utils @$(PYTHON) utils/check_sources3.py $(DONT_CHECK) . else +check: @$(PYTHON) utils/check_sources.py $(DONT_CHECK) . endif @@ -40,10 +41,11 @@ clean-generated: pylint: @pylint --rcfile utils/pylintrc sphinx -reindent: convert-utils ifeq ($(PYTHON), python3) +reindent: convert-utils @$(PYTHON) utils/reindent3.py -r -n . else +reindent: @$(PYTHON) utils/reindent.py -r -n . endif @@ -57,5 +59,7 @@ covertest: build build: @$(PYTHON) setup.py build +ifeq ($(PYTHON), python3) convert-utils: @python3 utils/convert.py -i utils/convert.py utils/ +endif From a83c48ec8a5f275c5ecb67251945eb454969ef90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 24 May 2010 17:32:00 +0200 Subject: [PATCH 046/744] test_autodoc.test_get_doc now passes --- sphinx/ext/autodoc.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 8a827a91f..1113f97a0 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -411,9 +411,11 @@ class Documenter(object): def get_doc(self, encoding=None): """Decode and return lines of the docstring(s) for the object.""" docstring = self.get_attr(self.object, '__doc__', None) - if docstring: - # make sure we have Unicode docstrings, then sanitize and split - # into lines + # make sure we have Unicode docstrings, then sanitize and split + # into lines + if isinstance(docstring, unicode): + return [prepare_docstring(docstring)] + elif docstring: return [prepare_docstring(force_decode(docstring, encoding))] return [] @@ -934,9 +936,12 @@ class ClassDocumenter(ModuleLevelDocumenter): docstrings = [initdocstring] else: docstrings.append(initdocstring) - - return [prepare_docstring(force_decode(docstring, encoding)) - for docstring in docstrings] + doc = [] + for docstring in docstrings: + if not isinstance(docstring, unicode): + docstring = force_decode(docstring, encoding) + doc.append(prepare_docstring(docstring)) + return doc def add_content(self, more_content, no_docstring=False): if self.doc_as_attr: From f8b12a45dae561b611769bd4f1cd231497d7c2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 24 May 2010 17:35:43 +0200 Subject: [PATCH 047/744] use open instead of file --- tests/path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/path.py b/tests/path.py index 7b89c0cd3..f27e58a9d 100644 --- a/tests/path.py +++ b/tests/path.py @@ -516,7 +516,7 @@ class path(_base): def open(self, mode='r'): """ Open this file. Return a file object. """ - return file(self, mode) + return open(self, mode) def bytes(self): """ Open this file, read all bytes, return them as a string. """ From d1d5d20f65e0cd298fa1a9885c133ae4bddb4c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 24 May 2010 18:13:56 +0200 Subject: [PATCH 048/744] Workaround for 2to3 --- sphinx/ext/intersphinx.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 0a210879a..fb1f0e4ff 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -36,8 +36,10 @@ from sphinx.builders.html import INVENTORY_FILENAME handlers = [urllib2.ProxyHandler(), urllib2.HTTPRedirectHandler(), urllib2.HTTPHandler()] -if hasattr(urllib2, 'HTTPSHandler'): +try: handlers.append(urllib2.HTTPSHandler) +except NameError: + pass urllib2.install_opener(urllib2.build_opener(*handlers)) From bedbbe288ed774d9d26dd114fcf6c2769fc1a2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 24 May 2010 18:25:20 +0200 Subject: [PATCH 049/744] don't assume strings to be byte strings --- sphinx/pycode/__init__.py | 3 ++- sphinx/pycode/pgen2/literals.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index b8e2fded2..cb9c08878 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -98,7 +98,8 @@ class AttrDocVisitor(nodes.NodeVisitor): if not pnode or pnode.type not in (token.INDENT, token.DEDENT): break prefix = pnode.get_prefix() - prefix = prefix.decode(self.encoding) + if not isinstance(prefix, unicode): + prefix = prefix.decode(self.encoding) docstring = prepare_commentdoc(prefix) self.add_docstring(node, docstring) diff --git a/sphinx/pycode/pgen2/literals.py b/sphinx/pycode/pgen2/literals.py index 319002910..d48937028 100644 --- a/sphinx/pycode/pgen2/literals.py +++ b/sphinx/pycode/pgen2/literals.py @@ -66,7 +66,7 @@ uni_escape_re = re.compile(r"\\(\'|\"|\\|[abfnrtv]|x.{0,2}|[0-7]{1,3}|" def evalString(s, encoding=None): regex = escape_re repl = escape - if encoding: + if encoding and not isinstance(s, unicode): s = s.decode(encoding) if s.startswith('u') or s.startswith('U'): regex = uni_escape_re From 24f1d4c12d76b4eb90818b3a10e0cb26ca120a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 24 May 2010 19:41:02 +0200 Subject: [PATCH 050/744] fixed test_markup test --- sphinx/util/osutil.py | 11 +++++++---- tests/test_markup.py | 9 ++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index beab38cbd..1010fb2fe 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -14,6 +14,7 @@ import re import time import errno import shutil +import sys from os import path # Errnos that we need. @@ -124,7 +125,9 @@ no_fn_re = re.compile(r'[^a-zA-Z0-9_-]') def make_filename(string): return no_fn_re.sub('', string) - -def ustrftime(format, *args): - # strftime for unicode strings - return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8') +if sys.version_info < (3, 0): + def ustrftime(format, *args): + # strftime for unicode strings + return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8') +else: + ustrftime = time.strftime diff --git a/tests/test_markup.py b/tests/test_markup.py index 31817df62..6d6badbbc 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -10,6 +10,7 @@ """ import re +import sys from util import * @@ -20,6 +21,12 @@ from sphinx.util import texescape from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator +if sys.version_info > (3, 0): + def b(s): + return s.encode('utf-8') +else: + b = str + def setup_module(): global app, settings, parser texescape.init() # otherwise done by the latex builder @@ -50,7 +57,7 @@ class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator): def verify_re(rst, html_expected, latex_expected): - document = utils.new_document('test data', settings) + document = utils.new_document(b('test data'), settings) document['file'] = 'dummy' parser.parse(rst, document) for msg in document.traverse(nodes.system_message): From ec5a5e739bd93273be9649250443c1267069b203 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:43:40 +0200 Subject: [PATCH 051/744] Move the "b" function to pycompat. --- sphinx/util/osutil.py | 2 +- sphinx/util/pycompat.py | 8 ++++++++ tests/test_markup.py | 8 +------- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 1010fb2fe..250e75741 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -11,10 +11,10 @@ import os import re +import sys import time import errno import shutil -import sys from os import path # Errnos that we need. diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 7bf768fac..8787a144e 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -26,6 +26,14 @@ except NameError: base_exception = Exception +# the ubiquitous "bytes" helper function +if sys.version_info > (3, 0): + def b(s): + return s.encode('utf-8') +else: + b = str + + try: any = any all = all diff --git a/tests/test_markup.py b/tests/test_markup.py index 6d6badbbc..092113bbe 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -10,7 +10,6 @@ """ import re -import sys from util import * @@ -18,15 +17,10 @@ from docutils import frontend, utils, nodes from docutils.parsers import rst from sphinx.util import texescape +from sphinx.util.pycompat import b from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator -if sys.version_info > (3, 0): - def b(s): - return s.encode('utf-8') -else: - b = str - def setup_module(): global app, settings, parser texescape.init() # otherwise done by the latex builder From 6af81b8506d3f5ab7eec86ad031bf89870fd6035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Tue, 25 May 2010 00:55:30 +0200 Subject: [PATCH 052/744] fix line length --- sphinx/util/osutil.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 250e75741..9943b207f 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -128,6 +128,7 @@ def make_filename(string): if sys.version_info < (3, 0): def ustrftime(format, *args): # strftime for unicode strings - return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8') + return time.strftime(unicode(format).encode('utf-8'), *args) \ + .decode('utf-8') else: ustrftime = time.strftime From 7f2990455155c67b906b0cc93d9b20bbbf23d85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 30 May 2010 02:07:53 +0200 Subject: [PATCH 053/744] Use .gettext() instead of .ugettext() on python3 --- sphinx/locale/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index b0b89720c..8df5f0060 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -8,6 +8,7 @@ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import sys import gettext import UserString @@ -178,8 +179,12 @@ pairindextypes = { translator = None -def _(message): - return translator.ugettext(message) +if sys.version_info >= (3, 0): + def _(message): + return translator.gettext(message) +else: + def _(message): + return translator.ugettext(message) def init(locale_dirs, language): global translator From ba02a69c1c9c7fa742e37f477cc99f8c8454c4f2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:46:12 +0200 Subject: [PATCH 054/744] Nit. --- sphinx/locale/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index 8df5f0060..02958457b 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -8,6 +8,7 @@ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ + import sys import gettext import UserString From 918a509efc91189ab548533e29a839ec62d125c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 30 May 2010 02:28:57 +0200 Subject: [PATCH 055/744] Check if a string is not unicode as a workaround for 3.x --- sphinx/highlighting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index f5ea859cb..c94405bc2 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -175,7 +175,7 @@ class PygmentsBridge(object): return True def highlight_block(self, source, lang, linenos=False, warn=None): - if isinstance(source, str): + if not isinstance(source, unicode): source = source.decode() if not pygments: return self.unhighlighted(source) From 23ef216a157c1da346ffc635c128692e367874d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 30 May 2010 15:15:57 +0200 Subject: [PATCH 056/744] encode source code for parsing only on python 2.x --- sphinx/highlighting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index c94405bc2..c168aeffc 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -156,7 +156,7 @@ class PygmentsBridge(object): if sys.version_info >= (2, 5): src = 'from __future__ import with_statement\n' + src - if isinstance(src, unicode): + if sys.version_info < (3, 0) and isinstance(src, unicode): # Non-ASCII chars will only occur in string literals # and comments. If we wanted to give them to the parser # correctly, we'd have to find out the correct source From 31275a34c2a80c6243833f658a465c1a3338f4e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 30 May 2010 17:51:14 +0200 Subject: [PATCH 057/744] Fix encoding in config test and open configs in binary mode to warn for possible encoding errors --- sphinx/config.py | 2 +- tests/test_config.py | 5 +++-- tests/util.py | 11 +++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 2ec769871..07c3d63af 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -165,7 +165,7 @@ class Config(object): try: try: os.chdir(dirname) - f = open(config_file, 'U') + f = open(config_file, 'Ub') try: code = compile(f.read(), config_file, 'exec') finally: diff --git a/tests/test_config.py b/tests/test_config.py index cb4e11056..23d92e39c 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -84,11 +84,12 @@ def test_extension_values(app): @with_tempdir def test_errors_warnings(dir): # test the error for syntax errors in the config file - write_file(dir / 'conf.py', 'project = \n') + write_file(dir / 'conf.py', u'project = \n', 'ascii') raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None) # test the warning for bytestrings with non-ascii content - write_file(dir / 'conf.py', '# -*- coding: latin-1\nproject = "foo\xe4"\n') + write_file(dir / 'conf.py', + u'# -*- coding: latin-1\nproject = "fooä"\n', 'latin-1') cfg = Config(dir, 'conf.py', {}, None) warned = [False] def warn(msg): diff --git a/tests/util.py b/tests/util.py index 1b24af0e2..2cf4a775b 100644 --- a/tests/util.py +++ b/tests/util.py @@ -11,6 +11,7 @@ import sys import StringIO import tempfile import shutil +from codecs import open try: from functools import wraps @@ -191,8 +192,14 @@ def with_tempdir(func): return new_func -def write_file(name, contents): - f = open(str(name), 'wb') +def write_file(name, contents, encoding=None): + if encoding is None: + mode = 'wb' + if isinstance(contents, unicode): + contents = contents.encode('ascii') + else: + mode = 'w' + f = open(str(name), 'wb', encoding=encoding) f.write(contents) f.close() From a153f84a171c9b6e87a10721491661c27b87cee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Tue, 1 Jun 2010 18:10:06 +0200 Subject: [PATCH 058/744] Replaced the path module with my own version --- tests/path.py | 980 +++----------------------------------------------- 1 file changed, 53 insertions(+), 927 deletions(-) diff --git a/tests/path.py b/tests/path.py index f27e58a9d..36ab3a9a0 100644 --- a/tests/path.py +++ b/tests/path.py @@ -1,952 +1,78 @@ -""" path.py - An object representing a path to a file or directory. - -Example: - -from path import path -d = path('/home/guido/bin') -for f in d.files('*.py'): - f.chmod(0755) - -This module requires Python 2.2 or later. - - -URL: http://www.jorendorff.com/articles/python/path -Author: Jason Orendorff (and others - see the url!) -Date: 9 Mar 2007 +#!/usr/bin/env python +# coding: utf-8 """ + path + ~~~~ + :copyright: Copyright 2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import os +import sys +import shutil +from codecs import open -# TODO -# - Tree-walking functions don't avoid symlink loops. Matt Harrison -# sent me a patch for this. -# - Bug in write_text(). It doesn't support Universal newline mode. -# - Better error message in listdir() when self isn't a -# directory. (On Windows, the error message really sucks.) -# - Make sure everything has a good docstring. -# - Add methods for regex find and replace. -# - guess_content_type() method? -# - Perhaps support arguments to touch(). -from __future__ import generators +FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding() -import sys, warnings, os, fnmatch, glob, shutil, codecs -__version__ = '2.2' -__all__ = ['path'] - -# Platform-specific support for path.owner -if os.name == 'nt': - try: - import win32security - except ImportError: - win32security = None -else: - try: - import pwd - except ImportError: - pwd = None - -# Pre-2.3 support. Are unicode filenames supported? -_base = str -_getcwd = os.getcwd -try: - if os.path.supports_unicode_filenames: - _base = unicode - _getcwd = os.getcwdu -except AttributeError: - pass - -# Pre-2.3 workaround for basestring. -try: - basestring -except NameError: - basestring = (str, unicode) - -# Universal newline support -_textmode = 'r' -try: - file -except NameError: - _textmode = 'U' -else: - if hasattr(file, 'newlines'): - _textmode = 'U' - - -class TreeWalkWarning(Warning): - pass - -class path(_base): - """ Represents a filesystem path. - - For documentation on individual methods, consult their - counterparts in os.path. - """ - - # --- Special Python methods. - - def __repr__(self): - return 'path(%s)' % _base.__repr__(self) - - # Adding a path and a string yields a path. - def __add__(self, more): - try: - resultStr = _base.__add__(self, more) - except TypeError: #Python bug - resultStr = NotImplemented - if resultStr is NotImplemented: - return resultStr - return self.__class__(resultStr) - - def __radd__(self, other): - if isinstance(other, basestring): - return self.__class__(other.__add__(self)) - else: - return NotImplemented - - # The / operator joins paths. - def __div__(self, rel): - """ fp.__div__(rel) == fp / rel == fp.joinpath(rel) - - Join two path components, adding a separator character if - needed. - """ - return self.__class__(os.path.join(self, rel)) - - # Make the / operator work even when true division is enabled. - __truediv__ = __div__ - - def getcwd(cls): - """ Return the current working directory as a path object. """ - return cls(_getcwd()) - getcwd = classmethod(getcwd) - - - # --- Operations on path strings. - - isabs = os.path.isabs - def abspath(self): return self.__class__(os.path.abspath(self)) - def normcase(self): return self.__class__(os.path.normcase(self)) - def normpath(self): return self.__class__(os.path.normpath(self)) - def realpath(self): return self.__class__(os.path.realpath(self)) - def expanduser(self): return self.__class__(os.path.expanduser(self)) - def expandvars(self): return self.__class__(os.path.expandvars(self)) - def dirname(self): return self.__class__(os.path.dirname(self)) - basename = os.path.basename - - def expand(self): - """ Clean up a filename by calling expandvars(), - expanduser(), and normpath() on it. - - This is commonly everything needed to clean up a filename - read from a configuration file, for example. - """ - return self.expandvars().expanduser().normpath() - - def _get_namebase(self): - base, ext = os.path.splitext(self.name) - return base - - def _get_ext(self): - f, ext = os.path.splitext(_base(self)) - return ext - - def _get_drive(self): - drive, r = os.path.splitdrive(self) - return self.__class__(drive) - - parent = property( - dirname, None, None, - """ This path's parent directory, as a new path object. - - For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib') - """) - - name = property( - basename, None, None, - """ The name of this file or directory without the full path. - - For example, path('/usr/local/lib/libpython.so').name == 'libpython.so' - """) - - namebase = property( - _get_namebase, None, None, - """ The same as path.name, but with one file extension stripped off. - - For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz', - but path('/home/guido/python.tar.gz').namebase == 'python.tar' - """) - - ext = property( - _get_ext, None, None, - """ The file extension, for example '.py'. """) - - drive = property( - _get_drive, None, None, - """ The drive specifier, for example 'C:'. - This is always empty on systems that don't use drive specifiers. - """) - - def splitpath(self): - """ p.splitpath() -> Return (p.parent, p.name). """ - parent, child = os.path.split(self) - return self.__class__(parent), child - - def splitdrive(self): - """ p.splitdrive() -> Return (p.drive, ). - - Split the drive specifier from this path. If there is - no drive specifier, p.drive is empty, so the return value - is simply (path(''), p). This is always the case on Unix. - """ - drive, rel = os.path.splitdrive(self) - return self.__class__(drive), rel - - def splitext(self): - """ p.splitext() -> Return (p.stripext(), p.ext). - - Split the filename extension from this path and return - the two parts. Either part may be empty. - - The extension is everything from '.' to the end of the - last path segment. This has the property that if - (a, b) == p.splitext(), then a + b == p. - """ - filename, ext = os.path.splitext(self) - return self.__class__(filename), ext - - def stripext(self): - """ p.stripext() -> Remove one file extension from the path. - - For example, path('/home/guido/python.tar.gz').stripext() - returns path('/home/guido/python.tar'). - """ - return self.splitext()[0] - - if hasattr(os.path, 'splitunc'): - def splitunc(self): - unc, rest = os.path.splitunc(self) - return self.__class__(unc), rest - - def _get_uncshare(self): - unc, r = os.path.splitunc(self) - return self.__class__(unc) - - uncshare = property( - _get_uncshare, None, None, - """ The UNC mount point for this path. - This is empty for paths on local drives. """) - - def joinpath(self, *args): - """ Join two or more path components, adding a separator - character (os.sep) if needed. Returns a new path - object. - """ - return self.__class__(os.path.join(self, *args)) - - def splitall(self): - r""" Return a list of the path components in this path. - - The first item in the list will be a path. Its value will be - either os.curdir, os.pardir, empty, or the root directory of - this path (for example, '/' or 'C:\\'). The other items in - the list will be strings. - - path.path.joinpath(*result) will yield the original path. - """ - parts = [] - loc = self - while loc != os.curdir and loc != os.pardir: - prev = loc - loc, child = prev.splitpath() - if loc == prev: - break - parts.append(child) - parts.append(loc) - parts.reverse() - return parts - - def relpath(self): - """ Return this path as a relative path, - based from the current working directory. - """ - cwd = self.__class__(os.getcwd()) - return cwd.relpathto(self) - - def relpathto(self, dest): - """ Return a relative path from self to dest. - - If there is no relative path from self to dest, for example if - they reside on different drives in Windows, then this returns - dest.abspath(). - """ - origin = self.abspath() - dest = self.__class__(dest).abspath() - - orig_list = origin.normcase().splitall() - # Don't normcase dest! We want to preserve the case. - dest_list = dest.splitall() - - if orig_list[0] != os.path.normcase(dest_list[0]): - # Can't get here from there. - return dest - - # Find the location where the two paths start to differ. - i = 0 - for start_seg, dest_seg in zip(orig_list, dest_list): - if start_seg != os.path.normcase(dest_seg): - break - i += 1 - - # Now i is the point where the two paths diverge. - # Need a certain number of "os.pardir"s to work up - # from the origin to the point of divergence. - segments = [os.pardir] * (len(orig_list) - i) - # Need to add the diverging part of dest_list. - segments += dest_list[i:] - if len(segments) == 0: - # If they happen to be identical, use os.curdir. - relpath = os.curdir - else: - relpath = os.path.join(*segments) - return self.__class__(relpath) - - # --- Listing, searching, walking, and matching - - def listdir(self, pattern=None): - """ D.listdir() -> List of items in this directory. - - Use D.files() or D.dirs() instead if you want a listing - of just files or just subdirectories. - - The elements of the list are path objects. - - With the optional 'pattern' argument, this only lists - items whose names match the given pattern. - """ - names = os.listdir(self) - if pattern is not None: - names = fnmatch.filter(names, pattern) - return [self / child for child in names] - - def dirs(self, pattern=None): - """ D.dirs() -> List of this directory's subdirectories. - - The elements of the list are path objects. - This does not walk recursively into subdirectories - (but see path.walkdirs). - - With the optional 'pattern' argument, this only lists - directories whose names match the given pattern. For - example, d.dirs('build-*'). - """ - return [p for p in self.listdir(pattern) if p.isdir()] - - def files(self, pattern=None): - """ D.files() -> List of the files in this directory. - - The elements of the list are path objects. - This does not walk into subdirectories (see path.walkfiles). - - With the optional 'pattern' argument, this only lists files - whose names match the given pattern. For example, - d.files('*.pyc'). - """ - - return [p for p in self.listdir(pattern) if p.isfile()] - - def walk(self, pattern=None, errors='strict'): - """ D.walk() -> iterator over files and subdirs, recursively. - - The iterator yields path objects naming each child item of - this directory and its descendants. This requires that - D.isdir(). - - This performs a depth-first traversal of the directory tree. - Each directory is returned just before all its children. - - The errors= keyword argument controls behavior when an - error occurs. The default is 'strict', which causes an - exception. The other allowed values are 'warn', which - reports the error via warnings.warn(), and 'ignore'. - """ - if errors not in ('strict', 'warn', 'ignore'): - raise ValueError("invalid errors parameter") - - try: - childList = self.listdir() - except Exception: - if errors == 'ignore': - return - elif errors == 'warn': - warnings.warn( - "Unable to list directory '%s': %s" - % (self, sys.exc_info()[1]), - TreeWalkWarning) - return - else: - raise - - for child in childList: - if pattern is None or child.fnmatch(pattern): - yield child - try: - isdir = child.isdir() - except Exception: - if errors == 'ignore': - isdir = False - elif errors == 'warn': - warnings.warn( - "Unable to access '%s': %s" - % (child, sys.exc_info()[1]), - TreeWalkWarning) - isdir = False +class path(str): + if sys.version_info < (3, 0): + def __new__(cls, s, encoding=FILESYSTEMENCODING, errors=None): + if isinstance(s, unicode): + if errors is None: + s = s.encode(encoding) else: - raise + s = s.encode(encoding, errors=errors) + return str.__new__(cls, s) + return str.__new__(cls, s) - if isdir: - for item in child.walk(pattern, errors): - yield item + @property + def parent(self): + return self.__class__(os.path.dirname(self)) - def walkdirs(self, pattern=None, errors='strict'): - """ D.walkdirs() -> iterator over subdirs, recursively. + def abspath(self): + return self.__class__(os.path.abspath(self)) - With the optional 'pattern' argument, this yields only - directories whose names match the given pattern. For - example, mydir.walkdirs('*test') yields only directories - with names ending in 'test'. + def isdir(self): + return os.path.isdir(self) - The errors= keyword argument controls behavior when an - error occurs. The default is 'strict', which causes an - exception. The other allowed values are 'warn', which - reports the error via warnings.warn(), and 'ignore'. - """ - if errors not in ('strict', 'warn', 'ignore'): - raise ValueError("invalid errors parameter") + def isfile(self): + return os.path.isfile(self) + def rmtree(self, ignore_errors=False, onerror=None): + shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror) + + def copytree(self, destination, symlinks=False, ignore=None): + shutil.copytree(self, destination, symlinks=symlinks, ignore=ignore) + + def unlink(self): + os.unlink(self) + + def write_text(self, text, **kwargs): + f = open(self, 'w', **kwargs) try: - dirs = self.dirs() - except Exception: - if errors == 'ignore': - return - elif errors == 'warn': - warnings.warn( - "Unable to list directory '%s': %s" - % (self, sys.exc_info()[1]), - TreeWalkWarning) - return - else: - raise + f.write(text) + finally: + f.close() - for child in dirs: - if pattern is None or child.fnmatch(pattern): - yield child - for subsubdir in child.walkdirs(pattern, errors): - yield subsubdir - - def walkfiles(self, pattern=None, errors='strict'): - """ D.walkfiles() -> iterator over files in D, recursively. - - The optional argument, pattern, limits the results to files - with names that match the pattern. For example, - mydir.walkfiles('*.tmp') yields only files with the .tmp - extension. - """ - if errors not in ('strict', 'warn', 'ignore'): - raise ValueError("invalid errors parameter") - - try: - childList = self.listdir() - except Exception: - if errors == 'ignore': - return - elif errors == 'warn': - warnings.warn( - "Unable to list directory '%s': %s" - % (self, sys.exc_info()[1]), - TreeWalkWarning) - return - else: - raise - - for child in childList: - try: - isfile = child.isfile() - isdir = not isfile and child.isdir() - except: - if errors == 'ignore': - continue - elif errors == 'warn': - warnings.warn( - "Unable to access '%s': %s" - % (self, sys.exc_info()[1]), - TreeWalkWarning) - continue - else: - raise - - if isfile: - if pattern is None or child.fnmatch(pattern): - yield child - elif isdir: - for f in child.walkfiles(pattern, errors): - yield f - - def fnmatch(self, pattern): - """ Return True if self.name matches the given pattern. - - pattern - A filename pattern with wildcards, - for example '*.py'. - """ - return fnmatch.fnmatch(self.name, pattern) - - def glob(self, pattern): - """ Return a list of path objects that match the pattern. - - pattern - a path relative to this directory, with wildcards. - - For example, path('/users').glob('*/bin/*') returns a list - of all the files users have in their bin directories. - """ - cls = self.__class__ - return [cls(s) for s in glob.glob(_base(self / pattern))] - - - # --- Reading or writing an entire file at once. - - def open(self, mode='r'): - """ Open this file. Return a file object. """ - return open(self, mode) - - def bytes(self): - """ Open this file, read all bytes, return them as a string. """ - f = self.open('rb') + def text(self, **kwargs): + f = open(self, mode='U', **kwargs) try: return f.read() finally: f.close() - def write_bytes(self, bytes, append=False): - """ Open this file and write the given bytes to it. - - Default behavior is to overwrite any existing file. - Call p.write_bytes(bytes, append=True) to append instead. - """ - if append: - mode = 'ab' - else: - mode = 'wb' - f = self.open(mode) - try: - f.write(bytes) - finally: - f.close() - - def text(self, encoding=None, errors='strict'): - r""" Open this file, read it in, return the content as a string. - - This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r' - are automatically translated to '\n'. - - Optional arguments: - - encoding - The Unicode encoding (or character set) of - the file. If present, the content of the file is - decoded and returned as a unicode object; otherwise - it is returned as an 8-bit str. - errors - How to handle Unicode errors; see help(str.decode) - for the options. Default is 'strict'. - """ - if encoding is None: - # 8-bit - f = self.open(_textmode) - try: - return f.read() - finally: - f.close() - else: - # Unicode - f = codecs.open(self, 'r', encoding, errors) - # (Note - Can't use 'U' mode here, since codecs.open - # doesn't support 'U' mode, even in Python 2.3.) - try: - t = f.read() - finally: - f.close() - return (t.replace(u'\r\n', u'\n') - .replace(u'\r\x85', u'\n') - .replace(u'\r', u'\n') - .replace(u'\x85', u'\n') - .replace(u'\u2028', u'\n')) - - def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False): - r""" Write the given text to this file. - - The default behavior is to overwrite any existing file; - to append instead, use the 'append=True' keyword argument. - - There are two differences between path.write_text() and - path.write_bytes(): newline handling and Unicode handling. - See below. - - Parameters: - - - text - str/unicode - The text to be written. - - - encoding - str - The Unicode encoding that will be used. - This is ignored if 'text' isn't a Unicode string. - - - errors - str - How to handle Unicode encoding errors. - Default is 'strict'. See help(unicode.encode) for the - options. This is ignored if 'text' isn't a Unicode - string. - - - linesep - keyword argument - str/unicode - The sequence of - characters to be used to mark end-of-line. The default is - os.linesep. You can also specify None; this means to - leave all newlines as they are in 'text'. - - - append - keyword argument - bool - Specifies what to do if - the file already exists (True: append to the end of it; - False: overwrite it.) The default is False. - - - --- Newline handling. - - write_text() converts all standard end-of-line sequences - ('\n', '\r', and '\r\n') to your platform's default end-of-line - sequence (see os.linesep; on Windows, for example, the - end-of-line marker is '\r\n'). - - If you don't like your platform's default, you can override it - using the 'linesep=' keyword argument. If you specifically want - write_text() to preserve the newlines as-is, use 'linesep=None'. - - This applies to Unicode text the same as to 8-bit text, except - there are three additional standard Unicode end-of-line sequences: - u'\x85', u'\r\x85', and u'\u2028'. - - (This is slightly different from when you open a file for - writing with fopen(filename, "w") in C or file(filename, 'w') - in Python.) - - - --- Unicode - - If 'text' isn't Unicode, then apart from newline handling, the - bytes are written verbatim to the file. The 'encoding' and - 'errors' arguments are not used and must be omitted. - - If 'text' is Unicode, it is first converted to bytes using the - specified 'encoding' (or the default encoding if 'encoding' - isn't specified). The 'errors' argument applies only to this - conversion. - - """ - if isinstance(text, unicode): - if linesep is not None: - # Convert all standard end-of-line sequences to - # ordinary newline characters. - text = (text.replace(u'\r\n', u'\n') - .replace(u'\r\x85', u'\n') - .replace(u'\r', u'\n') - .replace(u'\x85', u'\n') - .replace(u'\u2028', u'\n')) - text = text.replace(u'\n', linesep) - if encoding is None: - encoding = sys.getdefaultencoding() - bytes = text.encode(encoding, errors) - else: - # It is an error to specify an encoding if 'text' is - # an 8-bit string. - assert encoding is None - - if linesep is not None: - text = (text.replace('\r\n', '\n') - .replace('\r', '\n')) - bytes = text.replace('\n', linesep) - - self.write_bytes(bytes, append) - - def lines(self, encoding=None, errors='strict', retain=True): - r""" Open this file, read all lines, return them in a list. - - Optional arguments: - encoding - The Unicode encoding (or character set) of - the file. The default is None, meaning the content - of the file is read as 8-bit characters and returned - as a list of (non-Unicode) str objects. - errors - How to handle Unicode errors; see help(str.decode) - for the options. Default is 'strict' - retain - If true, retain newline characters; but all newline - character combinations ('\r', '\n', '\r\n') are - translated to '\n'. If false, newline characters are - stripped off. Default is True. - - This uses 'U' mode in Python 2.3 and later. - """ - if encoding is None and retain: - f = self.open(_textmode) - try: - return f.readlines() - finally: - f.close() - else: - return self.text(encoding, errors).splitlines(retain) - - def write_lines(self, lines, encoding=None, errors='strict', - linesep=os.linesep, append=False): - r""" Write the given lines of text to this file. - - By default this overwrites any existing file at this path. - - This puts a platform-specific newline sequence on every line. - See 'linesep' below. - - lines - A list of strings. - - encoding - A Unicode encoding to use. This applies only if - 'lines' contains any Unicode strings. - - errors - How to handle errors in Unicode encoding. This - also applies only to Unicode strings. - - linesep - The desired line-ending. This line-ending is - applied to every line. If a line already has any - standard line ending ('\r', '\n', '\r\n', u'\x85', - u'\r\x85', u'\u2028'), that will be stripped off and - this will be used instead. The default is os.linesep, - which is platform-dependent ('\r\n' on Windows, '\n' on - Unix, etc.) Specify None to write the lines as-is, - like file.writelines(). - - Use the keyword argument append=True to append lines to the - file. The default is to overwrite the file. Warning: - When you use this with Unicode data, if the encoding of the - existing data in the file is different from the encoding - you specify with the encoding= parameter, the result is - mixed-encoding data, which can really confuse someone trying - to read the file later. - """ - if append: - mode = 'ab' - else: - mode = 'wb' - f = self.open(mode) - try: - for line in lines: - isUnicode = isinstance(line, unicode) - if linesep is not None: - # Strip off any existing line-end and add the - # specified linesep string. - if isUnicode: - if line[-2:] in (u'\r\n', u'\x0d\x85'): - line = line[:-2] - elif line[-1:] in (u'\r', u'\n', - u'\x85', u'\u2028'): - line = line[:-1] - else: - if line[-2:] == '\r\n': - line = line[:-2] - elif line[-1:] in ('\r', '\n'): - line = line[:-1] - line += linesep - if isUnicode: - if encoding is None: - encoding = sys.getdefaultencoding() - line = line.encode(encoding, errors) - f.write(line) - finally: - f.close() - - # --- Methods for querying the filesystem. - - exists = os.path.exists - isdir = os.path.isdir - isfile = os.path.isfile - islink = os.path.islink - ismount = os.path.ismount - - if hasattr(os.path, 'samefile'): - samefile = os.path.samefile - - getatime = os.path.getatime - atime = property( - getatime, None, None, - """ Last access time of the file. """) - - getmtime = os.path.getmtime - mtime = property( - getmtime, None, None, - """ Last-modified time of the file. """) - - if hasattr(os.path, 'getctime'): - getctime = os.path.getctime - ctime = property( - getctime, None, None, - """ Creation time of the file. """) - - getsize = os.path.getsize - size = property( - getsize, None, None, - """ Size of the file, in bytes. """) - - if hasattr(os, 'access'): - def access(self, mode): - """ Return true if current user has access to this path. - - mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK - """ - return os.access(self, mode) - - def stat(self): - """ Perform a stat() system call on this path. """ - return os.stat(self) - - def lstat(self): - """ Like path.stat(), but do not follow symbolic links. """ - return os.lstat(self) - - def get_owner(self): - r""" Return the name of the owner of this file or directory. - - This follows symbolic links. - - On Windows, this returns a name of the form ur'DOMAIN\User Name'. - On Windows, a group can own a file or directory. - """ - if os.name == 'nt': - if win32security is None: - raise Exception("path.owner requires win32all to be installed") - desc = win32security.GetFileSecurity( - self, win32security.OWNER_SECURITY_INFORMATION) - sid = desc.GetSecurityDescriptorOwner() - account, domain, typecode = win32security.LookupAccountSid(None, sid) - return domain + u'\\' + account - else: - if pwd is None: - raise NotImplementedError("path.owner is not implemented on this platform.") - st = self.stat() - return pwd.getpwuid(st.st_uid).pw_name - - owner = property( - get_owner, None, None, - """ Name of the owner of this file or directory. """) - - if hasattr(os, 'statvfs'): - def statvfs(self): - """ Perform a statvfs() system call on this path. """ - return os.statvfs(self) - - if hasattr(os, 'pathconf'): - def pathconf(self, name): - return os.pathconf(self, name) - - - # --- Modifying operations on files and directories - - def utime(self, times): - """ Set the access and modified times of this file. """ - os.utime(self, times) - - def chmod(self, mode): - os.chmod(self, mode) - - if hasattr(os, 'chown'): - def chown(self, uid, gid): - os.chown(self, uid, gid) - - def rename(self, new): - os.rename(self, new) - - def renames(self, new): - os.renames(self, new) - - - # --- Create/delete operations on directories - - def mkdir(self, mode=0777): - os.mkdir(self, mode) + def exists(self): + return os.path.exists(self) def makedirs(self, mode=0777): os.makedirs(self, mode) - def rmdir(self): - os.rmdir(self) + def joinpath(self, *args): + return self.__class__(os.path.join(self, *map(self.__class__, args))) - def removedirs(self): - os.removedirs(self) - - - # --- Modifying operations on files - - def touch(self): - """ Set the access/modified times of this file to the current time. - Create the file if it does not exist. - """ - fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666) - os.close(fd) - os.utime(self, None) - - def remove(self): - os.remove(self) - - def unlink(self): - os.unlink(self) - - - # --- Links - - if hasattr(os, 'link'): - def link(self, newpath): - """ Create a hard link at 'newpath', pointing to this file. """ - os.link(self, newpath) - - if hasattr(os, 'symlink'): - def symlink(self, newlink): - """ Create a symbolic link at 'newlink', pointing here. """ - os.symlink(self, newlink) - - if hasattr(os, 'readlink'): - def readlink(self): - """ Return the path to which this symbolic link points. - - The result may be an absolute or a relative path. - """ - return self.__class__(os.readlink(self)) - - def readlinkabs(self): - """ Return the path to which this symbolic link points. - - The result is always an absolute path. - """ - p = self.readlink() - if p.isabs(): - return p - else: - return (self.parent / p).abspath() - - - # --- High-level functions from shutil - - copyfile = shutil.copyfile - copymode = shutil.copymode - copystat = shutil.copystat - copy = shutil.copy - copy2 = shutil.copy2 - copytree = shutil.copytree - if hasattr(shutil, 'move'): - move = shutil.move - rmtree = shutil.rmtree - - - # --- Special stuff from os - - if hasattr(os, 'chroot'): - def chroot(self): - os.chroot(self) - - if hasattr(os, 'startfile'): - def startfile(self): - os.startfile(self) + __div__ = __truediv__ = joinpath + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, str.__repr__(self)) From 2683b839d5b701e4e7aa46efe1e57fed804afd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Tue, 1 Jun 2010 19:59:38 +0200 Subject: [PATCH 059/744] Added a movetree method to the path object to make it more consistent along with documentation for every method of it. --- tests/path.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tests/path.py b/tests/path.py index 36ab3a9a0..dccdac3ab 100644 --- a/tests/path.py +++ b/tests/path.py @@ -17,6 +17,9 @@ FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding() class path(str): + """ + Represents a path which behaves like a string. + """ if sys.version_info < (3, 0): def __new__(cls, s, encoding=FILESYSTEMENCODING, errors=None): if isinstance(s, unicode): @@ -29,27 +32,103 @@ class path(str): @property def parent(self): + """ + The name of the directory the file or directory is in. + """ return self.__class__(os.path.dirname(self)) def abspath(self): + """ + Returns the absolute path. + """ return self.__class__(os.path.abspath(self)) + def isabs(self): + """ + Returns ``True`` if the path is absolute. + """ + return os.path.isabs(self) + def isdir(self): + """ + Returns ``True`` if the path is a directory. + """ return os.path.isdir(self) def isfile(self): + """ + Returns ``True`` if the path is a file. + """ return os.path.isfile(self) + def islink(self): + """ + Returns ``True`` if the path is a symbolic link. + """ + return os.path.islink(self) + + def ismount(self): + """ + Returns ``True`` if the path is a mount point. + """ + return os.path.ismount(self) + def rmtree(self, ignore_errors=False, onerror=None): + """ + Removes the file or directory and any files or directories it may + contain. + + :param ignore_errors: + If ``True`` errors are silently ignored, otherwise an exception + is raised in case an error occurs. + + :param onerror: + A callback which gets called with the arguments `func`, `path` and + `exc_info`. `func` is one of :func:`os.listdir`, :func:`os.remove` + or :func:`os.rmdir`. `path` is the argument to the function which + caused it to fail and `exc_info` is a tuple as returned by + :func:`sys.exc_info`. + """ shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror) def copytree(self, destination, symlinks=False, ignore=None): + """ + Recursively copy a directory to the given `destination`. If the given + `destination` does not exist it will be created. + + :param symlinks: + If ``True`` symbolic links in the source tree result in symbolic + links in the destination tree otherwise the contents of the files + pointed to by the symbolic links are copied. + + :param ignore: + A callback which gets called with the path of the directory being + copied and a list of paths as returned by :func:`os.listdir`. + """ shutil.copytree(self, destination, symlinks=symlinks, ignore=ignore) + def movetree(self, destination): + """ + Recursively move the file or directory to the given `destination` + similar to the Unix "mv" command. + + If the `destination` is a file it may be overwritten depending on the + :func:`os.rename` semantics. + """ + shutil.move(self, destination) + + move = movetree + def unlink(self): + """ + Removes a file. + """ os.unlink(self) def write_text(self, text, **kwargs): + """ + Writes the given `text` to the file. + """ f = open(self, 'w', **kwargs) try: f.write(text) @@ -57,6 +136,9 @@ class path(str): f.close() def text(self, **kwargs): + """ + Returns the text in the file. + """ f = open(self, mode='U', **kwargs) try: return f.read() @@ -64,12 +146,28 @@ class path(str): f.close() def exists(self): + """ + Returns ``True`` if the path exist. + """ return os.path.exists(self) + def lexists(self): + """ + Returns ``True`` if the path exists unless it is a broken symbolic + link. + """ + return os.path.lexists(self) + def makedirs(self, mode=0777): + """ + Recursively create directories. + """ os.makedirs(self, mode) def joinpath(self, *args): + """ + Joins the path with the argument given and returns the result. + """ return self.__class__(os.path.join(self, *map(self.__class__, args))) __div__ = __truediv__ = joinpath From 87bbe8ddd50deb29eead624ff91356ede8a3fea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 6 Jun 2010 23:13:06 +0200 Subject: [PATCH 060/744] don't use string.strip anymore --- tests/test_autosummary.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_autosummary.py b/tests/test_autosummary.py index 7e3093676..20fb06e0e 100644 --- a/tests/test_autosummary.py +++ b/tests/test_autosummary.py @@ -9,8 +9,6 @@ :license: BSD, see LICENSE for details. """ -import string - from util import * from sphinx.ext.autosummary import mangle_signature @@ -27,7 +25,7 @@ def test_mangle_signature(): (a, b, c='foobar()', d=123) :: (a, b[, c, d]) """ - TEST = [map(string.strip, x.split("::")) for x in TEST.split("\n") + TEST = [map(lambda x: x.strip(), x.split("::")) for x in TEST.split("\n") if '::' in x] for inp, outp in TEST: res = mangle_signature(inp).strip().replace(u"\u00a0", " ") From 434da2a4fbd48e437d77b10a70e927ab83309e1c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:51:57 +0200 Subject: [PATCH 061/744] Use next() function instead of iter.next(). --- sphinx/pycode/__init__.py | 3 ++- sphinx/util/pycompat.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index cb9c08878..ef92297c7 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -18,6 +18,7 @@ from sphinx.errors import PycodeError from sphinx.pycode import nodes from sphinx.pycode.pgen2 import driver, token, tokenize, parse, literals from sphinx.util import get_module_source +from sphinx.util.pycompat import next from sphinx.util.docstrings import prepare_docstring, prepare_commentdoc @@ -279,7 +280,7 @@ class ModuleAnalyzer(object): result[fullname] = (dtype, startline, endline) expect_indent = False if tok in ('def', 'class'): - name = tokeniter.next()[1] + name = next(tokeniter)[1] namespace.append(name) fullname = '.'.join(namespace) stack.append((tok, fullname, spos[0], indent)) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 8787a144e..365cd703c 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -34,6 +34,14 @@ else: b = str +try: + next +except NameError: + # this is on Python 2, where the method is called "next" + def next(iterator): + return iterator.next() + + try: any = any all = all From 563f506d1b72188c711df57ad85954869e842550 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:08:44 +0200 Subject: [PATCH 062/744] Fix assignment. --- sphinx/util/pycompat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 365cd703c..0725545da 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -35,7 +35,7 @@ else: try: - next + next = next except NameError: # this is on Python 2, where the method is called "next" def next(iterator): From 57272142991d7e26fa6bcadb60175a1bd6a23e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 7 Jun 2010 00:44:09 +0200 Subject: [PATCH 063/744] open file in binary mode to get byte strings on python3 --- sphinx/directives/code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 0647daf09..54d8edf19 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -119,7 +119,7 @@ class LiteralInclude(Directive): encoding = self.options.get('encoding', env.config.source_encoding) codec_info = codecs.lookup(encoding) try: - f = codecs.StreamReaderWriter(open(fn, 'U'), + f = codecs.StreamReaderWriter(open(fn, 'Ub'), codec_info[2], codec_info[3], 'strict') lines = f.readlines() f.close() From 817e1dd4cda3c23868632c7493c3b764d7bc34ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 7 Jun 2010 01:34:01 +0200 Subject: [PATCH 064/744] fixed file handling and parsing in intersphinx so it properly handles encodings --- sphinx/ext/intersphinx.py | 27 +++++++++++++++++++-------- tests/test_intersphinx.py | 17 ++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index fb1f0e4ff..31de2315c 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -28,12 +28,20 @@ import time import zlib import urllib2 import posixpath +import codecs +import sys from os import path from docutils import nodes from sphinx.builders.html import INVENTORY_FILENAME +if sys.version_info >= (3, 0): + def b(s): + return s.encode('utf-8') +else: + b = str + handlers = [urllib2.ProxyHandler(), urllib2.HTTPRedirectHandler(), urllib2.HTTPHandler()] try: @@ -43,11 +51,14 @@ except NameError: urllib2.install_opener(urllib2.build_opener(*handlers)) +UTF8StreamReader = codecs.lookup('utf-8')[2] + def read_inventory_v1(f, uri, join): + f = UTF8StreamReader(f) invdata = {} line = f.next() - projname = line.rstrip()[11:].decode('utf-8') + projname = line.rstrip()[11:] line = f.next() version = line.rstrip()[11:] for line in f: @@ -70,25 +81,25 @@ def read_inventory_v2(f, uri, join, bufsize=16*1024): projname = line.rstrip()[11:].decode('utf-8') line = f.readline() version = line.rstrip()[11:].decode('utf-8') - line = f.readline() + line = f.readline().decode('utf-8') if 'zlib' not in line: raise ValueError def read_chunks(): decompressor = zlib.decompressobj() - for chunk in iter(lambda: f.read(bufsize), ''): + for chunk in iter(lambda: f.read(bufsize), b('')): yield decompressor.decompress(chunk) yield decompressor.flush() def split_lines(iter): - buf = '' + buf = b('') for chunk in iter: buf += chunk - lineend = buf.find('\n') + lineend = buf.find(b('\n')) while lineend != -1: yield buf[:lineend].decode('utf-8') buf = buf[lineend+1:] - lineend = buf.find('\n') + lineend = buf.find(b('\n')) assert not buf for line in split_lines(read_chunks()): @@ -111,13 +122,13 @@ def fetch_inventory(app, uri, inv): if inv.find('://') != -1: f = urllib2.urlopen(inv) else: - f = open(path.join(app.srcdir, inv)) + f = open(path.join(app.srcdir, inv), 'rb') except Exception, err: app.warn('intersphinx inventory %r not fetchable due to ' '%s: %s' % (inv, err.__class__, err)) return try: - line = f.readline().rstrip() + line = f.readline().rstrip().decode('utf-8') try: if line == '# Sphinx inventory version 1': invdata = read_inventory_v1(f, uri, join) diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py index 8b6547e54..4f70bd205 100644 --- a/tests/test_intersphinx.py +++ b/tests/test_intersphinx.py @@ -11,7 +11,10 @@ import zlib import posixpath -from cStringIO import StringIO +try: + from io import BytesIO +except ImportError: + from cStringIO import StringIO as BytesIO from docutils import nodes @@ -28,23 +31,23 @@ inventory_v1 = '''\ # Version: 1.0 module mod foo.html module.cls class foo.html -''' +'''.encode('utf-8') inventory_v2 = '''\ # Sphinx inventory version 2 # Project: foo # Version: 2.0 # The remainder of this file is compressed with zlib. -''' + zlib.compress('''\ +'''.encode('utf-8') + zlib.compress('''\ module1 py:module 0 foo.html#module-module1 Long Module desc module2 py:module 0 foo.html#module-$ - module1.func py:function 1 sub/foo.html#$ - CFunc c:function 2 cfunc.html#CFunc - -''') +'''.encode('utf-8')) def test_read_inventory_v1(): - f = StringIO(inventory_v1) + f = BytesIO(inventory_v1) f.readline() invdata = read_inventory_v1(f, '/util', posixpath.join) assert invdata['py:module']['module'] == \ @@ -54,12 +57,12 @@ def test_read_inventory_v1(): def test_read_inventory_v2(): - f = StringIO(inventory_v2) + f = BytesIO(inventory_v2) f.readline() invdata1 = read_inventory_v2(f, '/util', posixpath.join) # try again with a small buffer size to test the chunking algorithm - f = StringIO(inventory_v2) + f = BytesIO(inventory_v2) f.readline() invdata2 = read_inventory_v2(f, '/util', posixpath.join, bufsize=5) From 2737ec4dc9ab633f38765e9553a85043fe6fe49b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:54:24 +0200 Subject: [PATCH 065/744] Use b() from pycompat. --- sphinx/ext/intersphinx.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 31de2315c..07ee24e38 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -26,21 +26,16 @@ import time import zlib +import codecs import urllib2 import posixpath -import codecs -import sys from os import path from docutils import nodes from sphinx.builders.html import INVENTORY_FILENAME +from sphinx.util.pycompat import b -if sys.version_info >= (3, 0): - def b(s): - return s.encode('utf-8') -else: - b = str handlers = [urllib2.ProxyHandler(), urllib2.HTTPRedirectHandler(), urllib2.HTTPHandler()] From bade03c8243cbdf7296126c3a1c54bccccc354ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 12 Jun 2010 15:16:57 +0200 Subject: [PATCH 066/744] Copy converted tests to build/lib/tests not build/lib/test --- tests/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run.py b/tests/run.py index 5fd30d62b..f384fe267 100755 --- a/tests/run.py +++ b/tests/run.py @@ -14,10 +14,10 @@ import sys from os import path, chdir if sys.version_info >= (3,): - print('Copying and converting sources to build/lib/test...') + print('Copying and converting sources to build/lib/tests...') from distutils.util import copydir_run_2to3 testroot = path.dirname(__file__) or '.' - newroot = path.join(testroot, path.pardir, 'build', 'lib', 'test') + newroot = path.join(testroot, path.pardir, 'build', 'lib', 'tests') copydir_run_2to3(testroot, newroot) # switch to the converted dir so nose tests the right tests chdir(newroot) From 79a958b867c2941d68fbb9bd594a1c26f511003c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 12 Jun 2010 20:21:14 +0200 Subject: [PATCH 067/744] Remove unnecessary code --- tests/path.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/path.py b/tests/path.py index dccdac3ab..28a8c22af 100644 --- a/tests/path.py +++ b/tests/path.py @@ -21,12 +21,9 @@ class path(str): Represents a path which behaves like a string. """ if sys.version_info < (3, 0): - def __new__(cls, s, encoding=FILESYSTEMENCODING, errors=None): + def __new__(cls, s, encoding=FILESYSTEMENCODING, errors='strict'): if isinstance(s, unicode): - if errors is None: - s = s.encode(encoding) - else: - s = s.encode(encoding, errors=errors) + s = s.encode(encoding, errors=errors) return str.__new__(cls, s) return str.__new__(cls, s) From f0c316f6c57632dbdd2a13cb7021db90854138f9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:55:39 +0200 Subject: [PATCH 068/744] Pass document name as bytes. --- sphinx/builders/html.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 8d96c146c..c5f7312a4 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -35,7 +35,7 @@ from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \ movefile, ustrftime, copyfile from sphinx.util.nodes import inline_all_toctrees from sphinx.util.matching import patmatch, compile_matchers -from sphinx.util.pycompat import any +from sphinx.util.pycompat import any, b from sphinx.errors import SphinxError from sphinx.locale import _ from sphinx.search import js_index @@ -199,7 +199,7 @@ class StandaloneHTMLBuilder(Builder): """Utility: Render a lone doctree node.""" if node is None: return {'fragment': ''} - doc = new_document('') + doc = new_document(b('')) doc.append(node) if self._publisher is None: From ddefaff0b7144d81e3fcedc03d563becc99f90cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 13 Jun 2010 23:08:32 +0200 Subject: [PATCH 069/744] fix a unboundlocalerror occuring with python3 --- sphinx/ext/doctest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 9d681f904..62fbfdff4 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -149,14 +149,14 @@ class TestCode(object): class SphinxDocTestRunner(doctest.DocTestRunner): def summarize(self, out, verbose=None): - io = StringIO.StringIO() + string_io = StringIO.StringIO() old_stdout = sys.stdout - sys.stdout = io + sys.stdout = string_io try: res = doctest.DocTestRunner.summarize(self, verbose) finally: sys.stdout = old_stdout - out(io.getvalue()) + out(string_io.getvalue()) return res def _DocTestRunner__patched_linecache_getlines(self, filename, From 4534657f3e37a8aa59e5135f9a9f6d18f1ab6aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Tue, 15 Jun 2010 23:16:33 +0200 Subject: [PATCH 070/744] update distribute_setup.py from http://python-distribute.org/distribute_setup.py --- distribute_setup.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/distribute_setup.py b/distribute_setup.py index 4f7bd08c0..37117b34e 100644 --- a/distribute_setup.py +++ b/distribute_setup.py @@ -46,7 +46,7 @@ except ImportError: args = [quote(arg) for arg in args] return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 -DEFAULT_VERSION = "0.6.12" +DEFAULT_VERSION = "0.6.13" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" @@ -227,7 +227,6 @@ def _no_sandbox(function): return __no_sandbox -@_no_sandbox def _patch_file(path, content): """Will backup the file then patch it""" existing_content = open(path).read() @@ -244,6 +243,7 @@ def _patch_file(path, content): f.close() return True +_patch_file = _no_sandbox(_patch_file) def _same_content(path, content): return open(path).read() == content @@ -254,7 +254,6 @@ def _rename_path(path): os.rename(path, new_name) return new_name -@_no_sandbox def _remove_flat_installation(placeholder): if not os.path.isdir(placeholder): log.warn('Unkown installation at %s', placeholder) @@ -288,13 +287,13 @@ def _remove_flat_installation(placeholder): 'Setuptools distribution', element) return True +_remove_flat_installation = _no_sandbox(_remove_flat_installation) def _after_install(dist): log.warn('After install bootstrap.') placeholder = dist.get_command_obj('install').install_purelib _create_fake_setuptools_pkg_info(placeholder) -@_no_sandbox def _create_fake_setuptools_pkg_info(placeholder): if not placeholder or not os.path.exists(placeholder): log.warn('Could not find the install location') @@ -322,7 +321,8 @@ def _create_fake_setuptools_pkg_info(placeholder): finally: f.close() -@_no_sandbox +_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) + def _patch_egg_dir(path): # let's check if it's already patched pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') @@ -341,6 +341,7 @@ def _patch_egg_dir(path): f.close() return True +_patch_egg_dir = _no_sandbox(_patch_egg_dir) def _before_install(): log.warn('Before install bootstrap.') @@ -360,8 +361,8 @@ def _under_prefix(location): if len(args) > index: top_dir = args[index+1] return location.startswith(top_dir) - elif option == '--user' and USER_SITE is not None: - return location.startswith(USER_SITE) + if arg == '--user' and USER_SITE is not None: + return location.startswith(USER_SITE) return True @@ -420,6 +421,9 @@ def _fake_setuptools(): def _relaunch(): log.warn('Relaunching...') # we have to relaunch the process + # pip marker to avoid a relaunch bug + if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: + sys.argv[0] = 'setup.py' args = [sys.executable] + sys.argv sys.exit(subprocess.call(args)) From bf14a7c362e97127ca6ab60d962e2731c0f337fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Wed, 16 Jun 2010 23:20:30 +0200 Subject: [PATCH 071/744] Encode strings after they have been formatted --- sphinx/builders/html.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index c5f7312a4..4e07acf78 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -727,10 +727,12 @@ class StandaloneHTMLBuilder(Builder): self.info(bold('dumping object inventory... '), nonl=True) f = open(path.join(self.outdir, INVENTORY_FILENAME), 'wb') try: - f.write('# Sphinx inventory version 2\n') - f.write('# Project: %s\n' % self.config.project.encode('utf-8')) - f.write('# Version: %s\n' % self.config.version.encode('utf-8')) - f.write('# The remainder of this file is compressed using zlib.\n') + f.write((u'# Sphinx inventory version 2\n' + u'# Project: %s\n' + u'# Version: %s\n' + u'# The remainder of this file is compressed using zlib.\n' + % (self.config.project, self.config.version))\ + .encode('utf-8')) compressor = zlib.compressobj(9) for domainname, domain in self.env.domains.iteritems(): for name, dispname, type, docname, anchor, prio in \ @@ -742,11 +744,9 @@ class StandaloneHTMLBuilder(Builder): if dispname == name: dispname = u'-' f.write(compressor.compress( - '%s %s:%s %s %s %s\n' % (name.encode('utf-8'), - domainname.encode('utf-8'), - type.encode('utf-8'), prio, - uri.encode('utf-8'), - dispname.encode('utf-8')))) + (u'%s %s:%s %s %s %s\n' % (name, domainname, type, prio, + uri, dispname))\ + .encode('utf-8'))) f.write(compressor.flush()) finally: f.close() From 7add1755985875eb79b2bc724cbbfde23d685c61 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 18:58:18 +0200 Subject: [PATCH 072/744] Fix code formatting. --- sphinx/builders/html.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 4e07acf78..0b39d38e7 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -731,8 +731,8 @@ class StandaloneHTMLBuilder(Builder): u'# Project: %s\n' u'# Version: %s\n' u'# The remainder of this file is compressed using zlib.\n' - % (self.config.project, self.config.version))\ - .encode('utf-8')) + % (self.config.project, self.config.version) + ).encode('utf-8')) compressor = zlib.compressobj(9) for domainname, domain in self.env.domains.iteritems(): for name, dispname, type, docname, anchor, prio in \ @@ -744,9 +744,9 @@ class StandaloneHTMLBuilder(Builder): if dispname == name: dispname = u'-' f.write(compressor.compress( - (u'%s %s:%s %s %s %s\n' % (name, domainname, type, prio, - uri, dispname))\ - .encode('utf-8'))) + (u'%s %s:%s %s %s %s\n' % (name, domainname, type, + prio, uri, dispname) + ).encode('utf-8'))) f.write(compressor.flush()) finally: f.close() From 9e56fac893192187c033fbdf5e008459bc4b3ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 17 Jun 2010 05:26:19 +0200 Subject: [PATCH 073/744] Decode templates using utf-8 as jinja2 requires that for python3. This change will very likely cause problems for some people --- sphinx/util/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 2ef420ed1..ec48009f4 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -18,6 +18,7 @@ import tempfile import posixpath import traceback from os import path +from codecs import open import docutils from docutils.utils import relative_path @@ -140,8 +141,8 @@ def copy_static_entry(source, targetdir, builder, context={}, target = path.join(targetdir, path.basename(source)) if source.lower().endswith('_t') and builder.templates: # templated! - fsrc = open(source, 'rb') - fdst = open(target[:-2], 'wb') + fsrc = open(source, 'r', encoding='utf-8') + fdst = open(target[:-2], 'w', encoding='utf-8') fdst.write(builder.templates.render_string(fsrc.read(), context)) fsrc.close() fdst.close() From 24a1512293a09f865c16a28e4d42580a4589e1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 17 Jun 2010 05:38:22 +0200 Subject: [PATCH 074/744] Removed duplicated code --- sphinx/builders/qthelp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index ffc52334c..53c7a9b15 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -230,7 +230,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder): link = node['refuri'] title = escape(node.astext()).replace('"','"') item = section_template % {'title': title, 'ref': link} - item = ' '*4*indentlevel + item.encode('ascii', 'xmlcharrefreplace') + item = u' ' * 4 * indentlevel + item parts.append(item.encode('ascii', 'xmlcharrefreplace')) elif isinstance(node, nodes.bullet_list): for subnode in node: From 579528e0b3182e30ee00577b1a753d20533e6976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 17 Jun 2010 05:47:44 +0200 Subject: [PATCH 075/744] Fixed error caused by mixing of string types --- sphinx/builders/qthelp.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 53c7a9b15..e86f19217 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -130,8 +130,16 @@ class QtHelpBuilder(StandaloneHTMLBuilder): for indexname, indexcls, content, collapse in self.domain_indices: item = section_template % {'title': indexcls.localname, 'ref': '%s.html' % indexname} - sections.append(' '*4*4 + item) - sections = '\n'.join(sections) + sections.append((' ' * 4 * 4 + item).encode('utf-8')) + # sections may be unicode strings or byte strings, we have to make sure + # they are all byte strings before joining them + new_sections = [] + for section in sections: + if isinstance(section, unicode): + new_sections.append(section.encode('utf-8')) + else: + new_sections.append(section) + sections = u'\n'.encode('utf-8').join(new_sections) # keywords keywords = [] From b1f29495ab10503e8c942d4f49d4b195d325b23f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 17 Jun 2010 05:51:24 +0200 Subject: [PATCH 076/744] Don't mix string types when writing to a stream --- sphinx/builders/htmlhelp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 538f4c848..e3a58e724 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -258,7 +258,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): def write_index(title, refs, subitems): def write_param(name, value): item = ' \n' % (name, value) - f.write(item.encode('ascii', 'xmlcharrefreplace')) + f.write(item.encode('ascii', 'xmlcharrefreplace') + .decode('ascii')) title = cgi.escape(title) f.write('
  • \n') write_param('Keyword', title) From 38d11bbe147fc0f1b3d8d0df0eb8558f189f2029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 17 Jun 2010 05:56:06 +0200 Subject: [PATCH 077/744] the sphinx.search.js_index is now able to load and dump data from binary streams --- sphinx/search.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx/search.py b/sphinx/search.py index 729b63b2a..6d5a0614d 100644 --- a/sphinx/search.py +++ b/sphinx/search.py @@ -58,10 +58,13 @@ class _JavaScriptIndex(object): return jsdump.loads(data) def dump(self, data, f): - f.write(self.dumps(data)) + f.write(self.dumps(data).encode('utf-8')) def load(self, f): - return self.loads(f.read()) + data = f.read() + if isinstance(data, unicode): + return self.loads(data) + return self.loads(data.decode('utf-8')) js_index = _JavaScriptIndex() From 2ce553a71aa2a98789e008c0b77edbe2b259c5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 19 Jun 2010 16:38:52 +0200 Subject: [PATCH 078/744] Fix test to respect the new .truncate() behavior --- tests/test_application.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_application.py b/tests/test_application.py index 3d287a57c..d1154863c 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -45,9 +45,11 @@ def test_output(): app = TestApp(status=status, warning=warnings) try: status.truncate(0) # __init__ writes to status + status.seek(0) app.info("Nothing here...") assert status.getvalue() == "Nothing here...\n" status.truncate(0) + status.seek(0) app.info("Nothing here...", True) assert status.getvalue() == "Nothing here..." From 1f5c4e6c9c2385498ae7e10f5aeea4844e142c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 19 Jun 2010 21:50:00 +0200 Subject: [PATCH 079/744] make doctest work with python2 and python3 --- tests/root/conf.py | 2 +- tests/root/doctest.txt | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/root/conf.py b/tests/root/conf.py index 2b6d6a9af..b47341899 100644 --- a/tests/root/conf.py +++ b/tests/root/conf.py @@ -22,7 +22,7 @@ copyright = '2010, Georg Brandl & Team' version = '0.6' release = '0.6alpha1' today_fmt = '%B %d, %Y' -#unused_docs = [] +# unused_docs = [] exclude_patterns = ['_build', '**/excluded.*'] keep_warnings = True pygments_style = 'sphinx' diff --git a/tests/root/doctest.txt b/tests/root/doctest.txt index 35cdd589c..6ac0b2863 100644 --- a/tests/root/doctest.txt +++ b/tests/root/doctest.txt @@ -30,7 +30,7 @@ Special directives .. testcode:: - print 1+1 + print(1+1) .. testoutput:: @@ -50,30 +50,30 @@ Special directives .. testsetup:: * - from math import floor + from math import factorial .. doctest:: - >>> floor(1.2) - 1.0 + >>> factorial(1) + 1 .. testcode:: - print floor(1.2) + print(factorial(1)) .. testoutput:: - 1.0 + 1 - >>> floor(1.2) - 1.0 + >>> factorial(1) + 1 * options for testcode/testoutput blocks .. testcode:: :hide: - print 'Output text.' + print('Output text.') .. testoutput:: :hide: @@ -85,36 +85,36 @@ Special directives .. testsetup:: group1 - from math import ceil + from math import trunc - ``ceil`` is now known in "group1", but not in others. + ``trunc`` is now known in "group1", but not in others. .. doctest:: group1 - >>> ceil(0.8) - 1.0 + >>> trunc(1.1) + 1 .. doctest:: group2 - >>> ceil(0.8) + >>> trunc(1.1) Traceback (most recent call last): ... - NameError: name 'ceil' is not defined + NameError: name 'trunc' is not defined Interleaving testcode/testoutput: .. testcode:: group1 - print ceil(0.8) + print(factorial(3)) .. testcode:: group2 - print floor(0.8) + print(factorial(4)) .. testoutput:: group1 - 1.0 + 6 .. testoutput:: group2 - 0.0 + 24 From 0780242572de30cb19b524898d37afd313fc66c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 19 Jun 2010 19:58:28 +0200 Subject: [PATCH 080/744] Fixed the coverage extension test as well as the coverage extension itself for python3 --- sphinx/ext/coverage.py | 5 ++++- tests/path.py | 27 +++++++++++++++++++++++++++ tests/test_coverage.py | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index 4924d30b0..f41820e2a 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -173,8 +173,11 @@ class CoverageBuilder(Builder): attrs = [] + for attr_name in dir(obj): + attr = getattr(obj, attr_name) for attr_name, attr in inspect.getmembers( - obj, inspect.ismethod): + obj, lambda x: inspect.ismethod(x) or \ + inspect.isfunction(x)): if attr_name[0] == '_': # starts with an underscore, ignore it continue diff --git a/tests/path.py b/tests/path.py index 28a8c22af..df96bce45 100644 --- a/tests/path.py +++ b/tests/path.py @@ -142,6 +142,33 @@ class path(str): finally: f.close() + def bytes(self): + """ + Returns the bytes in the file. + """ + f = open(self, mode='rb') + try: + return f.read() + finally: + f.close() + + def write_bytes(self, bytes, append=False): + """ + Writes the given `bytes` to the file. + + :param append: + If ``True`` given `bytes` are added at the end of the file. + """ + if append: + mode = 'ab' + else: + mode = 'wb' + f = open(self, mode=mode) + try: + f.write(bytes) + finally: + f.close() + def exists(self): """ Returns ``True`` if the path exist. diff --git a/tests/test_coverage.py b/tests/test_coverage.py index 1262ebf5b..cb8316358 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -33,7 +33,7 @@ def test_build(app): assert 'api.h' in c_undoc assert ' * Py_SphinxTest' in c_undoc - undoc_py, undoc_c = pickle.loads((app.outdir / 'undoc.pickle').text()) + undoc_py, undoc_c = pickle.loads((app.outdir / 'undoc.pickle').bytes()) assert len(undoc_c) == 1 # the key is the full path to the header file, which isn't testable assert undoc_c.values()[0] == [('function', 'Py_SphinxTest')] From 24d8ce473430ed65111df88428c7152013ba1fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 20 Jun 2010 18:50:22 +0200 Subject: [PATCH 081/744] make sure to encode strings passed to md5 --- sphinx/builders/html.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 0b39d38e7..5a3e9bb3d 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -146,8 +146,9 @@ class StandaloneHTMLBuilder(Builder): cfgdict = dict((name, self.config[name]) for (name, desc) in self.config.values.iteritems() if desc[1] == 'html') - self.config_hash = md5(str(cfgdict)).hexdigest() - self.tags_hash = md5(str(sorted(self.tags))).hexdigest() + self.config_hash = md5(unicode(cfgdict).encode('ascii')).hexdigest() + self.tags_hash = md5(unicode(sorted(self.tags)).encode('ascii')) \ + .hexdigest() old_config_hash = old_tags_hash = '' try: fp = open(path.join(self.outdir, '.buildinfo')) From eaa3cb4f1fef44737b7f59537efc3b28676e8f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 20 Jun 2010 18:54:19 +0200 Subject: [PATCH 082/744] Use utf-8 instead of ascii to encode strings for hashing --- sphinx/builders/html.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 5a3e9bb3d..16afd3479 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -146,8 +146,8 @@ class StandaloneHTMLBuilder(Builder): cfgdict = dict((name, self.config[name]) for (name, desc) in self.config.values.iteritems() if desc[1] == 'html') - self.config_hash = md5(unicode(cfgdict).encode('ascii')).hexdigest() - self.tags_hash = md5(unicode(sorted(self.tags)).encode('ascii')) \ + self.config_hash = md5(unicode(cfgdict).encode('utf-8')).hexdigest() + self.tags_hash = md5(unicode(sorted(self.tags)).encode('utf-8')) \ .hexdigest() old_config_hash = old_tags_hash = '' try: From 710a7d75a18e2db712e0f0689f5ce6d5cc74ba13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 20 Jun 2010 19:34:49 +0200 Subject: [PATCH 083/744] Fix warning for bytestrings with non-ascii content for python3 --- sphinx/config.py | 12 +++++++++--- tests/test_config.py | 9 +++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 07c3d63af..7ec5cfe80 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -11,12 +11,18 @@ import os import re +import sys from os import path from sphinx.errors import ConfigError from sphinx.util.osutil import make_filename -nonascii_re = re.compile(r'[\x80-\xff]') +nonascii_re = re.compile(ur'[\x80-\xff]'.encode('ascii')) + +try: + bytes +except NameError: + bytes = str class Config(object): @@ -187,10 +193,10 @@ class Config(object): # check all string values for non-ASCII characters in bytestrings, # since that can result in UnicodeErrors all over the place for name, value in self._raw_config.iteritems(): - if isinstance(value, str) and nonascii_re.search(value): + if isinstance(value, bytes) and nonascii_re.search(value): warn('the config value %r is set to a string with non-ASCII ' 'characters; this can lead to Unicode errors occurring. ' - 'Please use Unicode strings, e.g. u"Content".' % name) + 'Please use Unicode strings, e.g. %r.' % (name, u'Content')) def init_values(self): config = self._raw_config diff --git a/tests/test_config.py b/tests/test_config.py index 23d92e39c..ecf90f609 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -9,6 +9,7 @@ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import sys from util import * @@ -88,8 +89,12 @@ def test_errors_warnings(dir): raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None) # test the warning for bytestrings with non-ascii content - write_file(dir / 'conf.py', - u'# -*- coding: latin-1\nproject = "fooä"\n', 'latin-1') + # bytestrings with non-ascii content are a syntax error in python3 so we + # skip the test there + if sys.version_info >= (3, 0): + return + write_file(dir / 'conf.py', u'# -*- coding: latin-1\nproject = "fooä"\n', + 'latin-1') cfg = Config(dir, 'conf.py', {}, None) warned = [False] def warn(msg): From fc1003839861c02f6181260c1236f75e36ad1d4b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:13:25 +0200 Subject: [PATCH 084/744] Move bytes to pycompat. --- sphinx/config.py | 8 ++------ sphinx/util/pycompat.py | 5 +++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 7ec5cfe80..210bb9e2b 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -16,13 +16,9 @@ from os import path from sphinx.errors import ConfigError from sphinx.util.osutil import make_filename +from sphinx.util.pycompat import bytes, b -nonascii_re = re.compile(ur'[\x80-\xff]'.encode('ascii')) - -try: - bytes -except NameError: - bytes = str +nonascii_re = re.compile(b(r'[\x80-\xff]')) class Config(object): diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 0725545da..624749fc3 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -41,6 +41,11 @@ except NameError: def next(iterator): return iterator.next() +try: + bytes = bytes +except NameError: + bytes = str + try: any = any From 20a21ed35a05cb1019affab4a6601ae7478853c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 20 Jun 2010 22:24:00 +0200 Subject: [PATCH 085/744] fix line length --- sphinx/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/config.py b/sphinx/config.py index 210bb9e2b..273bb97d9 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -192,7 +192,8 @@ class Config(object): if isinstance(value, bytes) and nonascii_re.search(value): warn('the config value %r is set to a string with non-ASCII ' 'characters; this can lead to Unicode errors occurring. ' - 'Please use Unicode strings, e.g. %r.' % (name, u'Content')) + 'Please use Unicode strings, e.g. %r.' % (name, u'Content') + ) def init_values(self): config = self._raw_config From 98b6073a7e85f91db1cd54de1709ba5d763ffb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 20 Jun 2010 22:39:38 +0200 Subject: [PATCH 086/744] Fixed warnings in python3 --- sphinx/environment.py | 2 +- tests/test_build_html.py | 5 +++++ tests/test_build_latex.py | 3 +++ tests/util.py | 7 ++++++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 21994a746..fd171d443 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -80,7 +80,7 @@ class WarningStream(object): self.warnfunc = warnfunc def write(self, text): if text.strip(): - self.warnfunc(text, None, '') + self.warnfunc(text.strip(), None, '') class NoUri(Exception): diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 4dee513ae..65c1840ea 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -12,6 +12,7 @@ import os import re import htmlentitydefs +import sys from StringIO import StringIO try: @@ -60,6 +61,10 @@ def tail_check(check): return checker +if sys.version_info >= (3, 0): + ENV_WARNINGS = remove_unicode_literals(ENV_WARNINGS) + HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS) + HTML_XPATH = { 'images.html': [ (".//img[@src='_images/img.png']", ''), diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 4405395a0..6c1ccad9b 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -32,6 +32,9 @@ None:None: WARNING: no matching candidate for image URI u'foo.\\*' WARNING: invalid pair index entry u'' """ +if sys.version_info >= (3, 0): + LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS) + @with_app(buildername='latex', warning=latex_warnfile, cleanenv=True) def test_latex(app): diff --git a/tests/util.py b/tests/util.py index 2cf4a775b..950ea23f5 100644 --- a/tests/util.py +++ b/tests/util.py @@ -11,6 +11,7 @@ import sys import StringIO import tempfile import shutil +import re from codecs import open try: @@ -32,7 +33,7 @@ __all__ = [ 'raises', 'raises_msg', 'Struct', 'ListOutput', 'TestApp', 'with_app', 'gen_with_app', 'path', 'with_tempdir', 'write_file', - 'sprint', + 'sprint', 'remove_unicode_literals', ] @@ -206,3 +207,7 @@ def write_file(name, contents, encoding=None): def sprint(*args): sys.stderr.write(' '.join(map(str, args)) + '\n') + +_unicode_literals_re = re.compile(r'u(".*")|u(\'.*\')') +def remove_unicode_literals(s): + return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s) From 3002eb391f4dc3e4f478e693a4bebec65ab461ec Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:15:04 +0200 Subject: [PATCH 087/744] Make string contents nongreedy. --- tests/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/util.py b/tests/util.py index 950ea23f5..b81e15b6e 100644 --- a/tests/util.py +++ b/tests/util.py @@ -208,6 +208,6 @@ def write_file(name, contents, encoding=None): def sprint(*args): sys.stderr.write(' '.join(map(str, args)) + '\n') -_unicode_literals_re = re.compile(r'u(".*")|u(\'.*\')') +_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')') def remove_unicode_literals(s): return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s) From 37db093428112925a7dc86ce7b33a7cbe53227ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Tue, 22 Jun 2010 22:49:58 +0200 Subject: [PATCH 088/744] the test suite now runs on ubuntu, hopefully also debian and other system --- tests/run.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/run.py b/tests/run.py index f384fe267..59a3ffa5b 100755 --- a/tests/run.py +++ b/tests/run.py @@ -11,13 +11,14 @@ """ import sys -from os import path, chdir +from os import path, chdir, listdir if sys.version_info >= (3,): print('Copying and converting sources to build/lib/tests...') from distutils.util import copydir_run_2to3 testroot = path.dirname(__file__) or '.' - newroot = path.join(testroot, path.pardir, 'build', 'lib', 'tests') + newroot = path.join(testroot, path.pardir, 'build') + newroot = path.join(newroot, listdir(newroot)[0], 'tests') copydir_run_2to3(testroot, newroot) # switch to the converted dir so nose tests the right tests chdir(newroot) From a7ca488a8ece7ac83d02937d3ee3b97f481ddc1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 10 Jul 2010 19:47:32 +0200 Subject: [PATCH 089/744] Removed XMLParser._fixtext which fixes several errors in the test suite --- tests/etree13/ElementTree.py | 17 ++++------------- tests/test_build_html.py | 2 +- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/etree13/ElementTree.py b/tests/etree13/ElementTree.py index d37325049..e50ad640c 100644 --- a/tests/etree13/ElementTree.py +++ b/tests/etree13/ElementTree.py @@ -1425,13 +1425,6 @@ class XMLParser(object): err.position = value.lineno, value.offset raise err - def _fixtext(self, text): - # convert text string to ascii, if possible - try: - return text.encode("ascii") - except UnicodeError: - return text - def _fixname(self, key): # expand qname, and convert name string to ascii, if possible try: @@ -1440,30 +1433,28 @@ class XMLParser(object): name = key if "}" in name: name = "{" + name - self._names[key] = name = self._fixtext(name) + self._names[key] return name def _start(self, tag, attrib_in): fixname = self._fixname - fixtext = self._fixtext tag = fixname(tag) attrib = {} for key, value in attrib_in.items(): - attrib[fixname(key)] = fixtext(value) + attrib[fixname(key)] = value return self.target.start(tag, attrib) def _start_list(self, tag, attrib_in): fixname = self._fixname - fixtext = self._fixtext tag = fixname(tag) attrib = {} if attrib_in: for i in range(0, len(attrib_in), 2): - attrib[fixname(attrib_in[i])] = fixtext(attrib_in[i+1]) + attrib[fixname(attrib_in[i])] = attrib_in[i+1] return self.target.start(tag, attrib) def _data(self, text): - return self.target.data(self._fixtext(text)) + return self.target.data(text) def _end(self, tag): return self.target.end(self._fixname(tag)) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 65c1840ea..5e3a20188 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -258,7 +258,7 @@ class NslessParser(ET.XMLParser): br = name.find('}') if br > 0: name = name[br+1:] - self._names[key] = name = self._fixtext(name) + self._names[key] = name return name From 839cc5aaeb166d5ef2f310459f742456a160db19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 11 Jul 2010 00:57:08 +0200 Subject: [PATCH 090/744] Revert changes from the last commit which caused problems with 2.x --- tests/etree13/ElementTree.py | 17 +++++++++++++---- tests/test_build_html.py | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/etree13/ElementTree.py b/tests/etree13/ElementTree.py index e50ad640c..d37325049 100644 --- a/tests/etree13/ElementTree.py +++ b/tests/etree13/ElementTree.py @@ -1425,6 +1425,13 @@ class XMLParser(object): err.position = value.lineno, value.offset raise err + def _fixtext(self, text): + # convert text string to ascii, if possible + try: + return text.encode("ascii") + except UnicodeError: + return text + def _fixname(self, key): # expand qname, and convert name string to ascii, if possible try: @@ -1433,28 +1440,30 @@ class XMLParser(object): name = key if "}" in name: name = "{" + name - self._names[key] + self._names[key] = name = self._fixtext(name) return name def _start(self, tag, attrib_in): fixname = self._fixname + fixtext = self._fixtext tag = fixname(tag) attrib = {} for key, value in attrib_in.items(): - attrib[fixname(key)] = value + attrib[fixname(key)] = fixtext(value) return self.target.start(tag, attrib) def _start_list(self, tag, attrib_in): fixname = self._fixname + fixtext = self._fixtext tag = fixname(tag) attrib = {} if attrib_in: for i in range(0, len(attrib_in), 2): - attrib[fixname(attrib_in[i])] = attrib_in[i+1] + attrib[fixname(attrib_in[i])] = fixtext(attrib_in[i+1]) return self.target.start(tag, attrib) def _data(self, text): - return self.target.data(text) + return self.target.data(self._fixtext(text)) def _end(self, tag): return self.target.end(self._fixname(tag)) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 5e3a20188..65c1840ea 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -258,7 +258,7 @@ class NslessParser(ET.XMLParser): br = name.find('}') if br > 0: name = name[br+1:] - self._names[key] = name + self._names[key] = name = self._fixtext(name) return name From 23af2ea8755557677445276ea89b31119e1ae5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 11 Jul 2010 01:05:27 +0200 Subject: [PATCH 091/744] Provided a working fix for the remaining errors in the test suite --- tests/etree13/ElementTree.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/etree13/ElementTree.py b/tests/etree13/ElementTree.py index d37325049..f459c7f8f 100644 --- a/tests/etree13/ElementTree.py +++ b/tests/etree13/ElementTree.py @@ -1425,12 +1425,16 @@ class XMLParser(object): err.position = value.lineno, value.offset raise err - def _fixtext(self, text): - # convert text string to ascii, if possible - try: - return text.encode("ascii") - except UnicodeError: + if sys.version_info >= (3, 0): + def _fixtext(self, text): return text + else: + def _fixtext(self, text): + # convert text string to ascii, if possible + try: + return text.encode("ascii") + except UnicodeError: + return text def _fixname(self, key): # expand qname, and convert name string to ascii, if possible From 7acfe972a4a6dec95bdfed667a6efca943176b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 11 Jul 2010 11:32:16 +0200 Subject: [PATCH 092/744] Fixed test_env.test_images test for python3 --- tests/test_env.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_env.py b/tests/test_env.py index 4ecbaac49..124ed08cd 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -8,6 +8,7 @@ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import sys from util import * @@ -54,8 +55,10 @@ def test_images(): app._warning.reset() htmlbuilder = StandaloneHTMLBuilder(app) htmlbuilder.post_process_images(tree) - assert "no matching candidate for image URI u'foo.*'" in \ - app._warning.content[-1] + image_uri_message = "no matching candidate for image URI u'foo.*'" + if sys.version_info >= (3, 0): + image_uri_message = remove_unicode_literals(image_uri_message) + assert image_uri_message in app._warning.content[-1] assert set(htmlbuilder.images.keys()) == \ set(['subdir/img.png', 'img.png', 'subdir/simg.png', 'svgimg.svg']) assert set(htmlbuilder.images.values()) == \ @@ -64,8 +67,7 @@ def test_images(): app._warning.reset() latexbuilder = LaTeXBuilder(app) latexbuilder.post_process_images(tree) - assert "no matching candidate for image URI u'foo.*'" in \ - app._warning.content[-1] + assert image_uri_message in app._warning.content[-1] assert set(latexbuilder.images.keys()) == \ set(['subdir/img.png', 'subdir/simg.png', 'img.png', 'img.pdf', 'svgimg.pdf']) From ddeb627cef1d5ed566e9d6af242833b01f0590ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 11 Jul 2010 12:24:50 +0200 Subject: [PATCH 093/744] Don't use (in this case) unnecessary python2 unicode literals --- tests/root/literal.inc | 2 +- tests/test_build_html.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/root/literal.inc b/tests/root/literal.inc index d5b9890c9..694f15ed9 100644 --- a/tests/root/literal.inc +++ b/tests/root/literal.inc @@ -1,7 +1,7 @@ # Literally included file using Python highlighting # -*- coding: utf-8 -*- -foo = u"Including Unicode characters: üöä" +foo = "Including Unicode characters: üöä" class Foo: pass diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 65c1840ea..3ca2c757f 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -232,7 +232,7 @@ if pygments: (".//div[@class='inc-lines highlight-text']//pre", r'^class Foo:\n pass\nclass Bar:\n$'), (".//div[@class='inc-startend highlight-text']//pre", - ur'^foo = u"Including Unicode characters: üöä"\n$'), + ur'^foo = "Including Unicode characters: üöä"\n$'), (".//div[@class='inc-preappend highlight-text']//pre", r'(?m)^START CODE$'), (".//div[@class='inc-pyobj-dedent highlight-python']//span", From 5248e8d3158b79b552585827c22b71f482cfe086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 24 Jul 2010 13:04:30 +0200 Subject: [PATCH 094/744] Added Python 3.x support to the changelog again under a different section (1.1) --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index b247543f8..573b4edd6 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,8 @@ Release 1.0.1 (Jul 27, 2010) * Fix hyperrefs in object descriptions for LaTeX. + * Added Python 3.x support. + Release 1.0 (Jul 23, 2010) ========================== From 14ca77a1d4e1dd7f43a5f90d29e596f481bed29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Tue, 27 Jul 2010 21:01:27 +0200 Subject: [PATCH 095/744] Fixed the JSONHTMLBuilder --- sphinx/builders/html.py | 49 ++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 16afd3479..064f9d2f1 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -754,6 +754,10 @@ class StandaloneHTMLBuilder(Builder): self.info('done') def dump_search_index(self): + # NOTE: If you change this code you have to change it in + # JSONHTMLBuilder.dump_search_index as well because the code is + # mostly copied from here for reasons explained in a comment in + # said method. self.info(bold('dumping search index... '), nonl=True) self.indexer.prune(self.env.all_docs) searchindexfn = path.join(self.outdir, self.searchindex_filename) @@ -938,6 +942,13 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): return docname[:-5] # up to sep return docname + SEP + def dump_context(self, context, filename): + f = open(filename, 'wb') + try: + self.implementation.dump(context, f, 2) + finally: + f.close() + def handle_page(self, pagename, ctx, templatename='page.html', outfilename=None, event_arg=None): ctx['current_page_name'] = pagename @@ -951,11 +962,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): ctx, event_arg) ensuredir(path.dirname(outfilename)) - f = open(outfilename, 'wb') - try: - self.implementation.dump(ctx, f, 2) - finally: - f.close() + self.dump_context(ctx, outfilename) # if there is a source file, copy the source file for the # "show source" link @@ -968,11 +975,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): def handle_finish(self): # dump the global context outfilename = path.join(self.outdir, self.globalcontext_filename) - f = open(outfilename, 'wb') - try: - self.implementation.dump(self.globalcontext, f, 2) - finally: - f.close() + self.dump_context(self.globalcontext, outfilename) # super here to dump the search index StandaloneHTMLBuilder.handle_finish(self) @@ -1019,3 +1022,29 @@ class JSONHTMLBuilder(SerializingHTMLBuilder): 'The module simplejson (or json in Python >= 2.6) ' 'is not available. The JSONHTMLBuilder builder will not work.') SerializingHTMLBuilder.init(self) + + def dump_context(self, context, filename): + # json operates entirely on "unicode" but the filesystem doesn't so we + # have to specify an encoding. + f = codecs.open(filename, 'w', encoding='utf-8') + try: + self.implementation.dump(context, f, 2) + finally: + f.close() + + def dump_search_index(self): + # this code is nearly completely copied from the super class, in which + # this method was initially defined, the only difference is that we + # specify an encoding for the file. + self.info(bold('dumping search index...'), nonl=True) + self.indexer.prune(self.env.all_docs) + searchindexfn = path.join(self.outdir, self.searchindex_filename) + # first write to a temporary file, so that if dumping fails, + # the existing index won't be overwritten + f = codecs.open(searchindexfn + '.tmp', 'w', encoding='utf-8') + try: + self.indexer.dump(f, self.indexer_format) + finally: + f.close() + movefile(searchindexfn + '.tmp', searchindexfn) + self.info('done') From bd2eb1e085882bbe97d449ac1879de40b50f458b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:24:48 +0200 Subject: [PATCH 096/744] Declare if serializers/indexers dump unicode or bytes. Removes duplication of methods. --- sphinx/builders/html.py | 47 +++++++++++++---------------------------- sphinx/search.py | 7 ++---- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 064f9d2f1..5a7d49cd0 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -63,6 +63,7 @@ class StandaloneHTMLBuilder(Builder): out_suffix = '.html' link_suffix = '.html' # defaults to matching out_suffix indexer_format = js_index + indexer_dumps_unicode = True supported_image_types = ['image/svg+xml', 'image/png', 'image/gif', 'image/jpeg'] searchindex_filename = 'searchindex.js' @@ -754,16 +755,15 @@ class StandaloneHTMLBuilder(Builder): self.info('done') def dump_search_index(self): - # NOTE: If you change this code you have to change it in - # JSONHTMLBuilder.dump_search_index as well because the code is - # mostly copied from here for reasons explained in a comment in - # said method. self.info(bold('dumping search index... '), nonl=True) self.indexer.prune(self.env.all_docs) searchindexfn = path.join(self.outdir, self.searchindex_filename) # first write to a temporary file, so that if dumping fails, # the existing index won't be overwritten - f = open(searchindexfn + '.tmp', 'wb') + if self.indexer_dumps_unicode: + f = codecs.open(searchindexfn + '.tmp', 'w', encoding='utf-8') + else: + f = open(searchindexfn + '.tmp', 'wb') try: self.indexer.dump(f, self.indexer_format) finally: @@ -920,6 +920,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): #: implements a `dump`, `load`, `dumps` and `loads` functions #: (pickle, simplejson etc.) implementation = None + implementation_dumps_unicode = False #: the filename for the global context file globalcontext_filename = None @@ -943,8 +944,12 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): return docname + SEP def dump_context(self, context, filename): - f = open(filename, 'wb') + if self.implementation_dumps_unicode: + f = codecs.open(filename, 'w', encoding='utf-8') + else: + f = open(filename, 'wb') try: + # XXX: the third argument is pickle-specific! self.implementation.dump(context, f, 2) finally: f.close() @@ -995,7 +1000,9 @@ class PickleHTMLBuilder(SerializingHTMLBuilder): A Builder that dumps the generated HTML into pickle files. """ implementation = pickle + implementation_dumps_unicode = False indexer_format = pickle + indexer_dumps_unicode = False name = 'pickle' out_suffix = '.fpickle' globalcontext_filename = 'globalcontext.pickle' @@ -1010,7 +1017,9 @@ class JSONHTMLBuilder(SerializingHTMLBuilder): A builder that dumps the generated HTML into JSON files. """ implementation = jsonimpl + implementation_dumps_unicode = True indexer_format = jsonimpl + indexer_dumps_unicode = True name = 'json' out_suffix = '.fjson' globalcontext_filename = 'globalcontext.json' @@ -1022,29 +1031,3 @@ class JSONHTMLBuilder(SerializingHTMLBuilder): 'The module simplejson (or json in Python >= 2.6) ' 'is not available. The JSONHTMLBuilder builder will not work.') SerializingHTMLBuilder.init(self) - - def dump_context(self, context, filename): - # json operates entirely on "unicode" but the filesystem doesn't so we - # have to specify an encoding. - f = codecs.open(filename, 'w', encoding='utf-8') - try: - self.implementation.dump(context, f, 2) - finally: - f.close() - - def dump_search_index(self): - # this code is nearly completely copied from the super class, in which - # this method was initially defined, the only difference is that we - # specify an encoding for the file. - self.info(bold('dumping search index...'), nonl=True) - self.indexer.prune(self.env.all_docs) - searchindexfn = path.join(self.outdir, self.searchindex_filename) - # first write to a temporary file, so that if dumping fails, - # the existing index won't be overwritten - f = codecs.open(searchindexfn + '.tmp', 'w', encoding='utf-8') - try: - self.indexer.dump(f, self.indexer_format) - finally: - f.close() - movefile(searchindexfn + '.tmp', searchindexfn) - self.info('done') diff --git a/sphinx/search.py b/sphinx/search.py index 6d5a0614d..729b63b2a 100644 --- a/sphinx/search.py +++ b/sphinx/search.py @@ -58,13 +58,10 @@ class _JavaScriptIndex(object): return jsdump.loads(data) def dump(self, data, f): - f.write(self.dumps(data).encode('utf-8')) + f.write(self.dumps(data)) def load(self, f): - data = f.read() - if isinstance(data, unicode): - return self.loads(data) - return self.loads(data.decode('utf-8')) + return self.loads(f.read()) js_index = _JavaScriptIndex() From 8cb5a023129a5203691611aea6fa45e6f9c1e268 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:27:45 +0200 Subject: [PATCH 097/744] Give a binary document name. --- tests/test_search.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_search.py b/tests/test_search.py index 0b5b158b8..c0750366c 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -13,6 +13,7 @@ from docutils import frontend, utils from docutils.parsers import rst from sphinx.search import IndexBuilder +from sphinx.util.pycompat import b settings = parser = None @@ -31,7 +32,7 @@ test that non-comments are indexed: fermion ''' def test_wordcollector(): - doc = utils.new_document('test data', settings) + doc = utils.new_document(b('test data'), settings) doc['file'] = 'dummy' parser.parse(FILE_CONTENTS, doc) From 6f277bb7bd05bf2d96411235650f91717459bdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 16 May 2010 17:51:40 +0200 Subject: [PATCH 098/744] Stop modifying PYTHONPATH in the Makefile --- Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile b/Makefile index 21a87e367..13228c788 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,5 @@ PYTHON ?= python3 -export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx - .PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint \ reindent test From 7c292a43d8301e278c3664c9d8bf4ddaccd1eccc Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:31:05 +0200 Subject: [PATCH 099/744] Factor out a replace(). --- sphinx/environment.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index fd171d443..70690a8a6 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -383,14 +383,14 @@ class BuildEnvironment: If base is a path string, return absolute path under that. If suffix is not None, add it instead of config.source_suffix. """ + docname = docname.replace(SEP, path.sep) suffix = suffix or self.config.source_suffix if base is True: - return path.join(self.srcdir, - docname.replace(SEP, path.sep)) + suffix + return path.join(self.srcdir, docname) + suffix elif base is None: - return docname.replace(SEP, path.sep) + suffix + return docname + suffix else: - return path.join(base, docname.replace(SEP, path.sep)) + suffix + return path.join(base, docname) + suffix def find_files(self, config): """ From 0580c7c81a8b34a116d37ff4718ba632fb083858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Thu, 3 Jun 2010 17:33:50 +0200 Subject: [PATCH 100/744] test if decoding is required first --- sphinx/ext/autodoc.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 1113f97a0..efb762e27 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -435,8 +435,11 @@ class Documenter(object): # set sourcename and add content from attribute documentation if self.analyzer: # prevent encoding errors when the file name is non-ASCII - filename = unicode(self.analyzer.srcname, - sys.getfilesystemencoding(), 'replace') + if not isinstance(self.analyzer.srcname, unicode): + filename = unicode(self.analyzer.srcname, + sys.getfilesystemencoding(), 'replace') + else: + filename = self.analyzer.srcname sourcename = u'%s:docstring of %s' % (filename, self.fullname) attr_docs = self.analyzer.find_attr_docs() From f16a3173b39fef4faea27ace659c7383cd9ecc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sun, 6 Jun 2010 23:57:37 +0200 Subject: [PATCH 101/744] pass paths as bytes to docutils --- sphinx/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 70690a8a6..46ec60164 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -649,7 +649,7 @@ class BuildEnvironment: destination_class=NullOutput) pub.set_components(None, 'restructuredtext', None) pub.process_programmatic_settings(None, self.settings, None) - pub.set_source(None, src_path) + pub.set_source(None, src_path.encode(FILESYSTEMENCODING)) pub.set_destination(None, None) try: pub.publish() From 52afc7ab6b1bee163cb9ef43fe13f316769b805d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:33:24 +0200 Subject: [PATCH 102/744] Introduce constant. --- sphinx/environment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 46ec60164..6ad28ec8a 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -11,6 +11,7 @@ import re import os +import sys import time import types import codecs @@ -43,6 +44,7 @@ from sphinx.util.pycompat import all, class_types from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _ +fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() orig_role_function = roles.role orig_directive_function = directives.directive @@ -649,7 +651,7 @@ class BuildEnvironment: destination_class=NullOutput) pub.set_components(None, 'restructuredtext', None) pub.process_programmatic_settings(None, self.settings, None) - pub.set_source(None, src_path.encode(FILESYSTEMENCODING)) + pub.set_source(None, src_path.encode(fs_encoding)) pub.set_destination(None, None) try: pub.publish() From 85ef01660227156305a3dba48f065be16458493c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 7 Jun 2010 00:07:38 +0200 Subject: [PATCH 103/744] only decode code if necessary --- sphinx/ext/viewcode.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index 81881beb6..db04ac794 100644 --- a/sphinx/ext/viewcode.py +++ b/sphinx/ext/viewcode.py @@ -31,7 +31,11 @@ def doctree_read(app, doctree): env._viewcode_modules[modname] = False return analyzer.find_tags() - entry = analyzer.code.decode(analyzer.encoding), analyzer.tags, {} + if not isinstance(analyzer.code, unicode): + code = analyzer.code.decode(analyzer.encoding) + else: + code = analyzer.code + entry = code, analyzer.tags, {} env._viewcode_modules[modname] = entry elif entry is False: return From 5f5f9177906c05f7d9049fc6456e1386537edabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Wed, 16 Jun 2010 23:05:56 +0200 Subject: [PATCH 104/744] only decode if possible --- sphinx/environment.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/environment.py b/sphinx/environment.py index 6ad28ec8a..bf255165e 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -630,6 +630,8 @@ class BuildEnvironment: class SphinxSourceClass(FileInput): def decode(self_, data): + if isinstance(data, unicode): + return data return data.decode(self_.encoding, 'sphinx') def read(self_): From 024ec6696fb72b6658c67891a53021e86bb4b893 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:35:35 +0200 Subject: [PATCH 105/744] Mode "Ub" does not exist. --- sphinx/config.py | 2 +- sphinx/directives/code.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 273bb97d9..708da162e 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -167,7 +167,7 @@ class Config(object): try: try: os.chdir(dirname) - f = open(config_file, 'Ub') + f = open(config_file, 'rb') try: code = compile(f.read(), config_file, 'exec') finally: diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 54d8edf19..1808cdaba 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -119,7 +119,7 @@ class LiteralInclude(Directive): encoding = self.options.get('encoding', env.config.source_encoding) codec_info = codecs.lookup(encoding) try: - f = codecs.StreamReaderWriter(open(fn, 'Ub'), + f = codecs.StreamReaderWriter(open(fn, 'rb'), codec_info[2], codec_info[3], 'strict') lines = f.readlines() f.close() From b621cfaec1501409d1cc81a24340975f7b445ac3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 19:36:57 +0200 Subject: [PATCH 106/744] Unify version_info checks. --- sphinx/util/pycompat.py | 2 +- tests/run.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 624749fc3..229b54b45 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -27,7 +27,7 @@ except NameError: # the ubiquitous "bytes" helper function -if sys.version_info > (3, 0): +if sys.version_info >= (3, 0): def b(s): return s.encode('utf-8') else: diff --git a/tests/run.py b/tests/run.py index 59a3ffa5b..50567fbc5 100755 --- a/tests/run.py +++ b/tests/run.py @@ -13,7 +13,7 @@ import sys from os import path, chdir, listdir -if sys.version_info >= (3,): +if sys.version_info >= (3, 0): print('Copying and converting sources to build/lib/tests...') from distutils.util import copydir_run_2to3 testroot = path.dirname(__file__) or '.' From c967651712521e96e53966f9a1ad916e4670a1a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 19 Jun 2010 18:23:49 +0200 Subject: [PATCH 107/744] Implemented sphinx.ext.autodoc.MethodDocumenter.import_object for python3 --- sphinx/ext/autodoc.py | 57 +++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index efb762e27..eef181de4 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -251,6 +251,9 @@ class Documenter(object): self.retann = None # the object to document (set after import_object succeeds) self.object = None + self.object_name = None + # the parent/owner of the object to document + self.parent = None # the module analyzer to get at attribute docs, or None self.analyzer = None @@ -316,9 +319,13 @@ class Documenter(object): """ try: __import__(self.modname) + parent = None obj = self.module = sys.modules[self.modname] for part in self.objpath: + parent = obj obj = self.get_attr(obj, part) + self.object_name = part + self.parent = parent self.object = obj return True # this used to only catch SyntaxError, ImportError and AttributeError, @@ -1007,24 +1014,38 @@ class MethodDocumenter(ClassLevelDocumenter): return inspect.isroutine(member) and \ not isinstance(parent, ModuleDocumenter) - def import_object(self): - ret = ClassLevelDocumenter.import_object(self) - if isinstance(self.object, classmethod) or \ - (isinstance(self.object, MethodType) and - self.object.im_self is not None): - self.directivetype = 'classmethod' - # document class and static members before ordinary ones - self.member_order = self.member_order - 1 - elif isinstance(self.object, FunctionType) or \ - (isinstance(self.object, BuiltinFunctionType) and - hasattr(self.object, '__self__') and - self.object.__self__ is not None): - self.directivetype = 'staticmethod' - # document class and static members before ordinary ones - self.member_order = self.member_order - 1 - else: - self.directivetype = 'method' - return ret + if sys.version_info >= (3, 0): + def import_object(self): + ret = ClassLevelDocumenter.import_object(self) + obj_from_parent = self.parent.__dict__.get(self.object_name) + if isinstance(obj_from_parent, classmethod): + self.directivetype = 'classmethod' + self.member_order = self.member_order - 1 + elif isinstance(obj_from_parent, staticmethod): + self.directivetype = 'staticmethod' + self.member_order = self.member_order - 1 + else: + self.directivetype = 'method' + return ret + else: + def import_object(self): + ret = ClassLevelDocumenter.import_object(self) + if isinstance(self.object, classmethod) or \ + (isinstance(self.object, MethodType) and + self.object.im_self is not None): + self.directivetype = 'classmethod' + # document class and static members before ordinary ones + self.member_order = self.member_order - 1 + elif isinstance(self.object, FunctionType) or \ + (isinstance(self.object, BuiltinFunctionType) and + hasattr(self.object, '__self__') and + self.object.__self__ is not None): + self.directivetype = 'staticmethod' + # document class and static members before ordinary ones + self.member_order = self.member_order - 1 + else: + self.directivetype = 'method' + return ret def format_args(self): if inspect.isbuiltin(self.object) or \ From 625127fd639008a98be06e96f514c84b8613ba30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Fri, 28 May 2010 04:08:15 +0200 Subject: [PATCH 108/744] Fix SyntaxError in config generated by quickstart and the quickstart test --- sphinx/quickstart.py | 14 +++++++++++++- tests/test_quickstart.py | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 892bd641d..557f8c094 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -9,7 +9,7 @@ :license: BSD, see LICENSE for details. """ -import sys, os, time +import sys, os, time, re from os import path from codecs import open @@ -685,6 +685,18 @@ def do_prompt(d, key, text, default=None, validator=nonempty): d[key] = x +if sys.version_info >= (3, 0): + # remove Unicode literal prefixes + _unicode_string_re = re.compile(r"[uU]('.*?')") + def _convert_python_source(source): + return _unicode_string_re.sub('\\1', source) + + for f in ['QUICKSTART_CONF', 'EPUB_CONFIG', 'INTERSPHINX_CONFIG']: + globals()[f] = convert_python_source(globals()[f]) + + del _unicode_string_re, _convert_python_source + + def inner_main(args): d = {} texescape.init() diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index d0403d3b2..72ae764d6 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -117,8 +117,8 @@ def test_quickstart_all_answers(tempdir): 'Root path': tempdir, 'Separate source and build': 'y', 'Name prefix for templates': '.', - 'Project name': 'STASI\xe2\x84\xa2', - 'Author name': 'Wolfgang Sch\xc3\xa4uble & G\'Beckstein', + 'Project name': u'STASI™'.encode('utf-8'), + 'Author name': u'Wolfgang Schäuble & G\'Beckstein'.encode('utf-8'), 'Project version': '2.0', 'Project release': '2.0.1', 'Source file suffix': '.txt', From d405575620c4a69cbde7fb1db771e90d45708773 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Sat, 29 May 2010 18:14:42 +0200 Subject: [PATCH 109/744] Skeleton for PO builder. --- sphinx/builders/intl.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 sphinx/builders/intl.py diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py new file mode 100644 index 000000000..5bc186971 --- /dev/null +++ b/sphinx/builders/intl.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.intl + ~~~~~~~~~~~~~~~~~~~~ + + The MessageCatalogBuilder class. + + :copyright: Copyright 2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from sphinx.builders import Builder + +class MessageCatalogBuilder(Builder): + pass + + def get_target_uri(self, docname, typ=None): + return '' + + def get_outdated_docs(self): + return self.env.found_docs + + def prepare_writing(self, docnames): + return + + def write_doc(self, docname, doctree): + return + + def finish(self): + return From f0cbf2c4e19dba35fd0beb6595dd3e49b49de0fa Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Sat, 29 May 2010 20:26:38 +0200 Subject: [PATCH 110/744] Add PO builder to sphinx-quickstart. --- sphinx/quickstart.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 8479575a8..0aab261e7 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -321,7 +321,7 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) %(rsrcdir)s .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp \ -epub latex latexpdf text man changes linkcheck doctest +epub latex latexpdf text man changes linkcheck doctest gettext help: \t@echo "Please use \\`make ' where is one of" @@ -338,6 +338,7 @@ help: \t@echo " latexpdf to make LaTeX files and run them through pdflatex" \t@echo " text to make text files" \t@echo " man to make manual pages" +\t@echo " gettext to make PO message catalogs" \t@echo " changes to make an overview of all changed/added/deprecated items" \t@echo " linkcheck to check all external links for integrity" \t@echo " doctest to run all doctests embedded in the documentation \ @@ -424,6 +425,11 @@ man: \t@echo \t@echo "Build finished. The manual pages are in $(BUILDDIR)/man." +gettext: +\t$(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) $(BUILDDIR)/locale +\t@echo +\t@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + changes: \t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes \t@echo @@ -472,6 +478,7 @@ if "%%1" == "help" ( \techo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter \techo. text to make text files \techo. man to make manual pages +\techo. gettext to make PO message catalogs \techo. changes to make an overview over all changed/added/deprecated items \techo. linkcheck to check all external links for integrity \techo. doctest to run all doctests embedded in the documentation if enabled @@ -573,6 +580,13 @@ if "%%1" == "man" ( \tgoto end ) +if "%%1" == "gettext" ( +\t%%SPHINXBUILD%% -b gettext %%ALLSPHINXOPTS%% %%BUILDDIR%%/locale +\techo. +\techo.Build finished. The message catalogs are in %%BUILDDIR%%/locale. +\tgoto end +) + if "%%1" == "changes" ( \t%%SPHINXBUILD%% -b changes %%ALLSPHINXOPTS%% %%BUILDDIR%%/changes \techo. From ab16ae303a15e838fcf97dc477681da91eff0586 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Sun, 30 May 2010 09:36:23 +0200 Subject: [PATCH 111/744] Add gettext build using intl.MessageCatalogBuilder. --- sphinx/builders/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index e345d570f..cf7f39557 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -329,4 +329,5 @@ BUILTIN_BUILDERS = { 'man': ('manpage', 'ManualPageBuilder'), 'changes': ('changes', 'ChangesBuilder'), 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'), + 'gettext': ('intl', 'MessageCatalogBuilder'), } From a1932f62f0fe7121385b81842660688fdfa7bc4c Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Sun, 30 May 2010 17:02:54 +0200 Subject: [PATCH 112/744] Add gettext builder to Sphinx docs. --- doc/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/Makefile b/doc/Makefile index b873c7e5a..ff3cb22b3 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -29,6 +29,7 @@ help: @echo " epub to make an epub file" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run pdflatex" + @echo " gettext to make PO message catalogs" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @@ -112,6 +113,11 @@ latexpdf: make -C _build/latex all-pdf @echo "pdflatex finished; the PDF files are in _build/latex." +gettext: + $(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) _build/locale + @echo + @echo "Build finished. The message catalogs are in _build/locale." + changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo From 460874e11951815c3c96baf4ceaf79cfee5889cb Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 30 May 2010 20:52:24 -0500 Subject: [PATCH 113/744] Revert incomplete change in last revision. --- sphinx/cmdline.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py index 8f8e6b313..e3e944656 100644 --- a/sphinx/cmdline.py +++ b/sphinx/cmdline.py @@ -21,7 +21,7 @@ from sphinx import __version__ from sphinx.errors import SphinxError from sphinx.application import Sphinx from sphinx.util import Tee, format_exception_cut_frames, save_traceback -from sphinx.util.console import red, nocolor, init_color +from sphinx.util.console import red, nocolor, color_terminal def usage(argv, msg=None): @@ -57,7 +57,10 @@ Modi: def main(argv): - init_color() + if not color_terminal(): + # Windows' poor cmd box doesn't understand ANSI sequences + nocolor() + try: opts, args = getopt.getopt(argv[1:], 'ab:t:d:c:CD:A:ng:NEqQWw:P') allopts = set(opt[0] for opt in opts) From 099a58e0b7b37b0737c83253fda5b1ff344bdec2 Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 30 May 2010 21:33:04 -0500 Subject: [PATCH 114/744] "Initial commit": Added sphinx.websupport module, as well as a builder and writer for the web support package. --- CHANGES.jacobmason | 2 ++ sphinx/builders/__init__.py | 1 + sphinx/builders/websupport.py | 56 +++++++++++++++++++++++++++++++++++ sphinx/websupport/__init__.py | 14 +++++++++ sphinx/websupport/api.py | 40 +++++++++++++++++++++++++ sphinx/websupport/document.py | 37 +++++++++++++++++++++++ sphinx/writers/websupport.py | 29 ++++++++++++++++++ 7 files changed, 179 insertions(+) create mode 100644 CHANGES.jacobmason create mode 100644 sphinx/builders/websupport.py create mode 100644 sphinx/websupport/__init__.py create mode 100644 sphinx/websupport/api.py create mode 100644 sphinx/websupport/document.py create mode 100644 sphinx/writers/websupport.py diff --git a/CHANGES.jacobmason b/CHANGES.jacobmason new file mode 100644 index 000000000..42adc4270 --- /dev/null +++ b/CHANGES.jacobmason @@ -0,0 +1,2 @@ +May 30: Added files builders/websupport.py, writers/websupport.py, +websupport/api.py, and websupport/document.api. Provides a rudimentary method of building websupport data, and rendering it as html. \ No newline at end of file diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index e345d570f..328b26683 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -329,4 +329,5 @@ BUILTIN_BUILDERS = { 'man': ('manpage', 'ManualPageBuilder'), 'changes': ('changes', 'ChangesBuilder'), 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'), + 'websupport': ('websupport', 'WebSupportBuilder'), } diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py new file mode 100644 index 000000000..d39bbd6de --- /dev/null +++ b/sphinx/builders/websupport.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.websupport + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Builder for the web support package. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from os import path + +from sphinx.util.osutil import ensuredir, os_path +from sphinx.builders.html import PickleHTMLBuilder +from sphinx.writers.websupport import WebSupportTranslator + +class WebSupportBuilder(PickleHTMLBuilder): + """ + Builds documents for the web support package. + """ + name = 'websupport' + template_suffix = '.html' + + def init_translator_class(self): + self.translator_class = WebSupportTranslator + + def write_doc(self, docname, doctree): + # The translator needs the docuname to generate ids. + self.docname = docname + PickleHTMLBuilder.write_doc(self, docname, doctree) + + def handle_page(self, pagename, ctx, templatename='', **ignored): + # Mostly copied from PickleHTMLBuilder. + ctx['current_page_name'] = pagename + self.add_sidebars(pagename, ctx) + + self.app.emit('html-page-context', pagename, ctx) + + # Instead of pickling ctx as PickleHTMLBuilder does, we + # create a Document object and pickle that. + document = self.docwriter.visitor.support_document + document.body = ctx['body'] if 'body' in ctx else '' + document.title = ctx['title'] if 'title' in ctx else '' + + doc_filename = path.join(self.outdir, + os_path(pagename) + self.out_suffix) + ensuredir(path.dirname(doc_filename)) + f = open(doc_filename, 'wb') + try: + self.implementation.dump(document, f, 2) + finally: + f.close() + + def get_target_uri(self, docname, typ=None): + return docname diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py new file mode 100644 index 000000000..36c2dcc91 --- /dev/null +++ b/sphinx/websupport/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport + ~~~~~~~~~~~~~~~~~ + + Web Support Package + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from sphinx.websupport.api import WebSupport + +support = WebSupport() diff --git a/sphinx/websupport/api.py b/sphinx/websupport/api.py new file mode 100644 index 000000000..da6fc9e1f --- /dev/null +++ b/sphinx/websupport/api.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.api + ~~~~~~~~~~~~~~~~~~~~ + + All API functions. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import cPickle as pickle +from os import path + +from jinja2 import Template + +from sphinx.application import Sphinx + +class WebSupport(object): + + def init(self, srcdir, outdir='', comment_html=''): + self.srcdir = srcdir + self.outdir = outdir or os.path.join(self.srcdir, '_build', + 'websupport') + self.comment_template = Template(comment_html) + + def build(self, **kwargs): + doctreedir = kwargs.pop('doctreedir', + path.join(self.outdir, 'doctrees')) + app = Sphinx(self.srcdir, self.srcdir, + self.outdir, doctreedir, 'websupport') + app.build() + + def get_document(self, docname): + infilename = path.join(self.outdir, docname + '.fpickle') + f = open(infilename, 'rb') + document = pickle.load(f) + # The document renders the comment_template. + document.comment_template = self.comment_template + return document diff --git a/sphinx/websupport/document.py b/sphinx/websupport/document.py new file mode 100644 index 000000000..d1f5677bb --- /dev/null +++ b/sphinx/websupport/document.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.document + ~~~~~~~~~~~~~~~~~~~~ + + Contains a Document class for working with Sphinx documents. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from os import path + +from jinja2 import Template +from docutils import nodes +from sphinx import addnodes + +class Document(object): + """A single Document such as 'index'.""" + def __init__(self): + self.commentable_nodes = [] + self.template = None + + def add_commentable(self, node_id, rst_source=''): + node = CommentableNode(node_id, rst_source) + + def render_comment(self, id): + return self.comment_template.render(id=id) + + def render_html(self, comments=False): + template = Template(self.body) + return template.render(render_comment=self.render_comment) + +class CommentableNode(object): + def __init__(self, id, rst_source=''): + self.id = id + self.rst_source='' diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py new file mode 100644 index 000000000..3b2507558 --- /dev/null +++ b/sphinx/writers/websupport.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +""" + sphinx.writers.websupport + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + docutils writers handling Sphinx' custom nodes. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from sphinx.writers.html import HTMLTranslator +from sphinx.websupport.document import Document + +class WebSupportTranslator(HTMLTranslator): + """ + Our custom HTML translator. + """ + def __init__(self, builder, *args, **kwargs): + HTMLTranslator.__init__(self, builder, *args, **kwargs) + self.support_document = Document() + self.current_id = 0 + + def depart_paragraph(self, node): + HTMLTranslator.depart_paragraph(self, node) + self.support_document.add_commentable(self.current_id) + self.body.append("{{ render_comment('%s-p%s') }}" % + (self.builder.docname, self.current_id)) + self.current_id += 1 From 0e26a6d46c1a61378b57d042968500032ba41a48 Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 30 May 2010 22:20:59 -0500 Subject: [PATCH 115/744] Fixed bad call to os.path --- sphinx/websupport/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/websupport/api.py b/sphinx/websupport/api.py index da6fc9e1f..3cf112a30 100644 --- a/sphinx/websupport/api.py +++ b/sphinx/websupport/api.py @@ -20,8 +20,8 @@ class WebSupport(object): def init(self, srcdir, outdir='', comment_html=''): self.srcdir = srcdir - self.outdir = outdir or os.path.join(self.srcdir, '_build', - 'websupport') + self.outdir = outdir or path.join(self.srcdir, '_build', + 'websupport') self.comment_template = Template(comment_html) def build(self, **kwargs): From 777386ef566a88246f6c4cadeba88fc79cc71d56 Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 31 May 2010 16:07:47 -0500 Subject: [PATCH 116/744] Switched to creating a list of html slices --- sphinx/builders/websupport.py | 6 ++---- sphinx/websupport/api.py | 7 +------ sphinx/websupport/document.py | 22 ++++++++-------------- sphinx/writers/websupport.py | 23 +++++++++++++++++++---- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index d39bbd6de..f6b64849c 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -26,7 +26,7 @@ class WebSupportBuilder(PickleHTMLBuilder): self.translator_class = WebSupportTranslator def write_doc(self, docname, doctree): - # The translator needs the docuname to generate ids. + # The translator needs the docname to generate ids. self.docname = docname PickleHTMLBuilder.write_doc(self, docname, doctree) @@ -38,10 +38,8 @@ class WebSupportBuilder(PickleHTMLBuilder): self.app.emit('html-page-context', pagename, ctx) # Instead of pickling ctx as PickleHTMLBuilder does, we - # create a Document object and pickle that. + # have created a Document object and pickle that. document = self.docwriter.visitor.support_document - document.body = ctx['body'] if 'body' in ctx else '' - document.title = ctx['title'] if 'title' in ctx else '' doc_filename = path.join(self.outdir, os_path(pagename) + self.out_suffix) diff --git a/sphinx/websupport/api.py b/sphinx/websupport/api.py index 3cf112a30..eca24fb5e 100644 --- a/sphinx/websupport/api.py +++ b/sphinx/websupport/api.py @@ -12,17 +12,14 @@ import cPickle as pickle from os import path -from jinja2 import Template - from sphinx.application import Sphinx class WebSupport(object): - def init(self, srcdir, outdir='', comment_html=''): + def init(self, srcdir, outdir=''): self.srcdir = srcdir self.outdir = outdir or path.join(self.srcdir, '_build', 'websupport') - self.comment_template = Template(comment_html) def build(self, **kwargs): doctreedir = kwargs.pop('doctreedir', @@ -35,6 +32,4 @@ class WebSupport(object): infilename = path.join(self.outdir, docname + '.fpickle') f = open(infilename, 'rb') document = pickle.load(f) - # The document renders the comment_template. - document.comment_template = self.comment_template return document diff --git a/sphinx/websupport/document.py b/sphinx/websupport/document.py index d1f5677bb..16a60934b 100644 --- a/sphinx/websupport/document.py +++ b/sphinx/websupport/document.py @@ -18,20 +18,14 @@ from sphinx import addnodes class Document(object): """A single Document such as 'index'.""" def __init__(self): - self.commentable_nodes = [] - self.template = None + self.slices = [] - def add_commentable(self, node_id, rst_source=''): - node = CommentableNode(node_id, rst_source) + def add_slice(self, html, id=None, commentable=False): + slice = HTMLSlice(html, id, commentable) + self.slices.append(slice) - def render_comment(self, id): - return self.comment_template.render(id=id) - - def render_html(self, comments=False): - template = Template(self.body) - return template.render(render_comment=self.render_comment) - -class CommentableNode(object): - def __init__(self, id, rst_source=''): +class HTMLSlice(object): + def __init__(self, html, id, commentable): + self.html = html self.id = id - self.rst_source='' + self.commentable = commentable diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 3b2507558..e712b1339 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -18,12 +18,27 @@ class WebSupportTranslator(HTMLTranslator): """ def __init__(self, builder, *args, **kwargs): HTMLTranslator.__init__(self, builder, *args, **kwargs) + self.init_support() + + def init_support(self): self.support_document = Document() self.current_id = 0 + + def handle_visit_commentable(self, node): + self.support_document.add_slice(''.join(self.body)) + self.body = [] + + def handle_depart_commentable(self, node): + slice_id = '%s-%s' % (self.builder.docname, self.current_id) + self.support_document.add_slice(''.join(self.body), + slice_id, commentable=True) + self.body = [] + self.current_id += 1 + + def visit_paragraph(self, node): + HTMLTranslator.visit_paragraph(self, node) + self.handle_visit_commentable(node) def depart_paragraph(self, node): HTMLTranslator.depart_paragraph(self, node) - self.support_document.add_commentable(self.current_id) - self.body.append("{{ render_comment('%s-p%s') }}" % - (self.builder.docname, self.current_id)) - self.current_id += 1 + self.handle_depart_commentable(node) From 9e81b8f003f0b0d055189367c1a701048796644a Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 31 May 2010 17:12:32 -0500 Subject: [PATCH 117/744] Slice at bullet_list, desc, or paragraph now. --- sphinx/writers/websupport.py | 43 +++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index e712b1339..a1e59788c 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -16,29 +16,46 @@ class WebSupportTranslator(HTMLTranslator): """ Our custom HTML translator. """ + commentable_nodes = ['bullet_list', 'paragraph', 'desc'] + def __init__(self, builder, *args, **kwargs): HTMLTranslator.__init__(self, builder, *args, **kwargs) self.init_support() def init_support(self): self.support_document = Document() + self.in_commentable = False self.current_id = 0 + def dispatch_visit(self, node): + if node.__class__.__name__ in self.commentable_nodes: + self.handle_visit_commentable(node) + HTMLTranslator.dispatch_visit(self, node) + + def dispatch_departure(self, node): + HTMLTranslator.dispatch_departure(self, node) + if node.__class__.__name__ in self.commentable_nodes: + self.handle_depart_commentable(node) + def handle_visit_commentable(self, node): - self.support_document.add_slice(''.join(self.body)) - self.body = [] + # If we are already recording a commentable slice we don't do + # anything. We can't handle nesting. + if not self.in_commentable: + self.support_document.add_slice(''.join(self.body)) + node.commented = self.in_commentable = True + self.body = [] + else: + node.commented = False def handle_depart_commentable(self, node): - slice_id = '%s-%s' % (self.builder.docname, self.current_id) - self.support_document.add_slice(''.join(self.body), - slice_id, commentable=True) - self.body = [] - self.current_id += 1 + assert(self.in_commentable) + if node.commented: + slice_id = '%s-%s' % (self.builder.docname, self.current_id) + self.current_id += 1 - def visit_paragraph(self, node): - HTMLTranslator.visit_paragraph(self, node) - self.handle_visit_commentable(node) + body = ''.join(self.body) + self.support_document.add_slice(body, slice_id, commentable=True) + + self.in_commentable = False + self.body = [] - def depart_paragraph(self, node): - HTMLTranslator.depart_paragraph(self, node) - self.handle_depart_commentable(node) From 2cbe83cf5e07657bef7cc7b5fc0ec86799d35474 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 2 Jun 2010 09:17:42 +0200 Subject: [PATCH 118/744] Collect raw messages for translation. --- sphinx/builders/intl.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 5bc186971..0aecdcf3d 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -9,10 +9,14 @@ :license: BSD, see LICENSE for details. """ +import collections +from docutils import nodes + from sphinx.builders import Builder class MessageCatalogBuilder(Builder): - pass + def init(self): + self.catalogs = collections.defaultdict(list) def get_target_uri(self, docname, typ=None): return '' @@ -24,7 +28,9 @@ class MessageCatalogBuilder(Builder): return def write_doc(self, docname, doctree): - return + catalog = self.catalogs[docname.split('/')[0]] + for node in doctree.traverse(nodes.TextElement): + catalog.append(node.astext()) def finish(self): return From 8a87a46318f0deda6a29c389a394dc95de56e575 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 2 Jun 2010 09:18:30 +0200 Subject: [PATCH 119/744] Normalize messages for later rewrapping. --- sphinx/builders/intl.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 0aecdcf3d..2cbb17cf3 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -30,7 +30,8 @@ class MessageCatalogBuilder(Builder): def write_doc(self, docname, doctree): catalog = self.catalogs[docname.split('/')[0]] for node in doctree.traverse(nodes.TextElement): - catalog.append(node.astext()) + msg = node.astext().replace('\n', ' ') + catalog.append(msg) def finish(self): return From 8413f2ce503f6f28e9454ae3256317de424280d7 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 2 Jun 2010 09:19:08 +0200 Subject: [PATCH 120/744] Ignore invisible and inline nodes during translation. --- sphinx/builders/intl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 2cbb17cf3..9373baae8 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -30,6 +30,8 @@ class MessageCatalogBuilder(Builder): def write_doc(self, docname, doctree): catalog = self.catalogs[docname.split('/')[0]] for node in doctree.traverse(nodes.TextElement): + if isinstance(node, (nodes.Invisible, nodes.Inline)): + continue msg = node.astext().replace('\n', ' ') catalog.append(msg) From 995c18609c4e68dd6b8450f8b7206952a78f189d Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 2 Jun 2010 09:35:33 +0200 Subject: [PATCH 121/744] Write message catalogs to POT files. --- sphinx/builders/intl.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 9373baae8..c40fff266 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -10,6 +10,8 @@ """ import collections +from os import path + from docutils import nodes from sphinx.builders import Builder @@ -36,4 +38,8 @@ class MessageCatalogBuilder(Builder): catalog.append(msg) def finish(self): - return + for section, messages in self.catalogs.iteritems(): + pofile = open(path.join(self.outdir, '%s.pot' % section), 'w') + for message in messages: + pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message + pofile.write(pomsg.encode('utf-8')) From 08381bb2ad2d17e3fc4d9faeba241640c80a412f Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 2 Jun 2010 09:45:13 +0200 Subject: [PATCH 122/744] Add meta information to PO headers. --- sphinx/builders/intl.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index c40fff266..3224d92f3 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -16,6 +16,27 @@ from docutils import nodes from sphinx.builders import Builder +POHEADER = r""" +# SOME DESCRIPTIVE TITLE. +# Copyright (C) %(copyright)s +# This file is distributed under the same license as the %(project)s package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-05-08 18:29+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +"""[1:] + class MessageCatalogBuilder(Builder): def init(self): self.catalogs = collections.defaultdict(list) @@ -40,6 +61,7 @@ class MessageCatalogBuilder(Builder): def finish(self): for section, messages in self.catalogs.iteritems(): pofile = open(path.join(self.outdir, '%s.pot' % section), 'w') + pofile.write(POHEADER % self.config) for message in messages: pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message pofile.write(pomsg.encode('utf-8')) From c894c38cd35a05a763655bfaccaa64212e4895c8 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 2 Jun 2010 19:16:12 -0500 Subject: [PATCH 123/744] Fixed bug that clipped the end of bodies. --- sphinx/writers/websupport.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index a1e59788c..6d255e64c 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -59,3 +59,8 @@ class WebSupportTranslator(HTMLTranslator): self.in_commentable = False self.body = [] + def depart_document(self, node): + assert(not self.in_commentable) + self.support_document.add_slice(''.join(self.body)) + + From e6c4d8f75235dbdef0fc5c7d13b707d3f372b1c4 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 2 Jun 2010 19:22:33 -0500 Subject: [PATCH 124/744] Added ctx to document. --- sphinx/builders/websupport.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index f6b64849c..48e895db5 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -32,7 +32,7 @@ class WebSupportBuilder(PickleHTMLBuilder): def handle_page(self, pagename, ctx, templatename='', **ignored): # Mostly copied from PickleHTMLBuilder. - ctx['current_page_name'] = pagename + ctx['current_page_name'] = ctx['pagename'] = pagename self.add_sidebars(pagename, ctx) self.app.emit('html-page-context', pagename, ctx) @@ -40,13 +40,14 @@ class WebSupportBuilder(PickleHTMLBuilder): # Instead of pickling ctx as PickleHTMLBuilder does, we # have created a Document object and pickle that. document = self.docwriter.visitor.support_document + document.__dict__.update(ctx) doc_filename = path.join(self.outdir, os_path(pagename) + self.out_suffix) ensuredir(path.dirname(doc_filename)) f = open(doc_filename, 'wb') try: - self.implementation.dump(document, f, 2) + self.implementation.dump(document, f) finally: f.close() From d60c41afa9585cc751b1240ad0d66acef8afaf9d Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Fri, 4 Jun 2010 16:54:37 +0200 Subject: [PATCH 125/744] Add public name to MessageCatalogBuilder. --- sphinx/builders/intl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 3224d92f3..8966cf8b3 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -38,6 +38,8 @@ msgstr "" """[1:] class MessageCatalogBuilder(Builder): + name = 'gettext' + def init(self): self.catalogs = collections.defaultdict(list) From 01e6e183035b8d7520a4741df093ba8b83b24b2b Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Fri, 4 Jun 2010 16:55:35 +0200 Subject: [PATCH 126/744] Escaped quotation marks in msgids. --- sphinx/builders/intl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 8966cf8b3..6291c4050 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -65,5 +65,6 @@ class MessageCatalogBuilder(Builder): pofile = open(path.join(self.outdir, '%s.pot' % section), 'w') pofile.write(POHEADER % self.config) for message in messages: + message = message.replace(u'"', ur'\"') pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message pofile.write(pomsg.encode('utf-8')) From 2801f52f15fa3051e903e3aaa4b7940378766141 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Fri, 4 Jun 2010 17:14:04 +0200 Subject: [PATCH 127/744] Properly close open .pot files. --- sphinx/builders/intl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 6291c4050..f388537d4 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -68,3 +68,4 @@ class MessageCatalogBuilder(Builder): message = message.replace(u'"', ur'\"') pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message pofile.write(pomsg.encode('utf-8')) + pofile.close() From 3f6fa966d000cc04d083926175e51087bb0826bc Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Fri, 4 Jun 2010 18:21:19 +0200 Subject: [PATCH 128/744] Use progress indicator for gettext builds. --- sphinx/builders/intl.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index f388537d4..67937582a 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -15,6 +15,7 @@ from os import path from docutils import nodes from sphinx.builders import Builder +from sphinx.util.console import darkgreen POHEADER = r""" # SOME DESCRIPTIVE TITLE. @@ -61,7 +62,9 @@ class MessageCatalogBuilder(Builder): catalog.append(msg) def finish(self): - for section, messages in self.catalogs.iteritems(): + for section, messages in self.status_iterator( + self.catalogs.iteritems(), "writing message catalogs... ", + lambda (section, _):darkgreen(section), len(self.catalogs)): pofile = open(path.join(self.outdir, '%s.pot' % section), 'w') pofile.write(POHEADER % self.config) for message in messages: From 4a197b29ce36b5aa4274007967a2180326f673b6 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Fri, 4 Jun 2010 18:23:06 +0200 Subject: [PATCH 129/744] Safeguard file.close() against failure. --- sphinx/builders/intl.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 67937582a..dc6cbf25e 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -66,9 +66,11 @@ class MessageCatalogBuilder(Builder): self.catalogs.iteritems(), "writing message catalogs... ", lambda (section, _):darkgreen(section), len(self.catalogs)): pofile = open(path.join(self.outdir, '%s.pot' % section), 'w') - pofile.write(POHEADER % self.config) - for message in messages: - message = message.replace(u'"', ur'\"') - pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message - pofile.write(pomsg.encode('utf-8')) - pofile.close() + try: + pofile.write(POHEADER % self.config) + for message in messages: + message = message.replace(u'"', ur'\"') + pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message + pofile.write(pomsg.encode('utf-8')) + finally: + pofile.close() From 7c80750ee33eef4464e0c2f27f60349937381041 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Fri, 4 Jun 2010 18:26:47 +0200 Subject: [PATCH 130/744] Prepare msgid for escaped sequences. --- sphinx/builders/intl.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index dc6cbf25e..506f90010 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -69,7 +69,8 @@ class MessageCatalogBuilder(Builder): try: pofile.write(POHEADER % self.config) for message in messages: - message = message.replace(u'"', ur'\"') + # message contains *one* line of text ready for translation + message = message.replace(u'\\', ur'\\').replace(u'"', ur'\"') pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message pofile.write(pomsg.encode('utf-8')) finally: From 0c21f913eb4fdb8e3ad5dcaa880fac6f560fddb6 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Fri, 4 Jun 2010 18:38:16 +0200 Subject: [PATCH 131/744] Document basic workflow in gettext builder. --- sphinx/builders/intl.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 506f90010..660155f38 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -39,6 +39,9 @@ msgstr "" """[1:] class MessageCatalogBuilder(Builder): + """ + Builds gettext-style message catalogs (.pot files). + """ name = 'gettext' def init(self): @@ -54,6 +57,11 @@ class MessageCatalogBuilder(Builder): return def write_doc(self, docname, doctree): + """ + Store a document's translatable strings in the message catalog of its + section. For this purpose a document's *top-level directory* -- or + otherwise its *name* -- is considered its section. + """ catalog = self.catalogs[docname.split('/')[0]] for node in doctree.traverse(nodes.TextElement): if isinstance(node, (nodes.Invisible, nodes.Inline)): From 4627e2a498b66603fa0304627a724dd82976f7c3 Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 4 Jun 2010 16:12:29 -0500 Subject: [PATCH 132/744] Made srcdir a kwarg. --- sphinx/websupport/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/websupport/api.py b/sphinx/websupport/api.py index eca24fb5e..0d2722d66 100644 --- a/sphinx/websupport/api.py +++ b/sphinx/websupport/api.py @@ -16,7 +16,7 @@ from sphinx.application import Sphinx class WebSupport(object): - def init(self, srcdir, outdir=''): + def init(self, srcdir='', outdir=''): self.srcdir = srcdir self.outdir = outdir or path.join(self.srcdir, '_build', 'websupport') From 65ac358d80c477172fa5e8a51ff0f8b0276514cb Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 4 Jun 2010 16:13:53 -0500 Subject: [PATCH 133/744] rough documentation --- doc/contents.rst | 1 + doc/web/api.rst | 26 +++++++++++++++ doc/web/quickstart.rst | 72 ++++++++++++++++++++++++++++++++++++++++++ doc/websupport.rst | 9 ++++++ 4 files changed, 108 insertions(+) create mode 100644 doc/web/api.rst create mode 100644 doc/web/quickstart.rst create mode 100644 doc/websupport.rst diff --git a/doc/contents.rst b/doc/contents.rst index 079f93f26..1fb667112 100644 --- a/doc/contents.rst +++ b/doc/contents.rst @@ -17,6 +17,7 @@ Sphinx documentation contents theming templating extensions + websupport faq glossary diff --git a/doc/web/api.rst b/doc/web/api.rst new file mode 100644 index 000000000..a371fe3fc --- /dev/null +++ b/doc/web/api.rst @@ -0,0 +1,26 @@ +.. _websupportapi: + +Web Support API +=============== + +.. module:: sphinx.websupport.api +.. class:: WebSupport + + The :class:`WebSupport` class provides a central interface for + working with :ref:`~sphinx.websupport.document.Document's. + +.. method:: init(srcdir='', outdir='') + + Initialize attributes. + +.. method:: get_document(docname) + + Retrieve the :class:`~sphinx.websupport.document.Document` object + corresponding to the *docname*. + +.. module:: sphinx.websupport.document +.. class:: Document + + The :class:`Document` provides access to a single document. It + is not instantiated directly, but is returned by methods of the + :class:`~sphinx.websupport.api.WebSupport` object. diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst new file mode 100644 index 000000000..0a7094bfd --- /dev/null +++ b/doc/web/quickstart.rst @@ -0,0 +1,72 @@ +.. _websupportquickstart: + +Web Support Quick Start +======================= + +To use the :ref:`websupportapi` in your application you must import +the :class:`~sphinx.websupport.api.WebSupport` object:: + + from sphinx.websupport import support + +This provides a reference to a :class:`~sphinx.websupport.api.WebSupport` +object. You will then need to provide some information about your +environment:: + + support.init(srcdir='/path/to/rst/sources/', + outdir='/path/to/build/outdir') + +You only need to provide a srcdir if you are building documentation:: + + support.build() + +This will create the data the web support package needs and place +it in *outdir*. You can then access +:class:`~sphinx.websupport.document.Document` objects by calling +the get_document(docname) method. For example, to retrieve the "contents" +document, do this:: + + contents_doc = support.get_document('contents') + +A more useful example, in the form of a `Flask `_ +application is:: + + from flask import Flask, render_template + from sphinx.websupport import support + + app = Flask(__name__) + + support.init(outdir='/path/to/sphinx/data') + + @app.route('/docs/') + def doc(docname): + document = support.get_document(docname) + return render_template('doc.html', document=document) + +This simple application will return a +:class:`~sphinx.websupport.document.Document` object corresponding +to the *docname* variable. This object will have *title* attribute, +as well as a list of HTML "slices". Each slice contains some HTML, +and when joined they form the body of a Sphinx document. Each slice +may or may not be commentable. If a slice is commentable, it will +have an *id* attribute which is used to associate a comment with +part of a document. + +In the previous example the doc.html template would look something +like this:: + + {% extends "base.html" %} + + {% block title %} + {{ document.title }} + {% endblock %} + + {% block body %} + {% for slice in document.slices -%} + {{ slice.html|safe }} + {% if slice.commentable -%} + + comment + + {%- endif %} + {%- endfor %} + {% endblock %} diff --git a/doc/websupport.rst b/doc/websupport.rst new file mode 100644 index 000000000..a78704606 --- /dev/null +++ b/doc/websupport.rst @@ -0,0 +1,9 @@ +.. _websupport: + +Sphinx Web Support +================== + +.. toctree:: + + web/quickstart + web/api \ No newline at end of file From b1f39eac033517bbb5bc56f2d022d1eb298a466c Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 4 Jun 2010 16:46:06 -0500 Subject: [PATCH 134/744] updated CHANGES.jacobmason --- CHANGES.jacobmason | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.jacobmason b/CHANGES.jacobmason index 42adc4270..fe7c57fbb 100644 --- a/CHANGES.jacobmason +++ b/CHANGES.jacobmason @@ -1,2 +1,6 @@ May 30: Added files builders/websupport.py, writers/websupport.py, -websupport/api.py, and websupport/document.api. Provides a rudimentary method of building websupport data, and rendering it as html. \ No newline at end of file +websupport/api.py, and websupport/document.api. Provides a rudimentary +method of building websupport data, and rendering it as html. + +May 31-June 4: Continued changing way web support data is represented +and accessed. \ No newline at end of file From d3f161859842c59a3c16159ffc75975cb60076d3 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Sun, 6 Jun 2010 20:54:01 +0200 Subject: [PATCH 135/744] Supply version information in PO header. --- sphinx/builders/intl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 660155f38..199a5a3e1 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -17,7 +17,7 @@ from docutils import nodes from sphinx.builders import Builder from sphinx.util.console import darkgreen -POHEADER = r""" +POHEADER = ur""" # SOME DESCRIPTIVE TITLE. # Copyright (C) %(copyright)s # This file is distributed under the same license as the %(project)s package. @@ -26,7 +26,7 @@ POHEADER = r""" #, fuzzy msgid "" msgstr "" -"Project-Id-Version: 1.0\n" +"Project-Id-Version: %(version)s\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-05-08 18:29+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" From e588fa8176538b2720be667a9e49fe79dfe60e90 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Sun, 6 Jun 2010 20:55:57 +0200 Subject: [PATCH 136/744] Supply initial creation date in PO header. --- sphinx/builders/intl.py | 2 +- sphinx/config.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 199a5a3e1..2fc5011cb 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -28,7 +28,7 @@ msgid "" msgstr "" "Project-Id-Version: %(version)s\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-05-08 18:29+0200\n" +"POT-Creation-Date: %(gettext_ctime)s\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/sphinx/config.py b/sphinx/config.py index e25427828..ddaa3dfd3 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -11,6 +11,7 @@ import os import re +from datetime import datetime from os import path from sphinx.errors import ConfigError @@ -150,6 +151,11 @@ class Config(object): # manpage options man_pages = ([], None), + + # gettext options + gettext_ctime = (lambda self:datetime.now() # should supply tz + .strftime('%Y-%m-%d %H:%M%z'), + 'gettext'), ) def __init__(self, dirname, filename, overrides, tags): From ead9fa89b0a6dd5d45efbc2191a61d0c78e6f099 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Mon, 7 Jun 2010 13:58:04 +0200 Subject: [PATCH 137/744] Add generic test for gettext builder. --- tests/test_build.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_build.py b/tests/test_build.py index f18ff1754..6d98e3995 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -57,3 +57,7 @@ else: @with_app(buildername='singlehtml', cleanenv=True) def test_singlehtml(app): app.builder.build_all() + +@with_app(buildername='gettext') +def test_gettext(app): + app.builder.build_all() From 0299008e55fc2f0e2c585d5e97139f90a1ac62b3 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Mon, 7 Jun 2010 14:04:16 +0200 Subject: [PATCH 138/744] Initial tests for gettext build. --- tests/test_build_gettext.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/test_build_gettext.py diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py new file mode 100644 index 000000000..caa775875 --- /dev/null +++ b/tests/test_build_gettext.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +""" + test_build_gettext + ~~~~~~~~~~~~~~~~ + + Test the build process with gettext builder with the test root. + + :copyright: Copyright 2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from util import * + + +def teardown_module(): + (test_root / '_build').rmtree(True) + + +@with_app(buildername='gettext', cleanenv=True) +def test_gettext(app): + app.builder.build_all() + assert (app.outdir / 'contents.pot').isfile() + # group into sections + assert (app.outdir / 'subdir.pot').isfile() From 8e8fd9ca98c7335fab76797c6428d1b8769b33b9 Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 7 Jun 2010 16:09:19 -0500 Subject: [PATCH 139/744] Now serves static body. --- doc/web/api.rst | 2 +- sphinx/builders/websupport.py | 25 ----------------------- sphinx/locale/__init__.py | 3 +++ sphinx/writers/websupport.py | 38 ++++++++++++++++------------------- 4 files changed, 21 insertions(+), 47 deletions(-) diff --git a/doc/web/api.rst b/doc/web/api.rst index a371fe3fc..66e89af3e 100644 --- a/doc/web/api.rst +++ b/doc/web/api.rst @@ -7,7 +7,7 @@ Web Support API .. class:: WebSupport The :class:`WebSupport` class provides a central interface for - working with :ref:`~sphinx.websupport.document.Document's. + working with :class:`~sphinx.websupport.document.Document`'s. .. method:: init(srcdir='', outdir='') diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 48e895db5..8bc94174b 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -9,9 +9,6 @@ :license: BSD, see LICENSE for details. """ -from os import path - -from sphinx.util.osutil import ensuredir, os_path from sphinx.builders.html import PickleHTMLBuilder from sphinx.writers.websupport import WebSupportTranslator @@ -20,7 +17,6 @@ class WebSupportBuilder(PickleHTMLBuilder): Builds documents for the web support package. """ name = 'websupport' - template_suffix = '.html' def init_translator_class(self): self.translator_class = WebSupportTranslator @@ -30,26 +26,5 @@ class WebSupportBuilder(PickleHTMLBuilder): self.docname = docname PickleHTMLBuilder.write_doc(self, docname, doctree) - def handle_page(self, pagename, ctx, templatename='', **ignored): - # Mostly copied from PickleHTMLBuilder. - ctx['current_page_name'] = ctx['pagename'] = pagename - self.add_sidebars(pagename, ctx) - - self.app.emit('html-page-context', pagename, ctx) - - # Instead of pickling ctx as PickleHTMLBuilder does, we - # have created a Document object and pickle that. - document = self.docwriter.visitor.support_document - document.__dict__.update(ctx) - - doc_filename = path.join(self.outdir, - os_path(pagename) + self.out_suffix) - ensuredir(path.dirname(doc_filename)) - f = open(doc_filename, 'wb') - try: - self.implementation.dump(document, f) - finally: - f.close() - def get_target_uri(self, docname, typ=None): return docname diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index badcca1cd..43e0942cd 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -32,6 +32,9 @@ class _TranslationProxy(UserString.UserString, object): return unicode(func) return object.__new__(cls) + def __getnewargs__(self): + return (self._func,) + self._args + def __init__(self, func, *args): self._func = func self._args = args diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 6d255e64c..d99d7dc91 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -10,7 +10,6 @@ """ from sphinx.writers.html import HTMLTranslator -from sphinx.websupport.document import Document class WebSupportTranslator(HTMLTranslator): """ @@ -23,7 +22,6 @@ class WebSupportTranslator(HTMLTranslator): self.init_support() def init_support(self): - self.support_document = Document() self.in_commentable = False self.current_id = 0 @@ -38,29 +36,27 @@ class WebSupportTranslator(HTMLTranslator): self.handle_depart_commentable(node) def handle_visit_commentable(self, node): - # If we are already recording a commentable slice we don't do - # anything. We can't handle nesting. - if not self.in_commentable: - self.support_document.add_slice(''.join(self.body)) - node.commented = self.in_commentable = True - self.body = [] - else: + # If this node is nested inside another commentable node this + # node will not be commented. + if self.in_commentable: node.commented = False + else: + node.commented = self.in_commentable = True + node.id = self.create_id(node) + # We will place the node in the HTML id attribute. If the node + # already has another id (for indexing purposes) put an empty + # span with the existing id directly before this node's HTML. + if node.attributes['ids']: + self.body.append('' + % node.attributes['ids'][0]) + node.attributes['ids'] = [node.id] + node.attributes['classes'].append('spxcmt') def handle_depart_commentable(self, node): assert(self.in_commentable) if node.commented: - slice_id = '%s-%s' % (self.builder.docname, self.current_id) - self.current_id += 1 - - body = ''.join(self.body) - self.support_document.add_slice(body, slice_id, commentable=True) - self.in_commentable = False - self.body = [] - - def depart_document(self, node): - assert(not self.in_commentable) - self.support_document.add_slice(''.join(self.body)) - + def create_id(self, node): + self.current_id += 1 + return '%s_%s' % (node.__class__.__name__, self.current_id) From 51ea0cb2fe5f71f3674a6cea45ceacbe43a16e96 Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 7 Jun 2010 18:19:40 -0500 Subject: [PATCH 140/744] removed document.py --- sphinx/websupport/document.py | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 sphinx/websupport/document.py diff --git a/sphinx/websupport/document.py b/sphinx/websupport/document.py deleted file mode 100644 index 16a60934b..000000000 --- a/sphinx/websupport/document.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.document - ~~~~~~~~~~~~~~~~~~~~ - - Contains a Document class for working with Sphinx documents. - - :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from os import path - -from jinja2 import Template -from docutils import nodes -from sphinx import addnodes - -class Document(object): - """A single Document such as 'index'.""" - def __init__(self): - self.slices = [] - - def add_slice(self, html, id=None, commentable=False): - slice = HTMLSlice(html, id, commentable) - self.slices.append(slice) - -class HTMLSlice(object): - def __init__(self, html, id, commentable): - self.html = html - self.id = id - self.commentable = commentable From 5ac5f23f2b031eba4e9dba8f482b1bbe42cf291b Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 9 Jun 2010 06:46:30 +0200 Subject: [PATCH 141/744] Verify PO file format with msginit. --- tests/test_build_gettext.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index caa775875..3a992e12f 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -9,6 +9,9 @@ :license: BSD, see LICENSE for details. """ +import os +from subprocess import Popen, PIPE + from util import * @@ -22,3 +25,22 @@ def test_gettext(app): assert (app.outdir / 'contents.pot').isfile() # group into sections assert (app.outdir / 'subdir.pot').isfile() + + cwd = os.getcwd() + os.chdir(app.outdir) + try: + try: + p = Popen(['msginit', '--no-translator', '-i', 'contents.pot'], + stdout=PIPE, stderr=PIPE) + except OSError: + return # most likely msginit was not found + else: + stdout, stderr = p.communicate() + if p.returncode != 0: + print stdout + print stderr + del app.cleanup_trees[:] + assert False, 'msginit exited with return code %s' % p.returncode + assert (app.outdir / 'en_US.po').isfile(), 'msginit failed' + finally: + os.chdir(cwd) From 438c24e2226811299246b62fd2497a5cca899c11 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 9 Jun 2010 06:54:59 +0200 Subject: [PATCH 142/744] Prepare test root catalogs for gettext with msgfmt. --- tests/test_build_gettext.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 3a992e12f..72c00a0dd 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -26,6 +26,7 @@ def test_gettext(app): # group into sections assert (app.outdir / 'subdir.pot').isfile() + (app.outdir / 'en' / 'LC_MESSAGES').makedirs() cwd = os.getcwd() os.chdir(app.outdir) try: @@ -42,5 +43,19 @@ def test_gettext(app): del app.cleanup_trees[:] assert False, 'msginit exited with return code %s' % p.returncode assert (app.outdir / 'en_US.po').isfile(), 'msginit failed' + try: + p = Popen(['msgfmt', 'en_US.po', '-o', + os.path.join('en', 'LC_MESSAGES', 'test_root.mo')], + stdout=PIPE, stderr=PIPE) + except OSError: + return # most likely msgfmt was not found + else: + stdout, stderr = p.communicate() + if p.returncode != 0: + print stdout + print stderr + del app.cleanup_trees[:] + assert False, 'msgfmt exited with return code %s' % p.returncode + assert (app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(), 'msgfmt failed' finally: os.chdir(cwd) From 3e449e300531ce52bdce48a7ef268120e6330432 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 9 Jun 2010 07:46:40 +0200 Subject: [PATCH 143/744] Delete generated files on test failure. --- tests/test_build_gettext.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 72c00a0dd..5a7945150 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -40,7 +40,6 @@ def test_gettext(app): if p.returncode != 0: print stdout print stderr - del app.cleanup_trees[:] assert False, 'msginit exited with return code %s' % p.returncode assert (app.outdir / 'en_US.po').isfile(), 'msginit failed' try: @@ -54,7 +53,6 @@ def test_gettext(app): if p.returncode != 0: print stdout print stderr - del app.cleanup_trees[:] assert False, 'msgfmt exited with return code %s' % p.returncode assert (app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(), 'msgfmt failed' finally: From c9dd331c12c8200f6074d76b815eaa68ee2e81f4 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 9 Jun 2010 07:47:14 +0200 Subject: [PATCH 144/744] Fix empty and duplicate nodes. --- sphinx/builders/intl.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 2fc5011cb..4943fb29c 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -66,7 +66,11 @@ class MessageCatalogBuilder(Builder): for node in doctree.traverse(nodes.TextElement): if isinstance(node, (nodes.Invisible, nodes.Inline)): continue - msg = node.astext().replace('\n', ' ') + msg = node.astext().replace('\n', ' ').strip() + # XXX nodes rendering empty are likely a bug in sphinx.addnodes + # XXX msgctxt for duplicate messages? + if not msg or msg in catalog: + continue catalog.append(msg) def finish(self): From 0109af80b56088e12325b0a499c67d3867fc33ef Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 9 Jun 2010 15:19:58 +0200 Subject: [PATCH 145/744] Refactor message extractor into utilities. --- sphinx/builders/intl.py | 14 +++++--------- sphinx/util/nodes.py | 11 +++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 4943fb29c..72fb78964 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -15,6 +15,7 @@ from os import path from docutils import nodes from sphinx.builders import Builder +from sphinx.util.nodes import extract_messages from sphinx.util.console import darkgreen POHEADER = ur""" @@ -63,15 +64,10 @@ class MessageCatalogBuilder(Builder): otherwise its *name* -- is considered its section. """ catalog = self.catalogs[docname.split('/')[0]] - for node in doctree.traverse(nodes.TextElement): - if isinstance(node, (nodes.Invisible, nodes.Inline)): - continue - msg = node.astext().replace('\n', ' ').strip() - # XXX nodes rendering empty are likely a bug in sphinx.addnodes - # XXX msgctxt for duplicate messages? - if not msg or msg in catalog: - continue - catalog.append(msg) + for msg in extract_messages(doctree): + # XXX msgctxt for duplicate messages + if msg not in catalog: + catalog.append(msg) def finish(self): for section, messages in self.status_iterator( diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 82427f134..84182b153 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -22,6 +22,17 @@ explicit_title_re = re.compile(r'^(.+?)\s*(?$', re.DOTALL) caption_ref_re = explicit_title_re # b/w compat alias +def extract_messages(doctree): + """Extract translatable messages from a document tree.""" + for node in doctree.traverse(nodes.TextElement): + if isinstance(node, (nodes.Invisible, nodes.Inline)): + continue + msg = node.astext().replace('\n', ' ').strip() + # XXX nodes rendering empty are likely a bug in sphinx.addnodes + if msg: + yield msg + + def nested_parse_with_titles(state, content, node): # hack around title style bookkeeping surrounding_title_styles = state.memo.title_styles From ed7c1ad43dd15c112c919e96a8474d9660ee90ae Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 15 Jun 2010 22:10:36 -0500 Subject: [PATCH 146/744] Don't add attributes to node. --- doc/websupport.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/websupport.rst b/doc/websupport.rst index a78704606..2927f5a79 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -3,6 +3,10 @@ Sphinx Web Support ================== +Sphinx provides a way to easily integrate Sphinx documentation +into your web application. To learn more read the +:ref:`websupportquickstart`. + .. toctree:: web/quickstart From cd227483ea2f162ea0e62e42563400a7bab808c3 Mon Sep 17 00:00:00 2001 From: jacob Date: Tue, 15 Jun 2010 22:15:10 -0500 Subject: [PATCH 147/744] Don't add attributes to node. --- sphinx/writers/websupport.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index d99d7dc91..4afc3ecbd 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -19,6 +19,7 @@ class WebSupportTranslator(HTMLTranslator): def __init__(self, builder, *args, **kwargs): HTMLTranslator.__init__(self, builder, *args, **kwargs) + self.comment_class = 'spxcmt' self.init_support() def init_support(self): @@ -38,23 +39,21 @@ class WebSupportTranslator(HTMLTranslator): def handle_visit_commentable(self, node): # If this node is nested inside another commentable node this # node will not be commented. - if self.in_commentable: - node.commented = False - else: - node.commented = self.in_commentable = True - node.id = self.create_id(node) + if not self.in_commentable: + self.in_commentable = True + id = self.create_id(node) # We will place the node in the HTML id attribute. If the node # already has another id (for indexing purposes) put an empty # span with the existing id directly before this node's HTML. if node.attributes['ids']: self.body.append('' % node.attributes['ids'][0]) - node.attributes['ids'] = [node.id] - node.attributes['classes'].append('spxcmt') + node.attributes['ids'] = [id] + node.attributes['classes'].append(self.comment_class) def handle_depart_commentable(self, node): assert(self.in_commentable) - if node.commented: + if self.comment_class in node.attributes['classes']: self.in_commentable = False def create_id(self, node): From 3613ce1a42848f237d7da6d65f02a667e40863d0 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 16 Jun 2010 08:33:33 -0500 Subject: [PATCH 148/744] updated documentation --- doc/web/api.rst | 12 ++--------- doc/web/quickstart.rst | 47 +++++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/doc/web/api.rst b/doc/web/api.rst index 66e89af3e..65bf0c583 100644 --- a/doc/web/api.rst +++ b/doc/web/api.rst @@ -7,7 +7,7 @@ Web Support API .. class:: WebSupport The :class:`WebSupport` class provides a central interface for - working with :class:`~sphinx.websupport.document.Document`'s. + working Sphinx documentation. .. method:: init(srcdir='', outdir='') @@ -15,12 +15,4 @@ Web Support API .. method:: get_document(docname) - Retrieve the :class:`~sphinx.websupport.document.Document` object - corresponding to the *docname*. - -.. module:: sphinx.websupport.document -.. class:: Document - - The :class:`Document` provides access to a single document. It - is not instantiated directly, but is returned by methods of the - :class:`~sphinx.websupport.api.WebSupport` object. + Retrieve the context dictionary corresponding to the *docname*. diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst index 0a7094bfd..94dfb576a 100644 --- a/doc/web/quickstart.rst +++ b/doc/web/quickstart.rst @@ -20,13 +20,15 @@ You only need to provide a srcdir if you are building documentation:: support.build() This will create the data the web support package needs and place -it in *outdir*. You can then access -:class:`~sphinx.websupport.document.Document` objects by calling -the get_document(docname) method. For example, to retrieve the "contents" +it in *outdir*. You can then access this data by calling the +get_document(docname) method. For example, to retrieve the "contents" document, do this:: contents_doc = support.get_document('contents') +This will return a dictionary containing the context you need to render +a document. + A more useful example, in the form of a `Flask `_ application is:: @@ -42,15 +44,6 @@ application is:: document = support.get_document(docname) return render_template('doc.html', document=document) -This simple application will return a -:class:`~sphinx.websupport.document.Document` object corresponding -to the *docname* variable. This object will have *title* attribute, -as well as a list of HTML "slices". Each slice contains some HTML, -and when joined they form the body of a Sphinx document. Each slice -may or may not be commentable. If a slice is commentable, it will -have an *id* attribute which is used to associate a comment with -part of a document. - In the previous example the doc.html template would look something like this:: @@ -60,13 +53,25 @@ like this:: {{ document.title }} {% endblock %} - {% block body %} - {% for slice in document.slices -%} - {{ slice.html|safe }} - {% if slice.commentable -%} - - comment - - {%- endif %} - {%- endfor %} + {% block extra_js %} + + + {% endblock %} + + {% block body %} + {{ document.body|safe }} + {% endblock %} + + {% block sidebar %} {% endblock %} From 95d95edacc09715b1e4834b9ae8b787832d7efcf Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 16 Jun 2010 08:41:43 -0500 Subject: [PATCH 149/744] fixed typo in docs --- doc/web/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/web/api.rst b/doc/web/api.rst index 65bf0c583..13df6a40f 100644 --- a/doc/web/api.rst +++ b/doc/web/api.rst @@ -7,7 +7,7 @@ Web Support API .. class:: WebSupport The :class:`WebSupport` class provides a central interface for - working Sphinx documentation. + working with Sphinx documentation. .. method:: init(srcdir='', outdir='') From cd07472676920e16560d1ae85f9ef8ea845834f2 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 16 Jun 2010 20:27:27 +0200 Subject: [PATCH 150/744] Extract translatable strings alongside their doctree nodes. --- sphinx/builders/intl.py | 2 +- sphinx/util/nodes.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 72fb78964..b760854b5 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -64,7 +64,7 @@ class MessageCatalogBuilder(Builder): otherwise its *name* -- is considered its section. """ catalog = self.catalogs[docname.split('/')[0]] - for msg in extract_messages(doctree): + for _, msg in extract_messages(doctree): # XXX msgctxt for duplicate messages if msg not in catalog: catalog.append(msg) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 84182b153..0b23d17fa 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -30,7 +30,7 @@ def extract_messages(doctree): msg = node.astext().replace('\n', ' ').strip() # XXX nodes rendering empty are likely a bug in sphinx.addnodes if msg: - yield msg + yield node, msg def nested_parse_with_titles(state, content, node): From e63bb0f4a98459138e1b853f960f9b716c7faa99 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Thu, 17 Jun 2010 11:46:49 +0200 Subject: [PATCH 151/744] Split up tests into logical units. --- tests/test_build_gettext.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 5a7945150..ee18d96e1 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -19,12 +19,17 @@ def teardown_module(): (test_root / '_build').rmtree(True) +@with_app(buildername='gettext', cleanenv=True) +def test_build(app): + app.builder.build_all() + # documents end up in a message catalog + assert (app.outdir / 'contents.pot').isfile() + # ..and are grouped into sections + assert (app.outdir / 'subdir.pot').isfile() + @with_app(buildername='gettext', cleanenv=True) def test_gettext(app): app.builder.build_all() - assert (app.outdir / 'contents.pot').isfile() - # group into sections - assert (app.outdir / 'subdir.pot').isfile() (app.outdir / 'en' / 'LC_MESSAGES').makedirs() cwd = os.getcwd() From b1c480f5c68b63f5c80c2577e615b0d668e4721f Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Thu, 17 Jun 2010 11:49:07 +0200 Subject: [PATCH 152/744] Remove cleanenv setting from tests. --- tests/test_build_gettext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index ee18d96e1..236558359 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -19,7 +19,7 @@ def teardown_module(): (test_root / '_build').rmtree(True) -@with_app(buildername='gettext', cleanenv=True) +@with_app(buildername='gettext') def test_build(app): app.builder.build_all() # documents end up in a message catalog @@ -27,7 +27,7 @@ def test_build(app): # ..and are grouped into sections assert (app.outdir / 'subdir.pot').isfile() -@with_app(buildername='gettext', cleanenv=True) +@with_app(buildername='gettext') def test_gettext(app): app.builder.build_all() From b5f29b2c7c7e6a054ac2f5deee9feafad2289801 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Thu, 17 Jun 2010 11:57:59 +0200 Subject: [PATCH 153/744] Strip down tests to build only critical parts. --- tests/test_build_gettext.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 236558359..c0dff938a 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -21,22 +21,22 @@ def teardown_module(): @with_app(buildername='gettext') def test_build(app): - app.builder.build_all() + app.builder.build(['extapi', 'subdir/includes']) # documents end up in a message catalog - assert (app.outdir / 'contents.pot').isfile() + assert (app.outdir / 'extapi.pot').isfile() # ..and are grouped into sections assert (app.outdir / 'subdir.pot').isfile() @with_app(buildername='gettext') def test_gettext(app): - app.builder.build_all() + app.builder.build(['markup']) (app.outdir / 'en' / 'LC_MESSAGES').makedirs() cwd = os.getcwd() os.chdir(app.outdir) try: try: - p = Popen(['msginit', '--no-translator', '-i', 'contents.pot'], + p = Popen(['msginit', '--no-translator', '-i', 'markup.pot'], stdout=PIPE, stderr=PIPE) except OSError: return # most likely msginit was not found From 804a92dad23363f32a9b6baef0cd6b37c86065ab Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Fri, 18 Jun 2010 10:15:08 +0200 Subject: [PATCH 154/744] Patch translatable messages with custom doctree. --- sphinx/environment.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index c8b3f018c..acf76f6eb 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -24,7 +24,7 @@ from itertools import izip, groupby from docutils import nodes from docutils.io import FileInput, NullOutput from docutils.core import Publisher -from docutils.utils import Reporter, relative_path +from docutils.utils import Reporter, relative_path, new_document from docutils.readers import standalone from docutils.parsers.rst import roles, directives from docutils.parsers.rst.languages import en as english @@ -36,7 +36,7 @@ from docutils.transforms.parts import ContentsFilter from sphinx import addnodes from sphinx.util import url_re, get_matching_docs, docname_join, \ FilenameUniqDict -from sphinx.util.nodes import clean_astext, make_refnode +from sphinx.util.nodes import clean_astext, make_refnode, extract_messages from sphinx.util.osutil import movefile, SEP, ustrftime from sphinx.util.matching import compile_matchers from sphinx.errors import SphinxError, ExtensionError @@ -168,13 +168,26 @@ class CitationReferences(Transform): refnode += nodes.Text('[' + cittext + ']') citnode.parent.replace(citnode, refnode) +class Locale(Transform): + """ + Replace translatable nodes with their translated doctree. + """ + default_priority = 0 + + def apply(self): + settings = self.document.settings + for node, msg in extract_messages(self.document): + ctx = node.parent + patch = new_document(msg, settings) + ctx.replace(node, patch.children) + class SphinxStandaloneReader(standalone.Reader): """ Add our own transforms. """ - transforms = [CitationReferences, DefaultSubstitutions, MoveModuleTargets, - HandleCodeBlocks, SortIds] + transforms = [Locale, CitationReferences, DefaultSubstitutions, + MoveModuleTargets, HandleCodeBlocks, SortIds] def get_transforms(self): return standalone.Reader.get_transforms(self) + self.transforms From 3a7ce4039ac8458f64f5a77cfc820fccc28633a2 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 23 Jun 2010 06:18:47 +0200 Subject: [PATCH 155/744] Check compiled message catalogs are processable with gettext. --- tests/test_build_gettext.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index c0dff938a..7041dcac8 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for details. """ +import gettext import os from subprocess import Popen, PIPE @@ -62,3 +63,6 @@ def test_gettext(app): assert (app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(), 'msgfmt failed' finally: os.chdir(cwd) + + _ = gettext.translation('test_root', app.outdir, languages=['en']).ugettext + assert _("Testing various markup") == u"Testing various markup" From 3c4cad50855d43ea28cf9242cdc962b5f74e3e2a Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 23 Jun 2010 07:28:58 +0200 Subject: [PATCH 156/744] Add parsing step to translation integration. --- sphinx/environment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index acf76f6eb..cab03cd23 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -26,7 +26,7 @@ from docutils.io import FileInput, NullOutput from docutils.core import Publisher from docutils.utils import Reporter, relative_path, new_document from docutils.readers import standalone -from docutils.parsers.rst import roles, directives +from docutils.parsers.rst import roles, directives, Parser as RSTParser from docutils.parsers.rst.languages import en as english from docutils.parsers.rst.directives.html import MetaBody from docutils.writers import UnfilteredWriter @@ -176,9 +176,12 @@ class Locale(Transform): def apply(self): settings = self.document.settings + parser = RSTParser() for node, msg in extract_messages(self.document): ctx = node.parent patch = new_document(msg, settings) + msgstr = "Insert translation **here**." + parser.parse(msgstr, patch) ctx.replace(node, patch.children) From 35ee258b2e391e62dd22900e5406194b64ac567c Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 23 Jun 2010 07:30:24 +0200 Subject: [PATCH 157/744] Fix source file reference in patched documents. --- sphinx/environment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index cab03cd23..a2c30ddd2 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -175,11 +175,11 @@ class Locale(Transform): default_priority = 0 def apply(self): - settings = self.document.settings + settings, source = self.document.settings, self.document['source'] parser = RSTParser() for node, msg in extract_messages(self.document): ctx = node.parent - patch = new_document(msg, settings) + patch = new_document(source, settings) msgstr = "Insert translation **here**." parser.parse(msgstr, patch) ctx.replace(node, patch.children) From 8e12af649f9dd5355ec5f2b681b61aab414e0880 Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Wed, 23 Jun 2010 07:36:33 +0200 Subject: [PATCH 158/744] Ignore orphan metadata field in translatable messages. --- sphinx/util/nodes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 0b23d17fa..87fd362c7 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -27,6 +27,9 @@ def extract_messages(doctree): for node in doctree.traverse(nodes.TextElement): if isinstance(node, (nodes.Invisible, nodes.Inline)): continue + # orphan + if isinstance(node, nodes.field_name) and node.children[0] == 'orphan': + continue msg = node.astext().replace('\n', ' ').strip() # XXX nodes rendering empty are likely a bug in sphinx.addnodes if msg: From 00f841be2ab3bb44766a1cff18924f6e65a98207 Mon Sep 17 00:00:00 2001 From: jacob Date: Wed, 23 Jun 2010 14:37:07 -0500 Subject: [PATCH 159/744] Added relbar and sidebar to documents --- sphinx/builders/websupport.py | 67 ++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 8bc94174b..55b90e683 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -9,22 +9,81 @@ :license: BSD, see LICENSE for details. """ -from sphinx.builders.html import PickleHTMLBuilder +import cPickle as pickle +from os import path + +from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile +from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.writers.websupport import WebSupportTranslator -class WebSupportBuilder(PickleHTMLBuilder): +class WebSupportBuilder(StandaloneHTMLBuilder): """ Builds documents for the web support package. """ name = 'websupport' - + out_suffix = '.fpickle' + def init_translator_class(self): self.translator_class = WebSupportTranslator def write_doc(self, docname, doctree): # The translator needs the docname to generate ids. self.docname = docname - PickleHTMLBuilder.write_doc(self, docname, doctree) + StandaloneHTMLBuilder.write_doc(self, docname, doctree) def get_target_uri(self, docname, typ=None): return docname + + def handle_page(self, pagename, addctx, templatename='page.html', + outfilename=None, event_arg=None): + # This is mostly copied from StandaloneHTMLBuilder. However, instead + # of rendering the template and saving the html, create a context + # dict and pickle it. + ctx = self.globalcontext.copy() + ctx['pagename'] = pagename + + def pathto(otheruri, resource=False, + baseuri=self.get_target_uri(pagename)): + if not resource: + otheruri = self.get_target_uri(otheruri) + uri = relative_uri(baseuri, otheruri) or '#' + return uri + ctx['pathto'] = pathto + ctx['hasdoc'] = lambda name: name in self.env.all_docs + ctx['encoding'] = encoding = self.config.html_output_encoding + ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw) + self.add_sidebars(pagename, ctx) + ctx.update(addctx) + + self.app.emit('html-page-context', pagename, templatename, + ctx, event_arg) + + # Create a dict that will be pickled and used by webapps. + doc_ctx = {'body': ctx.get('body', '')} + # Partially render the html template to proved a more useful ctx. + template = self.templates.environment.get_template(templatename) + template_module = template.make_module(ctx) + if hasattr(template_module, 'sidebar'): + doc_ctx['sidebar'] = template_module.sidebar() + if hasattr(template_module, 'relbar'): + doc_ctx['relbar'] = template_module.relbar() + + if not outfilename: + outfilename = path.join(self.outdir, + os_path(pagename) + self.out_suffix) + + ensuredir(path.dirname(outfilename)) + f = open(outfilename, 'wb') + try: + pickle.dump(doc_ctx, f, 2) + finally: + f.close() + + # if there is a source file, copy the source file for the + # "show source" link + if ctx.get('sourcename'): + source_name = path.join(self.outdir, '_sources', + os_path(ctx['sourcename'])) + ensuredir(path.dirname(source_name)) + copyfile(self.env.doc2path(pagename), source_name) + From 8c4e5351702f5125e8ed9044bf2dcaa9b5485a0a Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 24 Jun 2010 14:30:02 -0500 Subject: [PATCH 160/744] Added xapian search --- sphinx/builders/websupport.py | 18 +++++- sphinx/themes/basic/searchresults.html | 36 +++++++++++ sphinx/websupport/api.py | 44 ++++++++++++-- sphinx/websupport/search/__init__.py | 36 +++++++++++ sphinx/websupport/search/xapiansearch.py | 76 ++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 sphinx/themes/basic/searchresults.html create mode 100644 sphinx/websupport/search/__init__.py create mode 100644 sphinx/websupport/search/xapiansearch.py diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 55b90e683..8972c5479 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -23,12 +23,26 @@ class WebSupportBuilder(StandaloneHTMLBuilder): name = 'websupport' out_suffix = '.fpickle' + def init(self): + self.init_search() + StandaloneHTMLBuilder.init(self) + + def init_search(self): + self.search = self.app.search + if self.search is not None: + self.search.create_index() + def init_translator_class(self): self.translator_class = WebSupportTranslator def write_doc(self, docname, doctree): # The translator needs the docname to generate ids. self.docname = docname + # Index the page if search is enabled. + if self.search is not None: + doc_contents = doctree.astext() + title = doc_contents[:20] + self.search.add_document(docname, title, doc_contents) StandaloneHTMLBuilder.write_doc(self, docname, doctree) def get_target_uri(self, docname, typ=None): @@ -59,7 +73,8 @@ class WebSupportBuilder(StandaloneHTMLBuilder): ctx, event_arg) # Create a dict that will be pickled and used by webapps. - doc_ctx = {'body': ctx.get('body', '')} + doc_ctx = {'body': ctx.get('body', ''), + 'title': ctx.get('title', '')} # Partially render the html template to proved a more useful ctx. template = self.templates.environment.get_template(templatename) template_module = template.make_module(ctx) @@ -86,4 +101,3 @@ class WebSupportBuilder(StandaloneHTMLBuilder): os_path(ctx['sourcename'])) ensuredir(path.dirname(source_name)) copyfile(self.env.doc2path(pagename), source_name) - diff --git a/sphinx/themes/basic/searchresults.html b/sphinx/themes/basic/searchresults.html new file mode 100644 index 000000000..0fec38dea --- /dev/null +++ b/sphinx/themes/basic/searchresults.html @@ -0,0 +1,36 @@ +{# + basic/searchresults.html + ~~~~~~~~~~~~~~~~~ + + Template for the body of the search results page. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +

    Search

    +

    + From here you can search these documents. Enter your search + words into the box below and click "search". +

    +
    + + + +
    +{% if search_performed %} +

    Search Results

    +{% if not search_results %} +

    'Your search did not match any results.

    +{% endif %} +{% endif %} +
    + {% if search_results %} +
      + {% for href, caption, context in search_results %} +
    • {{ caption }} +
      {{ context|e }}
      +
    • + {% endfor %} +
    + {% endif %} +
    diff --git a/sphinx/websupport/api.py b/sphinx/websupport/api.py index 0d2722d66..cc5f2f500 100644 --- a/sphinx/websupport/api.py +++ b/sphinx/websupport/api.py @@ -12,20 +12,47 @@ import cPickle as pickle from os import path +from jinja2 import Environment, FileSystemLoader + from sphinx.application import Sphinx +from sphinx.websupport.search import search_adapters + +class WebSupportApp(Sphinx): + def __init__(self, *args, **kwargs): + self.search = kwargs.pop('search', None) + Sphinx.__init__(self, *args, **kwargs) class WebSupport(object): - - def init(self, srcdir='', outdir=''): + def init(self, srcdir='', outdir='', search=None): self.srcdir = srcdir self.outdir = outdir or path.join(self.srcdir, '_build', 'websupport') + self.init_templating() + if search is not None: + self.init_search(search) + + def init_templating(self): + import sphinx + template_path = path.join(path.dirname(sphinx.__file__), + 'themes', 'basic') + loader = FileSystemLoader(template_path) + self.template_env = Environment(loader=loader) + + def init_search(self, search): + mod, cls = search_adapters[search] + search_class = getattr(__import__('sphinx.websupport.search.' + mod, + None, None, [cls]), cls) + search_path = path.join(self.outdir, 'search') + self.search = search_class(search_path) + self.results_template = \ + self.template_env.get_template('searchresults.html') def build(self, **kwargs): doctreedir = kwargs.pop('doctreedir', path.join(self.outdir, 'doctrees')) - app = Sphinx(self.srcdir, self.srcdir, - self.outdir, doctreedir, 'websupport') + app = WebSupportApp(self.srcdir, self.srcdir, + self.outdir, doctreedir, 'websupport', + search=self.search) app.build() def get_document(self, docname): @@ -33,3 +60,12 @@ class WebSupport(object): f = open(infilename, 'rb') document = pickle.load(f) return document + + def get_search_results(self, q): + results, results_found, results_displayed = self.search.query(q) + ctx = {'search_performed': True, + 'search_results': results} + document = self.get_document('search') + document['body'] = self.results_template.render(ctx) + document['title'] = 'Search Results' + return document diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py new file mode 100644 index 000000000..ae82005a9 --- /dev/null +++ b/sphinx/websupport/search/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.search + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Server side search support for the web support package. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +class BaseSearch(object): + def create_index(self, path): + raise NotImplemented + + def add_document(self, path, title, text): + raise NotImplemented + + def query(self, q): + raise NotImplemented + + def extract_context(self, text, query_string): + # From GSOC 2009 + with_context_re = '([\W\w]{0,80})(%s)([\W\w]{0,80})' % (query_string) + try: + res = re.findall(with_context_re, text, re.I|re.U)[0] + return tuple((unicode(i, errors='ignore') for i in res)) + except IndexError: + return '', '', '' + +search_adapters = { + 'xapian': ('xapiansearch', 'XapianSearch'), + 'whoosh': ('whooshsearch', 'WhooshSearch'), + } diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py new file mode 100644 index 000000000..746a644d3 --- /dev/null +++ b/sphinx/websupport/search/xapiansearch.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.search.xapian + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Xapian search adapter. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from os import path + +import xapian + +from sphinx.util.osutil import ensuredir +from sphinx.websupport.search import BaseSearch + +class XapianSearch(BaseSearch): + # Adapted from the GSOC 2009 webapp project. + + # Xapian metadata constants + DOC_PATH = 0 + DOC_TITLE = 1 + + def __init__(self, db_path): + self.db_path = db_path + + def create_index(self): + ensuredir(self.db_path) + self.database = xapian.WritableDatabase(self.db_path, + xapian.DB_CREATE_OR_OPEN) + self.indexer = xapian.TermGenerator() + stemmer = xapian.Stem("english") + self.indexer.set_stemmer(stemmer) + + def add_document(self, path, title, text): + self.database.begin_transaction() + doc = xapian.Document() + doc.set_data(text) + doc.add_value(self.DOC_PATH, path) + doc.add_value(self.DOC_TITLE, title) + self.indexer.set_document(doc) + self.indexer.index_text(text) + for word in text.split(): + doc.add_posting(word, 1) + self.database.add_document(doc) + self.database.commit_transaction() + + def query(self, q): + database = xapian.Database(self.db_path) + enquire = xapian.Enquire(database) + qp = xapian.QueryParser() + stemmer = xapian.Stem("english") + qp.set_stemmer(stemmer) + qp.set_database(database) + qp.set_stemming_strategy(xapian.QueryParser.STEM_SOME) + query = qp.parse_query(q) + + # Find the top 100 results for the query. + enquire.set_query(query) + matches = enquire.get_mset(0, 100) + + results_found = matches.get_matches_estimated() + results_displayed = matches.size() + + results = [] + + for m in matches: + context = self.extract_context(m.document.get_data(), q) + results.append((m.document.get_value(self.DOC_PATH), + m.document.get_value(self.DOC_TITLE), + ''.join(context) )) + + return results, results_found, results_displayed + From 05c9c2842b28e55e49f0d1c00df7471f53b65885 Mon Sep 17 00:00:00 2001 From: Jacob Mason Date: Fri, 25 Jun 2010 14:37:46 -0500 Subject: [PATCH 161/744] Fixed styling problems in search results. --- sphinx/themes/basic/searchresults.html | 4 ++-- sphinx/websupport/api.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sphinx/themes/basic/searchresults.html b/sphinx/themes/basic/searchresults.html index 0fec38dea..e7fc84f8f 100644 --- a/sphinx/themes/basic/searchresults.html +++ b/sphinx/themes/basic/searchresults.html @@ -25,9 +25,9 @@ {% endif %}
    {% if search_results %} -
      +
    +Now that this is done it's time to define the functions that handle +the AJAX calls from the script. You will need three functions. The first +function is used to add a new comment, and will call the web support method +:meth:`~sphinx.websupport.WebSupport.add_comment`:: + + @app.route('/docs/add_comment', methods=['POST']) + def add_comment(): + parent_id = request.form.get('parent', '') + text = request.form.get('text', '') + username = g.user.name if g.user is not None else 'Anonymous' + comment = support.add_comment(parent_id, text, username=username) + return jsonify(comment=comment) + +Then next function handles the retrieval of comments for a specific node, +and is aptly named :meth:`~sphinx.websupport.WebSupport.get_comments`:: + + @app.route('/docs/get_comments') + def get_comments(): + user_id = g.user.id if g.user else None + parent_id = request.args.get('parent', '') + comments = support.get_comments(parent_id, user_id) + return jsonify(comments=comments) + +The final function that is needed will call +:meth:`~sphinx.websupport.WebSupport.process_vote`, and will handle user +votes on comments:: + + @app.route('/docs/process_vote', methods=['POST']) + def process_vote(): + if g.user is None: + abort(401) + comment_id = request.form.get('comment_id') + value = request.form.get('value') + if value is None or comment_id is None: + abort(400) + support.process_vote(comment_id, g.user.id, value) + return "success" + +.. note:: + + Authentication is left up to your existing web application. If you do + not have an existing authentication system there are many readily + available for different frameworks. The web support system stores only + the user's unique integer `user_id` and uses this both for storing votes + and retrieving vote information. It is up to you to ensure that the + user_id passed in is unique, and that the user is authenticated. The + default backend will only allow one vote per comment per `user_id`. diff --git a/doc/websupport.rst b/doc/websupport.rst index 2927f5a79..e8fc238b8 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -10,4 +10,5 @@ into your web application. To learn more read the .. toctree:: web/quickstart - web/api \ No newline at end of file + web/api + web/frontend \ No newline at end of file From 77515dee798716ee5949d68e650ee408f3f535c6 Mon Sep 17 00:00:00 2001 From: Jacob Mason Date: Mon, 12 Jul 2010 12:41:32 -0500 Subject: [PATCH 192/744] added frontend.rst --- doc/web/frontend.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 doc/web/frontend.rst diff --git a/doc/web/frontend.rst b/doc/web/frontend.rst new file mode 100644 index 000000000..5ffe16674 --- /dev/null +++ b/doc/web/frontend.rst @@ -0,0 +1,6 @@ +.. _websupportfrontend: + +Web Support Frontend +==================== + +More coming soon. \ No newline at end of file From fa1e30c155d46b22d78b56eebc1650d1b564d4ba Mon Sep 17 00:00:00 2001 From: Jacob Mason Date: Mon, 12 Jul 2010 14:25:08 -0500 Subject: [PATCH 193/744] updated CHANGES.jacobmason --- CHANGES.jacobmason | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGES.jacobmason b/CHANGES.jacobmason index fe7c57fbb..bd87c71c6 100644 --- a/CHANGES.jacobmason +++ b/CHANGES.jacobmason @@ -2,5 +2,14 @@ May 30: Added files builders/websupport.py, writers/websupport.py, websupport/api.py, and websupport/document.api. Provides a rudimentary method of building websupport data, and rendering it as html. -May 31-June 4: Continued changing way web support data is represented -and accessed. \ No newline at end of file +May 31-June 10: Continued changing way web support data is represented +and accessed. + +June 14 - June 17: Continued making improvements to the web support package +and demo web application. Included sidebars, navlinks etc... + +June 21 - June 26: Implement server side search with two search adapters, +one for Xapian and one for Whoosh + +June 28 - July 12: Implement voting system on the backend, and created a +jQuery script to handle voting on the frontend. \ No newline at end of file From 8ff41de37b90dc7fbaaeefdb3d40cf5ac650fd23 Mon Sep 17 00:00:00 2001 From: Jacob Mason Date: Tue, 13 Jul 2010 15:33:12 -0500 Subject: [PATCH 194/744] API tweaks and more documentation --- doc/websupport.rst | 3 +- sphinx/websupport/__init__.py | 15 ++-- sphinx/websupport/search/__init__.py | 88 +++++++++++++++++++++--- sphinx/websupport/search/whooshsearch.py | 8 +-- sphinx/websupport/search/xapiansearch.py | 6 +- 5 files changed, 95 insertions(+), 25 deletions(-) diff --git a/doc/websupport.rst b/doc/websupport.rst index e8fc238b8..1b6725df1 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -11,4 +11,5 @@ into your web application. To learn more read the web/quickstart web/api - web/frontend \ No newline at end of file + web/frontend + web/searchadapters \ No newline at end of file diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 2dbbe3197..407cb4c94 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -17,7 +17,7 @@ from jinja2 import Environment, FileSystemLoader from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir -from sphinx.websupport.search import search_adapters +from sphinx.websupport.search import BaseSearch, search_adapters from sphinx.websupport import comments as sphinxcomments class WebSupportApp(Sphinx): @@ -66,11 +66,14 @@ class WebSupport(object): self.template_env = Environment(loader=loader) def _init_search(self, search): - mod, cls = search_adapters[search] - search_class = getattr(__import__('sphinx.websupport.search.' + mod, + if isinstance(search, BaseSearch): + self.search = search + else: + mod, cls = search_adapters[search] + search_class = getattr(__import__('sphinx.websupport.search.' + mod, None, None, [cls]), cls) - search_path = path.join(self.outdir, 'search') - self.search = search_class(search_path) + search_path = path.join(self.outdir, 'search') + self.search = search_class(search_path) self.results_template = \ self.template_env.get_template('searchresults.html') @@ -133,7 +136,7 @@ class WebSupport(object): :param q: the search query """ - results, results_found, results_displayed = self.search.query(q) + results = self.search.query(q) ctx = {'search_performed': True, 'search_results': results, 'q': q} diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index b4bf73868..1886776a4 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -13,39 +13,107 @@ import re class BaseSearch(object): def init_indexing(self, changed=[]): + """Called by the builder to initialize the search indexer. `changed` + is a list of pagenames that will be reindexed. You may want to remove + these from the search index before indexing begins. + + `param changed` is a list of pagenames that will be re-indexed + """ pass def finish_indexing(self): + """Called by the builder when writing has been completed. Use this + to perform any finalization or cleanup actions after indexing is + complete. + """ pass def feed(self, pagename, title, doctree): + """Called by the builder to add a doctree to the index. Converts the + `doctree` to text and passes it to :meth:`add_document`. You probably + won't want to override this unless you need access to the `doctree`. + Override :meth:`add_document` instead. + + `pagename` is the name of the page to be indexed + + `title` is the title of the page to be indexed + + `doctree` is the docutils doctree representation of the page + """ self.add_document(pagename, title, doctree.astext()) - def add_document(self, path, title, text): - raise NotImplemented + def add_document(self, pagename, title, text): + """Called by :meth:`feed` to add a document to the search index. + This method should should do everything necessary to add a single + document to the search index. + + `pagename` is name of the page being indexed. + It is the combination of the source files relative path and filename, + minus the extension. For example, if the source file is + "ext/builders.rst", the `pagename` would be "ext/builders". This + will need to be returned with search results when processing a + query. + + `title` is the page's title, and will need to be returned with + search results. + + `text` is the full text of the page. You probably want to store this + somehow to use while creating the context for search results. + """ + raise NotImplementedError() def query(self, q): + """Called by the web support api to get search results. This method + compiles the regular expression to be used when + :meth:`extracting context `, then calls + :meth:`handle_query`. You won't want to override this unless you + don't want to use the included :meth:`extract_context` method. + Override :meth:`handle_query` instead. + + `q` is the search query string. + """ self.context_re = re.compile('|'.join(q.split()), re.I) return self.handle_query(q) def handle_query(self, q): - raise NotImplemented + """Called by :meth:`query` to retrieve search results for a search + query `q`. This should return an iterable containing tuples of the + following format:: - def extract_context(self, text, query_string): + (, <context>) + + `path` and `title` are the same values that were passed to + :meth:`add_document`, and `context` should be a short text snippet + of the text surrounding the search query in the document. + + The :meth:`extract_context` method is provided as a simple way + to create the `context`. + """ + raise NotImplementedError() + + def extract_context(self, text, length=240): + """Extract the context for the search query from the documents + full `text`. + + `text` is the full text of the document to create the context for. + + `length` is the length of the context snippet to return. + """ res = self.context_re.search(text) if res is None: return '' - start = max(res.start() - 120, 0) - end = start + 240 - context = ''.join(['...' if start > 0 else '', - text[start:end], - '...' if end < len(text) else '']) + context_start = max(res.start() - length/2, 0) + context_end = start + length + context = ''.join(['...' if context_start > 0 else '', + text[context_start:context_end], + '...' if context_end < len(text) else '']) try: return unicode(context, errors='ignore') except TypeError: return context - + +# The build in search adapters. search_adapters = { 'xapian': ('xapiansearch', 'XapianSearch'), 'whoosh': ('whooshsearch', 'WhooshSearch'), diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 991d42326..00c7403c5 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -38,8 +38,8 @@ class WhooshSearch(BaseSearch): def finish_indexing(self): self.writer.commit() - def add_document(self, path, title, text): - self.writer.add_document(path=unicode(path), + def add_document(self, pagename, title, text): + self.writer.add_document(path=unicode(pagename), title=title, text=text) @@ -47,10 +47,10 @@ class WhooshSearch(BaseSearch): res = self.searcher.find('text', q) results = [] for result in res: - context = self.extract_context(result['text'], q) + context = self.extract_context(result['text']) results.append((result['path'], result.get('title', ''), context)) - return results, len(res), res.scored_length() + return results diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py index f8dbecd9c..f5ad9688a 100644 --- a/sphinx/websupport/search/xapiansearch.py +++ b/sphinx/websupport/search/xapiansearch.py @@ -70,15 +70,13 @@ class XapianSearch(BaseSearch): # Find the top 100 results for the query. enquire.set_query(query) matches = enquire.get_mset(0, 100) - results_found = matches.get_matches_estimated() - results_displayed = matches.size() results = [] for m in matches: - context = self.extract_context(m.document.get_data(), q) + context = self.extract_context(m.document.get_data()) results.append((m.document.get_value(self.DOC_PATH), m.document.get_value(self.DOC_TITLE), ''.join(context) )) - return results, results_found, results_displayed + return results From 1f2a52f45c752d9c843089d9c195aa74b2b47d61 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 13 Jul 2010 17:00:23 -0500 Subject: [PATCH 195/744] small cleanup of xapiansearch.py --- sphinx/websupport/search/__init__.py | 4 ++-- sphinx/websupport/search/whooshsearch.py | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index 1886776a4..e1d7ea471 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -103,11 +103,11 @@ class BaseSearch(object): if res is None: return '' context_start = max(res.start() - length/2, 0) - context_end = start + length + context_end = context_start + length context = ''.join(['...' if context_start > 0 else '', text[context_start:context_end], '...' if context_end < len(text) else '']) - + try: return unicode(context, errors='ignore') except TypeError: diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 00c7403c5..52f49d8d9 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -18,6 +18,9 @@ from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch class WhooshSearch(BaseSearch): + """The whoosh search adapter for sphinx web support.""" + + # Define the Whoosh Schema for the search index. schema = Schema(path=ID(stored=True, unique=True), title=TEXT(field_boost=2.0, stored=True), text=TEXT(analyzer=StemmingAnalyzer(), stored=True)) @@ -33,24 +36,22 @@ class WhooshSearch(BaseSearch): def init_indexing(self, changed=[]): for changed_path in changed: self.index.delete_by_term('path', changed_path) - self.writer = self.index.writer() + self.index_writer = self.index.writer() def finish_indexing(self): - self.writer.commit() + self.index_writer.commit() def add_document(self, pagename, title, text): - self.writer.add_document(path=unicode(pagename), - title=title, - text=text) + self.index_writer.add_document(path=unicode(pagename), + title=title, + text=text) def handle_query(self, q): - res = self.searcher.find('text', q) + whoosh_results = self.searcher.find('text', q) results = [] - for result in res: + for result in whoosh_results: context = self.extract_context(result['text']) - results.append((result['path'], result.get('title', ''), context)) - return results From 38e1e7770f98f3e041d14f7b949f5f69909f60a4 Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Wed, 14 Jul 2010 22:01:08 +0200 Subject: [PATCH 196/744] Move translation patching back into transform for chronological order. --- sphinx/environment.py | 68 ++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index ccdf7d15f..d85ca27f9 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -16,6 +16,7 @@ import types import codecs import imghdr import string +import posixpath import cPickle as pickle from os import path from glob import glob @@ -181,12 +182,48 @@ class CitationReferences(Transform): refnode += nodes.Text('[' + cittext + ']') citnode.parent.replace(citnode, refnode) +class Locale(Transform): + """ + Replace translatable nodes with their translated doctree. + """ + default_priority = 0 + def apply(self): + env = self.document.settings.env + settings, source = self.document.settings, self.document['source'] + # XXX check if this is reliable + docname = posixpath.splitext(posixpath.basename(source))[0] + section = docname.split(SEP, 1)[0] + + # fetch translations + dirs = [path.join(env.srcdir, x) + for x in env.config.locale_dirs] + catalog, empty = init_locale(dirs, env.config.language, section) + if not empty: + return + + parser = RSTParser() + + for node, msg in extract_messages(self.document): + ctx = node.parent + patch = new_document(source, settings) + msgstr = catalog.ugettext(msg) + #XXX add marker to untranslated parts + if not msgstr or msgstr == msg: # as-of-yet untranslated + continue + parser.parse(msgstr, patch) + patch = patch[0] + assert isinstance(patch, nodes.paragraph) + for child in patch.children: # update leaves + child.parent = node + node.children = patch.children + + class SphinxStandaloneReader(standalone.Reader): """ Add our own transforms. """ - transforms = [CitationReferences, DefaultSubstitutions, + transforms = [Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds] def get_transforms(self): @@ -595,7 +632,6 @@ class BuildEnvironment: Parse a file and add/update inventory entries for the doctree. If srcpath is given, read from a different source file. """ - section = docname.split(SEP, 1)[0] # remove all inventory entries for that file if app: app.emit('env-purge-doc', self, docname) @@ -660,7 +696,6 @@ class BuildEnvironment: # post-processing self.filter_messages(doctree) - self.process_translations(doctree, self.get_translation(section)) self.process_dependencies(docname, doctree) self.process_images(docname, doctree) self.process_downloads(docname, doctree) @@ -735,14 +770,6 @@ class BuildEnvironment: def note_dependency(self, filename): self.dependencies.setdefault(self.docname, set()).add(filename) - def get_translation(self, section): - dirs = [path.join(self.srcdir, x) for x in self.config.locale_dirs] - translation, has_trans = init_locale(dirs, - self.config.language, section) - if not has_trans: - return None - return translation - # post-processing of read doctrees def filter_messages(self, doctree): @@ -754,25 +781,6 @@ class BuildEnvironment: if node['level'] < filterlevel: node.parent.remove(node) - def process_translations(self, doctree, translation): - """ - Replace translatable nodes with their translated doctree. - """ - if not translation: - return - settings, source = doctree.settings, doctree['source'] - parser = RSTParser() - for node, msg in extract_messages(doctree): - ctx = node.parent - patch = new_document(source, settings) - msgstr = translation.ugettext(msg) - #XXX add marker to untranslated parts - if not msgstr or msgstr == msg: # as-of-yet untranslated - continue - parser.parse(msgstr, patch) - assert isinstance(patch[0], nodes.paragraph) - node.children = patch[0].children - def process_dependencies(self, docname, doctree): """ From fda1d0985a6482a51f78ae78e60baa0609b1100d Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Wed, 14 Jul 2010 23:41:38 +0200 Subject: [PATCH 197/744] Fixed docname resolution. --- sphinx/environment.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index d85ca27f9..b03e46258 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -191,7 +191,8 @@ class Locale(Transform): env = self.document.settings.env settings, source = self.document.settings, self.document['source'] # XXX check if this is reliable - docname = posixpath.splitext(posixpath.basename(source))[0] + assert source.startswith(env.srcdir) + docname = posixpath.splitext(source[len(env.srcdir):].lstrip('/'))[0] section = docname.split(SEP, 1)[0] # fetch translations From dc2f45208ff306926c99dffa55b78089ef85c476 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 15 Jul 2010 13:25:12 -0500 Subject: [PATCH 198/744] separate sqlalchemystorage from __init__ --- doc/web/searchadapters.rst | 47 +++++++ doc/web/storagebackends.rst | 47 +++++++ doc/websupport.rst | 3 +- sphinx/websupport/__init__.py | 14 +- sphinx/websupport/comments/__init__.py | 123 +---------------- .../websupport/comments/sqlalchemystorage.py | 125 ++++++++++++++++++ 6 files changed, 229 insertions(+), 130 deletions(-) create mode 100644 doc/web/searchadapters.rst create mode 100644 doc/web/storagebackends.rst create mode 100644 sphinx/websupport/comments/sqlalchemystorage.py diff --git a/doc/web/searchadapters.rst b/doc/web/searchadapters.rst new file mode 100644 index 000000000..83e928baa --- /dev/null +++ b/doc/web/searchadapters.rst @@ -0,0 +1,47 @@ +.. _searchadapters: + +.. currentmodule:: sphinx.websupport.search + +Search Adapters +=============== + +To create a custom search adapter you will need to subclass the +:class:`~BaseSearch` class. Then create an instance of the new class +and pass that as the `search` keyword argument when you create the +:class:`~sphinx.websupport.WebSupport` object:: + + support = Websupport(srcdir=srcdir, + outdir=outdir, + search=MySearch()) + +For more information about creating a custom search adapter, please see +the documentation of the :class:`BaseSearch` class below. + +.. class:: BaseSearch + + Defines an interface for search adapters. + +BaseSearch Methods +~~~~~~~~~~~~~~~~~~ + + The following methods are defined in the BaseSearch class. Some methods + do not need to be overridden, but some ( + :meth:`~sphinx.websupport.search.BaseSearch.add_document` and + :meth:`~sphinx.websupport.search.BaseSearch.handle_query`) must be + overridden in your subclass. For a working example, look at the + built-in adapter for whoosh. + +.. automethod:: sphinx.websupport.search.BaseSearch.init_indexing + +.. automethod:: sphinx.websupport.search.BaseSearch.finish_indexing + +.. automethod:: sphinx.websupport.search.BaseSearch.feed + +.. automethod:: sphinx.websupport.search.BaseSearch.add_document + +.. automethod:: sphinx.websupport.search.BaseSearch.query + +.. automethod:: sphinx.websupport.search.BaseSearch.handle_query + +.. automethod:: sphinx.websupport.search.BaseSearch.extract_context + diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst new file mode 100644 index 000000000..83e928baa --- /dev/null +++ b/doc/web/storagebackends.rst @@ -0,0 +1,47 @@ +.. _searchadapters: + +.. currentmodule:: sphinx.websupport.search + +Search Adapters +=============== + +To create a custom search adapter you will need to subclass the +:class:`~BaseSearch` class. Then create an instance of the new class +and pass that as the `search` keyword argument when you create the +:class:`~sphinx.websupport.WebSupport` object:: + + support = Websupport(srcdir=srcdir, + outdir=outdir, + search=MySearch()) + +For more information about creating a custom search adapter, please see +the documentation of the :class:`BaseSearch` class below. + +.. class:: BaseSearch + + Defines an interface for search adapters. + +BaseSearch Methods +~~~~~~~~~~~~~~~~~~ + + The following methods are defined in the BaseSearch class. Some methods + do not need to be overridden, but some ( + :meth:`~sphinx.websupport.search.BaseSearch.add_document` and + :meth:`~sphinx.websupport.search.BaseSearch.handle_query`) must be + overridden in your subclass. For a working example, look at the + built-in adapter for whoosh. + +.. automethod:: sphinx.websupport.search.BaseSearch.init_indexing + +.. automethod:: sphinx.websupport.search.BaseSearch.finish_indexing + +.. automethod:: sphinx.websupport.search.BaseSearch.feed + +.. automethod:: sphinx.websupport.search.BaseSearch.add_document + +.. automethod:: sphinx.websupport.search.BaseSearch.query + +.. automethod:: sphinx.websupport.search.BaseSearch.handle_query + +.. automethod:: sphinx.websupport.search.BaseSearch.extract_context + diff --git a/doc/websupport.rst b/doc/websupport.rst index 1b6725df1..c7833e7ab 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -12,4 +12,5 @@ into your web application. To learn more read the web/quickstart web/api web/frontend - web/searchadapters \ No newline at end of file + web/searchadapters + web/storagebackends \ No newline at end of file diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 407cb4c94..c60c93505 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -18,7 +18,7 @@ from jinja2 import Environment, FileSystemLoader from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch, search_adapters -from sphinx.websupport import comments as sphinxcomments +from sphinx.websupport.comments import StorageBackend class WebSupportApp(Sphinx): def __init__(self, *args, **kwargs): @@ -46,17 +46,18 @@ class WebSupport(object): self._init_comments(comments) def _init_comments(self, comments): - if isinstance(comments, sphinxcomments.CommentBackend): + if isinstance(comments, StorageBackend): self.comments = comments else: - # If a CommentBackend isn't provided, use the default + # If a StorageBackend isn't provided, use the default # SQLAlchemy backend with an SQLite db. - from sphinx.websupport.comments import SQLAlchemyComments + from sphinx.websupport.comments.sqlalchemystorage \ + import SQLAlchemyStorage from sqlalchemy import create_engine db_path = path.join(self.outdir, 'comments', 'comments.db') ensuredir(path.dirname(db_path)) engine = create_engine('sqlite:///%s' % db_path) - self.comments = SQLAlchemyComments(engine) + self.comments = SQLAlchemyStorage(engine) def _init_templating(self): import sphinx @@ -93,8 +94,7 @@ class WebSupport(object): self.outdir, doctreedir, 'websupport', search=self.search, comments=self.comments) - # TODO: - # Hook comments into Sphinx signals. + self.comments.pre_build() app.build() self.comments.post_build() diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index 395bde580..66b9012af 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -1,12 +1,5 @@ -from datetime import datetime -from sqlalchemy.orm import sessionmaker - -from sphinx.websupport.comments.db import Base, Node, Comment, Vote - -Session = sessionmaker() - -class CommentBackend(object): +class StorageBackend(object): def pre_build(self): pass @@ -22,117 +15,3 @@ class CommentBackend(object): def get_comments(self, parent_id): raise NotImplemented - - -class SQLAlchemyComments(CommentBackend): - def __init__(self, engine): - self.engine = engine - Base.metadata.bind = engine - Base.metadata.create_all() - Session.configure(bind=engine) - self.session = Session() - - def pre_build(self): - self.current_pk = None - - def add_node(self, document, line, source, treeloc): - node = Node(document, line, source, treeloc) - self.session.add(node) - if self.current_pk is None: - self.session.commit() - self.current_pk = node.id - else: - self.current_pk += 1 - return self.current_pk - - def post_build(self): - self.session.commit() - - def add_comment(self, parent_id, text, displayed, - username, rating, time): - time = time or datetime.now() - - id = parent_id[1:] - if parent_id[0] == 's': - node = self.session.query(Node).filter(Node.id == id).first() - comment = Comment(text, displayed, username, rating, - time, node=node) - elif parent_id[0] == 'c': - parent = self.session.query(Comment).filter(Comment.id == id).first() - comment = Comment(text, displayed, username, rating, - time, parent=parent) - - self.session.add(comment) - self.session.commit() - return self.serializable(comment) - - def get_comments(self, parent_id, user_id): - parent_id = parent_id[1:] - node = self.session.query(Node).filter(Node.id == parent_id).first() - comments = [] - for comment in node.comments: - comments.append(self.serializable(comment, user_id)) - - return comments - - def process_vote(self, comment_id, user_id, value): - vote = self.session.query(Vote).filter( - Vote.comment_id == comment_id).filter( - Vote.user_id == user_id).first() - - comment = self.session.query(Comment).filter( - Comment.id == comment_id).first() - - if vote is None: - vote = Vote(comment_id, user_id, value) - comment.rating += value - else: - comment.rating += value - vote.value - vote.value = value - self.session.add(vote) - self.session.commit() - - def serializable(self, comment, user_id=None): - delta = datetime.now() - comment.time - - time = {'year': comment.time.year, - 'month': comment.time.month, - 'day': comment.time.day, - 'hour': comment.time.hour, - 'minute': comment.time.minute, - 'second': comment.time.second, - 'iso': comment.time.isoformat(), - 'delta': self.pretty_delta(delta)} - - vote = '' - if user_id is not None: - vote = self.session.query(Vote).filter( - Vote.comment_id == comment.id).filter( - Vote.user_id == user_id).first() - if vote is not None: - vote = vote.value - - return {'text': comment.text, - 'username': comment.username or 'Anonymous', - 'id': comment.id, - 'rating': comment.rating, - 'age': delta.seconds, - 'time': time, - 'vote': vote or 0, - 'node': comment.node.id if comment.node else None, - 'parent': comment.parent.id if comment.parent else None, - 'children': [self.serializable(child, user_id) - for child in comment.children]} - - def pretty_delta(self, delta): - days = delta.days - seconds = delta.seconds - hours = seconds / 3600 - minutes = seconds / 60 - - if days == 0: - dt = (minutes, 'minute') if hours == 0 else (hours, 'hour') - else: - dt = (days, 'day') - - return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py new file mode 100644 index 000000000..31403c0ab --- /dev/null +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -0,0 +1,125 @@ +from datetime import datetime + +from sqlalchemy.orm import sessionmaker + +from sphinx.websupport.comments import StorageBackend +from sphinx.websupport.comments.db import Base, Node, Comment, Vote + +Session = sessionmaker() + +class SQLAlchemyStorage(StorageBackend): + def __init__(self, engine): + self.engine = engine + Base.metadata.bind = engine + Base.metadata.create_all() + Session.configure(bind=engine) + + def pre_build(self): + self.build_session = Session() + + def add_node(self, document, line, source, treeloc): + node = Node(document, line, source, treeloc) + self.build_session.add(node) + self.build_session.flush() + return node.id + + def post_build(self): + self.build_session.commit() + self.build_session.close() + + def add_comment(self, parent_id, text, displayed, + username, rating, time): + time = time or datetime.now() + + session = Session() + + id = parent_id[1:] + if parent_id[0] == 's': + node = session.query(Node).filter(Node.id == id).first() + comment = Comment(text, displayed, username, rating, + time, node=node) + elif parent_id[0] == 'c': + parent = session.query(Comment).filter(Comment.id == id).first() + comment = Comment(text, displayed, username, rating, + time, parent=parent) + + session.add(comment) + session.commit() + comment = self.serializable(session, comment) + session.close() + return comment + + def get_comments(self, parent_id, user_id): + parent_id = parent_id[1:] + session = Session() + node = session.query(Node).filter(Node.id == parent_id).first() + comments = [] + for comment in node.comments: + comments.append(self.serializable(session, comment, user_id)) + + session.close() + return comments + + def process_vote(self, comment_id, user_id, value): + session = Session() + vote = session.query(Vote).filter( + Vote.comment_id == comment_id).filter( + Vote.user_id == user_id).first() + + comment = session.query(Comment).filter( + Comment.id == comment_id).first() + + if vote is None: + vote = Vote(comment_id, user_id, value) + comment.rating += value + else: + comment.rating += value - vote.value + vote.value = value + session.add(vote) + session.commit() + session.close() + + def serializable(self, session, comment, user_id=None): + delta = datetime.now() - comment.time + + time = {'year': comment.time.year, + 'month': comment.time.month, + 'day': comment.time.day, + 'hour': comment.time.hour, + 'minute': comment.time.minute, + 'second': comment.time.second, + 'iso': comment.time.isoformat(), + 'delta': self.pretty_delta(delta)} + + vote = '' + if user_id is not None: + vote = session.query(Vote).filter( + Vote.comment_id == comment.id).filter( + Vote.user_id == user_id).first() + if vote is not None: + vote = vote.value + + return {'text': comment.text, + 'username': comment.username or 'Anonymous', + 'id': comment.id, + 'rating': comment.rating, + 'age': delta.seconds, + 'time': time, + 'vote': vote or 0, + 'node': comment.node.id if comment.node else None, + 'parent': comment.parent.id if comment.parent else None, + 'children': [self.serializable(session, child, user_id) + for child in comment.children]} + + def pretty_delta(self, delta): + days = delta.days + seconds = delta.seconds + hours = seconds / 3600 + minutes = seconds / 60 + + if days == 0: + dt = (minutes, 'minute') if hours == 0 else (hours, 'hour') + else: + dt = (days, 'day') + + return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt From dc4000f4af30a37b2e17f115a1a2e1fb079c70ba Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 16 Jul 2010 13:31:16 -0500 Subject: [PATCH 199/744] More docs --- doc/web/storagebackends.rst | 50 ++++--------------- sphinx/websupport/comments/__init__.py | 18 +++++++ .../websupport/comments/sqlalchemystorage.py | 2 + 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst index 83e928baa..87e1b478a 100644 --- a/doc/web/storagebackends.rst +++ b/doc/web/storagebackends.rst @@ -1,47 +1,19 @@ -.. _searchadapters: +.. _storagebackends: -.. currentmodule:: sphinx.websupport.search +.. currentmodule:: sphinx.websupport.comments -Search Adapters -=============== +Storage Backends +================ -To create a custom search adapter you will need to subclass the -:class:`~BaseSearch` class. Then create an instance of the new class -and pass that as the `search` keyword argument when you create the -:class:`~sphinx.websupport.WebSupport` object:: +StorageBackend Methods +~~~~~~~~~~~~~~~~~~~~~~ - support = Websupport(srcdir=srcdir, - outdir=outdir, - search=MySearch()) +.. automethod:: sphinx.websupport.comments.StorageBackend.pre_build -For more information about creating a custom search adapter, please see -the documentation of the :class:`BaseSearch` class below. +.. automethod:: sphinx.websupport.comments.StorageBackend.add_node -.. class:: BaseSearch +.. automethod:: sphinx.websupport.comments.StorageBackend.post_build - Defines an interface for search adapters. - -BaseSearch Methods -~~~~~~~~~~~~~~~~~~ - - The following methods are defined in the BaseSearch class. Some methods - do not need to be overridden, but some ( - :meth:`~sphinx.websupport.search.BaseSearch.add_document` and - :meth:`~sphinx.websupport.search.BaseSearch.handle_query`) must be - overridden in your subclass. For a working example, look at the - built-in adapter for whoosh. - -.. automethod:: sphinx.websupport.search.BaseSearch.init_indexing - -.. automethod:: sphinx.websupport.search.BaseSearch.finish_indexing - -.. automethod:: sphinx.websupport.search.BaseSearch.feed - -.. automethod:: sphinx.websupport.search.BaseSearch.add_document - -.. automethod:: sphinx.websupport.search.BaseSearch.query - -.. automethod:: sphinx.websupport.search.BaseSearch.handle_query - -.. automethod:: sphinx.websupport.search.BaseSearch.extract_context +.. automethod:: sphinx.websupport.comments.StorageBackend.add_comment +.. automethod:: sphinx.websupport.comments.StorageBackend.get_comments diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index 66b9012af..20d923860 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -1,17 +1,35 @@ class StorageBackend(object): def pre_build(self): + """Called immediately before the build process begins. Use this + to prepare the StorageBackend for the addition of nodes. + """ pass def add_node(self, document, line, source, treeloc): + """Add a node to the StorageBackend. + + `document` is the name of the document the node belongs to. + + `line` is the line in the source where the node begins. + + `source` is the source files name. + + `treeloc` is for future use. + """ raise NotImplemented def post_build(self): + """Called after a build has completed. Use this to finalize the + addition of nodes if needed. + """ pass def add_comment(self, parent_id, text, displayed, username, rating, time): + """Called when a comment is being added.""" raise NotImplemented def get_comments(self, parent_id): + """Called to retrieve all comments for a node.""" raise NotImplemented diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 31403c0ab..3d4672bf0 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -33,6 +33,8 @@ class SQLAlchemyStorage(StorageBackend): session = Session() + + id = parent_id[1:] if parent_id[0] == 's': node = session.query(Node).filter(Node.id == id).first() From decffe66111e92afcc60a49e4014ffc8980d4300 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 19 Jul 2010 11:37:17 -0500 Subject: [PATCH 200/744] remove whitespace --- sphinx/websupport/comments/sqlalchemystorage.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 3d4672bf0..a14ef476f 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -32,8 +32,6 @@ class SQLAlchemyStorage(StorageBackend): time = time or datetime.now() session = Session() - - id = parent_id[1:] if parent_id[0] == 's': From 90eb73e7b7c087de3c727ba71bdd2de7a58250de Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 19 Jul 2010 14:12:15 -0500 Subject: [PATCH 201/744] started proposal backend --- sphinx/websupport/__init__.py | 36 ++++++++++------- sphinx/websupport/comments/__init__.py | 16 ++++++-- sphinx/websupport/comments/db.py | 23 ++++++++++- .../websupport/comments/sqlalchemystorage.py | 39 +++++++++++++++---- sphinx/writers/websupport.py | 10 ++--- 5 files changed, 93 insertions(+), 31 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index c60c93505..7c39681ad 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -23,7 +23,7 @@ from sphinx.websupport.comments import StorageBackend class WebSupportApp(Sphinx): def __init__(self, *args, **kwargs): self.search = kwargs.pop('search', None) - self.comments = kwargs.pop('comments', None) + self.storage = kwargs.pop('storage', None) Sphinx.__init__(self, *args, **kwargs) class WebSupport(object): @@ -32,7 +32,7 @@ class WebSupport(object): """ def __init__(self, srcdir='', outdir='', datadir='', search=None, - comments=None): + storage=None): self.srcdir = srcdir self.outdir = outdir or path.join(self.srcdir, '_build', 'websupport') @@ -43,21 +43,21 @@ class WebSupport(object): if search is not None: self._init_search(search) - self._init_comments(comments) + self._init_storage(storage) - def _init_comments(self, comments): - if isinstance(comments, StorageBackend): - self.comments = comments + def _init_storage(self, storage): + if isinstance(storage, StorageBackend): + self.storage = storage else: # If a StorageBackend isn't provided, use the default # SQLAlchemy backend with an SQLite db. from sphinx.websupport.comments.sqlalchemystorage \ import SQLAlchemyStorage from sqlalchemy import create_engine - db_path = path.join(self.outdir, 'comments', 'comments.db') + db_path = path.join(self.outdir, 'db', 'websupport.db') ensuredir(path.dirname(db_path)) engine = create_engine('sqlite:///%s' % db_path) - self.comments = SQLAlchemyStorage(engine) + self.storage = SQLAlchemyStorage(engine) def _init_templating(self): import sphinx @@ -93,11 +93,11 @@ class WebSupport(object): app = WebSupportApp(self.srcdir, self.srcdir, self.outdir, doctreedir, 'websupport', search=self.search, - comments=self.comments) + storage=self.storage) - self.comments.pre_build() + self.storage.pre_build() app.build() - self.comments.post_build() + self.storage.post_build() def get_document(self, docname): """Load and return a document from a pickle. The document will @@ -178,7 +178,7 @@ class WebSupport(object): :param node_id: the id of the node to get comments for. :param user_id: the id of the user viewing the comments. """ - return self.comments.get_comments(node_id, user_id) + return self.storage.get_comments(node_id, user_id) def add_comment(self, parent_id, text, displayed=True, username=None, rating=0, time=None): @@ -202,9 +202,17 @@ class WebSupport(object): :param rating: the starting rating of the comment, defaults to 0. :param time: the time the comment was created, defaults to now. """ - return self.comments.add_comment(parent_id, text, displayed, + return self.storage.add_comment(parent_id, text, displayed, username, rating, time) + def get_proposals(self, node_id, user_id=None): + return self.storage.get_proposals(node_id, user_id) + + def add_proposal(self, parent_id, text, displayed=True, username=None, + rating=0, time=None): + return self.storage.add_proposal(parent_id, text, displayed, + username, rating, time) + def process_vote(self, comment_id, user_id, value): """Process a user's vote. The web support package relies on the API user to perform authentication. The API user will @@ -230,4 +238,4 @@ class WebSupport(object): :param value: 1 for an upvote, -1 for a downvote, 0 for an unvote. """ value = int(value) - self.comments.process_vote(comment_id, user_id, value) + self.storage.process_vote(comment_id, user_id, value) diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index 20d923860..a78a19c16 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -17,7 +17,7 @@ class StorageBackend(object): `treeloc` is for future use. """ - raise NotImplemented + raise NotImplementedError() def post_build(self): """Called after a build has completed. Use this to finalize the @@ -28,8 +28,18 @@ class StorageBackend(object): def add_comment(self, parent_id, text, displayed, username, rating, time): """Called when a comment is being added.""" - raise NotImplemented + raise NotImplementedError() def get_comments(self, parent_id): """Called to retrieve all comments for a node.""" - raise NotImplemented + raise NotImplementedError() + + def add_proposal(self, parent_id, text, displayed, username, + rating, time): + raise NotImplementedError() + + def get_proposals(self, parent_id): + raise NotImplementedError() + + def process_vote(self, comment_id, user_id, value): + raise NotImplementedError() diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 22020b4b5..1b3cd9d8b 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -50,7 +50,7 @@ class Comment(Base): self.node = node self.parent = parent -class Vote(Base): +class CommentVote(Base): __tablename__ = db_prefix + 'vote' id = Column(Integer, primary_key=True) @@ -68,4 +68,25 @@ class Vote(Base): self.value = value self.user_id = user_id self.comment_id = comment_id + +class Proposal(Base): + __tablename__ = db_prefix + 'proposals' + + id = Column(Integer, primary_key=True) + rating = Column(Integer, nullable=False) + time = Column(DateTime, nullable=False) + text = Column(Text, nullable=False) + displayed = Column(Boolean, index=True, default=False) + username = Column(String(64)) + + node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) + node = relation(Node, backref='proposals') + + def __init__(self, text, displayed, username, rating, time, node): + self.text = text + self.displayed = displayed + self.username = username + self.rating = rating + self.time = time + self.node = node diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index a14ef476f..26f352ac5 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -3,7 +3,7 @@ from datetime import datetime from sqlalchemy.orm import sessionmaker from sphinx.websupport.comments import StorageBackend -from sphinx.websupport.comments.db import Base, Node, Comment, Vote +from sphinx.websupport.comments.db import Base, Node, Comment, CommentVote Session = sessionmaker() @@ -60,17 +60,40 @@ class SQLAlchemyStorage(StorageBackend): session.close() return comments + def add_proposal(self, parent_id, text, displayed, username, + rating, time): + time = time or datetime.now() + + session = Session() + + node = session.query(Node).filter(Node.id == parent_id).first() + proposal= Proposal(text, displayed, username, rating, time, node) + + session.add(proposal) + session.commit() + session.close() + return proposal + + def get_proposals(self, parent_id): + session = Session() + node = session.query(Node).filter(Node.id == parent_id).first() + proposals = [] + + # TODO + + return proposals + def process_vote(self, comment_id, user_id, value): session = Session() - vote = session.query(Vote).filter( - Vote.comment_id == comment_id).filter( - Vote.user_id == user_id).first() + vote = session.query(CommentVote).filter( + CommentVote.comment_id == comment_id).filter( + CommentVote.user_id == user_id).first() comment = session.query(Comment).filter( Comment.id == comment_id).first() if vote is None: - vote = Vote(comment_id, user_id, value) + vote = CommentVote(comment_id, user_id, value) comment.rating += value else: comment.rating += value - vote.value @@ -93,9 +116,9 @@ class SQLAlchemyStorage(StorageBackend): vote = '' if user_id is not None: - vote = session.query(Vote).filter( - Vote.comment_id == comment.id).filter( - Vote.user_id == user_id).first() + vote = session.query(CommentVote).filter( + CommentVote.comment_id == comment.id).filter( + CommentVote.user_id == user_id).first() if vote is not None: vote = vote.value diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 18c0807d6..04c989b22 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -57,9 +57,9 @@ class WebSupportTranslator(HTMLTranslator): self.in_commentable = False def add_db_node(self, node): - comments = self.builder.app.comments - db_node_id = comments.add_node(document=self.builder.cur_docname, - line=node.line, - source=node.rawsource, - treeloc='???') + storage = self.builder.app.storage + db_node_id = storage.add_node(document=self.builder.cur_docname, + line=node.line, + source=node.rawsource, + treeloc='???') return db_node_id From 061db6cb22e1a7f24281c17bb77a8bd69e7d3f73 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 19 Jul 2010 14:53:37 -0500 Subject: [PATCH 202/744] moved serializable() to db.Comment --- sphinx/websupport/comments/db.py | 54 ++++++++++++++++++- .../websupport/comments/sqlalchemystorage.py | 54 ++----------------- 2 files changed, 55 insertions(+), 53 deletions(-) diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 1b3cd9d8b..9d0fc2a2f 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -1,11 +1,15 @@ +from datetime import datetime + from sqlalchemy import Column, Integer, Text, String, Boolean, ForeignKey,\ DateTime from sqlalchemy.schema import UniqueConstraint from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relation +from sqlalchemy.orm import relation, sessionmaker Base = declarative_base() +Session = sessionmaker() + db_prefix = 'sphinx_' class Node(Base): @@ -50,8 +54,54 @@ class Comment(Base): self.node = node self.parent = parent + def serializable(self, user_id=None): + delta = datetime.now() - self.time + + time = {'year': self.time.year, + 'month': self.time.month, + 'day': self.time.day, + 'hour': self.time.hour, + 'minute': self.time.minute, + 'second': self.time.second, + 'iso': self.time.isoformat(), + 'delta': self.pretty_delta(delta)} + + vote = '' + if user_id is not None: + session = Session() + vote = session.query(CommentVote).filter( + CommentVote.comment_id == self.id).filter( + CommentVote.user_id == user_id).first() + vote = vote.value if vote is not None else 0 + session.close() + + return {'text': self.text, + 'username': self.username or 'Anonymous', + 'id': self.id, + 'rating': self.rating, + 'age': delta.seconds, + 'time': time, + 'vote': vote or 0, + 'node': self.node.id if self.node else None, + 'parent': self.parent.id if self.parent else None, + 'children': [child.serializable(user_id) + for child in self.children]} + + def pretty_delta(self, delta): + days = delta.days + seconds = delta.seconds + hours = seconds / 3600 + minutes = seconds / 60 + + if days == 0: + dt = (minutes, 'minute') if hours == 0 else (hours, 'hour') + else: + dt = (days, 'day') + + return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt + class CommentVote(Base): - __tablename__ = db_prefix + 'vote' + __tablename__ = db_prefix + 'commentvote' id = Column(Integer, primary_key=True) # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted. diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 26f352ac5..10f1fa3cc 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -1,11 +1,7 @@ from datetime import datetime -from sqlalchemy.orm import sessionmaker - from sphinx.websupport.comments import StorageBackend -from sphinx.websupport.comments.db import Base, Node, Comment, CommentVote - -Session = sessionmaker() +from sphinx.websupport.comments.db import Base, Node, Comment, CommentVote, Session class SQLAlchemyStorage(StorageBackend): def __init__(self, engine): @@ -45,7 +41,7 @@ class SQLAlchemyStorage(StorageBackend): session.add(comment) session.commit() - comment = self.serializable(session, comment) + comment = comment.serializable() session.close() return comment @@ -55,7 +51,7 @@ class SQLAlchemyStorage(StorageBackend): node = session.query(Node).filter(Node.id == parent_id).first() comments = [] for comment in node.comments: - comments.append(self.serializable(session, comment, user_id)) + comments.append(comment.serializable(user_id)) session.close() return comments @@ -102,47 +98,3 @@ class SQLAlchemyStorage(StorageBackend): session.commit() session.close() - def serializable(self, session, comment, user_id=None): - delta = datetime.now() - comment.time - - time = {'year': comment.time.year, - 'month': comment.time.month, - 'day': comment.time.day, - 'hour': comment.time.hour, - 'minute': comment.time.minute, - 'second': comment.time.second, - 'iso': comment.time.isoformat(), - 'delta': self.pretty_delta(delta)} - - vote = '' - if user_id is not None: - vote = session.query(CommentVote).filter( - CommentVote.comment_id == comment.id).filter( - CommentVote.user_id == user_id).first() - if vote is not None: - vote = vote.value - - return {'text': comment.text, - 'username': comment.username or 'Anonymous', - 'id': comment.id, - 'rating': comment.rating, - 'age': delta.seconds, - 'time': time, - 'vote': vote or 0, - 'node': comment.node.id if comment.node else None, - 'parent': comment.parent.id if comment.parent else None, - 'children': [self.serializable(session, child, user_id) - for child in comment.children]} - - def pretty_delta(self, delta): - days = delta.days - seconds = delta.seconds - hours = seconds / 3600 - minutes = seconds / 60 - - if days == 0: - dt = (minutes, 'minute') if hours == 0 else (hours, 'hour') - else: - dt = (days, 'day') - - return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt From 8c188370f293bc248c1d1f60417c8538479e53d9 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 19 Jul 2010 14:58:06 -0500 Subject: [PATCH 203/744] use composite pk for db.CommentVote --- sphinx/websupport/comments/db.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 9d0fc2a2f..4cf8878f2 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -103,13 +103,12 @@ class Comment(Base): class CommentVote(Base): __tablename__ = db_prefix + 'commentvote' - id = Column(Integer, primary_key=True) + user_id = Column(Integer, primary_key=True) # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted. value = Column(Integer, nullable=False) - user_id = Column(Integer, index=True, nullable=False) comment_id = Column(Integer, ForeignKey(db_prefix + 'comments.id'), - nullable=False) + primary_key=True) comment = relation(Comment, backref="votes") __table_args__ = (UniqueConstraint(comment_id, user_id), {}) From 19042aaf94a2b08deee436455fcc7b1a78d15516 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 19 Jul 2010 15:14:02 -0500 Subject: [PATCH 204/744] add ProposalVote table to db --- sphinx/websupport/comments/db.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 4cf8878f2..31f4ff774 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -111,8 +111,6 @@ class CommentVote(Base): primary_key=True) comment = relation(Comment, backref="votes") - __table_args__ = (UniqueConstraint(comment_id, user_id), {}) - def __init__(self, comment_id, user_id, value): self.value = value self.user_id = user_id @@ -139,3 +137,19 @@ class Proposal(Base): self.time = time self.node = node +class ProposalVote(Base): + __tablename__ = db_prefix + 'proposalvote' + + user_id = Column(Integer, primary_key=True) + # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted. + value = Column(Integer, nullable=False) + + proposal_id = Column(Integer, ForeignKey(db_prefix + 'proposals.id'), + primary_key=True) + proposal = relation(Proposal, backref="votes") + + def __init__(self, proposal_id, user_id, value): + self.value = value + self.user_id = user_id + self.proposal_id = proposal_id + From a2b3fd4bbe3ed391516021bdd78bee9602aff5ae Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Tue, 20 Jul 2010 10:03:20 +0100 Subject: [PATCH 205/744] Fix undefined locals. --- sphinx/websupport/comments/db.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 31f4ff774..9ffe2fd81 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -12,16 +12,17 @@ Session = sessionmaker() db_prefix = 'sphinx_' + class Node(Base): """Data about a Node in a doctree.""" __tablename__ = db_prefix + 'nodes' - + id = Column(Integer, primary_key=True) document = Column(String(256), nullable=False) line = Column(Integer) source = Column(Text, nullable=False) treeloc = Column(String(32), nullable=False) - + def __init__(self, document, line, source, treeloc): self.document = document self.line = line @@ -44,7 +45,7 @@ class Comment(Base): parent_id = Column(Integer, ForeignKey(db_prefix + 'comments.id')) parent = relation('Comment', backref='children', remote_side=[id]) - def __init__(self, text, displayed, username, rating, time, + def __init__(self, text, displayed, username, rating, time, node=None, parent=None): self.text = text self.displayed = displayed @@ -84,7 +85,7 @@ class Comment(Base): 'vote': vote or 0, 'node': self.node.id if self.node else None, 'parent': self.parent.id if self.parent else None, - 'children': [child.serializable(user_id) + 'children': [child.serializable(user_id) for child in self.children]} def pretty_delta(self, delta): @@ -99,7 +100,7 @@ class Comment(Base): dt = (days, 'day') return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt - + class CommentVote(Base): __tablename__ = db_prefix + 'commentvote' @@ -136,7 +137,7 @@ class Proposal(Base): self.rating = rating self.time = time self.node = node - + class ProposalVote(Base): __tablename__ = db_prefix + 'proposalvote' From 6faa8885fb0ca40b8b32dc0ac93c7eb0c60008ca Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Tue, 20 Jul 2010 10:03:34 +0100 Subject: [PATCH 206/744] Style nits, trailing whitespace. --- sphinx/websupport/comments/__init__.py | 10 +++++----- sphinx/websupport/search/whooshsearch.py | 4 ++-- sphinx/websupport/search/xapiansearch.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index a78a19c16..47e7440c7 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -10,22 +10,22 @@ class StorageBackend(object): """Add a node to the StorageBackend. `document` is the name of the document the node belongs to. - + `line` is the line in the source where the node begins. `source` is the source files name. - + `treeloc` is for future use. """ raise NotImplementedError() - + def post_build(self): """Called after a build has completed. Use this to finalize the addition of nodes if needed. """ pass - def add_comment(self, parent_id, text, displayed, username, + def add_comment(self, parent_id, text, displayed, username, rating, time): """Called when a comment is being added.""" raise NotImplementedError() @@ -34,7 +34,7 @@ class StorageBackend(object): """Called to retrieve all comments for a node.""" raise NotImplementedError() - def add_proposal(self, parent_id, text, displayed, username, + def add_proposal(self, parent_id, text, displayed, username, rating, time): raise NotImplementedError() diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 52f49d8d9..658b764dd 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -40,10 +40,10 @@ class WhooshSearch(BaseSearch): def finish_indexing(self): self.index_writer.commit() - + def add_document(self, pagename, title, text): self.index_writer.add_document(path=unicode(pagename), - title=title, + title=title, text=text) def handle_query(self, q): diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py index f5ad9688a..16c7e2b1b 100644 --- a/sphinx/websupport/search/xapiansearch.py +++ b/sphinx/websupport/search/xapiansearch.py @@ -28,7 +28,7 @@ class XapianSearch(BaseSearch): def init_indexing(self, changed=[]): ensuredir(self.db_path) - self.database = xapian.WritableDatabase(self.db_path, + self.database = xapian.WritableDatabase(self.db_path, xapian.DB_CREATE_OR_OPEN) self.indexer = xapian.TermGenerator() stemmer = xapian.Stem("english") @@ -37,7 +37,7 @@ class XapianSearch(BaseSearch): def finish_indexing(self): # Ensure the db lock is removed. del self.database - + def add_document(self, path, title, text): self.database.begin_transaction() # sphinx_page_path is used to easily retrieve documents by path. From 3ba3640ea3d7d03d09ee828e4fdb0b6e14674493 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Tue, 20 Jul 2010 10:13:43 +0100 Subject: [PATCH 207/744] Use try-finally for open file. --- sphinx/themes/basic/searchresults.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/themes/basic/searchresults.html b/sphinx/themes/basic/searchresults.html index e7fc84f8f..4b5da1a39 100644 --- a/sphinx/themes/basic/searchresults.html +++ b/sphinx/themes/basic/searchresults.html @@ -20,7 +20,7 @@ {% if search_performed %} <h2>Search Results</h2> {% if not search_results %} -<p>'Your search did not match any results.</p> +<p>Your search did not match any results.</p> {% endif %} {% endif %} <div id="search-results"> From 022b229c36a1f46a5eabc9a6cb6465c8e490e88f Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 22 Jul 2010 16:42:29 -0500 Subject: [PATCH 208/744] Unification of Comments and Proposals. --- sphinx/websupport/comments/db.py | 42 +++----------------------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 31f4ff774..3a33cb266 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -37,6 +37,7 @@ class Comment(Base): text = Column(Text, nullable=False) displayed = Column(Boolean, index=True, default=False) username = Column(String(64)) + proposal = Column(Text) node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) node = relation(Node, backref='comments') @@ -45,7 +46,7 @@ class Comment(Base): parent = relation('Comment', backref='children', remote_side=[id]) def __init__(self, text, displayed, username, rating, time, - node=None, parent=None): + node=None, parent=None, proposal=None): self.text = text self.displayed = displayed self.username = username @@ -53,6 +54,7 @@ class Comment(Base): self.time = time self.node = node self.parent = parent + self.proposal = proposal def serializable(self, user_id=None): delta = datetime.now() - self.time @@ -115,41 +117,3 @@ class CommentVote(Base): self.value = value self.user_id = user_id self.comment_id = comment_id - -class Proposal(Base): - __tablename__ = db_prefix + 'proposals' - - id = Column(Integer, primary_key=True) - rating = Column(Integer, nullable=False) - time = Column(DateTime, nullable=False) - text = Column(Text, nullable=False) - displayed = Column(Boolean, index=True, default=False) - username = Column(String(64)) - - node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) - node = relation(Node, backref='proposals') - - def __init__(self, text, displayed, username, rating, time, node): - self.text = text - self.displayed = displayed - self.username = username - self.rating = rating - self.time = time - self.node = node - -class ProposalVote(Base): - __tablename__ = db_prefix + 'proposalvote' - - user_id = Column(Integer, primary_key=True) - # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted. - value = Column(Integer, nullable=False) - - proposal_id = Column(Integer, ForeignKey(db_prefix + 'proposals.id'), - primary_key=True) - proposal = relation(Proposal, backref="votes") - - def __init__(self, proposal_id, user_id, value): - self.value = value - self.user_id = user_id - self.proposal_id = proposal_id - From d7183639086ceb5511a2446e93535de501e5e370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 25 Jul 2010 19:50:20 +0200 Subject: [PATCH 209/744] Remove trailing whitespace --- sphinx/websupport/__init__.py | 12 ++++----- sphinx/websupport/comments/__init__.py | 6 ++--- sphinx/websupport/comments/db.py | 12 ++++----- .../websupport/comments/sqlalchemystorage.py | 25 +++++++++---------- sphinx/websupport/search/whooshsearch.py | 4 +-- sphinx/websupport/search/xapiansearch.py | 2 +- 6 files changed, 30 insertions(+), 31 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 7c39681ad..ddaac1f3b 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -44,7 +44,7 @@ class WebSupport(object): self._init_search(search) self._init_storage(storage) - + def _init_storage(self, storage): if isinstance(storage, StorageBackend): self.storage = storage @@ -58,7 +58,7 @@ class WebSupport(object): ensuredir(path.dirname(db_path)) engine = create_engine('sqlite:///%s' % db_path) self.storage = SQLAlchemyStorage(engine) - + def _init_templating(self): import sphinx template_path = path.join(path.dirname(sphinx.__file__), @@ -71,7 +71,7 @@ class WebSupport(object): self.search = search else: mod, cls = search_adapters[search] - search_class = getattr(__import__('sphinx.websupport.search.' + mod, + search_class = getattr(__import__('sphinx.websupport.search.' + mod, None, None, [cls]), cls) search_path = path.join(self.outdir, 'search') self.search = search_class(search_path) @@ -202,15 +202,15 @@ class WebSupport(object): :param rating: the starting rating of the comment, defaults to 0. :param time: the time the comment was created, defaults to now. """ - return self.storage.add_comment(parent_id, text, displayed, + return self.storage.add_comment(parent_id, text, displayed, username, rating, time) - + def get_proposals(self, node_id, user_id=None): return self.storage.get_proposals(node_id, user_id) def add_proposal(self, parent_id, text, displayed=True, username=None, rating=0, time=None): - return self.storage.add_proposal(parent_id, text, displayed, + return self.storage.add_proposal(parent_id, text, displayed, username, rating, time) def process_vote(self, comment_id, user_id, value): diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index a78a19c16..aca2ac351 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -18,14 +18,14 @@ class StorageBackend(object): `treeloc` is for future use. """ raise NotImplementedError() - + def post_build(self): """Called after a build has completed. Use this to finalize the addition of nodes if needed. """ pass - def add_comment(self, parent_id, text, displayed, username, + def add_comment(self, parent_id, text, displayed, username, rating, time): """Called when a comment is being added.""" raise NotImplementedError() @@ -34,7 +34,7 @@ class StorageBackend(object): """Called to retrieve all comments for a node.""" raise NotImplementedError() - def add_proposal(self, parent_id, text, displayed, username, + def add_proposal(self, parent_id, text, displayed, username, rating, time): raise NotImplementedError() diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 31f4ff774..35f25a2bb 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -15,13 +15,13 @@ db_prefix = 'sphinx_' class Node(Base): """Data about a Node in a doctree.""" __tablename__ = db_prefix + 'nodes' - + id = Column(Integer, primary_key=True) document = Column(String(256), nullable=False) line = Column(Integer) source = Column(Text, nullable=False) treeloc = Column(String(32), nullable=False) - + def __init__(self, document, line, source, treeloc): self.document = document self.line = line @@ -44,7 +44,7 @@ class Comment(Base): parent_id = Column(Integer, ForeignKey(db_prefix + 'comments.id')) parent = relation('Comment', backref='children', remote_side=[id]) - def __init__(self, text, displayed, username, rating, time, + def __init__(self, text, displayed, username, rating, time, node=None, parent=None): self.text = text self.displayed = displayed @@ -84,7 +84,7 @@ class Comment(Base): 'vote': vote or 0, 'node': self.node.id if self.node else None, 'parent': self.parent.id if self.parent else None, - 'children': [child.serializable(user_id) + 'children': [child.serializable(user_id) for child in self.children]} def pretty_delta(self, delta): @@ -99,7 +99,7 @@ class Comment(Base): dt = (days, 'day') return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt - + class CommentVote(Base): __tablename__ = db_prefix + 'commentvote' @@ -136,7 +136,7 @@ class Proposal(Base): self.rating = rating self.time = time self.node = node - + class ProposalVote(Base): __tablename__ = db_prefix + 'proposalvote' diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 10f1fa3cc..c57bee636 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -23,28 +23,28 @@ class SQLAlchemyStorage(StorageBackend): self.build_session.commit() self.build_session.close() - def add_comment(self, parent_id, text, displayed, + def add_comment(self, parent_id, text, displayed, username, rating, time): time = time or datetime.now() - + session = Session() - + id = parent_id[1:] if parent_id[0] == 's': node = session.query(Node).filter(Node.id == id).first() - comment = Comment(text, displayed, username, rating, + comment = Comment(text, displayed, username, rating, time, node=node) elif parent_id[0] == 'c': parent = session.query(Comment).filter(Comment.id == id).first() - comment = Comment(text, displayed, username, rating, + comment = Comment(text, displayed, username, rating, time, parent=parent) - + session.add(comment) session.commit() comment = comment.serializable() session.close() return comment - + def get_comments(self, parent_id, user_id): parent_id = parent_id[1:] session = Session() @@ -56,15 +56,15 @@ class SQLAlchemyStorage(StorageBackend): session.close() return comments - def add_proposal(self, parent_id, text, displayed, username, + def add_proposal(self, parent_id, text, displayed, username, rating, time): time = time or datetime.now() - + session = Session() - + node = session.query(Node).filter(Node.id == parent_id).first() proposal= Proposal(text, displayed, username, rating, time, node) - + session.add(proposal) session.commit() session.close() @@ -84,7 +84,7 @@ class SQLAlchemyStorage(StorageBackend): vote = session.query(CommentVote).filter( CommentVote.comment_id == comment_id).filter( CommentVote.user_id == user_id).first() - + comment = session.query(Comment).filter( Comment.id == comment_id).first() @@ -97,4 +97,3 @@ class SQLAlchemyStorage(StorageBackend): session.add(vote) session.commit() session.close() - diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 52f49d8d9..658b764dd 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -40,10 +40,10 @@ class WhooshSearch(BaseSearch): def finish_indexing(self): self.index_writer.commit() - + def add_document(self, pagename, title, text): self.index_writer.add_document(path=unicode(pagename), - title=title, + title=title, text=text) def handle_query(self, q): diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py index f5ad9688a..2f2ffbe59 100644 --- a/sphinx/websupport/search/xapiansearch.py +++ b/sphinx/websupport/search/xapiansearch.py @@ -37,7 +37,7 @@ class XapianSearch(BaseSearch): def finish_indexing(self): # Ensure the db lock is removed. del self.database - + def add_document(self, path, title, text): self.database.begin_transaction() # sphinx_page_path is used to easily retrieve documents by path. From 0672a8e379c711a63348af9b57a6a4a92938cb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 25 Jul 2010 19:53:57 +0200 Subject: [PATCH 210/744] Fixed line length and indentation of imports --- sphinx/websupport/comments/db.py | 2 +- sphinx/websupport/comments/sqlalchemystorage.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 35f25a2bb..b73d6a5ae 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -1,7 +1,7 @@ from datetime import datetime from sqlalchemy import Column, Integer, Text, String, Boolean, ForeignKey,\ -DateTime + DateTime from sqlalchemy.schema import UniqueConstraint from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relation, sessionmaker diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index c57bee636..8ee117502 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -1,7 +1,8 @@ from datetime import datetime from sphinx.websupport.comments import StorageBackend -from sphinx.websupport.comments.db import Base, Node, Comment, CommentVote, Session +from sphinx.websupport.comments.db import Base, Node, Comment, CommentVote,\ + Session class SQLAlchemyStorage(StorageBackend): def __init__(self, engine): From 6a4ec66967de0f25e03a73d43e4c57b7295f561c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 25 Jul 2010 20:03:28 +0200 Subject: [PATCH 211/744] Added missing module docstrings --- sphinx/websupport/comments/__init__.py | 10 ++++++++++ sphinx/websupport/comments/db.py | 13 ++++++++++++- sphinx/websupport/comments/sqlalchemystorage.py | 11 +++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index aca2ac351..1f1605184 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -1,3 +1,13 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.comments + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Comments for the websupport package. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" class StorageBackend(object): def pre_build(self): diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index b73d6a5ae..db9ab7a80 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -1,3 +1,15 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.comments.db + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + SQLAlchemy table and mapper definitions used by the + :class:`sphinx.websupport.comments.SQLAlchemyStorage`. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + from datetime import datetime from sqlalchemy import Column, Integer, Text, String, Boolean, ForeignKey,\ @@ -152,4 +164,3 @@ class ProposalVote(Base): self.value = value self.user_id = user_id self.proposal_id = proposal_id - diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 8ee117502..312663c81 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.comments.sqlalchemystorage + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + A SQLAlchemy storage backend. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + from datetime import datetime from sphinx.websupport.comments import StorageBackend From 1a9a9972911695ae4f64e7c6fccd686f814659bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 25 Jul 2010 20:45:24 +0200 Subject: [PATCH 212/744] Removed trailing whitespace --- sphinx/writers/websupport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 04c989b22..63281f182 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -25,7 +25,7 @@ class WebSupportTranslator(HTMLTranslator): def init_support(self): self.in_commentable = False self.current_id = 0 - + def dispatch_visit(self, node): if node.__class__.__name__ in self.commentable_nodes: self.handle_visit_commentable(node) From 6d4bd91c837b4bc29455b65f26fb518367a76401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 25 Jul 2010 23:03:14 +0200 Subject: [PATCH 213/744] Remove trailing whitespace --- sphinx/builders/websupport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index e2caeccd1..40901eef9 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -25,7 +25,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def init_translator_class(self): self.translator_class = WebSupportTranslator - + def write_doc(self, docname, doctree): # The translator needs the docname to generate ids. self.cur_docname = docname From 3a32d12bf89b6513ef5fc16d5636340a2fadd343 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sun, 25 Jul 2010 22:37:53 -0500 Subject: [PATCH 214/744] Save proposed changes --- sphinx/websupport/__init__.py | 13 ++++--- sphinx/websupport/comments/__init__.py | 11 +----- sphinx/websupport/comments/db.py | 2 +- .../websupport/comments/sqlalchemystorage.py | 38 ++++--------------- 4 files changed, 17 insertions(+), 47 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 7c39681ad..452dc2950 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -146,10 +146,11 @@ class WebSupport(object): return document def get_comments(self, node_id, user_id=None): - """Get the comments associated with `node_id`. If `user_id` is - given vote information will be included with the returned comments. - The default CommentBackend returns a list of dicts. Each dict - represents a comment, and has the following items: + """Get the comments and source associated with `node_id`. If + `user_id` is given vote information will be included with the + returned comments. The default CommentBackend returns dict with + two keys, *source*, and *comments*. *comments* is a list of + dicts that represent a comment, each having the following items: ============ ====================================================== Key Contents @@ -181,7 +182,7 @@ class WebSupport(object): return self.storage.get_comments(node_id, user_id) def add_comment(self, parent_id, text, displayed=True, username=None, - rating=0, time=None): + rating=0, time=None, proposal=None): """Add a comment to a node or another comment. `parent_id` will have a one letter prefix, distinguishing between node parents and comment parents, 'c' and 's' respectively. This function will @@ -203,7 +204,7 @@ class WebSupport(object): :param time: the time the comment was created, defaults to now. """ return self.storage.add_comment(parent_id, text, displayed, - username, rating, time) + username, rating, time, proposal) def get_proposals(self, node_id, user_id=None): return self.storage.get_proposals(node_id, user_id) diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index a78a19c16..8cd3c36c4 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -25,8 +25,8 @@ class StorageBackend(object): """ pass - def add_comment(self, parent_id, text, displayed, username, - rating, time): + def add_comment(self, parent_id, text, displayed, + username, rating, time, proposal): """Called when a comment is being added.""" raise NotImplementedError() @@ -34,12 +34,5 @@ class StorageBackend(object): """Called to retrieve all comments for a node.""" raise NotImplementedError() - def add_proposal(self, parent_id, text, displayed, username, - rating, time): - raise NotImplementedError() - - def get_proposals(self, parent_id): - raise NotImplementedError() - def process_vote(self, comment_id, user_id, value): raise NotImplementedError() diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 3a33cb266..3679e3d6d 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -46,7 +46,7 @@ class Comment(Base): parent = relation('Comment', backref='children', remote_side=[id]) def __init__(self, text, displayed, username, rating, time, - node=None, parent=None, proposal=None): + proposal, node=None, parent=None): self.text = text self.displayed = displayed self.username = username diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 10f1fa3cc..706ea4c03 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -24,7 +24,7 @@ class SQLAlchemyStorage(StorageBackend): self.build_session.close() def add_comment(self, parent_id, text, displayed, - username, rating, time): + username, rating, time, proposal): time = time or datetime.now() session = Session() @@ -33,11 +33,11 @@ class SQLAlchemyStorage(StorageBackend): if parent_id[0] == 's': node = session.query(Node).filter(Node.id == id).first() comment = Comment(text, displayed, username, rating, - time, node=node) + time, proposal, node=node) elif parent_id[0] == 'c': parent = session.query(Comment).filter(Comment.id == id).first() comment = Comment(text, displayed, username, rating, - time, parent=parent) + time,proposal, parent=parent) session.add(comment) session.commit() @@ -49,35 +49,11 @@ class SQLAlchemyStorage(StorageBackend): parent_id = parent_id[1:] session = Session() node = session.query(Node).filter(Node.id == parent_id).first() - comments = [] - for comment in node.comments: - comments.append(comment.serializable(user_id)) - + data = {'source': node.source, + 'comments': [comment.serializable(user_id) + for comment in node.comments]} session.close() - return comments - - def add_proposal(self, parent_id, text, displayed, username, - rating, time): - time = time or datetime.now() - - session = Session() - - node = session.query(Node).filter(Node.id == parent_id).first() - proposal= Proposal(text, displayed, username, rating, time, node) - - session.add(proposal) - session.commit() - session.close() - return proposal - - def get_proposals(self, parent_id): - session = Session() - node = session.query(Node).filter(Node.id == parent_id).first() - proposals = [] - - # TODO - - return proposals + return data def process_vote(self, comment_id, user_id, value): session = Session() From c23e26833a187be135244828633c76180b0caada Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 27 Jul 2010 16:12:35 -0500 Subject: [PATCH 215/744] added ugly proposals --- sphinx/websupport/__init__.py | 74 ++++++++++++++++--- sphinx/websupport/comments/__init__.py | 10 ++- sphinx/websupport/comments/db.py | 5 +- .../websupport/comments/sqlalchemystorage.py | 32 ++++---- 4 files changed, 95 insertions(+), 26 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 452dc2950..3fd9d83b7 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -10,7 +10,10 @@ """ import cPickle as pickle +import re from os import path +from cgi import escape +from difflib import Differ from datetime import datetime from jinja2 import Environment, FileSystemLoader @@ -203,17 +206,16 @@ class WebSupport(object): :param rating: the starting rating of the comment, defaults to 0. :param time: the time the comment was created, defaults to now. """ - return self.storage.add_comment(parent_id, text, displayed, - username, rating, time, proposal) + id = parent_id[1:] + is_node = parent_id[0] == 's' + + node = self.storage.get_node(id) if is_node else None + parent = self.storage.get_comment(id) if not is_node else None + diff = get_diff_html(node.source, proposal) if proposal else None + + return self.storage.add_comment(text, displayed, username, rating, + time, proposal, diff, node, parent) - def get_proposals(self, node_id, user_id=None): - return self.storage.get_proposals(node_id, user_id) - - def add_proposal(self, parent_id, text, displayed=True, username=None, - rating=0, time=None): - return self.storage.add_proposal(parent_id, text, displayed, - username, rating, time) - def process_vote(self, comment_id, user_id, value): """Process a user's vote. The web support package relies on the API user to perform authentication. The API user will @@ -240,3 +242,55 @@ class WebSupport(object): """ value = int(value) self.storage.process_vote(comment_id, user_id, value) + +highlight_regex = re.compile(r'([\+\-\^]+)') + +def highlight_text(text, next, tag): + next = next[2:] + new_text = [] + start = 0 + for match in highlight_regex.finditer(next): + new_text.append(text[start:match.start()]) + new_text.append('<%s>' % tag) + new_text.append(text[match.start():match.end()]) + new_text.append('</%s>' % tag) + start = match.end() + new_text.append(text[start:]) + return ''.join(new_text) + +def get_diff_html(source, proposal): + proposal = escape(proposal) + + def handle_line(line, next=None): + prefix = line[0] + text = line[2:] + + if prefix == ' ': + return text + elif prefix == '?': + return '' + + if next[0] == '?': + tag = 'ins' if prefix == '+' else 'del' + text = highlight_text(text, next, tag) + css_class = 'prop_added' if prefix == '+' else 'prop_removed' + + return '<span class="%s">%s</span>\n' % (css_class, text.rstrip()) + + differ = Differ() + diff = list(differ.compare(source.splitlines(1), proposal.splitlines(1))) + + html = [] + line = diff.pop(0) + next = diff.pop(0) + while True: + html.append(handle_line(line, next)) + line = next + try: + next = diff.pop(0) + except IndexError: + handle_line(line) + break + + return ''.join(html) + diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index 8cd3c36c4..0d9fa1549 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -19,6 +19,9 @@ class StorageBackend(object): """ raise NotImplementedError() + def get_node(self, node_id): + raise NotImplementedError() + def post_build(self): """Called after a build has completed. Use this to finalize the addition of nodes if needed. @@ -26,11 +29,14 @@ class StorageBackend(object): pass def add_comment(self, parent_id, text, displayed, - username, rating, time, proposal): + username, rating, time, proposal, proposal_diff): """Called when a comment is being added.""" raise NotImplementedError() - def get_comments(self, parent_id): + def get_comment(self, comment_id): + raise NotImplementedError() + + def get_comments(self, parent_id, user_id): """Called to retrieve all comments for a node.""" raise NotImplementedError() diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index 3679e3d6d..e55b6af27 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -38,6 +38,7 @@ class Comment(Base): displayed = Column(Boolean, index=True, default=False) username = Column(String(64)) proposal = Column(Text) + proposal_diff = Column(Text) node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) node = relation(Node, backref='comments') @@ -46,7 +47,7 @@ class Comment(Base): parent = relation('Comment', backref='children', remote_side=[id]) def __init__(self, text, displayed, username, rating, time, - proposal, node=None, parent=None): + proposal, proposal_diff, node, parent): self.text = text self.displayed = displayed self.username = username @@ -55,6 +56,7 @@ class Comment(Base): self.node = node self.parent = parent self.proposal = proposal + self.proposal_diff = proposal_diff def serializable(self, user_id=None): delta = datetime.now() - self.time @@ -86,6 +88,7 @@ class Comment(Base): 'vote': vote or 0, 'node': self.node.id if self.node else None, 'parent': self.parent.id if self.parent else None, + 'proposal_diff': self.proposal_diff, 'children': [child.serializable(user_id) for child in self.children]} diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 706ea4c03..067815a9c 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -19,32 +19,39 @@ class SQLAlchemyStorage(StorageBackend): self.build_session.flush() return node.id + def get_node(self, node_id): + session = Session() + node = session.query(Node).filter(Node.id == node_id).first() + session.close() + return node + def post_build(self): self.build_session.commit() self.build_session.close() - def add_comment(self, parent_id, text, displayed, - username, rating, time, proposal): + def add_comment(self, text, displayed, username, rating, time, + proposal, proposal_diff, node=None, parent=None): time = time or datetime.now() session = Session() - id = parent_id[1:] - if parent_id[0] == 's': - node = session.query(Node).filter(Node.id == id).first() - comment = Comment(text, displayed, username, rating, - time, proposal, node=node) - elif parent_id[0] == 'c': - parent = session.query(Comment).filter(Comment.id == id).first() - comment = Comment(text, displayed, username, rating, - time,proposal, parent=parent) - + comment = Comment(text, displayed, username, rating, time, + proposal, proposal_diff, node, parent) + session.add(comment) session.commit() comment = comment.serializable() session.close() return comment + def get_comment(self, comment_id): + session = Session() + comment = session.query(Comment) \ + .filter(Comment.id == comment_id).first() + session.close() + return comment + + def get_comments(self, parent_id, user_id): parent_id = parent_id[1:] session = Session() @@ -73,4 +80,3 @@ class SQLAlchemyStorage(StorageBackend): session.add(vote) session.commit() session.close() - From 0039b09a36b0d7e4e787185eff4499b33b237990 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 27 Jul 2010 21:16:07 -0500 Subject: [PATCH 216/744] fix regression that caused error when replying to a comment --- sphinx/websupport/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index d64c4ff18..9d5f0cbac 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -210,7 +210,10 @@ class WebSupport(object): is_node = parent_id[0] == 's' node = self.storage.get_node(id) if is_node else None parent = self.storage.get_comment(id) if not is_node else None - diff = get_diff_html(node.source, proposal) if proposal else None + if node and proposal: + diff = get_diff_html(node.source, proposal) + else: + diff = None return self.storage.add_comment(text, displayed, username, rating, time, proposal, diff, node, parent) From 2d0c9a0e7155bb71a8bc76fd6d5f2d2145c9cdbd Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 28 Jul 2010 19:43:30 +0200 Subject: [PATCH 217/744] Move item to the correct section. --- CHANGES | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 573b4edd6..f1fb982b8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.1 (in development) ============================ +* Added Python 3.x support. + + Release 1.0.2 (in development) ============================== @@ -23,8 +26,6 @@ Release 1.0.1 (Jul 27, 2010) * Fix hyperrefs in object descriptions for LaTeX. - * Added Python 3.x support. - Release 1.0 (Jul 23, 2010) ========================== From c683c6ed833766c63c20fc33c830a8db5604979a Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 28 Jul 2010 19:49:06 +0200 Subject: [PATCH 218/744] Add some changes not picked up in the transplantation process. --- Makefile | 2 +- README | 12 ++++++++++++ doc/intro.rst | 8 ++++---- tests/test_build_html.py | 9 +++++---- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 13228c788..fc1140f33 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PYTHON ?= python3 +PYTHON ?= python .PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint \ reindent test diff --git a/README b/README index bb2dea9d6..e31d6b936 100644 --- a/README +++ b/README @@ -26,6 +26,18 @@ Then, direct your browser to ``_build/html/index.html``. Or read them online at <http://sphinx.pocoo.org/>. +Testing +======= + +To run the tests with the interpreter available as ``python``, use:: + + make test + +If you want to use a different interpreter, e.g. ``python3``, use:: + + PYTHON=python3 make test + + Contributing ============ diff --git a/doc/intro.rst b/doc/intro.rst index 1a39e266c..c85fbbad3 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -45,10 +45,10 @@ See the :ref:`pertinent section in the FAQ list <usingwith>`. Prerequisites ------------- -Sphinx needs at least **Python 2.4** to run, as well as the docutils_ and -Jinja2_ libraries. Sphinx should work with docutils version 0.5 or some -(not broken) SVN trunk snapshot. If you like to have source code highlighting -support, you must also install the Pygments_ library. +Sphinx needs at least **Python 2.4** or **Python 3.1** to run, as well as the +docutils_ and Jinja2_ libraries. Sphinx should work with docutils version 0.5 +or some (not broken) SVN trunk snapshot. If you like to have source code +highlighting support, you must also install the Pygments_ library. .. _reStructuredText: http://docutils.sf.net/rst.html .. _docutils: http://docutils.sf.net/ diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 3ca2c757f..0c59d9cca 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -51,6 +51,11 @@ HTML_WARNINGS = ENV_WARNINGS + """\ %(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; ' """ +if sys.version_info >= (3, 0): + ENV_WARNINGS = remove_unicode_literals(ENV_WARNINGS) + HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS) + + def tail_check(check): rex = re.compile(check) def checker(nodes): @@ -61,10 +66,6 @@ def tail_check(check): return checker -if sys.version_info >= (3, 0): - ENV_WARNINGS = remove_unicode_literals(ENV_WARNINGS) - HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS) - HTML_XPATH = { 'images.html': [ (".//img[@src='_images/img.png']", ''), From 7866ce29c69e6a843c4bf0d5472e29f15e70ae87 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 28 Jul 2010 19:58:17 +0200 Subject: [PATCH 219/744] Keep sphinx/__init__.py executable with Python 3. --- sphinx/__init__.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 31c61a86a..1ea2e7bf7 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -9,6 +9,9 @@ :license: BSD, see LICENSE for details. """ +# Keep this file executable as-is in Python 3! +# (Otherwise getting the version out of it from setup.py is impossible.) + import sys from os import path @@ -35,13 +38,14 @@ if '+' in __version__ or 'pre' in __version__: def main(argv=sys.argv): if sys.version_info[:3] < (2, 4, 0): - print >>sys.stderr, \ - 'Error: Sphinx requires at least Python 2.4 to run.' + sys.stderr.write('Error: Sphinx requires at least ' + 'Python 2.4 to run.\n') return 1 try: from sphinx import cmdline - except ImportError, err: + except ImportError: + err = sys.exc_info()[1] errstr = str(err) if errstr.lower().startswith('no module named'): whichmod = errstr[16:] @@ -54,14 +58,14 @@ def main(argv=sys.argv): whichmod = 'roman module (which is distributed with Docutils)' hint = ('This can happen if you upgraded docutils using\n' 'easy_install without uninstalling the old version' - 'first.') + 'first.\n') else: whichmod += ' module' - print >>sys.stderr, ('Error: The %s cannot be found. ' - 'Did you install Sphinx and its dependencies ' - 'correctly?' % whichmod) + sys.stderr.write('Error: The %s cannot be found. ' + 'Did you install Sphinx and its dependencies ' + 'correctly?\n' % whichmod) if hint: - print >> sys.stderr, hint + sys.stderr.write(hint) return 1 raise return cmdline.main(argv) From 829a05e1ddf9bcf06911804b292a287a7ad7ff27 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 28 Jul 2010 19:58:47 +0200 Subject: [PATCH 220/744] Ignore distribute files. --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index 40c00aac7..b68cccf91 100644 --- a/.hgignore +++ b/.hgignore @@ -16,3 +16,4 @@ \.DS_Store$ ~$ ^utils/.*3\.py$ +^distribute- From 67ffb9bf5477b6d9489157ba761065f9218b937b Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 28 Jul 2010 20:24:35 +0200 Subject: [PATCH 221/744] Fix raw_input which is not converted by 2to3 if not called. --- sphinx/quickstart.py | 8 ++++++-- tests/test_quickstart.py | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 557f8c094..92a5bea2a 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -22,7 +22,11 @@ from sphinx.util.console import purple, bold, red, turquoise, \ from sphinx.util import texescape # function to get input from terminal -- overridden by the test suite -term_input = raw_input +try: + # this raw_input is not converted by 2to3 + term_input = raw_input +except NameError: + term_input = input PROMPT_PREFIX = '> ' @@ -692,7 +696,7 @@ if sys.version_info >= (3, 0): return _unicode_string_re.sub('\\1', source) for f in ['QUICKSTART_CONF', 'EPUB_CONFIG', 'INTERSPHINX_CONFIG']: - globals()[f] = convert_python_source(globals()[f]) + globals()[f] = _convert_python_source(globals()[f]) del _unicode_string_re, _convert_python_source diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 72ae764d6..541959bd3 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -36,8 +36,13 @@ def mock_raw_input(answers, needanswer=False): return '' return raw_input +try: + real_raw_input = raw_input +except NameError: + real_raw_input = input + def teardown_module(): - qs.term_input = raw_input + qs.term_input = real_raw_input qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None) coloron() From 27b4265a2f002e317872149eb5e8a28df407f005 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 28 Jul 2010 20:30:05 +0200 Subject: [PATCH 222/744] Update phony targets list. --- Makefile | 4 ++-- sphinx/ext/oldcmarkup.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index fc1140f33..09aa3c962 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ PYTHON ?= python -.PHONY: all check clean clean-pyc clean-patchfiles clean-generated pylint \ - reindent test +.PHONY: all check clean clean-pyc clean-patchfiles clean-backupfiles \ + clean-generated pylint reindent test covertest build convert-utils DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \ -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \ diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py index 84ae61dd2..3aa53fd86 100644 --- a/sphinx/ext/oldcmarkup.py +++ b/sphinx/ext/oldcmarkup.py @@ -31,6 +31,7 @@ class OldCDirective(Directive): def run(self): env = self.state.document.settings.env if not env.app._oldcmarkup_warned: + print 'XXXYYY' env.warn(env.docname, WARNING_MSG, self.lineno) env.app._oldcmarkup_warned = True newname = 'c:' + self.name[1:] From 86cd745dc1f5338804fa5adf24447a9427c09fb8 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 30 Jul 2010 16:59:47 +0200 Subject: [PATCH 223/744] Run 2to3 on config files which contain Python 2.x unicode literals. --- sphinx/config.py | 27 +++++++++++------ sphinx/quickstart.py | 8 ++++- sphinx/util/pycompat.py | 67 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 87 insertions(+), 15 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 708da162e..efa9f7407 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -13,13 +13,20 @@ import os import re import sys from os import path +try: + from distutils.util import run_2to3 +except ImportError: + run_2to3 = None from sphinx.errors import ConfigError from sphinx.util.osutil import make_filename -from sphinx.util.pycompat import bytes, b +from sphinx.util.pycompat import bytes, b, should_run_2to3, run_2to3 nonascii_re = re.compile(b(r'[\x80-\xff]')) +CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s" +if sys.version_info >= (3, 0): + CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?" class Config(object): """Configuration file abstraction.""" @@ -167,15 +174,17 @@ class Config(object): try: try: os.chdir(dirname) - f = open(config_file, 'rb') - try: - code = compile(f.read(), config_file, 'exec') - finally: - f.close() - exec code in config + if should_run_2to3(config_file): + code = run_2to3(config_file) + else: + f = open(config_file, 'rb') + try: + code = f.read() + finally: + f.close() + exec compile(code, config_file, 'exec') in config except SyntaxError, err: - raise ConfigError('There is a syntax error in your ' - 'configuration file: ' + str(err)) + raise ConfigError(CONFIG_SYNTAX_ERROR % err) finally: os.chdir(olddir) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 92a5bea2a..7e38a742a 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -31,7 +31,13 @@ except NameError: PROMPT_PREFIX = '> ' -QUICKSTART_CONF = '''\ +if sys.version_info >= (3, 0): + # prevents that the file is checked for being written in Python 2.x syntax + QUICKSTART_CONF = '#!/usr/bin/env python3\n' +else: + QUICKSTART_CONF = '' + +QUICKSTART_CONF += '''\ # -*- coding: utf-8 -*- # # %(project)s documentation build configuration file, created by diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 229b54b45..2ec71e72b 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -12,6 +12,7 @@ import sys import codecs import encodings +import re try: from types import ClassType @@ -20,11 +21,6 @@ except ImportError: # Python 3 class_types = (type,) -try: - base_exception = BaseException -except NameError: - base_exception = Exception - # the ubiquitous "bytes" helper function if sys.version_info >= (3, 0): @@ -34,6 +30,66 @@ else: b = str +encoding_re = re.compile(b(r'coding[=:]\s*([-\w.]+)')) +unicode_literal_re = re.compile(ur""" +(?: + "(?:[^"\]]*(?:\\.[^"\\]*)*)"| + '(?:[^'\]]*(?:\\.[^'\\]*)*)' +) +""", re.VERBOSE) + + +try: + from lib2to3.refactor import RefactoringTool, get_fixers_from_package +except ImportError: + _run_2to3 = None + def should_run_2to3(filepath): + return False +else: + def should_run_2to3(filepath): + # th default source code encoding for python 2.x + encoding = 'ascii' + # only the first match of the encoding cookie counts + encoding_set = False + f = open(filepath, 'rb') + try: + for i, line in enumerate(f): + if line.startswith(b('#')): + if i == 0 and b('python3') in line: + return False + if not encoding_set: + encoding_match = encoding_re.match(line) + if encoding_match: + encoding = encoding_match.group(1) + encodin_set = True + elif line.strip(): + try: + line = line.decode(encoding) + except UnicodeDecodeError: + # I'm not sure this will work but let's try it anyway + return True + if unicode_literal_re.search(line) is not None: + return True + finally: + f.close() + return False + + def run_2to3(filepath): + sys.path.append('..') + fixers = get_fixers_from_package('lib2to3.fixes') + fixers.extend(get_fixers_from_package('custom_fixers')) + refactoring_tool = RefactoringTool(fixers) + source = refactoring_tool._read_python_source(filepath)[0] + ast = refactoring_tool.refactor_string(source, 'conf.py') + return unicode(ast) + + +try: + base_exception = BaseException +except NameError: + base_exception = Exception + + try: next = next except NameError: @@ -41,6 +97,7 @@ except NameError: def next(iterator): return iterator.next() + try: bytes = bytes except NameError: From f014641405385f8477f221242274c188aab0e172 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 30 Jul 2010 11:20:43 -0500 Subject: [PATCH 224/744] Added test for build and made web support buildable without specifying search adapter --- sphinx/websupport/__init__.py | 21 ++++++------ sphinx/websupport/search/__init__.py | 5 +++ sphinx/websupport/search/nullsearch.py | 22 ++++++++++++ tests/test_websupport.py | 47 ++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 sphinx/websupport/search/nullsearch.py create mode 100644 tests/test_websupport.py diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 9d5f0cbac..4d73c0e10 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -10,7 +10,7 @@ """ import cPickle as pickle -import re +import re, sys from os import path from cgi import escape from difflib import Differ @@ -35,7 +35,7 @@ class WebSupport(object): """ def __init__(self, srcdir='', outdir='', datadir='', search=None, - storage=None): + storage=None, status=sys.stdout, warning=sys.stderr): self.srcdir = srcdir self.outdir = outdir or path.join(self.srcdir, '_build', 'websupport') @@ -43,9 +43,10 @@ class WebSupport(object): self.outdir = outdir or datadir - if search is not None: - self._init_search(search) + self.status = status + self.warning = warning + self._init_search(search) self._init_storage(storage) def _init_storage(self, storage): @@ -73,11 +74,11 @@ class WebSupport(object): if isinstance(search, BaseSearch): self.search = search else: - mod, cls = search_adapters[search] - search_class = getattr(__import__('sphinx.websupport.search.' + mod, - None, None, [cls]), cls) + mod, cls = search_adapters[search or 'null'] + mod = 'sphinx.websupport.search.' + mod + SearchClass = getattr(__import__(mod, None, None, [cls]), cls) search_path = path.join(self.outdir, 'search') - self.search = search_class(search_path) + self.search = SearchClass(search_path) self.results_template = \ self.template_env.get_template('searchresults.html') @@ -95,8 +96,8 @@ class WebSupport(object): doctreedir = path.join(self.outdir, 'doctrees') app = WebSupportApp(self.srcdir, self.srcdir, self.outdir, doctreedir, 'websupport', - search=self.search, - storage=self.storage) + search=self.search, status=self.status, + warning=self.warning, storage=self.storage) self.storage.pre_build() app.build() diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index e1d7ea471..d41b560c3 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -12,6 +12,9 @@ import re class BaseSearch(object): + def __init__(self, path): + pass + def init_indexing(self, changed=[]): """Called by the builder to initialize the search indexer. `changed` is a list of pagenames that will be reindexed. You may want to remove @@ -117,4 +120,6 @@ class BaseSearch(object): search_adapters = { 'xapian': ('xapiansearch', 'XapianSearch'), 'whoosh': ('whooshsearch', 'WhooshSearch'), + 'null': ('nullsearch', 'NullSearch') } + diff --git a/sphinx/websupport/search/nullsearch.py b/sphinx/websupport/search/nullsearch.py new file mode 100644 index 000000000..ad3d7daef --- /dev/null +++ b/sphinx/websupport/search/nullsearch.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.search.nullsearch + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + The default search adapter, does nothing. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from sphinx.websupport.search import BaseSearch + +class NullSearchException(Exception): + pass + +class NullSearch(BaseSearch): + def feed(self, pagename, title, doctree): + pass + + def query(self, q): + raise NullSearchException('No search adapter specified.') diff --git a/tests/test_websupport.py b/tests/test_websupport.py new file mode 100644 index 000000000..d9251eb61 --- /dev/null +++ b/tests/test_websupport.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +""" + test_websupport + ~~~~~~~~~~~~~~~ + + Test the Web Support Package + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +from StringIO import StringIO + +from sphinx.websupport import WebSupport + +try: + from functools import wraps +except ImportError: + # functools is new in 2.4 + wraps = lambda f: (lambda w: w) + +from util import * + +def teardown_module(): + (test_root / 'websupport').rmtree(True) + +def with_support(*args, **kwargs): + """Make a WebSupport object and pass it the test.""" + settings = {'srcdir': test_root, + 'outdir': os.path.join(test_root, 'websupport'), + 'status': StringIO(), + 'warning': StringIO()} + settings.update(kwargs) + + def generator(func): + @wraps(func) + def new_func(*args2, **kwargs2): + support = WebSupport(**settings) + func(support, *args2, **kwargs2) + return new_func + return generator + +@with_support() +def test_build(support): + support.build() + From 846ab3a74374a1f7e72d5e7a56857beb8eb494f4 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 30 Jul 2010 15:53:02 -0500 Subject: [PATCH 225/744] Added DocumentNotFoundError --- sphinx/websupport/__init__.py | 9 ++++++++- tests/test_websupport.py | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 4d73c0e10..6c2920708 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -22,6 +22,7 @@ from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch, search_adapters from sphinx.websupport.comments import StorageBackend +from sphinx.websupport.errors import DocumentNotFoundError class WebSupportApp(Sphinx): def __init__(self, *args, **kwargs): @@ -126,7 +127,13 @@ class WebSupport(object): :param docname: the name of the document to load. """ infilename = path.join(self.outdir, docname + '.fpickle') - f = open(infilename, 'rb') + + try: + f = open(infilename, 'rb') + except IOError: + raise DocumentNotFoundError( + 'The document "%s" could not be found' % docname) + document = pickle.load(f) return document diff --git a/tests/test_websupport.py b/tests/test_websupport.py index d9251eb61..80a4affed 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -13,6 +13,7 @@ import os from StringIO import StringIO from sphinx.websupport import WebSupport +from sphinx.websupport.errors import DocumentNotFoundError try: from functools import wraps @@ -44,4 +45,5 @@ def with_support(*args, **kwargs): @with_support() def test_build(support): support.build() + raises(DocumentNotFoundError, support.get_document, 'nonexisting') From 2528f3ddce3209fec1b4910cfbb73d291ac5a25b Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 30 Jul 2010 16:13:51 -0500 Subject: [PATCH 226/744] Added SrcdirNotSpecifiedError --- sphinx/websupport/__init__.py | 5 ++++- tests/test_websupport.py | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 6c2920708..ae55ee2e8 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -22,7 +22,7 @@ from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch, search_adapters from sphinx.websupport.comments import StorageBackend -from sphinx.websupport.errors import DocumentNotFoundError +from sphinx.websupport.errors import * class WebSupportApp(Sphinx): def __init__(self, *args, **kwargs): @@ -94,6 +94,9 @@ class WebSupport(object): build the pickles and search index, placing them into `outdir`. It will also save node data to the database. """ + if not self.srcdir: + raise SrcdirNotSpecifiedError( \ + 'No srcdir associated with WebSupport object') doctreedir = path.join(self.outdir, 'doctrees') app = WebSupportApp(self.srcdir, self.srcdir, self.outdir, doctreedir, 'websupport', diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 80a4affed..b8884bf91 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -13,7 +13,7 @@ import os from StringIO import StringIO from sphinx.websupport import WebSupport -from sphinx.websupport.errors import DocumentNotFoundError +from sphinx.websupport.errors import * try: from functools import wraps @@ -23,13 +23,14 @@ except ImportError: from util import * + def teardown_module(): (test_root / 'websupport').rmtree(True) + def with_support(*args, **kwargs): """Make a WebSupport object and pass it the test.""" - settings = {'srcdir': test_root, - 'outdir': os.path.join(test_root, 'websupport'), + settings = {'outdir': os.path.join(test_root, 'websupport'), 'status': StringIO(), 'warning': StringIO()} settings.update(kwargs) @@ -42,8 +43,20 @@ def with_support(*args, **kwargs): return new_func return generator + @with_support() +def test_no_srcdir(support): + """Make sure the correct exception is raised if srcdir is not given.""" + raises(SrcdirNotSpecifiedError, support.build) + +@with_support(srcdir=test_root) def test_build(support): support.build() + +@with_support() +def test_get_document(support): raises(DocumentNotFoundError, support.get_document, 'nonexisting') + contents = support.get_document('contents') + assert contents['title'] and contents['body'] \ + and contents['sidebar'] and contents['relbar'] From bc155cac92da11d738af89b128ad7f3ff137bd76 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 30 Jul 2010 16:15:41 -0500 Subject: [PATCH 227/744] Somehow I always forget to add files... (sphinx.websupport.errors) --- sphinx/websupport/errors.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 sphinx/websupport/errors.py diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py new file mode 100644 index 000000000..b1c47915e --- /dev/null +++ b/sphinx/websupport/errors.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.errors + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Contains Error classes for the web support package. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +__all__ = ['DocumentNotFoundError', 'SrcdirNotSpecifiedError'] + +class DocumentNotFoundError(Exception): + pass + + +class SrcdirNotSpecifiedError(Exception): + pass From a36175298e252c4a92902e5c35a5f52ec35edaab Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 30 Jul 2010 17:07:48 -0500 Subject: [PATCH 228/744] Added basic search tests. --- tests/test_websupport.py | 50 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/tests/test_websupport.py b/tests/test_websupport.py index b8884bf91..3d61e55c2 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -9,7 +9,7 @@ :license: BSD, see LICENSE for details. """ -import os +import os, sys from StringIO import StringIO from sphinx.websupport import WebSupport @@ -24,15 +24,22 @@ except ImportError: from util import * -def teardown_module(): +default_settings = {'outdir': os.path.join(test_root, 'websupport'), + 'status': StringIO(), + 'warning': StringIO()} + + +def clear_builddir(): (test_root / 'websupport').rmtree(True) +def teardown_module(): + clear_builddir() + + def with_support(*args, **kwargs): """Make a WebSupport object and pass it the test.""" - settings = {'outdir': os.path.join(test_root, 'websupport'), - 'status': StringIO(), - 'warning': StringIO()} + settings = default_settings.copy() settings.update(kwargs) def generator(func): @@ -49,10 +56,12 @@ def test_no_srcdir(support): """Make sure the correct exception is raised if srcdir is not given.""" raises(SrcdirNotSpecifiedError, support.build) + @with_support(srcdir=test_root) def test_build(support): support.build() + @with_support() def test_get_document(support): raises(DocumentNotFoundError, support.get_document, 'nonexisting') @@ -60,3 +69,34 @@ def test_get_document(support): contents = support.get_document('contents') assert contents['title'] and contents['body'] \ and contents['sidebar'] and contents['relbar'] + + +def search_adapter_helper(adapter): + clear_builddir() + + settings = default_settings.copy() + settings.update({'srcdir': test_root, + 'search': adapter}) + support = WebSupport(**settings) + + support.build() + + +def test_xapian(): + # Don't run tests if xapian is not installed. + try: + import xapian + search_adapter_helper('xapian') + except ImportError: + sys.stderr.write('info: not running xapian tests, ' \ + 'xapian doesn\'t seem to be installed') + + +def test_whoosh(): + # Don't run tests if xapian is not installed. + try: + import whoosh + search_adapter_helper('whoosh') + except ImportError: + sys.stderr.write('info: not running xapian tests, ' \ + 'whoosh doesn\'t seem to be installed') From a31f3b7e73f2050efab07a9ed143c72e774af452 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 31 Jul 2010 12:23:26 -0500 Subject: [PATCH 229/744] More complete tests for search adapters. --- sphinx/websupport/search/__init__.py | 2 +- sphinx/websupport/search/whooshsearch.py | 2 ++ tests/test_websupport.py | 25 +++++++++++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index d41b560c3..0e613222e 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -83,7 +83,7 @@ class BaseSearch(object): query `q`. This should return an iterable containing tuples of the following format:: - (<path>, <title> <context>) + (<path>, <title>, <context>) `path` and `title` are the same values that were passed to :meth:`add_document`, and `context` should be a short text snippet diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 658b764dd..257393a6a 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -40,6 +40,8 @@ class WhooshSearch(BaseSearch): def finish_indexing(self): self.index_writer.commit() + # Create a new searcher so changes can be seen immediately + self.searcher = self.index.searcher() def add_document(self, pagename, title, text): self.index_writer.add_document(path=unicode(pagename), diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 3d61e55c2..3f352cd6c 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -78,9 +78,32 @@ def search_adapter_helper(adapter): settings.update({'srcdir': test_root, 'search': adapter}) support = WebSupport(**settings) - support.build() + s = support.search + + # Test the adapters query method. A search for "Epigraph" should return + # one result. + results = s.query(u'Epigraph') + assert len(results) == 1, \ + '%s search adapter returned %s search result(s), should have been 1'\ + % (adapter, len(results)) + + # Make sure documents are properly updated by the search adapter. + s.init_indexing(changed=['markup']) + s.add_document(u'markup', u'title', u'SomeLongRandomWord') + s.finish_indexing() + # Now a search for "Epigraph" should return zero results. + results = s.query(u'Epigraph') + assert len(results) == 0, \ + '%s search adapter returned %s search result(s), should have been 0'\ + % (adapter, len(results)) + # A search for "SomeLongRandomWord" should return one result. + results = s.query(u'SomeLongRandomWord') + assert len(results) == 1, \ + '%s search adapter returned %s search result(s), should have been 1'\ + % (adapter, len(results)) + def test_xapian(): # Don't run tests if xapian is not installed. From 0e84c758229f0a71a52b33c5e19d2e0b7d8b0ef1 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 31 Jul 2010 19:47:15 +0200 Subject: [PATCH 230/744] Improve support for automatic 2to3 conversion of config files. It now kicks in whenever the original file raises SyntaxErrors on compiling. --- sphinx/config.py | 37 +++++++++++++---------- sphinx/util/pycompat.py | 65 +++++++++++------------------------------ tests/test_config.py | 6 ++++ 3 files changed, 45 insertions(+), 63 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index efa9f7407..6c27f85f0 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -13,14 +13,10 @@ import os import re import sys from os import path -try: - from distutils.util import run_2to3 -except ImportError: - run_2to3 = None from sphinx.errors import ConfigError from sphinx.util.osutil import make_filename -from sphinx.util.pycompat import bytes, b, should_run_2to3, run_2to3 +from sphinx.util.pycompat import bytes, b, convert_with_2to3 nonascii_re = re.compile(b(r'[\x80-\xff]')) @@ -172,17 +168,28 @@ class Config(object): config['tags'] = tags olddir = os.getcwd() try: + # we promise to have the config dir as current dir while the + # config file is executed + os.chdir(dirname) + # get config source + f = open(config_file, 'rb') try: - os.chdir(dirname) - if should_run_2to3(config_file): - code = run_2to3(config_file) - else: - f = open(config_file, 'rb') - try: - code = f.read() - finally: - f.close() - exec compile(code, config_file, 'exec') in config + source = f.read() + finally: + f.close() + try: + # compile to a code object, handle syntax errors + try: + code = compile(source, config_file, 'exec') + except SyntaxError: + if convert_with_2to3: + # maybe the file uses 2.x syntax; try to refactor to + # 3.x syntax using 2to3 + source = convert_with_2to3(config_file) + code = compile(source, config_file, 'exec') + else: + raise + exec code in config except SyntaxError, err: raise ConfigError(CONFIG_SYNTAX_ERROR % err) finally: diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 2ec71e72b..5f23bbe18 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -30,58 +30,26 @@ else: b = str -encoding_re = re.compile(b(r'coding[=:]\s*([-\w.]+)')) -unicode_literal_re = re.compile(ur""" -(?: - "(?:[^"\]]*(?:\\.[^"\\]*)*)"| - '(?:[^'\]]*(?:\\.[^'\\]*)*)' -) -""", re.VERBOSE) +# Support for running 2to3 over config files - -try: - from lib2to3.refactor import RefactoringTool, get_fixers_from_package -except ImportError: - _run_2to3 = None - def should_run_2to3(filepath): - return False +if sys.version_info < (3, 0): + # no need to refactor on 2.x versions + convert_with_2to3 = None else: - def should_run_2to3(filepath): - # th default source code encoding for python 2.x - encoding = 'ascii' - # only the first match of the encoding cookie counts - encoding_set = False - f = open(filepath, 'rb') - try: - for i, line in enumerate(f): - if line.startswith(b('#')): - if i == 0 and b('python3') in line: - return False - if not encoding_set: - encoding_match = encoding_re.match(line) - if encoding_match: - encoding = encoding_match.group(1) - encodin_set = True - elif line.strip(): - try: - line = line.decode(encoding) - except UnicodeDecodeError: - # I'm not sure this will work but let's try it anyway - return True - if unicode_literal_re.search(line) is not None: - return True - finally: - f.close() - return False - - def run_2to3(filepath): - sys.path.append('..') + def convert_with_2to3(filepath): + from lib2to3.refactor import RefactoringTool, get_fixers_from_package + from lib2to3.pgen2.parse import ParseError fixers = get_fixers_from_package('lib2to3.fixes') - fixers.extend(get_fixers_from_package('custom_fixers')) refactoring_tool = RefactoringTool(fixers) source = refactoring_tool._read_python_source(filepath)[0] - ast = refactoring_tool.refactor_string(source, 'conf.py') - return unicode(ast) + try: + tree = refactoring_tool.refactor_string(source, 'conf.py') + except ParseError, err: + # do not propagate lib2to3 exceptions + lineno, offset = err.context[1] + # try to match ParseError details with SyntaxError details + raise SyntaxError(err.msg, (filepath, lineno, offset, err.value)) + return unicode(tree) try: @@ -93,7 +61,8 @@ except NameError: try: next = next except NameError: - # this is on Python 2, where the method is called "next" + # this is on Python 2, where the method is called "next" (it is refactored + # to __next__ by 2to3, but in that case never executed) def next(iterator): return iterator.next() diff --git a/tests/test_config.py b/tests/test_config.py index ecf90f609..7fce4495b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -88,6 +88,12 @@ def test_errors_warnings(dir): write_file(dir / 'conf.py', u'project = \n', 'ascii') raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None) + # test the automatic conversion of 2.x only code in configs + write_file(dir / 'conf.py', u'\n\nproject = u"Jägermeister"\n', 'utf-8') + cfg = Config(dir, 'conf.py', {}, None) + cfg.init_values() + assert cfg.project == u'Jägermeister' + # test the warning for bytestrings with non-ascii content # bytestrings with non-ascii content are a syntax error in python3 so we # skip the test there From 682fa466fe70704c7e58c703a387dcb082f56518 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 31 Jul 2010 15:51:13 -0500 Subject: [PATCH 231/744] Added test for comment system. --- tests/test_websupport.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 3f352cd6c..53307fb4e 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -14,6 +14,8 @@ from StringIO import StringIO from sphinx.websupport import WebSupport from sphinx.websupport.errors import * +from sphinx.websupport.comments.sqlalchemystorage import Session +from sphinx.websupport.comments.db import Node try: from functools import wraps @@ -116,10 +118,25 @@ def test_xapian(): def test_whoosh(): - # Don't run tests if xapian is not installed. + # Don't run tests if whoosh is not installed. try: import whoosh search_adapter_helper('whoosh') except ImportError: - sys.stderr.write('info: not running xapian tests, ' \ + sys.stderr.write('info: not running whoosh tests, ' \ 'whoosh doesn\'t seem to be installed') + + +@with_support() +def test_comments(support): + session = Session() + node = session.query(Node).first() + comment = support.add_comment('First test comment', node=str(node.id)) + support.add_comment('Child test comment', parent=str(comment['id'])) + data = support.get_comments(str(node.id)) + comments = data['comments'] + children = comments[0]['children'] + assert len(comments) == 1 + assert comments[0]['text'] == 'First test comment' + assert len(children) == 1 + assert children[0]['text'] == 'Child test comment' From 92e0cfc00100dc80719ac1819f5362e2468e549b Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 31 Jul 2010 15:53:13 -0500 Subject: [PATCH 232/744] refactored add_comment and get_comments; added CombinedHtmlDiff class --- sphinx/websupport/__init__.py | 91 ++++--------------- sphinx/websupport/comments/__init__.py | 10 +- sphinx/websupport/comments/differ.py | 66 ++++++++++++++ .../websupport/comments/sqlalchemystorage.py | 41 ++++----- 4 files changed, 105 insertions(+), 103 deletions(-) create mode 100644 sphinx/websupport/comments/differ.py diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index ae55ee2e8..7af8aa1cb 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -9,11 +9,9 @@ :license: BSD, see LICENSE for details. """ +import sys import cPickle as pickle -import re, sys from os import path -from cgi import escape -from difflib import Differ from datetime import datetime from jinja2 import Environment, FileSystemLoader @@ -195,20 +193,25 @@ class WebSupport(object): """ return self.storage.get_comments(node_id, user_id) - def add_comment(self, parent_id, text, displayed=True, username=None, - rating=0, time=None, proposal=None): - """Add a comment to a node or another comment. `parent_id` will have - a one letter prefix, distinguishing between node parents and - comment parents, 'c' and 's' respectively. This function will - return the comment in the same format as :meth:`get_comments`. - Usage is simple:: + def add_comment(self, text, node='', parent='', displayed=True, + username=None, rating=0, time=None, proposal=None): + """Add a comment to a node or another comment. Returns the comment + in the same format as :meth:`get_comments`. If the comment is being + attached to a node, pass in the node's id (as a string) with the + node keyword argument:: + + comment = support.add_comment(text, node=node_id) + + If the comment is the child of another comment, provide the parent's + id (as a string) with the parent keyword argument:: + + comment = support.add_comment(text, parent=parent_id) - comment = support.add_comment(parent_id, text) - If you would like to store a username with the comment, pass in the optional `username` keyword argument:: - comment = support.add_comment(parent_id, text, username=username) + comment = support.add_comment(text, node=node_id, + username=username) :param parent_id: the prefixed id of the comment's parent. :param text: the text of the comment. @@ -217,16 +220,8 @@ class WebSupport(object): :param rating: the starting rating of the comment, defaults to 0. :param time: the time the comment was created, defaults to now. """ - id = parent_id[1:] - is_node = parent_id[0] == 's' - node = self.storage.get_node(id) if is_node else None - parent = self.storage.get_comment(id) if not is_node else None - if node and proposal: - diff = get_diff_html(node.source, proposal) - else: - diff = None return self.storage.add_comment(text, displayed, username, rating, - time, proposal, diff, node, parent) + time, proposal, node, parent) def process_vote(self, comment_id, user_id, value): """Process a user's vote. The web support package relies @@ -254,55 +249,3 @@ class WebSupport(object): """ value = int(value) self.storage.process_vote(comment_id, user_id, value) - -highlight_regex = re.compile(r'([\+\-\^]+)') - -def highlight_text(text, next, tag): - next = next[2:] - new_text = [] - start = 0 - for match in highlight_regex.finditer(next): - new_text.append(text[start:match.start()]) - new_text.append('<%s>' % tag) - new_text.append(text[match.start():match.end()]) - new_text.append('</%s>' % tag) - start = match.end() - new_text.append(text[start:]) - return ''.join(new_text) - -def get_diff_html(source, proposal): - proposal = escape(proposal) - - def handle_line(line, next=None): - prefix = line[0] - text = line[2:] - - if prefix == ' ': - return text - elif prefix == '?': - return '' - - if next[0] == '?': - tag = 'ins' if prefix == '+' else 'del' - text = highlight_text(text, next, tag) - css_class = 'prop_added' if prefix == '+' else 'prop_removed' - - return '<span class="%s">%s</span>\n' % (css_class, text.rstrip()) - - differ = Differ() - diff = list(differ.compare(source.splitlines(1), proposal.splitlines(1))) - - html = [] - line = diff.pop(0) - next = diff.pop(0) - while True: - html.append(handle_line(line, next)) - line = next - try: - next = diff.pop(0) - except IndexError: - handle_line(line) - break - - return ''.join(html) - diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index 2daf9b64b..3d7f51542 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -29,23 +29,17 @@ class StorageBackend(object): """ raise NotImplementedError() - def get_node(self, node_id): - raise NotImplementedError() - def post_build(self): """Called after a build has completed. Use this to finalize the addition of nodes if needed. """ pass - def add_comment(self, parent_id, text, displayed, - username, rating, time, proposal, proposal_diff): + def add_comment(self, text, displayed, username, rating, time, + proposal, node, parent): """Called when a comment is being added.""" raise NotImplementedError() - def get_comment(self, comment_id): - raise NotImplementedError() - def get_comments(self, parent_id, user_id): """Called to retrieve all comments for a node.""" raise NotImplementedError() diff --git a/sphinx/websupport/comments/differ.py b/sphinx/websupport/comments/differ.py new file mode 100644 index 000000000..786922346 --- /dev/null +++ b/sphinx/websupport/comments/differ.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +""" + sphinx.websupport.comments.differ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + A differ for creating an HTML representations of proposal diffs + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +from cgi import escape +from difflib import Differ + +class CombinedHtmlDiff(object): + + highlight_regex = re.compile(r'([\+\-\^]+)') + + def _highlight_text(self, text, next, tag): + next = next[2:] + new_text = [] + start = 0 + for match in highlight_regex.finditer(next): + new_text.append(text[start:match.start()]) + new_text.append('<%s>' % tag) + new_text.append(text[match.start():match.end()]) + new_text.append('</%s>' % tag) + start = match.end() + new_text.append(text[start:]) + return ''.join(new_text) + + def _handle_line(line, next=None): + prefix = line[0] + text = line[2:] + + if prefix == ' ': + return text + elif prefix == '?': + return '' + + if next[0] == '?': + tag = 'ins' if prefix == '+' else 'del' + text = self._highlight_text(text, next, tag) + css_class = 'prop_added' if prefix == '+' else 'prop_removed' + + return '<span class="%s">%s</span>\n' % (css_class, text.rstrip()) + + def make_html(self, source, proposal): + proposal = escape(proposal) + + differ = Differ() + diff = list(differ.compare(source.splitlines(1), + proposal.splitlines(1))) + html = [] + line = diff.pop(0) + next = diff.pop(0) + while True: + html.append(self._handle_line(line, next)) + line = next + try: + next = diff.pop(0) + except IndexError: + self._handle_line(line) + break + return ''.join(html) diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 6c9b06b3b..294c48f44 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -14,6 +14,7 @@ from datetime import datetime from sphinx.websupport.comments import StorageBackend from sphinx.websupport.comments.db import Base, Node, Comment, CommentVote,\ Session +from sphinx.websupport.comments.differ import CombinedHtmlDiff class SQLAlchemyStorage(StorageBackend): def __init__(self, engine): @@ -31,41 +32,39 @@ class SQLAlchemyStorage(StorageBackend): self.build_session.flush() return node.id - def get_node(self, node_id): - session = Session() - node = session.query(Node).filter(Node.id == node_id).first() - session.close() - return node - def post_build(self): self.build_session.commit() self.build_session.close() def add_comment(self, text, displayed, username, rating, time, - proposal, proposal_diff, node=None, parent=None): - time = time or datetime.now() - + proposal, node, parent): session = Session() - comment = Comment(text, displayed, username, rating, time, - proposal, proposal_diff, node, parent) + if node: + node = session.query(Node).filter(Node.id == node).first() + parent = None + else: + node = None + parent = session.query(Comment).filter( + Comment.id == parent).first() + if node and proposal: + differ = CombinedHtmlDiff() + proposal_diff = differ.make_html(node.source, proposal) + else: + proposal_diff = None + + comment = Comment(text, displayed, username, rating, + time or datetime.now(), proposal, proposal_diff, + node, parent) session.add(comment) session.commit() comment = comment.serializable() session.close() return comment - def get_comment(self, comment_id): + def get_comments(self, node_id, user_id): session = Session() - comment = session.query(Comment) \ - .filter(Comment.id == comment_id).first() - session.close() - return comment - - def get_comments(self, parent_id, user_id): - parent_id = parent_id[1:] - session = Session() - node = session.query(Node).filter(Node.id == parent_id).first() + node = session.query(Node).filter(Node.id == node_id).first() data = {'source': node.source, 'comments': [comment.serializable(user_id) for comment in node.comments]} From 62fe57c64118f45051f6d72c87a4032db436c88d Mon Sep 17 00:00:00 2001 From: jacob <jacob@panther> Date: Tue, 3 Aug 2010 12:21:43 -0500 Subject: [PATCH 233/744] Converted comment schema from adjacency list to materialized path. Added tests for commments. Layed groundwork for comment moderation. --- sphinx/websupport/__init__.py | 20 +++-- sphinx/websupport/comments/__init__.py | 2 +- sphinx/websupport/comments/db.py | 52 ++++++------ .../websupport/comments/sqlalchemystorage.py | 79 ++++++++++++++----- tests/test_websupport.py | 57 ++++++++++++- 5 files changed, 147 insertions(+), 63 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 7af8aa1cb..373622eb1 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -157,7 +157,7 @@ class WebSupport(object): document['title'] = 'Search Results' return document - def get_comments(self, node_id, user_id=None): + def get_comments(self, node_id, username=None, moderator=False): """Get the comments and source associated with `node_id`. If `user_id` is given vote information will be included with the returned comments. The default CommentBackend returns dict with @@ -191,10 +191,11 @@ class WebSupport(object): :param node_id: the id of the node to get comments for. :param user_id: the id of the user viewing the comments. """ - return self.storage.get_comments(node_id, user_id) + return self.storage.get_comments(node_id, username, moderator) - def add_comment(self, text, node='', parent='', displayed=True, - username=None, rating=0, time=None, proposal=None): + def add_comment(self, text, node_id='', parent_id='', displayed=True, + username=None, rating=0, time=None, proposal=None, + moderator=False): """Add a comment to a node or another comment. Returns the comment in the same format as :meth:`get_comments`. If the comment is being attached to a node, pass in the node's id (as a string) with the @@ -215,15 +216,16 @@ class WebSupport(object): :param parent_id: the prefixed id of the comment's parent. :param text: the text of the comment. - :param displayed: for future use... + :param displayed: for moderation purposes :param username: the username of the user making the comment. :param rating: the starting rating of the comment, defaults to 0. :param time: the time the comment was created, defaults to now. """ return self.storage.add_comment(text, displayed, username, rating, - time, proposal, node, parent) + time, proposal, node_id, parent_id, + moderator) - def process_vote(self, comment_id, user_id, value): + def process_vote(self, comment_id, username, value): """Process a user's vote. The web support package relies on the API user to perform authentication. The API user will typically receive a comment_id and value from a form, and then @@ -248,4 +250,6 @@ class WebSupport(object): :param value: 1 for an upvote, -1 for a downvote, 0 for an unvote. """ value = int(value) - self.storage.process_vote(comment_id, user_id, value) + if not -1 <= value <= 1: + raise ValueError('vote value %s out of range (-1, 1)' % value) + self.storage.process_vote(comment_id, username, value) diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/comments/__init__.py index 3d7f51542..10856dffd 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/comments/__init__.py @@ -40,7 +40,7 @@ class StorageBackend(object): """Called when a comment is being added.""" raise NotImplementedError() - def get_comments(self, parent_id, user_id): + def get_comments(self, parent_id, user_id, moderator): """Called to retrieve all comments for a node.""" raise NotImplementedError() diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/comments/db.py index ecb62afa5..91175ed38 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/comments/db.py @@ -32,7 +32,6 @@ class Node(Base): document = Column(String(256), nullable=False) line = Column(Integer) source = Column(Text, nullable=False) - treeloc = Column(String(32), nullable=False) def __init__(self, document, line, source, treeloc): self.document = document @@ -51,26 +50,32 @@ class Comment(Base): username = Column(String(64)) proposal = Column(Text) proposal_diff = Column(Text) + path = Column(String(256), index=True) - node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) - node = relation(Node, backref='comments') - - parent_id = Column(Integer, ForeignKey(db_prefix + 'comments.id')) - parent = relation('Comment', backref='children', remote_side=[id]) + #node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) + #node = relation(Node, backref='comments') def __init__(self, text, displayed, username, rating, time, - proposal, proposal_diff, node, parent): + proposal, proposal_diff): self.text = text self.displayed = displayed self.username = username self.rating = rating self.time = time - self.node = node - self.parent = parent self.proposal = proposal self.proposal_diff = proposal_diff - def serializable(self, user_id=None): + def set_path(self, node_id, parent_id): + if node_id: + self.path = '%s.%s' % (node_id, self.id) + else: + session = Session() + parent_path = session.query(Comment.path).\ + filter(Comment.id == parent_id).one().path + session.close() + self.path = '%s.%s' % (parent_path, self.id) + + def serializable(self, vote=0): delta = datetime.now() - self.time time = {'year': self.time.year, @@ -82,15 +87,6 @@ class Comment(Base): 'iso': self.time.isoformat(), 'delta': self.pretty_delta(delta)} - vote = '' - if user_id is not None: - session = Session() - vote = session.query(CommentVote).filter( - CommentVote.comment_id == self.id).filter( - CommentVote.user_id == user_id).first() - vote = vote.value if vote is not None else 0 - session.close() - return {'text': self.text, 'username': self.username or 'Anonymous', 'id': self.id, @@ -98,11 +94,8 @@ class Comment(Base): 'age': delta.seconds, 'time': time, 'vote': vote or 0, - 'node': self.node.id if self.node else None, - 'parent': self.parent.id if self.parent else None, 'proposal_diff': self.proposal_diff, - 'children': [child.serializable(user_id) - for child in self.children]} + 'children': []} def pretty_delta(self, delta): days = delta.days @@ -120,15 +113,14 @@ class Comment(Base): class CommentVote(Base): __tablename__ = db_prefix + 'commentvote' - user_id = Column(Integer, primary_key=True) - # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted. - value = Column(Integer, nullable=False) - + username = Column(String(64), primary_key=True) comment_id = Column(Integer, ForeignKey(db_prefix + 'comments.id'), primary_key=True) comment = relation(Comment, backref="votes") + # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted. + value = Column(Integer, nullable=False) - def __init__(self, comment_id, user_id, value): - self.value = value - self.user_id = user_id + def __init__(self, comment_id, username, value): self.comment_id = comment_id + self.username = username + self.value = value diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 294c48f44..0e11c4a09 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -11,6 +11,7 @@ from datetime import datetime +from sqlalchemy.orm import aliased from sphinx.websupport.comments import StorageBackend from sphinx.websupport.comments.db import Base, Node, Comment, CommentVote,\ Session @@ -37,51 +38,89 @@ class SQLAlchemyStorage(StorageBackend): self.build_session.close() def add_comment(self, text, displayed, username, rating, time, - proposal, node, parent): + proposal, node_id, parent_id, moderator): session = Session() - if node: - node = session.query(Node).filter(Node.id == node).first() - parent = None - else: - node = None - parent = session.query(Comment).filter( - Comment.id == parent).first() - if node and proposal: + if node_id and proposal: differ = CombinedHtmlDiff() proposal_diff = differ.make_html(node.source, proposal) else: proposal_diff = None comment = Comment(text, displayed, username, rating, - time or datetime.now(), proposal, proposal_diff, - node, parent) + time or datetime.now(), proposal, proposal_diff) session.add(comment) + session.flush() + comment.set_path(node_id, parent_id) session.commit() comment = comment.serializable() session.close() return comment - def get_comments(self, node_id, user_id): + def get_comments(self, node_id, username, moderator): session = Session() - node = session.query(Node).filter(Node.id == node_id).first() - data = {'source': node.source, - 'comments': [comment.serializable(user_id) - for comment in node.comments]} + node = session.query(Node).filter(Node.id == node_id).one() session.close() - return data + comments = self._serializable_list(node_id, username, moderator) + return {'source': node.source, + 'comments': comments} - def process_vote(self, comment_id, user_id, value): + def _serializable_list(self, node_id, username, moderator): + session = Session() + + if username: + # If a username is provided, create a subquery to retrieve all + # votes by this user. We will outerjoin with the comment query + # with this subquery so we have a user's voting information. + sq = session.query(CommentVote).\ + filter(CommentVote.username == username).subquery() + cvalias = aliased(CommentVote, sq) + q = session.query(Comment, cvalias.value).outerjoin(cvalias) + else: + q = session.query(Comment) + + # Filter out all comments not descending from this node. + q = q.filter(Comment.path.like(node_id + '.%')) + # Filter out non-displayed comments if this isn't a moderator. + if not moderator: + q = q.filter(Comment.displayed == True) + # Retrieve all results. Results must be ordered by Comment.path + # so that we can easily transform them from a flat list to a tree. + results = q.order_by(Comment.path).all() + session.close() + + # We now need to convert the flat list of results to a nested + # lists to form the comment tree. Results will by ordered by + # the materialized path. + comments = [] + list_stack = [comments] + for r in results: + comment, vote = r if username else (r, 0) + + inheritance_chain = comment.path.split('.')[1:] + + if len(inheritance_chain) == len(list_stack) + 1: + parent = list_stack[-1][-1] + list_stack.append(parent['children']) + elif len(inheritance_chain) < len(list_stack): + while len(inheritance_chain) < len(list_stack): + list_stack.pop() + + list_stack[-1].append(comment.serializable(vote=vote)) + + return comments + + def process_vote(self, comment_id, username, value): session = Session() vote = session.query(CommentVote).filter( CommentVote.comment_id == comment_id).filter( - CommentVote.user_id == user_id).first() + CommentVote.username == username).first() comment = session.query(Comment).filter( Comment.id == comment_id).first() if vote is None: - vote = CommentVote(comment_id, user_id, value) + vote = CommentVote(comment_id, username, value) comment.rating += value else: comment.rating += value - vote.value diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 53307fb4e..f92dbcdee 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -36,6 +36,7 @@ def clear_builddir(): def teardown_module(): + (test_root / 'generated').rmtree(True) clear_builddir() @@ -130,13 +131,61 @@ def test_whoosh(): @with_support() def test_comments(support): session = Session() - node = session.query(Node).first() - comment = support.add_comment('First test comment', node=str(node.id)) - support.add_comment('Child test comment', parent=str(comment['id'])) - data = support.get_comments(str(node.id)) + nodes = session.query(Node).all() + first_node = nodes[0] + second_node = nodes[1] + + # Create a displayed comment and a non displayed comment. + comment = support.add_comment('First test comment', + node_id=str(first_node.id)) + support.add_comment('Hidden comment', node_id=str(first_node.id), + displayed=False) + # Add a displayed and not displayed child to the displayed comment. + support.add_comment('Child test comment', parent_id=str(comment['id'])) + support.add_comment('Hidden child test comment', + parent_id=str(comment['id']), displayed=False) + # Add a comment to another node to make sure it isn't returned later. + support.add_comment('Second test comment', + node_id=str(second_node.id)) + + # Access the comments as a moderator. + data = support.get_comments(str(first_node.id), moderator=True) + comments = data['comments'] + children = comments[0]['children'] + assert len(comments) == 2 + assert comments[1]['text'] == 'Hidden comment' + assert len(children) == 2 + assert children[1]['text'] == 'Hidden child test comment' + + # Access the comments without being a moderator. + data = support.get_comments(str(first_node.id)) comments = data['comments'] children = comments[0]['children'] assert len(comments) == 1 assert comments[0]['text'] == 'First test comment' assert len(children) == 1 assert children[0]['text'] == 'Child test comment' + + def check_rating(val): + data = support.get_comments(str(first_node.id)) + comment = data['comments'][0] + assert comment['rating'] == val, '%s != %s' % (comment['rating'], val) + + support.process_vote(comment['id'], 'user_one', '1') + support.process_vote(comment['id'], 'user_two', '1') + support.process_vote(comment['id'], 'user_three', '1') + check_rating(3) + support.process_vote(comment['id'], 'user_one', '-1') + check_rating(1) + support.process_vote(comment['id'], 'user_one', '0') + check_rating(2) + + # Make sure a vote with value > 1 or < -1 can't be cast. + raises(ValueError, support.process_vote, comment['id'], 'user_one', '2') + raises(ValueError, support.process_vote, comment['id'], 'user_one', '-2') + + # Make sure past voting data is associated with comments when they are + # fetched. + data = support.get_comments(str(first_node.id), username='user_two') + comment = data['comments'][0] + assert comment['vote'] == 1, '%s != 1' % comment['vote'] From 248c01af3ec09ce34bb39ef43605c2ce37896bd8 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Wed, 4 Aug 2010 11:25:30 -0500 Subject: [PATCH 234/744] Separate search adapter tests from others --- tests/test_searchadapters.py | 82 ++++++++++++++++++++++++++++++++++++ tests/test_websupport.py | 74 +++----------------------------- 2 files changed, 88 insertions(+), 68 deletions(-) create mode 100644 tests/test_searchadapters.py diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py new file mode 100644 index 000000000..94f72cab4 --- /dev/null +++ b/tests/test_searchadapters.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" + test_searchadapters + ~~~~~~~~~~~~~~~~~~~ + + Test the Web Support Package search adapters. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +from StringIO import StringIO + +from util import * +from sphinx.websupport import WebSupport + + +def clear_builddir(): + (test_root / 'websupport').rmtree(True) + + +def teardown_module(): + (test_root / 'generated').rmtree(True) + clear_builddir() + + +def search_adapter_helper(adapter): + clear_builddir() + + settings = {'outdir': os.path.join(test_root, 'websupport'), + 'status': StringIO(), + 'warning': StringIO()} + settings.update({'srcdir': test_root, + 'search': adapter}) + support = WebSupport(**settings) + support.build() + + s = support.search + + # Test the adapters query method. A search for "Epigraph" should return + # one result. + results = s.query(u'Epigraph') + assert len(results) == 1, \ + '%s search adapter returned %s search result(s), should have been 1'\ + % (adapter, len(results)) + + # Make sure documents are properly updated by the search adapter. + s.init_indexing(changed=['markup']) + s.add_document(u'markup', u'title', u'SomeLongRandomWord') + s.finish_indexing() + # Now a search for "Epigraph" should return zero results. + results = s.query(u'Epigraph') + assert len(results) == 0, \ + '%s search adapter returned %s search result(s), should have been 0'\ + % (adapter, len(results)) + # A search for "SomeLongRandomWord" should return one result. + results = s.query(u'SomeLongRandomWord') + assert len(results) == 1, \ + '%s search adapter returned %s search result(s), should have been 1'\ + % (adapter, len(results)) + + +def test_xapian(): + # Don't run tests if xapian is not installed. + try: + import xapian + search_adapter_helper('xapian') + except ImportError: + sys.stderr.write('info: not running xapian tests, ' \ + 'xapian doesn\'t seem to be installed') + + +def test_whoosh(): + # Don't run tests if whoosh is not installed. + try: + import whoosh + search_adapter_helper('whoosh') + except ImportError: + sys.stderr.write('info: not running whoosh tests, ' \ + 'whoosh doesn\'t seem to be installed') + diff --git a/tests/test_websupport.py b/tests/test_websupport.py index f92dbcdee..5e0326047 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -9,13 +9,14 @@ :license: BSD, see LICENSE for details. """ -import os, sys +import os from StringIO import StringIO from sphinx.websupport import WebSupport from sphinx.websupport.errors import * from sphinx.websupport.comments.sqlalchemystorage import Session from sphinx.websupport.comments.db import Node +from util import * try: from functools import wraps @@ -23,26 +24,17 @@ except ImportError: # functools is new in 2.4 wraps = lambda f: (lambda w: w) -from util import * - - -default_settings = {'outdir': os.path.join(test_root, 'websupport'), - 'status': StringIO(), - 'warning': StringIO()} - - -def clear_builddir(): - (test_root / 'websupport').rmtree(True) - def teardown_module(): (test_root / 'generated').rmtree(True) - clear_builddir() + (test_root / 'websupport').rmtree(True) def with_support(*args, **kwargs): """Make a WebSupport object and pass it the test.""" - settings = default_settings.copy() + settings = {'outdir': os.path.join(test_root, 'websupport'), + 'status': StringIO(), + 'warning': StringIO()} settings.update(kwargs) def generator(func): @@ -74,60 +66,6 @@ def test_get_document(support): and contents['sidebar'] and contents['relbar'] -def search_adapter_helper(adapter): - clear_builddir() - - settings = default_settings.copy() - settings.update({'srcdir': test_root, - 'search': adapter}) - support = WebSupport(**settings) - support.build() - - s = support.search - - # Test the adapters query method. A search for "Epigraph" should return - # one result. - results = s.query(u'Epigraph') - assert len(results) == 1, \ - '%s search adapter returned %s search result(s), should have been 1'\ - % (adapter, len(results)) - - # Make sure documents are properly updated by the search adapter. - s.init_indexing(changed=['markup']) - s.add_document(u'markup', u'title', u'SomeLongRandomWord') - s.finish_indexing() - # Now a search for "Epigraph" should return zero results. - results = s.query(u'Epigraph') - assert len(results) == 0, \ - '%s search adapter returned %s search result(s), should have been 0'\ - % (adapter, len(results)) - # A search for "SomeLongRandomWord" should return one result. - results = s.query(u'SomeLongRandomWord') - assert len(results) == 1, \ - '%s search adapter returned %s search result(s), should have been 1'\ - % (adapter, len(results)) - - -def test_xapian(): - # Don't run tests if xapian is not installed. - try: - import xapian - search_adapter_helper('xapian') - except ImportError: - sys.stderr.write('info: not running xapian tests, ' \ - 'xapian doesn\'t seem to be installed') - - -def test_whoosh(): - # Don't run tests if whoosh is not installed. - try: - import whoosh - search_adapter_helper('whoosh') - except ImportError: - sys.stderr.write('info: not running whoosh tests, ' \ - 'whoosh doesn\'t seem to be installed') - - @with_support() def test_comments(support): session = Session() From 75ae087f7aece6b81c20a37641b363dc23ad5e83 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Wed, 4 Aug 2010 13:09:07 -0500 Subject: [PATCH 235/744] added more test coverage. --- sphinx/websupport/comments/differ.py | 4 +- .../websupport/comments/sqlalchemystorage.py | 1 + tests/test_searchadapters.py | 4 +- tests/test_websupport.py | 48 ++++++++++++++++--- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/sphinx/websupport/comments/differ.py b/sphinx/websupport/comments/differ.py index 786922346..2ecacea58 100644 --- a/sphinx/websupport/comments/differ.py +++ b/sphinx/websupport/comments/differ.py @@ -21,7 +21,7 @@ class CombinedHtmlDiff(object): next = next[2:] new_text = [] start = 0 - for match in highlight_regex.finditer(next): + for match in self.highlight_regex.finditer(next): new_text.append(text[start:match.start()]) new_text.append('<%s>' % tag) new_text.append(text[match.start():match.end()]) @@ -30,7 +30,7 @@ class CombinedHtmlDiff(object): new_text.append(text[start:]) return ''.join(new_text) - def _handle_line(line, next=None): + def _handle_line(self, line, next=None): prefix = line[0] text = line[2:] diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 0e11c4a09..63db1550e 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -42,6 +42,7 @@ class SQLAlchemyStorage(StorageBackend): session = Session() if node_id and proposal: + node = session.query(Node).filter(Node.id == node_id).one() differ = CombinedHtmlDiff() proposal_diff = differ.make_html(node.source, proposal) else: diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py index 94f72cab4..186b2e429 100644 --- a/tests/test_searchadapters.py +++ b/tests/test_searchadapters.py @@ -59,7 +59,9 @@ def search_adapter_helper(adapter): assert len(results) == 1, \ '%s search adapter returned %s search result(s), should have been 1'\ % (adapter, len(results)) - + # Make sure it works through the WebSupport API + html = support.get_search_results(u'SomeLongRandomWord') + def test_xapian(): # Don't run tests if xapian is not installed. diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 5e0326047..464c8c74b 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -14,7 +14,9 @@ from StringIO import StringIO from sphinx.websupport import WebSupport from sphinx.websupport.errors import * -from sphinx.websupport.comments.sqlalchemystorage import Session +from sphinx.websupport.comments.differ import CombinedHtmlDiff +from sphinx.websupport.comments.sqlalchemystorage import Session, \ + SQLAlchemyStorage from sphinx.websupport.comments.db import Node from util import * @@ -25,6 +27,10 @@ except ImportError: wraps = lambda f: (lambda w: w) +default_settings = {'outdir': os.path.join(test_root, 'websupport'), + 'status': StringIO(), + 'warning': StringIO()} + def teardown_module(): (test_root / 'generated').rmtree(True) (test_root / 'websupport').rmtree(True) @@ -32,9 +38,7 @@ def teardown_module(): def with_support(*args, **kwargs): """Make a WebSupport object and pass it the test.""" - settings = {'outdir': os.path.join(test_root, 'websupport'), - 'status': StringIO(), - 'warning': StringIO()} + settings = default_settings.copy() settings.update(kwargs) def generator(func): @@ -104,8 +108,17 @@ def test_comments(support): assert len(children) == 1 assert children[0]['text'] == 'Child test comment' + +@with_support() +def test_voting(support): + session = Session() + nodes = session.query(Node).all() + node = nodes[0] + + comment = support.get_comments(str(node.id))['comments'][0] + def check_rating(val): - data = support.get_comments(str(first_node.id)) + data = support.get_comments(str(node.id)) comment = data['comments'][0] assert comment['rating'] == val, '%s != %s' % (comment['rating'], val) @@ -124,6 +137,29 @@ def test_comments(support): # Make sure past voting data is associated with comments when they are # fetched. - data = support.get_comments(str(first_node.id), username='user_two') + data = support.get_comments(str(node.id), username='user_two') comment = data['comments'][0] assert comment['vote'] == 1, '%s != 1' % comment['vote'] + +@with_support() +def test_proposals(support): + session = Session() + nodes = session.query(Node).all() + node = nodes[0] + + data = support.get_comments(str(node.id)) + + source = data['source'] + proposal = source[:5] + source[10:15] + 'asdf' + source[15:] + + comment = support.add_comment('Proposal comment', + node_id=str(node.id), + proposal=proposal) + +def test_differ(): + differ = CombinedHtmlDiff() + source = 'Lorem ipsum dolor sit amet,\nconsectetur adipisicing elit,\n' \ + 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' + prop = 'Lorem dolor sit amet,\nconsectetur nihil adipisicing elit,\n' \ + 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' + differ.make_html(source, prop) From 338c349dd526d1a51eef4f6aaeacb09c333eee80 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Wed, 4 Aug 2010 13:12:57 -0500 Subject: [PATCH 236/744] renamed get_comments get_data --- sphinx/websupport/__init__.py | 4 ++-- sphinx/websupport/comments/sqlalchemystorage.py | 2 +- tests/test_websupport.py | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 373622eb1..8bc2a0b8b 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -157,7 +157,7 @@ class WebSupport(object): document['title'] = 'Search Results' return document - def get_comments(self, node_id, username=None, moderator=False): + def get_data(self, node_id, username=None, moderator=False): """Get the comments and source associated with `node_id`. If `user_id` is given vote information will be included with the returned comments. The default CommentBackend returns dict with @@ -191,7 +191,7 @@ class WebSupport(object): :param node_id: the id of the node to get comments for. :param user_id: the id of the user viewing the comments. """ - return self.storage.get_comments(node_id, username, moderator) + return self.storage.get_data(node_id, username, moderator) def add_comment(self, text, node_id='', parent_id='', displayed=True, username=None, rating=0, time=None, proposal=None, diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/comments/sqlalchemystorage.py index 63db1550e..085913fdf 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/comments/sqlalchemystorage.py @@ -58,7 +58,7 @@ class SQLAlchemyStorage(StorageBackend): session.close() return comment - def get_comments(self, node_id, username, moderator): + def get_data(self, node_id, username, moderator): session = Session() node = session.query(Node).filter(Node.id == node_id).one() session.close() diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 464c8c74b..8f701cd2a 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -91,7 +91,7 @@ def test_comments(support): node_id=str(second_node.id)) # Access the comments as a moderator. - data = support.get_comments(str(first_node.id), moderator=True) + data = support.get_data(str(first_node.id), moderator=True) comments = data['comments'] children = comments[0]['children'] assert len(comments) == 2 @@ -100,7 +100,7 @@ def test_comments(support): assert children[1]['text'] == 'Hidden child test comment' # Access the comments without being a moderator. - data = support.get_comments(str(first_node.id)) + data = support.get_data(str(first_node.id)) comments = data['comments'] children = comments[0]['children'] assert len(comments) == 1 @@ -115,10 +115,10 @@ def test_voting(support): nodes = session.query(Node).all() node = nodes[0] - comment = support.get_comments(str(node.id))['comments'][0] + comment = support.get_data(str(node.id))['comments'][0] def check_rating(val): - data = support.get_comments(str(node.id)) + data = support.get_data(str(node.id)) comment = data['comments'][0] assert comment['rating'] == val, '%s != %s' % (comment['rating'], val) @@ -137,7 +137,7 @@ def test_voting(support): # Make sure past voting data is associated with comments when they are # fetched. - data = support.get_comments(str(node.id), username='user_two') + data = support.get_data(str(node.id), username='user_two') comment = data['comments'][0] assert comment['vote'] == 1, '%s != 1' % comment['vote'] @@ -147,7 +147,7 @@ def test_proposals(support): nodes = session.query(Node).all() node = nodes[0] - data = support.get_comments(str(node.id)) + data = support.get_data(str(node.id)) source = data['source'] proposal = source[:5] + source[10:15] + 'asdf' + source[15:] From a08651e6d0fe539e1bed554cfec3c6c545c5357e Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Wed, 4 Aug 2010 13:20:43 -0500 Subject: [PATCH 237/744] rename comments package storage --- sphinx/websupport/__init__.py | 4 ++-- tests/test_searchadapters.py | 2 +- tests/test_websupport.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 8bc2a0b8b..4f469a4a2 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -19,7 +19,7 @@ from jinja2 import Environment, FileSystemLoader from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch, search_adapters -from sphinx.websupport.comments import StorageBackend +from sphinx.websupport.storage import StorageBackend from sphinx.websupport.errors import * class WebSupportApp(Sphinx): @@ -54,7 +54,7 @@ class WebSupport(object): else: # If a StorageBackend isn't provided, use the default # SQLAlchemy backend with an SQLite db. - from sphinx.websupport.comments.sqlalchemystorage \ + from sphinx.websupport.storage.sqlalchemystorage \ import SQLAlchemyStorage from sqlalchemy import create_engine db_path = path.join(self.outdir, 'db', 'websupport.db') diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py index 186b2e429..c9525f758 100644 --- a/tests/test_searchadapters.py +++ b/tests/test_searchadapters.py @@ -9,7 +9,7 @@ :license: BSD, see LICENSE for details. """ -import os +import os, sys from StringIO import StringIO from util import * diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 8f701cd2a..bfa07226f 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -14,10 +14,10 @@ from StringIO import StringIO from sphinx.websupport import WebSupport from sphinx.websupport.errors import * -from sphinx.websupport.comments.differ import CombinedHtmlDiff -from sphinx.websupport.comments.sqlalchemystorage import Session, \ +from sphinx.websupport.storage.differ import CombinedHtmlDiff +from sphinx.websupport.storage.sqlalchemystorage import Session, \ SQLAlchemyStorage -from sphinx.websupport.comments.db import Node +from sphinx.websupport.storage.db import Node from util import * try: From f58fe6eed69a40c962bac57e672ec0d41b4a714c Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Wed, 4 Aug 2010 14:28:30 -0500 Subject: [PATCH 238/744] allow custom db uris for sqlalchemybackend --- sphinx/websupport/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 4f469a4a2..b5cea0212 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -32,7 +32,6 @@ class WebSupport(object): """The main API class for the web support package. All interactions with the web support package should occur through this class. """ - def __init__(self, srcdir='', outdir='', datadir='', search=None, storage=None, status=sys.stdout, warning=sys.stderr): self.srcdir = srcdir @@ -53,13 +52,14 @@ class WebSupport(object): self.storage = storage else: # If a StorageBackend isn't provided, use the default - # SQLAlchemy backend with an SQLite db. + # SQLAlchemy backend. from sphinx.websupport.storage.sqlalchemystorage \ import SQLAlchemyStorage from sqlalchemy import create_engine db_path = path.join(self.outdir, 'db', 'websupport.db') ensuredir(path.dirname(db_path)) - engine = create_engine('sqlite:///%s' % db_path) + uri = storage or 'sqlite:///%s' % db_path + engine = create_engine(uri) self.storage = SQLAlchemyStorage(engine) def _init_templating(self): From 69a2c07396f94ec0099c24089dd452b30cc06052 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Wed, 4 Aug 2010 16:06:10 -0500 Subject: [PATCH 239/744] added delete_comment method --- sphinx/websupport/__init__.py | 19 +++++++++++++-- sphinx/websupport/errors.py | 7 +++++- tests/test_websupport.py | 46 ++++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index b5cea0212..03b9c8e8b 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -85,7 +85,7 @@ class WebSupport(object): """Build the documentation. Places the data into the `outdir` directory. Use it like this:: - support = WebSupport(srcdir, outdir, search) + support = WebSupport(srcdir, outdir, search='xapian') support.build() This will read reStructured text files from `srcdir`. Then it @@ -109,7 +109,7 @@ class WebSupport(object): """Load and return a document from a pickle. The document will be a dict object which can be used to render a template:: - support = WebSupport(outdir=outdir) + support = WebSupport(datadir=datadir) support.get_document('index') In most cases `docname` will be taken from the request path and @@ -193,6 +193,21 @@ class WebSupport(object): """ return self.storage.get_data(node_id, username, moderator) + def delete_comment(self, comment_id, username='', moderator=False): + """Delete a comment. Doesn't actually delete the comment, but + instead replaces the username and text files with "[deleted]" so + as not to leave any comments orphaned. + + If `moderator` is True, the comment will always be deleted. If + `moderator` is False, the comment will only be deleted if the + `username` matches the `username` on the comment. + + :param comment_id: the id of the comment to delete. + :param username: the username requesting the deletion. + :param moderator: whether the requestor is a moderator. + """ + self.storage.delete_comment(comment_id, username, moderator) + def add_comment(self, text, node_id='', parent_id='', displayed=True, username=None, rating=0, time=None, proposal=None, moderator=False): diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py index b1c47915e..fbb75a93d 100644 --- a/sphinx/websupport/errors.py +++ b/sphinx/websupport/errors.py @@ -9,7 +9,8 @@ :license: BSD, see LICENSE for details. """ -__all__ = ['DocumentNotFoundError', 'SrcdirNotSpecifiedError'] +__all__ = ['DocumentNotFoundError', 'SrcdirNotSpecifiedError', + 'UserNotAuthorizedError'] class DocumentNotFoundError(Exception): pass @@ -17,3 +18,7 @@ class DocumentNotFoundError(Exception): class SrcdirNotSpecifiedError(Exception): pass + + +class UserNotAuthorizedError(Exception): + pass diff --git a/tests/test_websupport.py b/tests/test_websupport.py index bfa07226f..ca64ec2d1 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -79,11 +79,13 @@ def test_comments(support): # Create a displayed comment and a non displayed comment. comment = support.add_comment('First test comment', - node_id=str(first_node.id)) + node_id=str(first_node.id), + username='user_one') support.add_comment('Hidden comment', node_id=str(first_node.id), displayed=False) # Add a displayed and not displayed child to the displayed comment. - support.add_comment('Child test comment', parent_id=str(comment['id'])) + support.add_comment('Child test comment', parent_id=str(comment['id']), + username='user_one') support.add_comment('Hidden child test comment', parent_id=str(comment['id']), displayed=False) # Add a comment to another node to make sure it isn't returned later. @@ -144,8 +146,7 @@ def test_voting(support): @with_support() def test_proposals(support): session = Session() - nodes = session.query(Node).all() - node = nodes[0] + node = session.query(Node).first() data = support.get_data(str(node.id)) @@ -156,6 +157,43 @@ def test_proposals(support): node_id=str(node.id), proposal=proposal) + +@with_support() +def test_user_delete_comments(support): + def get_comment(): + session = Session() + node = session.query(Node).first() + session.close() + return support.get_data(str(node.id))['comments'][0] + + comment = get_comment() + assert comment['username'] == 'user_one' + # Make sure other normal users can't delete someone elses comments. + raises(UserNotAuthorizedError, support.delete_comment, + comment['id'], username='user_two') + # Now delete the comment using the correct username. + support.delete_comment(comment['id'], username='user_one') + comment = get_comment() + assert comment['username'] == '[deleted]' + assert comment['text'] == '[deleted]' + + +@with_support() +def test_moderator_delete_comments(support): + def get_comment(): + session = Session() + node = session.query(Node).first() + session.close() + return support.get_data(str(node.id), moderator=True)['comments'][1] + + comment = get_comment() + support.delete_comment(comment['id'], username='user_two', + moderator=True) + comment = get_comment() + assert comment['username'] == '[deleted]' + assert comment['text'] == '[deleted]' + + def test_differ(): differ = CombinedHtmlDiff() source = 'Lorem ipsum dolor sit amet,\nconsectetur adipisicing elit,\n' \ From 790715d37b1247a606437392a65711187885b34a Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 5 Aug 2010 12:20:15 -0500 Subject: [PATCH 240/744] added update_username method --- sphinx/websupport/__init__.py | 13 +++++++++++++ tests/test_websupport.py | 22 ++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 03b9c8e8b..f137ce2c3 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -268,3 +268,16 @@ class WebSupport(object): if not -1 <= value <= 1: raise ValueError('vote value %s out of range (-1, 1)' % value) self.storage.process_vote(comment_id, username, value) + + def update_username(self, old_username, new_username): + """To remain decoupled from a webapp's authentication system, the + web support package stores a user's username with each of their + comments and votes. If the authentication system allows a user to + change their username, this can lead to stagnate data in the web + support system. To avoid this, each time a username is changed, this + method should be called. + + :param old_username: The original username. + :param new_username: The new username. + """ + self.storage.update_username(old_username, new_username) diff --git a/tests/test_websupport.py b/tests/test_websupport.py index ca64ec2d1..d0956916d 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -16,7 +16,7 @@ from sphinx.websupport import WebSupport from sphinx.websupport.errors import * from sphinx.websupport.storage.differ import CombinedHtmlDiff from sphinx.websupport.storage.sqlalchemystorage import Session, \ - SQLAlchemyStorage + SQLAlchemyStorage, Comment, CommentVote from sphinx.websupport.storage.db import Node from util import * @@ -90,7 +90,8 @@ def test_comments(support): parent_id=str(comment['id']), displayed=False) # Add a comment to another node to make sure it isn't returned later. support.add_comment('Second test comment', - node_id=str(second_node.id)) + node_id=str(second_node.id), + username='user_two') # Access the comments as a moderator. data = support.get_data(str(first_node.id), moderator=True) @@ -193,6 +194,23 @@ def test_moderator_delete_comments(support): assert comment['username'] == '[deleted]' assert comment['text'] == '[deleted]' +@with_support() +def test_update_username(support): + support.update_username('user_two', 'new_user_two') + session = Session() + comments = session.query(Comment).\ + filter(Comment.username == 'user_two').all() + assert len(comments) == 0 + votes = session.query(CommentVote).\ + filter(CommentVote.username == 'user_two') + assert len(comments) == 0 + comments = session.query(Comment).\ + filter(Comment.username == 'new_user_two').all() + assert len(comments) == 1 + votes = session.query(CommentVote).\ + filter(CommentVote.username == 'new_user_two') + assert len(comments) == 1 + def test_differ(): differ = CombinedHtmlDiff() From d0e272e61f34364f5cf9345363d5680f041fdf8f Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 5 Aug 2010 14:13:50 -0500 Subject: [PATCH 241/744] Add comment moderation backend --- sphinx/websupport/__init__.py | 27 +++++++++++++++++++++++---- tests/test_websupport.py | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index f137ce2c3..e04e6899b 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -33,10 +33,12 @@ class WebSupport(object): with the web support package should occur through this class. """ def __init__(self, srcdir='', outdir='', datadir='', search=None, - storage=None, status=sys.stdout, warning=sys.stderr): + storage=None, status=sys.stdout, warning=sys.stderr, + moderation_callback=None): self.srcdir = srcdir self.outdir = outdir or path.join(self.srcdir, '_build', 'websupport') + self.moderation_callback = moderation_callback self._init_templating() self.outdir = outdir or datadir @@ -236,9 +238,12 @@ class WebSupport(object): :param rating: the starting rating of the comment, defaults to 0. :param time: the time the comment was created, defaults to now. """ - return self.storage.add_comment(text, displayed, username, rating, - time, proposal, node_id, parent_id, - moderator) + comment = self.storage.add_comment(text, displayed, username, rating, + time, proposal, node_id, + parent_id, moderator) + if not displayed and self.moderation_callback: + self.moderation_callback(comment) + return comment def process_vote(self, comment_id, username, value): """Process a user's vote. The web support package relies @@ -281,3 +286,17 @@ class WebSupport(object): :param new_username: The new username. """ self.storage.update_username(old_username, new_username) + + def accept_comment(self, comment_id): + """Accept a comment that is pending moderation. + + :param comment_id: The id of the comment that was accepted. + """ + self.storage.accept_comment(comment_id) + + def reject_comment(self, comment_id): + """Reject a comment that is pending moderation. + + :param comment_id: The id of the comment that was accepted. + """ + self.storage.reject_comment(comment_id) diff --git a/tests/test_websupport.py b/tests/test_websupport.py index d0956916d..e9c68cf63 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -194,6 +194,7 @@ def test_moderator_delete_comments(support): assert comment['username'] == '[deleted]' assert comment['text'] == '[deleted]' + @with_support() def test_update_username(support): support.update_username('user_two', 'new_user_two') @@ -212,6 +213,28 @@ def test_update_username(support): assert len(comments) == 1 +called = False +def moderation_callback(comment): + global called + called = True + + +@with_support(moderation_callback=moderation_callback) +def test_moderation(support): + accepted = support.add_comment('Accepted Comment', node_id=3, + displayed=False) + rejected = support.add_comment('Rejected comment', node_id=3, + displayed=False) + # Make sure the moderation_callback is called. + assert called == True + support.accept_comment(accepted['id']) + support.reject_comment(rejected['id']) + comments = support.get_data(3)['comments'] + assert len(comments) == 1 + comments = support.get_data(3, moderator=True)['comments'] + assert len(comments) == 1 + + def test_differ(): differ = CombinedHtmlDiff() source = 'Lorem ipsum dolor sit amet,\nconsectetur adipisicing elit,\n' \ From fbd047b8f593a439d0a8ca9eec02ad2491d2bd78 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 5 Aug 2010 15:01:13 -0500 Subject: [PATCH 242/744] Ensure hidden comments can't be replied to --- sphinx/websupport/errors.py | 6 +++++- tests/test_websupport.py | 11 +++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py index fbb75a93d..e78abc217 100644 --- a/sphinx/websupport/errors.py +++ b/sphinx/websupport/errors.py @@ -10,7 +10,7 @@ """ __all__ = ['DocumentNotFoundError', 'SrcdirNotSpecifiedError', - 'UserNotAuthorizedError'] + 'UserNotAuthorizedError', 'CommentNotAllowedError'] class DocumentNotFoundError(Exception): pass @@ -22,3 +22,7 @@ class SrcdirNotSpecifiedError(Exception): class UserNotAuthorizedError(Exception): pass + + +class CommentNotAllowedError(Exception): + pass diff --git a/tests/test_websupport.py b/tests/test_websupport.py index e9c68cf63..2db29a2e1 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -81,8 +81,14 @@ def test_comments(support): comment = support.add_comment('First test comment', node_id=str(first_node.id), username='user_one') - support.add_comment('Hidden comment', node_id=str(first_node.id), - displayed=False) + hidden_comment = support.add_comment('Hidden comment', + node_id=str(first_node.id), + displayed=False) + # Make sure that comments can't be added to a comment where + # displayed == False, since it could break the algorithm that + # converts a nodes comments to a tree. + raises(CommentNotAllowedError, support.add_comment, 'Not allowed', + parent_id=str(hidden_comment['id'])) # Add a displayed and not displayed child to the displayed comment. support.add_comment('Child test comment', parent_id=str(comment['id']), username='user_one') @@ -144,6 +150,7 @@ def test_voting(support): comment = data['comments'][0] assert comment['vote'] == 1, '%s != 1' % comment['vote'] + @with_support() def test_proposals(support): session = Session() From d29f65112e327054dde4501a61d3c7d4fa16bcbf Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 5 Aug 2010 19:29:24 -0500 Subject: [PATCH 243/744] added storage package --- .../{comments => storage}/__init__.py | 8 +-- sphinx/websupport/{comments => storage}/db.py | 4 +- .../{comments => storage}/differ.py | 4 +- .../sqlalchemystorage.py | 61 ++++++++++++++++--- 4 files changed, 62 insertions(+), 15 deletions(-) rename sphinx/websupport/{comments => storage}/__init__.py (88%) rename sphinx/websupport/{comments => storage}/db.py (98%) rename sphinx/websupport/{comments => storage}/differ.py (96%) rename sphinx/websupport/{comments => storage}/sqlalchemystorage.py (68%) diff --git a/sphinx/websupport/comments/__init__.py b/sphinx/websupport/storage/__init__.py similarity index 88% rename from sphinx/websupport/comments/__init__.py rename to sphinx/websupport/storage/__init__.py index 10856dffd..6948c8c73 100644 --- a/sphinx/websupport/comments/__init__.py +++ b/sphinx/websupport/storage/__init__.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ - sphinx.websupport.comments - ~~~~~~~~~~~~~~~~~~~~~~~~~~ + sphinx.websupport.storage + ~~~~~~~~~~~~~~~~~~~~~~~~~ - Comments for the websupport package. + Storage for the websupport package. :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. @@ -40,7 +40,7 @@ class StorageBackend(object): """Called when a comment is being added.""" raise NotImplementedError() - def get_comments(self, parent_id, user_id, moderator): + def get_data(self, parent_id, user_id, moderator): """Called to retrieve all comments for a node.""" raise NotImplementedError() diff --git a/sphinx/websupport/comments/db.py b/sphinx/websupport/storage/db.py similarity index 98% rename from sphinx/websupport/comments/db.py rename to sphinx/websupport/storage/db.py index 91175ed38..23b6a462c 100644 --- a/sphinx/websupport/comments/db.py +++ b/sphinx/websupport/storage/db.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - sphinx.websupport.comments.db - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + sphinx.websupport.storage.db + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SQLAlchemy table and mapper definitions used by the :class:`sphinx.websupport.comments.SQLAlchemyStorage`. diff --git a/sphinx/websupport/comments/differ.py b/sphinx/websupport/storage/differ.py similarity index 96% rename from sphinx/websupport/comments/differ.py rename to sphinx/websupport/storage/differ.py index 2ecacea58..4e5660c59 100644 --- a/sphinx/websupport/comments/differ.py +++ b/sphinx/websupport/storage/differ.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - sphinx.websupport.comments.differ - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + sphinx.websupport.storage.differ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A differ for creating an HTML representations of proposal diffs diff --git a/sphinx/websupport/comments/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py similarity index 68% rename from sphinx/websupport/comments/sqlalchemystorage.py rename to sphinx/websupport/storage/sqlalchemystorage.py index 085913fdf..e96f38cf0 100644 --- a/sphinx/websupport/comments/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ - sphinx.websupport.comments.sqlalchemystorage - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + sphinx.websupport.storage.sqlalchemystorage + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - A SQLAlchemy storage backend. + An SQLAlchemy storage backend. :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. @@ -12,10 +12,11 @@ from datetime import datetime from sqlalchemy.orm import aliased -from sphinx.websupport.comments import StorageBackend -from sphinx.websupport.comments.db import Base, Node, Comment, CommentVote,\ +from sphinx.websupport.errors import * +from sphinx.websupport.storage import StorageBackend +from sphinx.websupport.storage.db import Base, Node, Comment, CommentVote,\ Session -from sphinx.websupport.comments.differ import CombinedHtmlDiff +from sphinx.websupport.storage.differ import CombinedHtmlDiff class SQLAlchemyStorage(StorageBackend): def __init__(self, engine): @@ -45,6 +46,13 @@ class SQLAlchemyStorage(StorageBackend): node = session.query(Node).filter(Node.id == node_id).one() differ = CombinedHtmlDiff() proposal_diff = differ.make_html(node.source, proposal) + elif parent_id: + parent = session.query(Comment.displayed).\ + filter(Comment.id == parent_id).one() + if not parent.displayed: + raise CommentNotAllowedError( + "Can't add child to a parent that is not displayed") + proposal_diff = None else: proposal_diff = None @@ -58,6 +66,19 @@ class SQLAlchemyStorage(StorageBackend): session.close() return comment + def delete_comment(self, comment_id, username, moderator): + session = Session() + comment = session.query(Comment).\ + filter(Comment.id == comment_id).one() + if moderator or comment.username == username: + comment.username = '[deleted]' + comment.text = '[deleted]' + session.commit() + session.close() + else: + session.close() + raise UserNotAuthorizedError() + def get_data(self, node_id, username, moderator): session = Session() node = session.query(Node).filter(Node.id == node_id).one() @@ -81,7 +102,7 @@ class SQLAlchemyStorage(StorageBackend): q = session.query(Comment) # Filter out all comments not descending from this node. - q = q.filter(Comment.path.like(node_id + '.%')) + q = q.filter(Comment.path.like(str(node_id) + '.%')) # Filter out non-displayed comments if this isn't a moderator. if not moderator: q = q.filter(Comment.displayed == True) @@ -129,3 +150,29 @@ class SQLAlchemyStorage(StorageBackend): session.add(vote) session.commit() session.close() + + def update_username(self, old_username, new_username): + session = Session() + session.query(Comment).filter(Comment.username == old_username).\ + update({Comment.username: new_username}) + session.query(CommentVote).\ + filter(CommentVote.username == old_username).\ + update({CommentVote.username: new_username}) + session.commit() + session.close() + + def accept_comment(self, comment_id): + session = Session() + comment = session.query(Comment).\ + filter(Comment.id == comment_id).one() + comment.displayed = True + session.commit() + session.close() + + def reject_comment(self, comment_id): + session = Session() + comment = session.query(Comment).\ + filter(Comment.id == comment_id).one() + session.delete(comment) + session.commit() + session.close() From e227abe1df51d701e00d3f55c08c954a3b342e5b Mon Sep 17 00:00:00 2001 From: Ali Afshar <aafshar@gmail.com> Date: Fri, 6 Aug 2010 11:35:48 +0100 Subject: [PATCH 244/744] Make the dot command part of the caching system for dot output generation --- sphinx/ext/graphviz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 106de7a65..257ff1b63 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -93,6 +93,7 @@ def render_dot(self, code, options, format, prefix='graphviz'): Render graphviz code into a PNG or PDF output file. """ hashkey = code.encode('utf-8') + str(options) + \ + str(self.builder.config.graphviz_dot) + \ str(self.builder.config.graphviz_dot_args) fname = '%s-%s.%s' % (prefix, sha(hashkey).hexdigest(), format) if hasattr(self.builder, 'imgpath'): From ad9ebe38cc7f478349dfb5594c341dbcf0833507 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 6 Aug 2010 14:07:12 -0500 Subject: [PATCH 245/744] use static paths for static files and resources --- sphinx/builders/websupport.py | 37 +++++++++++++++++++++++++++++------ sphinx/websupport/__init__.py | 17 ++++++++-------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 40901eef9..eeadfc231 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -11,6 +11,9 @@ import cPickle as pickle from os import path +import posixpath +import shutil +from docutils.io import StringOutput from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile from sphinx.builders.html import StandaloneHTMLBuilder @@ -29,7 +32,21 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def write_doc(self, docname, doctree): # The translator needs the docname to generate ids. self.cur_docname = docname - StandaloneHTMLBuilder.write_doc(self, docname, doctree) + destination = StringOutput(encoding='utf-8') + doctree.settings = self.docsettings + + self.secnumbers = self.env.toc_secnumbers.get(docname, {}) + self.imgpath = '/' + posixpath.join(self.app.staticdir, '_images') + self.post_process_images(doctree) + self.dlpath = '/' + posixpath.join(self.app.staticdir, '_downloads') + self.docwriter.write(doctree, destination) + self.docwriter.assemble_parts() + body = self.docwriter.parts['fragment'] + metatags = self.docwriter.clean_meta + + ctx = self.get_doc_context(docname, body, metatags) + self.index_page(docname, doctree, ctx.get('title', '')) + self.handle_page(docname, ctx, event_arg=doctree) def get_target_uri(self, docname, typ=None): return docname @@ -50,8 +67,9 @@ class WebSupportBuilder(StandaloneHTMLBuilder): baseuri=self.get_target_uri(pagename)): if not resource: otheruri = self.get_target_uri(otheruri) - uri = relative_uri(baseuri, otheruri) or '#' - return uri + return relative_uri(baseuri, otheruri) or '#' + else: + return '/' + posixpath.join(self.app.staticdir, otheruri) ctx['pathto'] = pathto ctx['hasdoc'] = lambda name: name in self.env.all_docs ctx['encoding'] = encoding = self.config.html_output_encoding @@ -74,7 +92,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): doc_ctx['relbar'] = template_module.relbar() if not outfilename: - outfilename = path.join(self.outdir, + outfilename = path.join(self.outdir, 'pickles', os_path(pagename) + self.out_suffix) ensuredir(path.dirname(outfilename)) @@ -87,10 +105,17 @@ class WebSupportBuilder(StandaloneHTMLBuilder): # if there is a source file, copy the source file for the # "show source" link if ctx.get('sourcename'): - source_name = path.join(self.outdir, '_sources', - os_path(ctx['sourcename'])) + source_name = path.join(self.outdir, self.app.staticdir, + '_sources', os_path(ctx['sourcename'])) ensuredir(path.dirname(source_name)) copyfile(self.env.doc2path(pagename), source_name) + def handle_finish(self): + StandaloneHTMLBuilder.handle_finish(self) + shutil.move(path.join(self.outdir, '_images'), + path.join(self.outdir, self.app.staticdir, '_images')) + shutil.move(path.join(self.outdir, '_static'), + path.join(self.outdir, self.app.staticdir, '_static')) + def dump_search_index(self): self.indexer.finish_indexing() diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index e04e6899b..29a0b0eee 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -24,6 +24,7 @@ from sphinx.websupport.errors import * class WebSupportApp(Sphinx): def __init__(self, *args, **kwargs): + self.staticdir = kwargs.pop('staticdir', None) self.search = kwargs.pop('search', None) self.storage = kwargs.pop('storage', None) Sphinx.__init__(self, *args, **kwargs) @@ -34,18 +35,15 @@ class WebSupport(object): """ def __init__(self, srcdir='', outdir='', datadir='', search=None, storage=None, status=sys.stdout, warning=sys.stderr, - moderation_callback=None): + moderation_callback=None, staticdir='static'): self.srcdir = srcdir - self.outdir = outdir or path.join(self.srcdir, '_build', - 'websupport') - self.moderation_callback = moderation_callback - self._init_templating() - self.outdir = outdir or datadir - + self.staticdir = staticdir.strip('/') self.status = status self.warning = warning + self.moderation_callback = moderation_callback + self._init_templating() self._init_search(search) self._init_storage(storage) @@ -101,7 +99,8 @@ class WebSupport(object): app = WebSupportApp(self.srcdir, self.srcdir, self.outdir, doctreedir, 'websupport', search=self.search, status=self.status, - warning=self.warning, storage=self.storage) + warning=self.warning, storage=self.storage, + staticdir=self.staticdir) self.storage.pre_build() app.build() @@ -129,7 +128,7 @@ class WebSupport(object): :param docname: the name of the document to load. """ - infilename = path.join(self.outdir, docname + '.fpickle') + infilename = path.join(self.outdir, 'pickles', docname + '.fpickle') try: f = open(infilename, 'rb') From bfbbbe5851c71706f79d823696648f96ff88af40 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 6 Aug 2010 15:07:59 -0500 Subject: [PATCH 246/744] moved _serializable_list into db.py --- sphinx/builders/websupport.py | 3 +- sphinx/websupport/storage/db.py | 53 +++++++++++++++++-- .../websupport/storage/sqlalchemystorage.py | 48 +---------------- 3 files changed, 51 insertions(+), 53 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index eeadfc231..095bd5556 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -30,11 +30,10 @@ class WebSupportBuilder(StandaloneHTMLBuilder): self.translator_class = WebSupportTranslator def write_doc(self, docname, doctree): - # The translator needs the docname to generate ids. - self.cur_docname = docname destination = StringOutput(encoding='utf-8') doctree.settings = self.docsettings + self.cur_docname = docname self.secnumbers = self.env.toc_secnumbers.get(docname, {}) self.imgpath = '/' + posixpath.join(self.app.staticdir, '_images') self.post_process_images(doctree) diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index 23b6a462c..568558a3d 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -16,7 +16,7 @@ from sqlalchemy import Column, Integer, Text, String, Boolean, ForeignKey,\ DateTime from sqlalchemy.schema import UniqueConstraint from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relation, sessionmaker +from sqlalchemy.orm import relation, sessionmaker, aliased Base = declarative_base() @@ -33,6 +33,54 @@ class Node(Base): line = Column(Integer) source = Column(Text, nullable=False) + def nested_comments(self, username, moderator): + session = Session() + + if username: + # If a username is provided, create a subquery to retrieve all + # votes by this user. We will outerjoin with the comment query + # with this subquery so we have a user's voting information. + sq = session.query(CommentVote).\ + filter(CommentVote.username == username).subquery() + cvalias = aliased(CommentVote, sq) + q = session.query(Comment, cvalias.value).outerjoin(cvalias) + else: + q = session.query(Comment) + + # Filter out all comments not descending from this node. + q = q.filter(Comment.path.like(str(self.id) + '.%')) + # Filter out non-displayed comments if this isn't a moderator. + if not moderator: + q = q.filter(Comment.displayed == True) + # Retrieve all results. Results must be ordered by Comment.path + # so that we can easily transform them from a flat list to a tree. + results = q.order_by(Comment.path).all() + session.close() + + # We now need to convert the flat list of results to a nested + # lists to form the comment tree. Results will by ordered by + # the materialized path. + return self._nest_comments(results, username) + + def _nest_comments(self, results, username): + comments = [] + list_stack = [comments] + for r in results: + comment, vote = r if username else (r, 0) + + inheritance_chain = comment.path.split('.')[1:] + + if len(inheritance_chain) == len(list_stack) + 1: + parent = list_stack[-1][-1] + list_stack.append(parent['children']) + elif len(inheritance_chain) < len(list_stack): + while len(inheritance_chain) < len(list_stack): + list_stack.pop() + + list_stack[-1].append(comment.serializable(vote=vote)) + + return comments + def __init__(self, document, line, source, treeloc): self.document = document self.line = line @@ -52,9 +100,6 @@ class Comment(Base): proposal_diff = Column(Text) path = Column(String(256), index=True) - #node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) - #node = relation(Node, backref='comments') - def __init__(self, text, displayed, username, rating, time, proposal, proposal_diff): self.text = text diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index e96f38cf0..02fa33b50 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -11,7 +11,6 @@ from datetime import datetime -from sqlalchemy.orm import aliased from sphinx.websupport.errors import * from sphinx.websupport.storage import StorageBackend from sphinx.websupport.storage.db import Base, Node, Comment, CommentVote,\ @@ -83,55 +82,10 @@ class SQLAlchemyStorage(StorageBackend): session = Session() node = session.query(Node).filter(Node.id == node_id).one() session.close() - comments = self._serializable_list(node_id, username, moderator) + comments = node.nested_comments(username, moderator) return {'source': node.source, 'comments': comments} - def _serializable_list(self, node_id, username, moderator): - session = Session() - - if username: - # If a username is provided, create a subquery to retrieve all - # votes by this user. We will outerjoin with the comment query - # with this subquery so we have a user's voting information. - sq = session.query(CommentVote).\ - filter(CommentVote.username == username).subquery() - cvalias = aliased(CommentVote, sq) - q = session.query(Comment, cvalias.value).outerjoin(cvalias) - else: - q = session.query(Comment) - - # Filter out all comments not descending from this node. - q = q.filter(Comment.path.like(str(node_id) + '.%')) - # Filter out non-displayed comments if this isn't a moderator. - if not moderator: - q = q.filter(Comment.displayed == True) - # Retrieve all results. Results must be ordered by Comment.path - # so that we can easily transform them from a flat list to a tree. - results = q.order_by(Comment.path).all() - session.close() - - # We now need to convert the flat list of results to a nested - # lists to form the comment tree. Results will by ordered by - # the materialized path. - comments = [] - list_stack = [comments] - for r in results: - comment, vote = r if username else (r, 0) - - inheritance_chain = comment.path.split('.')[1:] - - if len(inheritance_chain) == len(list_stack) + 1: - parent = list_stack[-1][-1] - list_stack.append(parent['children']) - elif len(inheritance_chain) < len(list_stack): - while len(inheritance_chain) < len(list_stack): - list_stack.pop() - - list_stack[-1].append(comment.serializable(vote=vote)) - - return comments - def process_vote(self, comment_id, username, value): session = Session() vote = session.query(CommentVote).filter( From 307f255407ad5ff642eac510ca164928d0dbb61f Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 6 Aug 2010 16:41:10 -0500 Subject: [PATCH 247/744] add node or parent id to serializable comment --- sphinx/websupport/storage/db.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index 568558a3d..ed2b3b11c 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -132,9 +132,15 @@ class Comment(Base): 'iso': self.time.isoformat(), 'delta': self.pretty_delta(delta)} + path = self.path.split('.') + node = path[0] if len(path) == 2 else None + parent = path[-2] if len(path) > 2 else None + return {'text': self.text, 'username': self.username or 'Anonymous', 'id': self.id, + 'node': node, + 'parent': parent, 'rating': self.rating, 'age': delta.seconds, 'time': time, From 63e96b2fd5d127a372a891953fbfcdb64994825c Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 6 Aug 2010 17:22:05 -0500 Subject: [PATCH 248/744] check for next in differ --- sphinx/websupport/storage/differ.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py index 4e5660c59..c82ba7427 100644 --- a/sphinx/websupport/storage/differ.py +++ b/sphinx/websupport/storage/differ.py @@ -39,7 +39,7 @@ class CombinedHtmlDiff(object): elif prefix == '?': return '' - if next[0] == '?': + if next is not None and next[0] == '?': tag = 'ins' if prefix == '+' else 'del' text = self._highlight_text(text, next, tag) css_class = 'prop_added' if prefix == '+' else 'prop_removed' From 1d98a50bdc5fb6256e2c66d1384267208a15597a Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 6 Aug 2010 21:13:06 -0500 Subject: [PATCH 249/744] add moderator kwarg to moderation methods. --- sphinx/websupport/__init__.py | 8 ++++++-- sphinx/websupport/storage/db.py | 1 + tests/test_websupport.py | 7 +++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 29a0b0eee..4812bb932 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -286,16 +286,20 @@ class WebSupport(object): """ self.storage.update_username(old_username, new_username) - def accept_comment(self, comment_id): + def accept_comment(self, comment_id, moderator=False): """Accept a comment that is pending moderation. :param comment_id: The id of the comment that was accepted. """ + if not moderator: + raise UserNotAuthorizedError() self.storage.accept_comment(comment_id) - def reject_comment(self, comment_id): + def reject_comment(self, comment_id, moderator=False): """Reject a comment that is pending moderation. :param comment_id: The id of the comment that was accepted. """ + if not moderator: + raise UserNotAuthorizedError() self.storage.reject_comment(comment_id) diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index ed2b3b11c..12c1e1d5e 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -142,6 +142,7 @@ class Comment(Base): 'node': node, 'parent': parent, 'rating': self.rating, + 'displayed': self.displayed, 'age': delta.seconds, 'time': time, 'vote': vote or 0, diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 2db29a2e1..37f0a679e 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -234,8 +234,11 @@ def test_moderation(support): displayed=False) # Make sure the moderation_callback is called. assert called == True - support.accept_comment(accepted['id']) - support.reject_comment(rejected['id']) + # Make sure the user must be a moderator. + raises(UserNotAuthorizedError, support.accept_comment, accepted['id']) + raises(UserNotAuthorizedError, support.reject_comment, accepted['id']) + support.accept_comment(accepted['id'], moderator=True) + support.reject_comment(rejected['id'], moderator=True) comments = support.get_data(3)['comments'] assert len(comments) == 1 comments = support.get_data(3, moderator=True)['comments'] From 690380b9f0b446866fc35efe8279c092834a5e60 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 7 Aug 2010 11:49:38 -0500 Subject: [PATCH 250/744] add DOCUMENTATION_OPTIONS to context --- sphinx/builders/websupport.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 095bd5556..70ce5d2bc 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -11,6 +11,7 @@ import cPickle as pickle from os import path +from cgi import escape import posixpath import shutil from docutils.io import StringOutput @@ -81,7 +82,8 @@ class WebSupportBuilder(StandaloneHTMLBuilder): # Create a dict that will be pickled and used by webapps. doc_ctx = {'body': ctx.get('body', ''), - 'title': ctx.get('title', '')} + 'title': ctx.get('title', ''), + 'DOCUMENTATION_OPTIONS': self._make_doc_options(ctx)} # Partially render the html template to proved a more useful ctx. template = self.templates.environment.get_template(templatename) template_module = template.make_module(ctx) @@ -118,3 +120,17 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def dump_search_index(self): self.indexer.finish_indexing() + + def _make_doc_options(self, ctx): + t = """ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: '%s', + VERSION: '%s', + COLLAPSE_INDEX: false, + FILE_SUFFIX: '', + HAS_SOURCE: '%s' +};""" + return t % (ctx.get('url_root', ''), escape(ctx['release']), + str(ctx['has_source']).lower()) + + From c5c5a3a69369b078cd4a3cb2fcfb73a69f0966b0 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 7 Aug 2010 14:10:49 -0500 Subject: [PATCH 251/744] add COMMENT_OPTIONS to context --- sphinx/websupport/__init__.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 4812bb932..de719e36d 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -35,12 +35,14 @@ class WebSupport(object): """ def __init__(self, srcdir='', outdir='', datadir='', search=None, storage=None, status=sys.stdout, warning=sys.stderr, - moderation_callback=None, staticdir='static'): + moderation_callback=None, staticdir='static', + docroot=''): self.srcdir = srcdir self.outdir = outdir or datadir self.staticdir = staticdir.strip('/') self.status = status self.warning = warning + self.docroot = docroot.strip('/') self.moderation_callback = moderation_callback self._init_templating() @@ -106,7 +108,7 @@ class WebSupport(object): app.build() self.storage.post_build() - def get_document(self, docname): + def get_document(self, docname, username='', moderator=False): """Load and return a document from a pickle. The document will be a dict object which can be used to render a template:: @@ -137,8 +139,32 @@ class WebSupport(object): 'The document "%s" could not be found' % docname) document = pickle.load(f) + document['COMMENT_OPTIONS'] = self._make_comment_options(username, + moderator) return document + def _make_comment_options(self, username, moderator): + parts = ['var COMMENT_OPTIONS = {'] + if self.docroot is not '': + parts.append('addCommentURL: "/%s/%s",' % (self.docroot, + 'add_comment')) + parts.append('getCommentsURL: "/%s/%s",' % (self.docroot, + 'get_comments')) + parts.append('processVoteURL: "/%s/%s",' % (self.docroot, + 'process_vote')) + parts.append('acceptCommentURL: "/%s/%s",' % (self.docroot, + 'accept_comment')) + parts.append('rejectCommentURL: "/%s/%s",' % (self.docroot, + 'reject_comment')) + parts.append('deleteCommentURL: "/%s/%s",' % (self.docroot, + 'delete_comment')) + if username is not '': + parts.append('voting: true,') + parts.append('username: "%s",' % username) + parts.append('moderator: %s' % str(moderator).lower()) + parts.append('};') + return '\n'.join(parts) + def get_search_results(self, q): """Perform a search for the query `q`, and create a set of search results. Then render the search results as html and From 76fcd457b2723ec4307f92a254beabf7ab9963b1 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 7 Aug 2010 17:04:45 -0500 Subject: [PATCH 252/744] updated docs --- doc/web/api.rst | 2 +- doc/web/quickstart.rst | 173 +++++++++++------------------------- doc/web/storagebackends.rst | 12 +-- 3 files changed, 58 insertions(+), 129 deletions(-) diff --git a/doc/web/api.rst b/doc/web/api.rst index 0b86309c0..fcd0513ee 100644 --- a/doc/web/api.rst +++ b/doc/web/api.rst @@ -22,7 +22,7 @@ Methods .. automethod:: sphinx.websupport.WebSupport.get_document -.. automethod:: sphinx.websupport.WebSupport.get_comments +.. automethod:: sphinx.websupport.WebSupport.get_data .. automethod:: sphinx.websupport.WebSupport.add_comment diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst index 16c650c27..b0a60507a 100644 --- a/doc/web/quickstart.rst +++ b/doc/web/quickstart.rst @@ -24,7 +24,15 @@ class and call it's :meth:`~sphinx.websupport.WebSupport.build` method:: This will read reStructuredText sources from `srcdir` and place the necessary data in `outdir`. This directory contains all the data needed to display documents, search through documents, and add comments to -documents. +documents. It will also contain a subdirectory named "static", which +contains static files. These files will be linked to by Sphinx documents, +and should be served from "/static". + +.. note:: + + If you wish to serve static files from a path other than "/static", you + can do so by providing the *staticdir* keyword argument when creating + the :class:`~sphinx.websupport.api.WebSupport` object. Integrating Sphinx Documents Into Your Webapp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -51,6 +59,8 @@ This will return a dictionary containing the following items: * **sidebar**: The sidebar of the document as HTML * **relbar**: A div containing links to related documents * **title**: The title of the document +* **DOCUMENTATION_OPTIONS**: Javascript containing documentation options +* **COMMENT_OPTIONS**: Javascript containing comment options This dict can then be used as context for templates. The goal is to be easy to integrate with your existing templating system. An example using @@ -64,6 +74,15 @@ easy to integrate with your existing templating system. An example using {{ document.title }} {%- endblock %} + {%- block js %} + <script type="text/javascript"> + {{ document.DOCUMENTATION_OPTIONS|safe }} + {{ document.COMMENT_OPTIONS|safe }} + </script> + {{ super() }} + <script type="text/javascript" src="/static/websupport.js"></script> + {%- endblock %} + {%- block relbar %} {{ document.relbar|safe }} {%- endblock %} @@ -76,18 +95,40 @@ easy to integrate with your existing templating system. An example using {{ document.sidebar|safe }} {%- endblock %} -Most likely you'll want to create one function that can handle all of -document requests. An example `Flask <http://flask.pocoo.org/>`_ function -that performs this is:: +Authentication +-------------- + +To use certain features such as voting it must be possible to authenticate +users. The details of the authentication are left to the your application. +Once a user has been authenticated you can pass the user's details to certain +:class:`~sphinx.websupport.WebSupport` methods using the *username* and +*moderator* keyword arguments. The web support package will store the +username with comments and votes. The only caveat is that if you allow users +to change their username, you must update the websupport package's data:: + + support.update_username(old_username, new_username) + +*username* should be a unique string which identifies a user, and *moderator* +should be a boolean representing whether the user has moderation +privilieges. The default value for *moderator* is *False*. + +An example `Flask <http://flask.pocoo.org/>`_ function that checks whether +a user is logged in, and the retrieves a document is:: @app.route('/<path:docname>') def doc(docname): - document = support.get_document(docname) + if g.user: + document = support.get_document(docname, g.user.name, + g.user.moderator) + else: + document = support.get_document(docname) return render_template('doc.html', document=document) -This captures the request path, and passes it directly to -:meth:`~sphinx.websupport.WebSupport.get_document`, which then retrieves -the correct document. +The first thing to notice is that the *docname* is just the request path. +If the user is authenticated then the username and moderation status are +passed along with the docname to +:meth:`~sphinx.websupport.WebSupport.get_document`. The web support package +will then add this data to the COMMENT_OPTIONS that are used in the template. .. note:: @@ -122,118 +163,6 @@ dict in the same format that Comments ~~~~~~~~ -The web support package provides a way to attach comments to some nodes -in your document. It marks these nodes by adding a class and id to these -nodes. A client side script can then locate these nodes, and manipulate -them to allow commenting. A `jQuery <http://jquery.com>`_ script is also -being developed that will be included when it's complete. For now you can -find the script here: `websupport.js <http://bit.ly/cyaRaF>`_. This script -will use AJAX for all communications with the server. You can create your -own script for the front end if this doesn't meet your needs. More -information on that can be found :ref:`here <websupportfrontend>`. - -Before loading this script in your page, you need to create a COMMENT_OPTIONS -object describing how the script should function. In the simplest case you -will just need tell the script whether the current user is allowed to vote. -Once this is done you can import the script as you would any other: - -.. sourcecode:: guess - - <script type="text/javascript"> - var COMMENT_OPTIONS = { - {%- if g.user %} - voting: true, - {%- endif %} - } - </script> - <script type="text/javascript" src="/static/websupport.js></script> - -You will then need to define some templates that the script uses to -display comments. The first template defines the layout for the popup -div used to display comments: - -.. sourcecode:: guess - - <script type="text/html" id="popup_template"> - <div class="popup_comment"> - <a id="comment_close" href="#">x</a> - <h1>Comments</h1> - <form method="post" id="comment_form" action="/docs/add_comment"> - <textarea name="comment"></textarea> - <input type="submit" value="add comment" id="comment_button" /> - <input type="hidden" name="parent" /> - <p class="sort_options"> - Sort by: - <a href="#" class="sort_option" id="rating">top</a> - <a href="#" class="sort_option" id="ascage">newest</a> - <a href="#" class="sort_option" id="age">oldest</a> - </p> - </form> - <h3 id="comment_notification">loading comments... <img src="/static/ajax-loader.gif" alt="" /></h3> - <ul id="comment_ul"></ul> - </div> - <div id="focuser"></div> - </script> - -The next template is an `li` that contains the form used to -reply to a comment: - -.. sourcecode:: guess - - <script type="text/html" id="reply_template"> - <li> - <div class="reply_div" id="rd<%id%>"> - <form id="rf<%id%>"> - <textarea name="comment"></textarea> - <input type="submit" value="add reply" /> - <input type="hidden" name="parent" value="c<%id%>" /> - </form> - </div> - </li> - </script> - -The final template contains HTML that will be used to display comments -in the comment tree: - -.. sourcecode:: guess - - <script type="text/html" id="comment_template"> - <div id="cd<%id%>" class="spxcdiv"> - <div class="vote"> - <div class="arrow"> - <a href="#" id="uv<%id%>" class="vote"> - <img src="<%upArrow%>" /> - </a> - <a href="#" id="uu<%id%>" class="un vote"> - <img src="<%upArrowPressed%>" /> - </a> - </div> - <div class="arrow"> - <a href="#" id="dv<%id%>" class="vote"> - <img src="<%downArrow%>" id="da<%id%>" /> - </a> - <a href="#" id="du<%id%>" class="un vote"> - <img src="<%downArrowPressed%>" /> - </a> - </div> - </div> - <div class="comment_content"> - <p class="tagline comment"> - <span class="user_id"><%username%></span> - <span class="rating"><%pretty_rating%></span> - <span class="delta"><%time.delta%></span> - </p> - <p class="comment_text comment"><%text%></p> - <p class="comment_opts comment"> - <a href="#" class="reply" id="rl<%id%>">reply</a> - <a href="#" class="close_reply" id="cr<%id%>">hide</a> - </p> - <ul class="children" id="cl<%id%>"></ul> - </div> - <div class="clearleft"></div> - </div> - </script> - Now that this is done it's time to define the functions that handle the AJAX calls from the script. You will need three functions. The first function is used to add a new comment, and will call the web support method @@ -248,13 +177,13 @@ function is used to add a new comment, and will call the web support method return jsonify(comment=comment) Then next function handles the retrieval of comments for a specific node, -and is aptly named :meth:`~sphinx.websupport.WebSupport.get_comments`:: +and is aptly named :meth:`~sphinx.websupport.WebSupport.get_data`:: @app.route('/docs/get_comments') def get_comments(): user_id = g.user.id if g.user else None parent_id = request.args.get('parent', '') - comments = support.get_comments(parent_id, user_id) + comments = support.get_data(parent_id, user_id) return jsonify(comments=comments) The final function that is needed will call diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst index 87e1b478a..4a10e1090 100644 --- a/doc/web/storagebackends.rst +++ b/doc/web/storagebackends.rst @@ -1,6 +1,6 @@ .. _storagebackends: -.. currentmodule:: sphinx.websupport.comments +.. currentmodule:: sphinx.websupport.storage Storage Backends ================ @@ -8,12 +8,12 @@ Storage Backends StorageBackend Methods ~~~~~~~~~~~~~~~~~~~~~~ -.. automethod:: sphinx.websupport.comments.StorageBackend.pre_build +.. automethod:: sphinx.websupport.storage.StorageBackend.pre_build -.. automethod:: sphinx.websupport.comments.StorageBackend.add_node +.. automethod:: sphinx.websupport.storage.StorageBackend.add_node -.. automethod:: sphinx.websupport.comments.StorageBackend.post_build +.. automethod:: sphinx.websupport.storage.StorageBackend.post_build -.. automethod:: sphinx.websupport.comments.StorageBackend.add_comment +.. automethod:: sphinx.websupport.storage.StorageBackend.add_comment -.. automethod:: sphinx.websupport.comments.StorageBackend.get_comments +.. automethod:: sphinx.websupport.storage.StorageBackend.get_data From 4939ed27864466a86d2bd6609b85f2e3c65394e2 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 7 Aug 2010 18:44:22 -0500 Subject: [PATCH 253/744] add css and js to context --- sphinx/builders/websupport.py | 45 +++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 70ce5d2bc..bc15b8c97 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -83,7 +83,8 @@ class WebSupportBuilder(StandaloneHTMLBuilder): # Create a dict that will be pickled and used by webapps. doc_ctx = {'body': ctx.get('body', ''), 'title': ctx.get('title', ''), - 'DOCUMENTATION_OPTIONS': self._make_doc_options(ctx)} + 'css': self._make_css(ctx), + 'js': self._make_js(ctx)} # Partially render the html template to proved a more useful ctx. template = self.templates.environment.get_template(templatename) template_module = template.make_module(ctx) @@ -121,16 +122,36 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def dump_search_index(self): self.indexer.finish_indexing() - def _make_doc_options(self, ctx): - t = """ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: '%s', - VERSION: '%s', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '', - HAS_SOURCE: '%s' -};""" - return t % (ctx.get('url_root', ''), escape(ctx['release']), - str(ctx['has_source']).lower()) + def _make_css(self, ctx): + def make_link(file): + path = ctx['pathto'](file, 1) + return '<link rel="stylesheet" href="%s" type=text/css />' % path + links = [make_link('_static/pygments.css')] + for file in ctx['css_files']: + links.append(make_link(file)) + return '\n'.join(links) + + def _make_js(self, ctx): + def make_script(file): + path = ctx['pathto'](file, 1) + return '<script type="text/javascript" src="%s"></script>' % path + + opts = """ +<script type="text/javascript"> + var DOCUMENTATION_OPTIONS = { + URL_ROOT: '%s', + VERSION: '%s', + COLLAPSE_INDEX: false, + FILE_SUFFIX: '', + HAS_SOURCE: '%s' + }; +</script>""" + opts = opts % (ctx.get('url_root', ''), escape(ctx['release']), + str(ctx['has_source']).lower()) + scripts = [] + for file in ctx['script_files']: + scripts.append(make_script(file)) + scripts.append(make_script('_static/websupport.js')) + return opts + '\n' + '\n'.join(scripts) From bf7a6526f52f175951d9da86b45c1c95e9fdf5e4 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 7 Aug 2010 18:59:11 -0500 Subject: [PATCH 254/744] moved websupport.js into sphinx --- sphinx/themes/basic/static/websupport.js | 751 +++++++++++++++++++++++ 1 file changed, 751 insertions(+) create mode 100644 sphinx/themes/basic/static/websupport.js diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js new file mode 100644 index 000000000..0e2b44303 --- /dev/null +++ b/sphinx/themes/basic/static/websupport.js @@ -0,0 +1,751 @@ +(function($) { + $.fn.autogrow = function(){ + return this.each(function(){ + var textarea = this; + + $.fn.autogrow.resize(textarea); + + $(textarea) + .focus(function() { + textarea.interval = setInterval(function() { + $.fn.autogrow.resize(textarea); + }, 500); + }) + .blur(function() { + clearInterval(textarea.interval); + }); + }); + }; + + $.fn.autogrow.resize = function(textarea) { + var lineHeight = parseInt($(textarea).css('line-height')); + var lines = textarea.value.split('\n'); + var columns = textarea.cols; + var lineCount = 0; + $.each(lines, function() { + lineCount += Math.ceil(this.length/columns) || 1; + }); + var height = lineHeight*(lineCount+1); + $(textarea).css('height', height); + }; +})(jQuery); + +(function($) { + var commentListEmpty, popup, comp; + + function init() { + initTemplates(); + initEvents(); + initComparator(); + }; + + function initEvents() { + $('a#comment_close').click(function(event) { + event.preventDefault(); + hide(); + }); + $('form#comment_form').submit(function(event) { + event.preventDefault(); + addComment($('form#comment_form')); + }); + $('.vote').live("click", function() { + handleVote($(this)); + return false; + }); + $('a.reply').live("click", function() { + openReply($(this).attr('id').substring(2)); + return false; + }); + $('a.close_reply').live("click", function() { + closeReply($(this).attr('id').substring(2)); + return false; + }); + $('a.sort_option').click(function(event) { + event.preventDefault(); + handleReSort($(this)); + }); + $('a.show_proposal').live("click", function() { + showProposal($(this).attr('id').substring(2)); + return false; + }); + $('a.hide_proposal').live("click", function() { + hideProposal($(this).attr('id').substring(2)); + return false; + }); + $('a.show_propose_change').live("click", function() { + showProposeChange($(this).attr('id').substring(2)); + return false; + }); + $('a.hide_propose_change').live("click", function() { + hideProposeChange($(this).attr('id').substring(2)); + return false; + }); + $('a.accept_comment').live("click", function() { + acceptComment($(this).attr('id').substring(2)); + return false; + }); + $('a.reject_comment').live("click", function() { + rejectComment($(this).attr('id').substring(2)); + return false; + }); + $('a.delete_comment').live("click", function() { + deleteComment($(this).attr('id').substring(2)); + return false; + }); + }; + + function initTemplates() { + // Create our popup div, the same div is recycled each time comments + // are displayed. + popup = $(renderTemplate(popupTemplate, opts)); + // Setup autogrow on the textareas + popup.find('textarea').autogrow(); + $('body').append(popup); + }; + + /** + * Create a comp function. If the user has preferences stored in + * the sortBy cookie, use those, otherwise use the default. + */ + function initComparator() { + var by = 'rating'; // Default to sort by rating. + // If the sortBy cookie is set, use that instead. + if (document.cookie.length > 0) { + var start = document.cookie.indexOf('sortBy='); + if (start != -1) { + start = start + 7; + var end = document.cookie.indexOf(";", start); + if (end == -1) + end = document.cookie.length; + by = unescape(document.cookie.substring(start, end)); + } + } + setComparator(by); + }; + + /** + * Show the comments popup window. + */ + function show(nodeId) { + var id = nodeId.substring(1); + + // Reset the main comment form, and set the value of the parent input. + $('form#comment_form') + .find('textarea,input') + .removeAttr('disabled').end() + .find('input[name="node"]') + .val(id).end() + .find('textarea[name="proposal"]') + .val('') + .hide(); + + // Position the popup and show it. + var clientWidth = document.documentElement.clientWidth; + var popupWidth = $('div.popup_comment').width(); + $('div#focuser').fadeIn('fast'); + $('div.popup_comment') + .css({ + 'top': 100+$(window).scrollTop(), + 'left': clientWidth/2-popupWidth/2, + 'position': 'absolute' + }) + .fadeIn('fast', function() { + getComments(id); + }); + }; + + /** + * Hide the comments popup window. + */ + function hide() { + $('div#focuser').fadeOut('fast'); + $('div.popup_comment').fadeOut('fast', function() { + $('ul#comment_ul').empty(); + $('h3#comment_notification').show(); + $('form#comment_form').find('textarea') + .val('').end() + .find('textarea, input') + .removeAttr('disabled'); + }); + }; + + /** + * Perform an ajax request to get comments for a node + * and insert the comments into the comments tree. + */ + function getComments(id) { + $.ajax({ + type: 'GET', + url: opts.getCommentsURL, + data: {node: id}, + success: function(data, textStatus, request) { + var ul = $('ul#comment_ul').hide(); + $('form#comment_form') + .find('textarea[name="proposal"]') + .data('source', data.source); + + if (data.comments.length == 0) { + ul.html('<li>No comments yet.</li>'); + commentListEmpty = true; + var speed = 100; + } + else { + // If there are comments, sort them and put them in the list. + var comments = sortComments(data.comments); + var speed = data.comments.length * 100; + appendComments(comments, ul); + commentListEmpty = false; + } + $('h3#comment_notification').slideUp(speed+200); + ul.slideDown(speed); + }, + error: function(request, textStatus, error) { + showError('Oops, there was a problem retrieving the comments.'); + }, + dataType: 'json' + }); + }; + + /** + * Add a comment via ajax and insert the comment into the comment tree. + */ + function addComment(form) { + // Disable the form that is being submitted. + form.find('textarea,input').attr('disabled', 'disabled'); + + // Send the comment to the server. + $.ajax({ + type: "POST", + url: opts.addCommentURL, + dataType: 'json', + data: {node: form.find('input[name="node"]').val(), + parent: form.find('input[name="parent"]').val(), + text: form.find('textarea[name="comment"]').val(), + proposal: form.find('textarea[name="proposal"]').val()}, + success: function(data, textStatus, error) { + // Reset the form. + form.find('textarea') + .val('') + .add(form.find('input')) + .removeAttr('disabled'); + if (commentListEmpty) { + $('ul#comment_ul').empty(); + commentListEmpty = false; + } + insertComment(data.comment); + }, + error: function(request, textStatus, error) { + form.find('textarea,input').removeAttr('disabled'); + showError('Oops, there was a problem adding the comment.'); + } + }); + }; + + /** + * Recursively append comments to the main comment list and children + * lists, creating the comment tree. + */ + function appendComments(comments, ul) { + $.each(comments, function() { + var div = createCommentDiv(this); + ul.append($('<li></li>').html(div)); + appendComments(this.children, div.find('ul.children')); + // To avoid stagnating data, don't store the comments children in data. + this.children = null; + div.data('comment', this); + }); + }; + + /** + * After adding a new comment, it must be inserted in the correct + * location in the comment tree. + */ + function insertComment(comment) { + var div = createCommentDiv(comment); + + // To avoid stagnating data, don't store the comments children in data. + comment.children = null; + div.data('comment', comment); + + if (comment.node != null) { + var ul = $('ul#comment_ul'); + var siblings = getChildren(ul); + } + else { + var ul = $('#cl' + comment.parent); + var siblings = getChildren(ul); + } + + var li = $('<li></li>'); + li.hide(); + + // Determine where in the parents children list to insert this comment. + for(i=0; i < siblings.length; i++) { + if (comp(comment, siblings[i]) <= 0) { + $('#cd' + siblings[i].id) + .parent() + .before(li.html(div)); + li.slideDown('fast'); + return; + } + } + + // If we get here, this comment rates lower than all the others, + // or it is the only comment in the list. + ul.append(li.html(div)); + li.slideDown('fast'); + }; + + function acceptComment(id) { + $.ajax({ + type: 'POST', + url: opts.acceptCommentURL, + data: {id: id}, + success: function(data, textStatus, request) { + $('#cm' + id).fadeOut('fast'); + }, + error: function(request, textStatus, error) { + showError("Oops, there was a problem accepting the comment."); + }, + }); + }; + + function rejectComment(id) { + $.ajax({ + type: 'POST', + url: opts.rejectCommentURL, + data: {id: id}, + success: function(data, textStatus, request) { + var div = $('#cd' + id); + div.slideUp('fast', function() { + div.remove(); + }); + }, + error: function(request, textStatus, error) { + showError("Oops, there was a problem rejecting the comment."); + }, + }); + }; + + function deleteComment(id) { + $.ajax({ + type: 'POST', + url: opts.deleteCommentURL, + data: {id: id}, + success: function(data, textStatus, request) { + var div = $('#cd' + id); + div + .find('span.user_id:first') + .text('[deleted]').end() + .find('p.comment_text:first') + .text('[deleted]').end() + .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + + ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) + .remove(); + var comment = div.data('comment'); + comment.username = '[deleted]'; + comment.text = '[deleted]'; + div.data('comment', comment); + }, + error: function(request, textStatus, error) { + showError("Oops, there was a problem deleting the comment."); + }, + }); + }; + + function showProposal(id) { + $('#sp' + id).hide(); + $('#hp' + id).show(); + $('#pr' + id).slideDown('fast'); + }; + + function hideProposal(id) { + $('#hp' + id).hide(); + $('#sp' + id).show(); + $('#pr' + id).slideUp('fast'); + }; + + function showProposeChange(id) { + $('a.show_propose_change').hide(); + $('a.hide_propose_change').show(); + var textarea = $('textarea[name="proposal"]'); + textarea.val(textarea.data('source')); + $.fn.autogrow.resize(textarea[0]); + textarea.slideDown('fast'); + }; + + function hideProposeChange(id) { + $('a.hide_propose_change').hide(); + $('a.show_propose_change').show(); + var textarea = $('textarea[name="proposal"]'); + textarea.val(''); + textarea.slideUp('fast'); + }; + + /** + * Handle when the user clicks on a sort by link. + */ + function handleReSort(link) { + setComparator(link.attr('id')); + // Save/update the sortBy cookie. + var expiration = new Date(); + expiration.setDate(expiration.getDate() + 365); + document.cookie= 'sortBy=' + escape(link.attr('id')) + + ';expires=' + expiration.toUTCString(); + var comments = getChildren($('ul#comment_ul'), true); + comments = sortComments(comments); + + appendComments(comments, $('ul#comment_ul').empty()); + }; + + /** + * Function to process a vote when a user clicks an arrow. + */ + function handleVote(link) { + if (!opts.voting) { + showError("You'll need to login to vote."); + return; + } + + var id = link.attr('id'); + // If it is an unvote, the new vote value is 0, + // Otherwise it's 1 for an upvote, or -1 for a downvote. + if (id.charAt(1) == 'u') + var value = 0; + else + var value = id.charAt(0) == 'u' ? 1 : -1; + + // The data to be sent to the server. + var d = { + comment_id: id.substring(2), + value: value + }; + + // Swap the vote and unvote links. + link.hide(); + $('#' + id.charAt(0) + (id.charAt(1) == 'u' ? 'v' : 'u') + d.comment_id) + .show(); + + // The div the comment is displayed in. + var div = $('div#cd' + d.comment_id); + var data = div.data('comment'); + + // If this is not an unvote, and the other vote arrow has + // already been pressed, unpress it. + if ((d.value != 0) && (data.vote == d.value*-1)) { + $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id) + .hide(); + $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id) + .show(); + } + + // Update the comments rating in the local data. + data.rating += (data.vote == 0) ? d.value : (d.value - data.vote); + data.vote = d.value; + div.data('comment', data); + + // Change the rating text. + div.find('.rating:first') + .text(data.rating + ' point' + (data.rating == 1 ? '' : 's')); + + // Send the vote information to the server. + $.ajax({ + type: "POST", + url: opts.processVoteURL, + data: d, + error: function(request, textStatus, error) { + showError("Oops, there was a problem casting that vote."); + } + }); + }; + + /** + * Open a reply form used to reply to an existing comment. + */ + function openReply(id) { + // Swap out the reply link for the hide link + $('#rl' + id).hide(); + $('#cr' + id).show(); + + // Add the reply li to the children ul. + var div = $(renderTemplate(replyTemplate, {id: id})).hide(); + $('#cl' + id) + .prepend(div) + // Setup the submit handler for the reply form. + .find('#rf' + id) + .submit(function(event) { + event.preventDefault(); + addComment($('#rf' + id)); + closeReply(id); + }); + div.slideDown('fast'); + }; + + /** + * Close the reply form opened with openReply. + */ + function closeReply(id) { + // Remove the reply div from the DOM. + $('#rd' + id).slideUp('fast', function() { + $(this).remove(); + }); + + // Swap out the hide link for the reply link + $('#cr' + id).hide(); + $('#rl' + id).show(); + }; + + /** + * Recursively sort a tree of comments using the comp comparator. + */ + function sortComments(comments) { + comments.sort(comp); + $.each(comments, function() { + this.children = sortComments(this.children); + }); + return comments; + }; + + /** + * Set comp, which is a comparator function used for sorting and + * inserting comments into the list. + */ + function setComparator(by) { + // If the first three letters are "asc", sort in ascending order + // and remove the prefix. + if (by.substring(0,3) == 'asc') { + var i = by.substring(3); + comp = function(a, b) { return a[i] - b[i]; } + } + // Otherwise sort in descending order. + else + comp = function(a, b) { return b[by] - a[by]; } + + // Reset link styles and format the selected sort option. + $('a.sel').attr('href', '#').removeClass('sel'); + $('#' + by).removeAttr('href').addClass('sel'); + }; + + /** + * Get the children comments from a ul. If recursive is true, + * recursively include childrens' children. + */ + function getChildren(ul, recursive) { + var children = []; + ul.children().children("[id^='cd']") + .each(function() { + var comment = $(this).data('comment'); + if (recursive) { + comment.children = + getChildren($(this).find('#cl' + comment.id), true); + } + children.push(comment); + }); + return children; + }; + + /** + * Create a div to display a comment in. + */ + function createCommentDiv(comment) { + // Prettify the comment rating. + comment.pretty_rating = comment.rating + ' point' + + (comment.rating == 1 ? '' : 's'); + // Create a div for this comment. + var context = $.extend({}, opts, comment); + var div = $(renderTemplate(commentTemplate, context)); + + // If the user has voted on this comment, highlight the correct arrow. + if (comment.vote) { + var direction = (comment.vote == 1) ? 'u' : 'd'; + div.find('#' + direction + 'v' + comment.id).hide(); + div.find('#' + direction + 'u' + comment.id).show(); + } + + if (comment.text != '[deleted]') { + div.find('a.reply').show(); + if (comment.proposal_diff) { + div.find('#sp' + comment.id).show(); + } + if (opts.moderator && !comment.displayed) { + div.find('#cm' + comment.id).show(); + } + if (opts.moderator || (opts.username == comment.username)) { + div.find('#dc' + comment.id).show(); + } + } + + return div; + } + + /** + * A simple template renderer. Placeholders such as <%id%> are replaced + * by context['id']. Items are always escaped. + */ + function renderTemplate(template, context) { + var esc = $('<span></span>'); + + function handle(ph, escape) { + var cur = context; + $.each(ph.split('.'), function() { + cur = cur[this]; + }); + return escape ? esc.text(cur || "").html() : cur; + } + + return template.replace(/<([%#])([\w\.]*)\1>/g, function(){ + return handle(arguments[2], arguments[1] == '%' ? true : false); + }); + }; + + function showError(message) { + $('<div class="popup_error">' + + '<h1>' + message + '</h1>' + + '</div>') + .appendTo('body') + .fadeIn("slow") + .delay(2000) + .fadeOut("slow"); + }; + + /** + * Add a link the user uses to open the comments popup. + */ + $.fn.comment = function() { + return this.each(function() { + $(this).append( + $('<a href="#" class="sphinx_comment"></a>') + .html(opts.commentHTML) + .click(function(event) { + event.preventDefault(); + show($(this).parent().attr('id')); + })); + }); + }; + + var replyTemplate = ' <li>\ + <div class="reply_div" id="rd<%id%>">\ + <form id="rf<%id%>">\ + <textarea name="comment"></textarea>\ + <input type="submit" value="add reply" />\ + <input type="hidden" name="parent" value="<%id%>" />\ + <input type="hidden" name="node" value="" />\ + </form>\ + </div>\ + </li>'; + + var commentTemplate = ' <div id="cd<%id%>" class="spxcdiv">\ + <div class="vote">\ + <div class="arrow">\ + <a href="#" id="uv<%id%>" class="vote">\ + <img src="<%upArrow%>" />\ + </a>\ + <a href="#" id="uu<%id%>" class="un vote">\ + <img src="<%upArrowPressed%>" />\ + </a>\ + </div>\ + <div class="arrow">\ + <a href="#" id="dv<%id%>" class="vote">\ + <img src="<%downArrow%>" id="da<%id%>" />\ + </a>\ + <a href="#" id="du<%id%>" class="un vote">\ + <img src="<%downArrowPressed%>" />\ + </a>\ + </div>\ + </div>\ + <div class="comment_content">\ + <p class="tagline comment">\ + <span class="user_id"><%username%></span>\ + <span class="rating"><%pretty_rating%></span>\ + <span class="delta"><%time.delta%></span>\ + </p>\ + <p class="comment_text comment"><%text%></p>\ + <p class="comment_opts comment">\ + <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\ + <a href="#" class="close_reply" id="cr<%id%>">reply ▿</a>\ + <a href="#" id="sp<%id%>" class="show_proposal">\ + proposal ▹\ + </a>\ + <a href="#" id="hp<%id%>" class="hide_proposal">\ + proposal ▿\ + </a>\ + <a href="#" id="dc<%id%>" class="delete_comment hidden">\ + delete\ + </a>\ + <span id="cm<%id%>" class="moderation hidden">\ + <a href="#" id="ac<%id%>" class="accept_comment">accept</a>\ + <a href="#" id="rc<%id%>" class="reject_comment">reject</a>\ + </span>\ + </p>\ + <pre class="proposal" id="pr<%id%>">\ +<#proposal_diff#>\ + </pre>\ + <ul class="children" id="cl<%id%>"></ul>\ + </div>\ + <div class="clearleft"></div>\ + </div>'; + + var popupTemplate = ' <div class="popup_comment">\ + <a id="comment_close" href="#">x</a>\ + <h1>Comments</h1>\ + <form method="post" id="comment_form" action="/docs/add_comment">\ + <textarea name="comment" cols="80"></textarea>\ + <p class="propose_button">\ + <a href="#" class="show_propose_change">\ + Propose a change ▹\ + </a>\ + <a href="#" class="hide_propose_change">\ + Propose a change ▿\ + </a>\ + </p>\ + <textarea name="proposal" cols="80" spellcheck="false"></textarea>\ + <input type="submit" value="add comment" id="comment_button" />\ + <input type="hidden" name="node" />\ + <input type="hidden" name="parent" value="" />\ + <p class="sort_options">\ + Sort by:\ + <a href="#" class="sort_option" id="rating">top</a>\ + <a href="#" class="sort_option" id="ascage">newest</a>\ + <a href="#" class="sort_option" id="age">oldest</a>\ + </p>\ + </form>\ + <h3 id="comment_notification">loading comments... <img src="/static/ajax-loader.gif" alt="" /></h3>\ + <ul id="comment_ul"></ul>\ + </div>\ + <div id="focuser"></div>'; + + + var opts = jQuery.extend({ + processVoteURL: '/process_vote', + addCommentURL: '/add_comment', + getCommentsURL: '/get_comments', + acceptCommentURL: '/accept_comment', + rejectCommentURL: '/reject_comment', + rejectCommentURL: '/delete_comment', + commentHTML: '<img src="/static/comment.png" alt="comment" />', + upArrow: '/static/up.png', + downArrow: '/static/down.png', + upArrowPressed: '/static/up-pressed.png', + downArrowPressed: '/static/down-pressed.png', + voting: false, + moderator: false + }, COMMENT_OPTIONS); + + $(document).ready(function() { + init(); + }); +})(jQuery); + +$(document).ready(function() { + $('.spxcmt').comment(); + + /** Highlight search words in search results. */ + $("div.context").each(function() { + var params = $.getQueryParameters(); + var terms = (params.q) ? params.q[0].split(/\s+/) : []; + var result = $(this); + $.each(terms, function() { + result.highlightText(this.toLowerCase(), 'highlighted'); + }); + }); +}); \ No newline at end of file From 40c7de1aff9a4efe1827b915ff447e0dd5e59080 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 7 Aug 2010 19:06:28 -0500 Subject: [PATCH 255/744] update get_document to add comment options to js --- sphinx/websupport/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index de719e36d..343928135 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -139,12 +139,13 @@ class WebSupport(object): 'The document "%s" could not be found' % docname) document = pickle.load(f) - document['COMMENT_OPTIONS'] = self._make_comment_options(username, - moderator) + comment_opts = self._make_comment_options(username, moderator) + document['js'] = comment_opts + '\n' + document['js'] return document def _make_comment_options(self, username, moderator): - parts = ['var COMMENT_OPTIONS = {'] + parts = ['<script type="text/javascript">', + 'var COMMENT_OPTIONS = {'] if self.docroot is not '': parts.append('addCommentURL: "/%s/%s",' % (self.docroot, 'add_comment')) @@ -163,6 +164,7 @@ class WebSupport(object): parts.append('username: "%s",' % username) parts.append('moderator: %s' % str(moderator).lower()) parts.append('};') + parts.append('</script>') return '\n'.join(parts) def get_search_results(self, q): From 6f3932660aed0db590e1a8a3f31293358e3aeb19 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 7 Aug 2010 20:21:53 -0500 Subject: [PATCH 256/744] an even nicer build directory --- sphinx/builders/websupport.py | 8 +++++--- sphinx/websupport/__init__.py | 17 ++++++++++------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index bc15b8c97..cdd0fb925 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -107,7 +107,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): # if there is a source file, copy the source file for the # "show source" link if ctx.get('sourcename'): - source_name = path.join(self.outdir, self.app.staticdir, + source_name = path.join(self.app.builddir, self.app.staticdir, '_sources', os_path(ctx['sourcename'])) ensuredir(path.dirname(source_name)) copyfile(self.env.doc2path(pagename), source_name) @@ -115,9 +115,11 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def handle_finish(self): StandaloneHTMLBuilder.handle_finish(self) shutil.move(path.join(self.outdir, '_images'), - path.join(self.outdir, self.app.staticdir, '_images')) + path.join(self.app.builddir, self.app.staticdir, + '_images')) shutil.move(path.join(self.outdir, '_static'), - path.join(self.outdir, self.app.staticdir, '_static')) + path.join(self.app.builddir, self.app.staticdir, + '_static')) def dump_search_index(self): self.indexer.finish_indexing() diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 343928135..9bd6cbda5 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -25,6 +25,7 @@ from sphinx.websupport.errors import * class WebSupportApp(Sphinx): def __init__(self, *args, **kwargs): self.staticdir = kwargs.pop('staticdir', None) + self.builddir = kwargs.pop('builddir', None) self.search = kwargs.pop('search', None) self.storage = kwargs.pop('storage', None) Sphinx.__init__(self, *args, **kwargs) @@ -33,16 +34,18 @@ class WebSupport(object): """The main API class for the web support package. All interactions with the web support package should occur through this class. """ - def __init__(self, srcdir='', outdir='', datadir='', search=None, + def __init__(self, srcdir='', builddir='', datadir='', search=None, storage=None, status=sys.stdout, warning=sys.stderr, moderation_callback=None, staticdir='static', docroot=''): self.srcdir = srcdir - self.outdir = outdir or datadir + self.builddir = builddir + self.outdir = path.join(builddir, 'data') + self.datadir = datadir or self.outdir self.staticdir = staticdir.strip('/') + self.docroot = docroot.strip('/') self.status = status self.warning = warning - self.docroot = docroot.strip('/') self.moderation_callback = moderation_callback self._init_templating() @@ -58,7 +61,7 @@ class WebSupport(object): from sphinx.websupport.storage.sqlalchemystorage \ import SQLAlchemyStorage from sqlalchemy import create_engine - db_path = path.join(self.outdir, 'db', 'websupport.db') + db_path = path.join(self.datadir, 'db', 'websupport.db') ensuredir(path.dirname(db_path)) uri = storage or 'sqlite:///%s' % db_path engine = create_engine(uri) @@ -78,7 +81,7 @@ class WebSupport(object): mod, cls = search_adapters[search or 'null'] mod = 'sphinx.websupport.search.' + mod SearchClass = getattr(__import__(mod, None, None, [cls]), cls) - search_path = path.join(self.outdir, 'search') + search_path = path.join(self.datadir, 'search') self.search = SearchClass(search_path) self.results_template = \ self.template_env.get_template('searchresults.html') @@ -102,7 +105,7 @@ class WebSupport(object): self.outdir, doctreedir, 'websupport', search=self.search, status=self.status, warning=self.warning, storage=self.storage, - staticdir=self.staticdir) + staticdir=self.staticdir, builddir=self.builddir) self.storage.pre_build() app.build() @@ -130,7 +133,7 @@ class WebSupport(object): :param docname: the name of the document to load. """ - infilename = path.join(self.outdir, 'pickles', docname + '.fpickle') + infilename = path.join(self.datadir, 'pickles', docname + '.fpickle') try: f = open(infilename, 'rb') From 2dcb0f017827443c673c902a266c6ec0f62c52be Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 7 Aug 2010 21:08:43 -0500 Subject: [PATCH 257/744] updated tests to use builddir instead of outdir --- tests/test_searchadapters.py | 2 +- tests/test_websupport.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py index c9525f758..cb6c6e968 100644 --- a/tests/test_searchadapters.py +++ b/tests/test_searchadapters.py @@ -28,7 +28,7 @@ def teardown_module(): def search_adapter_helper(adapter): clear_builddir() - settings = {'outdir': os.path.join(test_root, 'websupport'), + settings = {'builddir': os.path.join(test_root, 'websupport'), 'status': StringIO(), 'warning': StringIO()} settings.update({'srcdir': test_root, diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 37f0a679e..27a14e369 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -27,7 +27,7 @@ except ImportError: wraps = lambda f: (lambda w: w) -default_settings = {'outdir': os.path.join(test_root, 'websupport'), +default_settings = {'builddir': os.path.join(test_root, 'websupport'), 'status': StringIO(), 'warning': StringIO()} From 77b4c107cffcd54e4b9cc9ffd77cacf9861a917d Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sun, 8 Aug 2010 11:10:56 -0500 Subject: [PATCH 258/744] added static images used for web support --- sphinx/themes/basic/static/ajax-loader.gif | Bin 0 -> 673 bytes sphinx/themes/basic/static/comment.png | Bin 0 -> 3501 bytes sphinx/themes/basic/static/down-pressed.png | Bin 0 -> 368 bytes sphinx/themes/basic/static/down.png | Bin 0 -> 363 bytes sphinx/themes/basic/static/up-pressed.png | Bin 0 -> 372 bytes sphinx/themes/basic/static/up.png | Bin 0 -> 363 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 sphinx/themes/basic/static/ajax-loader.gif create mode 100644 sphinx/themes/basic/static/comment.png create mode 100644 sphinx/themes/basic/static/down-pressed.png create mode 100644 sphinx/themes/basic/static/down.png create mode 100644 sphinx/themes/basic/static/up-pressed.png create mode 100644 sphinx/themes/basic/static/up.png diff --git a/sphinx/themes/basic/static/ajax-loader.gif b/sphinx/themes/basic/static/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..61faf8cab23993bd3e1560bff0668bd628642330 GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nno%(3)e{?)x>&1u}A`t?OF7Z|1gRivOgXi&7IyQd1Pl zGfOfQ60;I3a`F>X^fL3(@);C=vM_KlFfb_o=k{|A33hf2a5d61U}gjg=>Rd%XaNQW zW@C<Bcm5fi^2`=a=CI<BoWt%nBaPE_qv4@lA~O$e(@QvVsPKYrw1nl|W$cy`JnUZC z&pm)ff{kWGHpc{Hj$e<Wf^-Yd?hVhnTne26LlO)n6%u@0qor2V$ZRdW|29#Ay+Pr+ z#G^K6$xW&%T0&5Rn2-%J<Je`StbNMy#Dp_b!t~i%lV$k6Ncw&BbV{7Dx<KXw*O|?G zWsa@TW{P|({)e&oFu&2t6sh_9S)fKSBO3+uTav2wDWkTDZ{~!>w{|b%Y*pl8F?4B9 zlo4Fz*0kZGJabY|>}Okf0}CCg{u4`zEPY^pV?j2@h+|igy0+Kz6p;@SpM4s6)XEMg z#3Y4GX>Hjlml5ftdH$4x0JGdn8~MX(U~_^d!Hi)=HU{V%g+mi8#UGbE-*ao8f#h+S z2a0-5+vc7MU$e-NhmBjLIC1v|)9+Im8x1yacJ7{^tLX(ZhYi^rpmXm0`@ku9b53aN zEXH@Y3JaztblgpxbJt{AtE1ad1Ca>{v$rwwvK(>{m~Gf_=-Ro7Fk{#;i~+{{>QtvI yb2P8Zac~?~=sRA>$6{!(^3;ZP0TPFR(G_-UDU(8Jl0?(IXu$~#4A!880|o%~Al1tN literal 0 HcmV?d00001 diff --git a/sphinx/themes/basic/static/comment.png b/sphinx/themes/basic/static/comment.png new file mode 100644 index 0000000000000000000000000000000000000000..bad742bb08696f9103edeb65b301e514d1bb81de GIT binary patch literal 3501 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 bKmCsZzk6ggYXo`_00000NkvXXu0mjfg2suj literal 0 HcmV?d00001 diff --git a/sphinx/themes/basic/static/down-pressed.png b/sphinx/themes/basic/static/down-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7ad782782e4f8e39b0c6e15c7344700cdd2527 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}Z23@f-Ava~9&<9T!#}JFtXD=!G zGdl{fK6ro2OGiOl+hKvH6i=D3%%Y^j`yIkRn!8O>@bG)IQR0{Kf+mxNd=_WScA8u_ z3;8(7x2){m9`nt+U(Nab&1G)!{`SPVpDX$w8McLTzAJ39wprG3p4XLq$06M`%}2Yk zRPPsb<eOBuJzzJtaBsDHruj&MO}CkI9@oM{u93MWs^*chA=$e7rUh<GoTlU#?wGGz z*_K*(>ES*dnYm1wkGL;iioAUB*Or2kz6(-M_r_#Me-<g+a_(*JjN&U*z02OX#~$%( z6#3ZLekrlFz3AKX``cC&$K^ccDa(j?XLM8b!=w9;#rq6gQ-fH_ZUFtx;OXk;vd$@? F2>`{mj$Z%( literal 0 HcmV?d00001 diff --git a/sphinx/themes/basic/static/down.png b/sphinx/themes/basic/static/down.png new file mode 100644 index 0000000000000000000000000000000000000000..3003a88770de3977d47a2ba69893436a2860f9e7 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}xaV3tUZ$qnrLa#kt978NlpS`ru z&)HFc^}^>{UOEce+71h5nn>6&w6A!ie<Ab_+oUB-{c$>Nbu1wh)UGh{8~et^#oZ1# z>T7oM=FZ~xXWnTo{qnXm$ZLOlqGswI_m2{XwVK)IJmBjW{J3-B3x@C=M{ShWt#<pw z+y29?H~r3Ic5@5&Ry#4lLZ0TfgDn1@D+(sA?zAeN{MGSl)<;wKn{AaYbJjJ-X;-#| zTD>fYS9M?R;8K$~YwlIqwf>VA7q=YKcwf2DS4Zj5inDKXXB1zl=(YO3ST6~rDq)&z z*o>z)=hxrfG-cDBW0G$!?6{M<$@{_4{m1o%Ub!naEtn|@^frU1tDnm{r-UW|!^@B8 literal 0 HcmV?d00001 diff --git a/sphinx/themes/basic/static/up-pressed.png b/sphinx/themes/basic/static/up-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..8bd587afee2fe38989383ff82010147ea56b93dd GIT binary patch literal 372 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}Z1|5lxjZvvUp)Z~;jv*GO&raT- z#pEb(tbY1#Ey4dH;Y+=<pEBRLsjGAOCY!v|CyvUA4wrPfZ{O_DPe^{q91)qJXqI&@ zO~JiL+CN7oqU^@cvS+{3Bz9yAOB-!e{LTNlK+)ab|H>wAPPMA->(Ug=YM6W%tgKtA zI`O=0Laf#Y-Y4f~`^K_)D_mvj{B=4?=t!I41ZLNlI~j_4kE*^nvF$)|>mH^X%(>6c z8XimFvvIAOoRJf!>6jzIa5w(S%7lxdZ{*qJxhxpj6S#UB!oTuMX^Z^6%)IfT_v-!3 z=PEaM_iSh6_`s$!$NaEMP6gw<x#pX-zc1lmBOrZAdYY~+^N*K~{#DY`%7Ol8@O1Ta JS?83{1OR?3hMxcc literal 0 HcmV?d00001 diff --git a/sphinx/themes/basic/static/up.png b/sphinx/themes/basic/static/up.png new file mode 100644 index 0000000000000000000000000000000000000000..b94625680b4a4b9647c3a6f3f283776930696aa9 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*yM+OLB!qm#z$3ZNi+iKnkC`z>}xaYa3wv(2tRq1T=+jv*GO&raUx z$K)u`w*Tuor>1}ySNCesuPuG-8#b%jw0sn-5fpk^!623V@1GR6+<`78?&Rhov&jx6 z*R7KttIVGJ=8yH~|HhI(uB&NIpYp$LXT}M`Z<Dv|Q9O9-{p!t<9#srg4(I=_Xg%_r zaf7X90Rxxu?X9UB7>)D=?%dxpN#UiKM#HZsJK4DUm#Y3a5!dMF634rTxz_l%hvABb z(=Pc<$5*Xj@eE$@$89c0_oa>Y5;`&;INvn7C-9xQbH92`*_(~*lcvS}m5Z2pGdgKc z>;tJC%=6B^QS*>ubT+QGD)v`9z&&Y`y-xHu*7vDC$|9@xfdY)d)78&qol`;+01iQm A<^TWy literal 0 HcmV?d00001 From ed33f6866dc6b667300a661842c592cfbb57f629 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sun, 8 Aug 2010 15:39:56 -0500 Subject: [PATCH 259/744] only provide pygments.css, update image locations. --- sphinx/builders/websupport.py | 14 +++----------- sphinx/themes/basic/static/websupport.js | 12 ++++++------ sphinx/websupport/__init__.py | 9 +++++++++ sphinx/writers/websupport.py | 3 +++ 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index cdd0fb925..3d0356b71 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -81,9 +81,11 @@ class WebSupportBuilder(StandaloneHTMLBuilder): ctx, event_arg) # Create a dict that will be pickled and used by webapps. + css = '<link rel="stylesheet" href="%s" type=text/css />' % \ + pathto('_static/pygmentcs.css', 1) doc_ctx = {'body': ctx.get('body', ''), 'title': ctx.get('title', ''), - 'css': self._make_css(ctx), + 'css': css, 'js': self._make_js(ctx)} # Partially render the html template to proved a more useful ctx. template = self.templates.environment.get_template(templatename) @@ -124,16 +126,6 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def dump_search_index(self): self.indexer.finish_indexing() - def _make_css(self, ctx): - def make_link(file): - path = ctx['pathto'](file, 1) - return '<link rel="stylesheet" href="%s" type=text/css />' % path - - links = [make_link('_static/pygments.css')] - for file in ctx['css_files']: - links.append(make_link(file)) - return '\n'.join(links) - def _make_js(self, ctx): def make_script(file): path = ctx['pathto'](file, 1) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 0e2b44303..aeef3ece1 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -615,7 +615,7 @@ return this.each(function() { $(this).append( $('<a href="#" class="sphinx_comment"></a>') - .html(opts.commentHTML) + .html('<img src="' + opts.commentImage + '" alt="comment" />') .click(function(event) { event.preventDefault(); show($(this).parent().attr('id')); @@ -722,11 +722,11 @@ acceptCommentURL: '/accept_comment', rejectCommentURL: '/reject_comment', rejectCommentURL: '/delete_comment', - commentHTML: '<img src="/static/comment.png" alt="comment" />', - upArrow: '/static/up.png', - downArrow: '/static/down.png', - upArrowPressed: '/static/up-pressed.png', - downArrowPressed: '/static/down-pressed.png', + commentImage: '/static/_static/comment.png', + upArrow: '/static/_static/up.png', + downArrow: '/static/_static/down.png', + upArrowPressed: '/static/_static/up-pressed.png', + downArrowPressed: '/static/_static/down-pressed.png', voting: false, moderator: false }, COMMENT_OPTIONS); diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 9bd6cbda5..3215e856e 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -162,6 +162,15 @@ class WebSupport(object): 'reject_comment')) parts.append('deleteCommentURL: "/%s/%s",' % (self.docroot, 'delete_comment')) + + if self.staticdir != 'static': + p = lambda file: '%s/_static/%s' % (self.staticdir, file) + parts.append('commentImage: "/%s",' % p('comment.png') ) + parts.append('upArrow: "/%s",' % p('up.png')) + parts.append('downArrow: "/%s",' % p('down.png')) + parts.append('upArrowPressed: "/%s",' % p('up-pressed.png')) + parts.append('downArrowPressed: "/%s",' % p('down-pressed.png')) + if username is not '': parts.append('voting: true,') parts.append('username: "%s",' % username) diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 63281f182..688fdbeaf 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -62,4 +62,7 @@ class WebSupportTranslator(HTMLTranslator): line=node.line, source=node.rawsource, treeloc='???') + if db_node_id == 30711: + import pdb + pdb.set_trace() return db_node_id From 978afa9c0df9ceba20f99c94dc5f3c82de87afd9 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 10:10:11 -0500 Subject: [PATCH 260/744] only comment on paragraphs --- .../websupport/storage/sqlalchemystorage.py | 2 +- sphinx/writers/websupport.py | 20 +++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index 02fa33b50..2e182311e 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -31,7 +31,7 @@ class SQLAlchemyStorage(StorageBackend): node = Node(document, line, source, treeloc) self.build_session.add(node) self.build_session.flush() - return node.id + return node def post_build(self): self.build_session.commit() diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 688fdbeaf..64a431d8e 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -15,7 +15,7 @@ class WebSupportTranslator(HTMLTranslator): """ Our custom HTML translator. """ - commentable_nodes = ['bullet_list', 'paragraph', 'desc'] + commentable_nodes = ['paragraph'] def __init__(self, builder, *args, **kwargs): HTMLTranslator.__init__(self, builder, *args, **kwargs) @@ -23,8 +23,7 @@ class WebSupportTranslator(HTMLTranslator): self.init_support() def init_support(self): - self.in_commentable = False - self.current_id = 0 + self.cur_node = None def dispatch_visit(self, node): if node.__class__.__name__ in self.commentable_nodes: @@ -39,30 +38,25 @@ class WebSupportTranslator(HTMLTranslator): def handle_visit_commentable(self, node): # If this node is nested inside another commentable node this # node will not be commented. - if not self.in_commentable: - self.in_commentable = True - node_id = self.add_db_node(node) + if self.cur_node is None: + self.cur_node = self.add_db_node(node) # We will place the node in the HTML id attribute. If the node # already has an id (for indexing purposes) put an empty # span with the existing id directly before this node's HTML. if node.attributes['ids']: self.body.append('<span id="%s"></span>' % node.attributes['ids'][0]) - node.attributes['ids'] = ['s%s' % node_id] + node.attributes['ids'] = ['s%s' % self.cur_node.id] node.attributes['classes'].append(self.comment_class) def handle_depart_commentable(self, node): - assert(self.in_commentable) if self.comment_class in node.attributes['classes']: - self.in_commentable = False + self.cur_node = None def add_db_node(self, node): storage = self.builder.app.storage db_node_id = storage.add_node(document=self.builder.cur_docname, line=node.line, - source=node.rawsource, + source=node.rawsource or node.astext(), treeloc='???') - if db_node_id == 30711: - import pdb - pdb.set_trace() return db_node_id From ac066fb54a15cc1e6a6b8e670b9f2446c6a94ffe Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 14:22:31 -0500 Subject: [PATCH 261/744] updated docs --- doc/web/api.rst | 43 +++- doc/web/frontend.rst | 6 - doc/web/quickstart.rst | 142 ++++++++---- doc/web/searchadapters.rst | 2 +- doc/web/storagebackends.rst | 26 +++ doc/websupport.rst | 1 - sphinx/websupport/__init__.py | 203 +++++++++++------- sphinx/websupport/search/__init__.py | 33 ++- sphinx/websupport/storage/__init__.py | 85 ++++++-- sphinx/websupport/storage/db.py | 3 +- .../websupport/storage/sqlalchemystorage.py | 8 +- sphinx/writers/websupport.py | 3 +- 12 files changed, 384 insertions(+), 171 deletions(-) delete mode 100644 doc/web/frontend.rst diff --git a/doc/web/api.rst b/doc/web/api.rst index fcd0513ee..b2b7ef952 100644 --- a/doc/web/api.rst +++ b/doc/web/api.rst @@ -10,10 +10,45 @@ The WebSupport Class The main API class for the web support package. All interactions with the web support package should occur through this class. - :param srcdir: the directory containing the reStructuredText files - :param outdir: the directory in which to place the built data - :param search: the search system to use - :param comments: an instance of a CommentBackend + The class takes the following keyword arguments: + + srcdir + The directory containing reStructuredText source files. + + builddir + The directory that build data and static files should be placed in. + This should be used when creating a :class:`WebSupport` object that + will be used to build data. + + datadir: + The directory that the web support data is in. This should be used + when creating a :class:`WebSupport` object that will be used to + retrieve data. + + search: + This may contain either a string (e.g. 'xapian') referencing a + built-in search adapter to use, or an instance of a subclass of + :class:`~sphinx.websupport.search.BaseSearch`. + + storage: + This may contain either a string representing a database uri, or an + instance of a subclass of + :class:`~sphinx.websupport.storage.StorageBackend`. If this is not + provided a new sqlite database will be created. + + moderation_callback: + A callable to be called when a new comment is added that is not + displayed. It must accept one argument: a dict representing the + comment that was added. + + staticdir: + If static files are served from a location besides "/static", this + should be a string with the name of that location + (e.g. '/static_files'). + + docroot: + If the documentation is not served from the base path of a URL, this + should be a string specifying that path (e.g. 'docs') Methods ~~~~~~~ diff --git a/doc/web/frontend.rst b/doc/web/frontend.rst deleted file mode 100644 index 5ffe16674..000000000 --- a/doc/web/frontend.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _websupportfrontend: - -Web Support Frontend -==================== - -More coming soon. \ No newline at end of file diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst index b0a60507a..302a4db0e 100644 --- a/doc/web/quickstart.rst +++ b/doc/web/quickstart.rst @@ -7,26 +7,26 @@ Building Documentation Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~ To make use of the web support package in your application you'll -need to build that data it uses. This data includes pickle files representing +need to build the data it uses. This data includes pickle files representing documents, search indices, and node data that is used to track where comments and other things are in a document. To do this you will need -to create an instance of the :class:`~sphinx.websupport.api.WebSupport` +to create an instance of the :class:`~sphinx.websupport.WebSupport` class and call it's :meth:`~sphinx.websupport.WebSupport.build` method:: from sphinx.websupport import WebSupport support = WebSupport(srcdir='/path/to/rst/sources/', - outdir='/path/to/build/outdir', + builddir='/path/to/build/outdir', search='xapian') support.build() This will read reStructuredText sources from `srcdir` and place the -necessary data in `outdir`. This directory contains all the data needed +necessary data in `builddir`. The `builddir` will contain two +sub-directories. One named "data" that contains all the data needed to display documents, search through documents, and add comments to -documents. It will also contain a subdirectory named "static", which -contains static files. These files will be linked to by Sphinx documents, -and should be served from "/static". +documents. The other directory will be called "static" and contains static +files that should be served from "/static". .. note:: @@ -37,7 +37,7 @@ and should be served from "/static". Integrating Sphinx Documents Into Your Webapp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Now that you have the data, it's time to do something useful with it. +Now that the data is built, it's time to do something useful with it. Start off by creating a :class:`~sphinx.websupport.WebSupport` object for your application:: @@ -59,8 +59,8 @@ This will return a dictionary containing the following items: * **sidebar**: The sidebar of the document as HTML * **relbar**: A div containing links to related documents * **title**: The title of the document -* **DOCUMENTATION_OPTIONS**: Javascript containing documentation options -* **COMMENT_OPTIONS**: Javascript containing comment options +* **css**: Links to css files used by Sphinx +* **js**: Javascript containing comment options This dict can then be used as context for templates. The goal is to be easy to integrate with your existing templating system. An example using @@ -74,13 +74,15 @@ easy to integrate with your existing templating system. An example using {{ document.title }} {%- endblock %} - {%- block js %} - <script type="text/javascript"> - {{ document.DOCUMENTATION_OPTIONS|safe }} - {{ document.COMMENT_OPTIONS|safe }} - </script> + {% block css %} {{ super() }} - <script type="text/javascript" src="/static/websupport.js"></script> + {{ document.css|safe }} + <link rel="stylesheet" href="/static/websupport-custom.css" type="text/css"> + {% endblock %} + + {%- block js %} + {{ super() }} + {{ document.js|safe }} {%- endblock %} {%- block relbar %} @@ -99,12 +101,12 @@ Authentication -------------- To use certain features such as voting it must be possible to authenticate -users. The details of the authentication are left to the your application. +users. The details of the authentication are left to your application. Once a user has been authenticated you can pass the user's details to certain :class:`~sphinx.websupport.WebSupport` methods using the *username* and *moderator* keyword arguments. The web support package will store the username with comments and votes. The only caveat is that if you allow users -to change their username, you must update the websupport package's data:: +to change their username you must update the websupport package's data:: support.update_username(old_username, new_username) @@ -113,18 +115,22 @@ should be a boolean representing whether the user has moderation privilieges. The default value for *moderator* is *False*. An example `Flask <http://flask.pocoo.org/>`_ function that checks whether -a user is logged in, and the retrieves a document is:: +a user is logged in and then retrieves a document is:: + + from sphinx.websupport.errors import * @app.route('/<path:docname>') def doc(docname): - if g.user: - document = support.get_document(docname, g.user.name, - g.user.moderator) - else: - document = support.get_document(docname) + username = g.user.name if g.user else '' + moderator = g.user.moderator if g.user else False + try: + document = support.get_document(docname, username, moderator) + except DocumentNotFoundError: + abort(404) return render_template('doc.html', document=document) The first thing to notice is that the *docname* is just the request path. +This makes accessing the correct document easy from a single view. If the user is authenticated then the username and moderation status are passed along with the docname to :meth:`~sphinx.websupport.WebSupport.get_document`. The web support package @@ -134,8 +140,12 @@ will then add this data to the COMMENT_OPTIONS that are used in the template. This only works works if your documentation is served from your document root. If it is served from another directory, you will - need to prefix the url route with that directory:: - + need to prefix the url route with that directory, and give the `docroot` + keyword argument when creating the web support object:: + + support = WebSupport(... + docroot='docs') + @app.route('/docs/<path:docname>') Performing Searches @@ -160,8 +170,8 @@ did to render our documents. That's because dict in the same format that :meth:`~sphinx.websupport.WebSupport.get_document` does. -Comments -~~~~~~~~ +Comments & Proposals +~~~~~~~~~~~~~~~~~~~~ Now that this is done it's time to define the functions that handle the AJAX calls from the script. You will need three functions. The first @@ -171,20 +181,29 @@ function is used to add a new comment, and will call the web support method @app.route('/docs/add_comment', methods=['POST']) def add_comment(): parent_id = request.form.get('parent', '') + node_id = request.form.get('node', '') text = request.form.get('text', '') + proposal = request.form.get('proposal', '') username = g.user.name if g.user is not None else 'Anonymous' - comment = support.add_comment(parent_id, text, username=username) + comment = support.add_comment(text, node_id='node_id', + parent_id='parent_id', + username=username, proposal=proposal) return jsonify(comment=comment) -Then next function handles the retrieval of comments for a specific node, -and is aptly named :meth:`~sphinx.websupport.WebSupport.get_data`:: +You'll notice that both a `parent_id` and `node_id` are sent with the +request. If the comment is being attached directly to a node, `parent_id` +will be empty. If the comment is a child of another comment, then `node_id` +will be empty. Then next function handles the retrieval of comments for a +specific node, and is aptly named +:meth:`~sphinx.websupport.WebSupport.get_data`:: @app.route('/docs/get_comments') def get_comments(): - user_id = g.user.id if g.user else None - parent_id = request.args.get('parent', '') - comments = support.get_data(parent_id, user_id) - return jsonify(comments=comments) + username = g.user.name if g.user else None + moderator = g.user.moderator if g.user else False + node_id = request.args.get('node', '') + data = support.get_data(parent_id, user_id) + return jsonify(**data) The final function that is needed will call :meth:`~sphinx.websupport.WebSupport.process_vote`, and will handle user @@ -201,12 +220,49 @@ votes on comments:: support.process_vote(comment_id, g.user.id, value) return "success" -.. note:: +Comment Moderation +~~~~~~~~~~~~~~~~~~ - Authentication is left up to your existing web application. If you do - not have an existing authentication system there are many readily - available for different frameworks. The web support system stores only - the user's unique integer `user_id` and uses this both for storing votes - and retrieving vote information. It is up to you to ensure that the - user_id passed in is unique, and that the user is authenticated. The - default backend will only allow one vote per comment per `user_id`. +By default all comments added through +:meth:`~sphinx.websupport.WebSupport.add_comment` are automatically +displayed. If you wish to have some form of moderation, you can pass +the `displayed` keyword argument:: + + comment = support.add_comment(text, node_id='node_id', + parent_id='parent_id', + username=username, proposal=proposal, + displayed=False) + +You can then create two new views to handle the moderation of comments. The +first will be called when a moderator decides a comment should be accepted +and displayed:: + + @app.route('/docs/accept_comment', methods=['POST']) + def accept_comment(): + moderator = g.user.moderator if g.user else False + comment_id = request.form.get('id') + support.accept_comment(comment_id, moderator=moderator) + return 'OK' + +The next is very similar, but used when rejecting a comment:: + + @app.route('/docs/reject_comment', methods=['POST']) + def reject_comment(): + moderator = g.user.moderator if g.user else False + comment_id = request.form.get('id') + support.reject_comment(comment_id, moderator=moderator) + return 'OK' + +To perform a custom action (such as emailing a moderator) when a new comment +is added but not displayed, you can pass callable to the +:class:`~sphinx.websupport.WebSupport` class when instantiating your support +object:: + + def moderation_callback(comment): + Do something... + + support = WebSupport(... + moderation_callback=moderation_callback) + +The moderation callback must take one argument, which will be the same +comment dict that is returned by add_comment. \ No newline at end of file diff --git a/doc/web/searchadapters.rst b/doc/web/searchadapters.rst index 83e928baa..e03fee81f 100644 --- a/doc/web/searchadapters.rst +++ b/doc/web/searchadapters.rst @@ -11,7 +11,7 @@ and pass that as the `search` keyword argument when you create the :class:`~sphinx.websupport.WebSupport` object:: support = Websupport(srcdir=srcdir, - outdir=outdir, + builddir=builddir, search=MySearch()) For more information about creating a custom search adapter, please see diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst index 4a10e1090..6411bf176 100644 --- a/doc/web/storagebackends.rst +++ b/doc/web/storagebackends.rst @@ -5,6 +5,22 @@ Storage Backends ================ +To create a custom storage backend you will need to subclass the +:class:`~StorageBackend` class. Then create an instance of the new class +and pass that as the `storage` keyword argument when you create the +:class:`~sphinx.websupport.WebSupport` object:: + + support = Websupport(srcdir=srcdir, + builddir=builddir, + storage=MyStorage()) + +For more information about creating a custom storage backend, please see +the documentation of the :class:`StorageBackend` class below. + +.. class:: StorageBackend + + Defines an interface for storage backends. + StorageBackend Methods ~~~~~~~~~~~~~~~~~~~~~~ @@ -16,4 +32,14 @@ StorageBackend Methods .. automethod:: sphinx.websupport.storage.StorageBackend.add_comment +.. automethod:: sphinx.websupport.storage.StorageBackend.delete_comment + .. automethod:: sphinx.websupport.storage.StorageBackend.get_data + +.. automethod:: sphinx.websupport.storage.StorageBackend.process_vote + +.. automethod:: sphinx.websupport.storage.StorageBackend.update_username + +.. automethod:: sphinx.websupport.storage.StorageBackend.accept_comment + +.. automethod:: sphinx.websupport.storage.StorageBackend.reject_comment \ No newline at end of file diff --git a/doc/websupport.rst b/doc/websupport.rst index c7833e7ab..59973d745 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -11,6 +11,5 @@ into your web application. To learn more read the web/quickstart web/api - web/frontend web/searchadapters web/storagebackends \ No newline at end of file diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 3215e856e..38ebd2340 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -52,6 +52,8 @@ class WebSupport(object): self._init_search(search) self._init_storage(storage) + self._make_base_comment_options() + def _init_storage(self, storage): if isinstance(storage, StorageBackend): self.storage = storage @@ -90,11 +92,11 @@ class WebSupport(object): """Build the documentation. Places the data into the `outdir` directory. Use it like this:: - support = WebSupport(srcdir, outdir, search='xapian') + support = WebSupport(srcdir, builddir, search='xapian') support.build() - This will read reStructured text files from `srcdir`. Then it - build the pickles and search index, placing them into `outdir`. + This will read reStructured text files from `srcdir`. Then it will + build the pickles and search index, placing them into `builddir`. It will also save node data to the database. """ if not self.srcdir: @@ -116,7 +118,7 @@ class WebSupport(object): be a dict object which can be used to render a template:: support = WebSupport(datadir=datadir) - support.get_document('index') + support.get_document('index', username, moderator) In most cases `docname` will be taken from the request path and passed directly to this function. In Flask, that would be something @@ -124,13 +126,28 @@ class WebSupport(object): @app.route('/<path:docname>') def index(docname): - q = request.args.get('q') - document = support.get_search_results(q) + username = g.user.name if g.user else '' + moderator = g.user.moderator if g.user else False + try: + document = support.get_document(docname, username, + moderator) + except DocumentNotFoundError: + abort(404) render_template('doc.html', document=document) The document dict that is returned contains the following items to be used during template rendering. + * **body**: The main body of the document as HTML + * **sidebar**: The sidebar of the document as HTML + * **relbar**: A div containing links to related documents + * **title**: The title of the document + * **css**: Links to css files used by Sphinx + * **js**: Javascript containing comment options + + This raises :class:`~sphinx.websupport.errors.DocumentNotFoundError` + if a document matching `docname` is not found. + :param docname: the name of the document to load. """ infilename = path.join(self.datadir, 'pickles', docname + '.fpickle') @@ -146,39 +163,6 @@ class WebSupport(object): document['js'] = comment_opts + '\n' + document['js'] return document - def _make_comment_options(self, username, moderator): - parts = ['<script type="text/javascript">', - 'var COMMENT_OPTIONS = {'] - if self.docroot is not '': - parts.append('addCommentURL: "/%s/%s",' % (self.docroot, - 'add_comment')) - parts.append('getCommentsURL: "/%s/%s",' % (self.docroot, - 'get_comments')) - parts.append('processVoteURL: "/%s/%s",' % (self.docroot, - 'process_vote')) - parts.append('acceptCommentURL: "/%s/%s",' % (self.docroot, - 'accept_comment')) - parts.append('rejectCommentURL: "/%s/%s",' % (self.docroot, - 'reject_comment')) - parts.append('deleteCommentURL: "/%s/%s",' % (self.docroot, - 'delete_comment')) - - if self.staticdir != 'static': - p = lambda file: '%s/_static/%s' % (self.staticdir, file) - parts.append('commentImage: "/%s",' % p('comment.png') ) - parts.append('upArrow: "/%s",' % p('up.png')) - parts.append('downArrow: "/%s",' % p('down.png')) - parts.append('upArrowPressed: "/%s",' % p('up-pressed.png')) - parts.append('downArrowPressed: "/%s",' % p('down-pressed.png')) - - if username is not '': - parts.append('voting: true,') - parts.append('username: "%s",' % username) - parts.append('moderator: %s' % str(moderator).lower()) - parts.append('};') - parts.append('</script>') - return '\n'.join(parts) - def get_search_results(self, q): """Perform a search for the query `q`, and create a set of search results. Then render the search results as html and @@ -200,37 +184,42 @@ class WebSupport(object): def get_data(self, node_id, username=None, moderator=False): """Get the comments and source associated with `node_id`. If - `user_id` is given vote information will be included with the - returned comments. The default CommentBackend returns dict with - two keys, *source*, and *comments*. *comments* is a list of - dicts that represent a comment, each having the following items: + `username` is given vote information will be included with the + returned comments. The default CommentBackend returns a dict with + two keys, *source*, and *comments*. *source* is raw source of the + node and is used as the starting point for proposals a user can + add. *comments* is a list of dicts that represent a comment, each + having the following items: - ============ ====================================================== - Key Contents - ============ ====================================================== - text The comment text. - username The username that was stored with the comment. - id The comment's unique identifier. - rating The comment's current rating. - age The time in seconds since the comment was added. - time A dict containing time information. It contains the - following keys: year, month, day, hour, minute, second, - iso, and delta. `iso` is the time formatted in ISO - 8601 format. `delta` is a printable form of how old - the comment is (e.g. "3 hours ago"). - vote If `user_id` was given, this will be an integer - representing the vote. 1 for an upvote, -1 for a - downvote, or 0 if unvoted. - node The node that the comment is attached to. If the - comment's parent is another comment rather than a - node, this will be null. - parent The id of the comment that this comment is attached - to if it is not attached to a node. - children A list of all children, in this format. - ============ ====================================================== + ============= ====================================================== + Key Contents + ============= ====================================================== + text The comment text. + username The username that was stored with the comment. + id The comment's unique identifier. + rating The comment's current rating. + age The time in seconds since the comment was added. + time A dict containing time information. It contains the + following keys: year, month, day, hour, minute, second, + iso, and delta. `iso` is the time formatted in ISO + 8601 format. `delta` is a printable form of how old + the comment is (e.g. "3 hours ago"). + vote If `user_id` was given, this will be an integer + representing the vote. 1 for an upvote, -1 for a + downvote, or 0 if unvoted. + node The id of the node that the comment is attached to. + If the comment's parent is another comment rather than + a node, this will be null. + parent The id of the comment that this comment is attached + to if it is not attached to a node. + children A list of all children, in this format. + proposal_diff An HTML representation of the differences between the + the current source and the user's proposed source. + ============= ====================================================== :param node_id: the id of the node to get comments for. - :param user_id: the id of the user viewing the comments. + :param username: the username of the user viewing the comments. + :param moderator: whether the user is a moderator. """ return self.storage.get_data(node_id, username, moderator) @@ -243,6 +232,10 @@ class WebSupport(object): `moderator` is False, the comment will only be deleted if the `username` matches the `username` on the comment. + This raises :class:`~sphinx.websupport.errors.UserNotAuthorizedError` + if moderator is False and `username` doesn't match username on the + comment. + :param comment_id: the id of the comment to delete. :param username: the username requesting the deletion. :param moderator: whether the requestor is a moderator. @@ -250,19 +243,19 @@ class WebSupport(object): self.storage.delete_comment(comment_id, username, moderator) def add_comment(self, text, node_id='', parent_id='', displayed=True, - username=None, rating=0, time=None, proposal=None, + username=None, time=None, proposal=None, moderator=False): """Add a comment to a node or another comment. Returns the comment in the same format as :meth:`get_comments`. If the comment is being attached to a node, pass in the node's id (as a string) with the node keyword argument:: - comment = support.add_comment(text, node=node_id) + comment = support.add_comment(text, node_id=node_id) If the comment is the child of another comment, provide the parent's id (as a string) with the parent keyword argument:: - comment = support.add_comment(text, parent=parent_id) + comment = support.add_comment(text, parent_id=parent_id) If you would like to store a username with the comment, pass in the optional `username` keyword argument:: @@ -274,10 +267,9 @@ class WebSupport(object): :param text: the text of the comment. :param displayed: for moderation purposes :param username: the username of the user making the comment. - :param rating: the starting rating of the comment, defaults to 0. :param time: the time the comment was created, defaults to now. """ - comment = self.storage.add_comment(text, displayed, username, rating, + comment = self.storage.add_comment(text, displayed, username, time, proposal, node_id, parent_id, moderator) if not displayed and self.moderation_callback: @@ -288,10 +280,9 @@ class WebSupport(object): """Process a user's vote. The web support package relies on the API user to perform authentication. The API user will typically receive a comment_id and value from a form, and then - make sure the user is authenticated. A unique integer `user_id` - (usually the User primary key) must be passed in, which will - also be used to retrieve the user's past voting information. - An example, once again in Flask:: + make sure the user is authenticated. A unique username must be + passed in, which will also be used to retrieve the user's past + voting data. An example, once again in Flask:: @app.route('/docs/process_vote', methods=['POST']) def process_vote(): @@ -301,11 +292,11 @@ class WebSupport(object): value = request.form.get('value') if value is None or comment_id is None: abort(400) - support.process_vote(comment_id, g.user.id, value) + support.process_vote(comment_id, g.user.name, value) return "success" :param comment_id: the comment being voted on - :param user_id: the unique integer id of the user voting + :param username: the unique username of the user voting :param value: 1 for an upvote, -1 for a downvote, 0 for an unvote. """ value = int(value) @@ -329,7 +320,11 @@ class WebSupport(object): def accept_comment(self, comment_id, moderator=False): """Accept a comment that is pending moderation. + This raises :class:`~sphinx.websupport.errors.UserNotAuthorizedError` + if moderator is False. + :param comment_id: The id of the comment that was accepted. + :param moderator: Whether the user making the request is a moderator. """ if not moderator: raise UserNotAuthorizedError() @@ -338,8 +333,60 @@ class WebSupport(object): def reject_comment(self, comment_id, moderator=False): """Reject a comment that is pending moderation. + This raises :class:`~sphinx.websupport.errors.UserNotAuthorizedError` + if moderator is False. + :param comment_id: The id of the comment that was accepted. + :param moderator: Whether the user making the request is a moderator. """ if not moderator: raise UserNotAuthorizedError() self.storage.reject_comment(comment_id) + + def _make_base_comment_options(self): + """Helper method to create the part of the COMMENT_OPTIONS javascript + that remains the same throughout the lifetime of the + :class:`~sphinx.websupport.WebSupport` object. + """ + parts = ['<script type="text/javascript">', + 'var COMMENT_OPTIONS = {'] + if self.docroot is not '': + parts.append('addCommentURL: "/%s/%s",' % (self.docroot, + 'add_comment')) + parts.append('getCommentsURL: "/%s/%s",' % (self.docroot, + 'get_comments')) + parts.append('processVoteURL: "/%s/%s",' % (self.docroot, + 'process_vote')) + parts.append('acceptCommentURL: "/%s/%s",' % (self.docroot, + 'accept_comment')) + parts.append('rejectCommentURL: "/%s/%s",' % (self.docroot, + 'reject_comment')) + parts.append('deleteCommentURL: "/%s/%s",' % (self.docroot, + 'delete_comment')) + + if self.staticdir != 'static': + p = lambda file: '%s/_static/%s' % (self.staticdir, file) + parts.append('commentImage: "/%s",' % p('comment.png') ) + parts.append('upArrow: "/%s",' % p('up.png')) + parts.append('downArrow: "/%s",' % p('down.png')) + parts.append('upArrowPressed: "/%s",' % p('up-pressed.png')) + parts.append('downArrowPressed: "/%s",' % p('down-pressed.png')) + + self.base_comment_opts = '\n'.join(parts) + + def _make_comment_options(self, username, moderator): + """Helper method to create the parts of the COMMENT_OPTIONS + javascript that are unique to each request. + + :param username: The username of the user making the request. + :param moderator: Whether the user making the request is a moderator. + """ + parts = [self.base_comment_opts] + if username is not '': + parts.append('voting: true,') + parts.append('username: "%s",' % username) + parts.append('moderator: %s' % str(moderator).lower()) + parts.append('};') + parts.append('</script>') + return '\n'.join(parts) + diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index 0e613222e..80f91ab1d 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -20,7 +20,7 @@ class BaseSearch(object): is a list of pagenames that will be reindexed. You may want to remove these from the search index before indexing begins. - `param changed` is a list of pagenames that will be re-indexed + :param changed: a list of pagenames that will be re-indexed """ pass @@ -37,11 +37,9 @@ class BaseSearch(object): won't want to override this unless you need access to the `doctree`. Override :meth:`add_document` instead. - `pagename` is the name of the page to be indexed - - `title` is the title of the page to be indexed - - `doctree` is the docutils doctree representation of the page + :param pagename: the name of the page to be indexed + :param title: the title of the page to be indexed + :param doctree: is the docutils doctree representation of the page """ self.add_document(pagename, title, doctree.astext()) @@ -50,18 +48,16 @@ class BaseSearch(object): This method should should do everything necessary to add a single document to the search index. - `pagename` is name of the page being indexed. - It is the combination of the source files relative path and filename, + `pagename` is name of the page being indexed. It is the combination + of the source files relative path and filename, minus the extension. For example, if the source file is "ext/builders.rst", the `pagename` would be "ext/builders". This will need to be returned with search results when processing a query. - - `title` is the page's title, and will need to be returned with - search results. - - `text` is the full text of the page. You probably want to store this - somehow to use while creating the context for search results. + + :param pagename: the name of the page being indexed + :param title: the page's title + :param text: the full text of the page """ raise NotImplementedError() @@ -73,7 +69,7 @@ class BaseSearch(object): don't want to use the included :meth:`extract_context` method. Override :meth:`handle_query` instead. - `q` is the search query string. + :param q: the search query string. """ self.context_re = re.compile('|'.join(q.split()), re.I) return self.handle_query(q) @@ -91,6 +87,8 @@ class BaseSearch(object): The :meth:`extract_context` method is provided as a simple way to create the `context`. + + :param q: the search query """ raise NotImplementedError() @@ -98,9 +96,8 @@ class BaseSearch(object): """Extract the context for the search query from the documents full `text`. - `text` is the full text of the document to create the context for. - - `length` is the length of the context snippet to return. + :param text: the full text of the document to create the context for + :param length: the length of the context snippet to return. """ res = self.context_re.search(text) if res is None: diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py index 6948c8c73..70b23150d 100644 --- a/sphinx/websupport/storage/__init__.py +++ b/sphinx/websupport/storage/__init__.py @@ -16,16 +16,14 @@ class StorageBackend(object): """ pass - def add_node(self, document, line, source, treeloc): + def add_node(self, document, line, source): """Add a node to the StorageBackend. - `document` is the name of the document the node belongs to. + :param document: the name of the document the node belongs to. - `line` is the line in the source where the node begins. + :param line: the line in the source where the node begins. - `source` is the source files name. - - `treeloc` is for future use. + :param source: the source files name. """ raise NotImplementedError() @@ -35,14 +33,77 @@ class StorageBackend(object): """ pass - def add_comment(self, text, displayed, username, rating, time, - proposal, node, parent): - """Called when a comment is being added.""" + def add_comment(self, text, displayed, username, time, + proposal, node_id, parent_id, moderator): + """Called when a comment is being added. + + :param text: the text of the comment + :param displayed: whether the comment should be displayed + :param username: the name of the user adding the comment + :param time: a date object with the time the comment was added + :param proposal: the text of the proposal the user made + :param node_id: the id of the node that the comment is being added to + :param parent_id: the id of the comment's parent comment. + :param moderator: whether the user adding the comment is a moderator + """ raise NotImplementedError() - def get_data(self, parent_id, user_id, moderator): - """Called to retrieve all comments for a node.""" + def delete_comment(self, comment_id, username, moderator): + """Delete a comment. + + Raises :class:`~sphinx.websupport.errors.UserNotAuthorizedError` + if moderator is False and `username` doesn't match the username + on the comment. + + :param comment_id: The id of the comment being deleted. + :param username: The username of the user requesting the deletion. + :param moderator: Whether the user is a moderator. + """ raise NotImplementedError() - def process_vote(self, comment_id, user_id, value): + def get_data(self, node_id, username, moderator): + """Called to retrieve all data for a node. This should return a + dict with two keys, *source* and *comments* as described by + :class:`~sphinx.websupport.WebSupport`'s + :meth:`~sphinx.websupport.WebSupport.get_data` method. + + :param node_id: The id of the node to get data for. + :param username: The name of the user requesting the data. + :param moderator: Whether the requestor is a moderator. + """ + raise NotImplementedError() + + def process_vote(self, comment_id, username, value): + """Process a vote that is being cast. `value` will be either -1, 0, + or 1. + + :param comment_id: The id of the comment being voted on. + :param username: The username of the user casting the vote. + :param value: The value of the vote being cast. + """ + raise NotImplementedError() + + def update_username(self, old_username, new_username): + """If a user is allowed to change their username this method should + be called so that there is not stagnate data in the storage system. + + :param old_username: The username being changed. + :param new_username: What the username is being changed to. + """ + raise NotImplementedError() + + def accept_comment(self, comment_id): + """Called when a moderator accepts a comment. After the method is + called the comment should be displayed to all users. + + :param comment_id: The id of the comment being accepted. + """ + raise NotImplementedError() + + def reject_comment(self, comment_id): + """Called when a moderator rejects a comment. The comment should + then be deleted. + + :param comment_id: The id of the comment being accepted. + """ raise NotImplementedError() diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index 12c1e1d5e..983eb66d9 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -81,11 +81,10 @@ class Node(Base): return comments - def __init__(self, document, line, source, treeloc): + def __init__(self, document, line, source): self.document = document self.line = line self.source = source - self.treeloc = treeloc class Comment(Base): __tablename__ = db_prefix + 'comments' diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index 2e182311e..7a906dcba 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -27,8 +27,8 @@ class SQLAlchemyStorage(StorageBackend): def pre_build(self): self.build_session = Session() - def add_node(self, document, line, source, treeloc): - node = Node(document, line, source, treeloc) + def add_node(self, document, line, source): + node = Node(document, line, source) self.build_session.add(node) self.build_session.flush() return node @@ -37,7 +37,7 @@ class SQLAlchemyStorage(StorageBackend): self.build_session.commit() self.build_session.close() - def add_comment(self, text, displayed, username, rating, time, + def add_comment(self, text, displayed, username, time, proposal, node_id, parent_id, moderator): session = Session() @@ -55,7 +55,7 @@ class SQLAlchemyStorage(StorageBackend): else: proposal_diff = None - comment = Comment(text, displayed, username, rating, + comment = Comment(text, displayed, username, 0, time or datetime.now(), proposal, proposal_diff) session.add(comment) session.flush() diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 64a431d8e..05bc2c8b3 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -57,6 +57,5 @@ class WebSupportTranslator(HTMLTranslator): storage = self.builder.app.storage db_node_id = storage.add_node(document=self.builder.cur_docname, line=node.line, - source=node.rawsource or node.astext(), - treeloc='???') + source=node.rawsource or node.astext()) return db_node_id From 71a14cdd34393c11ecc04c43859e6f5b36295b21 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 14:36:19 -0500 Subject: [PATCH 262/744] updated CHANGES.jacobmason --- CHANGES.jacobmason | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGES.jacobmason b/CHANGES.jacobmason index bd87c71c6..c445006c2 100644 --- a/CHANGES.jacobmason +++ b/CHANGES.jacobmason @@ -12,4 +12,16 @@ June 21 - June 26: Implement server side search with two search adapters, one for Xapian and one for Whoosh June 28 - July 12: Implement voting system on the backend, and created a -jQuery script to handle voting on the frontend. \ No newline at end of file +jQuery script to handle voting on the frontend. + +July 13 - July 19: Added documentation for the web support package. + +July 20 - July 27: Added a system to allow user's to propose changes to +documentation along with comments. + +July 28 - August 3: Added tests for the web support package. Refactored +sqlalchemy storage to be more efficient. + +August 4 - August 7: Added comment moderation system. Added more +documentation. General code cleanup. + From c5e43ca3c9aefa6191b862b17ed1a4136a0b12f3 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 14:43:32 -0500 Subject: [PATCH 263/744] create a searcher for each query --- sphinx/websupport/search/whooshsearch.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 257393a6a..0f4635314 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -31,7 +31,6 @@ class WhooshSearch(BaseSearch): self.index = index.open_dir(db_path) else: self.index = index.create_in(db_path, schema=self.schema) - self.searcher = self.index.searcher() def init_indexing(self, changed=[]): for changed_path in changed: @@ -40,8 +39,6 @@ class WhooshSearch(BaseSearch): def finish_indexing(self): self.index_writer.commit() - # Create a new searcher so changes can be seen immediately - self.searcher = self.index.searcher() def add_document(self, pagename, title, text): self.index_writer.add_document(path=unicode(pagename), @@ -49,7 +46,8 @@ class WhooshSearch(BaseSearch): text=text) def handle_query(self, q): - whoosh_results = self.searcher.find('text', q) + searcher = self.index.searcher() + whoosh_results = searcher.find('text', q) results = [] for result in whoosh_results: context = self.extract_context(result['text']) From 3f29ff6204501bc9ff3f1c763c5c90148bd333ca Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 14:47:18 -0500 Subject: [PATCH 264/744] moved NullSearchException to sphinx.websupport.errors --- sphinx/websupport/errors.py | 7 ++++++- sphinx/websupport/search/nullsearch.py | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py index e78abc217..53106dfb8 100644 --- a/sphinx/websupport/errors.py +++ b/sphinx/websupport/errors.py @@ -10,7 +10,8 @@ """ __all__ = ['DocumentNotFoundError', 'SrcdirNotSpecifiedError', - 'UserNotAuthorizedError', 'CommentNotAllowedError'] + 'UserNotAuthorizedError', 'CommentNotAllowedError', + 'NullSearchException'] class DocumentNotFoundError(Exception): pass @@ -26,3 +27,7 @@ class UserNotAuthorizedError(Exception): class CommentNotAllowedError(Exception): pass + + +class NullSearchException(Exception): + pass diff --git a/sphinx/websupport/search/nullsearch.py b/sphinx/websupport/search/nullsearch.py index ad3d7daef..743983c48 100644 --- a/sphinx/websupport/search/nullsearch.py +++ b/sphinx/websupport/search/nullsearch.py @@ -10,11 +10,12 @@ """ from sphinx.websupport.search import BaseSearch - -class NullSearchException(Exception): - pass +from sphinx.websupport.errors import * class NullSearch(BaseSearch): + """A search adapter that does nothing. Used when no search adapter + is specified. + """ def feed(self, pagename, title, doctree): pass From 3b3e001eb970846e4711e9a0b93d2296c6a2e29b Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 15:22:52 -0500 Subject: [PATCH 265/744] added more comments/docstrings --- sphinx/websupport/storage/db.py | 31 +++++++- sphinx/websupport/storage/differ.py | 73 +++++++++++-------- .../websupport/storage/sqlalchemystorage.py | 16 ++-- 3 files changed, 80 insertions(+), 40 deletions(-) diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index 983eb66d9..64f7f3e25 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -34,6 +34,12 @@ class Node(Base): source = Column(Text, nullable=False) def nested_comments(self, username, moderator): + """Create a tree of comments. First get all comments that are + descendents of this node, then convert them to a tree form. + + :param username: the name of the user to get comments for. + :param moderator: whether the user is moderator. + """ session = Session() if username: @@ -45,24 +51,30 @@ class Node(Base): cvalias = aliased(CommentVote, sq) q = session.query(Comment, cvalias.value).outerjoin(cvalias) else: + # If a username is not provided, we don't need to join with + # CommentVote. q = session.query(Comment) # Filter out all comments not descending from this node. q = q.filter(Comment.path.like(str(self.id) + '.%')) - # Filter out non-displayed comments if this isn't a moderator. + if not moderator: q = q.filter(Comment.displayed == True) + # Retrieve all results. Results must be ordered by Comment.path # so that we can easily transform them from a flat list to a tree. results = q.order_by(Comment.path).all() session.close() - # We now need to convert the flat list of results to a nested - # lists to form the comment tree. Results will by ordered by - # the materialized path. return self._nest_comments(results, username) def _nest_comments(self, results, username): + """Given the flat list of results, convert the list into a + tree. + + :param results: the flat list of comments + :param username: the name of the user requesting the comments. + """ comments = [] list_stack = [comments] for r in results: @@ -87,6 +99,7 @@ class Node(Base): self.source = source class Comment(Base): + """An individual Comment being stored.""" __tablename__ = db_prefix + 'comments' id = Column(Integer, primary_key=True) @@ -110,6 +123,9 @@ class Comment(Base): self.proposal_diff = proposal_diff def set_path(self, node_id, parent_id): + """Set the materialized path for this comment.""" + # This exists because the path can't be set until the session has + # been flushed and this Comment has an id. if node_id: self.path = '%s.%s' % (node_id, self.id) else: @@ -120,6 +136,9 @@ class Comment(Base): self.path = '%s.%s' % (parent_path, self.id) def serializable(self, vote=0): + """Creates a serializable representation of the comment. This is + converted to JSON, and used on the client side. + """ delta = datetime.now() - self.time time = {'year': self.time.year, @@ -149,6 +168,9 @@ class Comment(Base): 'children': []} def pretty_delta(self, delta): + """Create a pretty representation of the Comment's age. + (e.g. 2 minutes). + """ days = delta.days seconds = delta.seconds hours = seconds / 3600 @@ -162,6 +184,7 @@ class Comment(Base): return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt class CommentVote(Base): + """A vote a user has made on a Comment.""" __tablename__ = db_prefix + 'commentvote' username = Column(String(64), primary_key=True) diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py index c82ba7427..068d7e6fc 100644 --- a/sphinx/websupport/storage/differ.py +++ b/sphinx/websupport/storage/differ.py @@ -14,39 +14,18 @@ from cgi import escape from difflib import Differ class CombinedHtmlDiff(object): - + """Create an HTML representation of the differences between two pieces + of text. + """ highlight_regex = re.compile(r'([\+\-\^]+)') - def _highlight_text(self, text, next, tag): - next = next[2:] - new_text = [] - start = 0 - for match in self.highlight_regex.finditer(next): - new_text.append(text[start:match.start()]) - new_text.append('<%s>' % tag) - new_text.append(text[match.start():match.end()]) - new_text.append('</%s>' % tag) - start = match.end() - new_text.append(text[start:]) - return ''.join(new_text) - - def _handle_line(self, line, next=None): - prefix = line[0] - text = line[2:] - - if prefix == ' ': - return text - elif prefix == '?': - return '' - - if next is not None and next[0] == '?': - tag = 'ins' if prefix == '+' else 'del' - text = self._highlight_text(text, next, tag) - css_class = 'prop_added' if prefix == '+' else 'prop_removed' - - return '<span class="%s">%s</span>\n' % (css_class, text.rstrip()) - def make_html(self, source, proposal): + """Return the HTML representation of the differences between + `source` and `proposal`. + + :param source: the original text + :param proposal: the proposed text + """ proposal = escape(proposal) differ = Differ() @@ -64,3 +43,37 @@ class CombinedHtmlDiff(object): self._handle_line(line) break return ''.join(html) + + def _handle_line(self, line, next=None): + """Handle an individual line in a diff.""" + prefix = line[0] + text = line[2:] + + if prefix == ' ': + return text + elif prefix == '?': + return '' + + if next is not None and next[0] == '?': + tag = 'ins' if prefix == '+' else 'del' + text = self._highlight_text(text, next, tag) + css_class = 'prop_added' if prefix == '+' else 'prop_removed' + + return '<span class="%s">%s</span>\n' % (css_class, text.rstrip()) + + def _highlight_text(self, text, next, tag): + """Highlight the specific changes made to a line by adding + <ins> and <del> tags. + """ + next = next[2:] + new_text = [] + start = 0 + for match in self.highlight_regex.finditer(next): + new_text.append(text[start:match.start()]) + new_text.append('<%s>' % tag) + new_text.append(text[match.start():match.end()]) + new_text.append('</%s>' % tag) + start = match.end() + new_text.append(text[start:]) + return ''.join(new_text) + diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index 7a906dcba..553450d32 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -18,6 +18,9 @@ from sphinx.websupport.storage.db import Base, Node, Comment, CommentVote,\ from sphinx.websupport.storage.differ import CombinedHtmlDiff class SQLAlchemyStorage(StorageBackend): + """A :class:`~sphinx.websupport.storage.StorageBackend` using + SQLAlchemy. + """ def __init__(self, engine): self.engine = engine Base.metadata.bind = engine @@ -40,7 +43,8 @@ class SQLAlchemyStorage(StorageBackend): def add_comment(self, text, displayed, username, time, proposal, node_id, parent_id, moderator): session = Session() - + proposal_diff = None + if node_id and proposal: node = session.query(Node).filter(Node.id == node_id).one() differ = CombinedHtmlDiff() @@ -51,19 +55,18 @@ class SQLAlchemyStorage(StorageBackend): if not parent.displayed: raise CommentNotAllowedError( "Can't add child to a parent that is not displayed") - proposal_diff = None - else: - proposal_diff = None comment = Comment(text, displayed, username, 0, time or datetime.now(), proposal, proposal_diff) session.add(comment) session.flush() + # We have to flush the session before setting the path so the + # Comment has an id. comment.set_path(node_id, parent_id) session.commit() - comment = comment.serializable() + d = comment.serializable() session.close() - return comment + return d def delete_comment(self, comment_id, username, moderator): session = Session() @@ -72,6 +75,7 @@ class SQLAlchemyStorage(StorageBackend): if moderator or comment.username == username: comment.username = '[deleted]' comment.text = '[deleted]' + comment.proposal = '' session.commit() session.close() else: From e599abe386ed4a548be1c5f8d6a89014c78d368b Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 15:46:54 -0500 Subject: [PATCH 266/744] A few changes to sqlalchemystorage --- .../websupport/storage/sqlalchemystorage.py | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index 553450d32..94318a965 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -11,6 +11,8 @@ from datetime import datetime +from sqlalchemy.orm import aliased + from sphinx.websupport.errors import * from sphinx.websupport.storage import StorageBackend from sphinx.websupport.storage.db import Base, Node, Comment, CommentVote,\ @@ -92,12 +94,13 @@ class SQLAlchemyStorage(StorageBackend): def process_vote(self, comment_id, username, value): session = Session() - vote = session.query(CommentVote).filter( - CommentVote.comment_id == comment_id).filter( - CommentVote.username == username).first() - comment = session.query(Comment).filter( - Comment.id == comment_id).first() + subquery = session.query(CommentVote).filter( + CommentVote.username == username).subquery() + vote_alias = aliased(CommentVote, subquery) + q = session.query(Comment, vote_alias).outerjoin(vote_alias).filter( + Comment.id == comment_id) + comment, vote = q.one() if vote is None: vote = CommentVote(comment_id, username, value) @@ -105,32 +108,39 @@ class SQLAlchemyStorage(StorageBackend): else: comment.rating += value - vote.value vote.value = value + session.add(vote) session.commit() session.close() def update_username(self, old_username, new_username): session = Session() + session.query(Comment).filter(Comment.username == old_username).\ update({Comment.username: new_username}) session.query(CommentVote).\ filter(CommentVote.username == old_username).\ update({CommentVote.username: new_username}) + session.commit() session.close() def accept_comment(self, comment_id): session = Session() - comment = session.query(Comment).\ - filter(Comment.id == comment_id).one() - comment.displayed = True + + comment = session.query(Comment).filter( + Comment.id == comment_id).update( + {Comment.displayed: True}) + session.commit() session.close() def reject_comment(self, comment_id): session = Session() + comment = session.query(Comment).\ filter(Comment.id == comment_id).one() session.delete(comment) + session.commit() session.close() From 861289ad8e794919b8d2997199a7d08e6c5755eb Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 16:47:29 -0500 Subject: [PATCH 267/744] hide proposal textarea after a comment is added --- sphinx/themes/basic/static/websupport.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index aeef3ece1..62912d6b8 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -212,18 +212,22 @@ function addComment(form) { // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); + var node_id = form.find('input[name="node"]').val(); // Send the comment to the server. $.ajax({ type: "POST", url: opts.addCommentURL, dataType: 'json', - data: {node: form.find('input[name="node"]').val(), + data: {node: node_id, parent: form.find('input[name="parent"]').val(), text: form.find('textarea[name="comment"]').val(), proposal: form.find('textarea[name="proposal"]').val()}, success: function(data, textStatus, error) { // Reset the form. + if (node_id) { + hideProposeChange(node_id); + } form.find('textarea') .val('') .add(form.find('input')) @@ -378,7 +382,7 @@ $('a.hide_propose_change').hide(); $('a.show_propose_change').show(); var textarea = $('textarea[name="proposal"]'); - textarea.val(''); + textarea.val('').removeAttr('disabled'); textarea.slideUp('fast'); }; From 18ef86f0452595e0d7a414ceddfc9615ca476549 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 16:53:29 -0500 Subject: [PATCH 268/744] fixed comment reply width --- sphinx/themes/basic/static/websupport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 62912d6b8..c4fa57555 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -630,7 +630,7 @@ var replyTemplate = ' <li>\ <div class="reply_div" id="rd<%id%>">\ <form id="rf<%id%>">\ - <textarea name="comment"></textarea>\ + <textarea name="comment" cols="80"></textarea>\ <input type="submit" value="add reply" />\ <input type="hidden" name="parent" value="<%id%>" />\ <input type="hidden" name="node" value="" />\ From 0f98c779c0813ffe3413f8678bb7c8cd88aabdab Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 18:31:13 -0500 Subject: [PATCH 269/744] added get_metadata --- sphinx/websupport/__init__.py | 15 ++++++++++++++- sphinx/websupport/storage/__init__.py | 9 +++++++++ sphinx/websupport/storage/db.py | 5 +++++ sphinx/websupport/storage/sqlalchemystorage.py | 14 ++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 38ebd2340..090fee1ad 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -160,7 +160,11 @@ class WebSupport(object): document = pickle.load(f) comment_opts = self._make_comment_options(username, moderator) - document['js'] = comment_opts + '\n' + document['js'] + comment_metadata = self.storage.get_metadata(docname, moderator) + + document['js'] = '\n'.join([comment_opts, + self._make_metadata(comment_metadata), + document['js']]) return document def get_search_results(self, q): @@ -390,3 +394,12 @@ class WebSupport(object): parts.append('</script>') return '\n'.join(parts) + def _make_metadata(self, data): + node_js = ', '.join(['%s: %s' % (node_id, comment_count) + for node_id, comment_count in data.iteritems()]) + js = """ +<script type="text/javascript"> + var COMMENT_METADATA = {%s}; +</script>""" % node_js + return js + diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py index 70b23150d..24d4ade55 100644 --- a/sphinx/websupport/storage/__init__.py +++ b/sphinx/websupport/storage/__init__.py @@ -61,6 +61,15 @@ class StorageBackend(object): """ raise NotImplementedError() + def get_metadata(self, docname, moderator): + """Get metadata for a document. This is currently just a dict + of node_id's with associated comment counts. + + :param docname: the name of the document to get metadata for. + :param moderator: whether the requester is a moderator. + """ + raise NotImplementedError() + def get_data(self, node_id, username, moderator): """Called to retrieve all data for a node. This should return a dict with two keys, *source* and *comments* as described by diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index 64f7f3e25..4a84cd086 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -112,6 +112,9 @@ class Comment(Base): proposal_diff = Column(Text) path = Column(String(256), index=True) + node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) + node = relation(Node, backref="comments") + def __init__(self, text, displayed, username, rating, time, proposal, proposal_diff): self.text = text @@ -127,12 +130,14 @@ class Comment(Base): # This exists because the path can't be set until the session has # been flushed and this Comment has an id. if node_id: + self.node_id = node_id self.path = '%s.%s' % (node_id, self.id) else: session = Session() parent_path = session.query(Comment.path).\ filter(Comment.id == parent_id).one().path session.close() + self.node_id = parent_path.split('.')[0] self.path = '%s.%s' % (parent_path, self.id) def serializable(self, vote=0): diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index 94318a965..1aaa84738 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -12,6 +12,7 @@ from datetime import datetime from sqlalchemy.orm import aliased +from sqlalchemy.sql import func from sphinx.websupport.errors import * from sphinx.websupport.storage import StorageBackend @@ -84,6 +85,19 @@ class SQLAlchemyStorage(StorageBackend): session.close() raise UserNotAuthorizedError() + def get_metadata(self, docname, moderator): + session = Session() + subquery = session.query( + Comment.id, Comment.node_id, + func.count('*').label('comment_count')).group_by( + Comment.node_id).subquery() + nodes = session.query(Node.id, subquery.c.comment_count).outerjoin( + (subquery, Node.id==subquery.c.node_id)).filter( + Node.document==docname) + session.close() + session.commit() + return dict([(k, v or 0) for k, v in nodes]) + def get_data(self, node_id, username, moderator): session = Session() node = session.query(Node).filter(Node.id == node_id).one() From d77cbf3043b88a31424d44538b8b8bb5fdc1e490 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 9 Aug 2010 19:07:18 -0500 Subject: [PATCH 270/744] added comment metadata to frontend --- sphinx/themes/basic/static/comment-bright.png | Bin 0 -> 3500 bytes sphinx/themes/basic/static/websupport.js | 8 +++++++- sphinx/websupport/__init__.py | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 sphinx/themes/basic/static/comment-bright.png diff --git a/sphinx/themes/basic/static/comment-bright.png b/sphinx/themes/basic/static/comment-bright.png new file mode 100644 index 0000000000000000000000000000000000000000..551517b8c83b76f734ff791f847829a760ad1903 GIT binary patch literal 3500 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 a`ab|E?o=hKA+1LM0000<MNUMnLSTYql%5L! literal 0 HcmV?d00001 diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index c4fa57555..99d1a2217 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -617,9 +617,14 @@ */ $.fn.comment = function() { return this.each(function() { + var id = $(this).attr('id').substring(1); + var count = COMMENT_METADATA[id] + var title = count + ' comment' + (count == 1 ? '' : 's'); + var image = count > 0 ? opts.commentBrightImage : opts.commentImage; $(this).append( $('<a href="#" class="sphinx_comment"></a>') - .html('<img src="' + opts.commentImage + '" alt="comment" />') + .html('<img src="' + image + '" alt="comment" />') + .attr('title', title) .click(function(event) { event.preventDefault(); show($(this).parent().attr('id')); @@ -727,6 +732,7 @@ rejectCommentURL: '/reject_comment', rejectCommentURL: '/delete_comment', commentImage: '/static/_static/comment.png', + commentBrightImage: '/static/_static/comment-bright.png', upArrow: '/static/_static/up.png', downArrow: '/static/_static/down.png', upArrowPressed: '/static/_static/up-pressed.png', diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 090fee1ad..76715d14a 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -371,6 +371,8 @@ class WebSupport(object): if self.staticdir != 'static': p = lambda file: '%s/_static/%s' % (self.staticdir, file) parts.append('commentImage: "/%s",' % p('comment.png') ) + parts.append( + 'commentBrightImage: "/%s",' % p('comment-bright.png') ) parts.append('upArrow: "/%s",' % p('up.png')) parts.append('downArrow: "/%s",' % p('down.png')) parts.append('upArrowPressed: "/%s",' % p('up-pressed.png')) From 828ad48e3de5036e1fc7e48f6687976abdb34f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 12:25:48 +0200 Subject: [PATCH 271/744] Initial version tracking implementation --- sphinx/util/__init__.py | 36 +++++++ sphinx/versioning.py | 145 ++++++++++++++++++++++++++ tests/root/contents.txt | 1 + tests/root/versioning/added.txt | 20 ++++ tests/root/versioning/deleted.txt | 12 +++ tests/root/versioning/deleted_end.txt | 11 ++ tests/root/versioning/index.txt | 11 ++ tests/root/versioning/insert.txt | 18 ++++ tests/root/versioning/modified.txt | 17 +++ tests/root/versioning/original.txt | 15 +++ tests/test_versioning.py | 88 ++++++++++++++++ 11 files changed, 374 insertions(+) create mode 100644 sphinx/versioning.py create mode 100644 tests/root/versioning/added.txt create mode 100644 tests/root/versioning/deleted.txt create mode 100644 tests/root/versioning/deleted_end.txt create mode 100644 tests/root/versioning/index.txt create mode 100644 tests/root/versioning/insert.txt create mode 100644 tests/root/versioning/modified.txt create mode 100644 tests/root/versioning/original.txt create mode 100644 tests/test_versioning.py diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index ec48009f4..a434f3a82 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -19,6 +19,7 @@ import posixpath import traceback from os import path from codecs import open +from collections import deque import docutils from docutils.utils import relative_path @@ -297,3 +298,38 @@ def format_exception_cut_frames(x=1): res += tbres[-x:] res += traceback.format_exception_only(typ, val) return ''.join(res) + +class PeekableIterator(object): + """ + An iterator which wraps any iterable and makes it possible to peek to see + what's the next item. + """ + def __init__(self, iterable): + self.remaining = deque() + self._iterator = iter(iterable) + + def __iter__(self): + return self + + def next(self): + """ + Returns the next item from the iterator. + """ + if self.remaining: + return self.remaining.popleft() + return self._iterator.next() + + def push(self, item): + """ + Pushes the `item` on the internal stack, it will be returned on the + next :meth:`next` call. + """ + self.remaining.append(item) + + def peek(self): + """ + Returns the next item without changing the state of the iterator. + """ + item = self.next() + self.push(item) + return item diff --git a/sphinx/versioning.py b/sphinx/versioning.py new file mode 100644 index 000000000..51fd28a94 --- /dev/null +++ b/sphinx/versioning.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +""" + sphinx.versioning + ~~~~~~~~~~~~~~~~~ + + Implements the low-level algorithms Sphinx uses for the versioning of + doctrees. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from uuid import uuid4 +from itertools import izip_longest, product +from difflib import SequenceMatcher + +from sphinx.util import PeekableIterator + +def add_uids(doctree, condition): + """ + Adds a unique id to every node in the `doctree` which matches the condition + and yields it. + + :param doctree: + A :class:`docutils.nodes.document` instance. + + :param condition: + A callable which returns either ``True`` or ``False`` for a given node. + """ + for node in doctree.traverse(condition): + node.uid = uuid4().hex + yield node + +def merge_node(old, new): + """ + Merges the `old` node with the `new` one, if it's successful the `new` node + get's the unique identifier of the `new` one and ``True`` is returned. If + the merge is unsuccesful ``False`` is returned. + """ + equals, changed, replaced = make_diff(old.rawsource, + new.rawsource) + if equals or changed: + new.uid = old.uid + return True + return False + +def merge_doctrees(old, new, condition): + """ + Merges the `old` doctree with the `new` one while looking at nodes matching + the `condition`. + + Each node which replaces another one or has been added to the `new` doctree + will be yielded. + + :param condition: + A callable which returns either ``True`` or ``False`` for a given node. + """ + old_iter = PeekableIterator(old.traverse(condition)) + new_iter = PeekableIterator(new.traverse(condition)) + old_nodes = [] + new_nodes = [] + for old_node, new_node in izip_longest(old_iter, new_iter): + if old_node is None: + new_nodes.append(new_node) + continue + if new_node is None: + old_nodes.append(old_node) + continue + if not merge_node(old_node, new_node): + if old_nodes: + for i, old_node in enumerate(old_nodes): + if merge_node(old_node, new_node): + del old_nodes[i] + # If the last identified node which has not matched the + # unidentified node matches the current one, we have to + # assume that the last unidentified one has been + # inserted. + # + # As the required time multiplies with each insert, we + # want to avoid that by checking if the next + # unidentified node matches the current identified one + # and if so we make a shift. + if i == len(old_nodes): + next_new_node = new_iter.next() + if not merge_node(old_node, next_new_node): + new_iter.push(next_new_node) + break + else: + old_nodes.append(old_node) + new_nodes.append(new_node) + for (i, new_node), (j, old_node) in product(enumerate(new_nodes), enumerate(old_nodes)): + if merge_node(old_node, new_node): + del new_nodes[i] + del old_nodes[j] + new_nodes = [n for n in new_nodes if not hasattr(n, 'uid')] + for node in new_nodes: + node.uid = uuid4().hex + # Yielding the new nodes here makes it possible to use this generator + # like add_uids + yield node + +def make_diff(old, new): + """ + Takes two strings `old` and `new` and returns a :class:`tuple` of boolean + values ``(equals, changed, replaced)``. + + equals + + ``True`` if the `old` string and the `new` one are equal. + + changed + + ``True`` if the `new` string differs from the `old` one with at least + one character. + + replaced + + ``True`` if the `new` string and the `old` string are totally + different. + + .. note:: This assumes the two strings are human readable text or at least + something very similar to that, otherwise it can not detect if + the string has been changed or replaced. In any case the + detection should not be considered reliable. + """ + if old == new: + return True, False, False + if new in old or levenshtein_distance(old, new) / (len(old) / 100.0) < 70: + return False, True, False + return False, False, True + +def levenshtein_distance(a, b): + if len(a) < len(b): + a, b = b, a + if not a: + return len(b) + previous_row = xrange(len(b) + 1) + for i, column1 in enumerate(a): + current_row = [i + 1] + for j, column2 in enumerate(b): + insertions = previous_row[j + 1] + 1 + deletions = current_row[j] + 1 + substitutions = previous_row[j] + (column1 != column2) + current_row.append(min(insertions, deletions, substitutions)) + previous_row = current_row + return previous_row[-1] diff --git a/tests/root/contents.txt b/tests/root/contents.txt index e052e04b2..280953b46 100644 --- a/tests/root/contents.txt +++ b/tests/root/contents.txt @@ -26,6 +26,7 @@ Contents: extensions doctest extensions + versioning/index Python <http://python.org/> diff --git a/tests/root/versioning/added.txt b/tests/root/versioning/added.txt new file mode 100644 index 000000000..22a70739c --- /dev/null +++ b/tests/root/versioning/added.txt @@ -0,0 +1,20 @@ +Versioning test text +==================== + +So the thing is I need some kind of text - not the lorem ipsum stuff, that +doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find +a good text for that under public domain so I thought the easiest solution is +to write one by myself. It's not really interesting, in fact it is *really* +boring. + +Anyway I need more than one paragraph, at least three for the original +document, I think, and another one for two different ones. + +So the previous paragraph was a bit short because I don't want to test this +only on long paragraphs, I hope it was short enough to cover most stuff. +Anyway I see this lacks ``some markup`` so I have to add a **little** bit. + +Woho another paragraph, if this test fails we really have a problem because +this means the algorithm itself fails and not the diffing algorithm which is +pretty much doomed anyway as it probably fails for some kind of language +respecting certain nodes anyway but we can't work around that anyway. diff --git a/tests/root/versioning/deleted.txt b/tests/root/versioning/deleted.txt new file mode 100644 index 000000000..a1a9c4c91 --- /dev/null +++ b/tests/root/versioning/deleted.txt @@ -0,0 +1,12 @@ +Versioning test text +==================== + +So the thing is I need some kind of text - not the lorem ipsum stuff, that +doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find +a good text for that under public domain so I thought the easiest solution is +to write one by myself. It's not really interesting, in fact it is *really* +boring. + +So the previous paragraph was a bit short because I don't want to test this +only on long paragraphs, I hope it was short enough to cover most stuff. +Anyway I see this lacks ``some markup`` so I have to add a **little** bit. diff --git a/tests/root/versioning/deleted_end.txt b/tests/root/versioning/deleted_end.txt new file mode 100644 index 000000000..f30e63007 --- /dev/null +++ b/tests/root/versioning/deleted_end.txt @@ -0,0 +1,11 @@ +Versioning test text +==================== + +So the thing is I need some kind of text - not the lorem ipsum stuff, that +doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find +a good text for that under public domain so I thought the easiest solution is +to write one by myself. It's not really interesting, in fact it is *really* +boring. + +Anyway I need more than one paragraph, at least three for the original +document, I think, and another one for two different ones. diff --git a/tests/root/versioning/index.txt b/tests/root/versioning/index.txt new file mode 100644 index 000000000..234e223f1 --- /dev/null +++ b/tests/root/versioning/index.txt @@ -0,0 +1,11 @@ +Versioning Stuff +================ + +.. toctree:: + + original + added + insert + deleted + deleted_end + modified diff --git a/tests/root/versioning/insert.txt b/tests/root/versioning/insert.txt new file mode 100644 index 000000000..1c157cc90 --- /dev/null +++ b/tests/root/versioning/insert.txt @@ -0,0 +1,18 @@ +Versioning test text +==================== + +So the thing is I need some kind of text - not the lorem ipsum stuff, that +doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find +a good text for that under public domain so I thought the easiest solution is +to write one by myself. It's not really interesting, in fact it is *really* +boring. + +So this paragraph is just something I inserted in this document to test if our +algorithm notices that this paragraph is not just a changed version. + +Anyway I need more than one paragraph, at least three for the original +document, I think, and another one for two different ones. + +So the previous paragraph was a bit short because I don't want to test this +only on long paragraphs, I hope it was short enough to cover most stuff. +Anyway I see this lacks ``some markup`` so I have to add a **little** bit. diff --git a/tests/root/versioning/modified.txt b/tests/root/versioning/modified.txt new file mode 100644 index 000000000..49cdad935 --- /dev/null +++ b/tests/root/versioning/modified.txt @@ -0,0 +1,17 @@ +Versioning test text +==================== + +So the thing is I need some kind of text - not the lorem ipsum stuff, that +doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find +a good text for that under public domain so I thought the easiest solution is +to write one by myself. Inserting something silly as a modification, btw. have +you seen the typo below?. It's not really interesting, in fact it is *really* +boring. + +Anyway I need more than one paragraph, at least three for the original +document, I think, and another one for two different ones. So this is a small +modification by adding something to this paragraph. + +So the previous paragraph was a bit short because I don't want to test this +only on long paragraphs, I hoep it was short enough to cover most stuff. +Anyway I see this lacks ``some markup`` so I have to add a **little** bit. diff --git a/tests/root/versioning/original.txt b/tests/root/versioning/original.txt new file mode 100644 index 000000000..b3fe06094 --- /dev/null +++ b/tests/root/versioning/original.txt @@ -0,0 +1,15 @@ +Versioning test text +==================== + +So the thing is I need some kind of text - not the lorem ipsum stuff, that +doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find +a good text for that under public domain so I thought the easiest solution is +to write one by myself. It's not really interesting, in fact it is *really* +boring. + +Anyway I need more than one paragraph, at least three for the original +document, I think, and another one for two different ones. + +So the previous paragraph was a bit short because I don't want to test this +only on long paragraphs, I hope it was short enough to cover most stuff. +Anyway I see this lacks ``some markup`` so I have to add a **little** bit. diff --git a/tests/test_versioning.py b/tests/test_versioning.py new file mode 100644 index 000000000..19ef89040 --- /dev/null +++ b/tests/test_versioning.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +""" + test_versioning + ~~~~~~~~~~~~~~~ + + Test the versioning implementation. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from util import * + +from docutils.statemachine import ViewList + +from sphinx.versioning import make_diff, add_uids, merge_doctrees + +def setup_module(): + global app, original, original_uids + app = TestApp() + app.builder.env.app = app + app.connect('doctree-resolved', on_doctree_resolved) + app.build() + original = doctrees['versioning/original'] + original_uids = [n.uid for n in add_uids(original, is_paragraph)] + +def teardown_module(): + app.cleanup() + +doctrees = {} + +def on_doctree_resolved(app, doctree, docname): + doctrees[docname] = doctree + +def test_make_diff(): + tests = [ + (('aaa', 'aaa'), (True, False, False)), + (('aaa', 'aab'), (False, True, False)), + (('aaa', 'abb'), (False, True, False)), + (('aaa', 'aba'), (False, True, False)), + (('aaa', 'baa'), (False, True, False)), + (('aaa', 'bbb'), (False, False, True)) + ] + for args, result in tests: + assert make_diff(*args) == result + +def is_paragraph(node): + return node.__class__.__name__ == 'paragraph' + +def test_add_uids(): + assert len(original_uids) == 3 + +def test_modified(): + modified = doctrees['versioning/modified'] + new_nodes = list(merge_doctrees(original, modified, is_paragraph)) + uids = [n.uid for n in modified.traverse(is_paragraph)] + assert not new_nodes + assert original_uids == uids + +def test_added(): + added = doctrees['versioning/added'] + new_nodes = list(merge_doctrees(original, added, is_paragraph)) + uids = [n.uid for n in added.traverse(is_paragraph)] + assert len(new_nodes) == 1 + assert original_uids == uids[:-1] + +def test_deleted(): + deleted = doctrees['versioning/deleted'] + new_nodes = list(merge_doctrees(original, deleted, is_paragraph)) + uids = [n.uid for n in deleted.traverse(is_paragraph)] + assert not new_nodes + assert original_uids[::2] == uids + +def test_deleted_end(): + deleted_end = doctrees['versioning/deleted_end'] + new_nodes = list(merge_doctrees(original, deleted_end, is_paragraph)) + uids = [n.uid for n in deleted_end.traverse(is_paragraph)] + assert not new_nodes + assert original_uids[:-1] == uids + +def test_insert(): + from nose import SkipTest + raise SkipTest('The algorithm does not work at the moment') + insert = doctrees['versioning/insert'] + new_nodes = list(merge_doctrees(original, insert, is_paragraph)) + uids = [n.uid for n in insert.traverse(is_paragraph)] + assert len(new_nodes) == 1 + assert original_uids[0] == uids[0] + assert original_uids[1:] == uids[2:] From b14f9005f8f0b0f5db065aa5da3e8c7235a0a44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 12:31:10 +0200 Subject: [PATCH 272/744] Fixed documentation --- sphinx/versioning.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 51fd28a94..1a9c1cedc 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -109,8 +109,7 @@ def make_diff(old, new): changed - ``True`` if the `new` string differs from the `old` one with at least - one character. + ``True`` if the `new` string is a changed version of the `old` one. replaced From 549c07bade107402565479b625f6b2bad44db019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 13:15:03 +0200 Subject: [PATCH 273/744] Fix line length --- sphinx/versioning.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 1a9c1cedc..9ba7e3f78 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -87,7 +87,8 @@ def merge_doctrees(old, new, condition): else: old_nodes.append(old_node) new_nodes.append(new_node) - for (i, new_node), (j, old_node) in product(enumerate(new_nodes), enumerate(old_nodes)): + for (i, new_node), (j, old_node) in product(enumerate(new_nodes), + enumerate(old_nodes)): if merge_node(old_node, new_node): del new_nodes[i] del old_nodes[j] From c614ba995d3d08043ceb4ed03be8c8c384b4710a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 15:22:11 +0200 Subject: [PATCH 274/744] Fixed algorithm test_insert passes now and everything seems to be working fine --- sphinx/versioning.py | 4 ++-- tests/test_versioning.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 9ba7e3f78..5806e971b 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -67,8 +67,8 @@ def merge_doctrees(old, new, condition): continue if not merge_node(old_node, new_node): if old_nodes: - for i, old_node in enumerate(old_nodes): - if merge_node(old_node, new_node): + for i, very_old_node in enumerate(old_nodes): + if merge_node(very_old_node, new_node): del old_nodes[i] # If the last identified node which has not matched the # unidentified node matches the current one, we have to diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 19ef89040..47c322bb6 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -78,8 +78,6 @@ def test_deleted_end(): assert original_uids[:-1] == uids def test_insert(): - from nose import SkipTest - raise SkipTest('The algorithm does not work at the moment') insert = doctrees['versioning/insert'] new_nodes = list(merge_doctrees(original, insert, is_paragraph)) uids = [n.uid for n in insert.traverse(is_paragraph)] From 53920deb3ee5a27d5687a8c5ec4234fb8fb0a13c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 15:22:42 +0200 Subject: [PATCH 275/744] Delete remaining files in the _build directory --- tests/test_versioning.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 47c322bb6..77306580c 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -25,6 +25,7 @@ def setup_module(): def teardown_module(): app.cleanup() + (test_root / '_build').rmtree(True) doctrees = {} From a054a02eec9068c814cf7939653e54af90ba9ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 15:29:57 +0200 Subject: [PATCH 276/744] Removed unnecessary check --- sphinx/versioning.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 5806e971b..42b8bd515 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -92,7 +92,6 @@ def merge_doctrees(old, new, condition): if merge_node(old_node, new_node): del new_nodes[i] del old_nodes[j] - new_nodes = [n for n in new_nodes if not hasattr(n, 'uid')] for node in new_nodes: node.uid = uuid4().hex # Yielding the new nodes here makes it possible to use this generator From 023f342bede78031ff498844033662318af9286d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 17:11:11 +0200 Subject: [PATCH 277/744] Removed trailing whitespace --- doc/web/api.rst | 69 ++++++------ doc/web/quickstart.rst | 104 +++++++++--------- doc/web/searchadapters.rst | 21 ++-- doc/web/storagebackends.rst | 10 +- doc/websupport.rst | 10 +- sphinx/builders/websupport.py | 5 +- sphinx/websupport/__init__.py | 73 ++++++------ sphinx/websupport/search/__init__.py | 13 +-- sphinx/websupport/search/xapiansearch.py | 2 +- sphinx/websupport/storage/__init__.py | 6 +- sphinx/websupport/storage/db.py | 6 +- sphinx/websupport/storage/differ.py | 5 +- .../websupport/storage/sqlalchemystorage.py | 6 +- tests/test_searchadapters.py | 3 +- tests/test_websupport.py | 28 ++--- 15 files changed, 177 insertions(+), 184 deletions(-) diff --git a/doc/web/api.rst b/doc/web/api.rst index b2b7ef952..b63e68643 100644 --- a/doc/web/api.rst +++ b/doc/web/api.rst @@ -7,49 +7,49 @@ The WebSupport Class .. class:: WebSupport - The main API class for the web support package. All interactions - with the web support package should occur through this class. + The main API class for the web support package. All interactions + with the web support package should occur through this class. - The class takes the following keyword arguments: + The class takes the following keyword arguments: - srcdir - The directory containing reStructuredText source files. + srcdir + The directory containing reStructuredText source files. - builddir - The directory that build data and static files should be placed in. - This should be used when creating a :class:`WebSupport` object that - will be used to build data. + builddir + The directory that build data and static files should be placed in. + This should be used when creating a :class:`WebSupport` object that + will be used to build data. - datadir: - The directory that the web support data is in. This should be used - when creating a :class:`WebSupport` object that will be used to - retrieve data. + datadir: + The directory that the web support data is in. This should be used + when creating a :class:`WebSupport` object that will be used to + retrieve data. - search: - This may contain either a string (e.g. 'xapian') referencing a - built-in search adapter to use, or an instance of a subclass of - :class:`~sphinx.websupport.search.BaseSearch`. + search: + This may contain either a string (e.g. 'xapian') referencing a + built-in search adapter to use, or an instance of a subclass of + :class:`~sphinx.websupport.search.BaseSearch`. - storage: - This may contain either a string representing a database uri, or an - instance of a subclass of - :class:`~sphinx.websupport.storage.StorageBackend`. If this is not - provided a new sqlite database will be created. + storage: + This may contain either a string representing a database uri, or an + instance of a subclass of + :class:`~sphinx.websupport.storage.StorageBackend`. If this is not + provided a new sqlite database will be created. - moderation_callback: - A callable to be called when a new comment is added that is not - displayed. It must accept one argument: a dict representing the - comment that was added. + moderation_callback: + A callable to be called when a new comment is added that is not + displayed. It must accept one argument: a dict representing the + comment that was added. - staticdir: - If static files are served from a location besides "/static", this - should be a string with the name of that location - (e.g. '/static_files'). + staticdir: + If static files are served from a location besides "/static", this + should be a string with the name of that location + (e.g. '/static_files'). + + docroot: + If the documentation is not served from the base path of a URL, this + should be a string specifying that path (e.g. 'docs') - docroot: - If the documentation is not served from the base path of a URL, this - should be a string specifying that path (e.g. 'docs') - Methods ~~~~~~~ @@ -64,4 +64,3 @@ Methods .. automethod:: sphinx.websupport.WebSupport.process_vote .. automethod:: sphinx.websupport.WebSupport.get_search_results - diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst index 302a4db0e..de9b76558 100644 --- a/doc/web/quickstart.rst +++ b/doc/web/quickstart.rst @@ -10,22 +10,22 @@ To make use of the web support package in your application you'll need to build the data it uses. This data includes pickle files representing documents, search indices, and node data that is used to track where comments and other things are in a document. To do this you will need -to create an instance of the :class:`~sphinx.websupport.WebSupport` +to create an instance of the :class:`~sphinx.websupport.WebSupport` class and call it's :meth:`~sphinx.websupport.WebSupport.build` method:: from sphinx.websupport import WebSupport support = WebSupport(srcdir='/path/to/rst/sources/', builddir='/path/to/build/outdir', - search='xapian') + search='xapian') support.build() This will read reStructuredText sources from `srcdir` and place the -necessary data in `builddir`. The `builddir` will contain two +necessary data in `builddir`. The `builddir` will contain two sub-directories. One named "data" that contains all the data needed to display documents, search through documents, and add comments to -documents. The other directory will be called "static" and contains static +documents. The other directory will be called "static" and contains static files that should be served from "/static". .. note:: @@ -40,14 +40,14 @@ Integrating Sphinx Documents Into Your Webapp Now that the data is built, it's time to do something useful with it. Start off by creating a :class:`~sphinx.websupport.WebSupport` object for your application:: - + from sphinx.websupport import WebSupport support = WebSupport(datadir='/path/to/the/data', search='xapian') You'll only need one of these for each set of documentation you will be -working with. You can then call it's +working with. You can then call it's :meth:`~sphinx.websupport.WebSupport.get_document` method to access individual documents:: @@ -56,14 +56,14 @@ individual documents:: This will return a dictionary containing the following items: * **body**: The main body of the document as HTML -* **sidebar**: The sidebar of the document as HTML +* **sidebar**: The sidebar of the document as HTML * **relbar**: A div containing links to related documents * **title**: The title of the document * **css**: Links to css files used by Sphinx * **js**: Javascript containing comment options This dict can then be used as context for templates. The goal is to be -easy to integrate with your existing templating system. An example using +easy to integrate with your existing templating system. An example using `Jinja2 <http://jinja.pocoo.org/2/>`_ is: .. sourcecode:: html+jinja @@ -71,30 +71,30 @@ easy to integrate with your existing templating system. An example using {%- extends "layout.html" %} {%- block title %} - {{ document.title }} + {{ document.title }} {%- endblock %} {% block css %} - {{ super() }} - {{ document.css|safe }} - <link rel="stylesheet" href="/static/websupport-custom.css" type="text/css"> + {{ super() }} + {{ document.css|safe }} + <link rel="stylesheet" href="/static/websupport-custom.css" type="text/css"> {% endblock %} {%- block js %} - {{ super() }} - {{ document.js|safe }} + {{ super() }} + {{ document.js|safe }} {%- endblock %} {%- block relbar %} - {{ document.relbar|safe }} + {{ document.relbar|safe }} {%- endblock %} {%- block body %} - {{ document.body|safe }} + {{ document.body|safe }} {%- endblock %} {%- block sidebar %} - {{ document.sidebar|safe }} + {{ document.sidebar|safe }} {%- endblock %} Authentication @@ -108,7 +108,7 @@ Once a user has been authenticated you can pass the user's details to certain username with comments and votes. The only caveat is that if you allow users to change their username you must update the websupport package's data:: - support.update_username(old_username, new_username) + support.update_username(old_username, new_username) *username* should be a unique string which identifies a user, and *moderator* should be a boolean representing whether the user has moderation @@ -121,32 +121,32 @@ a user is logged in and then retrieves a document is:: @app.route('/<path:docname>') def doc(docname): - username = g.user.name if g.user else '' - moderator = g.user.moderator if g.user else False - try: - document = support.get_document(docname, username, moderator) - except DocumentNotFoundError: - abort(404) + username = g.user.name if g.user else '' + moderator = g.user.moderator if g.user else False + try: + document = support.get_document(docname, username, moderator) + except DocumentNotFoundError: + abort(404) return render_template('doc.html', document=document) The first thing to notice is that the *docname* is just the request path. This makes accessing the correct document easy from a single view. If the user is authenticated then the username and moderation status are -passed along with the docname to +passed along with the docname to :meth:`~sphinx.websupport.WebSupport.get_document`. The web support package will then add this data to the COMMENT_OPTIONS that are used in the template. .. note:: - This only works works if your documentation is served from your - document root. If it is served from another directory, you will - need to prefix the url route with that directory, and give the `docroot` - keyword argument when creating the web support object:: - - support = WebSupport(... - docroot='docs') - - @app.route('/docs/<path:docname>') + This only works works if your documentation is served from your + document root. If it is served from another directory, you will + need to prefix the url route with that directory, and give the `docroot` + keyword argument when creating the web support object:: + + support = WebSupport(... + docroot='docs') + + @app.route('/docs/<path:docname>') Performing Searches ~~~~~~~~~~~~~~~~~~~ @@ -155,7 +155,7 @@ To use the search form built-in to the Sphinx sidebar, create a function to handle requests to the url 'search' relative to the documentation root. The user's search query will be in the GET parameters, with the key `q`. Then use the :meth:`~sphinx.websupport.WebSupport.get_search_results` method -to retrieve search results. In `Flask <http://flask.pocoo.org/>`_ that +to retrieve search results. In `Flask <http://flask.pocoo.org/>`_ that would be like this:: @app.route('/search') @@ -165,7 +165,7 @@ would be like this:: return render_template('doc.html', document=document) Note that we used the same template to render our search results as we -did to render our documents. That's because +did to render our documents. That's because :meth:`~sphinx.websupport.WebSupport.get_search_results` returns a context dict in the same format that :meth:`~sphinx.websupport.WebSupport.get_document` does. @@ -186,22 +186,22 @@ function is used to add a new comment, and will call the web support method proposal = request.form.get('proposal', '') username = g.user.name if g.user is not None else 'Anonymous' comment = support.add_comment(text, node_id='node_id', - parent_id='parent_id', - username=username, proposal=proposal) + parent_id='parent_id', + username=username, proposal=proposal) return jsonify(comment=comment) You'll notice that both a `parent_id` and `node_id` are sent with the request. If the comment is being attached directly to a node, `parent_id` will be empty. If the comment is a child of another comment, then `node_id` -will be empty. Then next function handles the retrieval of comments for a -specific node, and is aptly named +will be empty. Then next function handles the retrieval of comments for a +specific node, and is aptly named :meth:`~sphinx.websupport.WebSupport.get_data`:: @app.route('/docs/get_comments') def get_comments(): username = g.user.name if g.user else None - moderator = g.user.moderator if g.user else False - node_id = request.args.get('node', '') + moderator = g.user.moderator if g.user else False + node_id = request.args.get('node', '') data = support.get_data(parent_id, user_id) return jsonify(**data) @@ -223,15 +223,15 @@ votes on comments:: Comment Moderation ~~~~~~~~~~~~~~~~~~ -By default all comments added through +By default all comments added through :meth:`~sphinx.websupport.WebSupport.add_comment` are automatically displayed. If you wish to have some form of moderation, you can pass the `displayed` keyword argument:: comment = support.add_comment(text, node_id='node_id', - parent_id='parent_id', + parent_id='parent_id', username=username, proposal=proposal, - displayed=False) + displayed=False) You can then create two new views to handle the moderation of comments. The first will be called when a moderator decides a comment should be accepted @@ -240,18 +240,18 @@ and displayed:: @app.route('/docs/accept_comment', methods=['POST']) def accept_comment(): moderator = g.user.moderator if g.user else False - comment_id = request.form.get('id') - support.accept_comment(comment_id, moderator=moderator) - return 'OK' + comment_id = request.form.get('id') + support.accept_comment(comment_id, moderator=moderator) + return 'OK' The next is very similar, but used when rejecting a comment:: @app.route('/docs/reject_comment', methods=['POST']) def reject_comment(): moderator = g.user.moderator if g.user else False - comment_id = request.form.get('id') - support.reject_comment(comment_id, moderator=moderator) - return 'OK' + comment_id = request.form.get('id') + support.reject_comment(comment_id, moderator=moderator) + return 'OK' To perform a custom action (such as emailing a moderator) when a new comment is added but not displayed, you can pass callable to the @@ -265,4 +265,4 @@ object:: moderation_callback=moderation_callback) The moderation callback must take one argument, which will be the same -comment dict that is returned by add_comment. \ No newline at end of file +comment dict that is returned by add_comment. diff --git a/doc/web/searchadapters.rst b/doc/web/searchadapters.rst index e03fee81f..a84aa8da1 100644 --- a/doc/web/searchadapters.rst +++ b/doc/web/searchadapters.rst @@ -10,26 +10,26 @@ To create a custom search adapter you will need to subclass the and pass that as the `search` keyword argument when you create the :class:`~sphinx.websupport.WebSupport` object:: - support = Websupport(srcdir=srcdir, - builddir=builddir, - search=MySearch()) + support = Websupport(srcdir=srcdir, + builddir=builddir, + search=MySearch()) For more information about creating a custom search adapter, please see the documentation of the :class:`BaseSearch` class below. .. class:: BaseSearch - Defines an interface for search adapters. + Defines an interface for search adapters. BaseSearch Methods ~~~~~~~~~~~~~~~~~~ - The following methods are defined in the BaseSearch class. Some methods - do not need to be overridden, but some ( - :meth:`~sphinx.websupport.search.BaseSearch.add_document` and - :meth:`~sphinx.websupport.search.BaseSearch.handle_query`) must be - overridden in your subclass. For a working example, look at the - built-in adapter for whoosh. + The following methods are defined in the BaseSearch class. Some methods + do not need to be overridden, but some ( + :meth:`~sphinx.websupport.search.BaseSearch.add_document` and + :meth:`~sphinx.websupport.search.BaseSearch.handle_query`) must be + overridden in your subclass. For a working example, look at the + built-in adapter for whoosh. .. automethod:: sphinx.websupport.search.BaseSearch.init_indexing @@ -44,4 +44,3 @@ BaseSearch Methods .. automethod:: sphinx.websupport.search.BaseSearch.handle_query .. automethod:: sphinx.websupport.search.BaseSearch.extract_context - diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst index 6411bf176..6b701ea38 100644 --- a/doc/web/storagebackends.rst +++ b/doc/web/storagebackends.rst @@ -10,16 +10,16 @@ To create a custom storage backend you will need to subclass the and pass that as the `storage` keyword argument when you create the :class:`~sphinx.websupport.WebSupport` object:: - support = Websupport(srcdir=srcdir, - builddir=builddir, - storage=MyStorage()) + support = Websupport(srcdir=srcdir, + builddir=builddir, + storage=MyStorage()) For more information about creating a custom storage backend, please see the documentation of the :class:`StorageBackend` class below. .. class:: StorageBackend - Defines an interface for storage backends. + Defines an interface for storage backends. StorageBackend Methods ~~~~~~~~~~~~~~~~~~~~~~ @@ -42,4 +42,4 @@ StorageBackend Methods .. automethod:: sphinx.websupport.storage.StorageBackend.accept_comment -.. automethod:: sphinx.websupport.storage.StorageBackend.reject_comment \ No newline at end of file +.. automethod:: sphinx.websupport.storage.StorageBackend.reject_comment diff --git a/doc/websupport.rst b/doc/websupport.rst index 59973d745..4d743719d 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -4,12 +4,12 @@ Sphinx Web Support ================== Sphinx provides a way to easily integrate Sphinx documentation -into your web application. To learn more read the +into your web application. To learn more read the :ref:`websupportquickstart`. .. toctree:: - web/quickstart - web/api - web/searchadapters - web/storagebackends \ No newline at end of file + web/quickstart + web/api + web/searchadapters + web/storagebackends diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 3d0356b71..23d0f52c4 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -58,7 +58,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def handle_page(self, pagename, addctx, templatename='page.html', outfilename=None, event_arg=None): # This is mostly copied from StandaloneHTMLBuilder. However, instead - # of rendering the template and saving the html, create a context + # of rendering the template and saving the html, create a context # dict and pickle it. ctx = self.globalcontext.copy() ctx['pagename'] = pagename @@ -140,7 +140,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): FILE_SUFFIX: '', HAS_SOURCE: '%s' }; -</script>""" +</script>""" opts = opts % (ctx.get('url_root', ''), escape(ctx['release']), str(ctx['has_source']).lower()) scripts = [] @@ -148,4 +148,3 @@ class WebSupportBuilder(StandaloneHTMLBuilder): scripts.append(make_script(file)) scripts.append(make_script('_static/websupport.js')) return opts + '\n' + '\n'.join(scripts) - diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 76715d14a..17aef4025 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -49,7 +49,7 @@ class WebSupport(object): self.moderation_callback = moderation_callback self._init_templating() - self._init_search(search) + self._init_search(search) self._init_storage(storage) self._make_base_comment_options() @@ -105,7 +105,7 @@ class WebSupport(object): doctreedir = path.join(self.outdir, 'doctrees') app = WebSupportApp(self.srcdir, self.srcdir, self.outdir, doctreedir, 'websupport', - search=self.search, status=self.status, + search=self.search, status=self.status, warning=self.warning, storage=self.storage, staticdir=self.staticdir, builddir=self.builddir) @@ -119,8 +119,8 @@ class WebSupport(object): support = WebSupport(datadir=datadir) support.get_document('index', username, moderator) - - In most cases `docname` will be taken from the request path and + + In most cases `docname` will be taken from the request path and passed directly to this function. In Flask, that would be something like this:: @@ -139,7 +139,7 @@ class WebSupport(object): to be used during template rendering. * **body**: The main body of the document as HTML - * **sidebar**: The sidebar of the document as HTML + * **sidebar**: The sidebar of the document as HTML * **relbar**: A div containing links to related documents * **title**: The title of the document * **css**: Links to css files used by Sphinx @@ -161,7 +161,7 @@ class WebSupport(object): document = pickle.load(f) comment_opts = self._make_comment_options(username, moderator) comment_metadata = self.storage.get_metadata(docname, moderator) - + document['js'] = '\n'.join([comment_opts, self._make_metadata(comment_metadata), document['js']]) @@ -172,7 +172,7 @@ class WebSupport(object): of search results. Then render the search results as html and return a context dict like the one created by :meth:`get_document`:: - + document = support.get_search_results(q) :param q: the search query @@ -187,12 +187,12 @@ class WebSupport(object): return document def get_data(self, node_id, username=None, moderator=False): - """Get the comments and source associated with `node_id`. If - `username` is given vote information will be included with the + """Get the comments and source associated with `node_id`. If + `username` is given vote information will be included with the returned comments. The default CommentBackend returns a dict with two keys, *source*, and *comments*. *source* is raw source of the node and is used as the starting point for proposals a user can - add. *comments* is a list of dicts that represent a comment, each + add. *comments* is a list of dicts that represent a comment, each having the following items: ============= ====================================================== @@ -209,12 +209,12 @@ class WebSupport(object): 8601 format. `delta` is a printable form of how old the comment is (e.g. "3 hours ago"). vote If `user_id` was given, this will be an integer - representing the vote. 1 for an upvote, -1 for a + representing the vote. 1 for an upvote, -1 for a downvote, or 0 if unvoted. - node The id of the node that the comment is attached to. - If the comment's parent is another comment rather than + node The id of the node that the comment is attached to. + If the comment's parent is another comment rather than a node, this will be null. - parent The id of the comment that this comment is attached + parent The id of the comment that this comment is attached to if it is not attached to a node. children A list of all children, in this format. proposal_diff An HTML representation of the differences between the @@ -232,7 +232,7 @@ class WebSupport(object): instead replaces the username and text files with "[deleted]" so as not to leave any comments orphaned. - If `moderator` is True, the comment will always be deleted. If + If `moderator` is True, the comment will always be deleted. If `moderator` is False, the comment will only be deleted if the `username` matches the `username` on the comment. @@ -246,25 +246,25 @@ class WebSupport(object): """ self.storage.delete_comment(comment_id, username, moderator) - def add_comment(self, text, node_id='', parent_id='', displayed=True, + def add_comment(self, text, node_id='', parent_id='', displayed=True, username=None, time=None, proposal=None, moderator=False): - """Add a comment to a node or another comment. Returns the comment - in the same format as :meth:`get_comments`. If the comment is being - attached to a node, pass in the node's id (as a string) with the + """Add a comment to a node or another comment. Returns the comment + in the same format as :meth:`get_comments`. If the comment is being + attached to a node, pass in the node's id (as a string) with the node keyword argument:: comment = support.add_comment(text, node_id=node_id) If the comment is the child of another comment, provide the parent's id (as a string) with the parent keyword argument:: - + comment = support.add_comment(text, parent_id=parent_id) If you would like to store a username with the comment, pass in the optional `username` keyword argument:: - comment = support.add_comment(text, node=node_id, + comment = support.add_comment(text, node=node_id, username=username) :param parent_id: the prefixed id of the comment's parent. @@ -274,7 +274,7 @@ class WebSupport(object): :param time: the time the comment was created, defaults to now. """ comment = self.storage.add_comment(text, displayed, username, - time, proposal, node_id, + time, proposal, node_id, parent_id, moderator) if not displayed and self.moderation_callback: self.moderation_callback(comment) @@ -282,10 +282,10 @@ class WebSupport(object): def process_vote(self, comment_id, username, value): """Process a user's vote. The web support package relies - on the API user to perform authentication. The API user will + on the API user to perform authentication. The API user will typically receive a comment_id and value from a form, and then - make sure the user is authenticated. A unique username must be - passed in, which will also be used to retrieve the user's past + make sure the user is authenticated. A unique username must be + passed in, which will also be used to retrieve the user's past voting data. An example, once again in Flask:: @app.route('/docs/process_vote', methods=['POST']) @@ -352,20 +352,20 @@ class WebSupport(object): that remains the same throughout the lifetime of the :class:`~sphinx.websupport.WebSupport` object. """ - parts = ['<script type="text/javascript">', + parts = ['<script type="text/javascript">', 'var COMMENT_OPTIONS = {'] if self.docroot is not '': - parts.append('addCommentURL: "/%s/%s",' % (self.docroot, + parts.append('addCommentURL: "/%s/%s",' % (self.docroot, 'add_comment')) - parts.append('getCommentsURL: "/%s/%s",' % (self.docroot, + parts.append('getCommentsURL: "/%s/%s",' % (self.docroot, 'get_comments')) - parts.append('processVoteURL: "/%s/%s",' % (self.docroot, + parts.append('processVoteURL: "/%s/%s",' % (self.docroot, 'process_vote')) - parts.append('acceptCommentURL: "/%s/%s",' % (self.docroot, + parts.append('acceptCommentURL: "/%s/%s",' % (self.docroot, 'accept_comment')) - parts.append('rejectCommentURL: "/%s/%s",' % (self.docroot, + parts.append('rejectCommentURL: "/%s/%s",' % (self.docroot, 'reject_comment')) - parts.append('deleteCommentURL: "/%s/%s",' % (self.docroot, + parts.append('deleteCommentURL: "/%s/%s",' % (self.docroot, 'delete_comment')) if self.staticdir != 'static': @@ -378,8 +378,8 @@ class WebSupport(object): parts.append('upArrowPressed: "/%s",' % p('up-pressed.png')) parts.append('downArrowPressed: "/%s",' % p('down-pressed.png')) - self.base_comment_opts = '\n'.join(parts) - + self.base_comment_opts = '\n'.join(parts) + def _make_comment_options(self, username, moderator): """Helper method to create the parts of the COMMENT_OPTIONS javascript that are unique to each request. @@ -394,14 +394,13 @@ class WebSupport(object): parts.append('moderator: %s' % str(moderator).lower()) parts.append('};') parts.append('</script>') - return '\n'.join(parts) + return '\n'.join(parts) def _make_metadata(self, data): - node_js = ', '.join(['%s: %s' % (node_id, comment_count) + node_js = ', '.join(['%s: %s' % (node_id, comment_count) for node_id, comment_count in data.iteritems()]) js = """ <script type="text/javascript"> var COMMENT_METADATA = {%s}; </script>""" % node_js return js - diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index 80f91ab1d..cb66618b5 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -34,7 +34,7 @@ class BaseSearch(object): def feed(self, pagename, title, doctree): """Called by the builder to add a doctree to the index. Converts the `doctree` to text and passes it to :meth:`add_document`. You probably - won't want to override this unless you need access to the `doctree`. + won't want to override this unless you need access to the `doctree`. Override :meth:`add_document` instead. :param pagename: the name of the page to be indexed @@ -50,11 +50,11 @@ class BaseSearch(object): `pagename` is name of the page being indexed. It is the combination of the source files relative path and filename, - minus the extension. For example, if the source file is + minus the extension. For example, if the source file is "ext/builders.rst", the `pagename` would be "ext/builders". This - will need to be returned with search results when processing a + will need to be returned with search results when processing a query. - + :param pagename: the name of the page being indexed :param title: the page's title :param text: the full text of the page @@ -62,13 +62,13 @@ class BaseSearch(object): raise NotImplementedError() def query(self, q): - """Called by the web support api to get search results. This method + """Called by the web support api to get search results. This method compiles the regular expression to be used when :meth:`extracting context <extract_context>`, then calls :meth:`handle_query`. You won't want to override this unless you don't want to use the included :meth:`extract_context` method. Override :meth:`handle_query` instead. - + :param q: the search query string. """ self.context_re = re.compile('|'.join(q.split()), re.I) @@ -119,4 +119,3 @@ search_adapters = { 'whoosh': ('whooshsearch', 'WhooshSearch'), 'null': ('nullsearch', 'NullSearch') } - diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py index 2f2ffbe59..16c7e2b1b 100644 --- a/sphinx/websupport/search/xapiansearch.py +++ b/sphinx/websupport/search/xapiansearch.py @@ -28,7 +28,7 @@ class XapianSearch(BaseSearch): def init_indexing(self, changed=[]): ensuredir(self.db_path) - self.database = xapian.WritableDatabase(self.db_path, + self.database = xapian.WritableDatabase(self.db_path, xapian.DB_CREATE_OR_OPEN) self.indexer = xapian.TermGenerator() stemmer = xapian.Stem("english") diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py index 24d4ade55..17907e992 100644 --- a/sphinx/websupport/storage/__init__.py +++ b/sphinx/websupport/storage/__init__.py @@ -20,13 +20,13 @@ class StorageBackend(object): """Add a node to the StorageBackend. :param document: the name of the document the node belongs to. - + :param line: the line in the source where the node begins. :param source: the source files name. """ raise NotImplementedError() - + def post_build(self): """Called after a build has completed. Use this to finalize the addition of nodes if needed. @@ -36,7 +36,7 @@ class StorageBackend(object): def add_comment(self, text, displayed, username, time, proposal, node_id, parent_id, moderator): """Called when a comment is being added. - + :param text: the text of the comment :param displayed: whether the comment should be displayed :param username: the name of the user adding the comment diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index 4a84cd086..74a3e2b70 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -81,7 +81,7 @@ class Node(Base): comment, vote = r if username else (r, 0) inheritance_chain = comment.path.split('.')[1:] - + if len(inheritance_chain) == len(list_stack) + 1: parent = list_stack[-1][-1] list_stack.append(parent['children']) @@ -90,7 +90,7 @@ class Node(Base): list_stack.pop() list_stack[-1].append(comment.serializable(vote=vote)) - + return comments def __init__(self, document, line, source): @@ -115,7 +115,7 @@ class Comment(Base): node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) node = relation(Node, backref="comments") - def __init__(self, text, displayed, username, rating, time, + def __init__(self, text, displayed, username, rating, time, proposal, proposal_diff): self.text = text self.displayed = displayed diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py index 068d7e6fc..f0b6a8ea5 100644 --- a/sphinx/websupport/storage/differ.py +++ b/sphinx/websupport/storage/differ.py @@ -53,12 +53,12 @@ class CombinedHtmlDiff(object): return text elif prefix == '?': return '' - + if next is not None and next[0] == '?': tag = 'ins' if prefix == '+' else 'del' text = self._highlight_text(text, next, tag) css_class = 'prop_added' if prefix == '+' else 'prop_removed' - + return '<span class="%s">%s</span>\n' % (css_class, text.rstrip()) def _highlight_text(self, text, next, tag): @@ -76,4 +76,3 @@ class CombinedHtmlDiff(object): start = match.end() new_text.append(text[start:]) return ''.join(new_text) - diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index 1aaa84738..e2cd87ac5 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -21,7 +21,7 @@ from sphinx.websupport.storage.db import Base, Node, Comment, CommentVote,\ from sphinx.websupport.storage.differ import CombinedHtmlDiff class SQLAlchemyStorage(StorageBackend): - """A :class:`~sphinx.websupport.storage.StorageBackend` using + """A :class:`~sphinx.websupport.storage.StorageBackend` using SQLAlchemy. """ def __init__(self, engine): @@ -59,7 +59,7 @@ class SQLAlchemyStorage(StorageBackend): raise CommentNotAllowedError( "Can't add child to a parent that is not displayed") - comment = Comment(text, displayed, username, 0, + comment = Comment(text, displayed, username, 0, time or datetime.now(), proposal, proposal_diff) session.add(comment) session.flush() @@ -88,7 +88,7 @@ class SQLAlchemyStorage(StorageBackend): def get_metadata(self, docname, moderator): session = Session() subquery = session.query( - Comment.id, Comment.node_id, + Comment.id, Comment.node_id, func.count('*').label('comment_count')).group_by( Comment.node_id).subquery() nodes = session.query(Node.id, subquery.c.comment_count).outerjoin( diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py index cb6c6e968..a30141dfd 100644 --- a/tests/test_searchadapters.py +++ b/tests/test_searchadapters.py @@ -27,7 +27,7 @@ def teardown_module(): def search_adapter_helper(adapter): clear_builddir() - + settings = {'builddir': os.path.join(test_root, 'websupport'), 'status': StringIO(), 'warning': StringIO()} @@ -81,4 +81,3 @@ def test_whoosh(): except ImportError: sys.stderr.write('info: not running whoosh tests, ' \ 'whoosh doesn\'t seem to be installed') - diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 27a14e369..32249976d 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -64,7 +64,7 @@ def test_build(support): @with_support() def test_get_document(support): raises(DocumentNotFoundError, support.get_document, 'nonexisting') - + contents = support.get_document('contents') assert contents['title'] and contents['body'] \ and contents['sidebar'] and contents['relbar'] @@ -78,27 +78,27 @@ def test_comments(support): second_node = nodes[1] # Create a displayed comment and a non displayed comment. - comment = support.add_comment('First test comment', + comment = support.add_comment('First test comment', node_id=str(first_node.id), username='user_one') - hidden_comment = support.add_comment('Hidden comment', - node_id=str(first_node.id), + hidden_comment = support.add_comment('Hidden comment', + node_id=str(first_node.id), displayed=False) # Make sure that comments can't be added to a comment where # displayed == False, since it could break the algorithm that # converts a nodes comments to a tree. - raises(CommentNotAllowedError, support.add_comment, 'Not allowed', + raises(CommentNotAllowedError, support.add_comment, 'Not allowed', parent_id=str(hidden_comment['id'])) # Add a displayed and not displayed child to the displayed comment. support.add_comment('Child test comment', parent_id=str(comment['id']), username='user_one') - support.add_comment('Hidden child test comment', + support.add_comment('Hidden child test comment', parent_id=str(comment['id']), displayed=False) # Add a comment to another node to make sure it isn't returned later. - support.add_comment('Second test comment', + support.add_comment('Second test comment', node_id=str(second_node.id), username='user_two') - + # Access the comments as a moderator. data = support.get_data(str(first_node.id), moderator=True) comments = data['comments'] @@ -130,7 +130,7 @@ def test_voting(support): data = support.get_data(str(node.id)) comment = data['comments'][0] assert comment['rating'] == val, '%s != %s' % (comment['rating'], val) - + support.process_vote(comment['id'], 'user_one', '1') support.process_vote(comment['id'], 'user_two', '1') support.process_vote(comment['id'], 'user_three', '1') @@ -161,7 +161,7 @@ def test_proposals(support): source = data['source'] proposal = source[:5] + source[10:15] + 'asdf' + source[15:] - comment = support.add_comment('Proposal comment', + comment = support.add_comment('Proposal comment', node_id=str(node.id), proposal=proposal) @@ -195,7 +195,7 @@ def test_moderator_delete_comments(support): return support.get_data(str(node.id), moderator=True)['comments'][1] comment = get_comment() - support.delete_comment(comment['id'], username='user_two', + support.delete_comment(comment['id'], username='user_two', moderator=True) comment = get_comment() assert comment['username'] == '[deleted]' @@ -228,9 +228,9 @@ def moderation_callback(comment): @with_support(moderation_callback=moderation_callback) def test_moderation(support): - accepted = support.add_comment('Accepted Comment', node_id=3, + accepted = support.add_comment('Accepted Comment', node_id=3, displayed=False) - rejected = support.add_comment('Rejected comment', node_id=3, + rejected = support.add_comment('Rejected comment', node_id=3, displayed=False) # Make sure the moderation_callback is called. assert called == True @@ -248,7 +248,7 @@ def test_moderation(support): def test_differ(): differ = CombinedHtmlDiff() source = 'Lorem ipsum dolor sit amet,\nconsectetur adipisicing elit,\n' \ - 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' + 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' prop = 'Lorem dolor sit amet,\nconsectetur nihil adipisicing elit,\n' \ 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' differ.make_html(source, prop) From bd950c3bb4c687142559def3345fe36b84dd86cd Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Tue, 10 Aug 2010 17:16:49 +0200 Subject: [PATCH 278/744] Fix test_config under 2.x. --- tests/test_config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index 7fce4495b..b5f88a6f5 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -89,7 +89,8 @@ def test_errors_warnings(dir): raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None) # test the automatic conversion of 2.x only code in configs - write_file(dir / 'conf.py', u'\n\nproject = u"Jägermeister"\n', 'utf-8') + write_file(dir / 'conf.py', u'# -*- coding: utf-8\n\n' + u'project = u"Jägermeister"\n', 'utf-8') cfg = Config(dir, 'conf.py', {}, None) cfg.init_values() assert cfg.project == u'Jägermeister' From 82353ffc46a8527cfa40332314f60ccff21991fb Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 10 Aug 2010 12:49:12 -0500 Subject: [PATCH 279/744] fixed typo --- sphinx/builders/websupport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 3d0356b71..e789ead59 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -82,7 +82,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): # Create a dict that will be pickled and used by webapps. css = '<link rel="stylesheet" href="%s" type=text/css />' % \ - pathto('_static/pygmentcs.css', 1) + pathto('_static/pygment.css', 1) doc_ctx = {'body': ctx.get('body', ''), 'title': ctx.get('title', ''), 'css': css, From 51bf5c4240459b8b0d6b79f5b9dd9ee3cb598346 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 10 Aug 2010 13:04:23 -0500 Subject: [PATCH 280/744] fixed ajax-loader.gif path --- sphinx/themes/basic/static/websupport.js | 4 +++- sphinx/websupport/__init__.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 99d1a2217..11d812fdd 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -718,7 +718,8 @@ <a href="#" class="sort_option" id="age">oldest</a>\ </p>\ </form>\ - <h3 id="comment_notification">loading comments... <img src="/static/ajax-loader.gif" alt="" /></h3>\ + <h3 id="comment_notification">loading comments... <img src="' + + opts.loadingImage + '" alt="" /></h3>\ <ul id="comment_ul"></ul>\ </div>\ <div id="focuser"></div>'; @@ -732,6 +733,7 @@ rejectCommentURL: '/reject_comment', rejectCommentURL: '/delete_comment', commentImage: '/static/_static/comment.png', + loadingImage: '/static/_static/comment.png', commentBrightImage: '/static/_static/comment-bright.png', upArrow: '/static/_static/up.png', downArrow: '/static/_static/down.png', diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 76715d14a..2ac195def 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -371,6 +371,7 @@ class WebSupport(object): if self.staticdir != 'static': p = lambda file: '%s/_static/%s' % (self.staticdir, file) parts.append('commentImage: "/%s",' % p('comment.png') ) + parts.append('loadingImage: "/%s",' % p('ajax-loader.gif') ) parts.append( 'commentBrightImage: "/%s",' % p('comment-bright.png') ) parts.append('upArrow: "/%s",' % p('up.png')) From 9545df0d5645212ca0aaf3560ff6000c80d99c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 20:09:07 +0200 Subject: [PATCH 281/744] Don't create Javascript by ourselves, we have sphinx.util.jsonimpl.dumps for that --- sphinx/websupport/__init__.py | 80 +++++++++++++++++------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 17aef4025..1b33dfa3e 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -11,6 +11,7 @@ import sys import cPickle as pickle +import posixpath from os import path from datetime import datetime @@ -18,6 +19,7 @@ from jinja2 import Environment, FileSystemLoader from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir +from sphinx.util.jsonimpl import dumps as dump_json from sphinx.websupport.search import BaseSearch, search_adapters from sphinx.websupport.storage import StorageBackend from sphinx.websupport.errors import * @@ -352,33 +354,28 @@ class WebSupport(object): that remains the same throughout the lifetime of the :class:`~sphinx.websupport.WebSupport` object. """ - parts = ['<script type="text/javascript">', - 'var COMMENT_OPTIONS = {'] - if self.docroot is not '': - parts.append('addCommentURL: "/%s/%s",' % (self.docroot, - 'add_comment')) - parts.append('getCommentsURL: "/%s/%s",' % (self.docroot, - 'get_comments')) - parts.append('processVoteURL: "/%s/%s",' % (self.docroot, - 'process_vote')) - parts.append('acceptCommentURL: "/%s/%s",' % (self.docroot, - 'accept_comment')) - parts.append('rejectCommentURL: "/%s/%s",' % (self.docroot, - 'reject_comment')) - parts.append('deleteCommentURL: "/%s/%s",' % (self.docroot, - 'delete_comment')) + comment_urls = [ + ('addCommentURL', 'add_comment'), + ('getCommentsURL', 'get_comments'), + ('processVoteURL', 'process_vote'), + ('acceptCommentURL', 'accept_comment'), + ('rejectCommentURL', 'reject_comment'), + ('deleteCommentURL', 'delete_comment') + ] + static_urls = [ + ('commentImage', 'comment.png'), + ('commentBrightImage', 'comment-bright.png'), + ('upArrow', 'up.png'), + ('upArrowPressed', 'up-pressed.png'), + ('downArrow', 'down.png'), + ('downArrowPressed', 'down-pressed.png') + ] - if self.staticdir != 'static': - p = lambda file: '%s/_static/%s' % (self.staticdir, file) - parts.append('commentImage: "/%s",' % p('comment.png') ) - parts.append( - 'commentBrightImage: "/%s",' % p('comment-bright.png') ) - parts.append('upArrow: "/%s",' % p('up.png')) - parts.append('downArrow: "/%s",' % p('down.png')) - parts.append('upArrowPressed: "/%s",' % p('up-pressed.png')) - parts.append('downArrowPressed: "/%s",' % p('down-pressed.png')) - - self.base_comment_opts = '\n'.join(parts) + self.base_comment_opts = {} + for key, value in comment_urls: + self.base_comment_opts[key] = posixpath.join(self.docroot, value) + for key, value in static_urls: + self.base_comment_opts[key] = posixpath.join(self.staticdir, value) def _make_comment_options(self, username, moderator): """Helper method to create the parts of the COMMENT_OPTIONS @@ -388,19 +385,22 @@ class WebSupport(object): :param moderator: Whether the user making the request is a moderator. """ parts = [self.base_comment_opts] - if username is not '': - parts.append('voting: true,') - parts.append('username: "%s",' % username) - parts.append('moderator: %s' % str(moderator).lower()) - parts.append('};') - parts.append('</script>') - return '\n'.join(parts) + rv = self.base_comment_opts.copy() + if username: + rv.update({ + 'voting': True, + 'username': username, + 'moderator': str(moderator).lower(), + }) + return '\n'.join([ + '<script type="text/javascript">', + 'var COMMENT_OPTIONS = %s;' % dump_json(rv), + '</script>' + ]) def _make_metadata(self, data): - node_js = ', '.join(['%s: %s' % (node_id, comment_count) - for node_id, comment_count in data.iteritems()]) - js = """ -<script type="text/javascript"> - var COMMENT_METADATA = {%s}; -</script>""" % node_js - return js + return '\n'.join([ + '<script type="text/javascript">', + 'var COMMENT_METADATA = %s;' % dump_json(data), + '</script>' + ]) From 1ca2903d16bdf0c5e683078292895976874906db Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 10 Aug 2010 14:40:11 -0500 Subject: [PATCH 282/744] That typo I fixed... still not right... *slaps self in face* --- sphinx/builders/websupport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index c1decde41..30cf28314 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -82,7 +82,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): # Create a dict that will be pickled and used by webapps. css = '<link rel="stylesheet" href="%s" type=text/css />' % \ - pathto('_static/pygment.css', 1) + pathto('_static/pygments.css', 1) doc_ctx = {'body': ctx.get('body', ''), 'title': ctx.get('title', ''), 'css': css, From 2ca339557d1da7c1e05af5ea4bfa4717b7389e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 10 Aug 2010 21:50:48 +0200 Subject: [PATCH 283/744] Switch to sphinx.util.jsonimpl.dumps in the builder --- sphinx/builders/websupport.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index c1decde41..a801eada2 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -17,6 +17,7 @@ import shutil from docutils.io import StringOutput from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile +from sphinx.util.jsonimpl import dumps as dump_json from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.writers.websupport import WebSupportTranslator @@ -131,20 +132,17 @@ class WebSupportBuilder(StandaloneHTMLBuilder): path = ctx['pathto'](file, 1) return '<script type="text/javascript" src="%s"></script>' % path - opts = """ -<script type="text/javascript"> - var DOCUMENTATION_OPTIONS = { - URL_ROOT: '%s', - VERSION: '%s', - COLLAPSE_INDEX: false, - FILE_SUFFIX: '', - HAS_SOURCE: '%s' - }; -</script>""" - opts = opts % (ctx.get('url_root', ''), escape(ctx['release']), - str(ctx['has_source']).lower()) - scripts = [] - for file in ctx['script_files']: - scripts.append(make_script(file)) - scripts.append(make_script('_static/websupport.js')) - return opts + '\n' + '\n'.join(scripts) + opts = { + 'URL_ROOT': ctx.get('url_root', ''), + 'VERSION': ctx['release'], + 'COLLAPSE_INDEX': False, + 'FILE_SUFFIX': '', + 'HAS_SOURCE': ctx['has_source'] + } + scripts = [make_script('_static/websupport.js')] + scripts += [make_script(file) for file in ctx['script_files']] + return '\n'.join([ + '<script type="text/javascript">' + 'var DOCUMENTATION_OPTIONS = %s;' % dump_json(opts), + '</script>' + ] + scripts) From eb2557f479ec5d5eee2bf260983a7c63e37f77be Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 10 Aug 2010 14:59:22 -0500 Subject: [PATCH 284/744] move opts in js --- sphinx/themes/basic/static/websupport.js | 37 ++++++++++++------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 11d812fdd..2e43c7324 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -632,6 +632,24 @@ }); }; + var opts = jQuery.extend({ + processVoteURL: '/process_vote', + addCommentURL: '/add_comment', + getCommentsURL: '/get_comments', + acceptCommentURL: '/accept_comment', + rejectCommentURL: '/reject_comment', + rejectCommentURL: '/delete_comment', + commentImage: '/static/_static/comment.png', + loadingImage: '/static/_static/ajax-loader.gif', + commentBrightImage: '/static/_static/comment-bright.png', + upArrow: '/static/_static/up.png', + downArrow: '/static/_static/down.png', + upArrowPressed: '/static/_static/up-pressed.png', + downArrowPressed: '/static/_static/down-pressed.png', + voting: false, + moderator: false + }, COMMENT_OPTIONS); + var replyTemplate = ' <li>\ <div class="reply_div" id="rd<%id%>">\ <form id="rf<%id%>">\ @@ -724,25 +742,6 @@ </div>\ <div id="focuser"></div>'; - - var opts = jQuery.extend({ - processVoteURL: '/process_vote', - addCommentURL: '/add_comment', - getCommentsURL: '/get_comments', - acceptCommentURL: '/accept_comment', - rejectCommentURL: '/reject_comment', - rejectCommentURL: '/delete_comment', - commentImage: '/static/_static/comment.png', - loadingImage: '/static/_static/comment.png', - commentBrightImage: '/static/_static/comment-bright.png', - upArrow: '/static/_static/up.png', - downArrow: '/static/_static/down.png', - upArrowPressed: '/static/_static/up-pressed.png', - downArrowPressed: '/static/_static/down-pressed.png', - voting: false, - moderator: false - }, COMMENT_OPTIONS); - $(document).ready(function() { init(); }); From 1068ece635731ca80dbcdb777600d60513170d63 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 10 Aug 2010 15:07:30 -0500 Subject: [PATCH 285/744] fix paths in js --- sphinx/websupport/__init__.py | 48 +++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 6fa9cf338..dc0361bd8 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -354,29 +354,33 @@ class WebSupport(object): that remains the same throughout the lifetime of the :class:`~sphinx.websupport.WebSupport` object. """ - comment_urls = [ - ('addCommentURL', 'add_comment'), - ('getCommentsURL', 'get_comments'), - ('processVoteURL', 'process_vote'), - ('acceptCommentURL', 'accept_comment'), - ('rejectCommentURL', 'reject_comment'), - ('deleteCommentURL', 'delete_comment') - ] - static_urls = [ - ('commentImage', 'comment.png'), - ('loadingImage', 'ajax-loader.gif'), - ('commentBrightImage', 'comment-bright.png'), - ('upArrow', 'up.png'), - ('upArrowPressed', 'up-pressed.png'), - ('downArrow', 'down.png'), - ('downArrowPressed', 'down-pressed.png') - ] - self.base_comment_opts = {} - for key, value in comment_urls: - self.base_comment_opts[key] = posixpath.join(self.docroot, value) - for key, value in static_urls: - self.base_comment_opts[key] = posixpath.join(self.staticdir, value) + + if self.docroot is not '': + comment_urls = [ + ('addCommentURL', 'add_comment'), + ('getCommentsURL', 'get_comments'), + ('processVoteURL', 'process_vote'), + ('acceptCommentURL', 'accept_comment'), + ('rejectCommentURL', 'reject_comment'), + ('deleteCommentURL', 'delete_comment') + ] + for key, value in comment_urls: + self.base_comment_opts[key] = \ + '/' + posixpath.join(self.docroot, value) + if self.staticdir != 'static': + static_urls = [ + ('commentImage', 'comment.png'), + ('loadingImage', 'ajax-loader.gif'), + ('commentBrightImage', 'comment-bright.png'), + ('upArrow', 'up.png'), + ('upArrowPressed', 'up-pressed.png'), + ('downArrow', 'down.png'), + ('downArrowPressed', 'down-pressed.png') + ] + for key, value in static_urls: + self.base_comment_opts[key] = \ + '/' + posixpath.join(self.staticdir, '_static', value) def _make_comment_options(self, username, moderator): """Helper method to create the parts of the COMMENT_OPTIONS From 8c83ca35c97ee70d45c20494715d6b20e5d7342f Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 10 Aug 2010 18:42:18 -0500 Subject: [PATCH 286/744] Don't need to convert moderator to string anymore --- sphinx/websupport/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index dc0361bd8..cc065b7f7 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -395,7 +395,7 @@ class WebSupport(object): rv.update({ 'voting': True, 'username': username, - 'moderator': str(moderator).lower(), + 'moderator': moderator, }) return '\n'.join([ '<script type="text/javascript">', From 1b99787dcdfcc388cb69193a14dc09dbeab0bc58 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Tue, 10 Aug 2010 19:51:21 -0500 Subject: [PATCH 287/744] changed comment.png to faded image. --- sphinx/themes/basic/static/comment.png | Bin 3501 -> 3445 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/sphinx/themes/basic/static/comment.png b/sphinx/themes/basic/static/comment.png index bad742bb08696f9103edeb65b301e514d1bb81de..92feb52b8824c6b0f59b658b1196c61de9162a95 100644 GIT binary patch delta 725 zcmV;`0xJEj8}%BntqOk#3ji@Y?^d_J0007~Nkl<ZILmdB%WK?K6vsc`b7!11)Y$lF zs)Aq?6Sa-0)<-c4QUy_hl<pMVxNs%tLaO)|lx`w|V$hACSQJVL6^kx3Rt#xc@D(E? zR6A{>6SX0oG@1P7p5tQTP_2G9_j2y}KF+yf9e3~jhGYAN7#n{(plv&cZ>p5jjX~Vt z&W0e)wpw1RFRuRd<fFsqxCjCmeC`BuuieS`d(-_phOc?QyHdKP@VLS~$T=u-02Ik| zrdDsfzWbrA`$zZ9F|l_L?cV<*2VWfE)2X=~n|r&z?CvU+n?)w)pyeTtfPjd=gD96w z=ayEV-*xZ6tDS!YZylWJ9~tWV!QryC!jVLdzyJ?`AW(#dvPYDYH2>$y3y+W7IwpXx z=YE;$?ke50)^cT!h;`u(IthXZ;o*T(tt~9yRhpcsUZ0Zsn?)wKnMwj#Tf-et;2~0o z>>ff2#g^Wl%9c_<U(WQ%kbq=DW*zgCfCv;$c1UKzog#neGT2ng?p>{jQW=qwkPNf- z3~Hw*Lf{707BPurWI<tTg)c<JvP1}l36exIk<3UYOkgHVh6yA^cq}%HeAa$nT&bPE zP@ItrB$){PO-Nu#n5jLV6rez;v08lf$j-q90ep0HcC=?><<OOvS5#Ujr1diba*oLE zYGoqawRnHNIe-85o38_~S3U_I8@X*_snL95d8LSzW=2wYdw1<62oXdQ`t^_Hxmvxk z9f09O?f>5V^d$9n9%TH$^zhb!tKLYaZCCeh=mvCQwJ3@lO+0=(IluJbo?SyP@n5*( z<)1tE$>Y_JtEcAUixbr&6Q6ziz~Ra7Zy*0)dQ(lO;o7(PUmQL<J9zB7lcztQsUCUr z-KmZLe0}DUe*1S}Z7naJt~VZ@J-#sd>{ItO=)2hMQs=(_n!ZpXD%P(H00000NkvXX Hu0mjf_7rZ@ delta 782 zcmV+p1M&Rz8m$|!tqOkz1r-M@pqmTC0008tNkl<ZILn2RUuf246vw~kJkRg9_x11W zVon0J6*?w5rxj9&)J+gbh;-{kL3mN1gv@ei;6;fUjY#OvF4Qhw!l)sIo88PD3L|X} z*`~wB-pxOI=XAb*eD`~v=NvD7YuJVL<y;&%U%uyaID|R9)OvpveMdF`*h=qroP2)8 zvgkP{x^mWORzz>jRD8bs%7c-c$2Z@{6Cwa;{NOT%zS;z|oNGAQ-ke)imt9kFva~!a zC{+cOBmfE*8=5%PQ#jspsOjkTBSSdT+6dZlbQnMHTZ`S_-F)$lmfGHi`fPM}#-ox1 zloL?Z2}BhkD7=5DK9e=nJNR(V*YB)upC`cf3k|);ng<g_JXs82A_NgA5g>}7KoHf$ zD|s)vC}!&HySMlBo;6<*0Q}T9eQ8x)=H=;fphr&BD-kmQP47&%1I`OvP$XWIRd1a5 zeR8vH%jKV4m^BTBQoyWN5D{e~0&@zd3WOkV0bpVfiHd&`d58K}o0inu04y)f>Jnpx zjA6uv5Je1T0?h@uK;R%C0stme!1<6d2DP?a4ViKxwl=niEg_B=ktIYX4G;y4XCw>| zBqdUYWeKF@>~zr;oR_j?T5Jsv8KO+Yh-^wBqA)Cg15^NO0^$G_(FaoxtGD<<e<6Q8 zUzxC$!OVYT4QpfxH8G(k25Mp;j#DaYSgm0-mS}jabn)BwUMTSB_pemPZkJjwU!Oq; z3d0~`f-!_ljroT}4HGa|gMSxqf3a)zP5|Ho=M=dQo6cMvnR#n;qO4<uDws%p0K8WK z1j`^1=mJ*y?b77XiSF?h0Kmr1^v`v>&Zo_`?u~z+caC3d@48zWxaVYG%IUAyib<~T ze(}^Fg^?}$|N0D1Q}aio^L^Wo`LloCN%WJ``K~t)_iugs(}8t({cP>LVMWJNbFlN+ zaN{>UQ{x}!#=5Znmqq`czKj3WwuAlkueDuiMD~Xm0I<B{KmCsZzk6ggYXo`_00000 MNks-uM6N<$g3my5+5i9m From 1d4c7d4fe01bc768ebb0bfc9765f58a86dcc74c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Wed, 11 Aug 2010 14:23:58 +0200 Subject: [PATCH 288/744] Added initial versioning support --- sphinx/builders/websupport.py | 38 +++++++++++++++++++ sphinx/websupport/storage/__init__.py | 4 +- sphinx/websupport/storage/db.py | 8 ++-- .../websupport/storage/sqlalchemystorage.py | 4 +- sphinx/writers/websupport.py | 3 +- tests/test_websupport.py | 26 +++++++------ 6 files changed, 65 insertions(+), 18 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 8cc70ea0c..e1bd80111 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -12,14 +12,23 @@ import cPickle as pickle from os import path from cgi import escape +from glob import glob +import os import posixpath import shutil + from docutils.io import StringOutput +from docutils.utils import Reporter from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile from sphinx.util.jsonimpl import dumps as dump_json from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.writers.websupport import WebSupportTranslator +from sphinx.environment import WarningStream +from sphinx.versioning import add_uids, merge_doctrees + +def is_paragraph(node): + return node.__class__.__name__ == 'paragraph' class WebSupportBuilder(StandaloneHTMLBuilder): """ @@ -28,13 +37,39 @@ class WebSupportBuilder(StandaloneHTMLBuilder): name = 'websupport' out_suffix = '.fpickle' + def init(self): + StandaloneHTMLBuilder.init(self) + for f in glob(path.join(self.doctreedir, '*.doctree')): + copyfile(f, f + '.old') + def init_translator_class(self): self.translator_class = WebSupportTranslator + def get_old_doctree(self, docname): + fp = self.env.doc2path(docname, self.doctreedir, '.doctree.old') + try: + f = open(fp, 'rb') + try: + doctree = pickle.load(f) + finally: + f.close() + except IOError: + return None + doctree.settings.env = self.env + doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, + stream=WarningStream(self.env._warnfunc)) + return doctree + def write_doc(self, docname, doctree): destination = StringOutput(encoding='utf-8') doctree.settings = self.docsettings + old_doctree = self.get_old_doctree(docname) + if old_doctree: + list(merge_doctrees(old_doctree, doctree, is_paragraph)) + else: + list(add_uids(doctree, is_paragraph)) + self.cur_docname = docname self.secnumbers = self.env.toc_secnumbers.get(docname, {}) self.imgpath = '/' + posixpath.join(self.app.staticdir, '_images') @@ -123,6 +158,9 @@ class WebSupportBuilder(StandaloneHTMLBuilder): shutil.move(path.join(self.outdir, '_static'), path.join(self.app.builddir, self.app.staticdir, '_static')) + for f in glob(path.join(self.doctreedir, '*.doctree.old')): + os.remove(f) + def dump_search_index(self): self.indexer.finish_indexing() diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py index 17907e992..da815d0a3 100644 --- a/sphinx/websupport/storage/__init__.py +++ b/sphinx/websupport/storage/__init__.py @@ -16,9 +16,11 @@ class StorageBackend(object): """ pass - def add_node(self, document, line, source): + def add_node(self, id, document, line, source): """Add a node to the StorageBackend. + :param id: a unique id for the comment. + :param document: the name of the document the node belongs to. :param line: the line in the source where the node begins. diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index 74a3e2b70..54b16f225 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -11,6 +11,7 @@ """ from datetime import datetime +from uuid import uuid4 from sqlalchemy import Column, Integer, Text, String, Boolean, ForeignKey,\ DateTime @@ -28,7 +29,7 @@ class Node(Base): """Data about a Node in a doctree.""" __tablename__ = db_prefix + 'nodes' - id = Column(Integer, primary_key=True) + id = Column(String(32), primary_key=True) document = Column(String(256), nullable=False) line = Column(Integer) source = Column(Text, nullable=False) @@ -93,7 +94,8 @@ class Node(Base): return comments - def __init__(self, document, line, source): + def __init__(self, id, document, line, source): + self.id = id self.document = document self.line = line self.source = source @@ -112,7 +114,7 @@ class Comment(Base): proposal_diff = Column(Text) path = Column(String(256), index=True) - node_id = Column(Integer, ForeignKey(db_prefix + 'nodes.id')) + node_id = Column(String, ForeignKey(db_prefix + 'nodes.id')) node = relation(Node, backref="comments") def __init__(self, text, displayed, username, rating, time, diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index e2cd87ac5..d1683f603 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -33,8 +33,8 @@ class SQLAlchemyStorage(StorageBackend): def pre_build(self): self.build_session = Session() - def add_node(self, document, line, source): - node = Node(document, line, source) + def add_node(self, id, document, line, source): + node = Node(id, document, line, source) self.build_session.add(node) self.build_session.flush() return node diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 05bc2c8b3..306cfd869 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -55,7 +55,8 @@ class WebSupportTranslator(HTMLTranslator): def add_db_node(self, node): storage = self.builder.app.storage - db_node_id = storage.add_node(document=self.builder.cur_docname, + db_node_id = storage.add_node(id=node.uid, + document=self.builder.cur_docname, line=node.line, source=node.rawsource or node.astext()) return db_node_id diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 32249976d..3e784405e 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -12,6 +12,8 @@ import os from StringIO import StringIO +from nose import SkipTest + from sphinx.websupport import WebSupport from sphinx.websupport.errors import * from sphinx.websupport.storage.differ import CombinedHtmlDiff @@ -79,10 +81,10 @@ def test_comments(support): # Create a displayed comment and a non displayed comment. comment = support.add_comment('First test comment', - node_id=str(first_node.id), + node_id=first_node.id, username='user_one') hidden_comment = support.add_comment('Hidden comment', - node_id=str(first_node.id), + node_id=first_node.id, displayed=False) # Make sure that comments can't be added to a comment where # displayed == False, since it could break the algorithm that @@ -96,11 +98,11 @@ def test_comments(support): parent_id=str(comment['id']), displayed=False) # Add a comment to another node to make sure it isn't returned later. support.add_comment('Second test comment', - node_id=str(second_node.id), + node_id=second_node.id, username='user_two') # Access the comments as a moderator. - data = support.get_data(str(first_node.id), moderator=True) + data = support.get_data(first_node.id, moderator=True) comments = data['comments'] children = comments[0]['children'] assert len(comments) == 2 @@ -109,7 +111,7 @@ def test_comments(support): assert children[1]['text'] == 'Hidden child test comment' # Access the comments without being a moderator. - data = support.get_data(str(first_node.id)) + data = support.get_data(first_node.id) comments = data['comments'] children = comments[0]['children'] assert len(comments) == 1 @@ -124,10 +126,10 @@ def test_voting(support): nodes = session.query(Node).all() node = nodes[0] - comment = support.get_data(str(node.id))['comments'][0] + comment = support.get_data(node.id)['comments'][0] def check_rating(val): - data = support.get_data(str(node.id)) + data = support.get_data(node.id) comment = data['comments'][0] assert comment['rating'] == val, '%s != %s' % (comment['rating'], val) @@ -156,13 +158,13 @@ def test_proposals(support): session = Session() node = session.query(Node).first() - data = support.get_data(str(node.id)) + data = support.get_data(node.id) source = data['source'] proposal = source[:5] + source[10:15] + 'asdf' + source[15:] comment = support.add_comment('Proposal comment', - node_id=str(node.id), + node_id=node.id, proposal=proposal) @@ -172,7 +174,7 @@ def test_user_delete_comments(support): session = Session() node = session.query(Node).first() session.close() - return support.get_data(str(node.id))['comments'][0] + return support.get_data(node.id)['comments'][0] comment = get_comment() assert comment['username'] == 'user_one' @@ -192,7 +194,7 @@ def test_moderator_delete_comments(support): session = Session() node = session.query(Node).first() session.close() - return support.get_data(str(node.id), moderator=True)['comments'][1] + return support.get_data(node.id, moderator=True)['comments'][1] comment = get_comment() support.delete_comment(comment['id'], username='user_two', @@ -228,6 +230,8 @@ def moderation_callback(comment): @with_support(moderation_callback=moderation_callback) def test_moderation(support): + raise SkipTest( + 'test is broken, relies on order of test execution and numeric ids') accepted = support.add_comment('Accepted Comment', node_id=3, displayed=False) rejected = support.add_comment('Rejected comment', node_id=3, From e1ce9a63a664712ceeeda651e4dc039cd9f9eaa8 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 11 Aug 2010 17:16:20 +0200 Subject: [PATCH 289/744] Fix file name in manifest. --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 5e3104a82..cfc44c17e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,7 +7,7 @@ include TODO include babel.cfg include Makefile -include setup_distribute.py +include distribute_setup.py include sphinx-autogen.py include sphinx-build.py include sphinx-quickstart.py From ada394fe949ff246af428456754435fc10c26541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Wed, 11 Aug 2010 18:15:30 +0200 Subject: [PATCH 290/744] Fixing indentation etc. Note: We need a javascript styleguide --- sphinx/themes/basic/static/websupport.js | 252 +++++++++++------------ 1 file changed, 123 insertions(+), 129 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 2e43c7324..5ee59b845 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -5,15 +5,13 @@ $.fn.autogrow.resize(textarea); - $(textarea) - .focus(function() { - textarea.interval = setInterval(function() { - $.fn.autogrow.resize(textarea); - }, 500); - }) - .blur(function() { - clearInterval(textarea.interval); - }); + $(textarea).focus(function() { + textarea.interval = setInterval(function() { + $.fn.autogrow.resize(textarea); + }, 500); + }).blur(function() { + clearInterval(textarea.interval); + }); }); }; @@ -113,11 +111,11 @@ if (document.cookie.length > 0) { var start = document.cookie.indexOf('sortBy='); if (start != -1) { - start = start + 7; - var end = document.cookie.indexOf(";", start); - if (end == -1) - end = document.cookie.length; - by = unescape(document.cookie.substring(start, end)); + start = start + 7; + var end = document.cookie.indexOf(";", start); + if (end == -1) + end = document.cookie.length; + by = unescape(document.cookie.substring(start, end)); } } setComparator(by); @@ -132,12 +130,12 @@ // Reset the main comment form, and set the value of the parent input. $('form#comment_form') .find('textarea,input') - .removeAttr('disabled').end() + .removeAttr('disabled').end() .find('input[name="node"]') - .val(id).end() + .val(id).end() .find('textarea[name="proposal"]') - .val('') - .hide(); + .val('') + .hide(); // Position the popup and show it. var clientWidth = document.documentElement.clientWidth; @@ -145,12 +143,12 @@ $('div#focuser').fadeIn('fast'); $('div.popup_comment') .css({ - 'top': 100+$(window).scrollTop(), - 'left': clientWidth/2-popupWidth/2, - 'position': 'absolute' + 'top': 100+$(window).scrollTop(), + 'left': clientWidth/2-popupWidth/2, + 'position': 'absolute' }) .fadeIn('fast', function() { - getComments(id); + getComments(id); }); }; @@ -163,9 +161,9 @@ $('ul#comment_ul').empty(); $('h3#comment_notification').show(); $('form#comment_form').find('textarea') - .val('').end() - .find('textarea, input') - .removeAttr('disabled'); + .val('').end() + .find('textarea, input') + .removeAttr('disabled'); }); }; @@ -179,28 +177,27 @@ url: opts.getCommentsURL, data: {node: id}, success: function(data, textStatus, request) { - var ul = $('ul#comment_ul').hide(); - $('form#comment_form') - .find('textarea[name="proposal"]') - .data('source', data.source); + var ul = $('ul#comment_ul').hide(); + $('form#comment_form') + .find('textarea[name="proposal"]') + .data('source', data.source); - if (data.comments.length == 0) { - ul.html('<li>No comments yet.</li>'); - commentListEmpty = true; - var speed = 100; - } - else { - // If there are comments, sort them and put them in the list. - var comments = sortComments(data.comments); - var speed = data.comments.length * 100; - appendComments(comments, ul); - commentListEmpty = false; - } - $('h3#comment_notification').slideUp(speed+200); - ul.slideDown(speed); + if (data.comments.length == 0) { + ul.html('<li>No comments yet.</li>'); + commentListEmpty = true; + var speed = 100; + } else { + // If there are comments, sort them and put them in the list. + var comments = sortComments(data.comments); + var speed = data.comments.length * 100; + appendComments(comments, ul); + commentListEmpty = false; + } + $('h3#comment_notification').slideUp(speed+200); + ul.slideDown(speed); }, error: function(request, textStatus, error) { - showError('Oops, there was a problem retrieving the comments.'); + showError('Oops, there was a problem retrieving the comments.'); }, dataType: 'json' }); @@ -219,28 +216,30 @@ type: "POST", url: opts.addCommentURL, dataType: 'json', - data: {node: node_id, - parent: form.find('input[name="parent"]').val(), - text: form.find('textarea[name="comment"]').val(), - proposal: form.find('textarea[name="proposal"]').val()}, + data: { + node: node_id, + parent: form.find('input[name="parent"]').val(), + text: form.find('textarea[name="comment"]').val(), + proposal: form.find('textarea[name="proposal"]').val() + }, success: function(data, textStatus, error) { - // Reset the form. - if (node_id) { - hideProposeChange(node_id); - } - form.find('textarea') - .val('') - .add(form.find('input')) + // Reset the form. + if (node_id) { + hideProposeChange(node_id); + } + form.find('textarea') + .val('') + .add(form.find('input')) .removeAttr('disabled'); - if (commentListEmpty) { - $('ul#comment_ul').empty(); - commentListEmpty = false; - } - insertComment(data.comment); + if (commentListEmpty) { + $('ul#comment_ul').empty(); + commentListEmpty = false; + } + insertComment(data.comment); }, error: function(request, textStatus, error) { - form.find('textarea,input').removeAttr('disabled'); - showError('Oops, there was a problem adding the comment.'); + form.find('textarea,input').removeAttr('disabled'); + showError('Oops, there was a problem adding the comment.'); } }); }; @@ -274,8 +273,7 @@ if (comment.node != null) { var ul = $('ul#comment_ul'); var siblings = getChildren(ul); - } - else { + } else { var ul = $('#cl' + comment.parent); var siblings = getChildren(ul); } @@ -286,11 +284,11 @@ // Determine where in the parents children list to insert this comment. for(i=0; i < siblings.length; i++) { if (comp(comment, siblings[i]) <= 0) { - $('#cd' + siblings[i].id) - .parent() - .before(li.html(div)); - li.slideDown('fast'); - return; + $('#cd' + siblings[i].id) + .parent() + .before(li.html(div)); + li.slideDown('fast'); + return; } } @@ -306,10 +304,10 @@ url: opts.acceptCommentURL, data: {id: id}, success: function(data, textStatus, request) { - $('#cm' + id).fadeOut('fast'); + $('#cm' + id).fadeOut('fast'); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem accepting the comment."); + showError("Oops, there was a problem accepting the comment."); }, }); }; @@ -320,13 +318,13 @@ url: opts.rejectCommentURL, data: {id: id}, success: function(data, textStatus, request) { - var div = $('#cd' + id); - div.slideUp('fast', function() { - div.remove(); - }); + var div = $('#cd' + id); + div.slideUp('fast', function() { + div.remove(); + }); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem rejecting the comment."); + showError("Oops, there was a problem rejecting the comment."); }, }); }; @@ -337,22 +335,22 @@ url: opts.deleteCommentURL, data: {id: id}, success: function(data, textStatus, request) { - var div = $('#cd' + id); - div - .find('span.user_id:first') - .text('[deleted]').end() - .find('p.comment_text:first') - .text('[deleted]').end() - .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + - ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) - .remove(); - var comment = div.data('comment'); - comment.username = '[deleted]'; - comment.text = '[deleted]'; - div.data('comment', comment); + var div = $('#cd' + id); + div + .find('span.user_id:first') + .text('[deleted]').end() + .find('p.comment_text:first') + .text('[deleted]').end() + .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + + ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) + .remove(); + var comment = div.data('comment'); + comment.username = '[deleted]'; + comment.text = '[deleted]'; + div.data('comment', comment); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem deleting the comment."); + showError("Oops, there was a problem deleting the comment."); }, }); }; @@ -437,10 +435,8 @@ // If this is not an unvote, and the other vote arrow has // already been pressed, unpress it. if ((d.value != 0) && (data.vote == d.value*-1)) { - $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id) - .hide(); - $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id) - .show(); + $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide(); + $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show(); } // Update the comments rating in the local data. @@ -458,7 +454,7 @@ url: opts.processVoteURL, data: d, error: function(request, textStatus, error) { - showError("Oops, there was a problem casting that vote."); + showError("Oops, there was a problem casting that vote."); } }); }; @@ -477,12 +473,12 @@ .prepend(div) // Setup the submit handler for the reply form. .find('#rf' + id) - .submit(function(event) { - event.preventDefault(); - addComment($('#rf' + id)); - closeReply(id); - }); - div.slideDown('fast'); + .submit(function(event) { + event.preventDefault(); + addComment($('#rf' + id)); + closeReply(id); + }); + div.slideDown('fast'); }; /** @@ -520,10 +516,10 @@ if (by.substring(0,3) == 'asc') { var i = by.substring(3); comp = function(a, b) { return a[i] - b[i]; } - } - // Otherwise sort in descending order. - else + } else { + // Otherwise sort in descending order. comp = function(a, b) { return b[by] - a[by]; } + } // Reset link styles and format the selected sort option. $('a.sel').attr('href', '#').removeClass('sel'); @@ -536,15 +532,14 @@ */ function getChildren(ul, recursive) { var children = []; - ul.children().children("[id^='cd']") - .each(function() { - var comment = $(this).data('comment'); - if (recursive) { - comment.children = - getChildren($(this).find('#cl' + comment.id), true); - } - children.push(comment); - }); + ul.children().children("[id^='cd']").each(function() { + var comment = $(this).data('comment'); + if (recursive) { + comment.children = + getChildren($(this).find('#cl' + comment.id), true); + } + children.push(comment); + }); return children; }; @@ -569,16 +564,15 @@ if (comment.text != '[deleted]') { div.find('a.reply').show(); if (comment.proposal_diff) { - div.find('#sp' + comment.id).show(); + div.find('#sp' + comment.id).show(); } if (opts.moderator && !comment.displayed) { - div.find('#cm' + comment.id).show(); + div.find('#cm' + comment.id).show(); } if (opts.moderator || (opts.username == comment.username)) { - div.find('#dc' + comment.id).show(); + div.find('#dc' + comment.id).show(); } } - return div; } @@ -592,7 +586,7 @@ function handle(ph, escape) { var cur = context; $.each(ph.split('.'), function() { - cur = cur[this]; + cur = cur[this]; }); return escape ? esc.text(cur || "").html() : cur; } @@ -606,10 +600,10 @@ $('<div class="popup_error">' + '<h1>' + message + '</h1>' + '</div>') - .appendTo('body') - .fadeIn("slow") + .appendTo('body') + .fadeIn("slow") .delay(2000) - .fadeOut("slow"); + .fadeOut("slow"); }; /** @@ -622,13 +616,13 @@ var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; $(this).append( - $('<a href="#" class="sphinx_comment"></a>') - .html('<img src="' + image + '" alt="comment" />') - .attr('title', title) - .click(function(event) { - event.preventDefault(); - show($(this).parent().attr('id')); - })); + $('<a href="#" class="sphinx_comment"></a>') + .html('<img src="' + image + '" alt="comment" />') + .attr('title', title) + .click(function(event) { + event.preventDefault(); + show($(this).parent().attr('id')); + })); }); }; @@ -759,4 +753,4 @@ $(document).ready(function() { result.highlightText(this.toLowerCase(), 'highlighted'); }); }); -}); \ No newline at end of file +}); From 14a99c8b8610e43f811d9b2c9e360e72299ba1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Thu, 12 Aug 2010 02:54:54 +0200 Subject: [PATCH 291/744] Fallback to zip_longest for python3 --- sphinx/versioning.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 42b8bd515..ef8effab4 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -10,7 +10,11 @@ :license: BSD, see LICENSE for details. """ from uuid import uuid4 -from itertools import izip_longest, product +from itertools import product +try: + from itertools import izip_longest +except ImportError: + from itertools import zip_longest from difflib import SequenceMatcher from sphinx.util import PeekableIterator From 24979fbde15096d3e39f2cfafdb8d6cba83cb668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Thu, 12 Aug 2010 03:09:03 +0200 Subject: [PATCH 292/744] Fixed NameError introduced by last changeset --- sphinx/versioning.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index ef8effab4..d0ea18a77 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -12,7 +12,7 @@ from uuid import uuid4 from itertools import product try: - from itertools import izip_longest + from itertools import izip_longest as zip_longest except ImportError: from itertools import zip_longest from difflib import SequenceMatcher @@ -62,7 +62,7 @@ def merge_doctrees(old, new, condition): new_iter = PeekableIterator(new.traverse(condition)) old_nodes = [] new_nodes = [] - for old_node, new_node in izip_longest(old_iter, new_iter): + for old_node, new_node in zip_longest(old_iter, new_iter): if old_node is None: new_nodes.append(new_node) continue From 7e8fb7141b357ef6c1d726ad96b0e05f6db64be3 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 12 Aug 2010 14:41:25 -0500 Subject: [PATCH 293/744] allow commenting on literal_blocks --- sphinx/writers/websupport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 05bc2c8b3..c6516bf17 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -15,7 +15,7 @@ class WebSupportTranslator(HTMLTranslator): """ Our custom HTML translator. """ - commentable_nodes = ['paragraph'] + commentable_nodes = ['paragraph', 'literal_block'] def __init__(self, builder, *args, **kwargs): HTMLTranslator.__init__(self, builder, *args, **kwargs) From 5922d977392119d15f40963e8a5d7fe9ca368ade Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 12 Aug 2010 17:07:15 -0500 Subject: [PATCH 294/744] separate js templates from js scripts --- .../basic/static/websupport-templates.html | 97 ++++++++++++++ sphinx/themes/basic/static/websupport.js | 123 ++++-------------- sphinx/websupport/__init__.py | 3 +- 3 files changed, 122 insertions(+), 101 deletions(-) create mode 100644 sphinx/themes/basic/static/websupport-templates.html diff --git a/sphinx/themes/basic/static/websupport-templates.html b/sphinx/themes/basic/static/websupport-templates.html new file mode 100644 index 000000000..1ea58ab24 --- /dev/null +++ b/sphinx/themes/basic/static/websupport-templates.html @@ -0,0 +1,97 @@ +<div id="templates"> + <div id="reply_template"> + <Li> + <div class="reply_div" id="rd<%id%>"> + <form id="rf<%id%>"> + <textarea name="comment" cols="80"></textarea> + <input type="submit" value="add reply" /> + <input type="hidden" name="parent" value="<%id%>" /> + <input type="hidden" name="node" value="" /> + </form> + </div> + </li> + </div> + + <div id="comment_template"> + <div id="cd<%id%>" class="spxcdiv"> + <div class="vote"> + <div class="arrow"> + <a href="#" id="uv<%id%>" class="vote"> + <img src="<%upArrow%>" /> + </a> + <a href="#" id="uu<%id%>" class="un vote"> + <img src="<%upArrowPressed%>" /> + </a> + </div> + <div class="arrow"> + <a href="#" id="dv<%id%>" class="vote"> + <img src="<%downArrow%>" id="da<%id%>" /> + </a> + <a href="#" id="du<%id%>" class="un vote"> + <img src="<%downArrowPressed%>" /> + </a> + </div> + </div> + <div class="comment_content"> + <p class="tagline comment"> + <span class="user_id"><%username%></span> + <span class="rating"><%pretty_rating%></span> + <span class="delta"><%time.delta%></span> + </p> + <p class="comment_text comment"><%text%></p> + <p class="comment_opts comment"> + <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a> + <a href="#" class="close_reply" id="cr<%id%>">reply ▿</a> + <a href="#" id="sp<%id%>" class="show_proposal"> + proposal ▹ + </a> + <a href="#" id="hp<%id%>" class="hide_proposal"> + proposal ▿ + </a> + <a href="#" id="dc<%id%>" class="delete_comment hidden"> + delete + </a> + <span id="cm<%id%>" class="moderation hidden"> + <a href="#" id="ac<%id%>" class="accept_comment">accept</a> + <a href="#" id="rc<%id%>" class="reject_comment">reject</a> + </span> + </p> + <pre class="proposal" id="pr<%id%>"> + <#proposal_diff#> + </pre> + <ul class="children" id="cl<%id%>"></ul> + </div> + <div class="clearleft"></div> + </div> + </div> + + <div id="popup_template"> + <div class="popup_comment"> + <a id="comment_close" href="#">x</a> + <h1>Comments</h1> + <form method="post" id="comment_form" action="/docs/add_comment"> + <textarea name="comment" cols="80"></textarea> + <p class="propose_button"> + <a href="#" class="show_propose_change"> + Propose a change ▹ + </a> + <a href="#" class="hide_propose_change"> + Propose a change ▿ + </a> + </p> + <textarea name="proposal" cols="80" spellcheck="false"></textarea> + <input type="submit" value="add comment" id="comment_button" /> + <input type="hidden" name="node" /> + <input type="hidden" name="parent" value="" /> + <p class="sort_options"> + Sort by: + <a href="#" class="sort_option" id="rating">top</a> + <a href="#" class="sort_option" id="ascage">newest</a> + <a href="#" class="sort_option" id="age">oldest</a> + </p> + </form> + <h3 id="comment_notification">loading comments... <img src="<%loadingImage%>" alt="" /></h3> + <ul id="comment_ul"></ul> + </div> + </div> +</div> diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 2e43c7324..1f00ee465 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -31,11 +31,10 @@ })(jQuery); (function($) { - var commentListEmpty, popup, comp; + var commentListEmpty, popup, comp, commentTemplate, replyTemplate; function init() { initTemplates(); - initEvents(); initComparator(); }; @@ -95,12 +94,27 @@ }; function initTemplates() { - // Create our popup div, the same div is recycled each time comments - // are displayed. - popup = $(renderTemplate(popupTemplate, opts)); - // Setup autogrow on the textareas - popup.find('textarea').autogrow(); - $('body').append(popup); + var templateURL = opts.staticDir + '/_static/websupport-templates.html'; + $.get(templateURL, function(data) { + var templates = $(data); + function loadTemplate(id) { + var html = templates.find('#' + id).html(); + html = html.replace(/(<)|(%3C)/g, "<"); + html = html.replace(/(>)|(%3E)/g, ">"); + return html; + }; + // Create our popup div, the same div is recycled each time comments + // are displayed. + // Setup autogrow on the textareas + var popupTemplate = loadTemplate('popup_template'); + popup = $(renderTemplate(popupTemplate, opts)); + popup.find('textarea').autogrow(); + + commentTemplate = loadTemplate('#comment_template'); + replyTemplate = loadTemplate('#reply_template'); + $('body').append(popup); + initEvents(); + }); }; /** @@ -646,102 +660,11 @@ downArrow: '/static/_static/down.png', upArrowPressed: '/static/_static/up-pressed.png', downArrowPressed: '/static/_static/down-pressed.png', + staticDir: '/static', voting: false, moderator: false }, COMMENT_OPTIONS); - var replyTemplate = ' <li>\ - <div class="reply_div" id="rd<%id%>">\ - <form id="rf<%id%>">\ - <textarea name="comment" cols="80"></textarea>\ - <input type="submit" value="add reply" />\ - <input type="hidden" name="parent" value="<%id%>" />\ - <input type="hidden" name="node" value="" />\ - </form>\ - </div>\ - </li>'; - - var commentTemplate = ' <div id="cd<%id%>" class="spxcdiv">\ - <div class="vote">\ - <div class="arrow">\ - <a href="#" id="uv<%id%>" class="vote">\ - <img src="<%upArrow%>" />\ - </a>\ - <a href="#" id="uu<%id%>" class="un vote">\ - <img src="<%upArrowPressed%>" />\ - </a>\ - </div>\ - <div class="arrow">\ - <a href="#" id="dv<%id%>" class="vote">\ - <img src="<%downArrow%>" id="da<%id%>" />\ - </a>\ - <a href="#" id="du<%id%>" class="un vote">\ - <img src="<%downArrowPressed%>" />\ - </a>\ - </div>\ - </div>\ - <div class="comment_content">\ - <p class="tagline comment">\ - <span class="user_id"><%username%></span>\ - <span class="rating"><%pretty_rating%></span>\ - <span class="delta"><%time.delta%></span>\ - </p>\ - <p class="comment_text comment"><%text%></p>\ - <p class="comment_opts comment">\ - <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\ - <a href="#" class="close_reply" id="cr<%id%>">reply ▿</a>\ - <a href="#" id="sp<%id%>" class="show_proposal">\ - proposal ▹\ - </a>\ - <a href="#" id="hp<%id%>" class="hide_proposal">\ - proposal ▿\ - </a>\ - <a href="#" id="dc<%id%>" class="delete_comment hidden">\ - delete\ - </a>\ - <span id="cm<%id%>" class="moderation hidden">\ - <a href="#" id="ac<%id%>" class="accept_comment">accept</a>\ - <a href="#" id="rc<%id%>" class="reject_comment">reject</a>\ - </span>\ - </p>\ - <pre class="proposal" id="pr<%id%>">\ -<#proposal_diff#>\ - </pre>\ - <ul class="children" id="cl<%id%>"></ul>\ - </div>\ - <div class="clearleft"></div>\ - </div>'; - - var popupTemplate = ' <div class="popup_comment">\ - <a id="comment_close" href="#">x</a>\ - <h1>Comments</h1>\ - <form method="post" id="comment_form" action="/docs/add_comment">\ - <textarea name="comment" cols="80"></textarea>\ - <p class="propose_button">\ - <a href="#" class="show_propose_change">\ - Propose a change ▹\ - </a>\ - <a href="#" class="hide_propose_change">\ - Propose a change ▿\ - </a>\ - </p>\ - <textarea name="proposal" cols="80" spellcheck="false"></textarea>\ - <input type="submit" value="add comment" id="comment_button" />\ - <input type="hidden" name="node" />\ - <input type="hidden" name="parent" value="" />\ - <p class="sort_options">\ - Sort by:\ - <a href="#" class="sort_option" id="rating">top</a>\ - <a href="#" class="sort_option" id="ascage">newest</a>\ - <a href="#" class="sort_option" id="age">oldest</a>\ - </p>\ - </form>\ - <h3 id="comment_notification">loading comments... <img src="' + - opts.loadingImage + '" alt="" /></h3>\ - <ul id="comment_ul"></ul>\ - </div>\ - <div id="focuser"></div>'; - $(document).ready(function() { init(); }); diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index cc065b7f7..939428a6c 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -376,7 +376,8 @@ class WebSupport(object): ('upArrow', 'up.png'), ('upArrowPressed', 'up-pressed.png'), ('downArrow', 'down.png'), - ('downArrowPressed', 'down-pressed.png') + ('downArrowPressed', 'down-pressed.png'), + ('staticDir', '/' + self.staticdir) ] for key, value in static_urls: self.base_comment_opts[key] = \ From 4dc41e52939907558f881b4aa40fd822315aa6de Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 12 Aug 2010 17:08:27 -0500 Subject: [PATCH 295/744] remove modal focuser --- sphinx/themes/basic/static/websupport.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 1f00ee465..80c6a9a5b 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -156,7 +156,6 @@ // Position the popup and show it. var clientWidth = document.documentElement.clientWidth; var popupWidth = $('div.popup_comment').width(); - $('div#focuser').fadeIn('fast'); $('div.popup_comment') .css({ 'top': 100+$(window).scrollTop(), @@ -172,7 +171,6 @@ * Hide the comments popup window. */ function hide() { - $('div#focuser').fadeOut('fast'); $('div.popup_comment').fadeOut('fast', function() { $('ul#comment_ul').empty(); $('h3#comment_notification').show(); From 305977359d5a210c136d2ef638fb1365384bd187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 13 Aug 2010 11:37:21 +0200 Subject: [PATCH 296/744] Fix indentation --- .../basic/static/websupport-templates.html | 132 +++++++++--------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/sphinx/themes/basic/static/websupport-templates.html b/sphinx/themes/basic/static/websupport-templates.html index 1ea58ab24..9e7c06690 100644 --- a/sphinx/themes/basic/static/websupport-templates.html +++ b/sphinx/themes/basic/static/websupport-templates.html @@ -1,13 +1,13 @@ <div id="templates"> <div id="reply_template"> - <Li> + <li> <div class="reply_div" id="rd<%id%>"> - <form id="rf<%id%>"> - <textarea name="comment" cols="80"></textarea> + <form id="rf<%id%>"> + <textarea name="comment" cols="80"></textarea> <input type="submit" value="add reply" /> <input type="hidden" name="parent" value="<%id%>" /> <input type="hidden" name="node" value="" /> - </form> + </form> </div> </li> </div> @@ -15,51 +15,51 @@ <div id="comment_template"> <div id="cd<%id%>" class="spxcdiv"> <div class="vote"> - <div class="arrow"> - <a href="#" id="uv<%id%>" class="vote"> - <img src="<%upArrow%>" /> - </a> - <a href="#" id="uu<%id%>" class="un vote"> - <img src="<%upArrowPressed%>" /> - </a> - </div> - <div class="arrow"> - <a href="#" id="dv<%id%>" class="vote"> - <img src="<%downArrow%>" id="da<%id%>" /> - </a> - <a href="#" id="du<%id%>" class="un vote"> - <img src="<%downArrowPressed%>" /> - </a> - </div> + <div class="arrow"> + <a href="#" id="uv<%id%>" class="vote"> + <img src="<%upArrow%>" /> + </a> + <a href="#" id="uu<%id%>" class="un vote"> + <img src="<%upArrowPressed%>" /> + </a> + </div> + <div class="arrow"> + <a href="#" id="dv<%id%>" class="vote"> + <img src="<%downArrow%>" id="da<%id%>" /> + </a> + <a href="#" id="du<%id%>" class="un vote"> + <img src="<%downArrowPressed%>" /> + </a> + </div> </div> <div class="comment_content"> - <p class="tagline comment"> - <span class="user_id"><%username%></span> - <span class="rating"><%pretty_rating%></span> - <span class="delta"><%time.delta%></span> - </p> - <p class="comment_text comment"><%text%></p> - <p class="comment_opts comment"> - <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a> - <a href="#" class="close_reply" id="cr<%id%>">reply ▿</a> - <a href="#" id="sp<%id%>" class="show_proposal"> - proposal ▹ - </a> - <a href="#" id="hp<%id%>" class="hide_proposal"> - proposal ▿ - </a> - <a href="#" id="dc<%id%>" class="delete_comment hidden"> - delete - </a> - <span id="cm<%id%>" class="moderation hidden"> - <a href="#" id="ac<%id%>" class="accept_comment">accept</a> - <a href="#" id="rc<%id%>" class="reject_comment">reject</a> - </span> - </p> - <pre class="proposal" id="pr<%id%>"> - <#proposal_diff#> - </pre> - <ul class="children" id="cl<%id%>"></ul> + <p class="tagline comment"> + <span class="user_id"><%username%></span> + <span class="rating"><%pretty_rating%></span> + <span class="delta"><%time.delta%></span> + </p> + <p class="comment_text comment"><%text%></p> + <p class="comment_opts comment"> + <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a> + <a href="#" class="close_reply" id="cr<%id%>">reply ▿</a> + <a href="#" id="sp<%id%>" class="show_proposal"> + proposal ▹ + </a> + <a href="#" id="hp<%id%>" class="hide_proposal"> + proposal ▿ + </a> + <a href="#" id="dc<%id%>" class="delete_comment hidden"> + delete + </a> + <span id="cm<%id%>" class="moderation hidden"> + <a href="#" id="ac<%id%>" class="accept_comment">accept</a> + <a href="#" id="rc<%id%>" class="reject_comment">reject</a> + </span> + </p> + <pre class="proposal" id="pr<%id%>"> + <#proposal_diff#> + </pre> + <ul class="children" id="cl<%id%>"></ul> </div> <div class="clearleft"></div> </div> @@ -70,25 +70,25 @@ <a id="comment_close" href="#">x</a> <h1>Comments</h1> <form method="post" id="comment_form" action="/docs/add_comment"> - <textarea name="comment" cols="80"></textarea> - <p class="propose_button"> - <a href="#" class="show_propose_change"> - Propose a change ▹ - </a> - <a href="#" class="hide_propose_change"> - Propose a change ▿ - </a> - </p> - <textarea name="proposal" cols="80" spellcheck="false"></textarea> - <input type="submit" value="add comment" id="comment_button" /> - <input type="hidden" name="node" /> - <input type="hidden" name="parent" value="" /> - <p class="sort_options"> - Sort by: - <a href="#" class="sort_option" id="rating">top</a> - <a href="#" class="sort_option" id="ascage">newest</a> - <a href="#" class="sort_option" id="age">oldest</a> - </p> + <textarea name="comment" cols="80"></textarea> + <p class="propose_button"> + <a href="#" class="show_propose_change"> + Propose a change ▹ + </a> + <a href="#" class="hide_propose_change"> + Propose a change ▿ + </a> + </p> + <textarea name="proposal" cols="80" spellcheck="false"></textarea> + <input type="submit" value="add comment" id="comment_button" /> + <input type="hidden" name="node" /> + <input type="hidden" name="parent" value="" /> + <p class="sort_options"> + Sort by: + <a href="#" class="sort_option" id="rating">top</a> + <a href="#" class="sort_option" id="ascage">newest</a> + <a href="#" class="sort_option" id="age">oldest</a> + </p> </form> <h3 id="comment_notification">loading comments... <img src="<%loadingImage%>" alt="" /></h3> <ul id="comment_ul"></ul> From 134010e35734b986aa442a9e5047e5811dd1afa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 13 Aug 2010 11:44:09 +0200 Subject: [PATCH 297/744] Put literal_blocks under versioning --- sphinx/builders/websupport.py | 7 +++---- sphinx/util/websupport.py | 10 ++++++++++ sphinx/writers/websupport.py | 6 +++--- 3 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 sphinx/util/websupport.py diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index e1bd80111..2e05da5be 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -22,13 +22,12 @@ from docutils.utils import Reporter from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile from sphinx.util.jsonimpl import dumps as dump_json +from sphinx.util.websupport import is_commentable from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.writers.websupport import WebSupportTranslator from sphinx.environment import WarningStream from sphinx.versioning import add_uids, merge_doctrees -def is_paragraph(node): - return node.__class__.__name__ == 'paragraph' class WebSupportBuilder(StandaloneHTMLBuilder): """ @@ -66,9 +65,9 @@ class WebSupportBuilder(StandaloneHTMLBuilder): old_doctree = self.get_old_doctree(docname) if old_doctree: - list(merge_doctrees(old_doctree, doctree, is_paragraph)) + list(merge_doctrees(old_doctree, doctree, is_commentable)) else: - list(add_uids(doctree, is_paragraph)) + list(add_uids(doctree, is_commentable)) self.cur_docname = docname self.secnumbers = self.env.toc_secnumbers.get(docname, {}) diff --git a/sphinx/util/websupport.py b/sphinx/util/websupport.py new file mode 100644 index 000000000..f99f4d31d --- /dev/null +++ b/sphinx/util/websupport.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.websupport + ~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +def is_commentable(node): + return node.__class__.__name__ in ('paragraph', 'literal_block') diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 84af925ed..fbd3c1ef5 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -10,12 +10,12 @@ """ from sphinx.writers.html import HTMLTranslator +from sphinx.util.websupport import is_commentable class WebSupportTranslator(HTMLTranslator): """ Our custom HTML translator. """ - commentable_nodes = ['paragraph', 'literal_block'] def __init__(self, builder, *args, **kwargs): HTMLTranslator.__init__(self, builder, *args, **kwargs) @@ -26,13 +26,13 @@ class WebSupportTranslator(HTMLTranslator): self.cur_node = None def dispatch_visit(self, node): - if node.__class__.__name__ in self.commentable_nodes: + if is_commentable(node): self.handle_visit_commentable(node) HTMLTranslator.dispatch_visit(self, node) def dispatch_departure(self, node): HTMLTranslator.dispatch_departure(self, node) - if node.__class__.__name__ in self.commentable_nodes: + if is_commentable(node): self.handle_depart_commentable(node) def handle_visit_commentable(self, node): From 1509c0d3d353c72591f2ff5997c00f98d1b71555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 13 Aug 2010 15:10:48 +0200 Subject: [PATCH 298/744] Use a more consistent style in the websupport js --- sphinx/themes/basic/static/websupport.js | 409 ++++++++++++----------- 1 file changed, 205 insertions(+), 204 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 80c6a9a5b..863a57a5a 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -1,19 +1,19 @@ (function($) { $.fn.autogrow = function(){ return this.each(function(){ - var textarea = this; + var textarea = this; - $.fn.autogrow.resize(textarea); + $.fn.autogrow.resize(textarea); - $(textarea) - .focus(function() { - textarea.interval = setInterval(function() { - $.fn.autogrow.resize(textarea); - }, 500); - }) - .blur(function() { - clearInterval(textarea.interval); - }); + $(textarea) + .focus(function() { + textarea.interval = setInterval(function() { + $.fn.autogrow.resize(textarea); + }, 500); + }) + .blur(function() { + clearInterval(textarea.interval); + }); }); }; @@ -31,7 +31,7 @@ })(jQuery); (function($) { - var commentListEmpty, popup, comp, commentTemplate, replyTemplate; + var commentListEmpty, popup, comp, commentTemplate, replyTemplate; function init() { initTemplates(); @@ -98,9 +98,9 @@ $.get(templateURL, function(data) { var templates = $(data); function loadTemplate(id) { - var html = templates.find('#' + id).html(); - html = html.replace(/(<)|(%3C)/g, "<"); - html = html.replace(/(>)|(%3E)/g, ">"); + var html = templates.find('#' + id).html(); + html = html.replace(/(<)|(%3C)/g, "<"); + html = html.replace(/(>)|(%3E)/g, ">"); return html; }; // Create our popup div, the same div is recycled each time comments @@ -117,110 +117,109 @@ }); }; - /** - * Create a comp function. If the user has preferences stored in - * the sortBy cookie, use those, otherwise use the default. - */ + /* + Create a comp function. If the user has preferences stored in + the sortBy cookie, use those, otherwise use the default. + */ function initComparator() { var by = 'rating'; // Default to sort by rating. // If the sortBy cookie is set, use that instead. if (document.cookie.length > 0) { var start = document.cookie.indexOf('sortBy='); if (start != -1) { - start = start + 7; - var end = document.cookie.indexOf(";", start); - if (end == -1) - end = document.cookie.length; - by = unescape(document.cookie.substring(start, end)); - } + start = start + 7; + var end = document.cookie.indexOf(";", start); + if (end == -1) + end = document.cookie.length; + by = unescape(document.cookie.substring(start, end)); + } } setComparator(by); }; - /** - * Show the comments popup window. - */ + /* + Show the comments popup window. + */ function show(nodeId) { var id = nodeId.substring(1); // Reset the main comment form, and set the value of the parent input. $('form#comment_form') .find('textarea,input') - .removeAttr('disabled').end() + .removeAttr('disabled').end() .find('input[name="node"]') - .val(id).end() + .val(id).end() .find('textarea[name="proposal"]') - .val('') - .hide(); + .val('') + .hide(); // Position the popup and show it. var clientWidth = document.documentElement.clientWidth; var popupWidth = $('div.popup_comment').width(); $('div.popup_comment') .css({ - 'top': 100+$(window).scrollTop(), - 'left': clientWidth/2-popupWidth/2, - 'position': 'absolute' + 'top': 100+$(window).scrollTop(), + 'left': clientWidth/2-popupWidth/2, + 'position': 'absolute' }) .fadeIn('fast', function() { - getComments(id); + getComments(id); }); }; - /** - * Hide the comments popup window. - */ + /* + Hide the comments popup window. + */ function hide() { $('div.popup_comment').fadeOut('fast', function() { $('ul#comment_ul').empty(); $('h3#comment_notification').show(); $('form#comment_form').find('textarea') - .val('').end() - .find('textarea, input') - .removeAttr('disabled'); + .val('').end() + .find('textarea, input') + .removeAttr('disabled'); }); }; - /** - * Perform an ajax request to get comments for a node - * and insert the comments into the comments tree. - */ + /* + Perform an ajax request to get comments for a node + and insert the comments into the comments tree. + */ function getComments(id) { $.ajax({ - type: 'GET', - url: opts.getCommentsURL, - data: {node: id}, - success: function(data, textStatus, request) { - var ul = $('ul#comment_ul').hide(); - $('form#comment_form') - .find('textarea[name="proposal"]') - .data('source', data.source); + type: 'GET', + url: opts.getCommentsURL, + data: {node: id}, + success: function(data, textStatus, request) { + var ul = $('ul#comment_ul').hide(); + $('form#comment_form') + .find('textarea[name="proposal"]') + .data('source', data.source); - if (data.comments.length == 0) { - ul.html('<li>No comments yet.</li>'); - commentListEmpty = true; - var speed = 100; - } - else { - // If there are comments, sort them and put them in the list. - var comments = sortComments(data.comments); - var speed = data.comments.length * 100; - appendComments(comments, ul); - commentListEmpty = false; - } - $('h3#comment_notification').slideUp(speed+200); - ul.slideDown(speed); - }, - error: function(request, textStatus, error) { - showError('Oops, there was a problem retrieving the comments.'); - }, - dataType: 'json' + if (data.comments.length == 0) { + ul.html('<li>No comments yet.</li>'); + commentListEmpty = true; + var speed = 100; + } else { + // If there are comments, sort them and put them in the list. + var comments = sortComments(data.comments); + var speed = data.comments.length * 100; + appendComments(comments, ul); + commentListEmpty = false; + } + $('h3#comment_notification').slideUp(speed+200); + ul.slideDown(speed); + }, + error: function(request, textStatus, error) { + showError('Oops, there was a problem retrieving the comments.'); + }, + dataType: 'json' }); }; - /** - * Add a comment via ajax and insert the comment into the comment tree. - */ + /* + Add a comment via ajax and insert the comment into the comment tree. + */ function addComment(form) { // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); @@ -231,36 +230,38 @@ type: "POST", url: opts.addCommentURL, dataType: 'json', - data: {node: node_id, - parent: form.find('input[name="parent"]').val(), - text: form.find('textarea[name="comment"]').val(), - proposal: form.find('textarea[name="proposal"]').val()}, + data: { + node: node_id, + parent: form.find('input[name="parent"]').val(), + text: form.find('textarea[name="comment"]').val(), + proposal: form.find('textarea[name="proposal"]').val() + }, success: function(data, textStatus, error) { - // Reset the form. - if (node_id) { - hideProposeChange(node_id); - } - form.find('textarea') - .val('') - .add(form.find('input')) - .removeAttr('disabled'); - if (commentListEmpty) { - $('ul#comment_ul').empty(); - commentListEmpty = false; - } - insertComment(data.comment); + // Reset the form. + if (node_id) { + hideProposeChange(node_id); + } + form.find('textarea') + .val('') + .add(form.find('input')) + .removeAttr('disabled'); + if (commentListEmpty) { + $('ul#comment_ul').empty(); + commentListEmpty = false; + } + insertComment(data.comment); }, error: function(request, textStatus, error) { - form.find('textarea,input').removeAttr('disabled'); - showError('Oops, there was a problem adding the comment.'); + form.find('textarea,input').removeAttr('disabled'); + showError('Oops, there was a problem adding the comment.'); } }); }; - /** - * Recursively append comments to the main comment list and children - * lists, creating the comment tree. - */ + /* + Recursively append comments to the main comment list and children + lists, creating the comment tree. + */ function appendComments(comments, ul) { $.each(comments, function() { var div = createCommentDiv(this); @@ -272,10 +273,10 @@ }); }; - /** - * After adding a new comment, it must be inserted in the correct - * location in the comment tree. - */ + /* + After adding a new comment, it must be inserted in the correct + location in the comment tree. + */ function insertComment(comment) { var div = createCommentDiv(comment); @@ -286,8 +287,7 @@ if (comment.node != null) { var ul = $('ul#comment_ul'); var siblings = getChildren(ul); - } - else { + } else { var ul = $('#cl' + comment.parent); var siblings = getChildren(ul); } @@ -298,11 +298,11 @@ // Determine where in the parents children list to insert this comment. for(i=0; i < siblings.length; i++) { if (comp(comment, siblings[i]) <= 0) { - $('#cd' + siblings[i].id) - .parent() - .before(li.html(div)); - li.slideDown('fast'); - return; + $('#cd' + siblings[i].id) + .parent() + .before(li.html(div)); + li.slideDown('fast'); + return; } } @@ -318,10 +318,10 @@ url: opts.acceptCommentURL, data: {id: id}, success: function(data, textStatus, request) { - $('#cm' + id).fadeOut('fast'); + $('#cm' + id).fadeOut('fast'); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem accepting the comment."); + showError("Oops, there was a problem accepting the comment."); }, }); }; @@ -332,13 +332,13 @@ url: opts.rejectCommentURL, data: {id: id}, success: function(data, textStatus, request) { - var div = $('#cd' + id); - div.slideUp('fast', function() { - div.remove(); - }); + var div = $('#cd' + id); + div.slideUp('fast', function() { + div.remove(); + }); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem rejecting the comment."); + showError("Oops, there was a problem rejecting the comment."); }, }); }; @@ -349,22 +349,22 @@ url: opts.deleteCommentURL, data: {id: id}, success: function(data, textStatus, request) { - var div = $('#cd' + id); - div - .find('span.user_id:first') - .text('[deleted]').end() - .find('p.comment_text:first') - .text('[deleted]').end() - .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + - ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) - .remove(); - var comment = div.data('comment'); - comment.username = '[deleted]'; - comment.text = '[deleted]'; - div.data('comment', comment); + var div = $('#cd' + id); + div + .find('span.user_id:first') + .text('[deleted]').end() + .find('p.comment_text:first') + .text('[deleted]').end() + .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + + ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) + .remove(); + var comment = div.data('comment'); + comment.username = '[deleted]'; + comment.text = '[deleted]'; + div.data('comment', comment); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem deleting the comment."); + showError("Oops, there was a problem deleting the comment."); }, }); }; @@ -398,25 +398,25 @@ textarea.slideUp('fast'); }; - /** - * Handle when the user clicks on a sort by link. - */ + /* + Handle when the user clicks on a sort by link. + */ function handleReSort(link) { setComparator(link.attr('id')); - // Save/update the sortBy cookie. + // Save/update the sortBy cookie. var expiration = new Date(); expiration.setDate(expiration.getDate() + 365); document.cookie= 'sortBy=' + escape(link.attr('id')) + - ';expires=' + expiration.toUTCString(); + ';expires=' + expiration.toUTCString(); var comments = getChildren($('ul#comment_ul'), true); comments = sortComments(comments); appendComments(comments, $('ul#comment_ul').empty()); }; - /** - * Function to process a vote when a user clicks an arrow. - */ + /* + Function to process a vote when a user clicks an arrow. + */ function handleVote(link) { if (!opts.voting) { showError("You'll need to login to vote."); @@ -426,11 +426,11 @@ var id = link.attr('id'); // If it is an unvote, the new vote value is 0, // Otherwise it's 1 for an upvote, or -1 for a downvote. - if (id.charAt(1) == 'u') + if (id.charAt(1) == 'u') { var value = 0; - else + } else { var value = id.charAt(0) == 'u' ? 1 : -1; - + } // The data to be sent to the server. var d = { comment_id: id.substring(2), @@ -450,9 +450,9 @@ // already been pressed, unpress it. if ((d.value != 0) && (data.vote == d.value*-1)) { $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id) - .hide(); + .hide(); $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id) - .show(); + .show(); } // Update the comments rating in the local data. @@ -470,14 +470,14 @@ url: opts.processVoteURL, data: d, error: function(request, textStatus, error) { - showError("Oops, there was a problem casting that vote."); + showError("Oops, there was a problem casting that vote."); } }); }; - /** - * Open a reply form used to reply to an existing comment. - */ + /* + Open a reply form used to reply to an existing comment. + */ function openReply(id) { // Swap out the reply link for the hide link $('#rl' + id).hide(); @@ -489,17 +489,17 @@ .prepend(div) // Setup the submit handler for the reply form. .find('#rf' + id) - .submit(function(event) { - event.preventDefault(); - addComment($('#rf' + id)); - closeReply(id); - }); + .submit(function(event) { + event.preventDefault(); + addComment($('#rf' + id)); + closeReply(id); + }); div.slideDown('fast'); }; - /** - * Close the reply form opened with openReply. - */ + /* + Close the reply form opened with openReply. + */ function closeReply(id) { // Remove the reply div from the DOM. $('#rd' + id).slideUp('fast', function() { @@ -511,9 +511,9 @@ $('#rl' + id).show(); }; - /** - * Recursively sort a tree of comments using the comp comparator. - */ + /* + Recursively sort a tree of comments using the comp comparator. + */ function sortComments(comments) { comments.sort(comp); $.each(comments, function() { @@ -522,51 +522,50 @@ return comments; }; - /** - * Set comp, which is a comparator function used for sorting and - * inserting comments into the list. - */ + /* + Set comp, which is a comparator function used for sorting and + inserting comments into the list. + */ function setComparator(by) { // If the first three letters are "asc", sort in ascending order // and remove the prefix. if (by.substring(0,3) == 'asc') { var i = by.substring(3); comp = function(a, b) { return a[i] - b[i]; } - } - // Otherwise sort in descending order. - else + } else { + // Otherwise sort in descending order. comp = function(a, b) { return b[by] - a[by]; } + } // Reset link styles and format the selected sort option. $('a.sel').attr('href', '#').removeClass('sel'); $('#' + by).removeAttr('href').addClass('sel'); }; - /** - * Get the children comments from a ul. If recursive is true, - * recursively include childrens' children. - */ + /* + Get the children comments from a ul. If recursive is true, + recursively include childrens' children. + */ function getChildren(ul, recursive) { var children = []; ul.children().children("[id^='cd']") .each(function() { - var comment = $(this).data('comment'); - if (recursive) { - comment.children = - getChildren($(this).find('#cl' + comment.id), true); - } - children.push(comment); + var comment = $(this).data('comment'); + if (recursive) { + comment.children = getChildren($(this).find('#cl' + comment.id), true); + } + children.push(comment); }); return children; }; - /** - * Create a div to display a comment in. - */ + /* + Create a div to display a comment in. + */ function createCommentDiv(comment) { // Prettify the comment rating. comment.pretty_rating = comment.rating + ' point' + - (comment.rating == 1 ? '' : 's'); + (comment.rating == 1 ? '' : 's'); // Create a div for this comment. var context = $.extend({}, opts, comment); var div = $(renderTemplate(commentTemplate, context)); @@ -581,30 +580,30 @@ if (comment.text != '[deleted]') { div.find('a.reply').show(); if (comment.proposal_diff) { - div.find('#sp' + comment.id).show(); + div.find('#sp' + comment.id).show(); } if (opts.moderator && !comment.displayed) { - div.find('#cm' + comment.id).show(); + div.find('#cm' + comment.id).show(); } if (opts.moderator || (opts.username == comment.username)) { - div.find('#dc' + comment.id).show(); + div.find('#dc' + comment.id).show(); } } return div; } - /** - * A simple template renderer. Placeholders such as <%id%> are replaced - * by context['id']. Items are always escaped. - */ + /* + A simple template renderer. Placeholders such as <%id%> are replaced + by context['id']. Items are always escaped. + */ function renderTemplate(template, context) { var esc = $('<span></span>'); function handle(ph, escape) { var cur = context; $.each(ph.split('.'), function() { - cur = cur[this]; + cur = cur[this]; }); return escape ? esc.text(cur || "").html() : cur; } @@ -617,16 +616,17 @@ function showError(message) { $('<div class="popup_error">' + '<h1>' + message + '</h1>' + - '</div>') - .appendTo('body') - .fadeIn("slow") - .delay(2000) - .fadeOut("slow"); + '</div>' + ) + .appendTo('body') + .fadeIn("slow") + .delay(2000) + .fadeOut("slow"); }; - /** - * Add a link the user uses to open the comments popup. - */ + /* + Add a link the user uses to open the comments popup. + */ $.fn.comment = function() { return this.each(function() { var id = $(this).attr('id').substring(1); @@ -634,13 +634,14 @@ var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; $(this).append( - $('<a href="#" class="sphinx_comment"></a>') - .html('<img src="' + image + '" alt="comment" />') - .attr('title', title) - .click(function(event) { - event.preventDefault(); - show($(this).parent().attr('id')); - })); + $('<a href="#" class="sphinx_comment"></a>') + .html('<img src="' + image + '" alt="comment" />') + .attr('title', title) + .click(function(event) { + event.preventDefault(); + show($(this).parent().attr('id')); + }) + ); }); }; @@ -680,4 +681,4 @@ $(document).ready(function() { result.highlightText(this.toLowerCase(), 'highlighted'); }); }); -}); \ No newline at end of file +}); From 1fa4c78c38a0ed5cc983edfa1132cd65101614ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 13 Aug 2010 16:26:46 +0200 Subject: [PATCH 299/744] Put spaces around operators --- sphinx/themes/basic/static/websupport.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 863a57a5a..74e28c19a 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -23,9 +23,9 @@ var columns = textarea.cols; var lineCount = 0; $.each(lines, function() { - lineCount += Math.ceil(this.length/columns) || 1; + lineCount += Math.ceil(this.length / columns) || 1; }); - var height = lineHeight*(lineCount+1); + var height = lineHeight * (lineCount + 1); $(textarea).css('height', height); }; })(jQuery); @@ -158,8 +158,8 @@ var popupWidth = $('div.popup_comment').width(); $('div.popup_comment') .css({ - 'top': 100+$(window).scrollTop(), - 'left': clientWidth/2-popupWidth/2, + 'top': 100 + $(window).scrollTop(), + 'left': clientWidth / 2 - popupWidth / 2, 'position': 'absolute' }) .fadeIn('fast', function() { @@ -207,7 +207,7 @@ appendComments(comments, ul); commentListEmpty = false; } - $('h3#comment_notification').slideUp(speed+200); + $('h3#comment_notification').slideUp(speed + 200); ul.slideDown(speed); }, error: function(request, textStatus, error) { @@ -448,11 +448,9 @@ // If this is not an unvote, and the other vote arrow has // already been pressed, unpress it. - if ((d.value != 0) && (data.vote == d.value*-1)) { - $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id) - .hide(); - $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id) - .show(); + if ((d.value != 0) && (data.vote == d.value * -1)) { + $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide(); + $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show(); } // Update the comments rating in the local data. From bb0f113740d2c12daa3c47c7c37454945f914b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 13 Aug 2010 17:07:15 +0200 Subject: [PATCH 300/744] Use document.createElement which is faster than parsing the html to create an element --- sphinx/themes/basic/static/websupport.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 74e28c19a..8405374d0 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -265,7 +265,7 @@ function appendComments(comments, ul) { $.each(comments, function() { var div = createCommentDiv(this); - ul.append($('<li></li>').html(div)); + ul.append($(document.createElement('li')).html(div)); appendComments(this.children, div.find('ul.children')); // To avoid stagnating data, don't store the comments children in data. this.children = null; @@ -292,7 +292,7 @@ var siblings = getChildren(ul); } - var li = $('<li></li>'); + var li = $(document.createElement('li')); li.hide(); // Determine where in the parents children list to insert this comment. @@ -596,7 +596,7 @@ by context['id']. Items are always escaped. */ function renderTemplate(template, context) { - var esc = $('<span></span>'); + var esc = $(document.createElement('div')); function handle(ph, escape) { var cur = context; From 030833e8c5dced1840eac5094d67fe313d92ef7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 13 Aug 2010 17:10:22 +0200 Subject: [PATCH 301/744] Implement showError more efficiently --- sphinx/themes/basic/static/websupport.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 8405374d0..079497a06 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -612,10 +612,8 @@ }; function showError(message) { - $('<div class="popup_error">' + - '<h1>' + message + '</h1>' + - '</div>' - ) + $(document.createElement('div').attr({class: 'popup_error'})) + .append($(document.createElement('h1').val(message))) .appendTo('body') .fadeIn("slow") .delay(2000) From 2742399e800c2e677b15099b2adc65366899f9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 13 Aug 2010 17:17:40 +0200 Subject: [PATCH 302/744] Implement $.fn.comment more efficiently --- sphinx/themes/basic/static/websupport.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 079497a06..cf0edf9b5 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -630,9 +630,12 @@ var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; $(this).append( - $('<a href="#" class="sphinx_comment"></a>') - .html('<img src="' + image + '" alt="comment" />') - .attr('title', title) + $(document.createElement('a')).attr({href: '#', class: 'spinx_comment'}) + .append($(document.createElement('img')).attr({ + src: image, + alt: 'comment', + title: title + })) .click(function(event) { event.preventDefault(); show($(this).parent().attr('id')); From 5be29e0894ec4e2ca3cf1894ae9aaa94a612745b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 13 Aug 2010 17:28:37 +0200 Subject: [PATCH 303/744] Fixed showError --- sphinx/themes/basic/static/websupport.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index cf0edf9b5..276d550b4 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -612,8 +612,8 @@ }; function showError(message) { - $(document.createElement('div').attr({class: 'popup_error'})) - .append($(document.createElement('h1').val(message))) + $(document.createElement('div')).attr({class: 'popup_error'}) + .append($(document.createElement('h1')).text(message)) .appendTo('body') .fadeIn("slow") .delay(2000) From 3717927c83794ebfdd08832b8c53946754a1193c Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 13 Aug 2010 12:15:12 -0500 Subject: [PATCH 304/744] fixed bug in CombinedHtmlDiffer that clipped the last line. --- sphinx/themes/basic/static/websupport-templates.html | 2 +- sphinx/websupport/storage/differ.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/themes/basic/static/websupport-templates.html b/sphinx/themes/basic/static/websupport-templates.html index 1ea58ab24..b28ec9644 100644 --- a/sphinx/themes/basic/static/websupport-templates.html +++ b/sphinx/themes/basic/static/websupport-templates.html @@ -57,7 +57,7 @@ </span> </p> <pre class="proposal" id="pr<%id%>"> - <#proposal_diff#> +<#proposal_diff#> </pre> <ul class="children" id="cl<%id%>"></ul> </div> diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py index f0b6a8ea5..8d6c4a497 100644 --- a/sphinx/websupport/storage/differ.py +++ b/sphinx/websupport/storage/differ.py @@ -40,9 +40,9 @@ class CombinedHtmlDiff(object): try: next = diff.pop(0) except IndexError: - self._handle_line(line) + html.append(self._handle_line(line)) break - return ''.join(html) + return ''.join(html).rstrip() def _handle_line(self, line, next=None): """Handle an individual line in a diff.""" From 642cfd08aadb36914573a0cf53a480c493e1cd57 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 13 Aug 2010 14:32:30 -0500 Subject: [PATCH 305/744] readd modal focuser --- sphinx/themes/basic/static/websupport-templates.html | 1 + sphinx/themes/basic/static/websupport.js | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sphinx/themes/basic/static/websupport-templates.html b/sphinx/themes/basic/static/websupport-templates.html index b28ec9644..22bbaa413 100644 --- a/sphinx/themes/basic/static/websupport-templates.html +++ b/sphinx/themes/basic/static/websupport-templates.html @@ -94,4 +94,5 @@ <ul id="comment_ul"></ul> </div> </div> + <div id="focuser"></div> </div> diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 276d550b4..0d8fab41c 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -110,9 +110,11 @@ popup = $(renderTemplate(popupTemplate, opts)); popup.find('textarea').autogrow(); - commentTemplate = loadTemplate('#comment_template'); - replyTemplate = loadTemplate('#reply_template'); + commentTemplate = loadTemplate('comment_template'); + replyTemplate = loadTemplate('reply_template'); + var focuser = templates.find('#focuser'); $('body').append(popup); + $('body').append(focuser); initEvents(); }); }; @@ -156,6 +158,7 @@ // Position the popup and show it. var clientWidth = document.documentElement.clientWidth; var popupWidth = $('div.popup_comment').width(); + $('div#focuser').fadeIn('fast'); $('div.popup_comment') .css({ 'top': 100 + $(window).scrollTop(), @@ -171,6 +174,7 @@ Hide the comments popup window. */ function hide() { + $('div#focuser').fadeOut('fast'); $('div.popup_comment').fadeOut('fast', function() { $('ul#comment_ul').empty(); $('h3#comment_notification').show(); From d62d568a003e8079fc2d47a5d2e609b2c6b88f4f Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Fri, 13 Aug 2010 16:27:02 -0500 Subject: [PATCH 306/744] moved templates back into js, getting the html template file trys to load the images from template tags resulting in a bunch of 404's. This is a project for after pencils down date since it functions fine as is --- .../basic/static/websupport-templates.html | 98 ------------- sphinx/themes/basic/static/websupport.js | 132 ++++++++++++++---- sphinx/websupport/__init__.py | 3 +- 3 files changed, 108 insertions(+), 125 deletions(-) delete mode 100644 sphinx/themes/basic/static/websupport-templates.html diff --git a/sphinx/themes/basic/static/websupport-templates.html b/sphinx/themes/basic/static/websupport-templates.html deleted file mode 100644 index 22bbaa413..000000000 --- a/sphinx/themes/basic/static/websupport-templates.html +++ /dev/null @@ -1,98 +0,0 @@ -<div id="templates"> - <div id="reply_template"> - <Li> - <div class="reply_div" id="rd<%id%>"> - <form id="rf<%id%>"> - <textarea name="comment" cols="80"></textarea> - <input type="submit" value="add reply" /> - <input type="hidden" name="parent" value="<%id%>" /> - <input type="hidden" name="node" value="" /> - </form> - </div> - </li> - </div> - - <div id="comment_template"> - <div id="cd<%id%>" class="spxcdiv"> - <div class="vote"> - <div class="arrow"> - <a href="#" id="uv<%id%>" class="vote"> - <img src="<%upArrow%>" /> - </a> - <a href="#" id="uu<%id%>" class="un vote"> - <img src="<%upArrowPressed%>" /> - </a> - </div> - <div class="arrow"> - <a href="#" id="dv<%id%>" class="vote"> - <img src="<%downArrow%>" id="da<%id%>" /> - </a> - <a href="#" id="du<%id%>" class="un vote"> - <img src="<%downArrowPressed%>" /> - </a> - </div> - </div> - <div class="comment_content"> - <p class="tagline comment"> - <span class="user_id"><%username%></span> - <span class="rating"><%pretty_rating%></span> - <span class="delta"><%time.delta%></span> - </p> - <p class="comment_text comment"><%text%></p> - <p class="comment_opts comment"> - <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a> - <a href="#" class="close_reply" id="cr<%id%>">reply ▿</a> - <a href="#" id="sp<%id%>" class="show_proposal"> - proposal ▹ - </a> - <a href="#" id="hp<%id%>" class="hide_proposal"> - proposal ▿ - </a> - <a href="#" id="dc<%id%>" class="delete_comment hidden"> - delete - </a> - <span id="cm<%id%>" class="moderation hidden"> - <a href="#" id="ac<%id%>" class="accept_comment">accept</a> - <a href="#" id="rc<%id%>" class="reject_comment">reject</a> - </span> - </p> - <pre class="proposal" id="pr<%id%>"> -<#proposal_diff#> - </pre> - <ul class="children" id="cl<%id%>"></ul> - </div> - <div class="clearleft"></div> - </div> - </div> - - <div id="popup_template"> - <div class="popup_comment"> - <a id="comment_close" href="#">x</a> - <h1>Comments</h1> - <form method="post" id="comment_form" action="/docs/add_comment"> - <textarea name="comment" cols="80"></textarea> - <p class="propose_button"> - <a href="#" class="show_propose_change"> - Propose a change ▹ - </a> - <a href="#" class="hide_propose_change"> - Propose a change ▿ - </a> - </p> - <textarea name="proposal" cols="80" spellcheck="false"></textarea> - <input type="submit" value="add comment" id="comment_button" /> - <input type="hidden" name="node" /> - <input type="hidden" name="parent" value="" /> - <p class="sort_options"> - Sort by: - <a href="#" class="sort_option" id="rating">top</a> - <a href="#" class="sort_option" id="ascage">newest</a> - <a href="#" class="sort_option" id="age">oldest</a> - </p> - </form> - <h3 id="comment_notification">loading comments... <img src="<%loadingImage%>" alt="" /></h3> - <ul id="comment_ul"></ul> - </div> - </div> - <div id="focuser"></div> -</div> diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 0d8fab41c..a0aa79170 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -31,10 +31,11 @@ })(jQuery); (function($) { - var commentListEmpty, popup, comp, commentTemplate, replyTemplate; + var commentListEmpty, popup, comp; function init() { initTemplates(); + initEvents(); initComparator(); }; @@ -94,29 +95,12 @@ }; function initTemplates() { - var templateURL = opts.staticDir + '/_static/websupport-templates.html'; - $.get(templateURL, function(data) { - var templates = $(data); - function loadTemplate(id) { - var html = templates.find('#' + id).html(); - html = html.replace(/(<)|(%3C)/g, "<"); - html = html.replace(/(>)|(%3E)/g, ">"); - return html; - }; - // Create our popup div, the same div is recycled each time comments - // are displayed. - // Setup autogrow on the textareas - var popupTemplate = loadTemplate('popup_template'); - popup = $(renderTemplate(popupTemplate, opts)); - popup.find('textarea').autogrow(); - - commentTemplate = loadTemplate('comment_template'); - replyTemplate = loadTemplate('reply_template'); - var focuser = templates.find('#focuser'); - $('body').append(popup); - $('body').append(focuser); - initEvents(); - }); + // Create our popup div, the same div is recycled each time comments + // are displayed. + popup = $(renderTemplate(popupTemplate, opts)); + // Setup autogrow on the textareas + popup.find('textarea').autogrow(); + $('body').append(popup); }; /* @@ -662,11 +646,109 @@ downArrow: '/static/_static/down.png', upArrowPressed: '/static/_static/up-pressed.png', downArrowPressed: '/static/_static/down-pressed.png', - staticDir: '/static', voting: false, moderator: false }, COMMENT_OPTIONS); + var replyTemplate = '\ + <li>\ + <div class="reply_div" id="rd<%id%>">\ + <form id="rf<%id%>">\ + <textarea name="comment" cols="80"></textarea>\ + <input type="submit" value="add reply" />\ + <input type="hidden" name="parent" value="<%id%>" />\ + <input type="hidden" name="node" value="" />\ + </form>\ + </div>\ + </li>'; + + var commentTemplate = '\ + <div id="cd<%id%>" class="spxcdiv">\ + <div class="vote">\ + <div class="arrow">\ + <a href="#" id="uv<%id%>" class="vote">\ + <img src="<%upArrow%>" />\ + </a>\ + <a href="#" id="uu<%id%>" class="un vote">\ + <img src="<%upArrowPressed%>" />\ + </a>\ + </div>\ + <div class="arrow">\ + <a href="#" id="dv<%id%>" class="vote">\ + <img src="<%downArrow%>" id="da<%id%>" />\ + </a>\ + <a href="#" id="du<%id%>" class="un vote">\ + <img src="<%downArrowPressed%>" />\ + </a>\ + </div>\ + </div>\ + <div class="comment_content">\ + <p class="tagline comment">\ + <span class="user_id"><%username%></span>\ + <span class="rating"><%pretty_rating%></span>\ + <span class="delta"><%time.delta%></span>\ + </p>\ + <p class="comment_text comment"><%text%></p>\ + <p class="comment_opts comment">\ + <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\ + <a href="#" class="close_reply" id="cr<%id%>">reply ▿</a>\ + <a href="#" id="sp<%id%>" class="show_proposal">\ + proposal ▹\ + </a>\ + <a href="#" id="hp<%id%>" class="hide_proposal">\ + proposal ▿\ + </a>\ + <a href="#" id="dc<%id%>" class="delete_comment hidden">\ + delete\ + </a>\ + <span id="cm<%id%>" class="moderation hidden">\ + <a href="#" id="ac<%id%>" class="accept_comment">accept</a>\ + <a href="#" id="rc<%id%>" class="reject_comment">reject</a>\ + </span>\ + </p>\ + <pre class="proposal" id="pr<%id%>">\ +<#proposal_diff#>\ + </pre>\ + <ul class="children" id="cl<%id%>"></ul>\ + </div>\ + <div class="clearleft"></div>\ + </div>\ + </div>'; + + var popupTemplate = '\ + <div id="popup_template">\ + <div class="popup_comment">\ + <a id="comment_close" href="#">x</a>\ + <h1>Comments</h1>\ + <form method="post" id="comment_form" action="/docs/add_comment">\ + <textarea name="comment" cols="80"></textarea>\ + <p class="propose_button">\ + <a href="#" class="show_propose_change">\ + Propose a change ▹\ + </a>\ + <a href="#" class="hide_propose_change">\ + Propose a change ▿\ + </a>\ + </p>\ + <textarea name="proposal" cols="80" spellcheck="false"></textarea>\ + <input type="submit" value="add comment" id="comment_button" />\ + <input type="hidden" name="node" />\ + <input type="hidden" name="parent" value="" />\ + <p class="sort_options">\ + Sort by:\ + <a href="#" class="sort_option" id="rating">top</a>\ + <a href="#" class="sort_option" id="ascage">newest</a>\ + <a href="#" class="sort_option" id="age">oldest</a>\ + </p>\ + </form>\ + <h3 id="comment_notification">loading comments... <img src="' + + opts.loadingImage + '" alt="" /></h3>\ + <ul id="comment_ul"></ul>\ + </div>\ + </div>\ + <div id="focuser"></div>'; + + $(document).ready(function() { init(); }); diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 939428a6c..cc065b7f7 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -376,8 +376,7 @@ class WebSupport(object): ('upArrow', 'up.png'), ('upArrowPressed', 'up-pressed.png'), ('downArrow', 'down.png'), - ('downArrowPressed', 'down-pressed.png'), - ('staticDir', '/' + self.staticdir) + ('downArrowPressed', 'down-pressed.png') ] for key, value in static_urls: self.base_comment_opts[key] = \ From 07db8c03d2fa7eef1cba868adc64a01a88557ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 14 Aug 2010 11:11:50 +0200 Subject: [PATCH 307/744] Added a test to make sure pickled doctrees still have their uids --- tests/test_versioning.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 77306580c..54a48f4ad 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -8,10 +8,14 @@ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import pickle + from util import * from docutils.statemachine import ViewList +from docutils.parsers.rst.directives.html import MetaBody +from sphinx import addnodes from sphinx.versioning import make_diff, add_uids, merge_doctrees def setup_module(): @@ -50,6 +54,19 @@ def is_paragraph(node): def test_add_uids(): assert len(original_uids) == 3 +def test_picklablility(): + # we have to modify the doctree so we can pickle it + copy = original.copy() + copy.reporter = None + copy.transformer = None + copy.settings.warning_stream = None + copy.settings.env = None + copy.settings.record_dependencies = None + for metanode in copy.traverse(MetaBody.meta): + metanode.__class__ = addnodes.meta + loaded = pickle.loads(pickle.dumps(copy, pickle.HIGHEST_PROTOCOL)) + assert all(getattr(n, 'uid', False) for n in loaded.traverse(is_paragraph)) + def test_modified(): modified = doctrees['versioning/modified'] new_nodes = list(merge_doctrees(original, modified, is_paragraph)) From 1f0b25be796c8d62a3607d4e6498b43ed1cf8481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 14 Aug 2010 11:17:38 +0200 Subject: [PATCH 308/744] Fix finish handler of the websupport builder --- sphinx/builders/websupport.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 16e3a82bd..0c92c646d 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -151,12 +151,15 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def handle_finish(self): StandaloneHTMLBuilder.handle_finish(self) - shutil.move(path.join(self.outdir, '_images'), - path.join(self.app.builddir, self.app.staticdir, - '_images')) - shutil.move(path.join(self.outdir, '_static'), - path.join(self.app.builddir, self.app.staticdir, - '_static')) + directories = ['_images', '_static'] + for directory in directories: + try: + shutil.move(path.join(self.outdir, directory), + path.join(self.app.builddir, self.app.staticdir, + directory)) + except IOError: + # in case any of these directories don't exist + pass for f in glob(path.join(self.doctreedir, '*.doctree.old')): os.remove(f) From 7e7134e029dbf1ba88cdc3cb4aade11a84fdd783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 14 Aug 2010 11:22:40 +0200 Subject: [PATCH 309/744] Use the highest protocol to pickle doctrees and use the constants provided by the pickle module --- sphinx/builders/websupport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 0c92c646d..e43c46dee 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -137,7 +137,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): ensuredir(path.dirname(outfilename)) f = open(outfilename, 'wb') try: - pickle.dump(doc_ctx, f, 2) + pickle.dump(doc_ctx, f, pickle.HIGHEST_PROTOCOL) finally: f.close() From 54f3323ac78582d11422293ecab3b5c2e5d3dd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 14 Aug 2010 12:16:40 +0200 Subject: [PATCH 310/744] Removed unnecessary newline --- sphinx/builders/websupport.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index e43c46dee..5f27b9074 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -163,7 +163,6 @@ class WebSupportBuilder(StandaloneHTMLBuilder): for f in glob(path.join(self.doctreedir, '*.doctree.old')): os.remove(f) - def dump_search_index(self): self.indexer.finish_indexing() From 2be3327209c62605b717980d5234a8c10dba4b3e Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 14 Aug 2010 14:31:20 +0200 Subject: [PATCH 311/744] Fix typo. --- sphinx/themes/basic/static/websupport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index a0aa79170..2cd70e9ba 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -618,7 +618,7 @@ var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; $(this).append( - $(document.createElement('a')).attr({href: '#', class: 'spinx_comment'}) + $(document.createElement('a')).attr({href: '#', class: 'sphinx_comment'}) .append($(document.createElement('img')).attr({ src: image, alt: 'comment', From 6f91352415b0880e1b09f5c87468785b41ab38f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 14 Aug 2010 14:43:54 +0200 Subject: [PATCH 312/744] Take doctrees in subdirectories into account --- sphinx/builders/websupport.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 5f27b9074..ac9dd7152 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -12,7 +12,6 @@ import cPickle as pickle from os import path from cgi import escape -from glob import glob import os import posixpath import shutil @@ -38,8 +37,11 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def init(self): StandaloneHTMLBuilder.init(self) - for f in glob(path.join(self.doctreedir, '*.doctree')): - copyfile(f, f + '.old') + for root, dirs, files in os.walk(self.doctreedir): + for fn in files: + fp = path.join(root, fn) + if fp.endswith('.doctree'): + copyfile(fp, fp + '.old') def init_translator_class(self): self.translator_class = WebSupportTranslator @@ -160,8 +162,11 @@ class WebSupportBuilder(StandaloneHTMLBuilder): except IOError: # in case any of these directories don't exist pass - for f in glob(path.join(self.doctreedir, '*.doctree.old')): - os.remove(f) + for root, dirs, files in os.walk(self.doctreedir): + for fn in files: + fp = path.join(root, fn) + if fp.endswith('.doctree.old'): + os.remove(fp) def dump_search_index(self): self.indexer.finish_indexing() From fae5b3c1339ef08fac98ca3ed0a52350350acd70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 14 Aug 2010 19:52:04 +0200 Subject: [PATCH 313/744] Fix doctest to work with Python 2.5 and lower --- tests/root/doctest.txt | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/tests/root/doctest.txt b/tests/root/doctest.txt index 6ac0b2863..ba9a72c52 100644 --- a/tests/root/doctest.txt +++ b/tests/root/doctest.txt @@ -50,23 +50,24 @@ Special directives .. testsetup:: * - from math import factorial + def squared(x): + return x * x .. doctest:: - >>> factorial(1) - 1 + >>> squared(2) + 4 .. testcode:: - print(factorial(1)) + print(squared(2)) .. testoutput:: - 1 + 4 - >>> factorial(1) - 1 + >>> squared(2) + 4 * options for testcode/testoutput blocks @@ -85,36 +86,38 @@ Special directives .. testsetup:: group1 - from math import trunc + def add(x, y): + return x + y - ``trunc`` is now known in "group1", but not in others. + + ``add`` is now known in "group1", but not in others. .. doctest:: group1 - >>> trunc(1.1) - 1 + >>> add(1, 1) + 2 .. doctest:: group2 - >>> trunc(1.1) + >>> add(1, 1) Traceback (most recent call last): ... - NameError: name 'trunc' is not defined + NameError: name 'add' is not defined Interleaving testcode/testoutput: .. testcode:: group1 - print(factorial(3)) + print(squared(3)) .. testcode:: group2 - print(factorial(4)) + print(squared(4)) .. testoutput:: group1 - 6 + 9 .. testoutput:: group2 - 24 + 16 From 860fc27177370cc55d990584f45d39b3583ca378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 14 Aug 2010 19:48:42 +0200 Subject: [PATCH 314/744] shutil.copytree doesn't have an ignore argument in Python 2.4 --- tests/path.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/path.py b/tests/path.py index df96bce45..8e9afeaa8 100644 --- a/tests/path.py +++ b/tests/path.py @@ -88,7 +88,7 @@ class path(str): """ shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror) - def copytree(self, destination, symlinks=False, ignore=None): + def copytree(self, destination, symlinks=False): """ Recursively copy a directory to the given `destination`. If the given `destination` does not exist it will be created. @@ -97,12 +97,8 @@ class path(str): If ``True`` symbolic links in the source tree result in symbolic links in the destination tree otherwise the contents of the files pointed to by the symbolic links are copied. - - :param ignore: - A callback which gets called with the path of the directory being - copied and a list of paths as returned by :func:`os.listdir`. """ - shutil.copytree(self, destination, symlinks=symlinks, ignore=ignore) + shutil.copytree(self, destination, symlinks=symlinks) def movetree(self, destination): """ From ace0e37e03f3f8ba398b579b29dd734377fcb0f1 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 14 Aug 2010 14:48:27 -0500 Subject: [PATCH 315/744] fix syntax highlighting in quickstart --- doc/web/quickstart.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst index de9b76558..61a432a86 100644 --- a/doc/web/quickstart.rst +++ b/doc/web/quickstart.rst @@ -143,8 +143,7 @@ will then add this data to the COMMENT_OPTIONS that are used in the template. need to prefix the url route with that directory, and give the `docroot` keyword argument when creating the web support object:: - support = WebSupport(... - docroot='docs') + support = WebSupport(..., docroot='docs') @app.route('/docs/<path:docname>') @@ -259,10 +258,9 @@ is added but not displayed, you can pass callable to the object:: def moderation_callback(comment): - Do something... + """Do something...""" - support = WebSupport(... - moderation_callback=moderation_callback) + support = WebSupport(..., moderation_callback=moderation_callback) The moderation callback must take one argument, which will be the same comment dict that is returned by add_comment. From 89566e69c994546ec9cf3157d9c915555c4017b2 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 14 Aug 2010 16:05:19 -0500 Subject: [PATCH 316/744] simplify websupport writer now that we don't have nested commentable nodes. --- sphinx/writers/websupport.py | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index fbd3c1ef5..c3d989b7c 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -20,38 +20,22 @@ class WebSupportTranslator(HTMLTranslator): def __init__(self, builder, *args, **kwargs): HTMLTranslator.__init__(self, builder, *args, **kwargs) self.comment_class = 'spxcmt' - self.init_support() - - def init_support(self): - self.cur_node = None def dispatch_visit(self, node): if is_commentable(node): self.handle_visit_commentable(node) HTMLTranslator.dispatch_visit(self, node) - def dispatch_departure(self, node): - HTMLTranslator.dispatch_departure(self, node) - if is_commentable(node): - self.handle_depart_commentable(node) - def handle_visit_commentable(self, node): - # If this node is nested inside another commentable node this - # node will not be commented. - if self.cur_node is None: - self.cur_node = self.add_db_node(node) - # We will place the node in the HTML id attribute. If the node - # already has an id (for indexing purposes) put an empty - # span with the existing id directly before this node's HTML. - if node.attributes['ids']: - self.body.append('<span id="%s"></span>' - % node.attributes['ids'][0]) - node.attributes['ids'] = ['s%s' % self.cur_node.id] - node.attributes['classes'].append(self.comment_class) - - def handle_depart_commentable(self, node): - if self.comment_class in node.attributes['classes']: - self.cur_node = None + db_node = self.add_db_node(node) + # We will place the node in the HTML id attribute. If the node + # already has an id (for indexing purposes) put an empty + # span with the existing id directly before this node's HTML. + if node.attributes['ids']: + self.body.append('<span id="%s"></span>' + % node.attributes['ids'][0]) + node.attributes['ids'] = ['s%s' % db_node.id] + node.attributes['classes'].append(self.comment_class) def add_db_node(self, node): storage = self.builder.app.storage From f94df908bcc73701d96de8b99ff5d034d6186ba6 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 14 Aug 2010 17:43:20 -0500 Subject: [PATCH 317/744] resave pickle after uids are added --- sphinx/builders/websupport.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index ac9dd7152..59bfd7c86 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -61,6 +61,23 @@ class WebSupportBuilder(StandaloneHTMLBuilder): stream=WarningStream(self.env._warnfunc)) return doctree + def resave_doctree(self, docname, doctree): + # make it picklable, save the reporter, it's needed later. + reporter = doctree.reporter + doctree.reporter = None + doctree.settings.warning_stream = None + doctree.settings.env = None + doctree.settings.record_dependencies = None + + fp = self.env.doc2path(docname, self.doctreedir, '.doctree') + f = open(fp, 'wb') + try: + pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) + finally: + f.close() + + doctree.reporter = reporter + def write_doc(self, docname, doctree): destination = StringOutput(encoding='utf-8') doctree.settings = self.docsettings @@ -70,6 +87,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): list(merge_doctrees(old_doctree, doctree, is_commentable)) else: list(add_uids(doctree, is_commentable)) + self.resave_doctree(docname, doctree) self.cur_docname = docname self.secnumbers = self.env.toc_secnumbers.get(docname, {}) From a52fd809bf0eecefc1b5aba3c05d6d56a1f66538 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 14 Aug 2010 17:59:31 -0500 Subject: [PATCH 318/744] remove line column from node table --- sphinx/websupport/storage/__init__.py | 6 +----- sphinx/websupport/storage/db.py | 4 +--- sphinx/websupport/storage/sqlalchemystorage.py | 4 ++-- sphinx/writers/websupport.py | 1 - 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py index da815d0a3..6a5ff49b1 100644 --- a/sphinx/websupport/storage/__init__.py +++ b/sphinx/websupport/storage/__init__.py @@ -16,15 +16,11 @@ class StorageBackend(object): """ pass - def add_node(self, id, document, line, source): + def add_node(self, id, document, source): """Add a node to the StorageBackend. :param id: a unique id for the comment. - :param document: the name of the document the node belongs to. - - :param line: the line in the source where the node begins. - :param source: the source files name. """ raise NotImplementedError() diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index 54b16f225..be81a3334 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -31,7 +31,6 @@ class Node(Base): id = Column(String(32), primary_key=True) document = Column(String(256), nullable=False) - line = Column(Integer) source = Column(Text, nullable=False) def nested_comments(self, username, moderator): @@ -94,10 +93,9 @@ class Node(Base): return comments - def __init__(self, id, document, line, source): + def __init__(self, id, document, source): self.id = id self.document = document - self.line = line self.source = source class Comment(Base): diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index d1683f603..174bef6e5 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -33,8 +33,8 @@ class SQLAlchemyStorage(StorageBackend): def pre_build(self): self.build_session = Session() - def add_node(self, id, document, line, source): - node = Node(id, document, line, source) + def add_node(self, id, document, source): + node = Node(id, document, source) self.build_session.add(node) self.build_session.flush() return node diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index c3d989b7c..6beb4b987 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -41,6 +41,5 @@ class WebSupportTranslator(HTMLTranslator): storage = self.builder.app.storage db_node_id = storage.add_node(id=node.uid, document=self.builder.cur_docname, - line=node.line, source=node.rawsource or node.astext()) return db_node_id From 34379fedb7a579ebfc1a1888bef60e882e3885ba Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 14 Aug 2010 18:33:07 -0500 Subject: [PATCH 319/744] only add node if it doesn't already exist --- doc/web/quickstart.rst | 2 ++ sphinx/websupport/storage/__init__.py | 7 +++++++ sphinx/websupport/storage/sqlalchemystorage.py | 7 ++++++- sphinx/writers/websupport.py | 12 ++++++------ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst index 61a432a86..fa93c6d90 100644 --- a/doc/web/quickstart.rst +++ b/doc/web/quickstart.rst @@ -6,6 +6,8 @@ Web Support Quick Start Building Documentation Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +New Test p + To make use of the web support package in your application you'll need to build the data it uses. This data includes pickle files representing documents, search indices, and node data that is used to track where diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py index 6a5ff49b1..3d8a9ab58 100644 --- a/sphinx/websupport/storage/__init__.py +++ b/sphinx/websupport/storage/__init__.py @@ -16,6 +16,13 @@ class StorageBackend(object): """ pass + def has_node(self, id): + """Check to see if a node exists. + + :param id: the id to check for. + """ + raise NotImplementedError() + def add_node(self, id, document, source): """Add a node to the StorageBackend. diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index 174bef6e5..c775f3bb4 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -33,11 +33,16 @@ class SQLAlchemyStorage(StorageBackend): def pre_build(self): self.build_session = Session() + def has_node(self, id): + session = Session() + node = session.query(Node).filter(Node.id == id).first() + session.close() + return True if node else False + def add_node(self, id, document, source): node = Node(id, document, source) self.build_session.add(node) self.build_session.flush() - return node def post_build(self): self.build_session.commit() diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 6beb4b987..30e8c4dc8 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -27,19 +27,19 @@ class WebSupportTranslator(HTMLTranslator): HTMLTranslator.dispatch_visit(self, node) def handle_visit_commentable(self, node): - db_node = self.add_db_node(node) # We will place the node in the HTML id attribute. If the node # already has an id (for indexing purposes) put an empty # span with the existing id directly before this node's HTML. + self.add_db_node(node) if node.attributes['ids']: self.body.append('<span id="%s"></span>' % node.attributes['ids'][0]) - node.attributes['ids'] = ['s%s' % db_node.id] + node.attributes['ids'] = ['s%s' % node.uid] node.attributes['classes'].append(self.comment_class) def add_db_node(self, node): storage = self.builder.app.storage - db_node_id = storage.add_node(id=node.uid, - document=self.builder.cur_docname, - source=node.rawsource or node.astext()) - return db_node_id + if not storage.has_node(node.uid): + storage.add_node(id=node.uid, + document=self.builder.cur_docname, + source=node.rawsource or node.astext()) From d11e70518205f25b71414f3ea1972182f603b684 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 14 Aug 2010 18:34:01 -0500 Subject: [PATCH 320/744] remove the bogus paragraph I added to docs to test doctree merging --- doc/web/quickstart.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst index fa93c6d90..61a432a86 100644 --- a/doc/web/quickstart.rst +++ b/doc/web/quickstart.rst @@ -6,8 +6,6 @@ Web Support Quick Start Building Documentation Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -New Test p - To make use of the web support package in your application you'll need to build the data it uses. This data includes pickle files representing documents, search indices, and node data that is used to track where From 06d50c45aad20ff9e14fabd68cfbe6e0c2c0992c Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sat, 14 Aug 2010 20:13:55 -0500 Subject: [PATCH 321/744] remove old static dirs if they exist already --- sphinx/builders/websupport.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 59bfd7c86..283cff4fc 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -173,13 +173,13 @@ class WebSupportBuilder(StandaloneHTMLBuilder): StandaloneHTMLBuilder.handle_finish(self) directories = ['_images', '_static'] for directory in directories: - try: - shutil.move(path.join(self.outdir, directory), - path.join(self.app.builddir, self.app.staticdir, - directory)) - except IOError: - # in case any of these directories don't exist - pass + src = path.join(self.outdir, directory) + dst = path.join(self.app.builddir, self.app.staticdir, directory) + if path.isdir(src): + if path.isdir(dst): + shutil.rmtree(dst) + shutil.move(src, dst) + for root, dirs, files in os.walk(self.doctreedir): for fn in files: fp = path.join(root, fn) From 9222c314578b9307f2ffa376b70ff347c5f9d69b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 11:56:42 +0200 Subject: [PATCH 322/744] Fix copyright info --- sphinx/builders/intl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index de147c821..abb119a46 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -5,7 +5,7 @@ The MessageCatalogBuilder class. - :copyright: Copyright 2010 by the Sphinx team, see AUTHORS. + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ From 57e83c6a94f21b76cf2700a62a34f3a029324fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 11:59:01 +0200 Subject: [PATCH 323/744] Fix line length --- sphinx/builders/intl.py | 3 ++- tests/test_build_gettext.py | 12 ++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index abb119a46..7b01602e4 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -88,7 +88,8 @@ class MessageCatalogBuilder(Builder): pofile.write(POHEADER % data) for message in messages: # message contains *one* line of text ready for translation - message = message.replace(u'\\', ur'\\').replace(u'"', ur'\"') + message = message.replace(u'\\', ur'\\'). \ + replace(u'"', ur'\"') pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message pofile.write(pomsg.encode('utf-8')) finally: diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 9e98c36d6..3312f4f22 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -46,7 +46,8 @@ def test_gettext(app): if p.returncode != 0: print stdout print stderr - assert False, 'msginit exited with return code %s' % p.returncode + assert False, 'msginit exited with return code %s' % \ + p.returncode assert (app.outdir / 'en_US.po').isfile(), 'msginit failed' try: p = Popen(['msgfmt', 'en_US.po', '-o', @@ -59,8 +60,10 @@ def test_gettext(app): if p.returncode != 0: print stdout print stderr - assert False, 'msgfmt exited with return code %s' % p.returncode - assert (app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(), 'msgfmt failed' + assert False, 'msgfmt exited with return code %s' % \ + p.returncode + assert (app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(), \ + 'msgfmt failed' finally: os.chdir(cwd) @@ -95,7 +98,8 @@ def setup_patch(): print stdout print stderr assert False, 'msgfmt exited with return code %s' % p.returncode - assert (test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo').isfile(), 'msgfmt failed' + assert (test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo').isfile(), \ + 'msgfmt failed' def teardown_patch(): (test_root / 'xx').rmtree() test_patch.setup = setup_patch From 94d9644722601728d6ed12710ec5e7ff8a00d8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 12:04:27 +0200 Subject: [PATCH 324/744] Added a newline for readability --- tests/test_build_gettext.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 3312f4f22..772bba878 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -100,6 +100,7 @@ def setup_patch(): assert False, 'msgfmt exited with return code %s' % p.returncode assert (test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo').isfile(), \ 'msgfmt failed' + def teardown_patch(): (test_root / 'xx').rmtree() test_patch.setup = setup_patch From 45285b678562b51115e9ba57c0760ffbcbc1209a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 12:13:04 +0200 Subject: [PATCH 325/744] Move i18n part of the MessageCatalogBuilder in a seperate one --- sphinx/builders/intl.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 7b01602e4..fa1dc82a3 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -9,7 +9,7 @@ :license: BSD, see LICENSE for details. """ -import collections +from collections import defaultdict from datetime import datetime from os import path @@ -41,14 +41,12 @@ msgstr "" """[1:] -class MessageCatalogBuilder(Builder): - """ - Builds gettext-style message catalogs (.pot files). - """ - name = 'gettext' +class I18NBuilder(Builder): + name = 'i18n' def init(self): - self.catalogs = collections.defaultdict(list) + Builder.init(self) + self.catalogs = defaultdict(list) def get_target_uri(self, docname, typ=None): return '' @@ -60,17 +58,17 @@ class MessageCatalogBuilder(Builder): return def write_doc(self, docname, doctree): - """ - Store a document's translatable strings in the message catalog of its - section. For this purpose a document's *top-level directory* -- or - otherwise its *name* -- is considered its section. - """ catalog = self.catalogs[docname.split(SEP, 1)[0]] for _, msg in extract_messages(doctree): - # XXX msgctxt for duplicate messages if msg not in catalog: catalog.append(msg) +class MessageCatalogBuilder(I18NBuilder): + """ + Builds gettext-style message catalogs (.pot files). + """ + name = 'gettext' + def finish(self): data = dict( version = self.config.version, From 817a7bb2c8c467c864b25e3e305f40ea0dce3a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 12:16:48 +0200 Subject: [PATCH 326/744] Fix test which was broken to change in the path object api --- tests/test_build_gettext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 772bba878..6a770869a 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -78,7 +78,7 @@ def test_all(app): confoverrides={'language': 'xx', 'locale_dirs': ['.']}) def test_patch(app): app.builder.build(['bom']) - result = (app.outdir / 'bom.txt').text('utf-8') + result = (app.outdir / 'bom.txt').text(encoding='utf-8') expect = (u"\nDatei mit UTF-8" u"\n***************\n" # underline matches new translation u"\nThis file has umlauts: äöü.\n") From 532a0de6010bf4b3573ed5119ef02629c59af96e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 12:22:43 +0200 Subject: [PATCH 327/744] Monkey patch .gettext with .ugettext if possible (we use python 2.x) --- sphinx/environment.py | 2 +- sphinx/locale/__init__.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 6339675b5..4809158d3 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -209,7 +209,7 @@ class Locale(Transform): for node, msg in extract_messages(self.document): ctx = node.parent patch = new_document(source, settings) - msgstr = catalog.ugettext(msg) + msgstr = catalog.gettext(msg) #XXX add marker to untranslated parts if not msgstr or msgstr == msg: # as-of-yet untranslated continue diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index 481169918..2d3ab0269 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -217,4 +217,6 @@ def init(locale_dirs, language, catalog='sphinx'): translator = gettext.NullTranslations() has_translation = False translators[catalog] = translator + if hasattr(translator, 'ugettext'): + translator.gettext = translator.ugettext return translator, has_translation From f6680c85ca40d7f44f616d1f7d4919768077bb51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 12:26:37 +0200 Subject: [PATCH 328/744] Use codecs.open with python 2.x in the MessageCatalogBuilder --- sphinx/builders/intl.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index fa1dc82a3..d4f5d837e 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -12,6 +12,7 @@ from collections import defaultdict from datetime import datetime from os import path +from codecs import open from docutils import nodes @@ -81,7 +82,8 @@ class MessageCatalogBuilder(I18NBuilder): self.catalogs.iteritems(), "writing message catalogs... ", lambda (section, _):darkgreen(section), len(self.catalogs)): - pofile = open(path.join(self.outdir, '%s.pot' % section), 'w') + pofp = path.join(self.outdir, section + '.pot') + pofile = open(pofp, 'w', encoding='utf-8') try: pofile.write(POHEADER % data) for message in messages: @@ -89,6 +91,6 @@ class MessageCatalogBuilder(I18NBuilder): message = message.replace(u'\\', ur'\\'). \ replace(u'"', ur'\"') pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message - pofile.write(pomsg.encode('utf-8')) + pofile.write(pomsg) finally: pofile.close() From 42279f17bd3ef43833b6d99bff3bd6e19c72ff6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 12:30:13 +0200 Subject: [PATCH 329/744] Fix test_gettext test for python 3.x --- tests/test_build_gettext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 6a770869a..581c1cb85 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -67,7 +67,7 @@ def test_gettext(app): finally: os.chdir(cwd) - _ = gettext.translation('test_root', app.outdir, languages=['en']).ugettext + _ = gettext.translation('test_root', app.outdir, languages=['en']).gettext assert _("Testing various markup") == u"Testing various markup" @with_app(buildername='gettext') From 0dcf347d0074d64cbd2fd9df8b32b516b386d562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 12:53:00 +0200 Subject: [PATCH 330/744] Added versioning support to i18n builder --- sphinx/builders/intl.py | 68 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index d4f5d837e..88c128f56 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -13,13 +13,18 @@ from collections import defaultdict from datetime import datetime from os import path from codecs import open +import os +import pickle from docutils import nodes +from docutils.utils import Reporter from sphinx.builders import Builder from sphinx.util.nodes import extract_messages -from sphinx.util.osutil import SEP +from sphinx.util.osutil import SEP, copyfile from sphinx.util.console import darkgreen +from sphinx.environment import WarningStream +from sphinx.versioning import add_uids, merge_doctrees POHEADER = ur""" # SOME DESCRIPTIVE TITLE. @@ -47,7 +52,42 @@ class I18NBuilder(Builder): def init(self): Builder.init(self) - self.catalogs = defaultdict(list) + self.catalogs = defaultdict(dict) + for root, dirs, files in os.walk(self.doctreedir): + for fn in files: + fp = path.join(root, fn) + if fp.endswith('.doctree'): + copyfile(fp, fp + '.old') + + def get_old_doctree(self, docname): + fp = self.env.doc2path(docname, self.doctreedir, '.doctree.old') + try: + f = open(fp, 'rb') + try: + doctree = pickle.load(f) + finally: + f.close() + except IOError: + return None + doctree.settings.env = self.env + doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, + stream=WarningStream(self.env._warnfunc)) + + def resave_doctree(self, docname, doctree): + reporter = doctree.reporter + doctree.reporter = None + doctree.settings.warning_stream = None + doctree.settings.env = None + doctree.settings.record_dependencies = None + + fp = self.env.doc2path(docname, self.doctreedir, '.doctree') + f = open(fp, 'wb') + try: + pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) + finally: + f.close() + + doctree.reporter = reporter def get_target_uri(self, docname, typ=None): return '' @@ -60,9 +100,24 @@ class I18NBuilder(Builder): def write_doc(self, docname, doctree): catalog = self.catalogs[docname.split(SEP, 1)[0]] - for _, msg in extract_messages(doctree): - if msg not in catalog: - catalog.append(msg) + old_doctree = self.get_old_doctree(docname) + + if old_doctree: + list(merge_doctrees(old_doctree, doctree, nodes.TextElement)) + else: + list(add_uids(doctree, nodes.TextElement)) + self.resave_doctree(docname, doctree) + + for node, msg in extract_messages(doctree): + catalog.setdefault(node.uid, msg) + + def finish(self): + Builder.finish(self) + for root, dirs, files in os.walk(self.doctreedir): + for fn in files: + fp = path.join(root, fn) + if fp.endswith('.doctree.old'): + os.remove(fp) class MessageCatalogBuilder(I18NBuilder): """ @@ -71,6 +126,7 @@ class MessageCatalogBuilder(I18NBuilder): name = 'gettext' def finish(self): + I18NBuilder.finish(self) data = dict( version = self.config.version, copyright = self.config.copyright, @@ -86,7 +142,7 @@ class MessageCatalogBuilder(I18NBuilder): pofile = open(pofp, 'w', encoding='utf-8') try: pofile.write(POHEADER % data) - for message in messages: + for message in messages.itervalues(): # message contains *one* line of text ready for translation message = message.replace(u'\\', ur'\\'). \ replace(u'"', ur'\"') From 5e749197edc7c253e8ddfa6061bd61a80cf71d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 13:18:47 +0200 Subject: [PATCH 331/744] Before each id, str pair a comment with the uid can be found in the pot files --- sphinx/builders/intl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 88c128f56..19190926a 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -142,11 +142,11 @@ class MessageCatalogBuilder(I18NBuilder): pofile = open(pofp, 'w', encoding='utf-8') try: pofile.write(POHEADER % data) - for message in messages.itervalues(): + for uid, message in messages.iteritems(): # message contains *one* line of text ready for translation message = message.replace(u'\\', ur'\\'). \ replace(u'"', ur'\"') - pomsg = u'msgid "%s"\nmsgstr ""\n\n' % message + pomsg = u'#%s\nmsgid "%s"\nmsgstr ""\n\n' % (uid, message) pofile.write(pomsg) finally: pofile.close() From 8ebeb908fed66ec1857581dbc26941dba9b0382b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 13:38:10 +0200 Subject: [PATCH 332/744] Added a VersioningBuilderMixin --- sphinx/builders/versioning.py | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 sphinx/builders/versioning.py diff --git a/sphinx/builders/versioning.py b/sphinx/builders/versioning.py new file mode 100644 index 000000000..f00e08c23 --- /dev/null +++ b/sphinx/builders/versioning.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.versioning + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import os +import pickle + +from docutils.utils import Reporter + +from sphinx.util.osutil import copyfile +from sphinx.environment import WarningStream +from sphinx.versioning import add_uids, merge_doctrees + + +class VersioningBuilderMixin(object): + def walk_doctree_files(self): + for root, dirs, files in os.walk(self.doctreedir): + for fn in files: + yield os.path.join(root, fn) + + def init(self): + for fp in self.walk_doctree_files(self): + if fp.endswith('.doctree'): + copyfile(fp, fp + '.old') + + def get_old_doctree(self, docname): + fp = self.env.doc2path(docname, self.doctreedir, '.doctree.old') + try: + f = open(fp, 'rb') + try: + doctree = pickle.load(f) + finally: + f.close() + except IOError: + return None + doctree.settings.env = self.env + doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, + stream=WarningStream(self.env._warnfunc)) + + def resave_doctree(self, docname, doctree): + reporter = doctree.reporter + doctree.reporter = None + doctree.settings.warning_stream = None + doctree.settings.env = None + doctree.settings.record_dependencies = None + + fp = self.env.doc2path(docname, self.doctreedir, '.doctree') + f = open(fp, 'wb') + try: + pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) + finally: + f.close() + + doctree.reporter = reporter + + def handle_versioning(self, docname, doctree, condition): + old_doctree = self.get_old_doctree(docname) + if old_doctree: + list(merge_doctrees(old_doctree, doctree, condition)) + else: + list(add_uids(doctree, condition)) + self.resave_doctree(docname, doctree) + + def finish(self): + for fp in self.walk_doctree_files(self): + if fp.endswith('.doctree.old'): + os.remove(fp) From 568bb63f0d73c8c7228374d228ea72f0d70c0ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 13:44:14 +0200 Subject: [PATCH 333/744] Fix an error --- sphinx/builders/versioning.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/versioning.py b/sphinx/builders/versioning.py index f00e08c23..c47dcc98a 100644 --- a/sphinx/builders/versioning.py +++ b/sphinx/builders/versioning.py @@ -23,7 +23,7 @@ class VersioningBuilderMixin(object): yield os.path.join(root, fn) def init(self): - for fp in self.walk_doctree_files(self): + for fp in self.walk_doctree_files(): if fp.endswith('.doctree'): copyfile(fp, fp + '.old') @@ -66,6 +66,6 @@ class VersioningBuilderMixin(object): self.resave_doctree(docname, doctree) def finish(self): - for fp in self.walk_doctree_files(self): + for fp in self.walk_doctree_files(): if fp.endswith('.doctree.old'): os.remove(fp) From d31e8c2f59487b6e1edac1c2f67ca45cdc9beb00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 13:48:38 +0200 Subject: [PATCH 334/744] Switch VersioningBuilderMixin --- sphinx/builders/intl.py | 57 ++++------------------------------------- 1 file changed, 5 insertions(+), 52 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 19190926a..0af5b19a2 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -13,18 +13,14 @@ from collections import defaultdict from datetime import datetime from os import path from codecs import open -import os -import pickle from docutils import nodes -from docutils.utils import Reporter from sphinx.builders import Builder +from sphinx.builders.versioning import VersioningBuilderMixin from sphinx.util.nodes import extract_messages from sphinx.util.osutil import SEP, copyfile from sphinx.util.console import darkgreen -from sphinx.environment import WarningStream -from sphinx.versioning import add_uids, merge_doctrees POHEADER = ur""" # SOME DESCRIPTIVE TITLE. @@ -47,47 +43,13 @@ msgstr "" """[1:] -class I18NBuilder(Builder): +class I18NBuilder(Builder, VersioningBuilderMixin): name = 'i18n' def init(self): Builder.init(self) + VersioningBuilderMixin.init(self) self.catalogs = defaultdict(dict) - for root, dirs, files in os.walk(self.doctreedir): - for fn in files: - fp = path.join(root, fn) - if fp.endswith('.doctree'): - copyfile(fp, fp + '.old') - - def get_old_doctree(self, docname): - fp = self.env.doc2path(docname, self.doctreedir, '.doctree.old') - try: - f = open(fp, 'rb') - try: - doctree = pickle.load(f) - finally: - f.close() - except IOError: - return None - doctree.settings.env = self.env - doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, - stream=WarningStream(self.env._warnfunc)) - - def resave_doctree(self, docname, doctree): - reporter = doctree.reporter - doctree.reporter = None - doctree.settings.warning_stream = None - doctree.settings.env = None - doctree.settings.record_dependencies = None - - fp = self.env.doc2path(docname, self.doctreedir, '.doctree') - f = open(fp, 'wb') - try: - pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) - finally: - f.close() - - doctree.reporter = reporter def get_target_uri(self, docname, typ=None): return '' @@ -100,24 +62,15 @@ class I18NBuilder(Builder): def write_doc(self, docname, doctree): catalog = self.catalogs[docname.split(SEP, 1)[0]] - old_doctree = self.get_old_doctree(docname) - if old_doctree: - list(merge_doctrees(old_doctree, doctree, nodes.TextElement)) - else: - list(add_uids(doctree, nodes.TextElement)) - self.resave_doctree(docname, doctree) + self.handle_versioning(docname, doctree, nodes.TextElement) for node, msg in extract_messages(doctree): catalog.setdefault(node.uid, msg) def finish(self): Builder.finish(self) - for root, dirs, files in os.walk(self.doctreedir): - for fn in files: - fp = path.join(root, fn) - if fp.endswith('.doctree.old'): - os.remove(fp) + VersioningBuilderMixin.finish(self) class MessageCatalogBuilder(I18NBuilder): """ From e182a7d5ed337f29998e36aeb96c6e32ef8c189a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 13:56:01 +0200 Subject: [PATCH 335/744] Switch to VersioningBuilderMixin --- sphinx/builders/websupport.py | 59 +++-------------------------------- 1 file changed, 5 insertions(+), 54 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 283cff4fc..303adfe64 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -12,23 +12,20 @@ import cPickle as pickle from os import path from cgi import escape -import os import posixpath import shutil from docutils.io import StringOutput -from docutils.utils import Reporter from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile from sphinx.util.jsonimpl import dumps as dump_json from sphinx.util.websupport import is_commentable from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.builders.versioning import VersioningBuilderMixin from sphinx.writers.websupport import WebSupportTranslator -from sphinx.environment import WarningStream -from sphinx.versioning import add_uids, merge_doctrees -class WebSupportBuilder(StandaloneHTMLBuilder): +class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin): """ Builds documents for the web support package. """ @@ -37,57 +34,16 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def init(self): StandaloneHTMLBuilder.init(self) - for root, dirs, files in os.walk(self.doctreedir): - for fn in files: - fp = path.join(root, fn) - if fp.endswith('.doctree'): - copyfile(fp, fp + '.old') + VersioningBuilderMixin.init(self) def init_translator_class(self): self.translator_class = WebSupportTranslator - def get_old_doctree(self, docname): - fp = self.env.doc2path(docname, self.doctreedir, '.doctree.old') - try: - f = open(fp, 'rb') - try: - doctree = pickle.load(f) - finally: - f.close() - except IOError: - return None - doctree.settings.env = self.env - doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, - stream=WarningStream(self.env._warnfunc)) - return doctree - - def resave_doctree(self, docname, doctree): - # make it picklable, save the reporter, it's needed later. - reporter = doctree.reporter - doctree.reporter = None - doctree.settings.warning_stream = None - doctree.settings.env = None - doctree.settings.record_dependencies = None - - fp = self.env.doc2path(docname, self.doctreedir, '.doctree') - f = open(fp, 'wb') - try: - pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) - finally: - f.close() - - doctree.reporter = reporter - def write_doc(self, docname, doctree): destination = StringOutput(encoding='utf-8') doctree.settings = self.docsettings - old_doctree = self.get_old_doctree(docname) - if old_doctree: - list(merge_doctrees(old_doctree, doctree, is_commentable)) - else: - list(add_uids(doctree, is_commentable)) - self.resave_doctree(docname, doctree) + self.handle_versioning(docname, doctree, is_commentable) self.cur_docname = docname self.secnumbers = self.env.toc_secnumbers.get(docname, {}) @@ -171,6 +127,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder): def handle_finish(self): StandaloneHTMLBuilder.handle_finish(self) + VersioningBuilderMixin.finish(self) directories = ['_images', '_static'] for directory in directories: src = path.join(self.outdir, directory) @@ -180,12 +137,6 @@ class WebSupportBuilder(StandaloneHTMLBuilder): shutil.rmtree(dst) shutil.move(src, dst) - for root, dirs, files in os.walk(self.doctreedir): - for fn in files: - fp = path.join(root, fn) - if fp.endswith('.doctree.old'): - os.remove(fp) - def dump_search_index(self): self.indexer.finish_indexing() From 9854433ada87ff4c21807f5ad5e0ed0576ef8384 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Sun, 15 Aug 2010 10:43:52 -0500 Subject: [PATCH 336/744] return doctree from get_old_doctree --- sphinx/builders/versioning.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/builders/versioning.py b/sphinx/builders/versioning.py index c47dcc98a..6c2bccca4 100644 --- a/sphinx/builders/versioning.py +++ b/sphinx/builders/versioning.py @@ -40,6 +40,7 @@ class VersioningBuilderMixin(object): doctree.settings.env = self.env doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, stream=WarningStream(self.env._warnfunc)) + return doctree def resave_doctree(self, docname, doctree): reporter = doctree.reporter From 31f5e876a56f3a1bad58d65b5313523741065335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 18:08:00 +0200 Subject: [PATCH 337/744] Return doctree in get_old_doctree --- sphinx/builders/versioning.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/builders/versioning.py b/sphinx/builders/versioning.py index c47dcc98a..6c2bccca4 100644 --- a/sphinx/builders/versioning.py +++ b/sphinx/builders/versioning.py @@ -40,6 +40,7 @@ class VersioningBuilderMixin(object): doctree.settings.env = self.env doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, stream=WarningStream(self.env._warnfunc)) + return doctree def resave_doctree(self, docname, doctree): reporter = doctree.reporter From 37cc185fe789d91f6e40302ad100d05c882474b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 18:41:14 +0200 Subject: [PATCH 338/744] Added a test for paragraphs inserted at the beginning of a document --- tests/root/versioning/index.txt | 1 + tests/root/versioning/insert_beginning.txt | 18 ++++++++++++++++++ tests/test_versioning.py | 7 +++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/root/versioning/insert_beginning.txt diff --git a/tests/root/versioning/index.txt b/tests/root/versioning/index.txt index 234e223f1..a6d12007c 100644 --- a/tests/root/versioning/index.txt +++ b/tests/root/versioning/index.txt @@ -9,3 +9,4 @@ Versioning Stuff deleted deleted_end modified + insert_beginning diff --git a/tests/root/versioning/insert_beginning.txt b/tests/root/versioning/insert_beginning.txt new file mode 100644 index 000000000..57102a76c --- /dev/null +++ b/tests/root/versioning/insert_beginning.txt @@ -0,0 +1,18 @@ +Versioning test text +==================== + +Apperantly inserting a paragraph at the beginning of a document caused +problems earlier so this document should be used to test that. + +So the thing is I need some kind of text - not the lorem ipsum stuff, that +doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find +a good text for that under public domain so I thought the easiest solution is +to write one by myself. It's not really interesting, in fact it is *really* +boring. + +Anyway I need more than one paragraph, at least three for the original +document, I think, and another one for two different ones. + +So the previous paragraph was a bit short because I don't want to test this +only on long paragraphs, I hope it was short enough to cover most stuff. +Anyway I see this lacks ``some markup`` so I have to add a **little** bit. diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 54a48f4ad..549d760f8 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -102,3 +102,10 @@ def test_insert(): assert len(new_nodes) == 1 assert original_uids[0] == uids[0] assert original_uids[1:] == uids[2:] + +def test_insert_beginning(): + insert_beginning = doctrees['versioning/insert_beginning'] + new_nodes = list(merge_doctrees(original, insert_beginning, is_paragraph)) + uids = [n.uid for n in insert_beginning.traverse(is_paragraph)] + assert len(new_nodes) == 1 + assert original_uids == uids[1:] From 6ac27b4da77119b11b9e5538483e0f6c9da4b5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 18:44:09 +0200 Subject: [PATCH 339/744] Check also for the length and explicitly check that the first uids are different --- tests/test_versioning.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 549d760f8..136faa1c4 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -108,4 +108,6 @@ def test_insert_beginning(): new_nodes = list(merge_doctrees(original, insert_beginning, is_paragraph)) uids = [n.uid for n in insert_beginning.traverse(is_paragraph)] assert len(new_nodes) == 1 + assert len(uids) == 4 assert original_uids == uids[1:] + assert original_uids[0] != uids[0] From 0cf175e0b2d33b48994effaf5b88b7d0512e412f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 19:25:46 +0200 Subject: [PATCH 340/744] Added a test for the behavior described by Jacob and it does fail --- tests/root/versioning/index.txt | 1 + tests/root/versioning/insert_similar.txt | 17 +++++++++++++++++ tests/test_versioning.py | 9 +++++++++ 3 files changed, 27 insertions(+) create mode 100644 tests/root/versioning/insert_similar.txt diff --git a/tests/root/versioning/index.txt b/tests/root/versioning/index.txt index a6d12007c..9d098f750 100644 --- a/tests/root/versioning/index.txt +++ b/tests/root/versioning/index.txt @@ -10,3 +10,4 @@ Versioning Stuff deleted_end modified insert_beginning + insert_similar diff --git a/tests/root/versioning/insert_similar.txt b/tests/root/versioning/insert_similar.txt new file mode 100644 index 000000000..ee9b5305c --- /dev/null +++ b/tests/root/versioning/insert_similar.txt @@ -0,0 +1,17 @@ +Versioning test text +==================== + +So the thing is I need some kind of text - not the lorem ipsum stuff, that +doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find +a good text for that under public domain so I thought the easiest solution is +to write one by myself. It's not really interesting, in fact it is *really* +boring. + +Anyway I need more + +Anyway I need more than one paragraph, at least three for the original +document, I think, and another one for two different ones. + +So the previous paragraph was a bit short because I don't want to test this +only on long paragraphs, I hope it was short enough to cover most stuff. +Anyway I see this lacks ``some markup`` so I have to add a **little** bit. diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 136faa1c4..5c31f0c32 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -111,3 +111,12 @@ def test_insert_beginning(): assert len(uids) == 4 assert original_uids == uids[1:] assert original_uids[0] != uids[0] + +def test_insert_similar(): + insert_similar = doctrees['versioning/insert_similar'] + new_nodes = list(merge_doctrees(original, insert_similar, is_paragraph)) + uids = [n.uid for n in insert_similar.traverse(is_paragraph)] + assert len(new_nodes) == 1 + assert new_nodes[0].rawsource == u'Anyway I need more' + assert original_uids[0] == uids[0] + assert original_uids[1:] == uids[2:] From 85b8a451a696c13d4644d4da900c2bcb31de4010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 20:34:08 +0200 Subject: [PATCH 341/744] Replaced the merging algorithm with one that handles similarities better, it's awfully slow though, if anybody has a better idea please implement it --- sphinx/versioning.py | 110 ++++++++++--------------------------------- 1 file changed, 25 insertions(+), 85 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index d0ea18a77..0b2b1f24f 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -10,12 +10,9 @@ :license: BSD, see LICENSE for details. """ from uuid import uuid4 +from operator import itemgetter +from collections import defaultdict from itertools import product -try: - from itertools import izip_longest as zip_longest -except ImportError: - from itertools import zip_longest -from difflib import SequenceMatcher from sphinx.util import PeekableIterator @@ -34,19 +31,6 @@ def add_uids(doctree, condition): node.uid = uuid4().hex yield node -def merge_node(old, new): - """ - Merges the `old` node with the `new` one, if it's successful the `new` node - get's the unique identifier of the `new` one and ``True`` is returned. If - the merge is unsuccesful ``False`` is returned. - """ - equals, changed, replaced = make_diff(old.rawsource, - new.rawsource) - if equals or changed: - new.uid = old.uid - return True - return False - def merge_doctrees(old, new, condition): """ Merges the `old` doctree with the `new` one while looking at nodes matching @@ -58,78 +42,34 @@ def merge_doctrees(old, new, condition): :param condition: A callable which returns either ``True`` or ``False`` for a given node. """ - old_iter = PeekableIterator(old.traverse(condition)) - new_iter = PeekableIterator(new.traverse(condition)) - old_nodes = [] - new_nodes = [] - for old_node, new_node in zip_longest(old_iter, new_iter): - if old_node is None: - new_nodes.append(new_node) + old_nodes = old.traverse(condition) + new_nodes = new.traverse(condition) + ratios = defaultdict(list) + for old_node, new_node in product(old_nodes, new_nodes): + ratios[old_node, new_node] = get_ratio(old_node.rawsource, + new_node.rawsource) + ratios = sorted(ratios.iteritems(), key=itemgetter(1)) + seen = set() + for (old_node, new_node), ratio in ratios: + if new_node in seen: continue - if new_node is None: - old_nodes.append(old_node) - continue - if not merge_node(old_node, new_node): - if old_nodes: - for i, very_old_node in enumerate(old_nodes): - if merge_node(very_old_node, new_node): - del old_nodes[i] - # If the last identified node which has not matched the - # unidentified node matches the current one, we have to - # assume that the last unidentified one has been - # inserted. - # - # As the required time multiplies with each insert, we - # want to avoid that by checking if the next - # unidentified node matches the current identified one - # and if so we make a shift. - if i == len(old_nodes): - next_new_node = new_iter.next() - if not merge_node(old_node, next_new_node): - new_iter.push(next_new_node) - break - else: - old_nodes.append(old_node) - new_nodes.append(new_node) - for (i, new_node), (j, old_node) in product(enumerate(new_nodes), - enumerate(old_nodes)): - if merge_node(old_node, new_node): - del new_nodes[i] - del old_nodes[j] - for node in new_nodes: - node.uid = uuid4().hex - # Yielding the new nodes here makes it possible to use this generator - # like add_uids - yield node + else: + seen.add(new_node) + if ratio < 65: + new_node.uid = old_node.uid + else: + new_node.uid = uuid4().hex + yield new_node -def make_diff(old, new): +def get_ratio(old, new): """ - Takes two strings `old` and `new` and returns a :class:`tuple` of boolean - values ``(equals, changed, replaced)``. - - equals - - ``True`` if the `old` string and the `new` one are equal. - - changed - - ``True`` if the `new` string is a changed version of the `old` one. - - replaced - - ``True`` if the `new` string and the `old` string are totally - different. - - .. note:: This assumes the two strings are human readable text or at least - something very similar to that, otherwise it can not detect if - the string has been changed or replaced. In any case the - detection should not be considered reliable. + Returns a "similiarity ratio" representing the similarity between the two + strings where 0 is equal and anything above less than equal. """ if old == new: - return True, False, False - if new in old or levenshtein_distance(old, new) / (len(old) / 100.0) < 70: - return False, True, False - return False, False, True + return 0 + ratio = levenshtein_distance(old, new) / (len(old) / 100.0) + return ratio def levenshtein_distance(a, b): if len(a) < len(b): From ad42f2a93b0e27e5c6e6df45f4ba80221133f542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 20:34:58 +0200 Subject: [PATCH 342/744] shutil.copytree has no ignore argument in python 2.4 --- tests/path.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/path.py b/tests/path.py index df96bce45..d4bd7ddfe 100644 --- a/tests/path.py +++ b/tests/path.py @@ -88,7 +88,7 @@ class path(str): """ shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror) - def copytree(self, destination, symlinks=False, ignore=None): + def copytree(self, destination, symlinks=False): """ Recursively copy a directory to the given `destination`. If the given `destination` does not exist it will be created. @@ -102,7 +102,7 @@ class path(str): A callback which gets called with the path of the directory being copied and a list of paths as returned by :func:`os.listdir`. """ - shutil.copytree(self, destination, symlinks=symlinks, ignore=ignore) + shutil.copytree(self, destination, symlinks=symlinks) def movetree(self, destination): """ From c3905938eaa5762d9e407e0cc31616b757562be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 20:48:19 +0200 Subject: [PATCH 343/744] Use uuid as a requirement in the setup.py and mention it in the documentation for Python 2.4 --- doc/intro.rst | 4 ++++ setup.py | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/intro.rst b/doc/intro.rst index c85fbbad3..caff141d0 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -50,10 +50,14 @@ docutils_ and Jinja2_ libraries. Sphinx should work with docutils version 0.5 or some (not broken) SVN trunk snapshot. If you like to have source code highlighting support, you must also install the Pygments_ library. +If you use **Python 2.4** you also need uuid_. + .. _reStructuredText: http://docutils.sf.net/rst.html .. _docutils: http://docutils.sf.net/ .. _Jinja2: http://jinja.pocoo.org/2/ .. _Pygments: http://pygments.org/ +.. The given homepage is only a directory listing so I'm using the pypi site. +.. _uuid: http://pypi.python.org/pypi/uuid/ Usage diff --git a/setup.py b/setup.py index fe4066b80..af9e660c5 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ A development egg can be found `here <http://bitbucket.org/birkenfeld/sphinx/get/tip.gz#egg=Sphinx-dev>`_. ''' -requires = ['Pygments>=0.8', 'Jinja2>=2.2', 'docutils>=0.5'] +requires = ['Pygments>=0.8', 'Jinja2>=2.2', 'docutils>=0.5', 'uuid>=1.30'] if sys.version_info < (2, 4): print('ERROR: Sphinx requires at least Python 2.4 to run.') @@ -61,7 +61,10 @@ if sys.version_info < (2, 5): except: pass else: - del requires[-1] + del requires[-2] +elif sys.version_info >= (2, 5): + # An uuid module has been added to the stdlib in 2.5 + del requires[-1] # Provide a "compile_catalog" command that also creates the translated From ad5b5c740b3c6af1f6a84af7bfa9f41f6c9c69d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 21:06:47 +0200 Subject: [PATCH 344/744] Make levenshtein implementation faster for equal strings --- sphinx/versioning.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 0b2b1f24f..753629042 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -66,12 +66,11 @@ def get_ratio(old, new): Returns a "similiarity ratio" representing the similarity between the two strings where 0 is equal and anything above less than equal. """ - if old == new: - return 0 - ratio = levenshtein_distance(old, new) / (len(old) / 100.0) - return ratio + return levenshtein_distance(old, new) / (len(old) / 100.0) def levenshtein_distance(a, b): + if a == b: + return 0 if len(a) < len(b): a, b = b, a if not a: From e68b1cacd077f49bafa84b95103945b19910993c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 15 Aug 2010 21:25:31 +0200 Subject: [PATCH 345/744] Optimized merging algorithm --- sphinx/versioning.py | 14 ++++++++++---- tests/test_versioning.py | 14 +------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 753629042..5f3254554 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -45,11 +45,17 @@ def merge_doctrees(old, new, condition): old_nodes = old.traverse(condition) new_nodes = new.traverse(condition) ratios = defaultdict(list) - for old_node, new_node in product(old_nodes, new_nodes): - ratios[old_node, new_node] = get_ratio(old_node.rawsource, - new_node.rawsource) - ratios = sorted(ratios.iteritems(), key=itemgetter(1)) seen = set() + for old_node, new_node in product(old_nodes, new_nodes): + if new_node in seen: + continue + ratio = get_ratio(old_node.rawsource, new_node.rawsource) + if ratio == 0: + new_node.uid = old_node.uid + seen.add(new_node) + else: + ratios[old_node, new_node] = ratio + ratios = sorted(ratios.iteritems(), key=itemgetter(1)) for (old_node, new_node), ratio in ratios: if new_node in seen: continue diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 5c31f0c32..06c4ff2f1 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -16,7 +16,7 @@ from docutils.statemachine import ViewList from docutils.parsers.rst.directives.html import MetaBody from sphinx import addnodes -from sphinx.versioning import make_diff, add_uids, merge_doctrees +from sphinx.versioning import add_uids, merge_doctrees def setup_module(): global app, original, original_uids @@ -36,18 +36,6 @@ doctrees = {} def on_doctree_resolved(app, doctree, docname): doctrees[docname] = doctree -def test_make_diff(): - tests = [ - (('aaa', 'aaa'), (True, False, False)), - (('aaa', 'aab'), (False, True, False)), - (('aaa', 'abb'), (False, True, False)), - (('aaa', 'aba'), (False, True, False)), - (('aaa', 'baa'), (False, True, False)), - (('aaa', 'bbb'), (False, False, True)) - ] - for args, result in tests: - assert make_diff(*args) == result - def is_paragraph(node): return node.__class__.__name__ == 'paragraph' From 36a0aa1667639818a39d43c79bceaa7ccee0392a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Mon, 16 Aug 2010 08:10:14 +0200 Subject: [PATCH 346/744] Optimized further and added several comments explaining the merging algorithm --- sphinx/versioning.py | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 5f3254554..9a7cb2da6 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -13,6 +13,10 @@ from uuid import uuid4 from operator import itemgetter from collections import defaultdict from itertools import product +try: + from itertools import izip_longest as zip_longest +except ImportError: + from itertools import zip_longest from sphinx.util import PeekableIterator @@ -42,12 +46,19 @@ def merge_doctrees(old, new, condition): :param condition: A callable which returns either ``True`` or ``False`` for a given node. """ - old_nodes = old.traverse(condition) - new_nodes = new.traverse(condition) + old_iter = old.traverse(condition) + new_iter = new.traverse(condition) + old_nodes = [] + new_nodes = [] ratios = defaultdict(list) seen = set() - for old_node, new_node in product(old_nodes, new_nodes): - if new_node in seen: + # compare the nodes each doctree in order + for old_node, new_node in zip_longest(old_iter, new_iter): + if old_node is None: + new_nodes.append(new_node) + continue + if new_node is None: + old_nodes.append(old_node) continue ratio = get_ratio(old_node.rawsource, new_node.rawsource) if ratio == 0: @@ -55,6 +66,22 @@ def merge_doctrees(old, new, condition): seen.add(new_node) else: ratios[old_node, new_node] = ratio + old_nodes.append(old_node) + new_nodes.append(new_node) + # calculate the ratios for each unequal pair of nodes, should we stumble + # on a pair which is equal we set the uid and add it to the seen ones + for old_node, new_node in product(old_nodes, new_nodes): + if new_node in seen or (old_node, new_node) in ratios: + continue + ratio = get_ratio(old_node.rawsource, new_node.rawsource) + if ratio == 0: + new_node.uid = old_node.uid + seen.add(new_node) + else: + ratios[old_node, new_node] = ratio + # choose the old node with the best ratio for each new node and set the uid + # as long as the ratio is under a certain value, in which case we consider + # them not changed but different ratios = sorted(ratios.iteritems(), key=itemgetter(1)) for (old_node, new_node), ratio in ratios: if new_node in seen: @@ -66,6 +93,11 @@ def merge_doctrees(old, new, condition): else: new_node.uid = uuid4().hex yield new_node + # create new uuids for any new node we left out earlier, this happens + # if one or more nodes are simply added. + for new_node in set(new_nodes) - seen: + new_node.uid = uuid4().hex + yield new_node def get_ratio(old, new): """ From 97bf9b01af8d00d5bc7aaa56ed4ad0e963d286dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Mon, 16 Aug 2010 08:18:10 +0200 Subject: [PATCH 347/744] Fix get_ratio for empty strings --- sphinx/versioning.py | 7 ++++++- tests/test_versioning.py | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 9a7cb2da6..06acc63dc 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -20,6 +20,9 @@ except ImportError: from sphinx.util import PeekableIterator +# anything below that ratio is considered equal/changed +VERSIONING_RATIO = 65 + def add_uids(doctree, condition): """ Adds a unique id to every node in the `doctree` which matches the condition @@ -88,7 +91,7 @@ def merge_doctrees(old, new, condition): continue else: seen.add(new_node) - if ratio < 65: + if ratio < VERSIONING_RATIO: new_node.uid = old_node.uid else: new_node.uid = uuid4().hex @@ -104,6 +107,8 @@ def get_ratio(old, new): Returns a "similiarity ratio" representing the similarity between the two strings where 0 is equal and anything above less than equal. """ + if not all([old, new]): + return VERSIONING_RATIO return levenshtein_distance(old, new) / (len(old) / 100.0) def levenshtein_distance(a, b): diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 06c4ff2f1..923da2035 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -16,7 +16,7 @@ from docutils.statemachine import ViewList from docutils.parsers.rst.directives.html import MetaBody from sphinx import addnodes -from sphinx.versioning import add_uids, merge_doctrees +from sphinx.versioning import add_uids, merge_doctrees, get_ratio def setup_module(): global app, original, original_uids @@ -39,6 +39,10 @@ def on_doctree_resolved(app, doctree, docname): def is_paragraph(node): return node.__class__.__name__ == 'paragraph' +def test_get_ratio(): + assert get_ratio('', 'a') + assert get_ratio('a', '') + def test_add_uids(): assert len(original_uids) == 3 From fd10895e5a3e5b2d5c80db0fb9d544607eb1ed5d Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Mon, 16 Aug 2010 10:11:52 -0500 Subject: [PATCH 348/744] fix moderation test --- tests/test_websupport.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 3e784405e..5ef7a902f 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -230,11 +230,13 @@ def moderation_callback(comment): @with_support(moderation_callback=moderation_callback) def test_moderation(support): - raise SkipTest( - 'test is broken, relies on order of test execution and numeric ids') - accepted = support.add_comment('Accepted Comment', node_id=3, + session = Session() + nodes = session.query(Node).all() + node = nodes[7] + session.close() + accepted = support.add_comment('Accepted Comment', node_id=node.id, displayed=False) - rejected = support.add_comment('Rejected comment', node_id=3, + rejected = support.add_comment('Rejected comment', node_id=node.id, displayed=False) # Make sure the moderation_callback is called. assert called == True @@ -243,9 +245,9 @@ def test_moderation(support): raises(UserNotAuthorizedError, support.reject_comment, accepted['id']) support.accept_comment(accepted['id'], moderator=True) support.reject_comment(rejected['id'], moderator=True) - comments = support.get_data(3)['comments'] + comments = support.get_data(node.id)['comments'] assert len(comments) == 1 - comments = support.get_data(3, moderator=True)['comments'] + comments = support.get_data(node.id, moderator=True)['comments'] assert len(comments) == 1 From 363c2f702e440fef993ef754f2dade3a6addf4c4 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Wed, 18 Aug 2010 15:41:12 -0500 Subject: [PATCH 349/744] display comments inline --- sphinx/themes/basic/static/comment-close.png | Bin 0 -> 3578 bytes sphinx/themes/basic/static/websupport.js | 378 +++++++++---------- sphinx/websupport/__init__.py | 1 + 3 files changed, 187 insertions(+), 192 deletions(-) create mode 100644 sphinx/themes/basic/static/comment-close.png diff --git a/sphinx/themes/basic/static/comment-close.png b/sphinx/themes/basic/static/comment-close.png new file mode 100644 index 0000000000000000000000000000000000000000..09b54be46da3f0d4a5061da289dc91d8a2cdbc9c GIT binary patch literal 3578 zcmV<W4F&RvP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV000V4X+uL$P-t&- zZ*ypGa3D!TLm+T+Z)Rz1WdHzp+MQEpR8#2|J@?-9LQ9B%luK_?6$l_wLW_VDktQl3 z2@pz%A)(n7QNa;KMFbnjpojyGj)066Q7jCK3fKqaA)=0hqlk*i`{8?|Yu3E?=FR@K z*FNX0^PRKL2fzpnmPj*EHGmAMLLL#|gU7_i;p8qrfeIvW01ybXWFd3?BLM*Temp!Y zBESc}00DT@3kU$fO`E_l9Ebl8>Oz@Z0f2-7z;ux~O9+4z06=<<LZ$#fMgf4Gm?l#I zpacM5%VT2W08lLeU?+d((*S^-_?deF09%wH6#<};03Z`(h(rKrI{>WDR*FRcSTFz- zW=q650N5=6FiBTtNC2?60Km==3$g$R3;-}uh=nNt1bYBr$Ri_o0EC$U6h`t_Jn<{8 z5a%iY0C<_QJh>z}MS)ugEpZ1|S1ukX&Pf+56gFW3VVXcL!g-k)GJ!M?;PcD?0HBc- z5#WRK{dmp}uFlRjj<yb8E$Y7p{~}^y<NoE(t8hR70O53g(f%wivl@Uq27qn;q9yJG zXkH7Tb@z*AvJXJD0HEpGSMzZAemp!yp^&-R+2!Qq*h<7gTVcvqeg0>{U%*%WZ25jX z{P*?XzTzZ-GF^d31o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcq zjPo+3B8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S z1Au6Q;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO002awfhw>;8}z{#EWidF!3EsG z3;bX<ghC|5!a@*23S@vBa$qT}f<h>U&9EIRU@z1_9W=mEXoiz;4lcq~xDGvV5BgyU zp1~-*fe8db$Osc*A=-!mVv1NJjtCc-h4>-CNCXm#Bp}I%6j35eku^v$Qi@a{RY)E3 zJ#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH!8a<3Q zq36)6Xb;+tK10Vaz~~qUGsJ8#F2=(`u{bOVlVi)VBCHIn#u~6ztOL7=^<&SmcLWlF zMZgI*1b0FpVIDz9SWH+>*hr`#93(Um+6gxa1B6k+CnA%mOSC4s5&6UzVlpv@SV$}* z))J2sFA#f(L&P^E5{W}HC%KRUNwK6<(h|}}(r!{C=`5+6G)NjFlgZj-YqAG9lq?`C z$c5yc<iq4M<QwE6@>>d>VnA`E_*3F2Qp##d8RZb=H01_mm@+|Cqnc9PsG(F5HIG_C zt)aG3uTh7n6Et<2In9F>NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWw<V8OKyGH!<s&=a~<gZ&g?-wkmuTk;)2{N|h#+ z8!9hUsj8-`-l_{#^Hs}KkEvc$eXd4TGgITK3DlOWRjQp(>r)$3XQ?}=hpK0&Z&W{| zep&sA23f;Q!%st`QJ}G3<GjWo3u76xcq}1n4XcKAfi=V?vCY|hb}GA={T;iDJ*ugp zIYTo_Ggq@x^OR;k2jiG=_?&c33Fj!Mm-Bv#-W2aC;wc-ZG)%cMWn62jmY0@Tt4OO+ zt4Hg-Hm>cbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`ytDs%xbip}RzP zziy}Qn4Z2~fSycmr`~zJ=lUFdFa1>gZThG6M+{g7vkW8#+YHVaJjFF}Z#*3@$J_By zLtVo_L#1JrVVB{Ak-5=4qt!-@Mh}c>#$4kh<88)m#-k<%CLtzEP3leVno>=<rYWX7 zOgl`+&CJcB&DNPUn>{htGUuD;o7bD)w_sX$S}eAxwzy?UvgBH(S?;#HZiQMoS*2K2 zT3xe7t(~nU*1N5{rxB;QPLocnp4Ml>u<^FZwyC!nu;thW+pe~4wtZn|Vi#w(#jeBd zlf9FDx_yoPJqHbk*$%56S{;6Kv~m<WRyy9A&YbQ)eZ};a=`Uwk&k)bpGvl@s%PGWZ zol~3BM`ssjxpRZ_h>M9!g3B(KJ}#RZ#@)!h<Vtk)ab4kh()FF2vzx;0sN1jZHtuQe zhuojcG@mJ+Su=Cc!^lJ6QRUG;3!jxRYu~JXPeV_EXSL@eFJmu}SFP8ux21Qg_hIiB zKK4FxpW{B`JU8Al-dSJFH^8^Zx64n%Z=PR;-$Q>R|78Dq|Iq-afF%KE1Brn_fm;Im z_<DRHzm7jT+hz8$+3i7$pt(U6L63s1g5|-jA!x|#kgXy2=a|ls&S?&XP=4sv&<A1W zVT;3l3@3$$g;$0@j&O)r8qqPAHFwe6Lv!Cm`b3sQ-kWDJPdTqGN;N7zsxE3g+Bdp1 zx<AG)W?9VDSe;l&Y)c$DE-J1zZfw5a{O$9H;+^6P<9ipFFUVbRd7;k2^o6GusV)*M zI+j38h)y_^@IeqNs1}SR@)LI@jtY6g9l~cKFVQy9h}c71DjrVqNGeTwlI)SZHF+e( zGo>u$xr8UFki1L{Ox>G0o)(&RAZ;=|I=wN2l97;cLaHH6leTB-XXa*h%dBOEvi`+x zi?=Txl?TadvyiL>SuF~-LZ;|cS}4~l2eM~nS7yJ>iOM;atDY;(?aZ^v+mJV$@1Ote z62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~pu715HdQEGA zUct(O!L<Qv>kCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$+<4_1hi}Ti zncS4LsjI}fWY1>OX6feMEuLErma3QLmkw?X+1j)X-&VBk_4Y;EFPF_I+q;9dL%E~B zJh;4Nr^(LEJ3myURP<E(R5tF?-L+xY_-@he8+*L=H0;&eTfF!EKFPk@RRL8^)n?UY z`$_w=_dl+Qs_FQa`)ysVPHl1R#{<#>{Rblsw%57T)g973R8o)DE9*xN#~;4_o$q%o z4K@u`jhx2fBXC4{<mvYb-}fF3I@)%Od#vFH(;s#nXB{tULYnfLMw?Tb`&(jLx=+kL z(bnqTdi+P*9}k=~JXv{4^Hj-c+UbJRlV|eJjGdL8eSR+a++f?HwtMGe&fjVeZ|}Mg zbm7uP|BL54ygSZZ^0;*JvfJeoSGZT2uR33C>U8Qn{*%*B$Ge=nny$HAYq{=vy|sI0 z_vss+H_qMky?OB#|JK!>IX&II^LlUh#rO5!7TtbwC;iULyV-Xq?ybB}ykGP{?LpZ? z-G|jbTmIbG@7#ZCz;~eY(cDM(28Dyq{*m>M4?_iynUBkc4TkHUI6gT!;y-fz>HMcd z&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M!p0uH$#^p{Ui4P` z?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&GcDTy000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^RV2oe()A>y0J-2easEJ;K` zR5;6Jl3z%jbr{D#&+mQTbB>-f&3W<<%ayjKi&ZjBc2N<@)`~{dMXWB0(ajbV85_gJ zf(EU`iek}4Bt%55ix|sVMm1u8KvB#hnmU~_r<<A{rXG*J@B4e7ZVoQ$bM@tc=leWg z_zHRMKlBH^ZTA9=vi<0{cUBac>Ogd(A5vg_omvd-#L!=(BMV<WoZ(*xK;6sz3?APG zcYj{<QtPf0x0NL}XG1N{ttd-A<a`SdB!tQDd#`;wb$o04?zTZX57i-0wGVOfU@Z-A zOg1*x7hI^iHSWzWD!I_IylNyDC~A}_hzJEOaYTB@a<BFruKaNA1Da3Nv>klxVqhdT zofSj`QA^|)G*lu58>#vhvA)%0Or&dIsb%b)st*L<z&G>V8`ANnOipDbh%_*c7`d6# z21*z~Xd?ovgf>zq(o0?Et~9ti+pljZC~#_KvJhA>u91WRaq|uqBBKP6V0?p-NL59w zrK0w($_m#SDPQ!Z$nhd^JO|f+7k5xca94d2OLJ&sSxlB7F%NtrF@@O7WWlkHSDtor zzD?u;b&KN$*MnHx;JDy9P~G<{4}9__s&MATBV4R+MuA8TjlZ3ye&qZMCUe8ihBnHI zh<cu?V+7B2@Dd)Idw!sNc!0{Geje#KN_uvJ<mw_#=f2j5&U}U!`mVEDwre?2lz#5^ zs=~DE8u46L<8dUGH2CRK$_ILg-^fs~Fh~08D1{62`b0;o6lbP!qwseDs68;cd&{PE zr|zj;C&@w&FXd2}ghUEr!{am^X`!n77bFaE!T`sH05ibj&g(xZC-<)(X3PHm^>MSu zSERHwrmBb$SWVr+)Yk2k^<JQ4@-jvv6nud_Qh(~Kd25GI_WW4}+iN;M{ph=GQ)3I= z-RI|*t*T}hl*<>FgTMR6mP;@FY2{}BeV|SUo=mNk<-XSOHNErw>s{^rR-bu$@aN7= zj~-qXcS2!BA*(Q**BOOl{FggkyHdCJi_Fy>?_K+G+DYwIn8`29DYPg&s4$}7D`fv? zuyJ2sMfJX(I^yrf6u!(~9anf(AqAk&ke}uL0SIb-H!SaDQvd(}07*qoM6N<$g1Ha7 A2LJ#7 literal 0 HcmV?d00001 diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 8f5d07fef..c7ead6fd2 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -18,7 +18,7 @@ }; $.fn.autogrow.resize = function(textarea) { - var lineHeight = parseInt($(textarea).css('line-height')); + var lineHeight = parseInt($(textarea).css('line-height'), 10); var lines = textarea.value.split('\n'); var columns = textarea.cols; var lineCount = 0; @@ -31,22 +31,17 @@ })(jQuery); (function($) { - var commentListEmpty, popup, comp; + var comp; function init() { - initTemplates(); initEvents(); initComparator(); - }; + } function initEvents() { - $('a#comment_close').click(function(event) { - event.preventDefault(); - hide(); - }); - $('form#comment_form').submit(function(event) { - event.preventDefault(); - addComment($('form#comment_form')); + $('a.comment_close').live("click", function(event) { + hide($(this).attr('id').substring(2)); + return false; }); $('.vote').live("click", function() { handleVote($(this)); @@ -60,9 +55,9 @@ closeReply($(this).attr('id').substring(2)); return false; }); - $('a.sort_option').click(function(event) { - event.preventDefault(); + $('a.sort_option').live("click", function(event) { handleReSort($(this)); + return false; }); $('a.show_proposal').live("click", function() { showProposal($(this).attr('id').substring(2)); @@ -92,16 +87,27 @@ deleteComment($(this).attr('id').substring(2)); return false; }); - }; + } - function initTemplates() { - // Create our popup div, the same div is recycled each time comments - // are displayed. - popup = $(renderTemplate(popupTemplate, opts)); - // Setup autogrow on the textareas - popup.find('textarea').autogrow(); - $('body').append(popup); - }; + /* + Set comp, which is a comparator function used for sorting and + inserting comments into the list. + */ + function setComparator(by) { + // If the first three letters are "asc", sort in ascending order + // and remove the prefix. + if (by.substring(0,3) == 'asc') { + var i = by.substring(3); + comp = function(a, b) { return a[i] - b[i]; }; + } else { + // Otherwise sort in descending order. + comp = function(a, b) { return b[by] - a[by]; }; + } + + // Reset link styles and format the selected sort option. + $('a.sel').attr('href', '#').removeClass('sel'); + $('#' + by).removeAttr('href').addClass('sel'); + } /* Create a comp function. If the user has preferences stored in @@ -115,59 +121,46 @@ if (start != -1) { start = start + 7; var end = document.cookie.indexOf(";", start); - if (end == -1) + if (end == -1) { end = document.cookie.length; by = unescape(document.cookie.substring(start, end)); } + } } setComparator(by); - }; + } /* - Show the comments popup window. + Show a comment div. */ - function show(nodeId) { - var id = nodeId.substring(1); - - // Reset the main comment form, and set the value of the parent input. - $('form#comment_form') - .find('textarea,input') - .removeAttr('disabled').end() - .find('input[name="node"]') - .val(id).end() - .find('textarea[name="proposal"]') - .val('') - .hide(); - - // Position the popup and show it. - var clientWidth = document.documentElement.clientWidth; - var popupWidth = $('div.popup_comment').width(); - $('div#focuser').fadeIn('fast'); - $('div.popup_comment') - .css({ - 'top': 100 + $(window).scrollTop(), - 'left': clientWidth / 2 - popupWidth / 2, - 'position': 'absolute' - }) - .fadeIn('fast', function() { - getComments(id); - }); - }; - - /* - Hide the comments popup window. - */ - function hide() { - $('div#focuser').fadeOut('fast'); - $('div.popup_comment').fadeOut('fast', function() { - $('ul#comment_ul').empty(); - $('h3#comment_notification').show(); - $('form#comment_form').find('textarea') - .val('').end() - .find('textarea, input') - .removeAttr('disabled'); + function show(id) { + $('#ao' + id).hide(); + $('#ah' + id).show(); + var context = $.extend({id: id}, opts); + var popup = $(renderTemplate(popupTemplate, context)).hide(); + popup.find('textarea[name="proposal"]').hide(); + var form = popup.find('#cf' + id); + form.submit(function(event) { + event.preventDefault(); + addComment(form); }); - }; + $('#s' + id).after(popup); + popup.slideDown('fast', function() { + getComments(id); + }); + } + + /* + Hide a comment div. + */ + function hide(id) { + $('#ah' + id).hide(); + $('#ao' + id).show(); + var div = $('#sc' + id); + div.slideUp('fast', function() { + div.remove(); + }); + } /* Perform an ajax request to get comments for a node @@ -179,23 +172,23 @@ url: opts.getCommentsURL, data: {node: id}, success: function(data, textStatus, request) { - var ul = $('ul#comment_ul').hide(); - $('form#comment_form') + var ul = $('#cl' + id); + var speed = 100; + $('#cf' + id) .find('textarea[name="proposal"]') .data('source', data.source); - if (data.comments.length == 0) { + if (data.comments.length === 0) { ul.html('<li>No comments yet.</li>'); - commentListEmpty = true; - var speed = 100; + ul.data('empty', true); } else { // If there are comments, sort them and put them in the list. var comments = sortComments(data.comments); - var speed = data.comments.length * 100; + speed = data.comments.length * 100; appendComments(comments, ul); - commentListEmpty = false; + ul.data('empty', false); } - $('h3#comment_notification').slideUp(speed + 200); + $('#cn' + id).slideUp(speed + 200); ul.slideDown(speed); }, error: function(request, textStatus, error) { @@ -203,7 +196,7 @@ }, dataType: 'json' }); - }; + } /* Add a comment via ajax and insert the comment into the comment tree. @@ -212,6 +205,7 @@ // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); var node_id = form.find('input[name="node"]').val(); + var parent_id = form.find('input[name="parent"]').val(); // Send the comment to the server. $.ajax({ @@ -220,7 +214,7 @@ dataType: 'json', data: { node: node_id, - parent: form.find('input[name="parent"]').val(), + parent: parent_id, text: form.find('textarea[name="comment"]').val(), proposal: form.find('textarea[name="proposal"]').val() }, @@ -233,9 +227,10 @@ .val('') .add(form.find('input')) .removeAttr('disabled'); - if (commentListEmpty) { - $('ul#comment_ul').empty(); - commentListEmpty = false; + var ul = $('#cl' + (node_id || parent_id)); + if (ul.data('empty')) { + $(ul).empty(); + ul.data('empty', false); } insertComment(data.comment); }, @@ -244,7 +239,7 @@ showError('Oops, there was a problem adding the comment.'); } }); - }; + } /* Recursively append comments to the main comment list and children @@ -259,7 +254,7 @@ this.children = null; div.data('comment', this); }); - }; + } /* After adding a new comment, it must be inserted in the correct @@ -272,13 +267,8 @@ comment.children = null; div.data('comment', comment); - if (comment.node != null) { - var ul = $('ul#comment_ul'); - var siblings = getChildren(ul); - } else { - var ul = $('#cl' + comment.parent); - var siblings = getChildren(ul); - } + var ul = $('#cl' + (comment.node || comment.parent)); + var siblings = getChildren(ul); var li = $(document.createElement('li')); li.hide(); @@ -298,7 +288,7 @@ // or it is the only comment in the list. ul.append(li.html(div)); li.slideDown('fast'); - }; + } function acceptComment(id) { $.ajax({ @@ -310,9 +300,9 @@ }, error: function(request, textStatus, error) { showError("Oops, there was a problem accepting the comment."); - }, + } }); - }; + } function rejectComment(id) { $.ajax({ @@ -327,9 +317,9 @@ }, error: function(request, textStatus, error) { showError("Oops, there was a problem rejecting the comment."); - }, + } }); - }; + } function deleteComment(id) { $.ajax({ @@ -353,54 +343,62 @@ }, error: function(request, textStatus, error) { showError("Oops, there was a problem deleting the comment."); - }, + } }); - }; + } function showProposal(id) { $('#sp' + id).hide(); $('#hp' + id).show(); $('#pr' + id).slideDown('fast'); - }; + } function hideProposal(id) { $('#hp' + id).hide(); $('#sp' + id).show(); $('#pr' + id).slideUp('fast'); - }; + } function showProposeChange(id) { - $('a.show_propose_change').hide(); - $('a.hide_propose_change').show(); - var textarea = $('textarea[name="proposal"]'); + $('#pc' + id).hide(); + $('#hc' + id).show(); + var textarea = $('#pt' + id); textarea.val(textarea.data('source')); $.fn.autogrow.resize(textarea[0]); textarea.slideDown('fast'); - }; + } function hideProposeChange(id) { - $('a.hide_propose_change').hide(); - $('a.show_propose_change').show(); - var textarea = $('textarea[name="proposal"]'); + $('#hc' + id).hide(); + $('#pc' + id).show(); + var textarea = $('#pt' + id); textarea.val('').removeAttr('disabled'); textarea.slideUp('fast'); - }; + } /* Handle when the user clicks on a sort by link. */ function handleReSort(link) { - setComparator(link.attr('id')); + var classes = link.attr('class').split(/\s+/); + var by = ''; + for (var i=0; i<classes.length; i++) { + if (classes[i] != 'sort_option') { + by = classes[i]; + } + } + setComparator(by); // Save/update the sortBy cookie. var expiration = new Date(); expiration.setDate(expiration.getDate() + 365); - document.cookie= 'sortBy=' + escape(link.attr('id')) + + document.cookie= 'sortBy=' + escape(by) + ';expires=' + expiration.toUTCString(); - var comments = getChildren($('ul#comment_ul'), true); - comments = sortComments(comments); - - appendComments(comments, $('ul#comment_ul').empty()); - }; + $('ul.comment_ul').each(function(index, ul) { + var comments = getChildren($(ul), true); + comments = sortComments(comments); + appendComments(comments, $(ul).empty()); + }); + } /* Function to process a vote when a user clicks an arrow. @@ -414,10 +412,9 @@ var id = link.attr('id'); // If it is an unvote, the new vote value is 0, // Otherwise it's 1 for an upvote, or -1 for a downvote. - if (id.charAt(1) == 'u') { - var value = 0; - } else { - var value = id.charAt(0) == 'u' ? 1 : -1; + var value = 0; + if (id.charAt(1) != 'u') { + value = id.charAt(0) == 'u' ? 1 : -1; } // The data to be sent to the server. var d = { @@ -436,13 +433,13 @@ // If this is not an unvote, and the other vote arrow has // already been pressed, unpress it. - if ((d.value != 0) && (data.vote == d.value * -1)) { + if ((d.value !== 0) && (data.vote === d.value * -1)) { $('#' + (d.value == 1 ? 'd' : 'u') + 'u' + d.comment_id).hide(); $('#' + (d.value == 1 ? 'd' : 'u') + 'v' + d.comment_id).show(); } // Update the comments rating in the local data. - data.rating += (data.vote == 0) ? d.value : (d.value - data.vote); + data.rating += (data.vote === 0) ? d.value : (d.value - data.vote); data.vote = d.value; div.data('comment', data); @@ -459,7 +456,7 @@ showError("Oops, there was a problem casting that vote."); } }); - }; + } /* Open a reply form used to reply to an existing comment. @@ -481,7 +478,7 @@ closeReply(id); }); div.slideDown('fast'); - }; + } /* Close the reply form opened with openReply. @@ -495,7 +492,7 @@ // Swap out the hide link for the reply link $('#cr' + id).hide(); $('#rl' + id).show(); - }; + } /* Recursively sort a tree of comments using the comp comparator. @@ -506,27 +503,7 @@ this.children = sortComments(this.children); }); return comments; - }; - - /* - Set comp, which is a comparator function used for sorting and - inserting comments into the list. - */ - function setComparator(by) { - // If the first three letters are "asc", sort in ascending order - // and remove the prefix. - if (by.substring(0,3) == 'asc') { - var i = by.substring(3); - comp = function(a, b) { return a[i] - b[i]; } - } else { - // Otherwise sort in descending order. - comp = function(a, b) { return b[by] - a[by]; } - } - - // Reset link styles and format the selected sort option. - $('a.sel').attr('href', '#').removeClass('sel'); - $('#' + by).removeAttr('href').addClass('sel'); - }; + } /* Get the children comments from a ul. If recursive is true, @@ -543,7 +520,7 @@ children.push(comment); }); return children; - }; + } /* Create a div to display a comment in. @@ -580,7 +557,8 @@ /* A simple template renderer. Placeholders such as <%id%> are replaced - by context['id']. Items are always escaped. + by context['id'] with items being escaped. Placeholders such as <#id#> + are not escaped. */ function renderTemplate(template, context) { var esc = $(document.createElement('div')); @@ -596,16 +574,16 @@ return template.replace(/<([%#])([\w\.]*)\1>/g, function(){ return handle(arguments[2], arguments[1] == '%' ? true : false); }); - }; + } function showError(message) { - $(document.createElement('div')).attr({class: 'popup_error'}) + $(document.createElement('div')).attr({'class': 'popup_error'}) .append($(document.createElement('h1')).text(message)) .appendTo('body') .fadeIn("slow") .delay(2000) .fadeOut("slow"); - }; + } /* Add a link the user uses to open the comments popup. @@ -613,21 +591,42 @@ $.fn.comment = function() { return this.each(function() { var id = $(this).attr('id').substring(1); - var count = COMMENT_METADATA[id] + var count = COMMENT_METADATA[id]; var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; - $(this).append( - $(document.createElement('a')).attr({href: '#', class: 'sphinx_comment'}) - .append($(document.createElement('img')).attr({ - src: image, - alt: 'comment', - title: title - })) - .click(function(event) { - event.preventDefault(); - show($(this).parent().attr('id')); + $(this) + .append( + $(document.createElement('a')).attr({ + href: '#', + 'class': 'sphinx_comment', + id: 'ao' + id }) - ); + .append($(document.createElement('img')).attr({ + src: image, + alt: 'comment', + title: title + })) + .click(function(event) { + event.preventDefault(); + show($(this).attr('id').substring(2)); + }) + ) + .append( + $(document.createElement('a')).attr({ + href: '#', + 'class': 'sphinx_comment_close hidden', + id: 'ah' + id + }) + .append($(document.createElement('img')).attr({ + src: opts.closeCommentImage, + alt: 'close', + title: 'close' + })) + .click(function(event) { + event.preventDefault(); + hide($(this).attr('id').substring(2)); + }) + ); }); }; @@ -637,8 +636,9 @@ getCommentsURL: '/get_comments', acceptCommentURL: '/accept_comment', rejectCommentURL: '/reject_comment', - rejectCommentURL: '/delete_comment', + deleteCommentURL: '/delete_comment', commentImage: '/static/_static/comment.png', + closeCommentImage: '/static/_static/comment-close.png', loadingImage: '/static/_static/ajax-loader.gif', commentBrightImage: '/static/_static/comment-bright.png', upArrow: '/static/_static/up.png', @@ -715,38 +715,32 @@ </div>'; var popupTemplate = '\ - <div id="popup_template">\ - <div class="popup_comment">\ - <a id="comment_close" href="#">x</a>\ - <h1>Comments</h1>\ - <form method="post" id="comment_form" action="/docs/add_comment">\ - <textarea name="comment" cols="80"></textarea>\ - <p class="propose_button">\ - <a href="#" class="show_propose_change">\ - Propose a change ▹\ - </a>\ - <a href="#" class="hide_propose_change">\ - Propose a change ▿\ - </a>\ - </p>\ - <textarea name="proposal" cols="80" spellcheck="false"></textarea>\ - <input type="submit" value="add comment" id="comment_button" />\ - <input type="hidden" name="node" />\ - <input type="hidden" name="parent" value="" />\ - <p class="sort_options">\ - Sort by:\ - <a href="#" class="sort_option" id="rating">top</a>\ - <a href="#" class="sort_option" id="ascage">newest</a>\ - <a href="#" class="sort_option" id="age">oldest</a>\ - </p>\ - </form>\ - <h3 id="comment_notification">loading comments... <img src="' + - opts.loadingImage + '" alt="" /></h3>\ - <ul id="comment_ul"></ul>\ - </div>\ - </div>\ - <div id="focuser"></div>'; - + <div class="sphinx_comments" id="sc<%id%>">\ + <h1>Comments</h1>\ + <form method="post" id="cf<%id%>" class="comment_form" action="/docs/add_comment">\ + <textarea name="comment" cols="80"></textarea>\ + <p class="propose_button">\ + <a href="#" id="pc<%id%>" class="show_propose_change">\ + Propose a change ▹\ + </a>\ + <a href="#" id="hc<%id%>" class="hide_propose_change">\ + Propose a change ▿\ + </a>\ + </p>\ + <textarea name="proposal" id="pt<%id%>" cols="80" spellcheck="false"></textarea>\ + <input type="submit" value="add comment" />\ + <input type="hidden" name="node" value="<%id%>" />\ + <input type="hidden" name="parent" value="" />\ + <p class="sort_options">\ + Sort by:\ + <a href="#" class="sort_option rating">top</a>\ + <a href="#" class="sort_option ascage">newest</a>\ + <a href="#" class="sort_option age">oldest</a>\ + </p>\ + </form>\ + <h3 id="cn<%id%>">loading comments... <img src="<%loadingImage%>" alt="" /></h3>\ + <ul id="cl<%id%>"></ul>\ + </div>'; $(document).ready(function() { init(); diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index cc065b7f7..152516728 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -371,6 +371,7 @@ class WebSupport(object): if self.staticdir != 'static': static_urls = [ ('commentImage', 'comment.png'), + ('closeCommentImage', 'comment-close.png'), ('loadingImage', 'ajax-loader.gif'), ('commentBrightImage', 'comment-bright.png'), ('upArrow', 'up.png'), From 0ff840dc48023e8a3f2be9aef6f38e375e5a7911 Mon Sep 17 00:00:00 2001 From: Jacob Mason <jacoblmason@gmail.com> Date: Thu, 19 Aug 2010 11:25:02 -0500 Subject: [PATCH 350/744] fix sorting --- sphinx/themes/basic/static/websupport.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index c7ead6fd2..870b0cdc0 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -31,7 +31,7 @@ })(jQuery); (function($) { - var comp; + var comp, by; function init() { initEvents(); @@ -93,7 +93,7 @@ Set comp, which is a comparator function used for sorting and inserting comments into the list. */ - function setComparator(by) { + function setComparator() { // If the first three letters are "asc", sort in ascending order // and remove the prefix. if (by.substring(0,3) == 'asc') { @@ -106,7 +106,7 @@ // Reset link styles and format the selected sort option. $('a.sel').attr('href', '#').removeClass('sel'); - $('#' + by).removeAttr('href').addClass('sel'); + $('a.' + by).removeAttr('href').addClass('sel'); } /* @@ -114,7 +114,7 @@ the sortBy cookie, use those, otherwise use the default. */ function initComparator() { - var by = 'rating'; // Default to sort by rating. + by = 'rating'; // Default to sort by rating. // If the sortBy cookie is set, use that instead. if (document.cookie.length > 0) { var start = document.cookie.indexOf('sortBy='); @@ -127,7 +127,7 @@ } } } - setComparator(by); + setComparator(); } /* @@ -139,6 +139,7 @@ var context = $.extend({id: id}, opts); var popup = $(renderTemplate(popupTemplate, context)).hide(); popup.find('textarea[name="proposal"]').hide(); + popup.find('a.' + by).addClass('sel'); var form = popup.find('#cf' + id); form.submit(function(event) { event.preventDefault(); @@ -381,13 +382,12 @@ */ function handleReSort(link) { var classes = link.attr('class').split(/\s+/); - var by = ''; for (var i=0; i<classes.length; i++) { if (classes[i] != 'sort_option') { by = classes[i]; } } - setComparator(by); + setComparator(); // Save/update the sortBy cookie. var expiration = new Date(); expiration.setDate(expiration.getDate() + 365); @@ -739,7 +739,7 @@ </p>\ </form>\ <h3 id="cn<%id%>">loading comments... <img src="<%loadingImage%>" alt="" /></h3>\ - <ul id="cl<%id%>"></ul>\ + <ul id="cl<%id%>" class="comment_ul"></ul>\ </div>'; $(document).ready(function() { From 4192925b46f66e1cceaa9b53938b1110f3738a28 Mon Sep 17 00:00:00 2001 From: Steve Borho <steve@borho.org> Date: Thu, 19 Aug 2010 11:56:44 -0500 Subject: [PATCH 351/744] htmlhelp: pick up all non java script files in _static and its subdirs "Our" documentation build script copies doxygen generated .html and .gif files into a subdir of _static, which our reST code then links to. This patch allows the nested .gif files to be added to the .hhp project file. --- sphinx/builders/htmlhelp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index e3a58e724..f5d716d98 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -200,7 +200,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): outdir += os.sep olen = len(outdir) for root, dirs, files in os.walk(outdir): - staticdir = (root == path.join(outdir, '_static')) + staticdir = root.startswith(path.join(outdir, '_static')) for fn in files: if (staticdir and not fn.endswith('.js')) or \ fn.endswith('.html'): From 6ffec123a92ffc903af7e60a776f222d6e01cee8 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 19:03:44 +0200 Subject: [PATCH 352/744] Small code style changes, remove unused imports. --- sphinx/builders/__init__.py | 1 + sphinx/util/__init__.py | 12 ++++------- sphinx/versioning.py | 21 ++++++++++--------- sphinx/websupport/__init__.py | 10 +++++---- sphinx/websupport/search/__init__.py | 20 +++++++++--------- sphinx/websupport/search/nullsearch.py | 3 ++- sphinx/websupport/search/whooshsearch.py | 4 ++-- sphinx/websupport/search/xapiansearch.py | 3 +-- sphinx/websupport/storage/db.py | 8 +++---- sphinx/websupport/storage/differ.py | 1 + .../websupport/storage/sqlalchemystorage.py | 14 ++++++++----- 11 files changed, 51 insertions(+), 46 deletions(-) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 328b26683..1938b3615 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -311,6 +311,7 @@ class Builder(object): """ Cleanup any resources. The default implementation does nothing. """ + pass BUILTIN_BUILDERS = { diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index a434f3a82..6a38351f7 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -299,6 +299,7 @@ def format_exception_cut_frames(x=1): res += traceback.format_exception_only(typ, val) return ''.join(res) + class PeekableIterator(object): """ An iterator which wraps any iterable and makes it possible to peek to see @@ -312,24 +313,19 @@ class PeekableIterator(object): return self def next(self): - """ - Returns the next item from the iterator. - """ + """Return the next item from the iterator.""" if self.remaining: return self.remaining.popleft() return self._iterator.next() def push(self, item): - """ - Pushes the `item` on the internal stack, it will be returned on the + """Push the `item` on the internal stack, it will be returned on the next :meth:`next` call. """ self.remaining.append(item) def peek(self): - """ - Returns the next item without changing the state of the iterator. - """ + """Return the next item without changing the state of the iterator.""" item = self.next() self.push(item) return item diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 06acc63dc..430dc1423 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -18,15 +18,14 @@ try: except ImportError: from itertools import zip_longest -from sphinx.util import PeekableIterator # anything below that ratio is considered equal/changed VERSIONING_RATIO = 65 + def add_uids(doctree, condition): - """ - Adds a unique id to every node in the `doctree` which matches the condition - and yields it. + """Add a unique id to every node in the `doctree` which matches the + condition and yield the nodes. :param doctree: A :class:`docutils.nodes.document` instance. @@ -38,10 +37,10 @@ def add_uids(doctree, condition): node.uid = uuid4().hex yield node + def merge_doctrees(old, new, condition): - """ - Merges the `old` doctree with the `new` one while looking at nodes matching - the `condition`. + """Merge the `old` doctree with the `new` one while looking at nodes + matching the `condition`. Each node which replaces another one or has been added to the `new` doctree will be yielded. @@ -102,16 +101,18 @@ def merge_doctrees(old, new, condition): new_node.uid = uuid4().hex yield new_node + def get_ratio(old, new): - """ - Returns a "similiarity ratio" representing the similarity between the two - strings where 0 is equal and anything above less than equal. + """Return a "similiarity ratio" (in percent) representing the similarity + between the two strings where 0 is equal and anything above less than equal. """ if not all([old, new]): return VERSIONING_RATIO return levenshtein_distance(old, new) / (len(old) / 100.0) + def levenshtein_distance(a, b): + """Return the Levenshtein edit distance between two strings *a* and *b*.""" if a == b: return 0 if len(a) < len(b): diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 152516728..f2d2ba451 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -13,17 +13,17 @@ import sys import cPickle as pickle import posixpath from os import path -from datetime import datetime from jinja2 import Environment, FileSystemLoader from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir from sphinx.util.jsonimpl import dumps as dump_json -from sphinx.websupport.search import BaseSearch, search_adapters +from sphinx.websupport.search import BaseSearch, SEARCH_ADAPTERS from sphinx.websupport.storage import StorageBackend from sphinx.websupport.errors import * + class WebSupportApp(Sphinx): def __init__(self, *args, **kwargs): self.staticdir = kwargs.pop('staticdir', None) @@ -32,6 +32,7 @@ class WebSupportApp(Sphinx): self.storage = kwargs.pop('storage', None) Sphinx.__init__(self, *args, **kwargs) + class WebSupport(object): """The main API class for the web support package. All interactions with the web support package should occur through this class. @@ -82,7 +83,7 @@ class WebSupport(object): if isinstance(search, BaseSearch): self.search = search else: - mod, cls = search_adapters[search or 'null'] + mod, cls = SEARCH_ADAPTERS[search or 'null'] mod = 'sphinx.websupport.search.' + mod SearchClass = getattr(__import__(mod, None, None, [cls]), cls) search_path = path.join(self.datadir, 'search') @@ -390,7 +391,8 @@ class WebSupport(object): :param username: The username of the user making the request. :param moderator: Whether the user making the request is a moderator. """ - parts = [self.base_comment_opts] + # XXX parts is not used? + #parts = [self.base_comment_opts] rv = self.base_comment_opts.copy() if username: rv.update({ diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index cb66618b5..0cba0f77f 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -11,6 +11,7 @@ import re + class BaseSearch(object): def __init__(self, path): pass @@ -63,11 +64,10 @@ class BaseSearch(object): def query(self, q): """Called by the web support api to get search results. This method - compiles the regular expression to be used when - :meth:`extracting context <extract_context>`, then calls - :meth:`handle_query`. You won't want to override this unless you - don't want to use the included :meth:`extract_context` method. - Override :meth:`handle_query` instead. + compiles the regular expression to be used when :meth:`extracting + context <extract_context>`, then calls :meth:`handle_query`. You + won't want to override this unless you don't want to use the included + :meth:`extract_context` method. Override :meth:`handle_query` instead. :param q: the search query string. """ @@ -93,7 +93,7 @@ class BaseSearch(object): raise NotImplementedError() def extract_context(self, text, length=240): - """Extract the context for the search query from the documents + """Extract the context for the search query from the document's full `text`. :param text: the full text of the document to create the context for @@ -113,9 +113,9 @@ class BaseSearch(object): except TypeError: return context -# The build in search adapters. -search_adapters = { +# The built-in search adapters. +SEARCH_ADAPTERS = { 'xapian': ('xapiansearch', 'XapianSearch'), 'whoosh': ('whooshsearch', 'WhooshSearch'), - 'null': ('nullsearch', 'NullSearch') - } + 'null': ('nullsearch', 'NullSearch'), +} diff --git a/sphinx/websupport/search/nullsearch.py b/sphinx/websupport/search/nullsearch.py index 743983c48..fd6d4dcfa 100644 --- a/sphinx/websupport/search/nullsearch.py +++ b/sphinx/websupport/search/nullsearch.py @@ -10,7 +10,8 @@ """ from sphinx.websupport.search import BaseSearch -from sphinx.websupport.errors import * +from sphinx.websupport.errors import NullSearchException + class NullSearch(BaseSearch): """A search adapter that does nothing. Used when no search adapter diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 0f4635314..d395dcd7f 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -10,13 +10,13 @@ """ from whoosh import index -from whoosh.fields import Schema, ID, TEXT, STORED +from whoosh.fields import Schema, ID, TEXT from whoosh.analysis import StemmingAnalyzer -from whoosh import highlight from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch + class WhooshSearch(BaseSearch): """The whoosh search adapter for sphinx web support.""" diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py index 16c7e2b1b..b0475435a 100644 --- a/sphinx/websupport/search/xapiansearch.py +++ b/sphinx/websupport/search/xapiansearch.py @@ -9,13 +9,12 @@ :license: BSD, see LICENSE for details. """ -from os import path - import xapian from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch + class XapianSearch(BaseSearch): # Adapted from the GSOC 2009 webapp project. diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/db.py index be81a3334..bf7b83dfd 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/db.py @@ -11,11 +11,9 @@ """ from datetime import datetime -from uuid import uuid4 -from sqlalchemy import Column, Integer, Text, String, Boolean, ForeignKey,\ - DateTime -from sqlalchemy.schema import UniqueConstraint +from sqlalchemy import Column, Integer, Text, String, Boolean, \ + ForeignKey, DateTime from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relation, sessionmaker, aliased @@ -98,6 +96,7 @@ class Node(Base): self.document = document self.source = source + class Comment(Base): """An individual Comment being stored.""" __tablename__ = db_prefix + 'comments' @@ -188,6 +187,7 @@ class Comment(Base): return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt + class CommentVote(Base): """A vote a user has made on a Comment.""" __tablename__ = db_prefix + 'commentvote' diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py index 8d6c4a497..d52250718 100644 --- a/sphinx/websupport/storage/differ.py +++ b/sphinx/websupport/storage/differ.py @@ -13,6 +13,7 @@ import re from cgi import escape from difflib import Differ + class CombinedHtmlDiff(object): """Create an HTML representation of the differences between two pieces of text. diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index c775f3bb4..ba011c06d 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -14,16 +14,19 @@ from datetime import datetime from sqlalchemy.orm import aliased from sqlalchemy.sql import func -from sphinx.websupport.errors import * +from sphinx.websupport.errors import CommentNotAllowedError, \ + UserNotAuthorizedError from sphinx.websupport.storage import StorageBackend -from sphinx.websupport.storage.db import Base, Node, Comment, CommentVote,\ - Session +from sphinx.websupport.storage.db import Base, Node, Comment, \ + CommentVote, Session from sphinx.websupport.storage.differ import CombinedHtmlDiff + class SQLAlchemyStorage(StorageBackend): - """A :class:`~sphinx.websupport.storage.StorageBackend` using - SQLAlchemy. """ + A :class:`.StorageBackend` using SQLAlchemy. + """ + def __init__(self, engine): self.engine = engine Base.metadata.bind = engine @@ -147,6 +150,7 @@ class SQLAlchemyStorage(StorageBackend): def accept_comment(self, comment_id): session = Session() + # XXX assignment to "comment" needed? comment = session.query(Comment).filter( Comment.id == comment_id).update( {Comment.displayed: True}) From 3048c13c63d0fa4a4be0bf408797e5c536e04853 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 19:15:14 +0200 Subject: [PATCH 353/744] Adapt style for websupport docs. --- doc/web/api.rst | 65 ++++---- doc/web/quickstart.rst | 325 ++++++++++++++++++------------------ doc/web/searchadapters.rst | 43 +++-- doc/web/storagebackends.rst | 39 ++--- doc/websupport.rst | 13 +- 5 files changed, 239 insertions(+), 246 deletions(-) diff --git a/doc/web/api.rst b/doc/web/api.rst index b63e68643..070cd3a21 100644 --- a/doc/web/api.rst +++ b/doc/web/api.rst @@ -7,48 +7,47 @@ The WebSupport Class .. class:: WebSupport - The main API class for the web support package. All interactions - with the web support package should occur through this class. + The main API class for the web support package. All interactions with the + web support package should occur through this class. - The class takes the following keyword arguments: + The class takes the following keyword arguments: - srcdir - The directory containing reStructuredText source files. + srcdir + The directory containing reStructuredText source files. - builddir - The directory that build data and static files should be placed in. - This should be used when creating a :class:`WebSupport` object that - will be used to build data. + builddir + The directory that build data and static files should be placed in. This + should be used when creating a :class:`WebSupport` object that will be + used to build data. - datadir: - The directory that the web support data is in. This should be used - when creating a :class:`WebSupport` object that will be used to - retrieve data. + datadir + The directory that the web support data is in. This should be used when + creating a :class:`WebSupport` object that will be used to retrieve data. - search: - This may contain either a string (e.g. 'xapian') referencing a - built-in search adapter to use, or an instance of a subclass of - :class:`~sphinx.websupport.search.BaseSearch`. + search + This may contain either a string (e.g. 'xapian') referencing a built-in + search adapter to use, or an instance of a subclass of + :class:`~.search.BaseSearch`. - storage: - This may contain either a string representing a database uri, or an - instance of a subclass of - :class:`~sphinx.websupport.storage.StorageBackend`. If this is not - provided a new sqlite database will be created. + storage + This may contain either a string representing a database uri, or an + instance of a subclass of :class:`~.storage.StorageBackend`. If this is + not provided, a new sqlite database will be created. - moderation_callback: - A callable to be called when a new comment is added that is not - displayed. It must accept one argument: a dict representing the - comment that was added. + moderation_callback + A callable to be called when a new comment is added that is not + displayed. It must accept one argument: a dictionary representing the + comment that was added. - staticdir: - If static files are served from a location besides "/static", this - should be a string with the name of that location - (e.g. '/static_files'). + staticdir + If static files are served from a location besides ``'/static'``, this + should be a string with the name of that location + (e.g. ``'/static_files'``). + + docroot + If the documentation is not served from the base path of a URL, this + should be a string specifying that path (e.g. ``'docs'``). - docroot: - If the documentation is not served from the base path of a URL, this - should be a string specifying that path (e.g. 'docs') Methods ~~~~~~~ diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst index 61a432a86..0627c9c33 100644 --- a/doc/web/quickstart.rst +++ b/doc/web/quickstart.rst @@ -6,52 +6,49 @@ Web Support Quick Start Building Documentation Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To make use of the web support package in your application you'll -need to build the data it uses. This data includes pickle files representing -documents, search indices, and node data that is used to track where -comments and other things are in a document. To do this you will need -to create an instance of the :class:`~sphinx.websupport.WebSupport` -class and call it's :meth:`~sphinx.websupport.WebSupport.build` method:: +To make use of the web support package in your application you'll need to build +the data it uses. This data includes pickle files representing documents, +search indices, and node data that is used to track where comments and other +things are in a document. To do this you will need to create an instance of the +:class:`~.WebSupport` class and call its :meth:`~.WebSupport.build` method:: - from sphinx.websupport import WebSupport + from sphinx.websupport import WebSupport - support = WebSupport(srcdir='/path/to/rst/sources/', - builddir='/path/to/build/outdir', - search='xapian') + support = WebSupport(srcdir='/path/to/rst/sources/', + builddir='/path/to/build/outdir', + search='xapian') - support.build() + support.build() -This will read reStructuredText sources from `srcdir` and place the -necessary data in `builddir`. The `builddir` will contain two -sub-directories. One named "data" that contains all the data needed -to display documents, search through documents, and add comments to -documents. The other directory will be called "static" and contains static -files that should be served from "/static". +This will read reStructuredText sources from `srcdir` and place the necessary +data in `builddir`. The `builddir` will contain two sub-directories: one named +"data" that contains all the data needed to display documents, search through +documents, and add comments to documents. The other directory will be called +"static" and contains static files that should be served from "/static". .. note:: - If you wish to serve static files from a path other than "/static", you - can do so by providing the *staticdir* keyword argument when creating - the :class:`~sphinx.websupport.api.WebSupport` object. + If you wish to serve static files from a path other than "/static", you can + do so by providing the *staticdir* keyword argument when creating the + :class:`~.WebSupport` object. + Integrating Sphinx Documents Into Your Webapp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Now that the data is built, it's time to do something useful with it. -Start off by creating a :class:`~sphinx.websupport.WebSupport` object -for your application:: +Now that the data is built, it's time to do something useful with it. Start off +by creating a :class:`~.WebSupport` object for your application:: - from sphinx.websupport import WebSupport + from sphinx.websupport import WebSupport - support = WebSupport(datadir='/path/to/the/data', - search='xapian') + support = WebSupport(datadir='/path/to/the/data', + search='xapian') -You'll only need one of these for each set of documentation you will be -working with. You can then call it's -:meth:`~sphinx.websupport.WebSupport.get_document` method to access +You'll only need one of these for each set of documentation you will be working +with. You can then call it's :meth:`~.WebSupport.get_document` method to access individual documents:: - contents = support.get_document('contents') + contents = support.get_document('contents') This will return a dictionary containing the following items: @@ -62,132 +59,132 @@ This will return a dictionary containing the following items: * **css**: Links to css files used by Sphinx * **js**: Javascript containing comment options -This dict can then be used as context for templates. The goal is to be -easy to integrate with your existing templating system. An example using -`Jinja2 <http://jinja.pocoo.org/2/>`_ is: +This dict can then be used as context for templates. The goal is to be easy to +integrate with your existing templating system. An example using `Jinja2 +<http://jinja.pocoo.org/2/>`_ is: .. sourcecode:: html+jinja - {%- extends "layout.html" %} + {%- extends "layout.html" %} - {%- block title %} - {{ document.title }} - {%- endblock %} + {%- block title %} + {{ document.title }} + {%- endblock %} - {% block css %} - {{ super() }} - {{ document.css|safe }} - <link rel="stylesheet" href="/static/websupport-custom.css" type="text/css"> - {% endblock %} + {% block css %} + {{ super() }} + {{ document.css|safe }} + <link rel="stylesheet" href="/static/websupport-custom.css" type="text/css"> + {% endblock %} - {%- block js %} - {{ super() }} - {{ document.js|safe }} - {%- endblock %} + {%- block js %} + {{ super() }} + {{ document.js|safe }} + {%- endblock %} - {%- block relbar %} - {{ document.relbar|safe }} - {%- endblock %} + {%- block relbar %} + {{ document.relbar|safe }} + {%- endblock %} - {%- block body %} - {{ document.body|safe }} - {%- endblock %} + {%- block body %} + {{ document.body|safe }} + {%- endblock %} + + {%- block sidebar %} + {{ document.sidebar|safe }} + {%- endblock %} - {%- block sidebar %} - {{ document.sidebar|safe }} - {%- endblock %} Authentication -------------- -To use certain features such as voting it must be possible to authenticate -users. The details of the authentication are left to your application. -Once a user has been authenticated you can pass the user's details to certain -:class:`~sphinx.websupport.WebSupport` methods using the *username* and -*moderator* keyword arguments. The web support package will store the -username with comments and votes. The only caveat is that if you allow users -to change their username you must update the websupport package's data:: +To use certain features such as voting, it must be possible to authenticate +users. The details of the authentication are left to your application. Once a +user has been authenticated you can pass the user's details to certain +:class:`~.WebSupport` methods using the *username* and *moderator* keyword +arguments. The web support package will store the username with comments and +votes. The only caveat is that if you allow users to change their username you +must update the websupport package's data:: - support.update_username(old_username, new_username) + support.update_username(old_username, new_username) *username* should be a unique string which identifies a user, and *moderator* -should be a boolean representing whether the user has moderation -privilieges. The default value for *moderator* is *False*. +should be a boolean representing whether the user has moderation privilieges. +The default value for *moderator* is *False*. -An example `Flask <http://flask.pocoo.org/>`_ function that checks whether -a user is logged in and then retrieves a document is:: +An example `Flask <http://flask.pocoo.org/>`_ function that checks whether a +user is logged in and then retrieves a document is:: - from sphinx.websupport.errors import * + from sphinx.websupport.errors import * - @app.route('/<path:docname>') - def doc(docname): - username = g.user.name if g.user else '' - moderator = g.user.moderator if g.user else False - try: - document = support.get_document(docname, username, moderator) - except DocumentNotFoundError: - abort(404) - return render_template('doc.html', document=document) + @app.route('/<path:docname>') + def doc(docname): + username = g.user.name if g.user else '' + moderator = g.user.moderator if g.user else False + try: + document = support.get_document(docname, username, moderator) + except DocumentNotFoundError: + abort(404) + return render_template('doc.html', document=document) -The first thing to notice is that the *docname* is just the request path. -This makes accessing the correct document easy from a single view. -If the user is authenticated then the username and moderation status are -passed along with the docname to -:meth:`~sphinx.websupport.WebSupport.get_document`. The web support package -will then add this data to the COMMENT_OPTIONS that are used in the template. +The first thing to notice is that the *docname* is just the request path. This +makes accessing the correct document easy from a single view. If the user is +authenticated, then the username and moderation status are passed along with the +docname to :meth:`~.WebSupport.get_document`. The web support package will then +add this data to the ``COMMENT_OPTIONS`` that are used in the template. .. note:: - This only works works if your documentation is served from your - document root. If it is served from another directory, you will - need to prefix the url route with that directory, and give the `docroot` - keyword argument when creating the web support object:: + This only works works if your documentation is served from your + document root. If it is served from another directory, you will + need to prefix the url route with that directory, and give the `docroot` + keyword argument when creating the web support object:: - support = WebSupport(..., docroot='docs') + support = WebSupport(..., docroot='docs') + + @app.route('/docs/<path:docname>') - @app.route('/docs/<path:docname>') Performing Searches ~~~~~~~~~~~~~~~~~~~ -To use the search form built-in to the Sphinx sidebar, create a function -to handle requests to the url 'search' relative to the documentation root. -The user's search query will be in the GET parameters, with the key `q`. -Then use the :meth:`~sphinx.websupport.WebSupport.get_search_results` method -to retrieve search results. In `Flask <http://flask.pocoo.org/>`_ that -would be like this:: +To use the search form built-in to the Sphinx sidebar, create a function to +handle requests to the url 'search' relative to the documentation root. The +user's search query will be in the GET parameters, with the key `q`. Then use +the :meth:`~sphinx.websupport.WebSupport.get_search_results` method to retrieve +search results. In `Flask <http://flask.pocoo.org/>`_ that would be like this:: - @app.route('/search') - def search(): - q = request.args.get('q') - document = support.get_search_results(q) - return render_template('doc.html', document=document) + @app.route('/search') + def search(): + q = request.args.get('q') + document = support.get_search_results(q) + return render_template('doc.html', document=document) + +Note that we used the same template to render our search results as we did to +render our documents. That's because :meth:`~.WebSupport.get_search_results` +returns a context dict in the same format that :meth:`~.WebSupport.get_document` +does. -Note that we used the same template to render our search results as we -did to render our documents. That's because -:meth:`~sphinx.websupport.WebSupport.get_search_results` returns a context -dict in the same format that -:meth:`~sphinx.websupport.WebSupport.get_document` does. Comments & Proposals ~~~~~~~~~~~~~~~~~~~~ -Now that this is done it's time to define the functions that handle -the AJAX calls from the script. You will need three functions. The first -function is used to add a new comment, and will call the web support method -:meth:`~sphinx.websupport.WebSupport.add_comment`:: +Now that this is done it's time to define the functions that handle the AJAX +calls from the script. You will need three functions. The first function is +used to add a new comment, and will call the web support method +:meth:`~.WebSupport.add_comment`:: - @app.route('/docs/add_comment', methods=['POST']) - def add_comment(): - parent_id = request.form.get('parent', '') - node_id = request.form.get('node', '') - text = request.form.get('text', '') - proposal = request.form.get('proposal', '') - username = g.user.name if g.user is not None else 'Anonymous' - comment = support.add_comment(text, node_id='node_id', - parent_id='parent_id', - username=username, proposal=proposal) - return jsonify(comment=comment) + @app.route('/docs/add_comment', methods=['POST']) + def add_comment(): + parent_id = request.form.get('parent', '') + node_id = request.form.get('node', '') + text = request.form.get('text', '') + proposal = request.form.get('proposal', '') + username = g.user.name if g.user is not None else 'Anonymous' + comment = support.add_comment(text, node_id='node_id', + parent_id='parent_id', + username=username, proposal=proposal) + return jsonify(comment=comment) You'll notice that both a `parent_id` and `node_id` are sent with the request. If the comment is being attached directly to a node, `parent_id` @@ -204,63 +201,61 @@ specific node, and is aptly named data = support.get_data(parent_id, user_id) return jsonify(**data) -The final function that is needed will call -:meth:`~sphinx.websupport.WebSupport.process_vote`, and will handle user -votes on comments:: +The final function that is needed will call :meth:`~.WebSupport.process_vote`, +and will handle user votes on comments:: + + @app.route('/docs/process_vote', methods=['POST']) + def process_vote(): + if g.user is None: + abort(401) + comment_id = request.form.get('comment_id') + value = request.form.get('value') + if value is None or comment_id is None: + abort(400) + support.process_vote(comment_id, g.user.id, value) + return "success" - @app.route('/docs/process_vote', methods=['POST']) - def process_vote(): - if g.user is None: - abort(401) - comment_id = request.form.get('comment_id') - value = request.form.get('value') - if value is None or comment_id is None: - abort(400) - support.process_vote(comment_id, g.user.id, value) - return "success" Comment Moderation ~~~~~~~~~~~~~~~~~~ -By default all comments added through -:meth:`~sphinx.websupport.WebSupport.add_comment` are automatically -displayed. If you wish to have some form of moderation, you can pass -the `displayed` keyword argument:: +By default, all comments added through :meth:`~.WebSupport.add_comment` are +automatically displayed. If you wish to have some form of moderation, you can +pass the `displayed` keyword argument:: - comment = support.add_comment(text, node_id='node_id', - parent_id='parent_id', - username=username, proposal=proposal, - displayed=False) + comment = support.add_comment(text, node_id='node_id', + parent_id='parent_id', + username=username, proposal=proposal, + displayed=False) -You can then create two new views to handle the moderation of comments. The -first will be called when a moderator decides a comment should be accepted -and displayed:: +You can then create two new views to handle the moderation of comments. The +first will be called when a moderator decides a comment should be accepted and +displayed:: - @app.route('/docs/accept_comment', methods=['POST']) - def accept_comment(): - moderator = g.user.moderator if g.user else False - comment_id = request.form.get('id') - support.accept_comment(comment_id, moderator=moderator) - return 'OK' + @app.route('/docs/accept_comment', methods=['POST']) + def accept_comment(): + moderator = g.user.moderator if g.user else False + comment_id = request.form.get('id') + support.accept_comment(comment_id, moderator=moderator) + return 'OK' The next is very similar, but used when rejecting a comment:: - @app.route('/docs/reject_comment', methods=['POST']) - def reject_comment(): - moderator = g.user.moderator if g.user else False - comment_id = request.form.get('id') - support.reject_comment(comment_id, moderator=moderator) - return 'OK' + @app.route('/docs/reject_comment', methods=['POST']) + def reject_comment(): + moderator = g.user.moderator if g.user else False + comment_id = request.form.get('id') + support.reject_comment(comment_id, moderator=moderator) + return 'OK' -To perform a custom action (such as emailing a moderator) when a new comment -is added but not displayed, you can pass callable to the -:class:`~sphinx.websupport.WebSupport` class when instantiating your support -object:: +To perform a custom action (such as emailing a moderator) when a new comment is +added but not displayed, you can pass callable to the :class:`~.WebSupport` +class when instantiating your support object:: - def moderation_callback(comment): - """Do something...""" + def moderation_callback(comment): + """Do something...""" - support = WebSupport(..., moderation_callback=moderation_callback) + support = WebSupport(..., moderation_callback=moderation_callback) -The moderation callback must take one argument, which will be the same -comment dict that is returned by add_comment. +The moderation callback must take one argument, which will be the same comment +dict that is returned by :meth:`add_comment`. diff --git a/doc/web/searchadapters.rst b/doc/web/searchadapters.rst index a84aa8da1..7d8634f71 100644 --- a/doc/web/searchadapters.rst +++ b/doc/web/searchadapters.rst @@ -6,41 +6,40 @@ Search Adapters =============== To create a custom search adapter you will need to subclass the -:class:`~BaseSearch` class. Then create an instance of the new class -and pass that as the `search` keyword argument when you create the -:class:`~sphinx.websupport.WebSupport` object:: +:class:`BaseSearch` class. Then create an instance of the new class and pass +that as the `search` keyword argument when you create the :class:`~.WebSupport` +object:: - support = Websupport(srcdir=srcdir, - builddir=builddir, - search=MySearch()) + support = WebSupport(srcdir=srcdir, + builddir=builddir, + search=MySearch()) -For more information about creating a custom search adapter, please see -the documentation of the :class:`BaseSearch` class below. +For more information about creating a custom search adapter, please see the +documentation of the :class:`BaseSearch` class below. .. class:: BaseSearch - Defines an interface for search adapters. + Defines an interface for search adapters. + BaseSearch Methods ~~~~~~~~~~~~~~~~~~ - The following methods are defined in the BaseSearch class. Some methods - do not need to be overridden, but some ( - :meth:`~sphinx.websupport.search.BaseSearch.add_document` and - :meth:`~sphinx.websupport.search.BaseSearch.handle_query`) must be - overridden in your subclass. For a working example, look at the - built-in adapter for whoosh. + The following methods are defined in the BaseSearch class. Some methods do + not need to be overridden, but some (:meth:`~BaseSearch.add_document` and + :meth:`~BaseSearch.handle_query`) must be overridden in your subclass. For a + working example, look at the built-in adapter for whoosh. -.. automethod:: sphinx.websupport.search.BaseSearch.init_indexing +.. automethod:: BaseSearch.init_indexing -.. automethod:: sphinx.websupport.search.BaseSearch.finish_indexing +.. automethod:: BaseSearch.finish_indexing -.. automethod:: sphinx.websupport.search.BaseSearch.feed +.. automethod:: BaseSearch.feed -.. automethod:: sphinx.websupport.search.BaseSearch.add_document +.. automethod:: BaseSearch.add_document -.. automethod:: sphinx.websupport.search.BaseSearch.query +.. automethod:: BaseSearch.query -.. automethod:: sphinx.websupport.search.BaseSearch.handle_query +.. automethod:: BaseSearch.handle_query -.. automethod:: sphinx.websupport.search.BaseSearch.extract_context +.. automethod:: BaseSearch.extract_context diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst index 6b701ea38..a46ea9e55 100644 --- a/doc/web/storagebackends.rst +++ b/doc/web/storagebackends.rst @@ -6,40 +6,41 @@ Storage Backends ================ To create a custom storage backend you will need to subclass the -:class:`~StorageBackend` class. Then create an instance of the new class -and pass that as the `storage` keyword argument when you create the -:class:`~sphinx.websupport.WebSupport` object:: +:class:`StorageBackend` class. Then create an instance of the new class and +pass that as the `storage` keyword argument when you create the +:class:`~.WebSupport` object:: - support = Websupport(srcdir=srcdir, - builddir=builddir, - storage=MyStorage()) + support = WebSupport(srcdir=srcdir, + builddir=builddir, + storage=MyStorage()) -For more information about creating a custom storage backend, please see -the documentation of the :class:`StorageBackend` class below. +For more information about creating a custom storage backend, please see the +documentation of the :class:`StorageBackend` class below. .. class:: StorageBackend - Defines an interface for storage backends. + Defines an interface for storage backends. + StorageBackend Methods ~~~~~~~~~~~~~~~~~~~~~~ -.. automethod:: sphinx.websupport.storage.StorageBackend.pre_build +.. automethod:: StorageBackend.pre_build -.. automethod:: sphinx.websupport.storage.StorageBackend.add_node +.. automethod:: StorageBackend.add_node -.. automethod:: sphinx.websupport.storage.StorageBackend.post_build +.. automethod:: StorageBackend.post_build -.. automethod:: sphinx.websupport.storage.StorageBackend.add_comment +.. automethod:: StorageBackend.add_comment -.. automethod:: sphinx.websupport.storage.StorageBackend.delete_comment +.. automethod:: StorageBackend.delete_comment -.. automethod:: sphinx.websupport.storage.StorageBackend.get_data +.. automethod:: StorageBackend.get_data -.. automethod:: sphinx.websupport.storage.StorageBackend.process_vote +.. automethod:: StorageBackend.process_vote -.. automethod:: sphinx.websupport.storage.StorageBackend.update_username +.. automethod:: StorageBackend.update_username -.. automethod:: sphinx.websupport.storage.StorageBackend.accept_comment +.. automethod:: StorageBackend.accept_comment -.. automethod:: sphinx.websupport.storage.StorageBackend.reject_comment +.. automethod:: StorageBackend.reject_comment diff --git a/doc/websupport.rst b/doc/websupport.rst index 4d743719d..7dbea03b6 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -3,13 +3,12 @@ Sphinx Web Support ================== -Sphinx provides a way to easily integrate Sphinx documentation -into your web application. To learn more read the -:ref:`websupportquickstart`. +Sphinx provides a Python API to easily integrate Sphinx documentation into your +web application. To learn more read the :ref:`websupportquickstart`. .. toctree:: - web/quickstart - web/api - web/searchadapters - web/storagebackends + web/quickstart + web/api + web/searchadapters + web/storagebackends From c170c5f5daba39de869ddb62759b91d503d2d2e2 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 19:19:12 +0200 Subject: [PATCH 354/744] Code style nits. --- sphinx/builders/intl.py | 2 -- sphinx/environment.py | 8 ++++---- sphinx/locale/__init__.py | 6 +++--- sphinx/util/nodes.py | 1 + 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index de147c821..41943b9be 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -13,8 +13,6 @@ import collections from datetime import datetime from os import path -from docutils import nodes - from sphinx.builders import Builder from sphinx.util.nodes import extract_messages from sphinx.util.osutil import SEP diff --git a/sphinx/environment.py b/sphinx/environment.py index 6339675b5..f5d77a1e6 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -184,6 +184,7 @@ class CitationReferences(Transform): refnode += nodes.Text('[' + cittext + ']') citnode.parent.replace(citnode, refnode) + class Locale(Transform): """ Replace translatable nodes with their translated doctree. @@ -207,10 +208,11 @@ class Locale(Transform): parser = RSTParser() for node, msg in extract_messages(self.document): - ctx = node.parent + # XXX ctx not used + #ctx = node.parent patch = new_document(source, settings) msgstr = catalog.ugettext(msg) - #XXX add marker to untranslated parts + # XXX add marker to untranslated parts if not msgstr or msgstr == msg: # as-of-yet untranslated continue parser.parse(msgstr, patch) @@ -221,7 +223,6 @@ class Locale(Transform): node.children = patch.children - class SphinxStandaloneReader(standalone.Reader): """ Add our own transforms. @@ -792,7 +793,6 @@ class BuildEnvironment: if node['level'] < filterlevel: node.parent.remove(node) - def process_dependencies(self, docname, doctree): """ Process docutils-generated dependency info. diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index 682fdc6f0..472fbed61 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -191,9 +191,9 @@ else: def init(locale_dirs, language, catalog='sphinx'): """ Look for message catalogs in `locale_dirs` and *ensure* that there is at - least a NullTranslations catalog set in `translators`. If called multiple - times or several ``.mo`` files are found their contents are merged - together (thus making `init` reentrable). + least a NullTranslations catalog set in `translators`. If called multiple + times or if several ``.mo`` files are found, their contents are merged + together (thus making ``init`` reentrable). """ global translators translator = translators.get(catalog) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index c2f96f078..9ecf0d4ab 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -28,6 +28,7 @@ def extract_messages(doctree): if isinstance(node, (nodes.Invisible, nodes.Inline)): continue # <field_name>orphan</field_name> + # XXX ignore all metadata (== docinfo) if isinstance(node, nodes.field_name) and node.children[0] == 'orphan': continue msg = node.rawsource.replace('\n', ' ').strip() From 7174c537c87b2b79c238cd32191d4bf002aa09aa Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 19:23:05 +0200 Subject: [PATCH 355/744] Update versionadded for SoC projects. --- doc/intl.rst | 13 ++++++++----- doc/websupport.rst | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/doc/intl.rst b/doc/intl.rst index 8722177de..d8c5ec78c 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -3,9 +3,12 @@ Internationalization ==================== -.. versionadded:: 1.XXX +.. versionadded:: 1.1 -Complementary to translations provided for internal messages such as navigation -bars Sphinx provides mechanisms facilitating *document* translations in itself. -It relies on the existing configuration values :confval:`language` and -:confval:`locale_dirs`. +Complementary to translations provided for Sphinx-generated messages such as +navigation bars Sphinx provides mechanisms facilitating *document* translations +in itself. It relies on the existing configuration values :confval:`language` +and :confval:`locale_dirs`. + + +.. XXX write more! diff --git a/doc/websupport.rst b/doc/websupport.rst index 7dbea03b6..3ccae2467 100644 --- a/doc/websupport.rst +++ b/doc/websupport.rst @@ -3,6 +3,8 @@ Sphinx Web Support ================== +.. versionadded:: 1.1 + Sphinx provides a Python API to easily integrate Sphinx documentation into your web application. To learn more read the :ref:`websupportquickstart`. From 436262679e9e92b00f716ab3037f20242ec67b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 21 Aug 2010 19:54:14 +0200 Subject: [PATCH 356/744] Added missing import --- sphinx/builders/intl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 49993b802..1572747ed 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -14,6 +14,8 @@ from codecs import open from datetime import datetime from collections import defaultdict +from docutils import nodes + from sphinx.builders import Builder from sphinx.builders.versioning import VersioningBuilderMixin from sphinx.util.nodes import extract_messages From e239e5168557e011e36c122fbb678080123390e0 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 20:30:11 +0200 Subject: [PATCH 357/744] Give an explicit locale so that the output file name of msginit is deterministic. --- tests/test_build_gettext.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 032911111..ba2440fd4 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -37,7 +37,8 @@ def test_gettext(app): os.chdir(app.outdir) try: try: - p = Popen(['msginit', '--no-translator', '-i', 'markup.pot'], + p = Popen(['msginit', '--no-translator', '-i', 'markup.pot', + '--locale', 'en_US'], stdout=PIPE, stderr=PIPE) except OSError: return # most likely msginit was not found @@ -74,15 +75,6 @@ def test_gettext(app): def test_all(app): app.builder.build_all() -@with_app(buildername='text', - confoverrides={'language': 'xx', 'locale_dirs': ['.']}) -def test_patch(app): - app.builder.build(['bom']) - result = (app.outdir / 'bom.txt').text(encoding='utf-8') - expect = (u"\nDatei mit UTF-8" - u"\n***************\n" # underline matches new translation - u"\nThis file has umlauts: äöü.\n") - assert result == expect def setup_patch(): (test_root / 'xx' / 'LC_MESSAGES').makedirs() @@ -103,5 +95,16 @@ def setup_patch(): def teardown_patch(): (test_root / 'xx').rmtree() + +@with_app(buildername='text', + confoverrides={'language': 'xx', 'locale_dirs': ['.']}) +def test_patch(app): + app.builder.build(['bom']) + result = (app.outdir / 'bom.txt').text(encoding='utf-8') + expect = (u"\nDatei mit UTF-8" + u"\n***************\n" # underline matches new translation + u"\nThis file has umlauts: äöü.\n") + assert result == expect + test_patch.setup = setup_patch test_patch.teardown = teardown_patch From 043579e468f2746ad5467e0bc45f50eaa22720b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 21 Aug 2010 20:53:05 +0200 Subject: [PATCH 358/744] Added a fallback for itertools product to pycompat for python versions < 2.6 --- sphinx/util/pycompat.py | 12 ++++++++++++ sphinx/versioning.py | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 5f23bbe18..faebcd01c 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -21,6 +21,18 @@ except ImportError: # Python 3 class_types = (type,) +try: + from itertools import product +except ImportError: # python < 2.6 + # this code has been taken from the python documentation + def product(*args, **kwargs): + pools = map(tuple, args) * kwargs.get('repeat', 1) + result = [[]] + for pool in pools: + result = [x + [y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + # the ubiquitous "bytes" helper function if sys.version_info >= (3, 0): diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 430dc1423..0ea494f06 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -12,12 +12,13 @@ from uuid import uuid4 from operator import itemgetter from collections import defaultdict -from itertools import product try: from itertools import izip_longest as zip_longest except ImportError: from itertools import zip_longest +from sphinx.util.pycompat import product + # anything below that ratio is considered equal/changed VERSIONING_RATIO = 65 From bbd39e55334adf39d35b8e1b3d620aac3afd23cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sat, 21 Aug 2010 21:08:15 +0200 Subject: [PATCH 359/744] Added a fallback for itertools.izip_longest to pycompat for python versions < 2.6 --- sphinx/util/pycompat.py | 21 +++++++++++++++++++++ sphinx/versioning.py | 6 +----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index faebcd01c..bfe96a987 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -33,6 +33,27 @@ except ImportError: # python < 2.6 for prod in result: yield tuple(prod) +try: + from itertools import izip_longest as zip_longest +except ImportError: # python > 2.6/2.7 or python < 2.6 + try: + from itertools import zip_longest + except ImportError: # python < 2.6 + # this code has been taken from the python documentation + from itertools import repeat, chain, izip + + def zip_longest(*args, **kwargs): + fillvalue = kwargs.get('fillvalue') + def sentinel(counter=([fillvalue] * (len(args) - 1)).pop): + yield counter() + fillers = repeat(fillvalue) + iters = [chain(it, sentinel(), fillers) for it in args] + try: + for tup in izip(*iters): + yield tup + except IndexError: + pass + # the ubiquitous "bytes" helper function if sys.version_info >= (3, 0): diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 0ea494f06..5b0b2127e 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -12,12 +12,8 @@ from uuid import uuid4 from operator import itemgetter from collections import defaultdict -try: - from itertools import izip_longest as zip_longest -except ImportError: - from itertools import zip_longest -from sphinx.util.pycompat import product +from sphinx.util.pycompat import product, zip_longest # anything below that ratio is considered equal/changed From 2887d24710adb77743cd38b92c41dc6eedc7eec2 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 21:54:50 +0200 Subject: [PATCH 360/744] Supply substitute implementation of itertools.(i)zip_longest. --- sphinx/util/pycompat.py | 29 ++++++++++++++++++++++++++--- sphinx/versioning.py | 6 +----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index faebcd01c..cba4ec14b 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -12,7 +12,7 @@ import sys import codecs import encodings -import re + try: from types import ClassType @@ -21,10 +21,11 @@ except ImportError: # Python 3 class_types = (type,) + try: from itertools import product -except ImportError: # python < 2.6 - # this code has been taken from the python documentation +except ImportError: # Python < 2.6 + # this code has been taken from the Python itertools documentation def product(*args, **kwargs): pools = map(tuple, args) * kwargs.get('repeat', 1) result = [[]] @@ -34,6 +35,28 @@ except ImportError: # python < 2.6 yield tuple(prod) +try: + from itertools import izip_longest as zip_longest +except ImportError: # Python < 2.6 or >= 3.0 + try: + from itertools import zip_longest + except ImportError: + from itertools import izip, repeat, chain + # this code has been taken from the Python itertools documentation + def izip_longest(*args, **kwds): + # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- + fillvalue = kwds.get('fillvalue') + def sentinel(counter = ([fillvalue]*(len(args)-1)).pop): + yield counter() # yields the fillvalue, or raises IndexError + fillers = repeat(fillvalue) + iters = [chain(it, sentinel(), fillers) for it in args] + try: + for tup in izip(*iters): + yield tup + except IndexError: + pass + + # the ubiquitous "bytes" helper function if sys.version_info >= (3, 0): def b(s): diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 0ea494f06..5b0b2127e 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -12,12 +12,8 @@ from uuid import uuid4 from operator import itemgetter from collections import defaultdict -try: - from itertools import izip_longest as zip_longest -except ImportError: - from itertools import zip_longest -from sphinx.util.pycompat import product +from sphinx.util.pycompat import product, zip_longest # anything below that ratio is considered equal/changed From b9c933046deb05c050a9e0a87c9a8a211baa26e2 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:01:42 +0200 Subject: [PATCH 361/744] Fix version. --- tests/test_websupport.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 5ef7a902f..bf66cb3b1 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -12,6 +12,12 @@ import os from StringIO import StringIO +try: + from functools import wraps +except ImportError: + # functools is new in 2.5 + wraps = lambda f: (lambda w: w) + from nose import SkipTest from sphinx.websupport import WebSupport @@ -20,13 +26,8 @@ from sphinx.websupport.storage.differ import CombinedHtmlDiff from sphinx.websupport.storage.sqlalchemystorage import Session, \ SQLAlchemyStorage, Comment, CommentVote from sphinx.websupport.storage.db import Node -from util import * -try: - from functools import wraps -except ImportError: - # functools is new in 2.4 - wraps = lambda f: (lambda w: w) +from util import * default_settings = {'builddir': os.path.join(test_root, 'websupport'), From 896be9a20135a6987fc245a34c5741ecffea28bd Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:11:01 +0200 Subject: [PATCH 362/744] Group conditional sections in pycompat. --- sphinx/util/pycompat.py | 137 +++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 72 deletions(-) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 5ae599074..e5a712c91 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -13,64 +13,17 @@ import sys import codecs import encodings +# ------------------------------------------------------------------------------ +# Python 2/3 compatibility -try: - from types import ClassType - class_types = (type, ClassType) -except ImportError: +if sys.version_info >= (3, 0): # Python 3 class_types = (type,) - - -try: - from itertools import product -except ImportError: # Python < 2.6 - # this code has been taken from the Python itertools documentation - def product(*args, **kwargs): - pools = map(tuple, args) * kwargs.get('repeat', 1) - result = [[]] - for pool in pools: - result = [x + [y] for x in result for y in pool] - for prod in result: - yield tuple(prod) - - -try: - from itertools import izip_longest as zip_longest -except ImportError: # Python < 2.6 or >= 3.0 - try: - from itertools import zip_longest - except ImportError: - from itertools import izip, repeat, chain - # this code has been taken from the Python itertools documentation - def zip_longest(*args, **kwds): - # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- - fillvalue = kwds.get('fillvalue') - def sentinel(counter = ([fillvalue]*(len(args)-1)).pop): - yield counter() # yields the fillvalue, or raises IndexError - fillers = repeat(fillvalue) - iters = [chain(it, sentinel(), fillers) for it in args] - try: - for tup in izip(*iters): - yield tup - except IndexError: - pass - - -# the ubiquitous "bytes" helper function -if sys.version_info >= (3, 0): + # the ubiquitous "bytes" helper functions def b(s): return s.encode('utf-8') -else: - b = str - - -# Support for running 2to3 over config files - -if sys.version_info < (3, 0): - # no need to refactor on 2.x versions - convert_with_2to3 = None -else: + bytes = bytes + # support for running 2to3 over config files def convert_with_2to3(filepath): from lib2to3.refactor import RefactoringTool, get_fixers_from_package from lib2to3.pgen2.parse import ParseError @@ -86,32 +39,74 @@ else: raise SyntaxError(err.msg, (filepath, lineno, offset, err.value)) return unicode(tree) +else: + # Python 2 + from types import ClassType + class_types = (type, ClassType) + b = str + bytes = str + # no need to refactor on 2.x versions + convert_with_2to3 = None -try: + +# ------------------------------------------------------------------------------ +# Missing itertools in Python < 2.6 + +if sys.version_info >= (2, 6): + # Python >= 2.6 + from itertools import product + try: + from itertools import zip_longest # Python 3 name + except ImportError: + from itertools import izip_longest as zip_longest + +else: + # Python < 2.6 + from itertools import izip, repeat, chain + + # These replacement functions have been taken from the Python 2.6 + # itertools documentation. + def product(*args, **kwargs): + pools = map(tuple, args) * kwargs.get('repeat', 1) + result = [[]] + for pool in pools: + result = [x + [y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + + def zip_longest(*args, **kwds): + # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- + fillvalue = kwds.get('fillvalue') + def sentinel(counter = ([fillvalue]*(len(args)-1)).pop): + yield counter() # yields the fillvalue, or raises IndexError + fillers = repeat(fillvalue) + iters = [chain(it, sentinel(), fillers) for it in args] + try: + for tup in izip(*iters): + yield tup + except IndexError: + pass + + +# ------------------------------------------------------------------------------ +# Missing builtins and codecs in Python < 2.5 + +if sys.version_info >= (2, 5): + # Python >= 2.5 base_exception = BaseException -except NameError: + next = next + any = any + all = all + +else: + # Python 2.4 base_exception = Exception - -try: - next = next -except NameError: # this is on Python 2, where the method is called "next" (it is refactored # to __next__ by 2to3, but in that case never executed) def next(iterator): return iterator.next() - -try: - bytes = bytes -except NameError: - bytes = str - - -try: - any = any - all = all -except NameError: def all(gen): for i in gen: if not i: @@ -124,8 +119,6 @@ except NameError: return True return False - -if sys.version_info < (2, 5): # Python 2.4 doesn't know the utf-8-sig encoding, so deliver it here def my_search_function(encoding): From 867b421178d5d19380166c2d6f5d6ca4951a00ea Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:14:57 +0200 Subject: [PATCH 363/744] Invert setup.py uuid logic. --- setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index af9e660c5..3b1d2d911 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ A development egg can be found `here <http://bitbucket.org/birkenfeld/sphinx/get/tip.gz#egg=Sphinx-dev>`_. ''' -requires = ['Pygments>=0.8', 'Jinja2>=2.2', 'docutils>=0.5', 'uuid>=1.30'] +requires = ['Pygments>=0.8', 'Jinja2>=2.2', 'docutils>=0.5'] if sys.version_info < (2, 4): print('ERROR: Sphinx requires at least Python 2.4 to run.') @@ -61,10 +61,10 @@ if sys.version_info < (2, 5): except: pass else: - del requires[-2] -elif sys.version_info >= (2, 5): - # An uuid module has been added to the stdlib in 2.5 - del requires[-1] + del requires[-1] + + # The uuid module is new in the stdlib in 2.5 + requires.append('uuid>=1.30') # Provide a "compile_catalog" command that also creates the translated From af509f4c73cec3c7c83db3d36983cd9e075a0e16 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:19:50 +0200 Subject: [PATCH 364/744] Next is new in 2.6. --- sphinx/util/pycompat.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index e5a712c91..319312a75 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -50,10 +50,12 @@ else: # ------------------------------------------------------------------------------ -# Missing itertools in Python < 2.6 +# Missing builtins and itertools in Python < 2.6 if sys.version_info >= (2, 6): # Python >= 2.6 + next = next + from itertools import product try: from itertools import zip_longest # Python 3 name @@ -64,6 +66,11 @@ else: # Python < 2.6 from itertools import izip, repeat, chain + # this is on Python 2, where the method is called "next" (it is refactored + # to __next__ by 2to3, but in that case never executed) + def next(iterator): + return iterator.next() + # These replacement functions have been taken from the Python 2.6 # itertools documentation. def product(*args, **kwargs): @@ -94,7 +101,6 @@ else: if sys.version_info >= (2, 5): # Python >= 2.5 base_exception = BaseException - next = next any = any all = all @@ -102,11 +108,6 @@ else: # Python 2.4 base_exception = Exception - # this is on Python 2, where the method is called "next" (it is refactored - # to __next__ by 2to3, but in that case never executed) - def next(iterator): - return iterator.next() - def all(gen): for i in gen: if not i: From 7dd3f40461b87e599d5fdc19fe7153edb428cc3e Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:28:44 +0200 Subject: [PATCH 365/744] Fail early with old sqlalchemy. --- sphinx/websupport/storage/sqlalchemystorage.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index ba011c06d..baef24816 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -11,9 +11,14 @@ from datetime import datetime +import sqlalchemy from sqlalchemy.orm import aliased from sqlalchemy.sql import func +if sqlalchemy.__version__[:3] < '0.5': + raise ImportError('SQLAlchemy version 0.5 or greater is required for this ' + 'storage backend; you have version %s' % sqlalchemy.__version__) + from sphinx.websupport.errors import CommentNotAllowedError, \ UserNotAuthorizedError from sphinx.websupport.storage import StorageBackend From 092ebec8a7eb938a2c720d57b258bd6e106bff9e Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:33:09 +0200 Subject: [PATCH 366/744] Move default sqlalchemy engine creation to storage backend. --- sphinx/websupport/__init__.py | 12 ++++++------ sphinx/websupport/storage/sqlalchemystorage.py | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index f2d2ba451..303031323 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -65,12 +65,12 @@ class WebSupport(object): # SQLAlchemy backend. from sphinx.websupport.storage.sqlalchemystorage \ import SQLAlchemyStorage - from sqlalchemy import create_engine - db_path = path.join(self.datadir, 'db', 'websupport.db') - ensuredir(path.dirname(db_path)) - uri = storage or 'sqlite:///%s' % db_path - engine = create_engine(uri) - self.storage = SQLAlchemyStorage(engine) + if not storage: + # no explicit DB path given; create default sqlite database + db_path = path.join(self.datadir, 'db', 'websupport.db') + ensuredir(path.dirname(db_path)) + storage = 'sqlite:///' + db_path + self.storage = SQLAlchemyStorage(storage) def _init_templating(self): import sphinx diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index baef24816..dea2eea02 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -32,11 +32,11 @@ class SQLAlchemyStorage(StorageBackend): A :class:`.StorageBackend` using SQLAlchemy. """ - def __init__(self, engine): - self.engine = engine - Base.metadata.bind = engine + def __init__(self, uri): + self.engine = sqlalchemy.create_engine(uri) + Base.metadata.bind = self.engine Base.metadata.create_all() - Session.configure(bind=engine) + Session.configure(bind=self.engine) def pre_build(self): self.build_session = Session() From 2399e4627c40b373869da4409213475ade655846 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:36:35 +0200 Subject: [PATCH 367/744] Rename module to make clear it is only for sqlalchemy. --- sphinx/websupport/storage/{db.py => sqlalchemy_db.py} | 10 +++++----- sphinx/websupport/storage/sqlalchemystorage.py | 4 ++-- tests/test_websupport.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) rename sphinx/websupport/storage/{db.py => sqlalchemy_db.py} (97%) diff --git a/sphinx/websupport/storage/db.py b/sphinx/websupport/storage/sqlalchemy_db.py similarity index 97% rename from sphinx/websupport/storage/db.py rename to sphinx/websupport/storage/sqlalchemy_db.py index bf7b83dfd..4e2757a9e 100644 --- a/sphinx/websupport/storage/db.py +++ b/sphinx/websupport/storage/sqlalchemy_db.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- """ - sphinx.websupport.storage.db - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + sphinx.websupport.storage.sqlalchemy_db + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SQLAlchemy table and mapper definitions used by the - :class:`sphinx.websupport.comments.SQLAlchemyStorage`. + :class:`sphinx.websupport.storage.sqlalchemystorage.SQLAlchemyStorage`. :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. @@ -14,15 +14,15 @@ from datetime import datetime from sqlalchemy import Column, Integer, Text, String, Boolean, \ ForeignKey, DateTime -from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relation, sessionmaker, aliased +from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() - Session = sessionmaker() db_prefix = 'sphinx_' + class Node(Base): """Data about a Node in a doctree.""" __tablename__ = db_prefix + 'nodes' diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index dea2eea02..6f13c91b6 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -22,8 +22,8 @@ if sqlalchemy.__version__[:3] < '0.5': from sphinx.websupport.errors import CommentNotAllowedError, \ UserNotAuthorizedError from sphinx.websupport.storage import StorageBackend -from sphinx.websupport.storage.db import Base, Node, Comment, \ - CommentVote, Session +from sphinx.websupport.storage.sqlalchemy_db import Base, Node, \ + Comment, CommentVote, Session from sphinx.websupport.storage.differ import CombinedHtmlDiff diff --git a/tests/test_websupport.py b/tests/test_websupport.py index bf66cb3b1..e008556c8 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -25,7 +25,7 @@ from sphinx.websupport.errors import * from sphinx.websupport.storage.differ import CombinedHtmlDiff from sphinx.websupport.storage.sqlalchemystorage import Session, \ SQLAlchemyStorage, Comment, CommentVote -from sphinx.websupport.storage.db import Node +from sphinx.websupport.storage.sqlalchemy_db import Node from util import * From bcf84166ca9cd886f7b7ee1bd834cee5213143b8 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:47:18 +0200 Subject: [PATCH 368/744] Add skip_if and skip_unless test decorators. --- tests/util.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/util.py b/tests/util.py index b81e15b6e..cb1a980a5 100644 --- a/tests/util.py +++ b/tests/util.py @@ -25,12 +25,12 @@ from sphinx.ext.autodoc import AutoDirective from path import path -from nose import tools +from nose import tools, SkipTest __all__ = [ 'test_root', - 'raises', 'raises_msg', 'Struct', + 'raises', 'raises_msg', 'skip_if', 'skip_unless', 'Struct', 'ListOutput', 'TestApp', 'with_app', 'gen_with_app', 'path', 'with_tempdir', 'write_file', 'sprint', 'remove_unicode_literals', @@ -71,6 +71,21 @@ def raises_msg(exc, msg, func, *args, **kwds): raise AssertionError('%s did not raise %s' % (func.__name__, _excstr(exc))) +def skip_if(condition, msg=None): + """Decorator to skip test if condition is true.""" + def deco(test): + @tools.make_decorator(test) + def skipper(*args, **kwds): + if condition: + raise SkipTest(msg or 'conditional skip') + return test(*args, **kwds) + return skipper + return deco + +def skip_unless(condition, msg=None): + """Decorator to skip test if condition is false.""" + return skip_if(not condition, msg) + class Struct(object): def __init__(self, **kwds): From 9acc57b616eeda8490b60de1199a9ddda8f00208 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 22:47:32 +0200 Subject: [PATCH 369/744] Skip tests accordingly if sqlalchemy is not present. --- tests/test_websupport.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/test_websupport.py b/tests/test_websupport.py index e008556c8..1ac96f8d2 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -23,9 +23,13 @@ from nose import SkipTest from sphinx.websupport import WebSupport from sphinx.websupport.errors import * from sphinx.websupport.storage.differ import CombinedHtmlDiff -from sphinx.websupport.storage.sqlalchemystorage import Session, \ - SQLAlchemyStorage, Comment, CommentVote -from sphinx.websupport.storage.sqlalchemy_db import Node +try: + from sphinx.websupport.storage.sqlalchemystorage import Session, \ + SQLAlchemyStorage, Comment, CommentVote + from sphinx.websupport.storage.sqlalchemy_db import Node + sqlalchemy_missing = False +except ImportError: + sqlalchemy_missing = True from util import * @@ -73,6 +77,7 @@ def test_get_document(support): and contents['sidebar'] and contents['relbar'] +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support() def test_comments(support): session = Session() @@ -121,6 +126,7 @@ def test_comments(support): assert children[0]['text'] == 'Child test comment' +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support() def test_voting(support): session = Session() @@ -154,6 +160,7 @@ def test_voting(support): assert comment['vote'] == 1, '%s != 1' % comment['vote'] +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support() def test_proposals(support): session = Session() @@ -169,6 +176,7 @@ def test_proposals(support): proposal=proposal) +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support() def test_user_delete_comments(support): def get_comment(): @@ -189,6 +197,7 @@ def test_user_delete_comments(support): assert comment['text'] == '[deleted]' +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support() def test_moderator_delete_comments(support): def get_comment(): @@ -205,6 +214,7 @@ def test_moderator_delete_comments(support): assert comment['text'] == '[deleted]' +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support() def test_update_username(support): support.update_username('user_two', 'new_user_two') @@ -229,6 +239,7 @@ def moderation_callback(comment): called = True +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support(moderation_callback=moderation_callback) def test_moderation(support): session = Session() From 2f2a09c9198f753c3a407fef0630a15b0ebe66af Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 23:03:06 +0200 Subject: [PATCH 370/744] Improve websupport test skipping, add new decorator for search adapter skipping. --- tests/test_searchadapters.py | 26 +++++++++++--------------- tests/test_websupport.py | 9 ++++++++- tests/util.py | 13 +++++++++++-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py index a30141dfd..cf5accb92 100644 --- a/tests/test_searchadapters.py +++ b/tests/test_searchadapters.py @@ -12,9 +12,13 @@ import os, sys from StringIO import StringIO -from util import * +from nose import SkipTest + from sphinx.websupport import WebSupport +from test_websupport import sqlalchemy_missing +from util import * + def clear_builddir(): (test_root / 'websupport').rmtree(True) @@ -63,21 +67,13 @@ def search_adapter_helper(adapter): html = support.get_search_results(u'SomeLongRandomWord') +@skip_unless_importable('xapian', 'needs xapian bindings installed') +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') def test_xapian(): - # Don't run tests if xapian is not installed. - try: - import xapian - search_adapter_helper('xapian') - except ImportError: - sys.stderr.write('info: not running xapian tests, ' \ - 'xapian doesn\'t seem to be installed') + search_adapter_helper('xapian') +@skip_unless_importable('whoosh', 'needs whoosh package installed') +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') def test_whoosh(): - # Don't run tests if whoosh is not installed. - try: - import whoosh - search_adapter_helper('whoosh') - except ImportError: - sys.stderr.write('info: not running whoosh tests, ' \ - 'whoosh doesn\'t seem to be installed') + search_adapter_helper('whoosh') diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 1ac96f8d2..65957378b 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -22,6 +22,7 @@ from nose import SkipTest from sphinx.websupport import WebSupport from sphinx.websupport.errors import * +from sphinx.websupport.storage import StorageBackend from sphinx.websupport.storage.differ import CombinedHtmlDiff try: from sphinx.websupport.storage.sqlalchemystorage import Session, \ @@ -57,17 +58,23 @@ def with_support(*args, **kwargs): return generator -@with_support() +class NullStorage(StorageBackend): + pass + + +@with_support(storage=NullStorage()) def test_no_srcdir(support): """Make sure the correct exception is raised if srcdir is not given.""" raises(SrcdirNotSpecifiedError, support.build) +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support(srcdir=test_root) def test_build(support): support.build() +@skip_if(sqlalchemy_missing, 'needs sqlalchemy') @with_support() def test_get_document(support): raises(DocumentNotFoundError, support.get_document, 'nonexisting') diff --git a/tests/util.py b/tests/util.py index cb1a980a5..d56f34646 100644 --- a/tests/util.py +++ b/tests/util.py @@ -29,8 +29,8 @@ from nose import tools, SkipTest __all__ = [ - 'test_root', - 'raises', 'raises_msg', 'skip_if', 'skip_unless', 'Struct', + 'test_root', 'raises', 'raises_msg', + 'skip_if', 'skip_unless', 'skip_unless_importable', 'Struct', 'ListOutput', 'TestApp', 'with_app', 'gen_with_app', 'path', 'with_tempdir', 'write_file', 'sprint', 'remove_unicode_literals', @@ -86,6 +86,15 @@ def skip_unless(condition, msg=None): """Decorator to skip test if condition is false.""" return skip_if(not condition, msg) +def skip_unless_importable(module, msg=None): + """Decorator to skip test if module is not importable.""" + try: + __import__(module) + except ImportError: + return skip_if(True, msg) + else: + return skip_if(False, msg) + class Struct(object): def __init__(self, **kwds): From 55cc70ee06ce9f37aee1bfe0b19663f895c5aae5 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 23:45:25 +0200 Subject: [PATCH 371/744] Remove debugging leftover. --- sphinx/ext/oldcmarkup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py index 571d82a51..00ac37495 100644 --- a/sphinx/ext/oldcmarkup.py +++ b/sphinx/ext/oldcmarkup.py @@ -31,7 +31,6 @@ class OldCDirective(Directive): def run(self): env = self.state.document.settings.env if not env.app._oldcmarkup_warned: - print 'XXXYYY' env.warn(env.docname, WARNING_MSG, self.lineno) env.app._oldcmarkup_warned = True newname = 'c:' + self.name[1:] From 73ba654b4ed701108a4382fa0eb38569d43712ab Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 21 Aug 2010 23:59:40 +0200 Subject: [PATCH 372/744] Use different Whoosh API in order to fix test failure. --- sphinx/websupport/search/whooshsearch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index d395dcd7f..e58c7342c 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -11,6 +11,7 @@ from whoosh import index from whoosh.fields import Schema, ID, TEXT +from whoosh.qparser import QueryParser from whoosh.analysis import StemmingAnalyzer from sphinx.util.osutil import ensuredir @@ -31,6 +32,7 @@ class WhooshSearch(BaseSearch): self.index = index.open_dir(db_path) else: self.index = index.create_in(db_path, schema=self.schema) + self.qparser = QueryParser('text', self.schema) def init_indexing(self, changed=[]): for changed_path in changed: @@ -47,7 +49,7 @@ class WhooshSearch(BaseSearch): def handle_query(self, q): searcher = self.index.searcher() - whoosh_results = searcher.find('text', q) + whoosh_results = searcher.search(self.qparser.parse(q)) results = [] for result in whoosh_results: context = self.extract_context(result['text']) From 757e9820a845f8195f882cc25746e163f97e0e77 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sun, 22 Aug 2010 00:32:09 +0200 Subject: [PATCH 373/744] Remove GSOC changelogs. --- CHANGES.DasIch | 36 ------------------------------------ CHANGES.jacobmason | 27 --------------------------- 2 files changed, 63 deletions(-) delete mode 100644 CHANGES.DasIch delete mode 100644 CHANGES.jacobmason diff --git a/CHANGES.DasIch b/CHANGES.DasIch deleted file mode 100644 index 3f7167263..000000000 --- a/CHANGES.DasIch +++ /dev/null @@ -1,36 +0,0 @@ -Changes -======= - -This file contains changes made by Daniel Neuhäuser, during the Google Summer -of Code 2010, to port Sphinx to Python 3.x. Changes are ordered descending by -date. - -May 16: - Added utils/convert.py which converts entire directories of python - files with 2to3 and names the converted files foo3.py. - - Modified the Makefile so that in case Python 3 is used the scripts in - utils get converted with utils/convert.py and are used instead of the - Python 2 scripts. - -May 10: Fixed a couple of tests and made several small changes. - -May 9: - Removed ez_setup.py which does not work with Python 3.x. and replaced - it with distribute_setup.py - - Use distribute (at least on 3.x) in order to run 2to3 automatically. - - Reverted some of the changes made in revision bac40c7c924c which - caused errors. - - Modified tests/run.py to test against the build created by - setup.py build in order to run the test suite with 3.x - - Several small changes to fix 3.x compatibilty. - -May 1: - Removed deprecated tuple parameter unpacking. - - Removed a pre-2.3 workaround for booleans because this creates a - deprecation warning for 3.x, in which you can't assign values to - booleans. - - Moved :func:`open()` calls out of the try-blocks, which fixes revision - c577c25bd44b. - -April 30: Made :cls:`sphinx.domains.cpp.DefExpr` unhashable as described by the - documentation because classes in 3.x don't inherit ``__hash__`` if - they implement ``__eq__``. - -April 29: Removed several deprecated function/method calls. diff --git a/CHANGES.jacobmason b/CHANGES.jacobmason deleted file mode 100644 index c445006c2..000000000 --- a/CHANGES.jacobmason +++ /dev/null @@ -1,27 +0,0 @@ -May 30: Added files builders/websupport.py, writers/websupport.py, -websupport/api.py, and websupport/document.api. Provides a rudimentary -method of building websupport data, and rendering it as html. - -May 31-June 10: Continued changing way web support data is represented -and accessed. - -June 14 - June 17: Continued making improvements to the web support package -and demo web application. Included sidebars, navlinks etc... - -June 21 - June 26: Implement server side search with two search adapters, -one for Xapian and one for Whoosh - -June 28 - July 12: Implement voting system on the backend, and created a -jQuery script to handle voting on the frontend. - -July 13 - July 19: Added documentation for the web support package. - -July 20 - July 27: Added a system to allow user's to propose changes to -documentation along with comments. - -July 28 - August 3: Added tests for the web support package. Refactored -sqlalchemy storage to be more efficient. - -August 4 - August 7: Added comment moderation system. Added more -documentation. General code cleanup. - From 2e47414658555310a677f662a98711a013c15f2f Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sun, 22 Aug 2010 11:09:35 +0200 Subject: [PATCH 374/744] Add node docstrings, remove duplication of class names. --- sphinx/addnodes.py | 195 ++++++++++++++++++++++++++++++--------------- 1 file changed, 130 insertions(+), 65 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 0a2f0f7f8..592bef5df 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -11,103 +11,168 @@ from docutils import nodes -# index markup -class index(nodes.Invisible, nodes.Inline, nodes.TextElement): pass + +class toctree(nodes.General, nodes.Element): + """Node for inserting a "TOC tree".""" + # domain-specific object descriptions (class, function etc.) -# parent node for signature and content -class desc(nodes.Admonition, nodes.Element): pass +class desc(nodes.Admonition, nodes.Element): + """Node for object descriptions. -# additional name parts (module name, class name) -class desc_addname(nodes.Part, nodes.Inline, nodes.TextElement): pass + This node is similar to a "definition list" with one definition. It + contains one or more ``desc_signature`` and a ``desc_content``. + """ + +class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for object signatures. + + The "term" part of the custom Sphinx definition list. + """ + + +# nodes to use within a desc_signature + +class desc_addname(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for additional name parts (module name, class name).""" # compatibility alias desc_classname = desc_addname -# return type (C); object type -class desc_type(nodes.Part, nodes.Inline, nodes.TextElement): pass -# -> annotation (Python) + +class desc_type(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for return types or object type names.""" + class desc_returns(desc_type): + """Node for a "returns" annotation (a la -> in Python).""" def astext(self): return ' -> ' + nodes.TextElement.astext(self) -# main name of object -class desc_name(nodes.Part, nodes.Inline, nodes.TextElement): pass -# argument list -class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement): pass + +class desc_name(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for the main object name.""" + class desc_parameterlist(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for a general parameter list.""" child_text_separator = ', ' -class desc_parameter(nodes.Part, nodes.Inline, nodes.TextElement): pass + +class desc_parameter(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for a single parameter.""" + class desc_optional(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for marking optional parts of the parameter list.""" child_text_separator = ', ' def astext(self): return '[' + nodes.TextElement.astext(self) + ']' -# annotation (not Python 3-style annotations) -class desc_annotation(nodes.Part, nodes.Inline, nodes.TextElement): pass -# node for content -class desc_content(nodes.General, nodes.Element): pass +class desc_annotation(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for signature annotations (not Python 3-style annotations).""" -# \versionadded, \versionchanged, \deprecated -class versionmodified(nodes.Admonition, nodes.TextElement): pass +class desc_content(nodes.General, nodes.Element): + """Node for object description content. -# seealso -class seealso(nodes.Admonition, nodes.Element): pass + This is the "definition" part of the custom Sphinx definition list. + """ -# productionlist -class productionlist(nodes.Admonition, nodes.Element): pass -class production(nodes.Part, nodes.Inline, nodes.TextElement): pass -# toc tree -class toctree(nodes.General, nodes.Element): pass +# new admonition-like constructs -# centered -class centered(nodes.Part, nodes.Element): pass +class versionmodified(nodes.Admonition, nodes.TextElement): + """Node for version change entries. -# pending xref -class pending_xref(nodes.Inline, nodes.Element): pass + Currently used for "versionadded", "versionchanged" and "deprecated" + directives. + """ -# compact paragraph -- never makes a <p> -class compact_paragraph(nodes.paragraph): pass +class seealso(nodes.Admonition, nodes.Element): + """Custom "see also" admonition.""" -# reference to a file to download -class download_reference(nodes.reference): pass +class productionlist(nodes.Admonition, nodes.Element): + """Node for grammar production lists. -# for the ACKS list -class acks(nodes.Element): pass + Contains ``production`` nodes. + """ -# for horizontal lists -class hlist(nodes.Element): pass -class hlistcol(nodes.Element): pass +class production(nodes.Part, nodes.Inline, nodes.TextElement): + """Node for a single grammar production rule.""" -# sets the highlighting language for literal blocks -class highlightlang(nodes.Element): pass -# like emphasis, but doesn't apply further text processors, e.g. smartypants -class literal_emphasis(nodes.emphasis): pass +# other directive-level nodes -# for abbreviations (with explanations) -class abbreviation(nodes.Inline, nodes.TextElement): pass +class index(nodes.Invisible, nodes.Inline, nodes.TextElement): + """Node for index entries. -# glossary -class glossary(nodes.Element): pass + This node is created by the ``index`` directive and has one attribute, + ``entries``. Its value is a list of 4-tuples of ``(entrytype, entryname, + target, ignored)``. -# start of a file, used in the LaTeX builder only -class start_of_file(nodes.Element): pass + *entrytype* is one of "single", "pair", "double", "triple". + """ -# tabular column specification, used for the LaTeX writer -class tabular_col_spec(nodes.Element): pass +class centered(nodes.Part, nodes.Element): + """Deprecated.""" -# only (in/exclusion based on tags) -class only(nodes.Element): pass +class acks(nodes.Element): + """Special node for "acks" lists.""" -# meta directive -- same as docutils' standard meta node, but pickleable -class meta(nodes.Special, nodes.PreBibliographic, nodes.Element): pass +class hlist(nodes.Element): + """Node for "horizontal lists", i.e. lists that should be compressed to + take up less vertical space. + """ -# make them known to docutils. this is needed, because the HTML writer -# will choke at some point if these are not added -nodes._add_node_class_names("""index desc desc_content desc_signature - desc_type desc_returns desc_addname desc_name desc_parameterlist - desc_parameter desc_optional download_reference hlist hlistcol - centered versionmodified seealso productionlist production toctree - pending_xref compact_paragraph highlightlang literal_emphasis - abbreviation glossary acks module start_of_file tabular_col_spec - meta""".split()) +class hlistcol(nodes.Element): + """Node for one column in a horizontal list.""" + +class compact_paragraph(nodes.paragraph): + """Node for a compact paragraph (which never makes a <p> node).""" + +class glossary(nodes.Element): + """Node to insert a glossary.""" + +class only(nodes.Element): + """Node for "only" directives (conditional inclusion based on tags).""" + + +# meta-information nodes + +class start_of_file(nodes.Element): + """Node to mark start of a new file, used in the LaTeX builder only.""" + +class highlightlang(nodes.Element): + """Inserted to set the highlight language and line number options for + subsequent code blocks. + """ + +class tabular_col_spec(nodes.Element): + """Node for specifying tabular columns, used for LaTeX output.""" + +class meta(nodes.Special, nodes.PreBibliographic, nodes.Element): + """Node for meta directive -- same as docutils' standard meta node, + but pickleable. + """ + + +# inline nodes + +class pending_xref(nodes.Inline, nodes.Element): + """Node for cross-references that cannot be resolved without complete + information about all documents. + + These nodes are resolved before writing output, in + BuildEnvironment.resolve_references. + """ + +class download_reference(nodes.reference): + """Node for download references, similar to pending_xref.""" + +class literal_emphasis(nodes.emphasis): + """Node that behaves like `emphasis`, but further text processors are not + applied (e.g. smartypants for HTML output). + """ + +class abbreviation(nodes.Inline, nodes.TextElement): + """Node for abbreviations with explanations.""" + + +# make the new nodes known to docutils; needed because the HTML writer will +# choke at some point if these are not added +nodes._add_node_class_names(k for k in globals().keys() + if k != 'nodes' and k[0] != '_') From 630791c42c29c68a3cee4d722152886d70ddeff5 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sun, 22 Aug 2010 11:36:08 +0200 Subject: [PATCH 375/744] Docstring harmonization. --- sphinx/__init__.py | 1 + sphinx/application.py | 19 +++----- sphinx/builders/__init__.py | 43 ++++++++---------- sphinx/builders/devhelp.py | 1 - sphinx/builders/epub.py | 32 +++++++------ sphinx/builders/html.py | 3 +- sphinx/config.py | 4 +- sphinx/domains/__init__.py | 29 ++++-------- sphinx/domains/cpp.py | 25 +++++----- sphinx/domains/javascript.py | 2 +- sphinx/domains/python.py | 22 ++++----- sphinx/domains/rst.py | 7 +-- sphinx/environment.py | 73 +++++++++++++----------------- sphinx/ext/autodoc.py | 68 ++++++++++++++-------------- sphinx/ext/autosummary/__init__.py | 22 ++++----- sphinx/ext/autosummary/generate.py | 13 +++--- sphinx/ext/graphviz.py | 4 +- sphinx/ext/inheritance_diagram.py | 32 +++++-------- sphinx/ext/oldcmarkup.py | 1 + sphinx/ext/pngmath.py | 3 +- sphinx/jinja2glue.py | 6 ++- sphinx/locale/__init__.py | 11 +++-- sphinx/roles.py | 11 ++--- sphinx/setup_command.py | 3 +- sphinx/theming.py | 14 ++---- sphinx/util/__init__.py | 19 +++----- sphinx/util/docstrings.py | 15 +++--- sphinx/util/jsonimpl.py | 2 +- sphinx/util/matching.py | 11 ++--- sphinx/util/nodes.py | 6 +++ sphinx/util/osutil.py | 4 +- sphinx/util/png.py | 8 ++-- sphinx/util/websupport.py | 1 + sphinx/writers/websupport.py | 3 +- 34 files changed, 233 insertions(+), 285 deletions(-) diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 1ea2e7bf7..211e2413a 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -37,6 +37,7 @@ if '+' in __version__ or 'pre' in __version__: def main(argv=sys.argv): + """Sphinx build "main" command-line entry.""" if sys.version_info[:3] < (2, 4, 0): sys.stderr.write('Error: Sphinx requires at least ' 'Python 2.4 to run.\n') diff --git a/sphinx/application.py b/sphinx/application.py index b3d2aebc4..6fe1ee059 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -133,9 +133,8 @@ class Sphinx(object): self._init_builder(buildername) def _init_i18n(self): - """ - Load translated strings from the configured localedirs if - enabled in the configuration. + """Load translated strings from the configured localedirs if enabled in + the configuration. """ if self.config.language is not None: self.info(bold('loading translations [%s]... ' % @@ -484,8 +483,7 @@ class TemplateBridge(object): """ def init(self, builder, theme=None, dirs=None): - """ - Called by the builder to initialize the template system. + """Called by the builder to initialize the template system. *builder* is the builder object; you'll probably want to look at the value of ``builder.config.templates_path``. @@ -496,23 +494,20 @@ class TemplateBridge(object): raise NotImplementedError('must be implemented in subclasses') def newest_template_mtime(self): - """ - Called by the builder to determine if output files are outdated + """Called by the builder to determine if output files are outdated because of template changes. Return the mtime of the newest template file that was changed. The default implementation returns ``0``. """ return 0 def render(self, template, context): - """ - Called by the builder to render a template given as a filename with a - specified context (a Python dictionary). + """Called by the builder to render a template given as a filename with + a specified context (a Python dictionary). """ raise NotImplementedError('must be implemented in subclasses') def render_string(self, template, context): - """ - Called by the builder to render a template given as a string with a + """Called by the builder to render a template given as a string with a specified context (a Python dictionary). """ raise NotImplementedError('must be implemented in subclasses') diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 9112af63c..ce04f7691 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -55,16 +55,13 @@ class Builder(object): # helper methods def init(self): - """ - Load necessary templates and perform initialization. The default + """Load necessary templates and perform initialization. The default implementation does nothing. """ pass def create_template_bridge(self): - """ - Return the template bridge configured. - """ + """Return the template bridge configured.""" if self.config.template_bridge: self.templates = self.app.import_object( self.config.template_bridge, 'template_bridge setting')() @@ -73,23 +70,23 @@ class Builder(object): self.templates = BuiltinTemplateLoader() def get_target_uri(self, docname, typ=None): - """ - Return the target URI for a document name (*typ* can be used to qualify - the link characteristic for individual builders). + """Return the target URI for a document name. + + *typ* can be used to qualify the link characteristic for individual + builders. """ raise NotImplementedError def get_relative_uri(self, from_, to, typ=None): - """ - Return a relative URI between two source filenames. May raise - environment.NoUri if there's no way to return a sensible URI. + """Return a relative URI between two source filenames. + + May raise environment.NoUri if there's no way to return a sensible URI. """ return relative_uri(self.get_target_uri(from_), self.get_target_uri(to, typ)) def get_outdated_docs(self): - """ - Return an iterable of output files that are outdated, or a string + """Return an iterable of output files that are outdated, or a string describing what an update build will build. If the builder does not output individual files corresponding to @@ -129,9 +126,7 @@ class Builder(object): supported_image_types = [] def post_process_images(self, doctree): - """ - Pick the best candidate for all image URIs. - """ + """Pick the best candidate for all image URIs.""" for node in doctree.traverse(nodes.image): if '?' in node['candidates']: # don't rewrite nonlocal image URIs @@ -198,9 +193,9 @@ class Builder(object): 'out of date' % len(to_build)) def build(self, docnames, summary=None, method='update'): - """ - Main build method. First updates the environment, and then - calls :meth:`write`. + """Main build method. + + First updates the environment, and then calls :meth:`write`. """ if summary: self.info(bold('building [%s]: ' % self.name), nonl=1) @@ -302,14 +297,16 @@ class Builder(object): raise NotImplementedError def finish(self): - """ - Finish the building process. The default implementation does nothing. + """Finish the building process. + + The default implementation does nothing. """ pass def cleanup(self): - """ - Cleanup any resources. The default implementation does nothing. + """Cleanup any resources. + + The default implementation does nothing. """ pass diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py index a5a0f280a..d43cd624b 100644 --- a/sphinx/builders/devhelp.py +++ b/sphinx/builders/devhelp.py @@ -42,7 +42,6 @@ except ImportError: class DevhelpBuilder(StandaloneHTMLBuilder): """ Builder that also outputs GNOME Devhelp file. - """ name = 'devhelp' diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index aea07d4d1..3e123a0a2 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -130,7 +130,8 @@ _refuri_re = re.compile("([^#:]*#)(.*)") # The epub publisher class EpubBuilder(StandaloneHTMLBuilder): - """Builder that outputs epub files. + """ + Builder that outputs epub files. It creates the metainfo files container.opf, toc.ncx, mimetype, and META-INF/container.xml. Afterwards, all necessary files are zipped to an @@ -222,12 +223,12 @@ class EpubBuilder(StandaloneHTMLBuilder): }) def fix_fragment(self, match): - """Return a href attribute with colons replaced by hyphens. - """ + """Return a href attribute with colons replaced by hyphens.""" return match.group(1) + match.group(2).replace(':', '-') def fix_ids(self, tree): """Replace colons with hyphens in href and id attributes. + Some readers crash because they interpret the part as a transport protocol specification. """ @@ -246,8 +247,7 @@ class EpubBuilder(StandaloneHTMLBuilder): node.attributes['ids'] = newids def add_visible_links(self, tree): - """Append visible link targets after external links. - """ + """Append visible link targets after external links.""" for node in tree.traverse(nodes.reference): uri = node.get('refuri', '') if (uri.startswith('http:') or uri.startswith('https:') or @@ -261,6 +261,7 @@ class EpubBuilder(StandaloneHTMLBuilder): def write_doc(self, docname, doctree): """Write one document file. + This method is overwritten in order to fix fragment identifiers and to add visible external links. """ @@ -269,8 +270,7 @@ class EpubBuilder(StandaloneHTMLBuilder): return StandaloneHTMLBuilder.write_doc(self, docname, doctree) def fix_genindex(self, tree): - """Fix href attributes for genindex pages. - """ + """Fix href attributes for genindex pages.""" # XXX: modifies tree inline # Logic modeled from themes/basic/genindex.html for key, columns in tree: @@ -288,8 +288,9 @@ class EpubBuilder(StandaloneHTMLBuilder): def handle_page(self, pagename, addctx, templatename='page.html', outfilename=None, event_arg=None): """Create a rendered page. - This method is overwritten for genindex pages in order to fix - href link attributes. + + This method is overwritten for genindex pages in order to fix href link + attributes. """ if pagename.startswith('genindex'): self.fix_genindex(addctx['genindexentries']) @@ -413,6 +414,7 @@ class EpubBuilder(StandaloneHTMLBuilder): def insert_subnav(self, node, subnav): """Insert nested navpoints for given node. + The node and subnav are already rendered to text. """ nlist = node.rsplit('\n', 1) @@ -422,8 +424,8 @@ class EpubBuilder(StandaloneHTMLBuilder): def build_navpoints(self, nodes): """Create the toc navigation structure. - Subelements of a node are nested inside the navpoint. - For nested nodes the parent node is reinserted in the subnav. + Subelements of a node are nested inside the navpoint. For nested nodes + the parent node is reinserted in the subnav. """ navstack = [] navlist = [] @@ -461,8 +463,8 @@ class EpubBuilder(StandaloneHTMLBuilder): return '\n'.join(navlist) def toc_metadata(self, level, navpoints): - """Create a dictionary with all metadata for the toc.ncx - file properly escaped. + """Create a dictionary with all metadata for the toc.ncx file + properly escaped. """ metadata = {} metadata['uid'] = self.config.epub_uid @@ -487,8 +489,8 @@ class EpubBuilder(StandaloneHTMLBuilder): def build_epub(self, outdir, outname): """Write the epub file. - It is a zip file with the mimetype file stored uncompressed - as the first entry. + It is a zip file with the mimetype file stored uncompressed as the first + entry. """ self.info('writing %s file...' % outname) projectfiles = ['META-INF/container.xml', 'content.opf', 'toc.ncx'] \ diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 5a7d49cd0..57330eb5b 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -587,8 +587,7 @@ class StandaloneHTMLBuilder(Builder): self.theme.cleanup() def post_process_images(self, doctree): - """ - Pick the best candidate for an image and link down-scaled images to + """Pick the best candidate for an image and link down-scaled images to their high res version. """ Builder.post_process_images(self, doctree) diff --git a/sphinx/config.py b/sphinx/config.py index 6c27f85f0..2e0a116cb 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -25,7 +25,9 @@ if sys.version_info >= (3, 0): CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?" class Config(object): - """Configuration file abstraction.""" + """ + Configuration file abstraction. + """ # the values are: (default, what needs to be rebuilt if changed) diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index d133a8123..484cd9688 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -66,9 +66,8 @@ class Index(object): self.domain = domain def generate(self, docnames=None): - """ - Return entries for the index given by *name*. If *docnames* is given, - restrict to entries referring to these docnames. + """Return entries for the index given by *name*. If *docnames* is + given, restrict to entries referring to these docnames. The return value is a tuple of ``(content, collapse)``, where *collapse* is a boolean that determines if sub-entries should start collapsed (for @@ -158,8 +157,7 @@ class Domain(object): self.objtypes_for_role = self._role2type.get def role(self, name): - """ - Return a role adapter function that always gives the registered + """Return a role adapter function that always gives the registered role its full name ('domain:name') as the first argument. """ if name in self._role_cache: @@ -175,8 +173,7 @@ class Domain(object): return role_adapter def directive(self, name): - """ - Return a directive adapter class that always gives the registered + """Return a directive adapter class that always gives the registered directive its full name ('domain:name') as ``self.name``. """ if name in self._directive_cache: @@ -195,21 +192,16 @@ class Domain(object): # methods that should be overwritten def clear_doc(self, docname): - """ - Remove traces of a document in the domain-specific inventories. - """ + """Remove traces of a document in the domain-specific inventories.""" pass def process_doc(self, env, docname, document): - """ - Process a document after it is read by the environment. - """ + """Process a document after it is read by the environment.""" pass def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): - """ - Resolve the ``pending_xref`` *node* with the given *typ* and *target*. + """Resolve the pending_xref *node* with the given *typ* and *target*. This method should return a new node, to replace the xref node, containing the *contnode* which is the markup content of the @@ -225,8 +217,7 @@ class Domain(object): pass def get_objects(self): - """ - Return an iterable of "object descriptions", which are tuples with + """Return an iterable of "object descriptions", which are tuples with five items: * `name` -- fully qualified name @@ -245,9 +236,7 @@ class Domain(object): return [] def get_type_name(self, type, primary=False): - """ - Return full name for given ObjType. - """ + """Return full name for given ObjType.""" if primary: return type.lname return _('%s %s') % (self.label, type.lname) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 8df89459b..a59b2b506 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -135,29 +135,31 @@ class DefExpr(object): __hash__ = None def clone(self): - """Close a definition expression node""" + """Clone a definition expression node.""" return deepcopy(self) def get_id(self): - """Returns the id for the node""" + """Return the id for the node.""" return u'' def get_name(self): - """Returns the name. Returns either `None` or a node with - a name you might call :meth:`split_owner` on. + """Return the name. + + Returns either `None` or a node with a name you might call + :meth:`split_owner` on. """ return None def split_owner(self): - """Nodes returned by :meth:`get_name` can split off their - owning parent. This function returns the owner and the - name as a tuple of two items. If a node does not support - it, :exc:`NotImplementedError` is raised. + """Nodes returned by :meth:`get_name` can split off their owning parent. + + This function returns the owner and the name as a tuple of two items. + If a node does not support it, :exc:`NotImplementedError` is raised. """ raise NotImplementedError() def prefix(self, prefix): - """Prefixes a name node (a node returned by :meth:`get_name`).""" + """Prefix a name node (a node returned by :meth:`get_name`).""" raise NotImplementedError() def __str__(self): @@ -984,8 +986,9 @@ class CPPFunctionObject(CPPObject): class CPPCurrentNamespace(Directive): - """This directive is just to tell Sphinx that we're documenting - stuff in namespace foo. + """ + This directive is just to tell Sphinx that we're documenting stuff in + namespace foo. """ has_content = False diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 582e2adc6..bc8e50f3d 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -148,7 +148,7 @@ class JSCallable(JSObject): class JSConstructor(JSCallable): - """Like a callable but with a different prefix""" + """Like a callable but with a different prefix.""" display_prefix = 'class ' diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index cd87bfbda..77604dece 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -63,22 +63,21 @@ class PyObject(ObjectDescription): ] def get_signature_prefix(self, sig): - """ - May return a prefix to put before the object name in the signature. + """May return a prefix to put before the object name in the + signature. """ return '' def needs_arglist(self): - """ - May return true if an empty argument list is to be generated even if + """May return true if an empty argument list is to be generated even if the document contains none. """ return False def handle_signature(self, sig, signode): - """ - Transform a Python signature into RST nodes. - Returns (fully qualified name of the thing, classname if any). + """Transform a Python signature into RST nodes. + + Return (fully qualified name of the thing, classname if any). If inside a class, the current class name is handled intelligently: * it is stripped from the displayed name if present @@ -167,9 +166,7 @@ class PyObject(ObjectDescription): return fullname, name_prefix def get_index_text(self, modname, name): - """ - Return the text for the index entry of the object. - """ + """Return the text for the index entry of the object.""" raise NotImplementedError('must be implemented in subclasses') def add_target_and_index(self, name_cls, sig, signode): @@ -548,9 +545,8 @@ class PythonDomain(Domain): del self.data['modules'][modname] def find_obj(self, env, modname, classname, name, type, searchmode=0): - """ - Find a Python object for "name", perhaps using the given module and/or - classname. Returns a list of (name, object entry) tuples. + """Find a Python object for "name", perhaps using the given module + and/or classname. Returns a list of (name, object entry) tuples. """ # skip parens if name[-2:] == '()': diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index d3ffc6bdc..30134d9ef 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -59,9 +59,10 @@ class ReSTMarkup(ObjectDescription): def parse_directive(d): - """ - Parses a directive signature. Returns (directive, arguments) string tuple. - if no arguments are given, returns (directive, ''). + """Parse a directive signature. + + Returns (directive, arguments) string tuple. If no arguments are given, + returns (directive, ''). """ dir = d.strip() if not dir.startswith('.'): diff --git a/sphinx/environment.py b/sphinx/environment.py index e9e984c35..f8d3641b3 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -417,12 +417,12 @@ class BuildEnvironment: domain.clear_doc(docname) def doc2path(self, docname, base=True, suffix=None): - """ - Return the filename for the document name. - If base is True, return absolute path under self.srcdir. - If base is None, return relative path to self.srcdir. - If base is a path string, return absolute path under that. - If suffix is not None, add it instead of config.source_suffix. + """Return the filename for the document name. + + If *base* is True, return absolute path under self.srcdir. + If *base* is None, return relative path to self.srcdir. + If *base* is a path string, return absolute path under that. + If *suffix* is not None, add it instead of config.source_suffix. """ docname = docname.replace(SEP, path.sep) suffix = suffix or self.config.source_suffix @@ -434,8 +434,8 @@ class BuildEnvironment: return path.join(base, docname) + suffix def find_files(self, config): - """ - Find all source files in the source dir and put them in self.found_docs. + """Find all source files in the source dir and put them in + self.found_docs. """ matchers = compile_matchers( config.exclude_patterns[:] + @@ -448,9 +448,7 @@ class BuildEnvironment: self.srcdir, config.source_suffix, exclude_matchers=matchers)) def get_outdated_files(self, config_changed): - """ - Return (added, changed, removed) sets. - """ + """Return (added, changed, removed) sets.""" # clear all files no longer present removed = set(self.all_docs) - self.found_docs @@ -496,12 +494,12 @@ class BuildEnvironment: return added, changed, removed def update(self, config, srcdir, doctreedir, app=None): - """ - (Re-)read all files new or changed since last update. Returns a - summary, the total count of documents to reread and an iterator that - yields docnames as it processes them. Store all environment docnames in - the canonical format (ie using SEP as a separator in place of - os.path.sep). + """(Re-)read all files new or changed since last update. + + Returns a summary, the total count of documents to reread and an + iterator that yields docnames as it processes them. Store all + environment docnames in the canonical format (ie using SEP as a + separator in place of os.path.sep). """ config_changed = False if self.config is None: @@ -632,8 +630,8 @@ class BuildEnvironment: roles.role = role def read_doc(self, docname, src_path=None, save_parsed=True, app=None): - """ - Parse a file and add/update inventory entries for the doctree. + """Parse a file and add/update inventory entries for the doctree. + If srcpath is given, read from a different source file. """ # remove all inventory entries for that file @@ -785,9 +783,7 @@ class BuildEnvironment: # post-processing of read doctrees def filter_messages(self, doctree): - """ - Filter system messages from a doctree. - """ + """Filter system messages from a doctree.""" filterlevel = self.config.keep_warnings and 2 or 5 for node in doctree.traverse(nodes.system_message): if node['level'] < filterlevel: @@ -795,9 +791,7 @@ class BuildEnvironment: def process_dependencies(self, docname, doctree): - """ - Process docutils-generated dependency info. - """ + """Process docutils-generated dependency info.""" cwd = os.getcwd() frompath = path.join(path.normpath(self.srcdir), 'dummy') deps = doctree.settings.record_dependencies @@ -811,9 +805,7 @@ class BuildEnvironment: self.dependencies.setdefault(docname, set()).add(relpath) def process_downloads(self, docname, doctree): - """ - Process downloadable file paths. - """ + """Process downloadable file paths. """ docdir = path.dirname(self.doc2path(docname, base=None)) for node in doctree.traverse(addnodes.download_reference): targetname = node['reftarget'] @@ -831,9 +823,7 @@ class BuildEnvironment: node['filename'] = uniquename def process_images(self, docname, doctree): - """ - Process and rewrite image URIs. - """ + """Process and rewrite image URIs.""" docdir = path.dirname(self.doc2path(docname, base=None)) for node in doctree.traverse(nodes.image): # Map the mimetype to the corresponding image. The writer may @@ -888,8 +878,8 @@ class BuildEnvironment: self.images.add_file(docname, imgpath) def process_metadata(self, docname, doctree): - """ - Process the docinfo part of the doctree as metadata. + """Process the docinfo part of the doctree as metadata. + Keep processing minimal -- just return what docutils says. """ self.metadata[docname] = md = {} @@ -974,8 +964,7 @@ class BuildEnvironment: item.replace(para, compact_para) def create_title_from(self, docname, document): - """ - Add a title node to the document (just copy the first section title), + """Add a title node to the document (just copy the first section title), and store that title in the environment. """ titlenode = nodes.title() @@ -1013,7 +1002,8 @@ class BuildEnvironment: def note_toctree(self, docname, toctreenode): """Note a TOC tree directive in a document and gather information about - file relations from it.""" + file relations from it. + """ if toctreenode['glob']: self.glob_toctrees.add(docname) if toctreenode.get('numbered'): @@ -1119,7 +1109,9 @@ class BuildEnvironment: def get_domain(self, domainname): """Return the domain instance with the specified name. - Raises an ExtensionError if the domain is not registered.""" + + Raises an ExtensionError if the domain is not registered. + """ try: return self.domains[domainname] except KeyError: @@ -1144,7 +1136,8 @@ class BuildEnvironment: def get_and_resolve_doctree(self, docname, builder, doctree=None, prune_toctrees=True): """Read the doctree from the pickle, resolve cross-references and - toctrees and return it.""" + toctrees and return it. + """ if doctree is None: doctree = self.get_doctree(docname) @@ -1164,8 +1157,7 @@ class BuildEnvironment: def resolve_toctree(self, docname, builder, toctree, prune=True, maxdepth=0, titles_only=False, collapse=False, includehidden=False): - """ - Resolve a *toctree* node into individual bullet lists with titles + """Resolve a *toctree* node into individual bullet lists with titles as items, returning None (if no containing titles are found) or a new node. @@ -1593,7 +1585,6 @@ class BuildEnvironment: def check_consistency(self): """Do consistency checks.""" - for docname in sorted(self.all_docs): if docname not in self.files_to_rebuild: if docname == self.config.master_doc: diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index eef181de4..3a2476a6b 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -85,7 +85,8 @@ def members_set_option(arg): def bool_option(arg): """Used to convert flag options to auto directives. (Instead of - directives.flag(), which returns None.)""" + directives.flag(), which returns None). + """ return True @@ -133,8 +134,7 @@ class AutodocReporter(object): # Some useful event listener factories for autodoc-process-docstring. def cut_lines(pre, post=0, what=None): - """ - Return a listener that removes the first *pre* and last *post* + """Return a listener that removes the first *pre* and last *post* lines of every docstring. If *what* is a sequence of strings, only docstrings of a type in *what* will be processed. @@ -160,9 +160,8 @@ def cut_lines(pre, post=0, what=None): return process def between(marker, what=None, keepempty=False, exclude=False): - """ - Return a listener that either keeps, or if *exclude* is True excludes, lines - between lines that match the *marker* regular expression. If no line + """Return a listener that either keeps, or if *exclude* is True excludes, + lines between lines that match the *marker* regular expression. If no line matches, the resulting docstring would be empty, so no change will be made unless *keepempty* is true. @@ -262,8 +261,7 @@ class Documenter(object): self.directive.result.append(self.indent + line, source, *lineno) def resolve_name(self, modname, parents, path, base): - """ - Resolve the module and name of the object to document given by the + """Resolve the module and name of the object to document given by the arguments and the current module/class. Must return a pair of the module name and a chain of attributes; for @@ -273,8 +271,7 @@ class Documenter(object): raise NotImplementedError('must be implemented in subclasses') def parse_name(self): - """ - Determine what module to import and what attribute to document. + """Determine what module to import and what attribute to document. Returns True and sets *self.modname*, *self.objpath*, *self.fullname*, *self.args* and *self.retann* if parsing and resolving was successful. @@ -311,8 +308,7 @@ class Documenter(object): return True def import_object(self): - """ - Import the object given by *self.modname* and *self.objpath* and sets + """Import the object given by *self.modname* and *self.objpath* and set it as *self.object*. Returns True if successful, False if an error occurred. @@ -338,15 +334,15 @@ class Documenter(object): return False def get_real_modname(self): - """ - Get the real module name of an object to document. (It can differ - from the name of the module through which the object was imported.) + """Get the real module name of an object to document. + + It can differ from the name of the module through which the object was + imported. """ return self.get_attr(self.object, '__module__', None) or self.modname def check_module(self): - """ - Check if *self.object* is really defined in the module given by + """Check if *self.object* is really defined in the module given by *self.modname*. """ modname = self.get_attr(self.object, '__module__', None) @@ -355,25 +351,26 @@ class Documenter(object): return True def format_args(self): - """ - Format the argument signature of *self.object*. Should return None if - the object does not have a signature. + """Format the argument signature of *self.object*. + + Should return None if the object does not have a signature. """ return None def format_name(self): - """ - Format the name of *self.object*. This normally should be something - that can be parsed by the generated directive, but doesn't need to be - (Sphinx will display it unparsed then). + """Format the name of *self.object*. + + This normally should be something that can be parsed by the generated + directive, but doesn't need to be (Sphinx will display it unparsed + then). """ # normally the name doesn't contain the module (except for module # directives of course) return '.'.join(self.objpath) or self.modname def format_signature(self): - """ - Format the signature (arguments and return annotation) of the object. + """Format the signature (arguments and return annotation) of the object. + Let the user process it via the ``autodoc-process-signature`` event. """ if self.args is not None: @@ -473,8 +470,7 @@ class Documenter(object): self.add_line(line, src[0], src[1]) def get_object_members(self, want_all): - """ - Return `(members_check_module, members)` where `members` is a + """Return `(members_check_module, members)` where `members` is a list of `(membername, member)` pairs of the members of *self.object*. If *want_all* is True, return all members. Else, only return those @@ -518,8 +514,9 @@ class Documenter(object): return False, sorted(members) def filter_members(self, members, want_all): - """ - Filter the given member list: members are skipped if + """Filter the given member list. + + Members are skipped if - they are private (except if given explicitly) - they are undocumented (except if undoc-members is given) @@ -572,9 +569,10 @@ class Documenter(object): return ret def document_members(self, all_members=False): - """ - Generate reST for member documentation. If *all_members* is True, - do all members, else those given by *self.options.members*. + """Generate reST for member documentation. + + If *all_members* is True, do all members, else those given by + *self.options.members*. """ # set current namespace for finding members self.env.temp_data['autodoc:module'] = self.modname @@ -632,8 +630,8 @@ class Documenter(object): def generate(self, more_content=None, real_modname=None, check_module=False, all_members=False): - """ - Generate reST for the object given by *self.name*, and possibly members. + """Generate reST for the object given by *self.name*, and possibly for + its members. If *more_content* is given, include that content. If *real_modname* is given, use that module name to find attribute docs. If *check_module* is diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index cf67c7fb4..8186a2e58 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -73,8 +73,7 @@ class autosummary_toc(nodes.comment): pass def process_autosummary_toc(app, doctree): - """ - Insert items described in autosummary:: to the TOC tree, but do + """Insert items described in autosummary:: to the TOC tree, but do not generate the toctree:: list. """ env = app.builder.env @@ -135,8 +134,8 @@ except AttributeError: isgetsetdescriptor = ismemberdescriptor def get_documenter(obj): - """ - Get an autodoc.Documenter class suitable for documenting the given object + """Get an autodoc.Documenter class suitable for documenting the given + object. """ import sphinx.ext.autodoc as autodoc @@ -218,8 +217,7 @@ class Autosummary(Directive): return self.warnings + nodes def get_items(self, names): - """ - Try to import the given names, and return a list of + """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name), ...]``. """ env = self.state.document.settings.env @@ -287,8 +285,7 @@ class Autosummary(Directive): return items def get_table(self, items): - """ - Generate a proper list of table nodes for autosummary:: directive. + """Generate a proper list of table nodes for autosummary:: directive. *items* is a list produced by :meth:`get_items`. """ @@ -351,8 +348,7 @@ def mangle_signature(sig, max_chars=30): return u"(%s)" % sig def limited_join(sep, items, max_chars=30, overflow_marker="..."): - """ - Join a number of strings to one, limiting the length to *max_chars*. + """Join a number of strings to one, limiting the length to *max_chars*. If the string overflows this limit, replace the last fitting item by *overflow_marker*. @@ -377,8 +373,7 @@ def limited_join(sep, items, max_chars=30, overflow_marker="..."): # -- Importing items ----------------------------------------------------------- def import_by_name(name, prefixes=[None]): - """ - Import a Python object that has the given *name*, under one of the + """Import a Python object that has the given *name*, under one of the *prefixes*. The first name that succeeds is used. """ tried = [] @@ -435,8 +430,7 @@ def _import_by_name(name): def autolink_role(typ, rawtext, etext, lineno, inliner, options={}, content=[]): - """ - Smart linking role. + """Smart linking role. Expands to ':obj:`text`' if `text` is an object that can be imported; otherwise expands to '*text*'. diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 66a124d27..4b6348b58 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -17,6 +17,7 @@ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ + import os import re import sys @@ -193,8 +194,8 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', # -- Finding documented entries in files --------------------------------------- def find_autosummary_in_files(filenames): - """ - Find out what items are documented in source/*.rst. + """Find out what items are documented in source/*.rst. + See `find_autosummary_in_lines`. """ documented = [] @@ -206,8 +207,8 @@ def find_autosummary_in_files(filenames): return documented def find_autosummary_in_docstring(name, module=None, filename=None): - """ - Find out what items are documented in the given object's docstring. + """Find out what items are documented in the given object's docstring. + See `find_autosummary_in_lines`. """ try: @@ -221,8 +222,8 @@ def find_autosummary_in_docstring(name, module=None, filename=None): return [] def find_autosummary_in_lines(lines, module=None, filename=None): - """ - Find out what items appear in autosummary:: directives in the given lines. + """Find out what items appear in autosummary:: directives in the + given lines. Returns a list of (name, toctree, template) where *name* is a name of an object and *toctree* the :toctree: path of the corresponding diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 257ff1b63..1fe2ac4b5 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -89,9 +89,7 @@ class GraphvizSimple(Directive): def render_dot(self, code, options, format, prefix='graphviz'): - """ - Render graphviz code into a PNG or PDF output file. - """ + """Render graphviz code into a PNG or PDF output file.""" hashkey = code.encode('utf-8') + str(options) + \ str(self.builder.config.graphviz_dot) + \ str(self.builder.config.graphviz_dot_args) diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index a12bad256..c697a4131 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -67,8 +67,7 @@ class InheritanceGraph(object): graphviz dot graph from them. """ def __init__(self, class_names, currmodule, show_builtins=False): - """ - *class_names* is a list of child classes to show bases from. + """*class_names* is a list of child classes to show bases from. If *show_builtins* is True, then Python builtins will be shown in the graph. @@ -82,9 +81,7 @@ class InheritanceGraph(object): self.show_builtins = show_builtins def _import_class_or_module(self, name, currmodule): - """ - Import a class using its fully-qualified *name*. - """ + """Import a class using its fully-qualified *name*.""" try: path, base = class_sig_re.match(name).groups() except ValueError: @@ -129,18 +126,14 @@ class InheritanceGraph(object): 'not a class or module' % name) def _import_classes(self, class_names, currmodule): - """ - Import a list of classes. - """ + """Import a list of classes.""" classes = [] for name in class_names: classes.extend(self._import_class_or_module(name, currmodule)) return classes def _all_classes(self, classes): - """ - Return a list of all classes that are ancestors of *classes*. - """ + """Return a list of all classes that are ancestors of *classes*.""" all_classes = {} def recurse(cls): @@ -155,10 +148,10 @@ class InheritanceGraph(object): return all_classes.keys() def class_name(self, cls, parts=0): - """ - Given a class object, return a fully-qualified name. This - works for things I've tested in matplotlib so far, but may not - be completely general. + """Given a class object, return a fully-qualified name. + + This works for things I've tested in matplotlib so far, but may not be + completely general. """ module = cls.__module__ if module == '__builtin__': @@ -171,9 +164,7 @@ class InheritanceGraph(object): return '.'.join(name_parts[-parts:]) def get_all_class_names(self): - """ - Get all of the class names involved in the graph. - """ + """Get all of the class names involved in the graph.""" return [self.class_name(x) for x in self.all_classes] # These are the default attrs for graphviz @@ -202,9 +193,8 @@ class InheritanceGraph(object): def generate_dot(self, name, parts=0, urls={}, env=None, graph_attrs={}, node_attrs={}, edge_attrs={}): - """ - Generate a graphviz dot graph from the classes that - were passed in to __init__. + """Generate a graphviz dot graph from the classes that were passed in + to __init__. *name* is the name of the graph. diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py index 00ac37495..bc921a23c 100644 --- a/sphinx/ext/oldcmarkup.py +++ b/sphinx/ext/oldcmarkup.py @@ -18,6 +18,7 @@ WARNING_MSG = 'using old C markup; please migrate to new-style markup ' \ '(e.g. c:function instead of cfunction), see ' \ 'http://sphinx.pocoo.org/domains.html' + class OldCDirective(Directive): has_content = True required_arguments = 1 diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py index 7f3997543..5903c53d5 100644 --- a/sphinx/ext/pngmath.py +++ b/sphinx/ext/pngmath.py @@ -61,8 +61,7 @@ DOC_BODY_PREVIEW = r''' depth_re = re.compile(r'\[\d+ depth=(-?\d+)\]') def render_math(self, math): - """ - Render the LaTeX math expression *math* using latex and dvipng. + """Render the LaTeX math expression *math* using latex and dvipng. Return the filename relative to the built document and the "depth", that is, the distance of image bottom and baseline in pixels, if the diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py index a6f1a853e..29ee334e1 100644 --- a/sphinx/jinja2glue.py +++ b/sphinx/jinja2glue.py @@ -37,8 +37,10 @@ def accesskey(context, key): class SphinxFileSystemLoader(FileSystemLoader): - """FileSystemLoader subclass that is not so strict about '..' - entries in template names.""" + """ + FileSystemLoader subclass that is not so strict about '..' entries in + template names. + """ def get_source(self, environment, template): for searchpath in self.searchpath: diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index de106bb9b..126a37b5a 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -15,8 +15,9 @@ import UserString class _TranslationProxy(UserString.UserString, object): - """Class for proxy strings from gettext translations. This is a helper - for the lazy_* functions from this module. + """ + Class for proxy strings from gettext translations. This is a helper for the + lazy_* functions from this module. The proxy implementation attempts to be as complete as possible, so that the lazy objects should mostly work as expected, for example for sorting. @@ -137,7 +138,8 @@ class _TranslationProxy(UserString.UserString, object): def mygettext(string): """Used instead of _ when creating TranslationProxies, because _ is - not bound yet at that time.""" + not bound yet at that time. + """ return _(string) def lazy_gettext(string): @@ -189,8 +191,7 @@ else: def init(locale_dirs, language, catalog='sphinx'): - """ - Look for message catalogs in `locale_dirs` and *ensure* that there is at + """Look for message catalogs in `locale_dirs` and *ensure* that there is at least a NullTranslations catalog set in `translators`. If called multiple times or if several ``.mo`` files are found, their contents are merged together (thus making ``init`` reentrable). diff --git a/sphinx/roles.py b/sphinx/roles.py index 0ea0ec485..f08c3f005 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -139,16 +139,15 @@ class XRefRole(object): # methods that can be overwritten def process_link(self, env, refnode, has_explicit_title, title, target): - """ - Called after parsing title and target text, and creating the reference - node (given in *refnode*). This method can alter the reference node and - must return a new (or the same) ``(title, target)`` tuple. + """Called after parsing title and target text, and creating the + reference node (given in *refnode*). This method can alter the + reference node and must return a new (or the same) ``(title, target)`` + tuple. """ return title, ws_re.sub(' ', target) def result_nodes(self, document, env, node, is_ref): - """ - Called before returning the finished nodes. *node* is the reference + """Called before returning the finished nodes. *node* is the reference node if one was created (*is_ref* is then true), else the content node. This method can add other nodes and must return a ``(nodes, messages)`` tuple (the usual return value of a role function). diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py index 8974b9888..865ebb67b 100644 --- a/sphinx/setup_command.py +++ b/sphinx/setup_command.py @@ -22,7 +22,8 @@ from sphinx.util.console import darkred, nocolor, color_terminal class BuildDoc(Command): - """Distutils command to build Sphinx documentation. + """ + Distutils command to build Sphinx documentation. The Sphinx build can then be triggered from distutils, and some Sphinx options can be set in ``setup.py`` or ``setup.cfg`` instead of Sphinx own diff --git a/sphinx/theming.py b/sphinx/theming.py index 0d0f28634..92e63f31c 100644 --- a/sphinx/theming.py +++ b/sphinx/theming.py @@ -98,8 +98,7 @@ class Theme(object): self.base = Theme(inherit) def get_confstr(self, section, name, default=NODEFAULT): - """ - Return the value for a theme configuration setting, searching the + """Return the value for a theme configuration setting, searching the base theme chain. """ try: @@ -114,9 +113,7 @@ class Theme(object): return default def get_options(self, overrides): - """ - Return a dictionary of theme options and their values. - """ + """Return a dictionary of theme options and their values.""" chain = [self.themeconf] base = self.base while base is not None: @@ -135,8 +132,7 @@ class Theme(object): return options def get_dirchain(self): - """ - Return a list of theme directories, beginning with this theme's, + """Return a list of theme directories, beginning with this theme's, then the base theme's, then that one's base theme's, etc. """ chain = [self.themedir] @@ -147,9 +143,7 @@ class Theme(object): return chain def cleanup(self): - """ - Remove temporary directories. - """ + """Remove temporary directories.""" if self.themedir_created: try: shutil.rmtree(self.themedir) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 6a38351f7..a3d30d9d2 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -50,8 +50,7 @@ def docname_join(basedocname, docname): def get_matching_files(dirname, exclude_matchers=()): - """ - Get all file names in a directory, recursively. + """Get all file names in a directory, recursively. Exclude files and dirs matching some matcher in *exclude_matchers*. """ @@ -77,9 +76,8 @@ def get_matching_files(dirname, exclude_matchers=()): def get_matching_docs(dirname, suffix, exclude_matchers=()): - """ - Get all file names (without suffix) matching a suffix in a - directory, recursively. + """Get all file names (without suffix) matching a suffix in a directory, + recursively. Exclude files and dirs matching a pattern in *exclude_patterns*. """ @@ -171,9 +169,7 @@ _DEBUG_HEADER = '''\ ''' def save_traceback(): - """ - Save the current exception's traceback in a temporary file. - """ + """Save the current exception's traceback in a temporary file.""" exc = traceback.format_exc() fd, path = tempfile.mkstemp('.log', 'sphinx-err-') os.write(fd, (_DEBUG_HEADER % @@ -233,8 +229,7 @@ class Tee(object): def parselinenos(spec, total): - """ - Parse a line number spec (such as "1,2,4-6") and return a list of + """Parse a line number spec (such as "1,2,4-6") and return a list of wanted line numbers. """ items = list() @@ -288,9 +283,7 @@ def rpartition(s, t): def format_exception_cut_frames(x=1): - """ - Format an exception with traceback, but only the last x frames. - """ + """Format an exception with traceback, but only the last x frames.""" typ, val, tb = sys.exc_info() #res = ['Traceback (most recent call last):\n'] res = [] diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py index 538af653e..d1a2ff8db 100644 --- a/sphinx/util/docstrings.py +++ b/sphinx/util/docstrings.py @@ -13,11 +13,11 @@ import sys def prepare_docstring(s): - """ - Convert a docstring into lines of parseable reST. Return it as a list of - lines usable for inserting into a docutils ViewList (used as argument - of nested_parse().) An empty line is added to act as a separator between - this docstring and following content. + """Convert a docstring into lines of parseable reST. + + Return it as a list of lines usable for inserting into a docutils ViewList + (used as argument of nested_parse().) An empty line is added to act as a + separator between this docstring and following content. """ lines = s.expandtabs().splitlines() # Find minimum indentation of any non-blank lines after first line. @@ -42,9 +42,8 @@ def prepare_docstring(s): def prepare_commentdoc(s): - """ - Extract documentation comment lines (starting with #:) and return them as a - list of lines. Returns an empty list if there is no documentation. + """Extract documentation comment lines (starting with #:) and return them + as a list of lines. Returns an empty list if there is no documentation. """ result = [] lines = [line.strip() for line in s.expandtabs().splitlines()] diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py index fda85b5e3..f654ef22a 100644 --- a/sphinx/util/jsonimpl.py +++ b/sphinx/util/jsonimpl.py @@ -13,7 +13,7 @@ import UserString try: import json - # json-py's json module has not JSONEncoder; this will raise AttributeError + # json-py's json module has no JSONEncoder; this will raise AttributeError # if json-py is imported instead of the built-in json module JSONEncoder = json.JSONEncoder except (ImportError, AttributeError): diff --git a/sphinx/util/matching.py b/sphinx/util/matching.py index c459aca2d..3746c87c4 100644 --- a/sphinx/util/matching.py +++ b/sphinx/util/matching.py @@ -13,8 +13,7 @@ import re def _translate_pattern(pat): - """ - Translate a shell-style glob pattern to a regular expression. + """Translate a shell-style glob pattern to a regular expression. Adapted from the fnmatch module, but enhanced so that single stars don't match slashes. @@ -65,16 +64,14 @@ def compile_matchers(patterns): _pat_cache = {} def patmatch(name, pat): - """ - Return if name matches pat. Adapted from fnmatch module. - """ + """Return if name matches pat. Adapted from fnmatch module.""" if pat not in _pat_cache: _pat_cache[pat] = re.compile(_translate_pattern(pat)) return _pat_cache[pat].match(name) def patfilter(names, pat): - """ - Return the subset of the list NAMES that match PAT. + """Return the subset of the list NAMES that match PAT. + Adapted from fnmatch module. """ if pat not in _pat_cache: diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 9ecf0d4ab..2e383b0ac 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -38,6 +38,12 @@ def extract_messages(doctree): def nested_parse_with_titles(state, content, node): + """Version of state.nested_parse() that allows titles and does not require + titles to have the same decoration as the calling document. + + This is useful when the parsed content comes from a completely different + context, such as docstrings. + """ # hack around title style bookkeeping surrounding_title_styles = state.memo.title_styles surrounding_section_level = state.memo.section_level diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 9943b207f..464e56ed9 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -59,8 +59,8 @@ def ensuredir(path): def walk(top, topdown=True, followlinks=False): - """ - Backport of os.walk from 2.6, where the followlinks argument was added. + """Backport of os.walk from 2.6, where the *followlinks* argument was + added. """ names = os.listdir(top) diff --git a/sphinx/util/png.py b/sphinx/util/png.py index 2cb2aa9d8..ca2273c4a 100644 --- a/sphinx/util/png.py +++ b/sphinx/util/png.py @@ -21,9 +21,7 @@ IEND_CHUNK = '\x00\x00\x00\x00IEND\xAE\x42\x60\x82' def read_png_depth(filename): - """ - Read the special tEXt chunk indicating the depth from a PNG file. - """ + """Read the special tEXt chunk indicating the depth from a PNG file.""" result = None f = open(filename, 'rb') try: @@ -39,8 +37,8 @@ def read_png_depth(filename): def write_png_depth(filename, depth): - """ - Write the special tEXt chunk indicating the depth to a PNG file. + """Write the special tEXt chunk indicating the depth to a PNG file. + The chunk is placed immediately before the special IEND chunk. """ data = struct.pack('!i', depth) diff --git a/sphinx/util/websupport.py b/sphinx/util/websupport.py index f99f4d31d..510ecbe0e 100644 --- a/sphinx/util/websupport.py +++ b/sphinx/util/websupport.py @@ -6,5 +6,6 @@ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ + def is_commentable(node): return node.__class__.__name__ in ('paragraph', 'literal_block') diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 30e8c4dc8..bb80fb7ee 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -3,7 +3,7 @@ sphinx.writers.websupport ~~~~~~~~~~~~~~~~~~~~~~~~~ - docutils writers handling Sphinx' custom nodes. + sphinx.websupport writer that adds comment-related annotations. :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. @@ -12,6 +12,7 @@ from sphinx.writers.html import HTMLTranslator from sphinx.util.websupport import is_commentable + class WebSupportTranslator(HTMLTranslator): """ Our custom HTML translator. From 62fc1ab87df00941faff3865e7201f160cb30f1b Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Mon, 23 Aug 2010 13:06:22 +0000 Subject: [PATCH 376/744] List GSOC project authors. --- AUTHORS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 10120c7b4..2ee77739f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,12 +15,14 @@ Other contributors, listed alphabetically, are: * Martin Hans -- autodoc improvements * Dave Kuhlman -- original LaTeX writer * Thomas Lamb -- linkcheck builder +* Robert Lehmann -- gettext builder (GSOC project) * Dan MacKinlay -- metadata fixes * Martin Mahner -- nature theme * Will Maier -- directory HTML builder +* Jacob Mason -- websupport library (GSOC project) * Roland Meister -- epub builder * Ezio Melotti -- collapsible sidebar JavaScript -* Daniel Neuhäuser -- JavaScript domain +* Daniel Neuhäuser -- JavaScript domain, Python 3 support (GSOC) * Christopher Perkins -- autosummary integration * Benjamin Peterson -- unittests * T. Powers -- HTML output improvements From 398fb3f00c93fcb7c5eb535027d01cb8ba10d271 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Mon, 23 Aug 2010 13:07:19 +0000 Subject: [PATCH 377/744] Add new env method to get the real path to a file reference, and use it. --- sphinx/directives/code.py | 24 ++++-------------- sphinx/directives/other.py | 9 +++---- sphinx/environment.py | 52 +++++++++++++++++++++++--------------- 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index 1808cdaba..d235c4cab 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -7,10 +7,8 @@ :license: BSD, see LICENSE for details. """ -import os import sys import codecs -from os import path from docutils import nodes from docutils.parsers.rst import Directive, directives @@ -93,23 +91,11 @@ class LiteralInclude(Directive): def run(self): document = self.state.document - filename = self.arguments[0] if not document.settings.file_insertion_enabled: return [document.reporter.warning('File insertion disabled', line=self.lineno)] env = document.settings.env - if filename.startswith('/') or filename.startswith(os.sep): - rel_fn = filename[1:] - else: - docdir = path.dirname(env.doc2path(env.docname, base=None)) - rel_fn = path.join(docdir, filename) - try: - fn = path.join(env.srcdir, rel_fn) - except UnicodeDecodeError: - # the source directory is a bytestring with non-ASCII characters; - # let's try to encode the rel_fn in the file system encoding - rel_fn = rel_fn.encode(sys.getfilesystemencoding()) - fn = path.join(env.srcdir, rel_fn) + rel_filename, filename = env.relfn2path(self.arguments[0]) if 'pyobject' in self.options and 'lines' in self.options: return [document.reporter.warning( @@ -119,7 +105,7 @@ class LiteralInclude(Directive): encoding = self.options.get('encoding', env.config.source_encoding) codec_info = codecs.lookup(encoding) try: - f = codecs.StreamReaderWriter(open(fn, 'rb'), + f = codecs.StreamReaderWriter(open(filename, 'rb'), codec_info[2], codec_info[3], 'strict') lines = f.readlines() f.close() @@ -136,7 +122,7 @@ class LiteralInclude(Directive): objectname = self.options.get('pyobject') if objectname is not None: from sphinx.pycode import ModuleAnalyzer - analyzer = ModuleAnalyzer.for_file(fn, '') + analyzer = ModuleAnalyzer.for_file(filename, '') tags = analyzer.find_tags() if objectname not in tags: return [document.reporter.warning( @@ -178,13 +164,13 @@ class LiteralInclude(Directive): text = ''.join(lines) if self.options.get('tab-width'): text = text.expandtabs(self.options['tab-width']) - retnode = nodes.literal_block(text, text, source=fn) + retnode = nodes.literal_block(text, text, source=filename) retnode.line = 1 if self.options.get('language', ''): retnode['language'] = self.options['language'] if 'linenos' in self.options: retnode['linenos'] = True - document.settings.env.note_dependency(rel_fn) + env.note_dependency(rel_filename) return [retnode] diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 332c40849..45c64030a 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -369,14 +369,13 @@ from docutils.parsers.rst.directives.misc import Include as BaseInclude class Include(BaseInclude): """ Like the standard "Include" directive, but interprets absolute paths - correctly. + "correctly", i.e. relative to source directory. """ def run(self): - if self.arguments[0].startswith('/') or \ - self.arguments[0].startswith(os.sep): - env = self.state.document.settings.env - self.arguments[0] = os.path.join(env.srcdir, self.arguments[0][1:]) + env = self.state.document.settings.env + rel_filename, filename = env.relfn2path(self.arguments[0]) + self.arguments[0] = filename return BaseInclude.run(self) diff --git a/sphinx/environment.py b/sphinx/environment.py index f8d3641b3..2a48bdf27 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -433,6 +433,27 @@ class BuildEnvironment: else: return path.join(base, docname) + suffix + def relfn2path(self, filename, docname=None): + """Return paths to a file referenced from a document, relative to + documentation root and absolute. + + Absolute filenames are relative to the source dir, while relative + filenames are relative to the dir of the containing document. + """ + if filename.startswith('/') or filename.startswith(os.sep): + rel_fn = filename[1:] + else: + docdir = path.dirname(self.doc2path(docname or self.docname, + base=None)) + rel_fn = path.join(docdir, filename) + try: + return rel_fn, path.join(self.srcdir, rel_fn) + except UnicodeDecodeError: + # the source directory is a bytestring with non-ASCII characters; + # let's try to encode the rel_fn in the file system encoding + enc_rel_fn = rel_fn.encode(sys.getfilesystemencoding()) + return rel_fn, path.join(self.srcdir, enc_rel_fn) + def find_files(self, config): """Find all source files in the source dir and put them in self.found_docs. @@ -806,25 +827,19 @@ class BuildEnvironment: def process_downloads(self, docname, doctree): """Process downloadable file paths. """ - docdir = path.dirname(self.doc2path(docname, base=None)) for node in doctree.traverse(addnodes.download_reference): targetname = node['reftarget'] - if targetname.startswith('/') or targetname.startswith(os.sep): - # absolute - filepath = targetname[1:] - else: - filepath = path.normpath(path.join(docdir, node['reftarget'])) - self.dependencies.setdefault(docname, set()).add(filepath) - if not os.access(path.join(self.srcdir, filepath), os.R_OK): - self.warn(docname, 'download file not readable: %s' % filepath, + rel_filename, filename = self.relfn2path(targetname, docname) + self.dependencies.setdefault(docname, set()).add(rel_filename) + if not os.access(filename, os.R_OK): + self.warn(docname, 'download file not readable: %s' % filename, getattr(node, 'line', None)) continue - uniquename = self.dlfiles.add_file(docname, filepath) + uniquename = self.dlfiles.add_file(docname, filename) node['filename'] = uniquename def process_images(self, docname, doctree): """Process and rewrite image URIs.""" - docdir = path.dirname(self.doc2path(docname, base=None)) for node in doctree.traverse(nodes.image): # Map the mimetype to the corresponding image. The writer may # choose the best image from these candidates. The special key * is @@ -837,16 +852,11 @@ class BuildEnvironment: node.line) candidates['?'] = imguri continue - # imgpath is the image path *from srcdir* - if imguri.startswith('/') or imguri.startswith(os.sep): - # absolute path (= relative to srcdir) - imgpath = path.normpath(imguri[1:]) - else: - imgpath = path.normpath(path.join(docdir, imguri)) + rel_imgpath, full_imgpath = self.relfn2path(imguri, docname) # set imgpath as default URI - node['uri'] = imgpath - if imgpath.endswith(os.extsep + '*'): - for filename in glob(path.join(self.srcdir, imgpath)): + node['uri'] = rel_imgpath + if rel_imgpath.endswith(os.extsep + '*'): + for filename in glob(full_imgpath): new_imgpath = relative_path(self.srcdir, filename) if filename.lower().endswith('.pdf'): candidates['application/pdf'] = new_imgpath @@ -866,7 +876,7 @@ class BuildEnvironment: if imgtype: candidates['image/' + imgtype] = new_imgpath else: - candidates['*'] = imgpath + candidates['*'] = rel_imgpath # map image paths to unique image names (so that they can be put # into a single directory) for imgpath in candidates.itervalues(): From 9c2747da91d0930af6c648c51b2a02a5830292e2 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Mon, 23 Aug 2010 13:07:28 +0000 Subject: [PATCH 378/744] #443: Allow referencing external graphviz files. --- CHANGES | 7 +++++++ doc/ext/graphviz.rst | 11 +++++++++++ sphinx/ext/graphviz.py | 33 +++++++++++++++++++++++++++------ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 3417d9aa7..d46d2288f 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,13 @@ Release 1.1 (in development) * Added Python 3.x support. +* Added i18n support for content, a ``gettext`` builder and + related utilities. + +* Added the ``websupport`` library. + +* #443: Allow referencing external graphviz files. + Release 1.0.2 (Aug 14, 2010) ============================ diff --git a/doc/ext/graphviz.rst b/doc/ext/graphviz.rst index 3741cec64..de6e03e27 100644 --- a/doc/ext/graphviz.rst +++ b/doc/ext/graphviz.rst @@ -29,6 +29,17 @@ It adds these directives: :confval:`graphviz_output_format`). In LaTeX output, the code will be rendered to an embeddable PDF file. + You can also embed external dot files, by giving the file name as an + argument to :rst:dir:`graphviz` and no additional content:: + + .. graphviz:: external.dot + + As for all file references in Sphinx, if the filename is absolute, it is + taken as relative to the source directory. + + .. versionchanged:: 1.1 + Added support for external files. + .. rst:directive:: graph diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 1fe2ac4b5..19dcd951a 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -11,6 +11,7 @@ """ import re +import codecs import posixpath from os import path from math import ceil @@ -46,18 +47,38 @@ class Graphviz(Directive): """ has_content = True required_arguments = 0 - optional_arguments = 0 + optional_arguments = 1 final_argument_whitespace = False option_spec = { 'alt': directives.unchanged, } def run(self): - dotcode = '\n'.join(self.content) - if not dotcode.strip(): - return [self.state_machine.reporter.warning( - 'Ignoring "graphviz" directive without content.', - line=self.lineno)] + if self.arguments: + document = self.state.document + if self.content: + return [document.reporter.warning( + 'Graphviz directive cannot have both content and ' + 'a filename argument', line=self.lineno)] + env = self.state.document.settings.env + rel_filename, filename = env.relfn2path(self.arguments[0]) + env.note_dependency(rel_filename) + try: + fp = codecs.open(filename, 'r', 'utf-8') + try: + dotcode = fp.read() + finally: + fp.close() + except (IOError, OSError): + return [document.reporter.warning( + 'External Graphviz file %r not found or reading ' + 'it failed' % filename, line=self.lineno)] + else: + dotcode = '\n'.join(self.content) + if not dotcode.strip(): + return [self.state_machine.reporter.warning( + 'Ignoring "graphviz" directive without content.', + line=self.lineno)] node = graphviz() node['code'] = dotcode node['options'] = [] From c0d493cec9803d5920c897e62ef776d985088122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Mon, 23 Aug 2010 18:16:19 +0200 Subject: [PATCH 379/744] Use b() so that the regex is a bytestring --- sphinx/ext/pngmath.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py index 5903c53d5..e4e7c2d06 100644 --- a/sphinx/ext/pngmath.py +++ b/sphinx/ext/pngmath.py @@ -26,6 +26,7 @@ from docutils import nodes from sphinx.errors import SphinxError from sphinx.util.png import read_png_depth, write_png_depth from sphinx.util.osutil import ensuredir, ENOENT +from sphinx.util.pycompat import b from sphinx.ext.mathbase import setup_math as mathbase_setup, wrap_displaymath class MathExtError(SphinxError): @@ -58,7 +59,7 @@ DOC_BODY_PREVIEW = r''' \end{document} ''' -depth_re = re.compile(r'\[\d+ depth=(-?\d+)\]') +depth_re = re.compile(b(r'\[\d+ depth=(-?\d+)\]')) def render_math(self, math): """Render the LaTeX math expression *math* using latex and dvipng. From 53192eebb0b6168750b054b2d6ffe12652e82ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Mon, 23 Aug 2010 18:18:04 +0200 Subject: [PATCH 380/744] Make chunks bytestrings --- sphinx/util/png.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sphinx/util/png.py b/sphinx/util/png.py index ca2273c4a..59c327151 100644 --- a/sphinx/util/png.py +++ b/sphinx/util/png.py @@ -12,12 +12,14 @@ import struct import binascii +from sphinx.util.pycompat import b + LEN_IEND = 12 LEN_DEPTH = 22 DEPTH_CHUNK_LEN = struct.pack('!i', 10) -DEPTH_CHUNK_START = 'tEXtDepth\x00' -IEND_CHUNK = '\x00\x00\x00\x00IEND\xAE\x42\x60\x82' +DEPTH_CHUNK_START = b('tEXtDepth\x00') +IEND_CHUNK = b('\x00\x00\x00\x00IEND\xAE\x42\x60\x82') def read_png_depth(filename): From 2e520e5e7be5fb5d355cb26a1b50dbc91433ceba Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 25 Aug 2010 09:59:53 +0000 Subject: [PATCH 381/744] #460: Allow limiting the depth of section numbers for HTML. --- CHANGES | 2 ++ doc/markup/toctree.rst | 14 +++++++++++++- sphinx/directives/other.py | 10 ++++++++-- sphinx/environment.py | 28 ++++++++++++++++++---------- sphinx/writers/html.py | 6 +++--- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 79012cfee..6f89ae15b 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,8 @@ Release 1.1 (in development) * Added the ``websupport`` library. +* #460: Allow limiting the depth of section numbers for HTML. + * #443: Allow referencing external graphviz files. diff --git a/doc/markup/toctree.rst b/doc/markup/toctree.rst index 2c0a418a2..0b6a46c17 100644 --- a/doc/markup/toctree.rst +++ b/doc/markup/toctree.rst @@ -41,6 +41,8 @@ tables of contents. The ``toctree`` directive is the central element. document, the library index. From this information it generates "next chapter", "previous chapter" and "parent chapter" links. + **Entries** + Document titles in the :rst:dir:`toctree` will be automatically read from the title of the referenced document. If that isn't what you want, you can specify an explicit title and target using a similar syntax to reST @@ -59,8 +61,10 @@ tables of contents. The ``toctree`` directive is the central element. You can also add external links, by giving an HTTP URL instead of a document name. + **Section numbering** + If you want to have section numbers even in HTML output, give the toctree a - ``numbered`` flag option. For example:: + ``numbered`` option. For example:: .. toctree:: :numbered: @@ -71,6 +75,11 @@ tables of contents. The ``toctree`` directive is the central element. Numbering then starts at the heading of ``foo``. Sub-toctrees are automatically numbered (don't give the ``numbered`` flag to those). + Numbering up to a specific depth is also possible, by giving the depth as a + numeric argument to ``numbered``. + + **Additional options** + If you want only the titles of documents in the tree to show up, not other headings of the same level, you can use the ``titlesonly`` option:: @@ -133,6 +142,9 @@ tables of contents. The ``toctree`` directive is the central element. .. versionchanged:: 1.0 Added "titlesonly" option. + .. versionchanged:: 1.1 + Added numeric argument to "numbered". + Special names ------------- diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 45c64030a..b5252e86f 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -20,6 +20,12 @@ from sphinx.util.compat import make_admonition from sphinx.util.matching import patfilter +def int_or_nothing(argument): + if not argument: + return 999 + return int(argument) + + class TocTree(Directive): """ Directive to notify Sphinx about the hierarchical structure of the docs, @@ -34,7 +40,7 @@ class TocTree(Directive): 'maxdepth': int, 'glob': directives.flag, 'hidden': directives.flag, - 'numbered': directives.flag, + 'numbered': int_or_nothing, 'titlesonly': directives.flag, } @@ -98,7 +104,7 @@ class TocTree(Directive): subnode['maxdepth'] = self.options.get('maxdepth', -1) subnode['glob'] = glob subnode['hidden'] = 'hidden' in self.options - subnode['numbered'] = 'numbered' in self.options + subnode['numbered'] = self.options.get('numbered', 0) subnode['titlesonly'] = 'titlesonly' in self.options wrappernode = nodes.compound(classes=['toctree-wrapper']) wrappernode.append(subnode) diff --git a/sphinx/environment.py b/sphinx/environment.py index 4a19968c6..883c91e99 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1426,46 +1426,54 @@ class BuildEnvironment: old_secnumbers = self.toc_secnumbers self.toc_secnumbers = {} - def _walk_toc(node, secnums, titlenode=None): + def _walk_toc(node, secnums, depth, titlenode=None): # titlenode is the title of the document, it will get assigned a # secnumber too, so that it shows up in next/prev/parent rellinks for subnode in node.children: if isinstance(subnode, nodes.bullet_list): numstack.append(0) - _walk_toc(subnode, secnums, titlenode) + _walk_toc(subnode, secnums, depth-1, titlenode) numstack.pop() titlenode = None elif isinstance(subnode, nodes.list_item): - _walk_toc(subnode, secnums, titlenode) + _walk_toc(subnode, secnums, depth, titlenode) titlenode = None elif isinstance(subnode, addnodes.compact_paragraph): numstack[-1] += 1 + if depth > 0: + number = tuple(numstack) + else: + number = None secnums[subnode[0]['anchorname']] = \ - subnode[0]['secnumber'] = tuple(numstack) + subnode[0]['secnumber'] = number if titlenode: - titlenode['secnumber'] = tuple(numstack) + titlenode['secnumber'] = number titlenode = None elif isinstance(subnode, addnodes.toctree): - _walk_toctree(subnode) + _walk_toctree(subnode, depth) - def _walk_toctree(toctreenode): + def _walk_toctree(toctreenode, depth): + if depth == 0: + return for (title, ref) in toctreenode['entries']: if url_re.match(ref) or ref == 'self': # don't mess with those continue if ref in self.tocs: secnums = self.toc_secnumbers[ref] = {} - _walk_toc(self.tocs[ref], secnums, self.titles.get(ref)) + _walk_toc(self.tocs[ref], secnums, depth, + self.titles.get(ref)) if secnums != old_secnumbers.get(ref): rewrite_needed.append(ref) for docname in self.numbered_toctrees: doctree = self.get_doctree(docname) for toctreenode in doctree.traverse(addnodes.toctree): - if toctreenode.get('numbered'): + depth = toctreenode.get('numbered', 0) + if depth: # every numbered toctree gets new numbering numstack = [0] - _walk_toctree(toctreenode) + _walk_toctree(toctreenode, depth) return rewrite_needed diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index f206e479a..33d90c915 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -180,7 +180,7 @@ class HTMLTranslator(BaseTranslator): atts['title'] = node['reftitle'] self.body.append(self.starttag(node, 'a', '', **atts)) - if node.hasattr('secnumber'): + if node.get('secnumber'): self.body.append(('%s' + self.secnumber_suffix) % '.'.join(map(str, node['secnumber']))) @@ -202,14 +202,14 @@ class HTMLTranslator(BaseTranslator): self.depart_admonition(node) def add_secnumber(self, node): - if node.hasattr('secnumber'): + if node.get('secnumber'): self.body.append('.'.join(map(str, node['secnumber'])) + self.secnumber_suffix) elif isinstance(node.parent, nodes.section): anchorname = '#' + node.parent['ids'][0] if anchorname not in self.builder.secnumbers: anchorname = '' # try first heading which has no anchor - if anchorname in self.builder.secnumbers: + if self.builder.secnumbers.get(anchorname): numbers = self.builder.secnumbers[anchorname] self.body.append('.'.join(map(str, numbers)) + self.secnumber_suffix) From 8ac32d08babf00f00b2ee9630844c8006ca06424 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 25 Aug 2010 10:09:24 +0000 Subject: [PATCH 382/744] #221: Add Swedish locale, thanks to Henrik Holmboe. --- CHANGES | 2 + doc/config.rst | 1 + sphinx/locale/sv/LC_MESSAGES/sphinx.js | 1 + sphinx/locale/sv/LC_MESSAGES/sphinx.mo | Bin 0 -> 9511 bytes sphinx/locale/sv/LC_MESSAGES/sphinx.po | 794 +++++++++++++++++++++++++ 5 files changed, 798 insertions(+) create mode 100644 sphinx/locale/sv/LC_MESSAGES/sphinx.js create mode 100644 sphinx/locale/sv/LC_MESSAGES/sphinx.mo create mode 100644 sphinx/locale/sv/LC_MESSAGES/sphinx.po diff --git a/CHANGES b/CHANGES index 6f89ae15b..45467a168 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,8 @@ Release 1.1 (in development) * #443: Allow referencing external graphviz files. +* #221: Add Swedish locale. + Release 1.0.4 (in development) ============================== diff --git a/doc/config.rst b/doc/config.rst index e0fbeb46e..3115c401c 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -300,6 +300,7 @@ Project information * ``pt_BR`` -- Brazilian Portuguese * ``ru`` -- Russian * ``sl`` -- Slovenian + * ``sv`` -- Swedish * ``tr`` -- Turkish * ``uk_UA`` -- Ukrainian * ``zh_CN`` -- Simplified Chinese diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.js b/sphinx/locale/sv/LC_MESSAGES/sphinx.js new file mode 100644 index 000000000..0cedfb45a --- /dev/null +++ b/sphinx/locale/sv/LC_MESSAGES/sphinx.js @@ -0,0 +1 @@ +Documentation.addTranslations({"locale": "sv", "plural_expr": "(n != 1)", "messages": {"Search Results": "S\u00f6kresultat", "Preparing search...": "F\u00f6rbereder s\u00f6kning...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Din s\u00f6kning gav inga resultat. Kolla stavning och att du valt tillr\u00e4ckligt med kategorier.", "Search finished, found %s page(s) matching the search query.": "S\u00f6kning f\u00e4rdig, hittade %s tr\u00e4ffar.", ", in ": ", i ", "Expand sidebar": "Expandera sidolist", "Permalink to this headline": "Permalink till denna rubrik", "Searching": "S\u00f6ker", "Collapse sidebar": "D\u00f6lj sidolist", "Permalink to this definition": "Permalink till denna definition", "Hide Search Matches": "D\u00f6lj S\u00f6kresultat"}}); \ No newline at end of file diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.mo b/sphinx/locale/sv/LC_MESSAGES/sphinx.mo new file mode 100644 index 0000000000000000000000000000000000000000..203ead14153900476a8749b38ff3d20aeb7db815 GIT binary patch literal 9511 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 VO`rcCgi5LuZWw?ng)<|){}%*nGF1Qo literal 0 HcmV?d00001 diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.po b/sphinx/locale/sv/LC_MESSAGES/sphinx.po new file mode 100644 index 000000000..f5f3b7e48 --- /dev/null +++ b/sphinx/locale/sv/LC_MESSAGES/sphinx.po @@ -0,0 +1,794 @@ +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2010-05-24 23:53+0200\n" +"PO-Revision-Date: \n" +"Last-Translator: Henrik Holmboe <henrik@holmboe.se>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Swedish\n" +"X-Poedit-Country: SWEDEN\n" +"X-Poedit-Basepath: /home/hh/Local/src/sphinx\n" + +#: sphinx/environment.py:106 +#: sphinx/writers/latex.py:184 +#: sphinx/writers/manpage.py:67 +#, python-format +msgid "%B %d, %Y" +msgstr "%B %d, %Y" + +#: sphinx/roles.py:174 +#, python-format +msgid "Python Enhancement Proposals!PEP %s" +msgstr "Python Enhancement Proposals!PEP %s" + +#: sphinx/builders/changes.py:72 +msgid "Builtins" +msgstr "Inbyggda" + +#: sphinx/builders/changes.py:74 +msgid "Module level" +msgstr "Modulnivå" + +#: sphinx/builders/html.py:266 +#, python-format +msgid "%b %d, %Y" +msgstr "%b %d, %Y" + +#: sphinx/builders/html.py:285 +#: sphinx/themes/basic/defindex.html:30 +msgid "General Index" +msgstr "Huvudindex" + +#: sphinx/builders/html.py:285 +msgid "index" +msgstr "index" + +#: sphinx/builders/html.py:345 +msgid "next" +msgstr "nästa" + +#: sphinx/builders/html.py:354 +msgid "previous" +msgstr "föregående" + +#: sphinx/builders/latex.py:151 +msgid " (in " +msgstr "(i " + +#: sphinx/directives/other.py:127 +msgid "Section author: " +msgstr "Sektionsförfattare" + +#: sphinx/directives/other.py:129 +msgid "Module author: " +msgstr "Modulförfattare" + +#: sphinx/directives/other.py:131 +msgid "Code author: " +msgstr "Källkodsförfattare" + +#: sphinx/directives/other.py:133 +msgid "Author: " +msgstr "Upphovsman:" + +#: sphinx/directives/other.py:238 +msgid "See also" +msgstr "Visa även" + +#: sphinx/domains/__init__.py:253 +#, python-format +msgid "%s %s" +msgstr "%s %s" + +#: sphinx/domains/c.py:51 +#: sphinx/domains/python.py:49 +msgid "Parameters" +msgstr "Parametrar" + +#: sphinx/domains/c.py:54 +#: sphinx/domains/javascript.py:137 +#: sphinx/domains/python.py:59 +msgid "Returns" +msgstr "Returnerar" + +#: sphinx/domains/c.py:56 +#: sphinx/domains/python.py:61 +msgid "Return type" +msgstr "Returtyp" + +#: sphinx/domains/c.py:133 +#, python-format +msgid "%s (C function)" +msgstr "%s (C funktion)" + +#: sphinx/domains/c.py:135 +#, python-format +msgid "%s (C member)" +msgstr "%s (C medlem)" + +#: sphinx/domains/c.py:137 +#, python-format +msgid "%s (C macro)" +msgstr "%s (C makro)" + +#: sphinx/domains/c.py:139 +#, python-format +msgid "%s (C type)" +msgstr "%s (C typ)" + +#: sphinx/domains/c.py:141 +#, python-format +msgid "%s (C variable)" +msgstr "%s (C variabel)" + +#: sphinx/domains/c.py:171 +#: sphinx/domains/cpp.py:1031 +#: sphinx/domains/javascript.py:166 +#: sphinx/domains/python.py:497 +msgid "function" +msgstr "funktion" + +#: sphinx/domains/c.py:172 +#: sphinx/domains/cpp.py:1032 +msgid "member" +msgstr "medlem" + +#: sphinx/domains/c.py:173 +msgid "macro" +msgstr "makro" + +#: sphinx/domains/c.py:174 +#: sphinx/domains/cpp.py:1033 +msgid "type" +msgstr "typ" + +#: sphinx/domains/c.py:175 +msgid "variable" +msgstr "variabel" + +#: sphinx/domains/cpp.py:876 +#, python-format +msgid "%s (C++ class)" +msgstr "%s (C++ klass)" + +#: sphinx/domains/cpp.py:891 +#, python-format +msgid "%s (C++ type)" +msgstr "%s (C++ typ)" + +#: sphinx/domains/cpp.py:910 +#, python-format +msgid "%s (C++ member)" +msgstr "%s (C++ medlem)" + +#: sphinx/domains/cpp.py:962 +#, python-format +msgid "%s (C++ function)" +msgstr "%s (C++ funktion)" + +#: sphinx/domains/cpp.py:1030 +#: sphinx/domains/python.py:499 +msgid "class" +msgstr "klass" + +#: sphinx/domains/javascript.py:117 +#: sphinx/domains/python.py:221 +#, python-format +msgid "%s() (built-in function)" +msgstr "%s() (inbyggd funktion)" + +#: sphinx/domains/javascript.py:118 +#: sphinx/domains/python.py:285 +#, python-format +msgid "%s() (%s method)" +msgstr "%s() (%s metod)" + +#: sphinx/domains/javascript.py:120 +#, python-format +msgid "%s (global variable or constant)" +msgstr "%s (global variabel eller konstant)" + +#: sphinx/domains/javascript.py:122 +#: sphinx/domains/python.py:323 +#, python-format +msgid "%s (%s attribute)" +msgstr "%s (%s attribut)" + +#: sphinx/domains/javascript.py:131 +msgid "Arguments" +msgstr "Argument" + +#: sphinx/domains/javascript.py:134 +msgid "Throws" +msgstr "Kastar" + +#: sphinx/domains/javascript.py:167 +#: sphinx/domains/python.py:498 +msgid "data" +msgstr "data" + +#: sphinx/domains/javascript.py:168 +#: sphinx/domains/python.py:504 +msgid "attribute" +msgstr "attribut" + +#: sphinx/domains/python.py:53 +msgid "Variables" +msgstr "Variabler" + +#: sphinx/domains/python.py:56 +msgid "Raises" +msgstr "Väcker" + +#: sphinx/domains/python.py:222 +#: sphinx/domains/python.py:279 +#: sphinx/domains/python.py:291 +#: sphinx/domains/python.py:304 +#, python-format +msgid "%s() (in module %s)" +msgstr "%s() (i modul %s)" + +#: sphinx/domains/python.py:225 +#, python-format +msgid "%s (built-in variable)" +msgstr "%s (inbyggd variabel)" + +#: sphinx/domains/python.py:226 +#: sphinx/domains/python.py:317 +#, python-format +msgid "%s (in module %s)" +msgstr "%s (i modul %s)" + +#: sphinx/domains/python.py:242 +#, python-format +msgid "%s (built-in class)" +msgstr "%s (inbyggd klass)" + +#: sphinx/domains/python.py:243 +#, python-format +msgid "%s (class in %s)" +msgstr "%s (klass i %s)" + +#: sphinx/domains/python.py:283 +#, python-format +msgid "%s() (%s.%s method)" +msgstr "%s() (%s.%s metod)" + +#: sphinx/domains/python.py:295 +#, python-format +msgid "%s() (%s.%s static method)" +msgstr "%s() (%s.%s statisk metod)" + +#: sphinx/domains/python.py:298 +#, python-format +msgid "%s() (%s static method)" +msgstr "%s() (%s statisk metod)" + +#: sphinx/domains/python.py:308 +#, python-format +msgid "%s() (%s.%s class method)" +msgstr "%s() (%s.%s klass metod)" + +#: sphinx/domains/python.py:311 +#, python-format +msgid "%s() (%s class method)" +msgstr "%s() (%s klass metod)" + +#: sphinx/domains/python.py:321 +#, python-format +msgid "%s (%s.%s attribute)" +msgstr "%s (%s.%s attribut)" + +#: sphinx/domains/python.py:366 +msgid "Platforms: " +msgstr "Plattformar:" + +#: sphinx/domains/python.py:372 +#, python-format +msgid "%s (module)" +msgstr "%s (modul)" + +#: sphinx/domains/python.py:429 +msgid "Python Module Index" +msgstr "Python Modul Index" + +#: sphinx/domains/python.py:430 +msgid "modules" +msgstr "moduler" + +#: sphinx/domains/python.py:475 +msgid "Deprecated" +msgstr "Ersatt" + +#: sphinx/domains/python.py:500 +#: sphinx/locale/__init__.py:162 +msgid "exception" +msgstr "undantag" + +#: sphinx/domains/python.py:501 +msgid "method" +msgstr "metod" + +#: sphinx/domains/python.py:502 +msgid "class method" +msgstr "klassmetod" + +#: sphinx/domains/python.py:503 +msgid "static method" +msgstr "statisk metod" + +#: sphinx/domains/python.py:505 +#: sphinx/locale/__init__.py:158 +msgid "module" +msgstr "modul" + +#: sphinx/domains/rst.py:53 +#, python-format +msgid "%s (directive)" +msgstr "%s (direktiv)" + +#: sphinx/domains/rst.py:55 +#, python-format +msgid "%s (role)" +msgstr "%s (roll)" + +#: sphinx/domains/rst.py:103 +msgid "directive" +msgstr "direktiv" + +#: sphinx/domains/rst.py:104 +msgid "role" +msgstr "roll" + +#: sphinx/domains/std.py:68 +#: sphinx/domains/std.py:84 +#, python-format +msgid "environment variable; %s" +msgstr "miljövariabel; %s" + +#: sphinx/domains/std.py:160 +#, python-format +msgid "%scommand line option; %s" +msgstr "%skommandorad växel; %s" + +#: sphinx/domains/std.py:328 +msgid "glossary term" +msgstr "ordlista" + +#: sphinx/domains/std.py:329 +msgid "grammar token" +msgstr "grammatisk token" + +#: sphinx/domains/std.py:330 +msgid "reference label" +msgstr "referensetikett" + +#: sphinx/domains/std.py:331 +msgid "environment variable" +msgstr "miljövariabel" + +#: sphinx/domains/std.py:332 +msgid "program option" +msgstr "programväxel" + +#: sphinx/domains/std.py:360 +#: sphinx/themes/basic/genindex-single.html:11 +#: sphinx/themes/basic/genindex-split.html:11 +#: sphinx/themes/basic/genindex-split.html:14 +#: sphinx/themes/basic/genindex.html:11 +#: sphinx/themes/basic/genindex.html:14 +#: sphinx/themes/basic/genindex.html:50 +#: sphinx/themes/basic/layout.html:125 +#: sphinx/writers/latex.py:173 +msgid "Index" +msgstr "Index" + +#: sphinx/domains/std.py:361 +msgid "Module Index" +msgstr "Modul Index" + +#: sphinx/domains/std.py:362 +#: sphinx/themes/basic/defindex.html:25 +msgid "Search Page" +msgstr "Söksida" + +#: sphinx/ext/autodoc.py:917 +#, python-format +msgid " Bases: %s" +msgstr " Baserad: %s" + +#: sphinx/ext/autodoc.py:950 +#, python-format +msgid "alias of :class:`%s`" +msgstr "alias av :klass:`%s`" + +#: sphinx/ext/todo.py:41 +msgid "Todo" +msgstr "Att göra" + +#: sphinx/ext/todo.py:109 +#, python-format +msgid "(The <<original entry>> is located in %s, line %d.)" +msgstr "(<<Ursprunget>> finns i %s, på rad %d.)" + +#: sphinx/ext/todo.py:117 +msgid "original entry" +msgstr "ursprungsvärde" + +#: sphinx/ext/viewcode.py:66 +msgid "[source]" +msgstr "[source]" + +#: sphinx/ext/viewcode.py:109 +msgid "[docs]" +msgstr "[docs]" + +#: sphinx/ext/viewcode.py:123 +msgid "Module code" +msgstr "Modulkällkod" + +#: sphinx/ext/viewcode.py:129 +#, python-format +msgid "<h1>Source code for %s</h1>" +msgstr "<h1>Källkod för %s</h1>" + +#: sphinx/ext/viewcode.py:156 +msgid "Overview: module code" +msgstr "Översikt: modulkällkod" + +#: sphinx/ext/viewcode.py:157 +msgid "<h1>All modules for which code is available</h1>" +msgstr "<h1>Alla moduler där källkod finns</h1>" + +#: sphinx/locale/__init__.py:139 +msgid "Attention" +msgstr "Observera" + +#: sphinx/locale/__init__.py:140 +msgid "Caution" +msgstr "Varning" + +#: sphinx/locale/__init__.py:141 +msgid "Danger" +msgstr "Risk" + +#: sphinx/locale/__init__.py:142 +msgid "Error" +msgstr "Fel" + +#: sphinx/locale/__init__.py:143 +msgid "Hint" +msgstr "Råd" + +#: sphinx/locale/__init__.py:144 +msgid "Important" +msgstr "Viktigt" + +#: sphinx/locale/__init__.py:145 +msgid "Note" +msgstr "Observera" + +#: sphinx/locale/__init__.py:146 +msgid "See Also" +msgstr "Visa även" + +#: sphinx/locale/__init__.py:147 +msgid "Tip" +msgstr "Tips" + +#: sphinx/locale/__init__.py:148 +msgid "Warning" +msgstr "Varning" + +#: sphinx/locale/__init__.py:152 +#, python-format +msgid "New in version %s" +msgstr "Nyheter i version %s" + +#: sphinx/locale/__init__.py:153 +#, python-format +msgid "Changed in version %s" +msgstr "Förändrat i version %s" + +#: sphinx/locale/__init__.py:154 +#, python-format +msgid "Deprecated since version %s" +msgstr "Ersatt sedan version %s" + +#: sphinx/locale/__init__.py:159 +msgid "keyword" +msgstr "nyckelord" + +#: sphinx/locale/__init__.py:160 +msgid "operator" +msgstr "operator" + +#: sphinx/locale/__init__.py:161 +msgid "object" +msgstr "objekt" + +#: sphinx/locale/__init__.py:163 +msgid "statement" +msgstr "uttryck" + +#: sphinx/locale/__init__.py:164 +msgid "built-in function" +msgstr "inbyggda funktioner" + +#: sphinx/themes/agogo/layout.html:45 +#: sphinx/themes/basic/globaltoc.html:10 +#: sphinx/themes/basic/localtoc.html:11 +msgid "Table Of Contents" +msgstr "Innehållsförteckning" + +#: sphinx/themes/agogo/layout.html:49 +#: sphinx/themes/basic/layout.html:128 +#: sphinx/themes/basic/search.html:11 +#: sphinx/themes/basic/search.html:14 +msgid "Search" +msgstr "Sök" + +#: sphinx/themes/agogo/layout.html:52 +#: sphinx/themes/basic/searchbox.html:15 +msgid "Go" +msgstr "Gå" + +#: sphinx/themes/agogo/layout.html:57 +#: sphinx/themes/basic/searchbox.html:20 +msgid "Enter search terms or a module, class or function name." +msgstr "Ange sökord eller modul-, klass- eller funktionsnamn." + +#: sphinx/themes/agogo/layout.html:78 +#: sphinx/themes/basic/sourcelink.html:14 +msgid "Show Source" +msgstr "Visa källfil" + +#: sphinx/themes/basic/defindex.html:11 +msgid "Overview" +msgstr "Översikt" + +#: sphinx/themes/basic/defindex.html:20 +msgid "Indices and tables:" +msgstr "Index och tabeller" + +#: sphinx/themes/basic/defindex.html:23 +msgid "Complete Table of Contents" +msgstr "Komplett Innehållsförteckning" + +#: sphinx/themes/basic/defindex.html:24 +msgid "lists all sections and subsections" +msgstr "lista över alla paragrafer och underparagrafer" + +#: sphinx/themes/basic/defindex.html:26 +msgid "search this documentation" +msgstr "sök i det här dokumentet" + +#: sphinx/themes/basic/defindex.html:28 +msgid "Global Module Index" +msgstr "Global Modulindex" + +#: sphinx/themes/basic/defindex.html:29 +msgid "quick access to all modules" +msgstr "genväg till alla moduler" + +#: sphinx/themes/basic/defindex.html:31 +msgid "all functions, classes, terms" +msgstr "alla funktioner, klasser, villkor" + +#: sphinx/themes/basic/genindex-single.html:14 +#, python-format +msgid "Index – %(key)s" +msgstr "Index – %(key)s" + +#: sphinx/themes/basic/genindex-single.html:46 +#: sphinx/themes/basic/genindex-split.html:24 +#: sphinx/themes/basic/genindex-split.html:38 +#: sphinx/themes/basic/genindex.html:56 +msgid "Full index on one page" +msgstr "Hela innehållsförteckningen på en sida" + +#: sphinx/themes/basic/genindex-split.html:16 +msgid "Index pages by letter" +msgstr "Innehållsförteckning per inledande bokstav" + +#: sphinx/themes/basic/genindex-split.html:25 +msgid "can be huge" +msgstr "kan bli stort" + +#: sphinx/themes/basic/layout.html:23 +msgid "Navigation" +msgstr "Navigation" + +#: sphinx/themes/basic/layout.html:113 +#, python-format +msgid "Search within %(docstitle)s" +msgstr "Sök bland %(docstitle)s" + +#: sphinx/themes/basic/layout.html:122 +msgid "About these documents" +msgstr "Om dessa dokument" + +#: sphinx/themes/basic/layout.html:131 +msgid "Copyright" +msgstr "Copyright" + +#: sphinx/themes/basic/layout.html:180 +#, python-format +msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." +msgstr "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." + +#: sphinx/themes/basic/layout.html:182 +#, python-format +msgid "© Copyright %(copyright)s." +msgstr "© Copyright %(copyright)s." + +#: sphinx/themes/basic/layout.html:186 +#, python-format +msgid "Last updated on %(last_updated)s." +msgstr "Senast uppdaterad %(last_updated)s." + +#: sphinx/themes/basic/layout.html:189 +#, python-format +msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." +msgstr "Skapad med <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." + +#: sphinx/themes/basic/opensearch.xml:4 +#, python-format +msgid "Search %(docstitle)s" +msgstr "Sök %(docstitle)s" + +#: sphinx/themes/basic/relations.html:11 +msgid "Previous topic" +msgstr "Föregående titel" + +#: sphinx/themes/basic/relations.html:13 +msgid "previous chapter" +msgstr "Föregående kapitel" + +#: sphinx/themes/basic/relations.html:16 +msgid "Next topic" +msgstr "Nästa titel" + +#: sphinx/themes/basic/relations.html:18 +msgid "next chapter" +msgstr "Nästa kapitel" + +#: sphinx/themes/basic/search.html:18 +msgid "" +"Please activate JavaScript to enable the search\n" +" functionality." +msgstr "Var god aktivera JavaScript för sökfunktionalitet." + +#: sphinx/themes/basic/search.html:23 +msgid "" +"From here you can search these documents. Enter your search\n" +" words into the box below and click \"search\". Note that the search\n" +" function will automatically search for all of the words. Pages\n" +" containing fewer words won't appear in the result list." +msgstr "" +"Här kan du söka bland dessa dokument. Ange sökord nedan och klicka \"sök\".\n" +" Sökningen måste träffa på samtliga angivna sökord." + +#: sphinx/themes/basic/search.html:30 +msgid "search" +msgstr "sök" + +#: sphinx/themes/basic/search.html:34 +#: sphinx/themes/basic/static/searchtools.js:489 +msgid "Search Results" +msgstr "Sökresultat" + +#: sphinx/themes/basic/search.html:36 +msgid "Your search did not match any results." +msgstr "Din sökning gav inga resultat." + +#: sphinx/themes/basic/searchbox.html:12 +msgid "Quick search" +msgstr "Snabbsök" + +#: sphinx/themes/basic/sourcelink.html:11 +msgid "This Page" +msgstr "Denna Sida" + +#: sphinx/themes/basic/changes/frameset.html:5 +#: sphinx/themes/basic/changes/versionchanges.html:12 +#, python-format +msgid "Changes in Version %(version)s — %(docstitle)s" +msgstr "Förändringar i Version %(version)s — %(docstitle)s" + +#: sphinx/themes/basic/changes/rstsource.html:5 +#, python-format +msgid "%(filename)s — %(docstitle)s" +msgstr "%(filename)s — %(docstitle)s" + +#: sphinx/themes/basic/changes/versionchanges.html:17 +#, python-format +msgid "Automatically generated list of changes in version %(version)s" +msgstr "Automatiskt genererad lista över förändringar i version %(version)s" + +#: sphinx/themes/basic/changes/versionchanges.html:18 +msgid "Library changes" +msgstr "Förändringar i bibliotek" + +#: sphinx/themes/basic/changes/versionchanges.html:23 +msgid "C API changes" +msgstr "Förändringar i C API" + +#: sphinx/themes/basic/changes/versionchanges.html:25 +msgid "Other changes" +msgstr "Övriga förändringar" + +#: sphinx/themes/basic/static/doctools.js:154 +#: sphinx/writers/html.py:482 +#: sphinx/writers/html.py:487 +msgid "Permalink to this headline" +msgstr "Permalink till denna rubrik" + +#: sphinx/themes/basic/static/doctools.js:160 +#: sphinx/writers/html.py:87 +msgid "Permalink to this definition" +msgstr "Permalink till denna definition" + +#: sphinx/themes/basic/static/doctools.js:189 +msgid "Hide Search Matches" +msgstr "Dölj Sökresultat" + +#: sphinx/themes/basic/static/searchtools.js:285 +msgid "Searching" +msgstr "Söker" + +#: sphinx/themes/basic/static/searchtools.js:290 +msgid "Preparing search..." +msgstr "Förbereder sökning..." + +#: sphinx/themes/basic/static/searchtools.js:364 +msgid ", in " +msgstr ", i " + +#: sphinx/themes/basic/static/searchtools.js:491 +msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." +msgstr "Din sökning gav inga resultat. Kolla stavning och att du valt tillräckligt med kategorier." + +#: sphinx/themes/basic/static/searchtools.js:493 +#, python-format +msgid "Search finished, found %s page(s) matching the search query." +msgstr "Sökning färdig, hittade %s träffar." + +#: sphinx/themes/default/static/sidebar.js:66 +msgid "Expand sidebar" +msgstr "Expandera sidolist" + +#: sphinx/themes/default/static/sidebar.js:79 +#: sphinx/themes/default/static/sidebar.js:106 +msgid "Collapse sidebar" +msgstr "Dölj sidolist" + +#: sphinx/themes/haiku/layout.html:26 +msgid "Contents" +msgstr "Innehåll" + +#: sphinx/writers/latex.py:171 +msgid "Release" +msgstr "Utgåva" + +#: sphinx/writers/latex.py:572 +#: sphinx/writers/manpage.py:178 +msgid "Footnotes" +msgstr "Fotnoter" + +#: sphinx/writers/latex.py:641 +msgid "continued from previous page" +msgstr "fortsättning från föregående sida" + +#: sphinx/writers/latex.py:646 +msgid "Continued on next page" +msgstr "Fortsätter på nästa sida" + +#: sphinx/writers/text.py:422 +msgid "[image]" +msgstr "[image]" + From 1127b870f96d15af33a44e543f4a10f7639521ca Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 25 Aug 2010 10:46:49 +0000 Subject: [PATCH 383/744] Review of Swedish translation by Ludvig Ericson. --- sphinx/locale/sv/LC_MESSAGES/sphinx.mo | Bin 9511 -> 9509 bytes sphinx/locale/sv/LC_MESSAGES/sphinx.po | 43 +++++++++++++------------ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.mo b/sphinx/locale/sv/LC_MESSAGES/sphinx.mo index 203ead14153900476a8749b38ff3d20aeb7db815..8cf7675160ed732108bb75ecd460ce2f193a67ce 100644 GIT binary patch delta 1266 zcmZA0OGwmF6vy#9zMrLKzRgjS6rD=3mjntuAj?{qlwjm!W7s%4IwrW9q##-)Of7_P z5sC>##zc!K2y9VQi)t02K?y+#MU+Jp_Wd>2ieTV#&%OV9?>XoHpNWR?hN;T9V1_ZK zHP@Ks*o#>>WF5g3#5a(ixo_uRVLI_w%*Mn#V>V$i7GevQ;ULD~Wh}=D%)zIq{qOTk z&?GSU$wVr~<r|ZX8Mq6bsP*HRiGGa7v#5*DV=Z1m74im`;Wtzw@dYG@YmqBVm5m#5 zFY&>GU?jsaCgPZwM0H>aRnY^C#zj;oqE|-}%EWWTg{aCVF$tfeDw{>^`-s~28P$n+ zvQhyl7>gM}2CEq4VkK@sZ8(f2=tsJ2E}$;Bj%j!s)v-r*{)Kf8)rk+7f-%lW2hvd; z%(HPds)M_?G0?%y$R5*%YSk&!0hh1_CsCc5MJ2d^N-(h~Qn4%4o8S%VWi2ox7f~rD z6i2SkMBU^<^?D1cCv~Wow8Od&)uTh$ifyRO?xOb1*!4NoJ@cs2z7+E{ai>WkFD<OY zmADy|$R1SFTTqD{!BRYdN@N(d?;`3e8by_Q&CcJmKD0hVy^EKqbLUHfkxD)>v7U(^ zcA;`jB=c(2gIle2s7iO*^+wdi2W;GqO6UaY{~18Nlhde|Jd7%A9QD5n*E7(APcQ~& ztglgD(OcAoQIxHXNf?dUsEhK$BT4&HqiQyW-(|Hsg07B}-QNDrp6+r}8gQ0vclq4j zzMemU=UA7=w-ogc`j>_S?!Hd<QBRkRD=J*xE_WdCH~jBd9`OBp7V+=XLXUGsL*Cr_ z@YUQ_M@qBb@0%X$^9I~LUnni#?{I~O@~fg9o#C5>XB>{sP;Jqj<R*`EdThYcZJIox U*y8W;9n-h^oVD%Yn39sHU*ln=;{X5v delta 1272 zcmZA0OGs2v9LMoHGmcH>tI1Nw*VR!_6dxc(+QJYjtq`KzD8!f(*o>pi=s>i%P`PN) zB4-g5M1(c9=mCR^pcWOls34*iEs~I0RFFiJV9?|H^Ij_g@pJy?+;h(V|G#&x_NII9 zG-X>G4Z|1=7)Alcu^3Na30^=yW{^K)j(=KT!1cI{dTw2rVQj`)til1T$K#lVGuVJP zuoPdQ_J1lfEW^j(7ZYpISI%MR#~oOSS`XtUjNuxbLS1|YJMbD7;R5F45-JgIg?(-v za)r_2#Jh11@qr4<&hQEo*-WHS72ZNsG>0Dif$D^-(oU!dPZL+5DobMyK1Wsd8ntf` zweJh66JD}X0XgVJzr{eWpah$+8MR>mgBU})Y)qp%a}x{kF6QB5)WxqH-=aFPh`H!7 z?G6;8Iv8-`HdF_DIvMDI{m35UAgWas>VS*bj@MD0nMWn~4V9p;+K!u2M|}}XCoM4} zT{U)!xwvvQYQ7EC>uso>bfaGWF2{YS9u49k4x=)AfZDg<tiMNn9G_7oFW2xjakr6A zURr3y4Y(E6tG%eE51<kWVI4+MiA<yRokM*^mr<qOcIF>CK5=}BdKdGka~Fe_UCCD_ zTA29hEHu~JnYW`J>~!3Ys&uEb-iNxl--*Mhgrcb5=OpT#Ora{CMirJu{jO%a80f)g zn1yc~-=V&u52y>XC|etI(SrfhMU|QJIemHV)-9Qj#fMy$d3Z7wnHU+1H5hdXv%cFL z4MpN(e}eFEG(5T*O&l9v9VSEZk<d^$>cowWW+WO)B>sl~9m@lw|DHv>a$4$X=_Qw$ zIuhtgoedOa?gx5YzP_PEIGzm0Q~vUCmzg<P-sW+eJ(=06Q!Y$(RNpJ;A4!DFM>ENA V%+N69s`>5>CC!dV#vKg0e*>=^q!$1H diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.po b/sphinx/locale/sv/LC_MESSAGES/sphinx.po index f5f3b7e48..f449e8f6e 100644 --- a/sphinx/locale/sv/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sv/LC_MESSAGES/sphinx.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2010-05-24 23:53+0200\n" -"PO-Revision-Date: \n" +"PO-Revision-Date: 2010-08-25 12:36+0200\n" "Last-Translator: Henrik Holmboe <henrik@holmboe.se>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -11,7 +11,10 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Swedish\n" "X-Poedit-Country: SWEDEN\n" -"X-Poedit-Basepath: /home/hh/Local/src/sphinx\n" + +# Translators (rev. chron. order): +# Ludvig Ericson <ludvig@lericson.se> +# Henrik Holmboe <henrik@holmboe.se> #: sphinx/environment.py:106 #: sphinx/writers/latex.py:184 @@ -77,7 +80,7 @@ msgstr "Upphovsman:" #: sphinx/directives/other.py:238 msgid "See also" -msgstr "Visa även" +msgstr "Se även" #: sphinx/domains/__init__.py:253 #, python-format @@ -103,27 +106,27 @@ msgstr "Returtyp" #: sphinx/domains/c.py:133 #, python-format msgid "%s (C function)" -msgstr "%s (C funktion)" +msgstr "%s (C-funktion)" #: sphinx/domains/c.py:135 #, python-format msgid "%s (C member)" -msgstr "%s (C medlem)" +msgstr "%s (C-medlem)" #: sphinx/domains/c.py:137 #, python-format msgid "%s (C macro)" -msgstr "%s (C makro)" +msgstr "%s (C-makro)" #: sphinx/domains/c.py:139 #, python-format msgid "%s (C type)" -msgstr "%s (C typ)" +msgstr "%s (C-typ)" #: sphinx/domains/c.py:141 #, python-format msgid "%s (C variable)" -msgstr "%s (C variabel)" +msgstr "%s (C-variabel)" #: sphinx/domains/c.py:171 #: sphinx/domains/cpp.py:1031 @@ -153,22 +156,22 @@ msgstr "variabel" #: sphinx/domains/cpp.py:876 #, python-format msgid "%s (C++ class)" -msgstr "%s (C++ klass)" +msgstr "%s (C++-klass)" #: sphinx/domains/cpp.py:891 #, python-format msgid "%s (C++ type)" -msgstr "%s (C++ typ)" +msgstr "%s (C++-typ)" #: sphinx/domains/cpp.py:910 #, python-format msgid "%s (C++ member)" -msgstr "%s (C++ medlem)" +msgstr "%s (C++-medlem)" #: sphinx/domains/cpp.py:962 #, python-format msgid "%s (C++ function)" -msgstr "%s (C++ funktion)" +msgstr "%s (C++-funktion)" #: sphinx/domains/cpp.py:1030 #: sphinx/domains/python.py:499 @@ -271,12 +274,12 @@ msgstr "%s() (%s statisk metod)" #: sphinx/domains/python.py:308 #, python-format msgid "%s() (%s.%s class method)" -msgstr "%s() (%s.%s klass metod)" +msgstr "%s() (%s.%s klassmetod)" #: sphinx/domains/python.py:311 #, python-format msgid "%s() (%s class method)" -msgstr "%s() (%s klass metod)" +msgstr "%s() (%s klassmetod)" #: sphinx/domains/python.py:321 #, python-format @@ -294,7 +297,7 @@ msgstr "%s (modul)" #: sphinx/domains/python.py:429 msgid "Python Module Index" -msgstr "Python Modul Index" +msgstr "Python Modulindex" #: sphinx/domains/python.py:430 msgid "modules" @@ -389,7 +392,7 @@ msgstr "Index" #: sphinx/domains/std.py:361 msgid "Module Index" -msgstr "Modul Index" +msgstr "Modulindex" #: sphinx/domains/std.py:362 #: sphinx/themes/basic/defindex.html:25 @@ -404,7 +407,7 @@ msgstr " Baserad: %s" #: sphinx/ext/autodoc.py:950 #, python-format msgid "alias of :class:`%s`" -msgstr "alias av :klass:`%s`" +msgstr "alias för :class:`%s`" #: sphinx/ext/todo.py:41 msgid "Todo" @@ -446,7 +449,7 @@ msgstr "<h1>Alla moduler där källkod finns</h1>" #: sphinx/locale/__init__.py:139 msgid "Attention" -msgstr "Observera" +msgstr "Uppmärksamma" #: sphinx/locale/__init__.py:140 msgid "Caution" @@ -474,7 +477,7 @@ msgstr "Observera" #: sphinx/locale/__init__.py:146 msgid "See Also" -msgstr "Visa även" +msgstr "Se även" #: sphinx/locale/__init__.py:147 msgid "Tip" @@ -716,7 +719,7 @@ msgstr "Förändringar i bibliotek" #: sphinx/themes/basic/changes/versionchanges.html:23 msgid "C API changes" -msgstr "Förändringar i C API" +msgstr "Förändringar i C-API" #: sphinx/themes/basic/changes/versionchanges.html:25 msgid "Other changes" From 22b5751eb568f51fd1d8e0f89f87fa26d1ceae61 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 25 Aug 2010 11:33:25 +0000 Subject: [PATCH 384/744] Fix test. --- tests/test_build_html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index c75e7c2d6..8f3a8f2a9 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -38,7 +38,7 @@ http://www.python.org/logo.png %(root)s/includes.txt:\\d*: \\(WARNING/2\\) Encoding 'utf-8-sig' used for \ reading included file u'.*?wrongenc.inc' seems to be wrong, try giving an \ :encoding: option\\n? -%(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png +%(root)s/includes.txt:4: WARNING: download file not readable: .*?nonexisting.png %(root)s/objects.txt:\\d*: WARNING: using old C markup; please migrate to \ new-style markup \(e.g. c:function instead of cfunction\), see \ http://sphinx.pocoo.org/domains.html From 40e692e6c34e5ef9f9e0a72afa339fdaa76f058b Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 25 Aug 2010 11:33:30 +0000 Subject: [PATCH 385/744] #504: Add an ``index`` role, to make inline index entries. --- CHANGES | 2 + doc/markup/inline.rst | 1 + doc/markup/misc.rst | 79 ++++++++++++++++++++++++++++++++++++++ doc/markup/para.rst | 62 ------------------------------ sphinx/directives/other.py | 31 ++------------- sphinx/roles.py | 24 +++++++++++- sphinx/util/nodes.py | 32 +++++++++++++++ tests/root/markup.txt | 2 + 8 files changed, 142 insertions(+), 91 deletions(-) diff --git a/CHANGES b/CHANGES index 45467a168..0ea9a3489 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,8 @@ Release 1.1 (in development) * #460: Allow limiting the depth of section numbers for HTML. +* #504: Add an ``index`` role, to make inline index entries. + * #443: Allow referencing external graphviz files. * #221: Add Swedish locale. diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst index 35981edc2..78aaea695 100644 --- a/doc/markup/inline.rst +++ b/doc/markup/inline.rst @@ -309,6 +309,7 @@ in a different style: If you don't need the "variable part" indication, use the standard ````code```` instead. +There is also an :rst:role:`index` role to generate index entries. The following roles generate external links: diff --git a/doc/markup/misc.rst b/doc/markup/misc.rst index 6173589b3..44da3aacc 100644 --- a/doc/markup/misc.rst +++ b/doc/markup/misc.rst @@ -62,6 +62,85 @@ Meta-information markup :confval:`show_authors` configuration value is True. +Index-generating markup +----------------------- + +Sphinx automatically creates index entries from all object descriptions (like +functions, classes or attributes) like discussed in :ref:`domains`. + +However, there is also explicit markup available, to make the index more +comprehensive and enable index entries in documents where information is not +mainly contained in information units, such as the language reference. + +.. rst:directive:: .. index:: <entries> + + This directive contains one or more index entries. Each entry consists of a + type and a value, separated by a colon. + + For example:: + + .. index:: + single: execution; context + module: __main__ + module: sys + triple: module; search; path + + The execution context + --------------------- + + ... + + This directive contains five entries, which will be converted to entries in + the generated index which link to the exact location of the index statement + (or, in case of offline media, the corresponding page number). + + Since index directives generate cross-reference targets at their location in + the source, it makes sense to put them *before* the thing they refer to -- + e.g. a heading, as in the example above. + + The possible entry types are: + + single + Creates a single index entry. Can be made a subentry by separating the + subentry text with a semicolon (this notation is also used below to + describe what entries are created). + pair + ``pair: loop; statement`` is a shortcut that creates two index entries, + namely ``loop; statement`` and ``statement; loop``. + triple + Likewise, ``triple: module; search; path`` is a shortcut that creates + three index entries, which are ``module; search path``, ``search; path, + module`` and ``path; module search``. + module, keyword, operator, object, exception, statement, builtin + These all create two index entries. For example, ``module: hashlib`` + creates the entries ``module; hashlib`` and ``hashlib; module``. (These + are Python-specific and therefore deprecated.) + + For index directives containing only "single" entries, there is a shorthand + notation:: + + .. index:: BNF, grammar, syntax, notation + + This creates four index entries. + +.. rst:role:: index + + While the :rst:dir:`index` directive is a block-level markup and links to the + beginning of the next paragraph, there is also a corresponding role that sets + the link target directly where it is used. + + The content of the role can be a simple phrase, which is then kept in the + text and used as an index entry. It can also be a combination of text and + index entry, styled like with explicit targets of cross-references. In that + case, the "target" part can be a full entry as described for the directive + above. For example:: + + This is a normal reST :index:`paragraph` that contains several + :index:`index entries <pair: index; entry>`. + + .. versionadded:: 1.1 + + .. _tags: Including content based on tags diff --git a/doc/markup/para.rst b/doc/markup/para.rst index ecc6b4a6b..52a5019b6 100644 --- a/doc/markup/para.rst +++ b/doc/markup/para.rst @@ -144,68 +144,6 @@ For local tables of contents, use the standard reST :dudir:`contents directive <contents>`. -Index-generating markup ------------------------ - -Sphinx automatically creates index entries from all object descriptions (like -functions, classes or attributes) like discussed in :ref:`domains`. - -However, there is also an explicit directive available, to make the index more -comprehensive and enable index entries in documents where information is not -mainly contained in information units, such as the language reference. - -.. rst:directive:: .. index:: <entries> - - This directive contains one or more index entries. Each entry consists of a - type and a value, separated by a colon. - - For example:: - - .. index:: - single: execution; context - module: __main__ - module: sys - triple: module; search; path - - The execution context - --------------------- - - ... - - This directive contains five entries, which will be converted to entries in - the generated index which link to the exact location of the index statement - (or, in case of offline media, the corresponding page number). - - Since index directives generate cross-reference targets at their location in - the source, it makes sense to put them *before* the thing they refer to -- - e.g. a heading, as in the example above. - - The possible entry types are: - - single - Creates a single index entry. Can be made a subentry by separating the - subentry text with a semicolon (this notation is also used below to - describe what entries are created). - pair - ``pair: loop; statement`` is a shortcut that creates two index entries, - namely ``loop; statement`` and ``statement; loop``. - triple - Likewise, ``triple: module; search; path`` is a shortcut that creates - three index entries, which are ``module; search path``, ``search; path, - module`` and ``path; module search``. - module, keyword, operator, object, exception, statement, builtin - These all create two index entries. For example, ``module: hashlib`` - creates the entries ``module; hashlib`` and ``hashlib; module``. (These - are Python-specific and therefore deprecated.) - - For index directives containing only "single" entries, there is a shorthand - notation:: - - .. index:: BNF, grammar, syntax, notation - - This creates four index entries. - - Glossary -------- diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index b5252e86f..cbf19b559 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -13,9 +13,9 @@ from docutils import nodes from docutils.parsers.rst import Directive, directives from sphinx import addnodes -from sphinx.locale import pairindextypes, _ +from sphinx.locale import _ from sphinx.util import url_re, docname_join -from sphinx.util.nodes import explicit_title_re +from sphinx.util.nodes import explicit_title_re, process_index_entry from sphinx.util.compat import make_admonition from sphinx.util.matching import patfilter @@ -157,10 +157,6 @@ class Index(Directive): final_argument_whitespace = True option_spec = {} - indextypes = [ - 'single', 'pair', 'double', 'triple', - ] - def run(self): arguments = self.arguments[0].split('\n') env = self.state.document.settings.env @@ -170,28 +166,7 @@ class Index(Directive): indexnode = addnodes.index() indexnode['entries'] = ne = [] for entry in arguments: - entry = entry.strip() - for type in pairindextypes: - if entry.startswith(type+':'): - value = entry[len(type)+1:].strip() - value = pairindextypes[type] + '; ' + value - ne.append(('pair', value, targetid, value)) - break - else: - for type in self.indextypes: - if entry.startswith(type+':'): - value = entry[len(type)+1:].strip() - if type == 'double': - type = 'pair' - ne.append((type, value, targetid, value)) - break - # shorthand notation for single entries - else: - for value in entry.split(','): - value = value.strip() - if not value: - continue - ne.append(('single', value, targetid, value)) + ne.extend(process_index_entry(entry, targetid)) return [indexnode, targetnode] diff --git a/sphinx/roles.py b/sphinx/roles.py index f08c3f005..b44868e6f 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -18,7 +18,7 @@ from docutils.parsers.rst import roles from sphinx import addnodes from sphinx.locale import _ from sphinx.util import ws_re -from sphinx.util.nodes import split_explicit_title +from sphinx.util.nodes import split_explicit_title, process_index_entry generic_docroles = { @@ -268,6 +268,27 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): return [addnodes.abbreviation(abbr, abbr, explanation=expl)], [] +def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): + # create new reference target + env = inliner.document.settings.env + targetid = 'index-%s' % env.new_serialno('index') + targetnode = nodes.target('', '', ids=[targetid]) + # split text and target in role content + has_explicit_title, title, target = split_explicit_title(text) + title = utils.unescape(title) + target = utils.unescape(target) + # if an explicit target is given, we can process it as a full entry + if has_explicit_title: + entries = process_index_entry(target, targetid) + # otherwise we just create a "single" entry + else: + entries = [('single', target, targetid, target)] + indexnode = addnodes.index() + indexnode['entries'] = entries + textnode = nodes.Text(title, title) + return [indexnode, targetnode, textnode], [] + + specific_docroles = { # links to download references 'download': XRefRole(nodeclass=addnodes.download_reference), @@ -281,6 +302,7 @@ specific_docroles = { 'file': emph_literal_role, 'samp': emph_literal_role, 'abbr': abbr_role, + 'index': index_role, } for rolename, func in specific_docroles.iteritems(): diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 2e383b0ac..adce565cc 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -14,6 +14,7 @@ import re from docutils import nodes from sphinx import addnodes +from sphinx.locale import pairindextypes from sphinx.util.pycompat import class_types @@ -72,6 +73,37 @@ def split_explicit_title(text): return False, text, text +indextypes = [ + 'single', 'pair', 'double', 'triple', +] + +def process_index_entry(entry, targetid): + indexentries = [] + entry = entry.strip() + for type in pairindextypes: + if entry.startswith(type+':'): + value = entry[len(type)+1:].strip() + value = pairindextypes[type] + '; ' + value + indexentries.append(('pair', value, targetid, value)) + break + else: + for type in indextypes: + if entry.startswith(type+':'): + value = entry[len(type)+1:].strip() + if type == 'double': + type = 'pair' + indexentries.append((type, value, targetid, value)) + break + # shorthand notation for single entries + else: + for value in entry.split(','): + value = value.strip() + if not value: + continue + indexentries.append(('single', value, targetid, value)) + return indexentries + + def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc): """Inline all toctrees in the *tree*. diff --git a/tests/root/markup.txt b/tests/root/markup.txt index a72285ed7..84d9581ad 100644 --- a/tests/root/markup.txt +++ b/tests/root/markup.txt @@ -132,6 +132,8 @@ Adding \n to test unescaping. Test :abbr:`abbr (abbreviation)` and another :abbr:`abbr (abbreviation)`. +Testing the :index:`index` role, also available with +:index:`explicit <pair: title; explicit>` title. .. _with: From 5263eca187dd215ba5b3bbf2a1294dd6d60a445c Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Wed, 25 Aug 2010 11:52:41 +0000 Subject: [PATCH 386/744] Give a better issue number. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 0ea9a3489..3833a25b5 100644 --- a/CHANGES +++ b/CHANGES @@ -10,7 +10,7 @@ Release 1.1 (in development) * #460: Allow limiting the depth of section numbers for HTML. -* #504: Add an ``index`` role, to make inline index entries. +* #138: Add an ``index`` role, to make inline index entries. * #443: Allow referencing external graphviz files. From 9420229ca8123a8bd19265022f694fa7aeb14bbc Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Thu, 26 Aug 2010 08:12:49 +0000 Subject: [PATCH 387/744] Whitespace and import fixes. --- sphinx/directives/other.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index cbf19b559..dbc7336ce 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -11,6 +11,8 @@ import os from docutils import nodes from docutils.parsers.rst import Directive, directives +from docutils.parsers.rst.directives.misc import Class +from docutils.parsers.rst.directives.misc import Include as BaseInclude from sphinx import addnodes from sphinx.locale import _ @@ -31,7 +33,6 @@ class TocTree(Directive): Directive to notify Sphinx about the hierarchical structure of the docs, and to include a table-of-contents like tree in the current document. """ - has_content = True required_arguments = 0 optional_arguments = 0 @@ -117,7 +118,6 @@ class Author(Directive): Directive to give the name of the author of the current document or section. Shown in the output only if the show_authors option is on. """ - has_content = False required_arguments = 1 optional_arguments = 0 @@ -150,7 +150,6 @@ class Index(Directive): """ Directive to add entries to the index. """ - has_content = False required_arguments = 1 optional_arguments = 0 @@ -174,7 +173,6 @@ class VersionChange(Directive): """ Directive to describe a change/addition/deprecation in a specific version. """ - has_content = True required_arguments = 1 optional_arguments = 1 @@ -204,7 +202,6 @@ class SeeAlso(Directive): """ An admonition mentioning things to look at as reference. """ - has_content = True required_arguments = 0 optional_arguments = 1 @@ -230,7 +227,6 @@ class TabularColumns(Directive): """ Directive to give an explicit tabulary column definition to LaTeX. """ - has_content = False required_arguments = 1 optional_arguments = 0 @@ -247,7 +243,6 @@ class Centered(Directive): """ Directive to create a centered line of bold text. """ - has_content = False required_arguments = 1 optional_arguments = 0 @@ -264,12 +259,10 @@ class Centered(Directive): return [subnode] + messages - class Acks(Directive): """ Directive for a list of names. """ - has_content = True required_arguments = 0 optional_arguments = 0 @@ -291,7 +284,6 @@ class HList(Directive): """ Directive for a list that gets compacted horizontally. """ - has_content = True required_arguments = 0 optional_arguments = 0 @@ -328,7 +320,6 @@ class Only(Directive): """ Directive to only include text if the given tag(s) are enabled. """ - has_content = True required_arguments = 1 optional_arguments = 0 @@ -345,8 +336,6 @@ class Only(Directive): return [node] -from docutils.parsers.rst.directives.misc import Include as BaseInclude - class Include(BaseInclude): """ Like the standard "Include" directive, but interprets absolute paths @@ -377,7 +366,6 @@ directives.register_directive('only', Only) directives.register_directive('include', Include) # register the standard rst class directive under a different name -from docutils.parsers.rst.directives.misc import Class # only for backwards compatibility now directives.register_directive('cssclass', Class) # new standard name when default-domain with "class" is in effect From 0546dd6912701663dee3805861a0ae0402b76603 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Thu, 26 Aug 2010 11:52:59 +0000 Subject: [PATCH 388/744] Translation update. --- sphinx/locale/bn/LC_MESSAGES/sphinx.js | 2 +- sphinx/locale/bn/LC_MESSAGES/sphinx.mo | Bin 12509 -> 13015 bytes sphinx/locale/bn/LC_MESSAGES/sphinx.po | 764 +++++++++++++--------- sphinx/locale/ca/LC_MESSAGES/sphinx.mo | Bin 9378 -> 9378 bytes sphinx/locale/ca/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/cs/LC_MESSAGES/sphinx.mo | Bin 9067 -> 9067 bytes sphinx/locale/cs/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/da/LC_MESSAGES/sphinx.js | 2 +- sphinx/locale/da/LC_MESSAGES/sphinx.mo | Bin 8668 -> 9469 bytes sphinx/locale/da/LC_MESSAGES/sphinx.po | 729 ++++++++++++--------- sphinx/locale/de/LC_MESSAGES/sphinx.mo | Bin 10037 -> 10037 bytes sphinx/locale/de/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/es/LC_MESSAGES/sphinx.mo | Bin 8121 -> 8121 bytes sphinx/locale/es/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/fi/LC_MESSAGES/sphinx.mo | Bin 9102 -> 9144 bytes sphinx/locale/fi/LC_MESSAGES/sphinx.po | 235 +++---- sphinx/locale/fr/LC_MESSAGES/sphinx.mo | Bin 8938 -> 8938 bytes sphinx/locale/fr/LC_MESSAGES/sphinx.po | 249 ++++--- sphinx/locale/hr/LC_MESSAGES/sphinx.mo | Bin 9195 -> 9195 bytes sphinx/locale/hr/LC_MESSAGES/sphinx.po | 249 ++++--- sphinx/locale/it/LC_MESSAGES/sphinx.mo | Bin 9461 -> 9461 bytes sphinx/locale/it/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/ja/LC_MESSAGES/sphinx.mo | Bin 9950 -> 9950 bytes sphinx/locale/ja/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/lt/LC_MESSAGES/sphinx.mo | Bin 10244 -> 10287 bytes sphinx/locale/lt/LC_MESSAGES/sphinx.po | 244 +++---- sphinx/locale/nl/LC_MESSAGES/sphinx.mo | Bin 9352 -> 9352 bytes sphinx/locale/nl/LC_MESSAGES/sphinx.po | 322 +++++---- sphinx/locale/pl/LC_MESSAGES/sphinx.mo | Bin 9370 -> 9370 bytes sphinx/locale/pl/LC_MESSAGES/sphinx.po | 249 ++++--- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo | Bin 10252 -> 10252 bytes sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po | 317 +++++---- sphinx/locale/ru/LC_MESSAGES/sphinx.mo | Bin 11308 -> 11308 bytes sphinx/locale/ru/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/sl/LC_MESSAGES/sphinx.mo | Bin 9146 -> 9146 bytes sphinx/locale/sl/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/sphinx.pot | 238 +++---- sphinx/locale/sv/LC_MESSAGES/sphinx.mo | Bin 9509 -> 9509 bytes sphinx/locale/sv/LC_MESSAGES/sphinx.po | 319 +++++---- sphinx/locale/tr/LC_MESSAGES/sphinx.mo | Bin 10111 -> 10111 bytes sphinx/locale/tr/LC_MESSAGES/sphinx.po | 306 ++++----- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo | Bin 10983 -> 10983 bytes sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po | 239 +++---- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo | Bin 8755 -> 8755 bytes sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po | 238 +++---- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo | Bin 8934 -> 8934 bytes sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po | 241 +++---- 47 files changed, 3593 insertions(+), 3254 deletions(-) diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.js b/sphinx/locale/bn/LC_MESSAGES/sphinx.js index 277cd3d03..6ffd0c0b8 100644 --- a/sphinx/locale/bn/LC_MESSAGES/sphinx.js +++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.js @@ -1 +1 @@ -Documentation.addTranslations({"locale": "bn", "plural_expr": "(n != 1)", "messages": {"Search Results": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09ab\u09b2\u09be\u09ab\u09b2", "Preparing search...": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09aa\u09cd\u09b0\u09b8\u09cd\u09a4\u09c1\u09a4\u09bf \u099a\u09b2\u099b\u09c7...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7 \u0995\u09c7\u09be\u09a8 \u09ab\u09b2\u09be\u09ab\u09b2 \u09aa\u09be\u0993\u09df\u09be \u09af\u09be\u09df\u09a8\u09bf\u0964 \u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09b6\u09ac\u09cd\u09a6\u0997\u09c1\u09b2\u09c7\u09be\u09b0 \u09b8\u09a0\u09bf\u0995 \u09ac\u09be\u09a8\u09be\u09a8 \u0993 \u09ac\u09bf\u09ad\u09be\u0997 \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09a8 \u09a8\u09bf\u09b6\u09cd\u099a\u09bf\u09a4 \u0995\u09b0\u09c1\u09a8\u0964", "Search finished, found %s page(s) matching the search query.": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u09b6\u09c7\u09b7 \u09b9\u09df\u09c7\u099b\u09c7, \u09ab\u09b2\u09be\u09ab\u09b2\u09c7 %s-\u099f\u09bf \u09aa\u09be\u09a4\u09be \u09aa\u09be\u0993\u09df\u09be \u0997\u09c7\u099b\u09c7\u0964", ", in ": ", -", "Permalink to this headline": "\u098f\u0987 \u09b6\u09bf\u09b0\u09c7\u09be\u09a8\u09be\u09ae\u09c7\u09b0 \u09aa\u09be\u09b0\u09cd\u09ae\u09be\u09b2\u09bf\u0999\u09cd\u0995", "Searching": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u099a\u09b2\u099b\u09c7", "Permalink to this definition": "\u098f\u0987 \u09b8\u0982\u099c\u09cd\u099e\u09be\u09b0 \u09aa\u09be\u09b0\u09cd\u09ae\u09be\u09b2\u09bf\u0999\u09cd\u0995", "Hide Search Matches": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09ae\u09cd\u09af\u09be\u099a\u0997\u09c1\u09b2\u09c7\u09be \u09b2\u09c1\u0995\u09be\u09a8"}}); \ No newline at end of file +Documentation.addTranslations({"locale": "bn", "plural_expr": "(n != 1)", "messages": {"Search Results": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09ab\u09b2\u09be\u09ab\u09b2", "Preparing search...": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09aa\u09cd\u09b0\u09b8\u09cd\u09a4\u09c1\u09a4\u09bf \u099a\u09b2\u099b\u09c7...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7 \u0995\u09c7\u09be\u09a8 \u09ab\u09b2\u09be\u09ab\u09b2 \u09aa\u09be\u0993\u09df\u09be \u09af\u09be\u09df\u09a8\u09bf\u0964 \u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09b6\u09ac\u09cd\u09a6\u0997\u09c1\u09b2\u09c7\u09be\u09b0 \u09b8\u09a0\u09bf\u0995 \u09ac\u09be\u09a8\u09be\u09a8 \u0993 \u09ac\u09bf\u09ad\u09be\u0997 \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09a8 \u09a8\u09bf\u09b6\u09cd\u099a\u09bf\u09a4 \u0995\u09b0\u09c1\u09a8\u0964", "Search finished, found %s page(s) matching the search query.": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u09b6\u09c7\u09b7 \u09b9\u09df\u09c7\u099b\u09c7, \u09ab\u09b2\u09be\u09ab\u09b2\u09c7 %s-\u099f\u09bf \u09aa\u09be\u09a4\u09be \u09aa\u09be\u0993\u09df\u09be \u0997\u09c7\u099b\u09c7\u0964", ", in ": ", -", "Expand sidebar": "", "Permalink to this headline": "\u098f\u0987 \u09b6\u09bf\u09b0\u09c7\u09be\u09a8\u09be\u09ae\u09c7\u09b0 \u09aa\u09be\u09b0\u09cd\u09ae\u09be\u09b2\u09bf\u0999\u09cd\u0995", "Searching": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u099a\u09b2\u099b\u09c7", "Collapse sidebar": "", "Permalink to this definition": "\u098f\u0987 \u09b8\u0982\u099c\u09cd\u099e\u09be\u09b0 \u09aa\u09be\u09b0\u09cd\u09ae\u09be\u09b2\u09bf\u0999\u09cd\u0995", "Hide Search Matches": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09ae\u09cd\u09af\u09be\u099a\u0997\u09c1\u09b2\u09c7\u09be \u09b2\u09c1\u0995\u09be\u09a8"}}); \ No newline at end of file diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.mo b/sphinx/locale/bn/LC_MESSAGES/sphinx.mo index 9b60397e2f33f760d5a6e098877fe7eab7663ef8..db568e1b2f741f0b87b37029291db8b5e7a97ca3 100644 GIT binary patch delta 3462 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 F`8UUhI3xf7 delta 2970 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 Y_2S(49aTep_JQhm?d8cic4ke-ziIRBg#Z8m diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.po b/sphinx/locale/bn/LC_MESSAGES/sphinx.po index d5141c797..88aed5e4c 100644 --- a/sphinx/locale/bn/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.po @@ -1,4 +1,4 @@ -# Translations template for Sphinx. +# Bengali translations for Sphinx. # Copyright (C) 2009 ORGANIZATION # This file is distributed under the same license as the Sphinx project. # FIRST AUTHOR <EMAIL@ADDRESS>, 2009. @@ -8,573 +8,654 @@ msgstr "" "Project-Id-Version: Sphinx 1.0pre/[?1034h2e1ab15e035e\n" "Report-Msgid-Bugs-To: nasim.haque@gmail.com\n" "POT-Creation-Date: 2009-11-08 16:28+0100\n" -"PO-Revision-Date: 2009-11-10 13:42+0100\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Nasimul Haque <nasim.haque@gmail.com>\n" "Language-Team: Nasimul Haque <nasim.haque@gmail.com>\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:130 -#: sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 +#: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%B %d, %Y" -#: sphinx/environment.py:348 -#: sphinx/themes/basic/genindex-single.html:2 -#: sphinx/themes/basic/genindex-split.html:2 -#: sphinx/themes/basic/genindex-split.html:5 -#: sphinx/themes/basic/genindex.html:2 -#: sphinx/themes/basic/genindex.html:5 -#: sphinx/themes/basic/genindex.html:48 -#: sphinx/themes/basic/layout.html:134 -#: sphinx/writers/latex.py:190 -msgid "Index" -msgstr "ইনডেক্স" - -#: sphinx/environment.py:349 -#: sphinx/writers/latex.py:189 -msgid "Module Index" -msgstr "মডিউল ইনডেক্স" - -#: sphinx/environment.py:350 -#: sphinx/themes/basic/defindex.html:16 -msgid "Search Page" -msgstr "অনুসন্ধান পাতা" - -#: sphinx/roles.py:167 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "পাইথন উন্নয়ন পরামর্শ!PEP %s" -#: sphinx/builders/changes.py:70 +#: sphinx/builders/changes.py:72 msgid "Builtins" msgstr "বিল্টইন সমূহ" -#: sphinx/builders/changes.py:72 +#: sphinx/builders/changes.py:74 msgid "Module level" msgstr "মডিউল লেভেল" -#: sphinx/builders/html.py:224 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%b %d, %Y" -#: sphinx/builders/html.py:243 -#: sphinx/themes/basic/defindex.html:21 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "সাধারণ ইনডেক্স" -#: sphinx/builders/html.py:243 +#: sphinx/builders/html.py:279 msgid "index" msgstr "ইনডেক্স" -#: sphinx/builders/html.py:247 -#: sphinx/builders/htmlhelp.py:220 -#: sphinx/builders/qthelp.py:133 -#: sphinx/themes/basic/defindex.html:19 -#: sphinx/themes/basic/modindex.html:2 -#: sphinx/themes/basic/modindex.html:13 -#: sphinx/themes/scrolls/modindex.html:2 -#: sphinx/themes/scrolls/modindex.html:13 -msgid "Global Module Index" -msgstr "গ্লোবাল মডিউল ইনডেক্স" - -#: sphinx/builders/html.py:248 -msgid "modules" -msgstr "মডিউল সমূহ" - -#: sphinx/builders/html.py:304 +#: sphinx/builders/html.py:339 msgid "next" msgstr "পরবর্তী" -#: sphinx/builders/html.py:313 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "পূর্ববর্তী" -#: sphinx/builders/latex.py:162 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "(-" -#: sphinx/directives/__init__.py:78 -#: sphinx/directives/__init__.py:79 -#: sphinx/directives/__init__.py:80 -#: sphinx/directives/__init__.py:81 -msgid "Raises" -msgstr "রেইজেস" - -#: sphinx/directives/__init__.py:82 -#: sphinx/directives/__init__.py:83 -#: sphinx/directives/__init__.py:84 -msgid "Variable" -msgstr "ভ্যারিয়েবল" - -#: sphinx/directives/__init__.py:85 -#: sphinx/directives/__init__.py:86 -#: sphinx/directives/__init__.py:92 -#: sphinx/directives/__init__.py:93 -msgid "Returns" -msgstr "রিটার্নস" - -#: sphinx/directives/__init__.py:94 -msgid "Return type" -msgstr "রিটার্ন টাইপ" - -#: sphinx/directives/__init__.py:169 -msgid "Parameter" -msgstr "প্যারামিটার" - -#: sphinx/directives/__init__.py:173 -msgid "Parameters" -msgstr "প্যারামিটার" - -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "অনুচ্ছেদ লেখক:" -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "মডিউল লেখক:" -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 +#, fuzzy +msgid "Code author: " +msgstr "মডিউল লেখক:" + +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "লেখক:" -#: sphinx/directives/other.py:233 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "আরও দেখুন" -#: sphinx/domains/c.py:124 +#: sphinx/domains/__init__.py:242 +#, python-format +msgid "%s %s" +msgstr "" + +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 +msgid "Parameters" +msgstr "প্যারামিটার" + +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 +msgid "Returns" +msgstr "রিটার্নস" + +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 +msgid "Return type" +msgstr "রিটার্ন টাইপ" + +#: sphinx/domains/c.py:133 #, python-format msgid "%s (C function)" msgstr "%s (C ফাংশন)" -#: sphinx/domains/c.py:126 +#: sphinx/domains/c.py:135 #, python-format msgid "%s (C member)" msgstr "%s (C মেম্বার)" -#: sphinx/domains/c.py:128 +#: sphinx/domains/c.py:137 #, python-format msgid "%s (C macro)" msgstr "%s (C ম্যাক্রো)" -#: sphinx/domains/c.py:130 +#: sphinx/domains/c.py:139 #, python-format msgid "%s (C type)" msgstr "%s (C টাইপ)" -#: sphinx/domains/c.py:132 +#: sphinx/domains/c.py:141 #, python-format msgid "%s (C variable)" msgstr "%s (C ভ্যারিয়েবল)" -#: sphinx/domains/c.py:162 -msgid "C function" -msgstr "C ফাংশন" +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 +msgid "function" +msgstr "ফাংশন" -#: sphinx/domains/c.py:163 -msgid "C member" +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 +#, fuzzy +msgid "member" msgstr "C মেম্বার" -#: sphinx/domains/c.py:164 -msgid "C macro" +#: sphinx/domains/c.py:173 +#, fuzzy +msgid "macro" msgstr "C ম্যাক্রো" -#: sphinx/domains/c.py:165 -msgid "C type" +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 +#, fuzzy +msgid "type" msgstr "C টাইপ" -#: sphinx/domains/c.py:166 -msgid "C variable" +#: sphinx/domains/c.py:175 +#, fuzzy +msgid "variable" msgstr "C ভ্যারিয়েবল" -#: sphinx/domains/python.py:186 +#: sphinx/domains/cpp.py:883 +#, python-format +msgid "%s (C++ class)" +msgstr "%s (C++ ক্লাসে)" + +#: sphinx/domains/cpp.py:898 +#, python-format +msgid "%s (C++ type)" +msgstr "%s (C++ টাইপ)" + +#: sphinx/domains/cpp.py:917 +#, python-format +msgid "%s (C++ member)" +msgstr "%s (C++ মেম্বার)" + +#: sphinx/domains/cpp.py:969 +#, python-format +msgid "%s (C++ function)" +msgstr "%s (C++ ফাংশন)" + +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 +msgid "class" +msgstr "ক্লাস" + +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (বিল্ট-ইন ফাংশন)" -#: sphinx/domains/python.py:187 -#: sphinx/domains/python.py:244 -#: sphinx/domains/python.py:256 -#: sphinx/domains/python.py:269 -#, python-format -msgid "%s() (in module %s)" -msgstr "%s() (%s মডিউলে)" - -#: sphinx/domains/python.py:190 -#, python-format -msgid "%s (built-in variable)" -msgstr "%s (বিল্ট-ইন ভ্যারিয়েবল)" - -#: sphinx/domains/python.py:191 -#: sphinx/domains/python.py:282 -#, python-format -msgid "%s (in module %s)" -msgstr "%s (%s মডিউলে)" - -#: sphinx/domains/python.py:207 -#, python-format -msgid "%s (built-in class)" -msgstr "%s (বিল্ট-ইন ক্লাস)" - -#: sphinx/domains/python.py:208 -#, python-format -msgid "%s (class in %s)" -msgstr "%s (%s ক্লাসে)" - -#: sphinx/domains/python.py:248 -#, python-format -msgid "%s() (%s.%s method)" -msgstr "%s (%s.%s মেথড)" - -#: sphinx/domains/python.py:250 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s মেথড)" -#: sphinx/domains/python.py:260 +#: sphinx/domains/javascript.py:108 #, python-format -msgid "%s() (%s.%s static method)" -msgstr "%s (%s.%s স্ট্যাটিক মেথড)" +msgid "%s() (class)" +msgstr "%s() (ক্লাসে)" -#: sphinx/domains/python.py:263 +#: sphinx/domains/javascript.py:110 #, python-format -msgid "%s() (%s static method)" -msgstr "%s() (%s স্ট্যাটিক মেথড)" +msgid "%s (global variable or constant)" +msgstr "" -#: sphinx/domains/python.py:273 -#, python-format -msgid "%s() (%s.%s class method)" -msgstr "%s() (%s.%s ক্লাস মেথড)" - -#: sphinx/domains/python.py:276 -#, python-format -msgid "%s() (%s class method)" -msgstr "%s() (%s ক্লাস মেথড)" - -#: sphinx/domains/python.py:286 -#, python-format -msgid "%s (%s.%s attribute)" -msgstr "%s (%s.%s এ্যট্রিবিউট)" - -#: sphinx/domains/python.py:288 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s এ্যট্রিবিউট)" +#: sphinx/domains/javascript.py:121 +#, fuzzy +msgid "Arguments" +msgstr "প্যারামিটার" + +#: sphinx/domains/javascript.py:124 +msgid "Throws" +msgstr "" + +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 +msgid "data" +msgstr "ডাটা" + +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 +msgid "attribute" +msgstr "এ্যট্রিবিউট" + +#: sphinx/domains/python.py:98 +#, fuzzy +msgid "Variables" +msgstr "ভ্যারিয়েবল" + +#: sphinx/domains/python.py:101 +msgid "Raises" +msgstr "রেইজেস" + +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 +#, python-format +msgid "%s() (in module %s)" +msgstr "%s() (%s মডিউলে)" + +#: sphinx/domains/python.py:248 +#, python-format +msgid "%s (built-in variable)" +msgstr "%s (বিল্ট-ইন ভ্যারিয়েবল)" + +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 +#, python-format +msgid "%s (in module %s)" +msgstr "%s (%s মডিউলে)" + +#: sphinx/domains/python.py:265 +#, python-format +msgid "%s (built-in class)" +msgstr "%s (বিল্ট-ইন ক্লাস)" + +#: sphinx/domains/python.py:266 +#, python-format +msgid "%s (class in %s)" +msgstr "%s (%s ক্লাসে)" + +#: sphinx/domains/python.py:306 +#, python-format +msgid "%s() (%s.%s method)" +msgstr "%s (%s.%s মেথড)" + +#: sphinx/domains/python.py:318 +#, python-format +msgid "%s() (%s.%s static method)" +msgstr "%s (%s.%s স্ট্যাটিক মেথড)" + +#: sphinx/domains/python.py:321 +#, python-format +msgid "%s() (%s static method)" +msgstr "%s() (%s স্ট্যাটিক মেথড)" + +#: sphinx/domains/python.py:331 +#, python-format +msgid "%s() (%s.%s class method)" +msgstr "%s() (%s.%s ক্লাস মেথড)" + #: sphinx/domains/python.py:334 +#, python-format +msgid "%s() (%s class method)" +msgstr "%s() (%s ক্লাস মেথড)" + +#: sphinx/domains/python.py:344 +#, python-format +msgid "%s (%s.%s attribute)" +msgstr "%s (%s.%s এ্যট্রিবিউট)" + +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "প্লাটফরম:" -#: sphinx/domains/python.py:340 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (মডিউল)" -#: sphinx/domains/python.py:396 -msgid "function" -msgstr "ফাংশন" +#: sphinx/domains/python.py:455 +#, fuzzy +msgid "Python Module Index" +msgstr "মডিউল ইনডেক্স" -#: sphinx/domains/python.py:397 -msgid "data" -msgstr "ডাটা" +#: sphinx/domains/python.py:456 +msgid "modules" +msgstr "মডিউল সমূহ" -#: sphinx/domains/python.py:398 -msgid "class" -msgstr "ক্লাস" +#: sphinx/domains/python.py:501 +msgid "Deprecated" +msgstr "ডেপ্রিকেটেড" -#: sphinx/domains/python.py:399 -#: sphinx/locale/__init__.py:161 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "এক্সেপশন" -#: sphinx/domains/python.py:400 +#: sphinx/domains/python.py:527 msgid "method" msgstr "মেথড" -#: sphinx/domains/python.py:401 -msgid "attribute" -msgstr "এ্যট্রিবিউট" +#: sphinx/domains/python.py:528 +msgid "class method" +msgstr "ক্লাস মেথড" -#: sphinx/domains/python.py:402 -#: sphinx/locale/__init__.py:157 +#: sphinx/domains/python.py:529 +msgid "static method" +msgstr "স্ট্যাটিক মেথড" + +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "মডিউল" -#: sphinx/domains/std.py:67 -#: sphinx/domains/std.py:83 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "ডেপ্রিকেটেড" + +#: sphinx/domains/rst.py:55 +#, python-format +msgid "%s (directive)" +msgstr "" + +#: sphinx/domains/rst.py:57 +#, fuzzy, python-format +msgid "%s (role)" +msgstr "%s (মডিউল)" + +#: sphinx/domains/rst.py:106 +msgid "directive" +msgstr "" + +#: sphinx/domains/rst.py:107 +#, fuzzy +msgid "role" +msgstr "মডিউল" + +#: sphinx/domains/std.py:68 sphinx/domains/std.py:84 #, python-format msgid "environment variable; %s" msgstr "এনভায়রনমেন্ট ভ্যারিয়েবল; %s" -#: sphinx/domains/std.py:156 +#: sphinx/domains/std.py:160 #, python-format msgid "%scommand line option; %s" msgstr "%sকমান্ড লাইন অপশন; %s" -#: sphinx/domains/std.py:324 +#: sphinx/domains/std.py:328 msgid "glossary term" msgstr "শব্দকোষ" -#: sphinx/domains/std.py:325 +#: sphinx/domains/std.py:329 msgid "grammar token" msgstr "ব্যকরণ টোকেন" -#: sphinx/domains/std.py:326 +#: sphinx/domains/std.py:330 +msgid "reference label" +msgstr "" + +#: sphinx/domains/std.py:331 msgid "environment variable" msgstr "এনভায়রনমেন্ট ভ্যারিয়েবল" -#: sphinx/domains/std.py:327 +#: sphinx/domains/std.py:332 msgid "program option" msgstr "প্রোগ্রাম অপশন" -#: sphinx/ext/autodoc.py:892 +#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11 +#: sphinx/themes/basic/genindex-split.html:11 +#: sphinx/themes/basic/genindex-split.html:14 +#: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 +#: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 +#: sphinx/writers/latex.py:174 +msgid "Index" +msgstr "ইনডেক্স" + +#: sphinx/domains/std.py:361 +msgid "Module Index" +msgstr "মডিউল ইনডেক্স" + +#: sphinx/domains/std.py:362 sphinx/themes/basic/defindex.html:25 +msgid "Search Page" +msgstr "অনুসন্ধান পাতা" + +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "বেস: %s" -#: sphinx/ext/autodoc.py:925 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr ":class:`%s` এর উপনাম" -#: sphinx/ext/todo.py:40 +#: sphinx/ext/todo.py:41 msgid "Todo" msgstr "অসমাপ্ত কাজ" -#: sphinx/ext/todo.py:98 +#: sphinx/ext/todo.py:109 +#, fuzzy, python-format +msgid "(The <<original entry>> is located in %s, line %d.)" +msgstr "(%s, %d লাইনে মূল অন্তর্ভুক্তিটি রয়েছে.)" + +#: sphinx/ext/todo.py:117 +msgid "original entry" +msgstr "" + +#: sphinx/ext/viewcode.py:70 +msgid "[source]" +msgstr "" + +#: sphinx/ext/viewcode.py:117 +msgid "[docs]" +msgstr "" + +#: sphinx/ext/viewcode.py:131 +#, fuzzy +msgid "Module code" +msgstr "মডিউল" + +#: sphinx/ext/viewcode.py:137 #, python-format -msgid "(The original entry is located in %s, line %d and can be found " -msgstr "(%s, %d লাইনে মূল অন্তর্ভুক্তিটি রয়েছে, যা পাওয়া যাবে" +msgid "<h1>Source code for %s</h1>" +msgstr "" -#: sphinx/ext/todo.py:104 -msgid "here" -msgstr "এখানে" +#: sphinx/ext/viewcode.py:164 +msgid "Overview: module code" +msgstr "" -#: sphinx/locale/__init__.py:138 +#: sphinx/ext/viewcode.py:165 +msgid "<h1>All modules for which code is available</h1>" +msgstr "" + +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "দৃষ্টি আকর্ষণ" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "সতর্কীকরণ" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "বিপজ্জনক" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "ভুল (এরর)" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "আভাস" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "গুরুত্বপূর্ণ" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "নোট" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "আরও দেখুন" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "পরামর্শ" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "সতর্কতা" -#: sphinx/locale/__init__.py:151 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "%s ভার্সনে নতুন" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "%s ভার্সনে পরিবর্তিত" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "%s ভার্সন থেকে ডেপ্রিকেটেড" -#: sphinx/locale/__init__.py:158 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "কিওয়ার্ড" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "অপারেটর" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "অবজেক্ট" -#: sphinx/locale/__init__.py:162 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "স্ট্যাটমেন্ট" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "বিল্ট-ইন ফাংশন" -#: sphinx/themes/basic/defindex.html:2 +#: sphinx/themes/agogo/layout.html:45 sphinx/themes/basic/globaltoc.html:10 +#: sphinx/themes/basic/localtoc.html:11 +msgid "Table Of Contents" +msgstr "সূচীপত্র" + +#: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 +msgid "Search" +msgstr "অনুসন্ধান" + +#: sphinx/themes/agogo/layout.html:52 sphinx/themes/basic/searchbox.html:15 +msgid "Go" +msgstr "যান" + +#: sphinx/themes/agogo/layout.html:57 sphinx/themes/basic/searchbox.html:20 +msgid "Enter search terms or a module, class or function name." +msgstr "অনুসন্ধানের জন্য টার্ম, মডিউল, ক্লাস অথবা ফাংশনের নাম দিন।" + +#: sphinx/themes/agogo/layout.html:78 sphinx/themes/basic/sourcelink.html:14 +msgid "Show Source" +msgstr "সোর্স দেখুন" + +#: sphinx/themes/basic/defindex.html:11 msgid "Overview" msgstr "ভুমিকা" -#: sphinx/themes/basic/defindex.html:11 +#: sphinx/themes/basic/defindex.html:20 msgid "Indices and tables:" msgstr "ইনডেক্স ও টেবিল সমূহ:" -#: sphinx/themes/basic/defindex.html:14 +#: sphinx/themes/basic/defindex.html:23 msgid "Complete Table of Contents" msgstr "পূর্ণাঙ্গ সূচীপত্র" -#: sphinx/themes/basic/defindex.html:15 +#: sphinx/themes/basic/defindex.html:24 msgid "lists all sections and subsections" msgstr "সকল অনুচ্ছেদ সমূহের তালিকা" -#: sphinx/themes/basic/defindex.html:17 +#: sphinx/themes/basic/defindex.html:26 msgid "search this documentation" msgstr "এই সহায়িকাতে অনুসন্ধা করুন" -#: sphinx/themes/basic/defindex.html:20 +#: sphinx/themes/basic/defindex.html:28 +msgid "Global Module Index" +msgstr "গ্লোবাল মডিউল ইনডেক্স" + +#: sphinx/themes/basic/defindex.html:29 msgid "quick access to all modules" msgstr "সকল মডিউলে দ্রুত প্রবেশ" -#: sphinx/themes/basic/defindex.html:22 +#: sphinx/themes/basic/defindex.html:31 msgid "all functions, classes, terms" msgstr "সকল ফাংশন, ক্লাস, টার্ম" -#: sphinx/themes/basic/genindex-single.html:5 +#: sphinx/themes/basic/genindex-single.html:14 #, python-format msgid "Index – %(key)s" msgstr "ইনডেক্স – %(key)s" -#: sphinx/themes/basic/genindex-single.html:44 -#: sphinx/themes/basic/genindex-split.html:14 -#: sphinx/themes/basic/genindex-split.html:27 -#: sphinx/themes/basic/genindex.html:54 +#: sphinx/themes/basic/genindex-single.html:46 +#: sphinx/themes/basic/genindex-split.html:24 +#: sphinx/themes/basic/genindex-split.html:38 +#: sphinx/themes/basic/genindex.html:56 msgid "Full index on one page" msgstr "এক পাতায় সম্পূর্ণ ইনডেক্স" -#: sphinx/themes/basic/genindex-split.html:7 +#: sphinx/themes/basic/genindex-split.html:16 msgid "Index pages by letter" msgstr "বর্ণানুসারে ইনডেক্স পাতা" -#: sphinx/themes/basic/genindex-split.html:15 +#: sphinx/themes/basic/genindex-split.html:25 msgid "can be huge" msgstr "খুব বড় হতে পারে" -#: sphinx/themes/basic/layout.html:10 +#: sphinx/themes/basic/layout.html:23 msgid "Navigation" msgstr "নেভিগেশন" -#: sphinx/themes/basic/layout.html:42 -msgid "Table Of Contents" -msgstr "সূচীপত্র" - -#: sphinx/themes/basic/layout.html:48 -msgid "Previous topic" -msgstr "পূর্ববর্তী টপিক" - -#: sphinx/themes/basic/layout.html:50 -msgid "previous chapter" -msgstr "পূর্ববর্তী অধ্যায়" - -#: sphinx/themes/basic/layout.html:53 -msgid "Next topic" -msgstr "পরবর্তী টপিক" - -#: sphinx/themes/basic/layout.html:55 -msgid "next chapter" -msgstr "পরবর্তী অধ্যায়" - -#: sphinx/themes/basic/layout.html:60 -msgid "This Page" -msgstr "এই পাতা" - -#: sphinx/themes/basic/layout.html:63 -msgid "Show Source" -msgstr "সোর্স দেখুন" - -#: sphinx/themes/basic/layout.html:73 -msgid "Quick search" -msgstr "দ্রুত অনুসন্ধান" - -#: sphinx/themes/basic/layout.html:76 -msgid "Go" -msgstr "যান" - -#: sphinx/themes/basic/layout.html:81 -msgid "Enter search terms or a module, class or function name." -msgstr "অনুসন্ধানের জন্য টার্ম, মডিউল, ক্লাস অথবা ফাংশনের নাম দিন।" - -#: sphinx/themes/basic/layout.html:122 +#: sphinx/themes/basic/layout.html:113 #, python-format msgid "Search within %(docstitle)s" msgstr "%(docstitle)s এর মধ্যে খুঁজুন" -#: sphinx/themes/basic/layout.html:131 +#: sphinx/themes/basic/layout.html:122 msgid "About these documents" msgstr "এই ডকুমেন্ট সম্পর্কে" -#: sphinx/themes/basic/layout.html:137 -#: sphinx/themes/basic/search.html:2 -#: sphinx/themes/basic/search.html:5 -msgid "Search" -msgstr "অনুসন্ধান" - -#: sphinx/themes/basic/layout.html:140 +#: sphinx/themes/basic/layout.html:131 msgid "Copyright" msgstr "কপিরাইট" -#: sphinx/themes/basic/layout.html:187 -#: sphinx/themes/scrolls/layout.html:83 +#: sphinx/themes/basic/layout.html:180 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">কপিরাইট</a> %(copyright)s." -#: sphinx/themes/basic/layout.html:189 -#: sphinx/themes/scrolls/layout.html:85 +#: sphinx/themes/basic/layout.html:182 #, python-format msgid "© Copyright %(copyright)s." msgstr "© কপিরাইট %(copyright)s." -#: sphinx/themes/basic/layout.html:193 -#: sphinx/themes/scrolls/layout.html:89 +#: sphinx/themes/basic/layout.html:186 #, python-format msgid "Last updated on %(last_updated)s." msgstr "%(last_updated)s সর্বশেষ পরিবর্তন করা হয়েছে।" -#: sphinx/themes/basic/layout.html:196 -#: sphinx/themes/scrolls/layout.html:92 +#: sphinx/themes/basic/layout.html:189 #, python-format -msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." -msgstr "<a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s দিয়ে তৈরী।" - -#: sphinx/themes/basic/modindex.html:36 -#: sphinx/themes/scrolls/modindex.html:37 -msgid "Deprecated" -msgstr "ডেপ্রিকেটেড" +msgid "" +"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." +msgstr "" +"<a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s দিয়ে " +"তৈরী।" #: sphinx/themes/basic/opensearch.xml:4 #, python-format msgid "Search %(docstitle)s" msgstr "%(docstitle)s-এ খুঁজুন" -#: sphinx/themes/basic/search.html:9 +#: sphinx/themes/basic/relations.html:11 +msgid "Previous topic" +msgstr "পূর্ববর্তী টপিক" + +#: sphinx/themes/basic/relations.html:13 +msgid "previous chapter" +msgstr "পূর্ববর্তী অধ্যায়" + +#: sphinx/themes/basic/relations.html:16 +msgid "Next topic" +msgstr "পরবর্তী টপিক" + +#: sphinx/themes/basic/relations.html:18 +msgid "next chapter" +msgstr "পরবর্তী অধ্যায়" + +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." @@ -582,7 +663,7 @@ msgstr "" "অনুসন্ধান করার জন্য অনুগ্রহপূর্বক জাভাস্ক্রিপ্ট \n" " সক্রিয় করুন।" -#: sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -590,23 +671,33 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "" "এখান থেকে এই নথিগুলোতে আপনি অনুসন্ধান করতে পারবেন। \n" -" আপনার কাঙ্ক্ষিত শব্দসমূহ নিচের বাক্সে লিখুন এবং \"অনুসন্ধান\" বাটনে ক্লিক করুন।\n" -" উল্লেখ্য, সকল শব্দসমূহের উপস্থিতি নিয়ে অনুসন্ধান করা হবে। যেসব পাতায় সকল\n" +" আপনার কাঙ্ক্ষিত শব্দসমূহ নিচের বাক্সে লিখুন এবং \"অনুসন্ধান\" বাটনে " +"ক্লিক করুন।\n" +" উল্লেখ্য, সকল শব্দসমূহের উপস্থিতি নিয়ে অনুসন্ধান করা হবে। যেসব পাতায় " +"সকল\n" " শব্দ নেই সেগুলো বাদ দেয়া হবে।" -#: sphinx/themes/basic/search.html:21 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "খুঁজুন" -#: sphinx/themes/basic/search.html:25 -#: sphinx/themes/basic/static/searchtools.js:473 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "অনুসন্ধানের ফলাফল" -#: sphinx/themes/basic/search.html:27 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "আপনার অনুসন্ধানে কোন ফলাফল পাওয়া যায়নি।" +#: sphinx/themes/basic/searchbox.html:12 +msgid "Quick search" +msgstr "দ্রুত অনুসন্ধান" + +#: sphinx/themes/basic/sourcelink.html:11 +msgid "This Page" +msgstr "এই পাতা" + #: sphinx/themes/basic/changes/frameset.html:5 #: sphinx/themes/basic/changes/versionchanges.html:12 #, python-format @@ -635,64 +726,89 @@ msgstr "C API পরিবর্তন" msgid "Other changes" msgstr "অন্যান্য পরিবর্তন" -#: sphinx/themes/basic/static/doctools.js:138 -#: sphinx/writers/html.py:462 -#: sphinx/writers/html.py:467 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "এই শিরোনামের পার্মালিঙ্ক" -#: sphinx/themes/basic/static/doctools.js:144 -#: sphinx/writers/html.py:80 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "এই সংজ্ঞার পার্মালিঙ্ক" -#: sphinx/themes/basic/static/doctools.js:173 +#: sphinx/themes/basic/static/doctools.js:189 msgid "Hide Search Matches" msgstr "অনুসন্ধানের ম্যাচগুলো লুকান" -#: sphinx/themes/basic/static/searchtools.js:274 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "অনুসন্ধান চলছে" -#: sphinx/themes/basic/static/searchtools.js:279 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "অনুসন্ধানের প্রস্তুতি চলছে..." -#: sphinx/themes/basic/static/searchtools.js:352 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", -" -#: sphinx/themes/basic/static/searchtools.js:475 -msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." -msgstr "আপনার অনুসন্ধানে কোন ফলাফল পাওয়া যায়নি। আপনার অনুসন্ধানের শব্দগুলোর সঠিক বানান ও বিভাগ নির্বাচন নিশ্চিত করুন।" +#: sphinx/themes/basic/static/searchtools.js:506 +msgid "" +"Your search did not match any documents. Please make sure that all words " +"are spelled correctly and that you've selected enough categories." +msgstr "" +"আপনার অনুসন্ধানে কোন ফলাফল পাওয়া যায়নি। আপনার অনুসন্ধানের শব্দগুলোর " +"সঠিক বানান ও বিভাগ নির্বাচন নিশ্চিত করুন।" -#: sphinx/themes/basic/static/searchtools.js:477 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "অনুসন্ধান শেষ হয়েছে, ফলাফলে %s-টি পাতা পাওয়া গেছে।" -#: sphinx/writers/latex.py:187 +#: sphinx/themes/default/static/sidebar.js:66 +msgid "Expand sidebar" +msgstr "" + +#: sphinx/themes/default/static/sidebar.js:79 +#: sphinx/themes/default/static/sidebar.js:107 +msgid "Collapse sidebar" +msgstr "" + +#: sphinx/themes/haiku/layout.html:26 +msgid "Contents" +msgstr "" + +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "রিলিজ" -#: sphinx/writers/latex.py:579 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "পাদটীকা" -#: sphinx/writers/latex.py:647 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "পূর্ববর্তী পাতা হতে চলমান" -#: sphinx/writers/latex.py:652 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "পরবর্তী পাতাতে চলমান" -#: sphinx/writers/text.py:166 -#, python-format -msgid "Platform: %s" -msgstr "প্লাটফরম: %s" - -#: sphinx/writers/text.py:428 +#: sphinx/writers/text.py:422 msgid "[image]" msgstr "[ছবি]" +#~ msgid "Variable" +#~ msgstr "ভ্যারিয়েবল" + +#~ msgid "Parameter" +#~ msgstr "প্যারামিটার" + +#~ msgid "C function" +#~ msgstr "C ফাংশন" + +#~ msgid "here" +#~ msgstr "এখানে" + +#~ msgid "Platform: %s" +#~ msgstr "প্লাটফরম: %s" + diff --git a/sphinx/locale/ca/LC_MESSAGES/sphinx.mo b/sphinx/locale/ca/LC_MESSAGES/sphinx.mo index 339c787f665fa514fd65782e080055ed1dde110f..c634314903b8bc0e2c7cb5759c22299efe821445 100644 GIT binary patch delta 32 ocmZ4FxyW<FN-<swT_ZCELqjW5BW(kN$=k%fGMa8)BmRvC0IgIC&;S4c delta 32 ocmZ4FxyW<FN-<tjT_aNk0|P5#6Kw;d$=k%fGMa2&BmRvC0IcE)%K!iX diff --git a/sphinx/locale/ca/LC_MESSAGES/sphinx.po b/sphinx/locale/ca/LC_MESSAGES/sphinx.po index 7bfb66427..a5fd94f4a 100644 --- a/sphinx/locale/ca/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ca/LC_MESSAGES/sphinx.po @@ -8,22 +8,22 @@ msgstr "" "Project-Id-Version: Sphinx 1.0\n" "Report-Msgid-Bugs-To: pau.fernandez@upc.edu\n" "POT-Creation-Date: 2009-05-22 18:51+0200\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Pau Fernández <pau.fernandez@upc.edu>\n" "Language-Team: ca <LL@li.org>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d de %B de %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -36,67 +36,67 @@ msgstr "Mòduls Interns" msgid "Module level" msgstr "Nivell de mòdul" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d %b, %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Índex General" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "índex" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "següent" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "anterior" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (a " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Autor de la secció:" -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Autor del mòdul: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Autor del mòdul: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Vegeu també" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Paràmetres" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Retorna" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Tipus de retorn" @@ -125,12 +125,12 @@ msgstr "%s (tipus de C)" msgid "%s (C variable)" msgstr "%s (variable de C)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "funció" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "membre" @@ -138,7 +138,7 @@ msgstr "membre" msgid "macro" msgstr "macro" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "tipus" @@ -147,190 +147,201 @@ msgstr "tipus" msgid "variable" msgstr "Variable" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (class de C++)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (tipus de C++)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (membre de C++)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (funció de C++)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "class" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (funció interna)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (mètode %s)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (class de C++)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (atribut %s)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Paràmetres" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "atribut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Variable" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Llença" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (al mòdul %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (variable interna)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (al mòdul %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (classe interna)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (class a %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (mètode %s.%s)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (mètode estàtic %s.%s)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (mètode estàtic %s)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (mètode %s.%s)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (mètode %s)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (atribut %s.%s)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Plataformes: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (mòdul)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Índex de Mòduls" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "mòduls" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Obsolet" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "excepció" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (mètode %s)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "mètode estàtic" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "mòdul" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Obsolet" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (mòdul)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "mòdul" @@ -370,7 +381,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Índex" @@ -382,12 +393,12 @@ msgstr "Índex de Mòduls" msgid "Search Page" msgstr "Pàgina de Cerca" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Bases: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "àlies de :class:`%s`" @@ -405,104 +416,104 @@ msgstr "(La <<entrada original>> està a %s, línia %d i.)" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "mòdul" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Atenció" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Compte" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Perill" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Error" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Suggerència" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Important" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Nota" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Vegeu També" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Truc" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Avís" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Novetat de la versió %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Canviat a la versió %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Obsolet desde la versió %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "paraula clau" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operador" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objecte" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "sentència" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "funció interna" @@ -512,7 +523,7 @@ msgid "Table Of Contents" msgstr "Taula de Contingut" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Cerca" @@ -642,7 +653,7 @@ msgstr "Tema següent" msgid "next chapter" msgstr "capítol següent" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." @@ -650,7 +661,7 @@ msgstr "" "Activa JavaScript per utilitzar la funcionalitat\n" "de cerca." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -662,16 +673,16 @@ msgstr "" "que la cerca inclourà totes les paraules que posis. Les pàgines que no\n" "tenen totes les paraules no sortiràn." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "cerca" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Resultats de la Cerca" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "La teva cerca no té resultats." @@ -711,12 +722,12 @@ msgstr "Canvis a la API de C" msgid "Other changes" msgstr "Altres canvis" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Link permanent a aquest títol" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Link permanent a aquesta definició" @@ -724,19 +735,19 @@ msgstr "Link permanent a aquesta definició" msgid "Hide Search Matches" msgstr "Oculta Resultats de Cerca" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Cercant" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Preparant la cerca..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", a " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -744,7 +755,7 @@ msgstr "" "La teva cerca no ha donat resultats. Assegura't que totes les paraules " "estan ben escrites i que has seleccionat prou categories." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Cerca finalitzada, s'han trobat %s pàgin(a/es) de resultats." @@ -754,7 +765,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -762,22 +773,23 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Versió" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "ve de la pàgina anterior" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Continua a la pàgina següent" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[imatge]" + diff --git a/sphinx/locale/cs/LC_MESSAGES/sphinx.mo b/sphinx/locale/cs/LC_MESSAGES/sphinx.mo index 8092a99271820232c09d054850e9a0473fe8cc8c..99db2eebb0b27bbdc9361522f0fba8d3e82ac9d7 100644 GIT binary patch delta 33 pcmaFu_S$X3TTxyMT_ZCELqjW5BW(kN&A&uL85vDC2Z&4a007DL3M>Es delta 33 pcmaFu_S$X3TTxz9T_aNk0|P5#6Kw;d&A&uL85vDB2Z&4a007B<3MT*n diff --git a/sphinx/locale/cs/LC_MESSAGES/sphinx.po b/sphinx/locale/cs/LC_MESSAGES/sphinx.po index 535d4b9fb..eda1e9aba 100644 --- a/sphinx/locale/cs/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/cs/LC_MESSAGES/sphinx.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-11-27 18:39+0100\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Pavel Kosina <pavel.kosina@gmail.com>\n" "Language-Team: Pavel Kosina <pavel.kosina@gmail.com>\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " @@ -16,15 +16,15 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d.%m.%Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -37,67 +37,67 @@ msgstr "Vestavěné funkce " msgid "Module level" msgstr "Úroveň modulů" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d.%m.%Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Rejstřík indexů" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "index" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "další" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "předchozí" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "(v" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Autor sekce: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Autor modulu: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Autor modulu: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Viz také" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parametry" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Vrací" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Typ navrácené hodnoty" @@ -126,12 +126,12 @@ msgstr "%s (C typ)" msgid "%s (C variable)" msgstr "%s (C proměnná)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "funkce" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "člen" @@ -139,7 +139,7 @@ msgstr "člen" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "typ" @@ -148,190 +148,201 @@ msgstr "typ" msgid "variable" msgstr "Proměnná" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (C++ třída)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ typ)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (člen C++)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ funkce)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "třída" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (vestavěná funkce)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (metoda %s)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (C++ třída)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s() (atribut %s)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Parametry" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "atribut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Proměnná" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Vyvolá" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (v modulu %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s() (vestavěná proměnná)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s() (v modulu %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s () (vestavěná proměnná)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s() (třída v %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (metoda %s.%s)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (statická metoda %s.%s)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (statická metoda %s)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (metoda %s.%s)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (metoda %s)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s() (atribut %s.%s)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Platformy: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (module)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Rejstřík modulů " -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "moduly" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Zastaralé" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "výjimka" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (metoda %s)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statická metoda" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "modul" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Zastaralé" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (module)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "modul" @@ -371,7 +382,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Index" @@ -383,12 +394,12 @@ msgstr "Rejstřík modulů " msgid "Search Page" msgstr "Vyhledávací stránka" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "" @@ -406,104 +417,104 @@ msgstr "(Původní záznam je v %s, řádka %d a lze jej nalézt" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "modul" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Výstraha" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Upozornění" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Nebezpečí" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Chyba" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Rada" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Důležité" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Poznámka" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Viz také" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Tip" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Varování" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Nové ve verzi %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Změněno ve verzi %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Zastaralé od verze %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "klíčové slovo" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operátor" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objekt" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "příkaz" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "vestavěná funkce" @@ -513,7 +524,7 @@ msgid "Table Of Contents" msgstr "Obsah" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Hledání" @@ -644,13 +655,13 @@ msgstr "Další téma" msgid "next chapter" msgstr "další kapitola" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "" -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -662,16 +673,16 @@ msgstr "" "Vyhledávání hledá automaticky všechna slova. Nebudou tedy nalezeny " "stránky, obsahující méně slov." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "hledej" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Výsledky hledání" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Nic jsme nenašli." @@ -711,12 +722,12 @@ msgstr "Změny API" msgid "Other changes" msgstr "Ostatní změny" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Trvalý odkaz na tento nadpis" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Trvalý odkaz na tuto definici" @@ -724,19 +735,19 @@ msgstr "Trvalý odkaz na tuto definici" msgid "Hide Search Matches" msgstr "Skrýt výsledky vyhledávání" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Hledám" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Připravuji vyhledávání...." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", v" -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -744,7 +755,7 @@ msgstr "" "Nenalezli jsme žádný dokument. Ujistěte se prosím, že všechna slova jsou " "správně a že jste vybral dostatek kategorií." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Vyhledávání skončilo, nalezeno %s stran." @@ -754,7 +765,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -762,19 +773,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Vydání" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 #, fuzzy msgid "Continued on next page" msgstr "Plný index na jedné stránce" @@ -782,3 +793,4 @@ msgstr "Plný index na jedné stránce" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[obrázek]" + diff --git a/sphinx/locale/da/LC_MESSAGES/sphinx.js b/sphinx/locale/da/LC_MESSAGES/sphinx.js index d17efc474..9bb070cda 100644 --- a/sphinx/locale/da/LC_MESSAGES/sphinx.js +++ b/sphinx/locale/da/LC_MESSAGES/sphinx.js @@ -1 +1 @@ -Documentation.addTranslations({"locale": "da", "plural_expr": "(n != 1)", "messages": {"Search Results": "S\u00f8geresultater", "Preparing search...": "Forbereder s\u00f8gning...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Din s\u00f8gning gav ingen resultater. Kontroll\u00e9r venligst at alle ord er stavet korrekt, og at du har valgt nok kategorier.", "Search finished, found %s page(s) matching the search query.": "S\u00f8gningen fuldf\u00f8rt - fandt %s sider for denne s\u00f8gning.", ", in ": ", i ", "Permalink to this headline": "Permalink til denne overskrift", "Searching": "S\u00f8ger", "Permalink to this definition": "Permalink til denne definition", "Hide Search Matches": "Skjul s\u00f8geresultater"}}); \ No newline at end of file +Documentation.addTranslations({"locale": "da", "plural_expr": "(n != 1)", "messages": {"Search Results": "S\u00f8geresultater", "Preparing search...": "Forbereder s\u00f8gning...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Din s\u00f8gning gav ingen resultater. Kontroll\u00e9r venligst at alle ord er stavet korrekt, og at du har valgt nok kategorier.", "Search finished, found %s page(s) matching the search query.": "S\u00f8gningen fuldf\u00f8rt - fandt %s sider for denne s\u00f8gning.", ", in ": ", i ", "Expand sidebar": "", "Permalink to this headline": "Permalink til denne overskrift", "Searching": "S\u00f8ger", "Collapse sidebar": "", "Permalink to this definition": "Permalink til denne definition", "Hide Search Matches": "Skjul s\u00f8geresultater"}}); \ No newline at end of file diff --git a/sphinx/locale/da/LC_MESSAGES/sphinx.mo b/sphinx/locale/da/LC_MESSAGES/sphinx.mo index a47f10deb890a700429fd86658a1e1022186ef93..bc95a44fa72637a6d567e2b392c08701e2cffca9 100644 GIT binary patch delta 3640 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 Tt))=@uwV7(wfJoPIVArCPoX;< delta 2883 zcmZA1eN5F=9LMp4qA3W7JSb24L(rLkqeLm^Qz@E=NR7~>tPA%CUbzo+L7i4!t=35A zA9iZ3EOblFEw#{DnYFZ4+NLg6ZndeatjTiAoIOZO?frqDtHmz&b<XcReb4ut-@U%z z?3~#75%Dh>{<iX;%m2=J)&Bh}9%{@mswFrGr{P$fjeJa_X9!cNufcSD9#gOrHGV%P z;So&4FZ$bK<_v{29{hxv*o(t)ctZb#`KSStQSGx(?R7X3n=uPxsCiz&Y}|*V@PzmL zdsM&QaVn-T=@=6;W*&uEJZM7ga5rY)UL+Rt2@b)NI1|r!^>ntS9pxc&nj*Xnr=t4L z#KAb<Yp?d&A41LV!!p)4YbYq<y{H`?M=g9F7h?}<qPtj43(dkDti*C`LIvK58n+Wo z94SZ5_jYoBpr4~M^feB^9*pV1B?>zBo2UumIXF$2jEXefb0Vt$Y|O`6<YQLzMU3V- zuWr5i8>oQaLEVibUi}nmy>C*;zk)r-1EuT|DuCa-jyF)3awrF`42?%EbPsY0rW)_X zWvIYjNA_U4umF#u`u~W^;1$#nT|<pGspMY|h7q1Rq@!+c4yr>bYQpI_9T%cf-+_Ip zM&>g6Q5*Q!^8{*qH}Wy(_)`DNI1qbL8Mq#!pn(HM^aqlNo2aMZc<l6^e})R|6z1U% zs0p=grZ5Sp1#?kHHvyHoNvMSuqQ=*t-U=5rKDM5M0_Z>;#a7SlsDb-X6COZi>V4Gv ze-xFm3!XoF?N?CadQqvr;k75Sy4D%tnS*35W(p}N#ZyqJor{|IG1U9ojEZz4c5$mW zqb8og)z(6lsDKuuE?d;|DI`gz19geFA=k-tp~fG>B)$KqDJXSky$(I70oPDJFv)!B zjK`x=U5;940V<WXsD(nPOS%EIk!`4X-a{SPDb$88AxSZV7@V#5Ka0XtEW<I_g1Xh4 zQ9C@18t@fr2NzKR|A{kjAhRl<dr*N?dG$w8N464``Y0;1Pk8N5V@#>tLV>KBmr;T2 zMF%gSb`sCF)X%{xoQMi+4Jwc)QJ1X)@55bQ`!A@4ui{nw8=2Dl%B0#zLN58&&axS; z2`8aaHW~GrEI{pG5i0Trky9}bqf*+28ow3wgR~2^k>l8Rq^P6o@#?*(ObueN0vMi0 z{?*apffg!84JboJUV++qwdYbCO})W$BkD5kK+UrcEATKX(3@U+CWG{YRDfD|K59ct zVqRf6Y9SXDVFZ<tb*LSwpW0tUP0)#V<9@Hb2Q}e!)E$WDB54C7Jss5e+ff;tj9Mo) zpMp~T0BXTH)WCXAKdz-7LcL}eQ4=LN{YRCKdOi^~ZV@VgI#ebDsKC}C_tk7ft@A4K z+Q-ax3L3B<711%&L?=<N+Zj~*MO2`Fp(eb63S<xmPtl~Ho|mG2u*y+KI>UQjf%=`P z^6K?CL_g{c6x6W=mD)B`$7hginio(JcOe(pyo(C7JKt6&)Yu&f5nG)&-F}dmYrjj} zZSAmF#e~`wt`ms{ngd~9&~d}<(X~#X%?U;teeJH_351<-ZN*M75O$q$e#aN~JB_}u z)8INykyTVovAS4)(#B$^X;rwfJrD_-IZnve7>!6b)Zj)bw6AS-VYM$B@HGTodm*XL zRwn1!*5q#wE={Q!Fto-O^@ZGaH)=mht%{$<ss~2wiyIzjHMJ3c#MIpycqHw^_&~_l zY&WO7_Ii50osjWJ{0cYf+GjH++fOsfZ9-;+^=B5@w=!Q#np#pi#kph3w6fBo(mM~< zjvO2pS7z%+)!434kK2Z<S++YX$NrIZ$R5t#Fn_{ax7~@fMgw8L8w@nNPV1_0OFMgQ z(vzsu7HD<?x6-jv`6zZG%}xvX=<Ctemkq~m%Gpvfr-W>@^k<_a<ZDqjN<ywb=!Pit zXQQM)8*b2M<}R`8bMx(<+{*`d=hgo=8=21iec3p;+<Ei=>0o5q*gb<A0>Pl`x7}k~ eZFRwDySku$APKQw7L?i4!fAF{;XHe&u;?F(*m+a{ diff --git a/sphinx/locale/da/LC_MESSAGES/sphinx.po b/sphinx/locale/da/LC_MESSAGES/sphinx.po index c975e747e..f83c7c042 100644 --- a/sphinx/locale/da/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/da/LC_MESSAGES/sphinx.po @@ -1,4 +1,4 @@ -# Translations template for Sphinx. +# Danish translations for Sphinx. # Copyright (C) 2009 The Sphinx Team # This file is distributed under the same license as the Sphinx project. # @@ -8,553 +8,649 @@ msgstr "" "Project-Id-Version: Sphinx 1.0pre/[?1034h2e1ab15e035e\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2009-11-08 16:28+0100\n" -"PO-Revision-Date: 2010-06-03 23:47+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Ask Hjorth Larsen <asklarsen@gmail.com>\n" "Language-Team: Danish <dansk@dansk-gruppen.dk\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -# 21. april, 2010 -#: sphinx/environment.py:130 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 +#: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d. %B, %Y" -#: sphinx/environment.py:348 sphinx/themes/basic/genindex-single.html:2 -#: sphinx/themes/basic/genindex-split.html:2 -#: sphinx/themes/basic/genindex-split.html:5 -#: sphinx/themes/basic/genindex.html:2 sphinx/themes/basic/genindex.html:5 -#: sphinx/themes/basic/genindex.html:48 sphinx/themes/basic/layout.html:134 -#: sphinx/writers/latex.py:190 -msgid "Index" -msgstr "Indeks" - -#: sphinx/environment.py:349 sphinx/writers/latex.py:189 -msgid "Module Index" -msgstr "Modulindeks" - -# Ikke 'Søg på side' -#: sphinx/environment.py:350 sphinx/themes/basic/defindex.html:16 -msgid "Search Page" -msgstr "Søgeside" - -#: sphinx/roles.py:167 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" -#: sphinx/builders/changes.py:70 +#: sphinx/builders/changes.py:72 msgid "Builtins" msgstr "Indbyggede" -#: sphinx/builders/changes.py:72 +#: sphinx/builders/changes.py:74 msgid "Module level" msgstr "Modulniveau" -# Apr 21, 2010 -#: sphinx/builders/html.py:224 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d. %b, %Y" -#: sphinx/builders/html.py:243 sphinx/themes/basic/defindex.html:21 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Generelt indeks" -#: sphinx/builders/html.py:243 +#: sphinx/builders/html.py:279 msgid "index" msgstr "indeks" -#: sphinx/builders/html.py:247 sphinx/builders/htmlhelp.py:220 -#: sphinx/builders/qthelp.py:133 sphinx/themes/basic/defindex.html:19 -#: sphinx/themes/basic/modindex.html:2 sphinx/themes/basic/modindex.html:13 -#: sphinx/themes/scrolls/modindex.html:2 sphinx/themes/scrolls/modindex.html:13 -msgid "Global Module Index" -msgstr "Globalt modulindeks" - -#: sphinx/builders/html.py:248 -msgid "modules" -msgstr "moduler" - -#: sphinx/builders/html.py:304 +#: sphinx/builders/html.py:339 msgid "next" msgstr "næste" -#: sphinx/builders/html.py:313 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "forrige" -#: sphinx/builders/latex.py:162 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (i " -#: sphinx/directives/__init__.py:78 sphinx/directives/__init__.py:79 -#: sphinx/directives/__init__.py:80 sphinx/directives/__init__.py:81 -msgid "Raises" -msgstr "Rejser" - -#: sphinx/directives/__init__.py:82 sphinx/directives/__init__.py:83 -#: sphinx/directives/__init__.py:84 -msgid "Variable" -msgstr "Variabel" - -#: sphinx/directives/__init__.py:85 sphinx/directives/__init__.py:86 -#: sphinx/directives/__init__.py:92 sphinx/directives/__init__.py:93 -msgid "Returns" -msgstr "Returnerer" - -#: sphinx/directives/__init__.py:94 -msgid "Return type" -msgstr "Returtype" - -#: sphinx/directives/__init__.py:169 -msgid "Parameter" -msgstr "Parameter" - -#: sphinx/directives/__init__.py:173 -msgid "Parameters" -msgstr "Parametre" - -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Afsnitsforfatter: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Modulforfatter: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 +#, fuzzy +msgid "Code author: " +msgstr "Modulforfatter: " + +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Forfatter: " -#: sphinx/directives/other.py:233 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Se også" -#: sphinx/domains/c.py:124 +#: sphinx/domains/__init__.py:242 +#, python-format +msgid "%s %s" +msgstr "" + +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 +msgid "Parameters" +msgstr "Parametre" + +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 +msgid "Returns" +msgstr "Returnerer" + +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 +msgid "Return type" +msgstr "Returtype" + +#: sphinx/domains/c.py:133 #, python-format msgid "%s (C function)" msgstr "%s (C-funktion)" -#: sphinx/domains/c.py:126 +#: sphinx/domains/c.py:135 #, python-format msgid "%s (C member)" msgstr "%s (C-medlem)" -#: sphinx/domains/c.py:128 +#: sphinx/domains/c.py:137 #, python-format msgid "%s (C macro)" msgstr "%s (C-makro)" -#: sphinx/domains/c.py:130 +#: sphinx/domains/c.py:139 #, python-format msgid "%s (C type)" msgstr "%s (C-type)" -#: sphinx/domains/c.py:132 +#: sphinx/domains/c.py:141 #, python-format msgid "%s (C variable)" msgstr "%s (C-variabel)" -#: sphinx/domains/c.py:162 -msgid "C function" -msgstr "C-funktion" +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 +msgid "function" +msgstr "funktion" -#: sphinx/domains/c.py:163 -msgid "C member" -msgstr "C-medlem" +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 +msgid "member" +msgstr "medlem" -#: sphinx/domains/c.py:164 -msgid "C macro" -msgstr "C-makro" +#: sphinx/domains/c.py:173 +msgid "macro" +msgstr "makro" -#: sphinx/domains/c.py:165 -msgid "C type" -msgstr "C-type" +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 +msgid "type" +msgstr "type" -#: sphinx/domains/c.py:166 -msgid "C variable" -msgstr "C-variabel" +#: sphinx/domains/c.py:175 +msgid "variable" +msgstr "variabel" -#: sphinx/domains/python.py:186 +#: sphinx/domains/cpp.py:883 +#, python-format +msgid "%s (C++ class)" +msgstr "%s (C++-klasse)" + +#: sphinx/domains/cpp.py:898 +#, python-format +msgid "%s (C++ type)" +msgstr "%s (C++-type)" + +#: sphinx/domains/cpp.py:917 +#, python-format +msgid "%s (C++ member)" +msgstr "%s (C++-medlem)" + +#: sphinx/domains/cpp.py:969 +#, python-format +msgid "%s (C++ function)" +msgstr "%s (C++-funktion)" + +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 +msgid "class" +msgstr "klasse" + +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (indbygget funktion)" -#: sphinx/domains/python.py:187 sphinx/domains/python.py:244 -#: sphinx/domains/python.py:256 sphinx/domains/python.py:269 -#, python-format -msgid "%s() (in module %s)" -msgstr "%s() (i modulet %s)" - -#: sphinx/domains/python.py:190 -#, python-format -msgid "%s (built-in variable)" -msgstr "%s (indbygget variabel)" - -#: sphinx/domains/python.py:191 sphinx/domains/python.py:282 -#, python-format -msgid "%s (in module %s)" -msgstr "%s (i modulet %s)" - -#: sphinx/domains/python.py:207 -#, python-format -msgid "%s (built-in class)" -msgstr "%s (indbygget klasse)" - -#: sphinx/domains/python.py:208 -#, python-format -msgid "%s (class in %s)" -msgstr "%s (klasse i %s)" - -#: sphinx/domains/python.py:248 -#, python-format -msgid "%s() (%s.%s method)" -msgstr "%s() (metode i %s.%s)" - -#: sphinx/domains/python.py:250 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (metode i %s)" -#: sphinx/domains/python.py:260 +#: sphinx/domains/javascript.py:108 #, python-format -msgid "%s() (%s.%s static method)" -msgstr "%s() (statisk metode i %s.%s)" +msgid "%s() (class)" +msgstr "%s() (klasse)" -#: sphinx/domains/python.py:263 +#: sphinx/domains/javascript.py:110 #, python-format -msgid "%s() (%s static method)" -msgstr "%s() (statisk metode i %s)" +msgid "%s (global variable or constant)" +msgstr "" -#: sphinx/domains/python.py:273 -#, python-format -msgid "%s() (%s.%s class method)" -msgstr "%s() (klassemetode i %s.%s)" - -#: sphinx/domains/python.py:276 -#, python-format -msgid "%s() (%s class method)" -msgstr "%s() (klassemetode i %s)" - -#: sphinx/domains/python.py:286 -#, python-format -msgid "%s (%s.%s attribute)" -msgstr "%s (attribut i %s.%s)" - -#: sphinx/domains/python.py:288 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (attribut i %s)" +#: sphinx/domains/javascript.py:121 +#, fuzzy +msgid "Arguments" +msgstr "Parametre" + +#: sphinx/domains/javascript.py:124 +msgid "Throws" +msgstr "" + +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 +msgid "data" +msgstr "data" + +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 +msgid "attribute" +msgstr "attribut" + +#: sphinx/domains/python.py:98 +#, fuzzy +msgid "Variables" +msgstr "Variabel" + +#: sphinx/domains/python.py:101 +msgid "Raises" +msgstr "Rejser" + +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 +#, python-format +msgid "%s() (in module %s)" +msgstr "%s() (i modulet %s)" + +#: sphinx/domains/python.py:248 +#, python-format +msgid "%s (built-in variable)" +msgstr "%s (indbygget variabel)" + +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 +#, python-format +msgid "%s (in module %s)" +msgstr "%s (i modulet %s)" + +#: sphinx/domains/python.py:265 +#, python-format +msgid "%s (built-in class)" +msgstr "%s (indbygget klasse)" + +#: sphinx/domains/python.py:266 +#, python-format +msgid "%s (class in %s)" +msgstr "%s (klasse i %s)" + +#: sphinx/domains/python.py:306 +#, python-format +msgid "%s() (%s.%s method)" +msgstr "%s() (metode i %s.%s)" + +#: sphinx/domains/python.py:318 +#, python-format +msgid "%s() (%s.%s static method)" +msgstr "%s() (statisk metode i %s.%s)" + +#: sphinx/domains/python.py:321 +#, python-format +msgid "%s() (%s static method)" +msgstr "%s() (statisk metode i %s)" + +#: sphinx/domains/python.py:331 +#, python-format +msgid "%s() (%s.%s class method)" +msgstr "%s() (klassemetode i %s.%s)" + #: sphinx/domains/python.py:334 +#, python-format +msgid "%s() (%s class method)" +msgstr "%s() (klassemetode i %s)" + +#: sphinx/domains/python.py:344 +#, python-format +msgid "%s (%s.%s attribute)" +msgstr "%s (attribut i %s.%s)" + +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Platforme: " -#: sphinx/domains/python.py:340 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (modul)" -#: sphinx/domains/python.py:396 -msgid "function" -msgstr "funktion" +#: sphinx/domains/python.py:455 +#, fuzzy +msgid "Python Module Index" +msgstr "Modulindeks" -#: sphinx/domains/python.py:397 -msgid "data" -msgstr "data" +#: sphinx/domains/python.py:456 +msgid "modules" +msgstr "moduler" -#: sphinx/domains/python.py:398 -msgid "class" -msgstr "klasse" +#: sphinx/domains/python.py:501 +msgid "Deprecated" +msgstr "Deprecieret" -#: sphinx/domains/python.py:399 sphinx/locale/__init__.py:161 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "undtagelse" -#: sphinx/domains/python.py:400 +#: sphinx/domains/python.py:527 msgid "method" msgstr "metode" -#: sphinx/domains/python.py:401 -msgid "attribute" -msgstr "attribut" +#: sphinx/domains/python.py:528 +msgid "class method" +msgstr "klassemetode" -#: sphinx/domains/python.py:402 sphinx/locale/__init__.py:157 +#: sphinx/domains/python.py:529 +msgid "static method" +msgstr "statisk metode" + +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "modul" -#: sphinx/domains/std.py:67 sphinx/domains/std.py:83 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr " (deprecieret)" + +#: sphinx/domains/rst.py:55 +#, python-format +msgid "%s (directive)" +msgstr "" + +#: sphinx/domains/rst.py:57 +#, python-format +msgid "%s (role)" +msgstr "" + +#: sphinx/domains/rst.py:106 +msgid "directive" +msgstr "" + +#: sphinx/domains/rst.py:107 +msgid "role" +msgstr "" + +#: sphinx/domains/std.py:68 sphinx/domains/std.py:84 #, python-format msgid "environment variable; %s" msgstr "miljøvariabel; %s" -#: sphinx/domains/std.py:156 +#: sphinx/domains/std.py:160 #, python-format msgid "%scommand line option; %s" msgstr "%skommandolinjetilvalg; %s" -#: sphinx/domains/std.py:324 +#: sphinx/domains/std.py:328 msgid "glossary term" msgstr "begreb i ordliste" -#: sphinx/domains/std.py:325 +#: sphinx/domains/std.py:329 msgid "grammar token" msgstr "grammatisk element" -#: sphinx/domains/std.py:326 +#: sphinx/domains/std.py:330 +msgid "reference label" +msgstr "" + +#: sphinx/domains/std.py:331 msgid "environment variable" msgstr "miljøvariabel" -#: sphinx/domains/std.py:327 +#: sphinx/domains/std.py:332 msgid "program option" msgstr "programtilvalg" -#: sphinx/ext/autodoc.py:892 +#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11 +#: sphinx/themes/basic/genindex-split.html:11 +#: sphinx/themes/basic/genindex-split.html:14 +#: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 +#: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 +#: sphinx/writers/latex.py:174 +msgid "Index" +msgstr "Indeks" + +#: sphinx/domains/std.py:361 +msgid "Module Index" +msgstr "Modulindeks" + +#: sphinx/domains/std.py:362 sphinx/themes/basic/defindex.html:25 +msgid "Search Page" +msgstr "Søgeside" + +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Baser: %s" -#: sphinx/ext/autodoc.py:925 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "alias for :class:`%s`" -#: sphinx/ext/todo.py:40 +#: sphinx/ext/todo.py:41 msgid "Todo" msgstr "Todo" -#: sphinx/ext/todo.py:98 +#: sphinx/ext/todo.py:109 +#, fuzzy, python-format +msgid "(The <<original entry>> is located in %s, line %d.)" +msgstr "(Det <<oprindelige punkt>> befinder sig i %s, linje %d.)" + +#: sphinx/ext/todo.py:117 +msgid "original entry" +msgstr "" + +#: sphinx/ext/viewcode.py:70 +msgid "[source]" +msgstr "" + +#: sphinx/ext/viewcode.py:117 +msgid "[docs]" +msgstr "" + +#: sphinx/ext/viewcode.py:131 +#, fuzzy +msgid "Module code" +msgstr "modul" + +#: sphinx/ext/viewcode.py:137 #, python-format -msgid "(The original entry is located in %s, line %d and can be found " -msgstr "(Det oprindelige punkt befinder sig i %s, linje %d, og kan findes " +msgid "<h1>Source code for %s</h1>" +msgstr "" -#: sphinx/ext/todo.py:104 -msgid "here" -msgstr "her" +#: sphinx/ext/viewcode.py:164 +msgid "Overview: module code" +msgstr "" -#: sphinx/locale/__init__.py:138 +#: sphinx/ext/viewcode.py:165 +msgid "<h1>All modules for which code is available</h1>" +msgstr "" + +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Vær opmærksom" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Forsigtig" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Fare" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Fejl" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Fif" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Vigtigt" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Bemærk" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Se også" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Tip" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Advarsel" -#: sphinx/locale/__init__.py:151 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Ny i version %s" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Ændret i version %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Deprecieret siden version %s" -#: sphinx/locale/__init__.py:158 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "nøgleord" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operator" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objekt" -#: sphinx/locale/__init__.py:162 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "erklæring" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "indbygget funktion" -#: sphinx/themes/basic/defindex.html:2 +#: sphinx/themes/agogo/layout.html:45 sphinx/themes/basic/globaltoc.html:10 +#: sphinx/themes/basic/localtoc.html:11 +msgid "Table Of Contents" +msgstr "Indholdsfortegnelse" + +#: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 +msgid "Search" +msgstr "Søg" + +#: sphinx/themes/agogo/layout.html:52 sphinx/themes/basic/searchbox.html:15 +msgid "Go" +msgstr "Søg" + +#: sphinx/themes/agogo/layout.html:57 sphinx/themes/basic/searchbox.html:20 +msgid "Enter search terms or a module, class or function name." +msgstr "Indtast søgeord eller navnet på et modul, en klasse eller en funktion." + +#: sphinx/themes/agogo/layout.html:78 sphinx/themes/basic/sourcelink.html:14 +msgid "Show Source" +msgstr "Vis kilde" + +#: sphinx/themes/basic/defindex.html:11 msgid "Overview" msgstr "Oversigt" -#: sphinx/themes/basic/defindex.html:11 +#: sphinx/themes/basic/defindex.html:20 msgid "Indices and tables:" msgstr "Indeks og tabeller:" -#: sphinx/themes/basic/defindex.html:14 +#: sphinx/themes/basic/defindex.html:23 msgid "Complete Table of Contents" msgstr "Fuldstændig indholdsfortegnelse" -#: sphinx/themes/basic/defindex.html:15 +#: sphinx/themes/basic/defindex.html:24 msgid "lists all sections and subsections" msgstr "viser alle afsnit og underafsnit" -#: sphinx/themes/basic/defindex.html:17 +#: sphinx/themes/basic/defindex.html:26 msgid "search this documentation" msgstr "søg i denne dokumentation" -#: sphinx/themes/basic/defindex.html:20 +#: sphinx/themes/basic/defindex.html:28 +msgid "Global Module Index" +msgstr "Globalt modulindeks" + +#: sphinx/themes/basic/defindex.html:29 msgid "quick access to all modules" msgstr "hurtig adgang til alle moduler" -#: sphinx/themes/basic/defindex.html:22 +#: sphinx/themes/basic/defindex.html:31 msgid "all functions, classes, terms" msgstr "alle funktioner, klasser, begreber" -#: sphinx/themes/basic/genindex-single.html:5 +#: sphinx/themes/basic/genindex-single.html:14 #, python-format msgid "Index – %(key)s" msgstr "Indeks – %(key)s" -#: sphinx/themes/basic/genindex-single.html:44 -#: sphinx/themes/basic/genindex-split.html:14 -#: sphinx/themes/basic/genindex-split.html:27 -#: sphinx/themes/basic/genindex.html:54 +#: sphinx/themes/basic/genindex-single.html:46 +#: sphinx/themes/basic/genindex-split.html:24 +#: sphinx/themes/basic/genindex-split.html:38 +#: sphinx/themes/basic/genindex.html:56 msgid "Full index on one page" msgstr "Fuldt indeks på én side" -#: sphinx/themes/basic/genindex-split.html:7 +#: sphinx/themes/basic/genindex-split.html:16 msgid "Index pages by letter" msgstr "Indeksér sider efter bogstav" -# refererer til indeks -#: sphinx/themes/basic/genindex-split.html:15 +#: sphinx/themes/basic/genindex-split.html:25 msgid "can be huge" msgstr "kan være enormt" -#: sphinx/themes/basic/layout.html:10 +#: sphinx/themes/basic/layout.html:23 msgid "Navigation" msgstr "Navigation" -#: sphinx/themes/basic/layout.html:42 -msgid "Table Of Contents" -msgstr "Indholdsfortegnelse" - -#: sphinx/themes/basic/layout.html:48 -msgid "Previous topic" -msgstr "Forrige emne" - -#: sphinx/themes/basic/layout.html:50 -msgid "previous chapter" -msgstr "forrige kapitel" - -#: sphinx/themes/basic/layout.html:53 -msgid "Next topic" -msgstr "Næste emne" - -#: sphinx/themes/basic/layout.html:55 -msgid "next chapter" -msgstr "næste kapitel" - -#: sphinx/themes/basic/layout.html:60 -msgid "This Page" -msgstr "Denne side" - -#: sphinx/themes/basic/layout.html:63 -msgid "Show Source" -msgstr "Vis kilde" - -#: sphinx/themes/basic/layout.html:73 -msgid "Quick search" -msgstr "Hurtig søgning" - -# Referencen fra layout.html:76 er til en søgeknap -#: sphinx/themes/basic/layout.html:76 -msgid "Go" -msgstr "Søg" - -#: sphinx/themes/basic/layout.html:81 -msgid "Enter search terms or a module, class or function name." -msgstr "Indtast søgeord eller navnet på et modul, en klasse eller en funktion." - -#: sphinx/themes/basic/layout.html:122 +#: sphinx/themes/basic/layout.html:113 #, python-format msgid "Search within %(docstitle)s" msgstr "Søg i %(docstitle)s" -#: sphinx/themes/basic/layout.html:131 +#: sphinx/themes/basic/layout.html:122 msgid "About these documents" msgstr "Om disse dokumenter" -#: sphinx/themes/basic/layout.html:137 sphinx/themes/basic/search.html:2 -#: sphinx/themes/basic/search.html:5 -msgid "Search" -msgstr "Søg" - -#: sphinx/themes/basic/layout.html:140 +#: sphinx/themes/basic/layout.html:131 msgid "Copyright" msgstr "Ophavsret" -#: sphinx/themes/basic/layout.html:187 sphinx/themes/scrolls/layout.html:83 +#: sphinx/themes/basic/layout.html:180 #, python-format msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." msgstr "© <a href=\"%(path)s\">Ophavsret</a> %(copyright)s." -#: sphinx/themes/basic/layout.html:189 sphinx/themes/scrolls/layout.html:85 +#: sphinx/themes/basic/layout.html:182 #, python-format msgid "© Copyright %(copyright)s." msgstr "© Ophavsret %(copyright)s." -# datoformatet passer ikke med "den %(last_updated)s" -#: sphinx/themes/basic/layout.html:193 sphinx/themes/scrolls/layout.html:89 +#: sphinx/themes/basic/layout.html:186 #, python-format msgid "Last updated on %(last_updated)s." msgstr "Sidst opdateret %(last_updated)s." -#: sphinx/themes/basic/layout.html:196 sphinx/themes/scrolls/layout.html:92 +#: sphinx/themes/basic/layout.html:189 #, python-format msgid "" "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " "%(sphinx_version)s." -msgstr "Bygget med <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." - -#: sphinx/themes/basic/modindex.html:36 sphinx/themes/scrolls/modindex.html:37 -msgid "Deprecated" -msgstr "Deprecieret" +msgstr "" +"Bygget med <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." #: sphinx/themes/basic/opensearch.xml:4 #, python-format msgid "Search %(docstitle)s" msgstr "Søg i %(docstitle)s" -#: sphinx/themes/basic/search.html:9 +#: sphinx/themes/basic/relations.html:11 +msgid "Previous topic" +msgstr "Forrige emne" + +#: sphinx/themes/basic/relations.html:13 +msgid "previous chapter" +msgstr "forrige kapitel" + +#: sphinx/themes/basic/relations.html:16 +msgid "Next topic" +msgstr "Næste emne" + +#: sphinx/themes/basic/relations.html:18 +msgid "next chapter" +msgstr "næste kapitel" + +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." @@ -562,7 +658,7 @@ msgstr "" "Aktivér venligst JavaScript for at aktivere\n" " søgefunktionalitet." -#: sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -574,19 +670,27 @@ msgstr "" " automatisk vil søge på alle ordene. Sider, der indeholder\n" " færre ord, vil ikke indgå i resultaterne." -#: sphinx/themes/basic/search.html:21 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "søg" -#: sphinx/themes/basic/search.html:25 -#: sphinx/themes/basic/static/searchtools.js:473 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Søgeresultater" -#: sphinx/themes/basic/search.html:27 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Din søgning gav ingen resultater." +#: sphinx/themes/basic/searchbox.html:12 +msgid "Quick search" +msgstr "Hurtig søgning" + +#: sphinx/themes/basic/sourcelink.html:11 +msgid "This Page" +msgstr "Denne side" + #: sphinx/themes/basic/changes/frameset.html:5 #: sphinx/themes/basic/changes/versionchanges.html:12 #, python-format @@ -615,64 +719,89 @@ msgstr "Ændringer i C-API" msgid "Other changes" msgstr "Andre ændringer" -#: sphinx/themes/basic/static/doctools.js:138 sphinx/writers/html.py:462 -#: sphinx/writers/html.py:467 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Permalink til denne overskrift" -#: sphinx/themes/basic/static/doctools.js:144 sphinx/writers/html.py:80 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Permalink til denne definition" -#: sphinx/themes/basic/static/doctools.js:173 +#: sphinx/themes/basic/static/doctools.js:189 msgid "Hide Search Matches" msgstr "Skjul søgeresultater" -#: sphinx/themes/basic/static/searchtools.js:274 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Søger" -#: sphinx/themes/basic/static/searchtools.js:279 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Forbereder søgning..." -#: sphinx/themes/basic/static/searchtools.js:352 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", i " -#: sphinx/themes/basic/static/searchtools.js:475 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." -msgstr "Din søgning gav ingen resultater. Kontrollér venligst at alle ord er stavet korrekt, og at du har valgt nok kategorier." +msgstr "" +"Din søgning gav ingen resultater. Kontrollér venligst at alle ord er " +"stavet korrekt, og at du har valgt nok kategorier." -#: sphinx/themes/basic/static/searchtools.js:477 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Søgningen fuldført - fandt %s sider for denne søgning." -#: sphinx/writers/latex.py:187 +#: sphinx/themes/default/static/sidebar.js:66 +msgid "Expand sidebar" +msgstr "" + +#: sphinx/themes/default/static/sidebar.js:79 +#: sphinx/themes/default/static/sidebar.js:107 +msgid "Collapse sidebar" +msgstr "" + +#: sphinx/themes/haiku/layout.html:26 +msgid "Contents" +msgstr "" + +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Udgave" -#: sphinx/writers/latex.py:579 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Fodnoter" -#: sphinx/writers/latex.py:647 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "fortsat fra forrige side" -#: sphinx/writers/latex.py:652 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Fortsættes på næste side" -#: sphinx/writers/text.py:166 -#, python-format -msgid "Platform: %s" -msgstr "Platform: %s" - -#: sphinx/writers/text.py:428 +#: sphinx/writers/text.py:422 msgid "[image]" msgstr "[billede]" +#~ msgid "Variable" +#~ msgstr "Variabel" + +#~ msgid "Parameter" +#~ msgstr "Parameter" + +#~ msgid "C function" +#~ msgstr "C-funktion" + +#~ msgid "here" +#~ msgstr "her" + +#~ msgid "Platform: %s" +#~ msgstr "Platform: %s" + diff --git a/sphinx/locale/de/LC_MESSAGES/sphinx.mo b/sphinx/locale/de/LC_MESSAGES/sphinx.mo index 5657b0ab860f6cfe66d2a6dbfed22fe5ddc1153c..de6b4082f86c8d036603c31f2c79a1fa5ee8b8ff 100644 GIT binary patch delta 32 ocmdn$x7BaMJ8@nMT_ZCELqjW5BW(kN$-l+lF`90ECE?8n0KBCO8vp<R delta 32 ocmdn$x7BaMJ8@o9T_aNk0|P5#6Kw;d$-l+lF`8_CCE?8n0K78`761SM diff --git a/sphinx/locale/de/LC_MESSAGES/sphinx.po b/sphinx/locale/de/LC_MESSAGES/sphinx.po index f92ba23cd..4d2aebc78 100644 --- a/sphinx/locale/de/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/de/LC_MESSAGES/sphinx.po @@ -7,22 +7,22 @@ msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-08-07 21:40+0200\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Georg Brandl <georg@python.org>\n" "Language-Team: de <LL@li.org>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d. %m. %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -35,66 +35,66 @@ msgstr "Builtins" msgid "Module level" msgstr "Modulebene" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d. %m. %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Allgemeiner Index" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "Index" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "weiter" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "zurück" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (in " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Autor des Abschnitts: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Autor des Moduls: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 msgid "Code author: " msgstr "Autor des Quellcode: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Siehe auch" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "%s-%s" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parameter" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Rückgabe" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Rückgabetyp" @@ -123,12 +123,12 @@ msgstr "%s (C-Typ)" msgid "%s (C variable)" msgstr "%s (C-Variable)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "Funktion" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "Member" @@ -136,7 +136,7 @@ msgstr "Member" msgid "macro" msgstr "Makro" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "Typ" @@ -144,188 +144,197 @@ msgstr "Typ" msgid "variable" msgstr "Variable" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (C++-Klasse)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++-Typ)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++-Member)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++-Funktion)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "Klasse" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (Standard-Funktion)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (Methode von %s)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (C++-Klasse)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "%s (globale Variable oder Konstante)" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (Attribut von %s)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 msgid "Arguments" msgstr "Parameter" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "Wirft" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "Daten" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 -#, python-format +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "Attribut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 msgid "Variables" msgstr "Variablen" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Verursacht" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (in Modul %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (Standard-Variable)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (in Modul %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (Standard-Klasse)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (Klasse in %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (Methode von %s.%s)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (statische Methode von %s.%s)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (statische Methode von %s)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (Klassenmethode von %s.%s)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, python-format msgid "%s() (%s class method)" msgstr "%s() (Klassenmethode von %s)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (Attribut von %s.%s)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Plattformen: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (Modul)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 msgid "Python Module Index" msgstr "Python-Modulindex" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "Module" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Veraltet" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "Exception" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "Methode" -#: sphinx/domains/python.py:502 -#, python-format +#: sphinx/domains/python.py:528 msgid "class method" msgstr "Klassenmethode" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statische Methode" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "Module" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Veraltet" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "%s (Direktive)" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, python-format msgid "%s (role)" msgstr "%s (Rolle)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "Direktive" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 msgid "role" msgstr "Rolle" @@ -352,7 +361,6 @@ msgid "reference label" msgstr "Referenz-Label" #: sphinx/domains/std.py:331 -#, python-format msgid "environment variable" msgstr "Umgebungsvariable" @@ -365,7 +373,7 @@ msgstr "Programmoption" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Stichwortverzeichnis" @@ -377,12 +385,12 @@ msgstr "Modulindex" msgid "Search Page" msgstr "Suche" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Basisklassen: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "Alias von :class:`%s`" @@ -400,103 +408,103 @@ msgstr "(Der <<ursprüngliche Eintrag>> steht in %s, Zeile %d.)" msgid "original entry" msgstr "ursprüngliche Eintrag" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "[Quelle]" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "[Doku]" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 msgid "Module code" msgstr "Modul-Quellcode" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "<h1>Quellcode für %s</h1>" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "Überblick: Modul-Quellcode" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "<h1>Alle Module, für die Quellcode verfügbar ist</h1>" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Achtung" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Vorsicht" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Gefahr" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Fehler" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Hinweis" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Wichtig" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Bemerkung" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Siehe auch" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Tipp" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Warnung" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Neu in Version %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Geändert in Version %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Veraltet ab Version %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "Schlüsselwort" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "Operator" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "Objekt" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "Anweisung" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "Standard-Funktion" @@ -506,7 +514,7 @@ msgid "Table Of Contents" msgstr "Inhalt" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Suche" @@ -638,13 +646,13 @@ msgstr "Nächstes Thema" msgid "next chapter" msgstr "nächstes Kapitel" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "Bitte aktivieren Sie JavaScript, wenn Sie die Suchfunktion nutzen wollen." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -657,16 +665,16 @@ msgstr "" "all diesen Worten suchen wird. Seiten, die nicht alle Worte enthalten, " "werden nicht in der Ergebnisliste erscheinen." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "suchen" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Suchergebnisse" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Deine Suche ergab leider keine Treffer." @@ -706,12 +714,12 @@ msgstr "C API-Änderungen" msgid "Other changes" msgstr "Andere Änderungen" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Permalink zu dieser Überschrift" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Permalink zu dieser Definition" @@ -719,19 +727,19 @@ msgstr "Permalink zu dieser Definition" msgid "Hide Search Matches" msgstr "Suchergebnisse ausblenden" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Suche..." -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Suche wird vorbereitet..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", in " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -739,7 +747,7 @@ msgstr "" "Es wurden keine zutreffenden Dokumente gefunden. Haben Sie alle " "Suchbegriffe richtig geschrieben und genügend Kategorien ausgewählt?" -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Suche beendet, %s zutreffende Seite(n) gefunden." @@ -749,7 +757,7 @@ msgid "Expand sidebar" msgstr "Sidebar ausklappen" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "Sidebar einklappen" @@ -757,19 +765,19 @@ msgstr "Sidebar einklappen" msgid "Contents" msgstr "Inhalt" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Release" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Fußnoten" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "Fortsetzung der vorherigen Seite" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Fortsetzung auf der nächsten Seite" diff --git a/sphinx/locale/es/LC_MESSAGES/sphinx.mo b/sphinx/locale/es/LC_MESSAGES/sphinx.mo index c1ee0bfe154c5c16db1089026a1c15f0ee911a4f..6115878f86f5fc8fc29cf263cb4f2102082d95b9 100644 GIT binary patch delta 32 ocmdmKztet0i!iT+u92C7p`n$jk+y-s<cY$+8BI6$is*0y0I1>#vj6}9 delta 32 ocmdmKztet0i!iULu92yNfq|8=iMD~!<cY$+8BI3#is*0y0H|;Yt^fc4 diff --git a/sphinx/locale/es/LC_MESSAGES/sphinx.po b/sphinx/locale/es/LC_MESSAGES/sphinx.po index a380a416f..b9698c9bc 100644 --- a/sphinx/locale/es/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/es/LC_MESSAGES/sphinx.po @@ -8,22 +8,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: guillem@torroja.dmt.upm.es\n" "POT-Creation-Date: 2008-09-11 23:58+0200\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Guillem Borrell <guillem@torroja.dmt.upm.es>\n" "Language-Team: es <LL@li.org>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, fuzzy, python-format msgid "%B %d, %Y" msgstr "%d de %B de %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -38,67 +38,67 @@ msgstr "Funciones de base" msgid "Module level" msgstr "Módulos" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d %b, %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Índice General" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "índice" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "siguiente" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "anterior" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Autor de la sección: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Autor del módulo: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Autor del módulo: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autor:" -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Ver también" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parámetros" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Devuelve" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 #, fuzzy msgid "Return type" msgstr "Tipo del argumento devuelto" @@ -128,12 +128,12 @@ msgstr "%s (tipo C)" msgid "%s (C variable)" msgstr "%s (variable C)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "función" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "miembro" @@ -141,7 +141,7 @@ msgstr "miembro" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "tipo" @@ -150,190 +150,201 @@ msgstr "tipo" msgid "variable" msgstr "Variable" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (clase C++)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (tipo C++)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, fuzzy, python-format msgid "%s (C++ member)" msgstr "%s (miembro C++)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, fuzzy, python-format msgid "%s (C++ function)" msgstr "%s (función C++)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "clase" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, fuzzy, python-format msgid "%s() (built-in function)" msgstr "%s() (función de base)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s método)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (clase C++)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s atributo)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Parámetros" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "atributo" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Variable" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Muestra" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (en el módulo %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, fuzzy, python-format msgid "%s (built-in variable)" msgstr "%s (variable de base)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (en el módulo %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, fuzzy, python-format msgid "%s (built-in class)" msgstr "%s (variable de base)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (clase en %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s método)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s método estático)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s método estático)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s método)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s método)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s atributo)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Plataformas:" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (módulo)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Índice de Módulos" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "módulos" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Obsoleto" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "excepción" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s método)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "método estático" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "módulo" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Obsoleto" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (módulo)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "módulo" @@ -373,7 +384,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Índice" @@ -385,12 +396,12 @@ msgstr "Índice de Módulos" msgid "Search Page" msgstr "Página de Búsqueda" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "" @@ -408,104 +419,104 @@ msgstr "" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "módulo" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Atención" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Prudencia" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Peligro" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Error" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Consejo" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Importante" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Nota" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Ver También" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Truco" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Advertencia" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Nuevo en la versión %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Distinto en la versión %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Obsoleto desde la versión %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "palabra clave" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operador" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objeto" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "sentencia" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 #, fuzzy msgid "built-in function" msgstr "función de base" @@ -516,7 +527,7 @@ msgid "Table Of Contents" msgstr "Contenidos" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Búsqueda" @@ -647,13 +658,13 @@ msgstr "Próximo tema" msgid "next chapter" msgstr "Próximo capítulo" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "" -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -666,16 +677,16 @@ msgstr "" " las palabras. Las páginas que contengan menos palabras no aparecerán en" " la lista de resultados." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "buscar" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Resultados de la búsqueda" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Tu consulta no obtuvo ningún resultado" @@ -715,12 +726,12 @@ msgstr "Cambios en la API C" msgid "Other changes" msgstr "Otros cambios" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Enlazar permanentemente con este título" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Enlazar permanentemente con esta definición" @@ -729,19 +740,19 @@ msgstr "Enlazar permanentemente con esta definición" msgid "Hide Search Matches" msgstr "Coincidencias de la búsqueda" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Buscando" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Preparando la búsqueda" -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr "" -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -750,7 +761,7 @@ msgstr "" "todas las palabras correctamente y que ha seleccionado suficientes " "categorías" -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" @@ -762,7 +773,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -770,20 +781,20 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 #, fuzzy msgid "Release" msgstr "Versión" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 #, fuzzy msgid "Continued on next page" msgstr "Índice completo en una página" @@ -791,3 +802,4 @@ msgstr "Índice completo en una página" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[imagen]" + diff --git a/sphinx/locale/fi/LC_MESSAGES/sphinx.mo b/sphinx/locale/fi/LC_MESSAGES/sphinx.mo index 7c30000606e2b0880ff55a78aff8778ffb9df6da..0195b3dd010ad1cce2cebcfdc0492f7d09a06244 100644 GIT binary patch delta 2688 zcmYM!duWzb9KiAOzD~_KTbt8uZn`<M#7l0@&0$tUg0)3nBJ+<#7;_qw*O5peud>Vw z<WN)4<t#yk(MsA3OQHr|x+qj+DT)ONmKvlqybPu9*BxlQdq2-}o^$#Ae&;;z)|#U= zsY}^uI|G0F`7h*uVt?=c|C^B>LI$@_p^xgY1n1x&Y>KXp&o^Qg?eAg^?!!#%LgzV- zL-0o&96~Dm7CT(Shk4M8PLR%12Nt3OCgM<>j>B;lK7x(txYbyU8_@9wkxw|vm;H~U z{V!lHUdMda4{3zu#G`NoPQg5U0r`Z*eEGZy^Km0q;)hs-=Wr_CMk_RlXiKpi3vdw* z!*yuIc11tHhgd(fbMZ8G+JHBaPw3@qAPyouC&)q<dK}GU0UGd9blfI%fo(V&TX6th zLy8)1q5<8*X_!xWQ${?O3);ebbb`eg5+STWOSh8M3}_QtiCvh)_ITfcY20_C<4>aV z{(uJlOY}NAKFR)!#^G6EM{_bO<#O|Aqky3i4R#gs32Wnh3%bT0G?sm6(jDj;pP}PU zqv!Znv^f7FI~_8!LzsyLXbcO{oL6OY2F7r)o(2OrfL85OwECZ;6LiP>Gx7OZ^nhQC z&u^d;-oqJ~!75g}7A;C6l7Co^2EH!3c_?SV1-8>bu%Qhda2O5b3+#v8Xa!Eh=ii|L z{)}7jB0h#oh{|zW(dRqS1@|JKaFDN&cnn?Ve2NPf{sRr92i?8|b+sY`(URt%0hFNw zE0M0D8hv5r#r9?B7OjcCgU<IJ8qgl}CuDzYPo3bxh`*2i6g`g)_#IvNPc)#v<MX>{ zW(f|drB6ri^U+e5#P)J@D`ui+Z6UheGUSa;g*Uh`qZTys-ROdc&<Q`n4ZILX&;Xt! zT{C(P&14qZZ)Nn&_`Dgd>}Ir5t!My;(RsecA^rxP;lk2ih#jt=8QnrB7(kc~%tHel zhirGKL}ChcXvX#U;8bHV_v_K|`_Tov(Ei<M|8tn@Z_r;{@Cgat87D44U!Tc*5ksg$ zCul&QuR#01jg{DjMR*pcVh>uO@zlK(OVKT;=gY&l7Oi+IrY!Y=*wBvE+;^cR?LjlX zAMZ0biEMiqhE6;I&13<xW>|`j+k`H#4QFF38ql@aegh4pw~+dqK|XhOn1hz^Wpsi@ zd;*svV?rC6`7!i&<1BiZ?x3X(qx$B_Mgz%1+ef2;mm^(6En3MXqo}{XhHuf}9`8jL z=s@@C6nZu;p}%lf(FuDni36#-0i>gq%0l}Up$m?W_tj_u&!d%k6`i*^#YGhtEl8Ea zH|WHd(G0JknG7!OTPO#8Eep^I#>D%{=vGccPx}nCf^*TtUcs^W8oJIlG|*HB7nb5E zI^pT)IW&XIXvTNZ=NXjCjPuYfD?-n}SaiWkG~jBi##%IyE$Fz9(FBel{ZipeE{ymj zx;GcliSD2a_bcf;{lm}&OXB@Fw4@bi<}ae-=A-k~qi@gZ*xrI}(eCJ>2kj{$;lhZ% zLO$UXUoLbNjrgDFEwtZ#G{XU;mB36)Viwvz2Yp_E9_B~Ue&uMtir8L-1<%k>!-ad) zfM(K!9>O(fM%&TI-;dAR(FH!i9V7FqIv1tiPV{ehy}qfb^QDY$(}z?|no?O@RyMt2 a%7oI=9piIvBq}-!^GcJk&$A=y2K@*A)%9-x delta 2676 zcmYM!duWzb9Ki9jrE`0CNtaF6)J;u`++ym~IWM_x(}>J;(S<_rR%yi6#H8lC(m^R+ zVrT^_RQtn_%$Si)OGjYPsu|2~URa`7TPX|0yfFHH@j&C<`#H~f&N;u|`JLx^_g8JI zN}kH?w>9whKL5k`zrDX#|Nj*yLdfE>1ie*(qi_~x;G*b@@%}0tM1MUF#?5#$Hlyq8 z!)$EB%n*{HBMvx@Io#+$7q}Yxvl!)sVR#GPg?Ts=2VylkZz&exN_74<<P&!B<@kN* z_;wtECorG*;UWze&LO?qa1;*38EF6Gn2#^uWL$@ZcmT`rI9i#3WLb)NSb$Y{D=tDS zwK}>M)4AS=Gl?Iz#g1dhC!FGI0RDw8a1jlZV4LpX9yHNM(S??y0qSrruE8|?0V!fQ zh9=a7Q}8;Pa2e;4q#Y_~xWF6_DkX&ZXvr23&4iYtl~|3bxG`RD!G2sfqw_yP7j8il z|03Ff&OeJI@d_HMU=a1EdSM&~W=A=CJsZuu8u^5U@wyfbSdS+523mnFXu$W-d7q#U zb1Pb@pOI4z|KPpYhbB~+OZ}}tb#4fwaWR^}n`jBQqDQeEU7$H$?~V8Oqfhyvc)t@} zuM4N+1+?_VM6*J(k<Ew4(8L!dV`BvxU?q}VcpaV4geI~BZ^CA@0=we<kI@7_$Mtv! z$Kpd|<-8j7{wrv}2ILdo;%hi2chWG>K{W6YG?8D>vpj=Vq#G^iWi)}o)Z2OaNLLt* zzBT2se;#^7Pe-3e*IR`qRFC|QBtv7ou?x+3PxQ0sL3F~`XyA4<p`-EsNpxpt(9-`M zudkz}?$48Ke;#@iW6)=<64QMD=h5)}egfT5Et>gjXu$PofX&#*+wm@%zzDYMj>e(C zh~v?53!=}&`%BTvu0Sib22G#|vwZ(Qq+!6lXz9O-1HMHw{|#N>&)9zr9iK_o9Cye^ zatbBrj_*h3Rbvq@M&~!8iNAx6Z^on(4$$x?@jK)b&hX{Jedz0x%NH?23EKZ4I_@c) zjCELuyRi)0(aKowQb<EfKZP$Jy7_3OYYM2pC4Mz_G~g_**Q2FuM|av8uTLY#8hX%$ zQ+OiXzyu^_xDTE8EE-@L&czxup|;rn4VuW0!>GSIxDW@7rPtCGqYF&KyYT^JNT@@1 z{x<r1u^WA8JJ1sUga-NpP2_y+zl<iHG2%wxLbQ@Ik~I8DoQFOXFQWn0qGz-NeJ1vy zzifxmK<$`{C(#7D&`O;{$6Z1LdM;k)p&J;9R%#}?Zt_VQ<usNcRSi4Qg<H`beu?hn zcQjBp`a1Ta3tWlU{flnAE!pT(o{LuS4s>JVaST?Xah4&8CBs@8mSPjS;0Mt?=nh)Z z9Un#SpGFfokCyrp`V3q{17=Z96VAg~Scq<9Av$jzx`7Rt?)%?F!;H5h#}qzA7wSL* z|BAjgJ!rsx<8>ce(#%mec77*1uLNCh3i{SO9Q&7`NAyy(-hSeT4K&PXEAk0D_%hI8 zG~*-D@6mCc=nhX{3ZBJOJco|&M(_8c5A#)YTn1%G#Z0t62WNN|!)bU%Q_<IP4*C#2 zg6`-!H1pbczX1)<*fKRSF|}oNR$C%_Qpto#MWv+`6U)YxPH3tc(wkbAXquiVZ>b%6 PIfWquQd;T@rltP}BKr8~ diff --git a/sphinx/locale/fi/LC_MESSAGES/sphinx.po b/sphinx/locale/fi/LC_MESSAGES/sphinx.po index 04839bba6..8b649a266 100644 --- a/sphinx/locale/fi/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/fi/LC_MESSAGES/sphinx.po @@ -8,22 +8,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.6\n" "Report-Msgid-Bugs-To: sphinx@awot.fi\n" "POT-Creation-Date: 2009-01-24 18:39+0000\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Jukka Inkeri <sphinx@awot.fi>\n" "Language-Team: fi <sphinx@awot.fi>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d.%m.%Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "" @@ -36,67 +36,67 @@ msgstr "" msgid "Module level" msgstr "Moduulitaso" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d.%m.%Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Yleinen sisällysluettelo" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "hakemisto" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr ">" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "<" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Luvun kirjoittaja: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Moduulin kirjoittaja: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Moduulin kirjoittaja: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Tekijä: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Katso myös" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "" @@ -125,13 +125,13 @@ msgstr "" msgid "%s (C variable)" msgstr "" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 #, fuzzy msgid "function" msgstr "Varoitus" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "" @@ -139,7 +139,7 @@ msgstr "" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "" @@ -147,187 +147,198 @@ msgstr "" msgid "variable" msgstr "" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, python-format +msgid "%s() (class)" +msgstr "" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 msgid "Arguments" msgstr "" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 msgid "Variables" msgstr "" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, python-format msgid "%s() (%s.%s class method)" msgstr "" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, python-format msgid "%s() (%s class method)" msgstr "" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Ympäristö" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (moduuli)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Moduuli sisällysluettelo" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "moduulit" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Poistettu" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 +#: sphinx/domains/python.py:528 msgid "class method" msgstr "" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "moduuli" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Poistettu" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (moduuli)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "moduuli" @@ -367,7 +378,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Sisällysluettelo" @@ -379,12 +390,12 @@ msgstr "Moduuli sisällysluettelo" msgid "Search Page" msgstr "Etsi sivu" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "" @@ -402,104 +413,104 @@ msgstr "" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "moduuli" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Huom" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Varoitus" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Vaara" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Virhe" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Vihje" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Tärkeä" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Muista" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Katso myös" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Vihje" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Varoitus" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Uusi versiossa %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Muutettu versiossa %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Poistettu versiosta %s alkaen" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "" @@ -509,7 +520,7 @@ msgid "Table Of Contents" msgstr "Sisällysluettelo" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Etsi" @@ -637,13 +648,13 @@ msgstr ">>" msgid "next chapter" msgstr ">>" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "Javascript pitää olla sallittu, jotta etsintä toimii." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -651,16 +662,16 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "Anna hakusanat kokonaan, osasanoilla ei haeta." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "etsi" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Etsinnän tulos" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Ei löytynyt ko. ehdoilla yhtään." @@ -700,12 +711,12 @@ msgstr "" msgid "Other changes" msgstr "" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "" @@ -713,25 +724,25 @@ msgstr "" msgid "Hide Search Matches" msgstr "Piilota löydetyt" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Etsitään" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Valmistellaan etsintää..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr "" -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "Ei löytynyt yhtään. Tarkista hakuehdot, sanahaku, ei sen osia" -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Etsintä tehty, löydetty %s sivu(a)." @@ -741,7 +752,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -749,19 +760,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 #, fuzzy msgid "Continued on next page" msgstr "" diff --git a/sphinx/locale/fr/LC_MESSAGES/sphinx.mo b/sphinx/locale/fr/LC_MESSAGES/sphinx.mo index bfa97b1938dfccc3d2cc3576ab82ad7593383913..e17b5c785c5db207c6646e852ed05965f51d03dc 100644 GIT binary patch delta 33 ocmaFm`pR{KiYTvzu92C7p`n$jk+y-sW<yaaMn==kK4SBD0I9eL*#H0l delta 33 ocmaFm`pR{KiYTv{u7QbyfrXWcfwqCsW<yaaMn;p(K4SBD0I7fo*8l(j diff --git a/sphinx/locale/fr/LC_MESSAGES/sphinx.po b/sphinx/locale/fr/LC_MESSAGES/sphinx.po index 8afcf435a..24da5c39a 100644 --- a/sphinx/locale/fr/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/fr/LC_MESSAGES/sphinx.po @@ -11,22 +11,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: larlet@gmail.com\n" "POT-Creation-Date: 2008-08-08 12:39+0000\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Sébastien Douche <sdouche@gmail.com>\n" "Language-Team: French Translation Team <sphinx-dev@googlegroups.com>\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d %B %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -39,67 +39,67 @@ msgstr "Fonctions de base" msgid "Module level" msgstr "Module" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d %b %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Index général" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "index" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "suivant" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "précédent" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "(dans" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Auteur de la section : " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Auteur du module : " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Auteur du module : " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Auteur : " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Voir aussi" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Paramètres" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Retourne" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Type retourné" @@ -128,12 +128,12 @@ msgstr "%s (type C)" msgid "%s (C variable)" msgstr "%s (variable C)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "fonction" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "membre" @@ -141,7 +141,7 @@ msgstr "membre" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "type" @@ -150,190 +150,201 @@ msgstr "type" msgid "variable" msgstr "Variable" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (classe C++)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (type C++)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (membre C++)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (fonction C++)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "classe" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (fonction de base)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (méthode %s)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (classe C++)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "%s (variable globale ou constante)" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (attribut %s)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Paramètres" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "Lance" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "données" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "attribut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Variable" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Lève" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (dans le module %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (variable de base)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (dans le module %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (classe de base)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (classe dans %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (méthode %s.%s)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (méthode statique %s.%s)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (méthode statique %s)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (méthode %s.%s)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (méthode %s)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (attribut %s.%s)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Plateformes : " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (module)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Index du module" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "modules" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Obsolète" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "exception" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "méthode" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (méthode %s)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "méthode statique" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "module" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Obsolète" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (module)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "module" @@ -373,7 +384,7 @@ msgstr "option du programme" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Index" @@ -385,12 +396,12 @@ msgstr "Index du module" msgid "Search Page" msgstr "Page de recherche" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "alias de :class:`%s`" @@ -408,104 +419,104 @@ msgstr "(L'<<entrée orginale>> se trouve dans %s, à la ligne %d.)" msgid "original entry" msgstr "entrée orginale" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "module" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "<h1>Code source de %s</h1>" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "Vue d'ensemble : code du module" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "<h1>Modules pour lesquels le code est disponible</h1>" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Attention" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Prudence" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Danger" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Erreur" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Indice" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Important" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Note" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Voir aussi" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Astuce" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Warning" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Introduit dans la version %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Modifié dans la version %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Obsolète depuis la version %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "mot-clé" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "opérateur" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objet" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "état" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "fonction de base" @@ -515,7 +526,7 @@ msgid "Table Of Contents" msgstr "Table des matières" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Recherche" @@ -646,13 +657,13 @@ msgstr "Sujet suivant" msgid "next chapter" msgstr "Chapitre suivant" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "Activez le JavaScript pour que la recherche fonctionne\n" -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 #, fuzzy msgid "" "From here you can search these documents. Enter your search\n" @@ -668,16 +679,16 @@ msgstr "" " contenant moins de mots n'apparaîtront pas dans la liste des " "résultats." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "rechercher" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Résultats de la recherche" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Votre recherche n'a retourné aucun résultat" @@ -717,12 +728,12 @@ msgstr "Modifications de l'API C" msgid "Other changes" msgstr "Autres modifications" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Lien permanent vers ce titre" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Lien permanent vers cette définition" @@ -730,19 +741,19 @@ msgstr "Lien permanent vers cette définition" msgid "Hide Search Matches" msgstr "Cacher les résultats de la recherche" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "En cours de recherche" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Préparation de la recherche..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", dans" -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -751,7 +762,7 @@ msgstr "" "des termes de recherche et que vous avez sélectionné suffisamment de " "catégories." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "La recherche est terminée, %s page(s) correspond(ent) à la requête." @@ -761,7 +772,7 @@ msgid "Expand sidebar" msgstr "Agrandir le menu" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "Réduire le menu" @@ -769,19 +780,19 @@ msgstr "Réduire le menu" msgid "Contents" msgstr "Contenu" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Version" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Notes de bas de page" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "Suite de la page précédente" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 #, fuzzy msgid "Continued on next page" msgstr "Suite sur la page suivante" @@ -790,15 +801,3 @@ msgstr "Suite sur la page suivante" msgid "[image]" msgstr "[image]" -#~ msgid "Parameter" -#~ msgstr "Paramètres" - -#~ msgid "here" -#~ msgstr "ici" - -#~ msgid "module, in " -#~ msgstr "module, dans" - -#~ msgid "Platform: %s" -#~ msgstr "Plateforme : %s" - diff --git a/sphinx/locale/hr/LC_MESSAGES/sphinx.mo b/sphinx/locale/hr/LC_MESSAGES/sphinx.mo index e032adeb105512f6ff388c52f9ea55db538e2442..c6b1b384d3fbd27ccea1eb29af6d9ba2f466e7ad 100644 GIT binary patch delta 33 ocmaFu{@Q(mvly?1u92C7p`n$jk+y-s<^VAkMn==kq2kkc0Is763jhEB delta 33 ocmaFu{@Q(mvly?bu92yNfq|8=iMD~!<^VAkMn;p(q2kkc0In?w1^@s6 diff --git a/sphinx/locale/hr/LC_MESSAGES/sphinx.po b/sphinx/locale/hr/LC_MESSAGES/sphinx.po index ddcf2299a..1b9f9c804 100644 --- a/sphinx/locale/hr/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/hr/LC_MESSAGES/sphinx.po @@ -4,22 +4,22 @@ msgstr "" "Project-Id-Version: Sphinx\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2010-09-11 23:58+0200\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Bojan Mihelač <bmihelac@mihelac.org>\n" "Language-Team: Bojan Mihelač <bmihelac@mihelac.org>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d %B, %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -32,67 +32,67 @@ msgstr "Ugrađeni dijelovi" msgid "Module level" msgstr "Nivo modula" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d %b, %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Opceniti abecedni indeks" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "abecedni indeks" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "naprijed" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "nazad" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (u " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Autor sekcije:" -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Autor modula:" -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Autor modula:" -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autor:" -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Pogledaj i" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parametri" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Vraća" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Vraća tip" @@ -121,12 +121,12 @@ msgstr "%s (C tip)" msgid "%s (C variable)" msgstr "%s (C varijabla)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "funkcija" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "član" @@ -134,7 +134,7 @@ msgstr "član" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "tip" @@ -143,191 +143,202 @@ msgstr "tip" msgid "variable" msgstr "Varijabla" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (C++ razred)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ tip)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ član)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ funkcija)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 #, fuzzy msgid "class" msgstr "razred" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (ugrađene funkcije)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s metoda)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (C++ razred)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s atribut)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Parametri" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "atribut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Varijabla" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Podiže" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (u modulu %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (ugrađene variable)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (u modulu %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (ugrađen razred)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (razred u %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s metoda)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s statična metoda)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s statična metoda)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s metoda)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s metoda)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s atribut)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Platforme:" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (modul)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Popis modula" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "Moduli" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Zastarjelo" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "izuzetak" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s metoda)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statična metoda" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "modul" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Zastarjelo" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (modul)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "modul" @@ -367,7 +378,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Abecedni popis" @@ -379,12 +390,12 @@ msgstr "Popis modula" msgid "Search Page" msgstr "Tražilica" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Osnove: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "nadimak za :class:`%s`" @@ -402,104 +413,104 @@ msgstr "(Originalan unos se nalazi u %s, u retku %d, i može biti pronađen " msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "modul" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Pozor" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Pažnja" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Opasnost" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Greška" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Savjet" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Važno" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Napomena" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Pogledaj i" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Savjet" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Upozorenje" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Novo u verziji %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Promijenjeno u verziji %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Zastarijelo od verzije %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "ključna riječ" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operator" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objekt" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "izjava" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "ugrađen funkcije" @@ -509,7 +520,7 @@ msgid "Table Of Contents" msgstr "Pregled sadržaja" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Traži" @@ -639,7 +650,7 @@ msgstr "Slijedeća tema" msgid "next chapter" msgstr "slijedeće poglavje" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." @@ -647,7 +658,7 @@ msgstr "" "Molimo omogućite JavaScript\n" " za djelovanje tražilice." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -659,16 +670,16 @@ msgstr "" " function will automatically search for all of the words. Pages\n" " containing fewer words won't appear in the result list." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "traži" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Rezultati pretrage" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Your search did not match any results." @@ -708,12 +719,12 @@ msgstr "C API changes" msgid "Other changes" msgstr "Ostale promjene" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Link na taj naslov" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Link na tu definiciju" @@ -721,19 +732,19 @@ msgstr "Link na tu definiciju" msgid "Hide Search Matches" msgstr "Sakrij rezultate pretrage" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Tražim" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Pripremam pretraživanje..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", u " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -741,7 +752,7 @@ msgstr "" "Za vašu pretragu nema rezultata. Molimo pregledajte da li so sve riječi " "ispravno napisane i da li ste izbrali dovoljno kategorija." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" @@ -753,7 +764,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -761,19 +772,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Distribucija" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "nastavak sa prethodne stranice" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "nastavak na slijedećoj stranici" @@ -781,15 +792,3 @@ msgstr "nastavak na slijedećoj stranici" msgid "[image]" msgstr "[slika]" -#~ msgid "Parameter" -#~ msgstr "Parametar" - -#~ msgid "here" -#~ msgstr "ovdje" - -#~ msgid "module, in " -#~ msgstr "modul, u " - -#~ msgid "Platform: %s" -#~ msgstr "Platforma: %s" - diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.mo b/sphinx/locale/it/LC_MESSAGES/sphinx.mo index 10ca29a50405fad5c844089ad062f60542d8be6d..c07af5723a9f4bf718ca04844147fc6ee5749ecd 100644 GIT binary patch delta 37 tcmezB`PFknl^Cyuu92C7p`n$jk+y-s<aV+DOr~6u?{HddJ|J$&3jp&T3^4!z delta 37 tcmezB`PFknl^Cz7u92yNfq|8=iMD~!<aV+DOeS2D?{HddJ|J$&3jp$y3@iWu diff --git a/sphinx/locale/it/LC_MESSAGES/sphinx.po b/sphinx/locale/it/LC_MESSAGES/sphinx.po index 40a2ca2a3..590c5c86d 100644 --- a/sphinx/locale/it/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/it/LC_MESSAGES/sphinx.po @@ -7,22 +7,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-11-27 18:39+0100\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Sandro Dentella <sandro@e-den.it>\n" "Language-Team: <sphinx-dev@googlegroups.com>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d %B %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -35,67 +35,67 @@ msgstr "Builtin" msgid "Module level" msgstr "Modulo" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d/%b/%Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Indice generale" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "indice" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "successivo" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "precedente" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (in " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Autore della sezione: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Autore del modulo: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Autore del modulo: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autore: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Vedi anche" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parametri" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Ritorna" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Tipo di ritorno" @@ -124,12 +124,12 @@ msgstr "%s (tipo C)" msgid "%s (C variable)" msgstr "%s (variabile C)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "funzione" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "membro" @@ -137,7 +137,7 @@ msgstr "membro" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "tipo" @@ -146,190 +146,201 @@ msgstr "tipo" msgid "variable" msgstr "Variabile" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (classe C++)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (tipo C++)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (membro C++)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (funzione C++)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (funzione built-in)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s metodo)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (classe C++)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s attributo)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Parametri" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "attributo" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Variabile" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Solleva" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (nel modulo %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (variabile built-in)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (nel modulo %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (classe built-in)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (classe in %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s metodo)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s metodo statico)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s metodo statico)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s metodo)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s metodo)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s attributo)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Piattaforme:" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (modulo)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Indice dei Moduli" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "moduli" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Deprecato" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "eccezione" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s metodo)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "metodo statico" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "modulo" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Deprecato" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (modulo)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "modulo" @@ -369,7 +380,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Indice" @@ -381,12 +392,12 @@ msgstr "Indice dei Moduli" msgid "Search Page" msgstr "Cerca" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "alias per :class:`%s`" @@ -404,104 +415,104 @@ msgstr "(La <<riga originale>> si trova in %s, linea %d.)" msgid "original entry" msgstr "riga originale" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "modulo" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Attenzione" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Attenzione" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Pericolo" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Errore" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Consiglio" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Importante" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Nota" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Vedi anche" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Suggerimento" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Avvertimento" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Nuovo nella versione %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Cambiato nella versione %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Deprecato dalla versione %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "keyword" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operatore" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "oggetto" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "statement" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "funzione built-in" @@ -511,7 +522,7 @@ msgid "Table Of Contents" msgstr "Tabella dei contenuti" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Cerca" @@ -641,13 +652,13 @@ msgstr "Argomento successivo" msgid "next chapter" msgstr "capitolo successivo" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "" -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -661,16 +672,16 @@ msgstr "" " di ricerca cerca automaticamente per tutte le parole. Le pagine\n" " che contendono meno parole non compariranno nei risultati di ricerca." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "cerca" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Risultati della ricerca" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "La tua ricerca non ha ottenuto risultati" @@ -710,12 +721,12 @@ msgstr "Modifiche nelle API C" msgid "Other changes" msgstr "Altre modifiche" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "link permanente per questa intestazione" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "link permanente per questa definizione" @@ -723,19 +734,19 @@ msgstr "link permanente per questa definizione" msgid "Hide Search Matches" msgstr "Nascondi i risultati della ricerca" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Ricerca in corso" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Preparazione della ricerca" -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", in " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -744,7 +755,7 @@ msgstr "" "dei termini di ricerca e di avere selezionato un numero sufficiente di " "categorie" -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Ricerca terminata, trovate %s pagine corrispondenti alla ricerca." @@ -754,7 +765,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -762,19 +773,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Release" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 #, fuzzy msgid "Continued on next page" msgstr "Indice completo in una pagina" @@ -782,3 +793,4 @@ msgstr "Indice completo in una pagina" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[immagine]" + diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.mo b/sphinx/locale/ja/LC_MESSAGES/sphinx.mo index b0284f9183f135283cd9f6f7095b08500a600d44..5a8ada49e2dd86560266fd49f5cf836ea68cc232 100644 GIT binary patch delta 32 ncmccTd(U@6lNhgsu92C7p`n$jk+y-s<bJU?jHa7w#FO{|wEPM< delta 32 ncmccTd(U@6lNhh5u92yNfq|8=iMD~!<bJU?j3%3F#FO{|w2BHd diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.po b/sphinx/locale/ja/LC_MESSAGES/sphinx.po index 5f65a6c3e..dd5a892e8 100644 --- a/sphinx/locale/ja/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.po @@ -8,22 +8,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-09-11 23:58+0200\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Yasushi MASUDA <whosaysni@gmail.com>\n" "Language-Team: ja <LL@li.org>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%Y 年 %m 月 %d 日" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -36,67 +36,67 @@ msgstr "組み込み" msgid "Module level" msgstr "モジュールレベル" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%Y 年 %m 月 %d 日" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "総合索引" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "索引" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "次へ" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "前へ" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "この節の作者: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "モジュールの作者: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "モジュールの作者: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "作者: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "参考" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "パラメタ" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "戻り値" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "戻り値の型" @@ -125,12 +125,12 @@ msgstr "%s (C のデータ型)" msgid "%s (C variable)" msgstr "%s (C の変数)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "の関数" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "のメンバ変数" @@ -138,7 +138,7 @@ msgstr "のメンバ変数" msgid "macro" msgstr "のマクロ" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "のデータ型" @@ -147,190 +147,201 @@ msgstr "のデータ型" msgid "variable" msgstr "変数" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, fuzzy, python-format msgid "%s (C++ class)" msgstr "%s (のクラス)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ のデータ型)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ のメンバ変数)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ の関数)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (組み込み関数)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s のメソッド)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (のクラス)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s の属性)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "パラメタ" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "の属性" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "変数" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "例外" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (%s モジュール)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (組み込み変数)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (%s モジュール)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (組み込み変数)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (%s のクラス)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s のメソッド)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s の静的メソッド)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s の静的メソッド)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s のメソッド)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s のメソッド)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s の属性)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "プラットフォーム: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (モジュール)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "モジュール索引" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "モジュール" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "撤廃" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "例外" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s のメソッド)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "の静的メソッド" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "モジュール" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "撤廃" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (モジュール)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "モジュール" @@ -370,7 +381,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "索引" @@ -382,12 +393,12 @@ msgstr "モジュール索引" msgid "Search Page" msgstr "検索ページ" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " ベースクラス: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr ":class:`%s` のエイリアス" @@ -405,104 +416,104 @@ msgstr "" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "モジュール" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "注意" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "ご用心" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "危険" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "エラー" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "ヒント" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "重要" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "ノート" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "参考" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "ちなみに" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "警告" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "バージョン %s で追加" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "バージョン %s で変更" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "バージョン %s で撤廃" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "キーワード" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "演算子" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "オブジェクト" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "文" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "組み込み関数" @@ -512,7 +523,7 @@ msgid "Table Of Contents" msgstr "目次" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "検索" @@ -642,13 +653,13 @@ msgstr "次のトピックへ" msgid "next chapter" msgstr "次の章へ" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "検索機能を使うには JavaScript を有効にしてください。" -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -656,16 +667,16 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "このページからドキュメントを検索できます。キーワードを下のボックスに入力して、「検索」をクリックしてください。入力された全てのキーワードを含むページが検索されます。一部のキーワードしか含まないページは検索結果に表示されないので注意してください。" -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "検索" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "検索結果" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "検索条件に一致する項目がありませんでした。" @@ -705,12 +716,12 @@ msgstr "C API に関する変更" msgid "Other changes" msgstr "その多の変更" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "このヘッドラインへのパーマリンク" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "この定義へのパーマリンク" @@ -718,25 +729,25 @@ msgstr "この定義へのパーマリンク" msgid "Hide Search Matches" msgstr "検索結果を隠す" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "検索中" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "検索の準備中..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr "" -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "検索条件に一致するドキュメントはありませんでした。検索したい言葉を正しいつづりで入力しているか確認してください。また、正しいカテゴリの検索を行っているか確認してください。" -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "検索が終了し、条件に一致するページが %s 個みつかりました。" @@ -746,7 +757,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -754,22 +765,23 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "リリース" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "注記" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "前のページからの続き" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "総索引" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[画像]" + diff --git a/sphinx/locale/lt/LC_MESSAGES/sphinx.mo b/sphinx/locale/lt/LC_MESSAGES/sphinx.mo index 794ae655fabb759c219848806ad0b3a751b7f164..1ed94be57a70124aee364edc187f34e2426bb1e9 100644 GIT binary patch delta 2803 zcmYM#3ux6<9KiA8W6sUZ=G1Mvrn7F_eATve+8mmerlM(O&|_wnnx!GWQ>ZIylCK_U zC~7_`pC}G*LP>~-X)mOdo`{rMfvHI}3nT0M-96CQ?&ti^>vzuYod4a@vHQm+&*ry$ zGw`#Uznl4+l;P9=KYO!7$l-Ax+H?qS!6TT3=cAWn`=8j3`hTzk-qeOaOrZ1h!CV}S z{3gQ~KJD;0w#8}a1Pf#RDs;dmY>#`e01smxeua+v8B4Jd9bZ5+ilG8sXdiUkKy=*0 zSV;UZg+eDPUP1$`$IiGHi|_>U8-C!!_RDDhW~{0Pi?J&{h<$JtnyH;wiTkh^&tgZs zjxH#VRc8@Dbf(Z66F3li$LHxt-!K<5aS1xXN;J^6*#0%TqYLQ58`1glNUJ;Uga)j{ zF<6VOa0Mngrtk&@SGWoL;rr+gt|D`VYv=?SMrp=zp%dqkXBSwGW~MJT#}V;)6t?8~ zNp$>Vblw^0M&`C7|4loh!U?uvH#~^M4Bw!s|0O>Efu3p0{B(UDx`7g;DOAMgS~T!b zbb%w$j68zIn~08^pHKe1^^2)6bz9H?hj0iUM;DgGhnXnBTd@~fKb8*{I0;SZO!TN; zK<8N$pI629wdh;1F}61(DLC<mScjjXN70&IrYwPG{dRN*gQCOH01u!$ACLB*fi7eL zw!lSbCYHwb<>&&};0jD`q)<kojJ!B-G}>Vt8n7Pe5+-8_E<^*ZM+3i&F62G*Y>%KB zIgV!bG`fJR=(t97zJHOoD;YX)lWgdX2JDBP+1=6m(TVHPg*}CS0X-M%SECDG6WtKq zhW6iuE}#Kj*umI-6kGfLe?q|{IMq}U!jEXmFUI=6(Id$&Okc_}bix`m@BsAtU<kUA zdUOL*(0+5!jV-`4ygiF!`veB%`2Ign!5u9`JH8d&f$n@in(BjS>Q10<#~I|hgiGko zyuq4__V0{ttXF&<5T8d!>oLh<!ZZq=)e_{whpn+g3d?yuiUz!h2DpyCKAEJ2*dZSc z*b8Y21JU^&MHf5|y*n#o`))Kt$BN0n0Z&npz@N}>z-&evcm&>wGtt1?@ishw2K*K| zop2Gogw2Rz26NE`7T{3qhK_#*J=!_wQN3J3{<(<ZH7fe!c66s_(VbjDQ<&8yeVK~T z9hagDsKtkH6guHnbOC#ip&^C$;g{%L>C`oSmrBw8bx8_6xD><CZ@!tO=@qU)1HFq5 zJdDf{K0*gxK<lrd0UObORyWXog-m8<YOp(wj?eSa47`eFKDmX0XK^5U1Wnz^*x@@g z;3f2Rx`t-xMyzkk%{6clI<5-cc^z_^;W0Gz^KdvWK{Nh&x;`1sQSdAEH}p2Q=fTvL zqXF+icQ_cGcqDphr^fn4=uVeo)8$1kT?+5TkI{wXa>1OZI~u1NJNo_)Ybx;GMrWac zUqLhTdVJoDW?~0AQ3JZ;575hZ3|He9*b|@Nrn>VLXvWr|{dOX!9a5MeemG6RiT^@Z z-n=ZmlS1^2d!Q3kp&1yAE?@#WZYsK?S!iaK#rDn7ooI#{&<vf3_1|OC+kKt_iw?h| zD=sWgfAJ*HO!SCWqk(HN1MfjIFe28ELw7hHUC^_!{zWvC%VPan<obkN<zZ0MS$|4} z6P`f>oI`K_ub7D$73o`>i}ve+22NC@He}Z|&v<(J#M!e`Cv)y-liR;{bxmniRc+tu Yo|ToU?4oyCw3<fiv305K#S?P=1M=?tUjP6A delta 2774 zcmYM!duW$c7{Kw9mrb2Ir>4`jtCwZVwmC0drVDdUu~ID4+*W^>cEMKQCDQ2|HF23m zB$nAkLM)ZiVwCAWCK`ev1p~Ec2D4Cdi)dH~UiyCbJCLZK_dU1gInO!o@6dv#1<8e> zeYOYw?Bm}Z{HyEh+y8%B281w>*Ij7W0W88Vus^m%+hhMF%%Z&uhvO~G#NipKb;jZ# z-pi4{NvPs~2h794Sc@+3LTqnDCv3%GxD!X<K^%hLq4UmS0sfB8Pa|s{LpGY|SajY! z=)7tiN&N5x52I*kKm#=6Xl%i3JRIA9M92MxQ}HI|;}o)+jEm3;ZNV~p7jy79-j3~P zBHhs&*pK)jo%Cnm5bTHZk-o45({Tm5Km!`6DfS;hcXA3%_&hql8{O%3G~h7GJ0Ei~ z4PQh~C9KB83=<xv;Ya8W+K{>7Cv<_!7;X#UZ*<{qqMG0U>TJcbu@~MQ-%GI%?+>B# z=b-B@L^rY|+K@&4U7(4ELfnDII*L>`oR073(6hW0+q=;ngrTXk&OqOD(ZF}22~I>S z@&Fom7CLSzdfAr`rT&)gbs7w?3uoefG_g*!626RqOtig%|4gt3E$QRvQPraBEQ{|i z#r{{&uVG{C--fQcJK>>{hlA)*TtQ2g&S=jz3*A9cv;++>87V?|5FNh|O{5Nc<1(}o z&&K|hXo72T3vR^mnDnPEPAo$QOhW@!A!EWE%*A>%(0VlRCNz-`(6ilxR%Aa~+ON?B z+R%CD(e*meuj=1adlH6lqYOA2J+m><a&+M;G_h**hv$*l{xX{Q+USO86FUAaG=Xhs zVmo60UNqikIN0z1tJrW7E%}+Aju0-ON798}%Kjr$3ywkq=b^s`<Io*ep&OWs240K? zs>5ISdX~rjnM^XF1z6<wU(bUBUyHtp?)*Kp)H~499Y7N~hFp{I1G><~=wImg8|dZA z<YxPxhrX9ZtB_p6qnLPBD|q0dhd1JY_pzAwy=cHQXrOlV^Z5gb5w4;EGAR>}p%7iK z5>5C?^zJmo{;g=7PjM_B&7uDO^8HGK0rQw$fwR$o>u?-yMgx70oJKf?UcR$v<vP&B zu3$C(gU+ucx<@(}J))(^y$jFay|^xy`n$tc8r;DNv{V<+OVouuJH?)z=7sPW?S<%q zYtRG|WJqYn8vF#kBYz@$3s=$cS@|K{#a+ljfAD4}1*r_5Lj%2vPTYpf2|Lk=N6_{Y zXu#9xXViv{>qIM)!KxFm2z`GTt-vz0^sCWJx;dI`=fTqLi31Lz0gs~<_#UlLTWr6G z2JS-VrSVO=^DK0yrD*Br;T&9omi*(`ei;2FJ&9c6BwXgf(q2ac4yM-~jzAaAM{n!& z*uDte*;D9_)}WWI8E4@xG?5N;otx;_lFp4Yk(_9GkDPxE4+gGBOS3$_uR<%a9$jb? zy5o1y%l9F^jeD^KE4Zod{AsjeE75U{$Z>~eWN6rju6qWD6F;2e!JTxXXM77?AZ>iA z0wd4_CZiLlqdTfWD^nZ$S4A7q3T;ByZHeuN(98V|n#d_k%(#<>zIY9-#I0z0aVl^o z+MkV9ATPF0KzBF^O=w1JUx-$+Hnu;HT$iu`J?h=)dS4cY(w?0iror3)EvDno=&kKQ v2X>=@uNAi*7*N@(wR>Q3#-OPa%kL{FpLG9}@{+QO*5d3vy<5-cEFSnD9uE1w diff --git a/sphinx/locale/lt/LC_MESSAGES/sphinx.po b/sphinx/locale/lt/LC_MESSAGES/sphinx.po index 48618e306..081c49949 100644 --- a/sphinx/locale/lt/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/lt/LC_MESSAGES/sphinx.po @@ -7,8 +7,9 @@ msgstr "" "Project-Id-Version: Sphinx 1.0pre/8b971dbc7d36\n" "Report-Msgid-Bugs-To: dalius@sandbox.lt\n" "POT-Creation-Date: 2010-05-24 23:53+0200\n" -"PO-Revision-Date: 2010-06-19 12:02+0300\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Dalius Dobravolskas <dalius@sandbox.lt>\n" +"Language-Team: lt <LL@li.org>\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "(n%100<10 || n%100>=20) ? 1 : 2)\n" "MIME-Version: 1.0\n" @@ -16,13 +17,13 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%Y-%m-%d" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "" @@ -35,66 +36,66 @@ msgstr "Įtaisytieji" msgid "Module level" msgstr "Modulio lygis" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%Y-%m-%d" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Bendras indeksas" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "indeksas" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "kitas" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "praeitas" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (kuris yra " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Skyriaus autorius: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Modulio autorius: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 msgid "Code author: " msgstr "Kodo autorius: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autorius: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Taip pat žiūrėkite" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "%s %s" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parametrai" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Grąžinamos reikšmės" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Grąžinamos reikšmės tipas" @@ -123,12 +124,12 @@ msgstr "%s (C tipas)" msgid "%s (C variable)" msgstr "%s (C kintamasis)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "funkcija" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "narys" @@ -136,7 +137,7 @@ msgstr "narys" msgid "macro" msgstr "makrokomanda" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "tipas" @@ -144,186 +145,197 @@ msgstr "tipas" msgid "variable" msgstr "kintamasis" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ tipas)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ narys)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ funkcija)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "klasė" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (itaisytoji funkcija)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s metodas)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, python-format +msgid "%s() (class)" +msgstr "%s() (klasė)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "%s (globalus kintamasis arba konstanta)" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s atributas)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 msgid "Arguments" msgstr "Argumentais" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "Išmeta" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "duomenys" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "atribudas" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 msgid "Variables" msgstr "Kintamieji" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Sukelia" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (modulyje %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (įtaisytasis kintamasis)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (modulje %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (įtaisytoji klasė)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (klasė iš %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s metodas)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s statinis metodas)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s statinis metodas)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s klasės metodas)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, python-format msgid "%s() (%s class method)" msgstr "%s() (%s klasės metodas)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s atributas)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Platformos: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (modulis)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 msgid "Python Module Index" msgstr "" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "moduliai" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Atmestas" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "išimtis" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "metodas" -#: sphinx/domains/python.py:502 +#: sphinx/domains/python.py:528 msgid "class method" msgstr "klasės metodas" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statinis metodas" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "modulis" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Atmestas" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "%s (direktyva)" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, python-format msgid "%s (role)" msgstr "%s (rolė)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "direktyva" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 msgid "role" msgstr "rolė" @@ -362,7 +374,7 @@ msgstr "programos parinktis" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Indeksas" @@ -374,12 +386,12 @@ msgstr "Modulio indeksas" msgid "Search Page" msgstr "Paieškos puslapis" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Bazės: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr ":class:`%s` alternatyvus vardas" @@ -397,103 +409,103 @@ msgstr "(<<original entry>> galima rasti %s, eilutėje %d.)" msgid "original entry" msgstr "originalus įrašas" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "[šaltinis]" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "[dokumentai]" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 msgid "Module code" msgstr "Modulio kodas" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "<h1>Kodas %s</h1>" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "Apžvalga: modulio kodas" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "<h1>Visi moduliai turintys kodą</h1>" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Dėmesio" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Atsargiai" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Pavojinga" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Klaida" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Patarimas" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Svarbu" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Pastaba" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Taip pat žiūrėkite" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Patarimas" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Įspėjimas" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Nauja %s versijoje" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Pakeista %s versijoje" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Nebepalaikoma nuo %s versijos" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "bazinis žodis" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operatorius" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objektas" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "sakinis" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "įtaisytoji funkcija" @@ -503,7 +515,7 @@ msgid "Table Of Contents" msgstr "Turinys" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Paieška" @@ -633,7 +645,7 @@ msgstr "Kita tema" msgid "next chapter" msgstr "kita dalis" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." @@ -641,25 +653,29 @@ msgstr "" "Prašome aktyvuoti JavaScript, kad veiktų paieškos\n" " funkcionalumas." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" " function will automatically search for all of the words. Pages\n" " containing fewer words won't appear in the result list." msgstr "" -"Čia jūs galite ieškoti šiuose dokumentuose. Įveskite savo paieškos\n žodžius į lauką apačioje ir paspauskite \"ieškoti\". Pastebėsime, kad paieškos\n funkcija automatiškai ieškos visų žodžių. Puslapiai,\n kuriuose yra mažiau žodžių nepasirodys tarp paieškos rezultatų." +"Čia jūs galite ieškoti šiuose dokumentuose. Įveskite savo paieškos\n" +" žodžius į lauką apačioje ir paspauskite \"ieškoti\". Pastebėsime, kad" +" paieškos\n" +" funkcija automatiškai ieškos visų žodžių. Puslapiai,\n" +" kuriuose yra mažiau žodžių nepasirodys tarp paieškos rezultatų." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "ieškoti" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Paieškos rezultatai" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Jūsų paieška neatitiko jokių rezultatų" @@ -699,12 +715,12 @@ msgstr "C API pakeitimai" msgid "Other changes" msgstr "Kiti pakeitimai" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Nuoroda į šią antraštę" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Nuoroda į šį apibrėžimą" @@ -712,25 +728,27 @@ msgstr "Nuoroda į šį apibrėžimą" msgid "Hide Search Matches" msgstr "Paslėpti paieškos rezultatus" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Ieškoma" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Ruošiama paieška..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", kuris yra " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." -msgstr "Jūsų paieška neatitiko jokių dokumentų. Prašom patikrinti ar visi žodžiai teisingai įvesti ir ar pasirinkote pakankamai kategorijų." +msgstr "" +"Jūsų paieška neatitiko jokių dokumentų. Prašom patikrinti ar visi žodžiai" +" teisingai įvesti ir ar pasirinkote pakankamai kategorijų." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Paieška baigta, paieškos rezultatus atitiko %s puslapis(-iai,-ių)" @@ -740,7 +758,7 @@ msgid "Expand sidebar" msgstr "Išplėsti šoninę juostą" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "Paslėpti šoninę juostą" @@ -748,19 +766,19 @@ msgstr "Paslėpti šoninę juostą" msgid "Contents" msgstr "Turinys" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Leidimas" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Išnašos" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "tęsinys iš praeito puslapio" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Tęsinys kitame puslapyje" diff --git a/sphinx/locale/nl/LC_MESSAGES/sphinx.mo b/sphinx/locale/nl/LC_MESSAGES/sphinx.mo index 2002607ff41c0a6fceb67cd372247b1dee0680db..39f46af0d4d5524b838475a5263c2e29c4bba7ad 100644 GIT binary patch delta 32 ocmeD1?C{*MP>k0?*T_u4(9p`%NZY_*@;b49jHa8{ir?k|0HyW{djJ3c delta 32 ocmeD1?C{*MP>k19*VsV8(9p`rMBBh<@;b49j3%4cir?k|0HsC>a{vGU diff --git a/sphinx/locale/nl/LC_MESSAGES/sphinx.po b/sphinx/locale/nl/LC_MESSAGES/sphinx.po index 8683db4f8..1e321591d 100644 --- a/sphinx/locale/nl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/nl/LC_MESSAGES/sphinx.po @@ -7,23 +7,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2009-08-06 23:04+0200\n" -"PO-Revision-Date: 2010-05-29 16:22+0100\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Marijn van der Zee <marijn.vanderzee@gmail.com>\n" "Language-Team: nl <LL@li.org>\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 -#: sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d. %B %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -36,71 +35,67 @@ msgstr "Builtins" msgid "Module level" msgstr "Moduleniveau" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d.%b.%Y" -#: sphinx/builders/html.py:285 -#: sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Algemene index" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "Index" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "volgende" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "vorige" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Auteur van deze sectie: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Auteur van deze module: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Auteur van deze module: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Auteur: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Zie ook" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 -#: sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parameters" -#: sphinx/domains/c.py:54 -#: sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Returns" -#: sphinx/domains/c.py:56 -#: sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Return type" @@ -129,15 +124,12 @@ msgstr "%s (C type)" msgid "%s (C variable)" msgstr "%s (C-variabele)" -#: sphinx/domains/c.py:171 -#: sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 -#: sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "functie" -#: sphinx/domains/c.py:172 -#: sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "member" @@ -145,8 +137,7 @@ msgstr "member" msgid "macro" msgstr "macro" -#: sphinx/domains/c.py:174 -#: sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "type" @@ -155,207 +146,206 @@ msgstr "type" msgid "variable" msgstr "variabele" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (C++ klasse)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ type)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ member)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ functie)" -#: sphinx/domains/cpp.py:1030 -#: sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "klasse" -#: sphinx/domains/javascript.py:117 -#: sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (geïntegreerde functie)" -#: sphinx/domains/javascript.py:118 -#: sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s methode)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (C++ klasse)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "%s (globale variabele of constante)" -#: sphinx/domains/javascript.py:122 -#: sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s attribuut)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Parameters" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 -#: sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 -#: sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "attribuut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Variabele" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Veroorzaakt" -#: sphinx/domains/python.py:222 -#: sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 -#: sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (in module %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (geïntegreerde variabele)" -#: sphinx/domains/python.py:226 -#: sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (in module %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (geïntegreerde klasse)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (klasse in %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s methode)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s statische methode)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s statische methode)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s methode)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s methode)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s attribuut)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Platformen: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (module)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Module-index" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "modules" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Verouderd" -#: sphinx/domains/python.py:500 -#: sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "exceptie" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s methode)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statische methode" -#: sphinx/domains/python.py:505 -#: sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "module" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Verouderd" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (module)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "module" -#: sphinx/domains/std.py:68 -#: sphinx/domains/std.py:84 +#: sphinx/domains/std.py:68 sphinx/domains/std.py:84 #, python-format msgid "environment variable; %s" msgstr "omgevingsvariabele; %s" @@ -385,15 +375,12 @@ msgstr "omgevingsvariabele" msgid "program option" msgstr "" -#: sphinx/domains/std.py:360 -#: sphinx/themes/basic/genindex-single.html:11 +#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11 #: sphinx/themes/basic/genindex-split.html:11 #: sphinx/themes/basic/genindex-split.html:14 -#: sphinx/themes/basic/genindex.html:11 -#: sphinx/themes/basic/genindex.html:14 -#: sphinx/themes/basic/genindex.html:50 -#: sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 +#: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Index" @@ -401,17 +388,16 @@ msgstr "Index" msgid "Module Index" msgstr "Module-index" -#: sphinx/domains/std.py:362 -#: sphinx/themes/basic/defindex.html:25 +#: sphinx/domains/std.py:362 sphinx/themes/basic/defindex.html:25 msgid "Search Page" msgstr "Zoekpagina" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "" @@ -429,132 +415,126 @@ msgstr "(Het <<originele item>> is te vinden in %s, regel %d.)" msgid "original entry" msgstr "originele item" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "module" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Let op" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Pas op" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Gevaar" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Fout" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Hint" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Belangrijk" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Notitie" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Zie ook" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Tip" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Waarschuwing" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Nieuw in versie %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Veranderd in versie %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Verouderd sinds versie %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "trefwoord" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operator" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "object" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "statement" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "ingebouwde functie" -#: sphinx/themes/agogo/layout.html:45 -#: sphinx/themes/basic/globaltoc.html:10 +#: sphinx/themes/agogo/layout.html:45 sphinx/themes/basic/globaltoc.html:10 #: sphinx/themes/basic/localtoc.html:11 msgid "Table Of Contents" msgstr "Inhoudsopgave" -#: sphinx/themes/agogo/layout.html:49 -#: sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 -#: sphinx/themes/basic/search.html:14 +#: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Zoeken" -#: sphinx/themes/agogo/layout.html:52 -#: sphinx/themes/basic/searchbox.html:15 +#: sphinx/themes/agogo/layout.html:52 sphinx/themes/basic/searchbox.html:15 msgid "Go" msgstr "Ga" -#: sphinx/themes/agogo/layout.html:57 -#: sphinx/themes/basic/searchbox.html:20 +#: sphinx/themes/agogo/layout.html:57 sphinx/themes/basic/searchbox.html:20 msgid "Enter search terms or a module, class or function name." msgstr "Geef zoekterm of de naam van een module, klasse of functie." -#: sphinx/themes/agogo/layout.html:78 -#: sphinx/themes/basic/sourcelink.html:14 +#: sphinx/themes/agogo/layout.html:78 sphinx/themes/basic/sourcelink.html:14 msgid "Show Source" msgstr "Broncode weergeven" @@ -644,8 +624,12 @@ msgstr "Laatste aanpassing op %(last_updated)s." #: sphinx/themes/basic/layout.html:189 #, python-format -msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." -msgstr "Aangemaakt met <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." +msgid "" +"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." +msgstr "" +"Aangemaakt met <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." #: sphinx/themes/basic/opensearch.xml:4 #, python-format @@ -668,13 +652,13 @@ msgstr "Volgend onderwerp" msgid "next chapter" msgstr "volgend hoofdstuk" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "Activeer JavaSscript om de zoekfunctionaliteit in te schakelen." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -682,20 +666,22 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "" "Hier kan u de documenten doorzoeken. Geef enkele trefwoorden\n" -" in het veld hieronder en klik \"zoeken\". Merk op dat de zoekfunctie\n" -" steeds naar alle woorden zoekt. Pagina's die minder woorden bevatten\n" +" in het veld hieronder en klik \"zoeken\". Merk op dat de zoekfunctie" +"\n" +" steeds naar alle woorden zoekt. Pagina's die minder woorden bevatten" +"\n" " zullen niet tussen de resultaten verschijnen." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "zoeken" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Zoekresultaten" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Uw zoekopdracht leverde geen resultaten op." @@ -735,14 +721,12 @@ msgstr "Veranderingen in de C-API" msgid "Other changes" msgstr "Andere veranderingen" -#: sphinx/themes/basic/static/doctools.js:154 -#: sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Permalink naar deze titel" -#: sphinx/themes/basic/static/doctools.js:160 -#: sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Permalink naar deze definitie" @@ -750,23 +734,27 @@ msgstr "Permalink naar deze definitie" msgid "Hide Search Matches" msgstr "Zoekresultaten verbergen" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Zoeken" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Het zoeken wordt voorbereid..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", in " -#: sphinx/themes/basic/static/searchtools.js:491 -msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." -msgstr "Uw zoekopdracht leverde geen resultaten op. Controleer of alle woorden correct gespeld zijn en dat u genoeg categoriën hebt geselecteerd." +#: sphinx/themes/basic/static/searchtools.js:506 +msgid "" +"Your search did not match any documents. Please make sure that all words " +"are spelled correctly and that you've selected enough categories." +msgstr "" +"Uw zoekopdracht leverde geen resultaten op. Controleer of alle woorden " +"correct gespeld zijn en dat u genoeg categoriën hebt geselecteerd." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Zoeken voltooid, %s pagina(s) gevonden." @@ -776,7 +764,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -784,20 +772,19 @@ msgstr "" msgid "Contents" msgstr "Inhoud" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Release" -#: sphinx/writers/latex.py:572 -#: sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Voetnoten" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "Vervolgd van vorige pagina" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Vervolgd op volgende pagina" @@ -805,12 +792,3 @@ msgstr "Vervolgd op volgende pagina" msgid "[image]" msgstr "[afbeelding]" -#~ msgid "Parameter" -#~ msgstr "Parameter" -#~ msgid "here" -#~ msgstr "hier" -#~ msgid "module, in " -#~ msgstr "module, in" -#~ msgid "Platform: %s" -#~ msgstr "Platform: %s" - diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.mo b/sphinx/locale/pl/LC_MESSAGES/sphinx.mo index 3c6105cdb223791e464d4a75192859d9fbf4afde..949564e0ebb767a50c8c98abacf1ce5153d4850a 100644 GIT binary patch delta 33 ocmbQ`Im>fHj2N$lu92C7p`n$jk+y-s=1eg?Mn==kGsKyA0iX>Cc>n+a delta 33 ocmbQ`Im>fHj2N$}u92yNfq|8=iMD~!=1eg?Mn;p(GsKyA0iTx$bN~PV diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.po b/sphinx/locale/pl/LC_MESSAGES/sphinx.po index 6044e8eef..e2588b974 100644 --- a/sphinx/locale/pl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pl/LC_MESSAGES/sphinx.po @@ -4,7 +4,7 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-08-10 11:43+0000\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Michał Kandulski <Michal.Kandulski@poczta.onet.pl>\n" "Language-Team: \n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && " @@ -12,15 +12,15 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%B %d %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -33,67 +33,67 @@ msgstr "Wbudowane" msgid "Module level" msgstr "Poziom modułu" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%b %d %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Indeks ogólny" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "indeks" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "dalej" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "wstecz" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (w " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Autor rozdziału: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Autor modułu: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Autor modułu: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Zobacz także" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parametry" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Zwraca" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Typ zwracany" @@ -122,12 +122,12 @@ msgstr "%s (typ C)" msgid "%s (C variable)" msgstr "%s (zmienna C)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "funkcja" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "pole" @@ -135,7 +135,7 @@ msgstr "pole" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "typ" @@ -144,190 +144,201 @@ msgstr "typ" msgid "variable" msgstr "Zmienna" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (klasie C++)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (typ C++)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (pole C++)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (funkcja C++)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "klasie" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (funkcja wbudowana)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s metoda)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (klasie C++)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s atrybut)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Parametry" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "atrybut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Zmienna" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Wyrzuca" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (w module %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (zmienna wbudowana)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (w module %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (klasa wbudowana)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (w klasie %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s metoda)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s statyczna metoda)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s statyczna metoda)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s metoda)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s metoda)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s atrybut)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Platformy: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (moduł)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Indeks modułów" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "moduły" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Niezalecane" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "wyjątek" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s metoda)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statyczna metoda" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "moduł" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Niezalecane" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (moduł)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "moduł" @@ -367,7 +378,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Indeks" @@ -379,12 +390,12 @@ msgstr "Indeks modułów" msgid "Search Page" msgstr "Wyszukiwanie" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Klasy bazowe: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "alias klasy :class:`%s`" @@ -404,104 +415,104 @@ msgstr "" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "moduł" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Uwaga" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Ostrożnie" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Niebezpieczeństwo" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Błąd" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Podpowiedź" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Ważne" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Uwaga" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Zobacz także" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Wskazówka" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Ostrzeżenie" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Nowe w wersji %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Zmienione w wersji %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Niezalecane od wersji %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "słowo kluczowe" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operator" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "obiekt" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "instrukcja" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "funkcja wbudowana" @@ -511,7 +522,7 @@ msgid "Table Of Contents" msgstr "Spis treści" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Szukaj" @@ -641,13 +652,13 @@ msgstr "Następny temat" msgid "next chapter" msgstr "następny rozdział" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "Aby umożliwić wuszukiwanie, proszę włączyć JavaScript." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -661,16 +672,16 @@ msgstr "" " nie zawierające wszystkich wpisanych słów nie znajdą się na wynikowej" " liście." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "Szukaj" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Wyniki wyszukiwania" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Nie znaleziono żadnych pasujących stron." @@ -710,12 +721,12 @@ msgstr "Zmiany w C API" msgid "Other changes" msgstr "Inne zmiany" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Stały odnośnik do tego nagłówka" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Stały odnośnik do tej definicji" @@ -723,19 +734,19 @@ msgstr "Stały odnośnik do tej definicji" msgid "Hide Search Matches" msgstr "Ukryj wyniki wyszukiwania" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Wyszukiwanie" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Przygotowanie wyszukiwania..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", w " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -743,7 +754,7 @@ msgstr "" "Nie znaleziono żadnych pasujących dokumentów. Upewnij się, że wszystkie " "słowa są poprawnie wpisane i że wybrałeś wystarczającąliczbę kategorii." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Przeszukiwanie zakończone, znaleziono %s pasujących stron." @@ -753,7 +764,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -761,19 +772,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Wydanie" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Przypisy" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "kontynuacja poprzedniej strony" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Kontynuacja na następnej stronie" @@ -781,15 +792,3 @@ msgstr "Kontynuacja na następnej stronie" msgid "[image]" msgstr "[obrazek]" -#~ msgid "Parameter" -#~ msgstr "Parametr" - -#~ msgid "here" -#~ msgstr "tutaj" - -#~ msgid "module, in " -#~ msgstr "moduł, w " - -#~ msgid "Platform: %s" -#~ msgstr "Platforma: %s" - diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo index 67c1ce549defc203511d37f64b4f4117ee3b9247..31558971e309c1243eda7050c2cb403724bc6909 100644 GIT binary patch delta 33 ocmeAP=n2^HO`O+4*T_u4(9p`%NZY_*GqVIgBcthNVacC-0I9AB_y7O^ delta 33 ocmeAP=n2^HO`O+U*T6`@(7?*XP}{(0GqVIgBcsV?VacC-0I2T>?*IS* diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po index 7df9013e7..75770ee95 100644 --- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po @@ -8,23 +8,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: roger.demetrescu@gmail.com\n" "POT-Creation-Date: 2008-11-09 19:46+0100\n" -"PO-Revision-Date: 2010-06-20 18:34-0300\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Roger Demetrescu <roger.demetrescu@gmail.com>\n" "Language-Team: pt_BR <roger.demetrescu@gmail.com>\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 -#: sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d/%m/%Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -37,70 +36,66 @@ msgstr "Internos" msgid "Module level" msgstr "Módulo" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d/%m/%Y" -#: sphinx/builders/html.py:285 -#: sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Índice Geral" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "índice" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "próximo" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "anterior" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (em " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Autor da seção: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Autor do módulo: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 msgid "Code author: " msgstr "Autor do código: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Autor: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Veja também" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "%s %s" -#: sphinx/domains/c.py:51 -#: sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parâmetros" -#: sphinx/domains/c.py:54 -#: sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Retorna" -#: sphinx/domains/c.py:56 -#: sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Tipo de retorno" @@ -129,15 +124,12 @@ msgstr "%s (tipo C)" msgid "%s (C variable)" msgstr "%s (variável C)" -#: sphinx/domains/c.py:171 -#: sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 -#: sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "função" -#: sphinx/domains/c.py:172 -#: sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "membro" @@ -145,8 +137,7 @@ msgstr "membro" msgid "macro" msgstr "macro" -#: sphinx/domains/c.py:174 -#: sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "tipo" @@ -154,203 +145,201 @@ msgstr "tipo" msgid "variable" msgstr "variável" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (classe C++)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (tipo C++)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (membro C++)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (função C++)" -#: sphinx/domains/cpp.py:1030 -#: sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "classe" -#: sphinx/domains/javascript.py:117 -#: sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (função interna)" -#: sphinx/domains/javascript.py:118 -#: sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (método %s)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (classe C++)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "%s (variável global ou constante)" -#: sphinx/domains/javascript.py:122 -#: sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (atributo %s)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 msgid "Arguments" msgstr "Parâmetros" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "Gera" -#: sphinx/domains/javascript.py:167 -#: sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "dado" -#: sphinx/domains/javascript.py:168 -#: sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "atributo" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 msgid "Variables" msgstr "Variáveis" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Levanta" -#: sphinx/domains/python.py:222 -#: sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 -#: sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (no módulo %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (variável interna)" -#: sphinx/domains/python.py:226 -#: sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (no módulo %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (classe interna)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (classe em %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (método %s.%s)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (método estático %s.%s)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (método estático %s)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (método de classe %s.%s)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, python-format msgid "%s() (%s class method)" msgstr "%s() (método de classe %s)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (atributo %s.%s)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Plataformas: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (módulo)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 msgid "Python Module Index" msgstr "Índice de Módulos do Python" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "módulos" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Obsoleto" -#: sphinx/domains/python.py:500 -#: sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "exceção" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "método" -#: sphinx/domains/python.py:502 -#, python-format +#: sphinx/domains/python.py:528 msgid "class method" msgstr "método de classe" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "método estático" -#: sphinx/domains/python.py:505 -#: sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "módulo" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Obsoleto" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "%s (diretiva)" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, python-format msgid "%s (role)" msgstr "%s (papel)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "diretiva" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 msgid "role" msgstr "papel" -#: sphinx/domains/std.py:68 -#: sphinx/domains/std.py:84 +#: sphinx/domains/std.py:68 sphinx/domains/std.py:84 #, python-format msgid "environment variable; %s" msgstr "váriavel de ambiente; %s" @@ -380,15 +369,12 @@ msgstr "váriavel de ambiente" msgid "program option" msgstr "opção de programa" -#: sphinx/domains/std.py:360 -#: sphinx/themes/basic/genindex-single.html:11 +#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11 #: sphinx/themes/basic/genindex-split.html:11 #: sphinx/themes/basic/genindex-split.html:14 -#: sphinx/themes/basic/genindex.html:11 -#: sphinx/themes/basic/genindex.html:14 -#: sphinx/themes/basic/genindex.html:50 -#: sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 +#: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Índice" @@ -396,17 +382,16 @@ msgstr "Índice" msgid "Module Index" msgstr "Índice do Módulo" -#: sphinx/domains/std.py:362 -#: sphinx/themes/basic/defindex.html:25 +#: sphinx/domains/std.py:362 sphinx/themes/basic/defindex.html:25 msgid "Search Page" msgstr "Página de Pesquisa" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Bases: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "apelido de :class:`%s`" @@ -424,131 +409,125 @@ msgstr "(A <<entrada original>> está localizada em %s, linha %d.)" msgid "original entry" msgstr "entrada original" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "[código fonte]" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "[documentos]" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 msgid "Module code" msgstr "Código do módulo" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "<h1>Código fonte de %s</h1>" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "Visão geral: código do módulo" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "<h1>Todos os módulos onde este código está disponível</h1>" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Atenção" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Cuidado" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Perigo" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Erro" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Dica" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Importante" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Nota" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Veja Também" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Dica" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Aviso" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Novo na versão %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Alterado na versão %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Obsoleto desde a versão %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "palavra-chave" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operador" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objeto" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "comando" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "função interna" -#: sphinx/themes/agogo/layout.html:45 -#: sphinx/themes/basic/globaltoc.html:10 +#: sphinx/themes/agogo/layout.html:45 sphinx/themes/basic/globaltoc.html:10 #: sphinx/themes/basic/localtoc.html:11 msgid "Table Of Contents" msgstr "Tabela de Conteúdo" -#: sphinx/themes/agogo/layout.html:49 -#: sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 -#: sphinx/themes/basic/search.html:14 +#: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Pesquisar" -#: sphinx/themes/agogo/layout.html:52 -#: sphinx/themes/basic/searchbox.html:15 +#: sphinx/themes/agogo/layout.html:52 sphinx/themes/basic/searchbox.html:15 msgid "Go" msgstr "Ir" -#: sphinx/themes/agogo/layout.html:57 -#: sphinx/themes/basic/searchbox.html:20 +#: sphinx/themes/agogo/layout.html:57 sphinx/themes/basic/searchbox.html:20 msgid "Enter search terms or a module, class or function name." msgstr "Digite os termos da busca ou o nome de um módulo, classe ou função." -#: sphinx/themes/agogo/layout.html:78 -#: sphinx/themes/basic/sourcelink.html:14 +#: sphinx/themes/agogo/layout.html:78 sphinx/themes/basic/sourcelink.html:14 msgid "Show Source" msgstr "Exibir Fonte" @@ -638,8 +617,12 @@ msgstr "Última atualização em %(last_updated)s." #: sphinx/themes/basic/layout.html:189 #, python-format -msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." -msgstr "Criado com <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." +msgid "" +"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." +msgstr "" +"Criado com <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." #: sphinx/themes/basic/opensearch.xml:4 #, python-format @@ -662,13 +645,13 @@ msgstr "Próximo tópico" msgid "next chapter" msgstr "próximo capítulo" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "Por favor ative o JavaScript para habilitar a funcionalidade de pesquisa." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -676,20 +659,22 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "" "A partir daqui você pode pesquisar estes documentos. Preencha suas \n" -" palavras de pesquisa na caixa abaixo e clique em \"pesquisar\". Observe que a função de pesquisa\n" +" palavras de pesquisa na caixa abaixo e clique em \"pesquisar\". " +"Observe que a função de pesquisa\n" " irá pesquisar automaticamente por todas as palavras.\n" -" Páginas contendo menos palavras não irão aparecer na lista de resultado." +" Páginas contendo menos palavras não irão aparecer na lista de " +"resultado." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "pesquisar" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Resultados da Pesquisa" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Sua pesquisa não encontrou nenhum resultado." @@ -729,14 +714,12 @@ msgstr "Alterações na API C" msgid "Other changes" msgstr "Outras alterações" -#: sphinx/themes/basic/static/doctools.js:154 -#: sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Link permanente para este título" -#: sphinx/themes/basic/static/doctools.js:160 -#: sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Link permanente para esta definição" @@ -744,33 +727,40 @@ msgstr "Link permanente para esta definição" msgid "Hide Search Matches" msgstr "Esconder Resultados da Pesquisa" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Pesquisando" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Preparando pesquisa..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", em " -#: sphinx/themes/basic/static/searchtools.js:491 -msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." -msgstr "Sua pesquisa não encontrou nenhum documento. Por favor assegure-se de que todas as palavras foram digitadas corretamente e de que você tenha selecionado o mínimo de categorias." +#: sphinx/themes/basic/static/searchtools.js:506 +msgid "" +"Your search did not match any documents. Please make sure that all words " +"are spelled correctly and that you've selected enough categories." +msgstr "" +"Sua pesquisa não encontrou nenhum documento. Por favor assegure-se de que" +" todas as palavras foram digitadas corretamente e de que você tenha " +"selecionado o mínimo de categorias." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." -msgstr "Pesquisa finalizada, foram encontrada(s) %s página(s) que conferem com o critério de pesquisa." +msgstr "" +"Pesquisa finalizada, foram encontrada(s) %s página(s) que conferem com o " +"critério de pesquisa." #: sphinx/themes/default/static/sidebar.js:66 msgid "Expand sidebar" msgstr "Expandir painel lateral" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "Recolher painel lateral" @@ -778,20 +768,19 @@ msgstr "Recolher painel lateral" msgid "Contents" msgstr "Conteúdo" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Versão" -#: sphinx/writers/latex.py:572 -#: sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Notas de rodapé" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "continuação da página anterior" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Continua na próxima página" diff --git a/sphinx/locale/ru/LC_MESSAGES/sphinx.mo b/sphinx/locale/ru/LC_MESSAGES/sphinx.mo index ecdc6860488cab3b5f332cb47d6851dd5211fd8c..a7d7b641be0e206f508dc68161ed8c50a575ca62 100644 GIT binary patch delta 33 ocmZ1zu_j`Jxfrj7u92C7p`n$jk+y-sW*0GAMn==kP2#IW0HhKL82|tP delta 33 ocmZ1zu_j`Jxfrjhu92yNfq|8=iMD~!W*0GAMn;p(P2#IW0Hd4<6aWAK diff --git a/sphinx/locale/ru/LC_MESSAGES/sphinx.po b/sphinx/locale/ru/LC_MESSAGES/sphinx.po index e2b1e3a13..acd409a45 100644 --- a/sphinx/locale/ru/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ru/LC_MESSAGES/sphinx.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: Sphinx 0.6b1\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2009-01-24 18:39+0000\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: alexander smishlajev <alex@tycobka.lv>\n" "Language-Team: ru <LL@li.org>\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " @@ -15,15 +15,15 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d %B %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -36,67 +36,67 @@ msgstr "Встроенные функции" msgid "Module level" msgstr "Модуль" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d %b %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Словарь-указатель" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "словарь" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "следующий" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "предыдущий" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (в " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Автор секции: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Автор модуля: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Автор модуля: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Автор: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "См.также" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Параметры" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Результат" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Тип результата" @@ -125,12 +125,12 @@ msgstr "%s (тип C)" msgid "%s (C variable)" msgstr "%s (переменная C)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "функция" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "поле" @@ -138,7 +138,7 @@ msgstr "поле" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "тип" @@ -147,190 +147,201 @@ msgstr "тип" msgid "variable" msgstr "Переменная" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (класс C++)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (тип C++)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (поле C++)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (функция C++)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "класс" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (встроенная функция)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (метод %s)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (класс C++)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (атрибут %s)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Параметры" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "атрибут" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Переменная" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Исключение" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (в модуле %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (встроенная переменная)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (в модуле %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (встроенный класс)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (класс в %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (метод %s.%s)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (статический метод %s.%s)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (статический метод %s)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (метод %s.%s)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (метод %s)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (атрибут %s.%s)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Платформы: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (модуль)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Состав модуля" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "модули" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Не рекомендуется" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "исключение" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (метод %s)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "статический метод" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "модуль" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Не рекомендуется" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (модуль)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "модуль" @@ -370,7 +381,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Алфавитный указатель" @@ -382,12 +393,12 @@ msgstr "Состав модуля" msgid "Search Page" msgstr "Поиск" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Базовые классы: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "псевдоним класса :class:`%s`" @@ -405,104 +416,104 @@ msgstr "(Исходный элемент находится в %s, в строк msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "модуль" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Внимание" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Осторожно" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Опасно" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Ошибка" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Подсказка" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Важно" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Примечание" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "См.также" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Совет" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Предупреждение" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Добавлено в версии %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Изменено в версии %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Не рекомендуется, начиная с версии %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "ключевое слово" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "оператор" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "объект" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "команда" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "базовая функция" @@ -512,7 +523,7 @@ msgid "Table Of Contents" msgstr "Содержание" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Поиск" @@ -642,13 +653,13 @@ msgstr "Следующий раздел" msgid "next chapter" msgstr "следующая глава" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "Для выполнения поиска необходима поддержка JavaScript в браузере." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -661,16 +672,16 @@ msgstr "" "упомянуты все указанные слова. Страницы, в которых встречается только " "часть этих слов, отобраны не будут." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "искать" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Результаты поиска" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Результатов по вашему запросу не найдено." @@ -710,12 +721,12 @@ msgstr "Изменения в C API" msgid "Other changes" msgstr "Другие изменения" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Ссылка на этот заголовок" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Ссылка на это определение" @@ -723,19 +734,19 @@ msgstr "Ссылка на это определение" msgid "Hide Search Matches" msgstr "Снять выделение" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Поиск" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Подготовка к поиску..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", в " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -743,7 +754,7 @@ msgstr "" "Нет документов, соответствующих вашему запросу. Проверьте, правильно ли " "выбраны категории и нет ли опечаток в запросе." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Поиск окончен, найдено страниц: %s." @@ -753,7 +764,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -761,19 +772,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Выпуск" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 #, fuzzy msgid "Continued on next page" msgstr "Полный алфавитный указатель на одной странице" @@ -781,3 +792,4 @@ msgstr "Полный алфавитный указатель на одной с #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[рисунок]" + diff --git a/sphinx/locale/sl/LC_MESSAGES/sphinx.mo b/sphinx/locale/sl/LC_MESSAGES/sphinx.mo index 21e64ffd5f1a15ddaffbfc65ec0281bba141c91d..ad2b8aa8f1435fe858cc9c1b03a968a9069b1eba 100644 GIT binary patch delta 32 ncmdnxzRP_>q!_P-u92C7p`n$jk+y-s<TSB=jHa7Y#Y1@jr3MLo delta 32 ncmdnxzRP_>q!_QMu92yNfq|8=iMD~!<TSB=j3%2?#Y1@jq?8GG diff --git a/sphinx/locale/sl/LC_MESSAGES/sphinx.po b/sphinx/locale/sl/LC_MESSAGES/sphinx.po index 6028c35c8..f47aa3d36 100644 --- a/sphinx/locale/sl/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sl/LC_MESSAGES/sphinx.po @@ -4,22 +4,22 @@ msgstr "" "Project-Id-Version: Sphinx\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-09-11 23:58+0200\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Luka Marinko <luka.marinko@simt.si>\n" "Language-Team: Rok Garbas <rok.garbas@gmail.com>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d %B, %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -32,67 +32,67 @@ msgstr "Vgrajeni deli" msgid "Module level" msgstr "Nivo modula" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d %b, %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Splošni abecedni seznam" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "abecedni seznam" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "naprej" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "nazaj" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (v " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Avtor sekcije: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Avtor modula: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Avtor modula: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Avtor: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Poglej Tudi" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parametri" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Vrne" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Vrne tip" @@ -121,12 +121,12 @@ msgstr "%s (C tip)" msgid "%s (C variable)" msgstr "%s (C spremenljivka)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "funkcija" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "član" @@ -134,7 +134,7 @@ msgstr "član" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "tip" @@ -143,190 +143,201 @@ msgstr "tip" msgid "variable" msgstr "Spremenljivka" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (C++ razred)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ tip)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ član)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ funkcija)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "razred" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (vgrajene funkcije)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s metoda)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (C++ razred)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s atribut)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Parametri" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "atribut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Spremenljivka" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Sproži izjemo" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (v modulu %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (vgrajene spremenljivke)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (v modulu %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (vgrajen razred)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (razred v %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s metoda)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s statična metoda)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s statična metoda)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s metoda)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s metoda)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s atribut)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Platforme:" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (modul)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Seznam modulov" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "Moduli" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Zastarelo" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "izjema" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s metoda)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statična metoda" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "modul" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Zastarelo" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (modul)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "modul" @@ -366,7 +377,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Abecedni seznam" @@ -378,12 +389,12 @@ msgstr "Seznam modulov" msgid "Search Page" msgstr "Iskalnik" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Baza: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "vzdevek za :class:`%s`" @@ -401,104 +412,104 @@ msgstr "(Originalen vnos se nahaja v %s, v vrstici %d, in ga je moč poiskati " msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "modul" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Pozor" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Previdno" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Nevarno" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Napaka" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Nasvet" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Pomembno" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Opomba" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Poglej Tudi" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Nasvet" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Opozorilo" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Novo v verziji %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Spremenjeno v verziji %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Zastarelo od verzije %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "ključna beseda" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operator" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objekt" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "izjava" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "vgrajene funkcije" @@ -508,7 +519,7 @@ msgid "Table Of Contents" msgstr "Seznam Vsebine" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Išči" @@ -638,7 +649,7 @@ msgstr "Naslednja tema" msgid "next chapter" msgstr "naslednje poglavje" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." @@ -646,7 +657,7 @@ msgstr "" "Za pravilno delovanje Iskanja morete vklopiti\n" " JavaScript." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -658,16 +669,16 @@ msgstr "" " bo iskalo po vseh besedah v iskalnem nizu. Strani, ki ne\n" " vsebujejo vseh besed ne bodo prikazane na seznamu rezultatov." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "išči" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Rezultati Iskanja" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Vaše iskanje ni imelo nobenega zadetka." @@ -707,12 +718,12 @@ msgstr "C API spremembe" msgid "Other changes" msgstr "Ostale spremembe" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Povezava na naslov" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Povezava na to definicijo" @@ -720,19 +731,19 @@ msgstr "Povezava na to definicijo" msgid "Hide Search Matches" msgstr "Skrij resultate iskanja" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Iščem" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Pripravljam iskanje..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", v " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -740,7 +751,7 @@ msgstr "" "Za vaše iskanje ni rezultatov. Prosimo preglejte ali so vse besede " "pravilno črkovane in ali ste izbrali dovolj kategorij." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Iskanje končano, najdeno %s strani, ki ustrezajo iskalnemu nizu." @@ -750,7 +761,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -758,22 +769,23 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Izdaja" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Opombe" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "nadaljevanje iz prejšnje strani" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Nadaljevanje na naslednji strani" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[slika]" + diff --git a/sphinx/locale/sphinx.pot b/sphinx/locale/sphinx.pot index 955663aa7..5d60cd4e5 100644 --- a/sphinx/locale/sphinx.pot +++ b/sphinx/locale/sphinx.pot @@ -6,24 +6,24 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Sphinx 1.0pre/8b971dbc7d36\n" +"Project-Id-Version: Sphinx 1.1pre/dd630cbbda23\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2010-05-24 23:53+0200\n" +"POT-Creation-Date: 2010-08-26 11:44+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "" @@ -36,66 +36,66 @@ msgstr "" msgid "Module level" msgstr "" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "" -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "" -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 msgid "Code author: " msgstr "" -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "" -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "" @@ -124,12 +124,12 @@ msgstr "" msgid "%s (C variable)" msgstr "" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "" @@ -137,7 +137,7 @@ msgstr "" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "" @@ -145,186 +145,196 @@ msgstr "" msgid "variable" msgstr "" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, python-format +msgid "%s() (class)" +msgstr "" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 msgid "Arguments" msgstr "" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 msgid "Variables" msgstr "" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, python-format msgid "%s() (%s.%s class method)" msgstr "" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, python-format msgid "%s() (%s class method)" msgstr "" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 msgid "Python Module Index" msgstr "" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 +#: sphinx/domains/python.py:528 msgid "class method" msgstr "" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "" -#: sphinx/domains/rst.py:53 -#, python-format -msgid "%s (directive)" +#: sphinx/domains/python.py:657 +msgid " (deprecated)" msgstr "" #: sphinx/domains/rst.py:55 #, python-format +msgid "%s (directive)" +msgstr "" + +#: sphinx/domains/rst.py:57 +#, python-format msgid "%s (role)" msgstr "" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 msgid "role" msgstr "" @@ -363,7 +373,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "" @@ -375,12 +385,12 @@ msgstr "" msgid "Search Page" msgstr "" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "" @@ -398,103 +408,103 @@ msgstr "" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 msgid "Module code" msgstr "" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "" @@ -504,7 +514,7 @@ msgid "Table Of Contents" msgstr "" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "" @@ -632,13 +642,13 @@ msgstr "" msgid "next chapter" msgstr "" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "" -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -646,16 +656,16 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "" -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "" @@ -695,12 +705,12 @@ msgstr "" msgid "Other changes" msgstr "" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "" @@ -708,25 +718,25 @@ msgstr "" msgid "Hide Search Matches" msgstr "" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "" -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr "" -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "" -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" @@ -736,7 +746,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -744,19 +754,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "" diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.mo b/sphinx/locale/sv/LC_MESSAGES/sphinx.mo index 8cf7675160ed732108bb75ecd460ce2f193a67ce..40e059e29131d65834aa029ebff7cc88c8d7c509 100644 GIT binary patch delta 20 bcmZ4LwbX0FV{vvf1w%tCQ=`qV#I1P&P|XJA delta 20 bcmZ4LwbX0FV{vv<1w#WX6VuJF#I1P&P{Ri0 diff --git a/sphinx/locale/sv/LC_MESSAGES/sphinx.po b/sphinx/locale/sv/LC_MESSAGES/sphinx.po index f449e8f6e..331271800 100644 --- a/sphinx/locale/sv/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/sv/LC_MESSAGES/sphinx.po @@ -1,29 +1,25 @@ + msgid "" msgstr "" -"Project-Id-Version: \n" +"Project-Id-Version: \n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2010-05-24 23:53+0200\n" -"PO-Revision-Date: 2010-08-25 12:36+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Henrik Holmboe <henrik@holmboe.se>\n" "Language-Team: \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" +"Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Poedit-Language: Swedish\n" -"X-Poedit-Country: SWEDEN\n" +"Generated-By: Babel 0.9.5\n" -# Translators (rev. chron. order): -# Ludvig Ericson <ludvig@lericson.se> -# Henrik Holmboe <henrik@holmboe.se> - -#: sphinx/environment.py:106 -#: sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%B %d, %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -36,70 +32,66 @@ msgstr "Inbyggda" msgid "Module level" msgstr "Modulnivå" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%b %d, %Y" -#: sphinx/builders/html.py:285 -#: sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Huvudindex" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "index" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "nästa" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "föregående" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "(i " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Sektionsförfattare" -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Modulförfattare" -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 msgid "Code author: " msgstr "Källkodsförfattare" -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Upphovsman:" -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Se även" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "%s %s" -#: sphinx/domains/c.py:51 -#: sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parametrar" -#: sphinx/domains/c.py:54 -#: sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Returnerar" -#: sphinx/domains/c.py:56 -#: sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Returtyp" @@ -128,15 +120,12 @@ msgstr "%s (C-typ)" msgid "%s (C variable)" msgstr "%s (C-variabel)" -#: sphinx/domains/c.py:171 -#: sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 -#: sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "funktion" -#: sphinx/domains/c.py:172 -#: sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "medlem" @@ -144,8 +133,7 @@ msgstr "medlem" msgid "macro" msgstr "makro" -#: sphinx/domains/c.py:174 -#: sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "typ" @@ -153,202 +141,201 @@ msgstr "typ" msgid "variable" msgstr "variabel" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (C++-klass)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++-typ)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++-medlem)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++-funktion)" -#: sphinx/domains/cpp.py:1030 -#: sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "klass" -#: sphinx/domains/javascript.py:117 -#: sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (inbyggd funktion)" -#: sphinx/domains/javascript.py:118 -#: sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s metod)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (C++-klass)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "%s (global variabel eller konstant)" -#: sphinx/domains/javascript.py:122 -#: sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s attribut)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 msgid "Arguments" msgstr "Argument" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "Kastar" -#: sphinx/domains/javascript.py:167 -#: sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "data" -#: sphinx/domains/javascript.py:168 -#: sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "attribut" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 msgid "Variables" msgstr "Variabler" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Väcker" -#: sphinx/domains/python.py:222 -#: sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 -#: sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (i modul %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (inbyggd variabel)" -#: sphinx/domains/python.py:226 -#: sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (i modul %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (inbyggd klass)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (klass i %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s metod)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s statisk metod)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s statisk metod)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s klassmetod)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, python-format msgid "%s() (%s class method)" msgstr "%s() (%s klassmetod)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s attribut)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Plattformar:" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (modul)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 msgid "Python Module Index" msgstr "Python Modulindex" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "moduler" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Ersatt" -#: sphinx/domains/python.py:500 -#: sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "undantag" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "metod" -#: sphinx/domains/python.py:502 +#: sphinx/domains/python.py:528 msgid "class method" msgstr "klassmetod" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statisk metod" -#: sphinx/domains/python.py:505 -#: sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "modul" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Ersatt" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "%s (direktiv)" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, python-format msgid "%s (role)" msgstr "%s (roll)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "direktiv" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 msgid "role" msgstr "roll" -#: sphinx/domains/std.py:68 -#: sphinx/domains/std.py:84 +#: sphinx/domains/std.py:68 sphinx/domains/std.py:84 #, python-format msgid "environment variable; %s" msgstr "miljövariabel; %s" @@ -378,15 +365,12 @@ msgstr "miljövariabel" msgid "program option" msgstr "programväxel" -#: sphinx/domains/std.py:360 -#: sphinx/themes/basic/genindex-single.html:11 +#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11 #: sphinx/themes/basic/genindex-split.html:11 #: sphinx/themes/basic/genindex-split.html:14 -#: sphinx/themes/basic/genindex.html:11 -#: sphinx/themes/basic/genindex.html:14 -#: sphinx/themes/basic/genindex.html:50 -#: sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 +#: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Index" @@ -394,17 +378,16 @@ msgstr "Index" msgid "Module Index" msgstr "Modulindex" -#: sphinx/domains/std.py:362 -#: sphinx/themes/basic/defindex.html:25 +#: sphinx/domains/std.py:362 sphinx/themes/basic/defindex.html:25 msgid "Search Page" msgstr "Söksida" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Baserad: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "alias för :class:`%s`" @@ -422,131 +405,125 @@ msgstr "(<<Ursprunget>> finns i %s, på rad %d.)" msgid "original entry" msgstr "ursprungsvärde" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "[source]" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "[docs]" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 msgid "Module code" msgstr "Modulkällkod" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "<h1>Källkod för %s</h1>" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "Översikt: modulkällkod" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "<h1>Alla moduler där källkod finns</h1>" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Uppmärksamma" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Varning" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Risk" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Fel" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Råd" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Viktigt" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Observera" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Se även" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Tips" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Varning" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Nyheter i version %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Förändrat i version %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Ersatt sedan version %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "nyckelord" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "operator" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "objekt" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "uttryck" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "inbyggda funktioner" -#: sphinx/themes/agogo/layout.html:45 -#: sphinx/themes/basic/globaltoc.html:10 +#: sphinx/themes/agogo/layout.html:45 sphinx/themes/basic/globaltoc.html:10 #: sphinx/themes/basic/localtoc.html:11 msgid "Table Of Contents" msgstr "Innehållsförteckning" -#: sphinx/themes/agogo/layout.html:49 -#: sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 -#: sphinx/themes/basic/search.html:14 +#: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Sök" -#: sphinx/themes/agogo/layout.html:52 -#: sphinx/themes/basic/searchbox.html:15 +#: sphinx/themes/agogo/layout.html:52 sphinx/themes/basic/searchbox.html:15 msgid "Go" msgstr "Gå" -#: sphinx/themes/agogo/layout.html:57 -#: sphinx/themes/basic/searchbox.html:20 +#: sphinx/themes/agogo/layout.html:57 sphinx/themes/basic/searchbox.html:20 msgid "Enter search terms or a module, class or function name." msgstr "Ange sökord eller modul-, klass- eller funktionsnamn." -#: sphinx/themes/agogo/layout.html:78 -#: sphinx/themes/basic/sourcelink.html:14 +#: sphinx/themes/agogo/layout.html:78 sphinx/themes/basic/sourcelink.html:14 msgid "Show Source" msgstr "Visa källfil" @@ -636,8 +613,12 @@ msgstr "Senast uppdaterad %(last_updated)s." #: sphinx/themes/basic/layout.html:189 #, python-format -msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." -msgstr "Skapad med <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." +msgid "" +"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." +msgstr "" +"Skapad med <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." #: sphinx/themes/basic/opensearch.xml:4 #, python-format @@ -660,32 +641,33 @@ msgstr "Nästa titel" msgid "next chapter" msgstr "Nästa kapitel" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "Var god aktivera JavaScript för sökfunktionalitet." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" " function will automatically search for all of the words. Pages\n" " containing fewer words won't appear in the result list." msgstr "" -"Här kan du söka bland dessa dokument. Ange sökord nedan och klicka \"sök\".\n" +"Här kan du söka bland dessa dokument. Ange sökord nedan och klicka " +"\"sök\".\n" " Sökningen måste träffa på samtliga angivna sökord." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "sök" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Sökresultat" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Din sökning gav inga resultat." @@ -725,14 +707,12 @@ msgstr "Förändringar i C-API" msgid "Other changes" msgstr "Övriga förändringar" -#: sphinx/themes/basic/static/doctools.js:154 -#: sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Permalink till denna rubrik" -#: sphinx/themes/basic/static/doctools.js:160 -#: sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Permalink till denna definition" @@ -740,23 +720,27 @@ msgstr "Permalink till denna definition" msgid "Hide Search Matches" msgstr "Dölj Sökresultat" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Söker" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Förbereder sökning..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", i " -#: sphinx/themes/basic/static/searchtools.js:491 -msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." -msgstr "Din sökning gav inga resultat. Kolla stavning och att du valt tillräckligt med kategorier." +#: sphinx/themes/basic/static/searchtools.js:506 +msgid "" +"Your search did not match any documents. Please make sure that all words " +"are spelled correctly and that you've selected enough categories." +msgstr "" +"Din sökning gav inga resultat. Kolla stavning och att du valt " +"tillräckligt med kategorier." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Sökning färdig, hittade %s träffar." @@ -766,7 +750,7 @@ msgid "Expand sidebar" msgstr "Expandera sidolist" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "Dölj sidolist" @@ -774,20 +758,19 @@ msgstr "Dölj sidolist" msgid "Contents" msgstr "Innehåll" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Utgåva" -#: sphinx/writers/latex.py:572 -#: sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Fotnoter" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "fortsättning från föregående sida" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Fortsätter på nästa sida" diff --git a/sphinx/locale/tr/LC_MESSAGES/sphinx.mo b/sphinx/locale/tr/LC_MESSAGES/sphinx.mo index 5054199c4177185f7978deeca2d75ba4d7f43c4e..746669eb2e50f8312497ce16f6e1033e33ec665f 100644 GIT binary patch delta 32 ocmezG_up^BPjOxgT_ZCELqjW5BW(kN$!roo7)>|-muTPv0LMQI8UO$Q delta 32 ocmezG_up^BPjOyTT_XzxBO@y#Q*8sI$!roo7)>_+muTPv0LN7e8~^|S diff --git a/sphinx/locale/tr/LC_MESSAGES/sphinx.po b/sphinx/locale/tr/LC_MESSAGES/sphinx.po index e1e5c1730..030097bf3 100644 --- a/sphinx/locale/tr/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/tr/LC_MESSAGES/sphinx.po @@ -8,23 +8,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.6.2+/6b02a19ccf31\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2009-08-06 22:48+0200\n" -"PO-Revision-Date: 2010-05-28 10:18+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Firat Ozgul <ozgulfirat@gmail.com>\n" "Language-Team: Turkish <kistihza@yahoo.com>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 -#: sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%d %B %Y" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python'u İyileştirme Önerileri!PEP %s" @@ -37,70 +36,66 @@ msgstr "Gömülüler" msgid "Module level" msgstr "Modül düzeyi" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%d %b %Y" -#: sphinx/builders/html.py:285 -#: sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Genel Dizin" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "dizin" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "sonraki" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "önceki" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (şunun içinde: " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Bölümü yazan: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Modülü yazan: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 msgid "Code author: " msgstr "Kodu yazan: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Yazan: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Ayrıca bkz." -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "%s %s" -#: sphinx/domains/c.py:51 -#: sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Parametreler" -#: sphinx/domains/c.py:54 -#: sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Şunu döndürür:" -#: sphinx/domains/c.py:56 -#: sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Dönüş tipi" @@ -129,15 +124,12 @@ msgstr "%s (C tipi)" msgid "%s (C variable)" msgstr "%s (C değişkeni)" -#: sphinx/domains/c.py:171 -#: sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 -#: sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "fonksiyonu" -#: sphinx/domains/c.py:172 -#: sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "öğesi" @@ -145,8 +137,7 @@ msgstr "öğesi" msgid "macro" msgstr "makrosu" -#: sphinx/domains/c.py:174 -#: sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "tipi" @@ -154,203 +145,201 @@ msgstr "tipi" msgid "variable" msgstr "değişkeni" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (C++ sınıfı)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ tipi)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ öğesi)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ fonksiyonu)" -#: sphinx/domains/cpp.py:1030 -#: sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "sınıfı" -#: sphinx/domains/javascript.py:117 -#: sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (gömülü fonksiyon)" -#: sphinx/domains/javascript.py:118 -#: sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s metodu)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (C++ sınıfı)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "%s (global değişken veya sabit)" -#: sphinx/domains/javascript.py:122 -#: sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s niteliği)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 msgid "Arguments" msgstr "Argümanlar" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "Şunu verir: " -#: sphinx/domains/javascript.py:167 -#: sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "verisi" -#: sphinx/domains/javascript.py:168 -#: sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "niteliği" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 msgid "Variables" msgstr "Değişkenler" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Şunu üretir:" -#: sphinx/domains/python.py:222 -#: sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 -#: sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (%s modülü içinde)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (gömülü değişken)" -#: sphinx/domains/python.py:226 -#: sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (%s modülü içinde)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (gömülü sınıf)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (%s içinde bir sınıf)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s metodu)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s statik metodu)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s statik metodu)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s sınıf metodu)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, python-format msgid "%s() (%s class method)" msgstr "%s() (%s sınıf metodu)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s niteliği)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Platformlar:" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (modül)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 msgid "Python Module Index" msgstr "Python Modül Dizini" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "modüller" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Önerilmiyor" -#: sphinx/domains/python.py:500 -#: sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "istisnası" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "metodu" -#: sphinx/domains/python.py:502 -#, python-format +#: sphinx/domains/python.py:528 msgid "class method" msgstr "sınıf metodu" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "statik metodu" -#: sphinx/domains/python.py:505 -#: sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "modülü" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Önerilmiyor" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "%s (yönerge)" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, python-format msgid "%s (role)" msgstr "%s (rol)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "yönergesi" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 msgid "role" msgstr "rolü" -#: sphinx/domains/std.py:68 -#: sphinx/domains/std.py:84 +#: sphinx/domains/std.py:68 sphinx/domains/std.py:84 #, python-format msgid "environment variable; %s" msgstr "çevre değişkeni; %s" @@ -380,15 +369,12 @@ msgstr "çevre değişkeni" msgid "program option" msgstr "program seçeneği" -#: sphinx/domains/std.py:360 -#: sphinx/themes/basic/genindex-single.html:11 +#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11 #: sphinx/themes/basic/genindex-split.html:11 #: sphinx/themes/basic/genindex-split.html:14 -#: sphinx/themes/basic/genindex.html:11 -#: sphinx/themes/basic/genindex.html:14 -#: sphinx/themes/basic/genindex.html:50 -#: sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 +#: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Dizin" @@ -396,17 +382,16 @@ msgstr "Dizin" msgid "Module Index" msgstr "Modül Dizini" -#: sphinx/domains/std.py:362 -#: sphinx/themes/basic/defindex.html:25 +#: sphinx/domains/std.py:362 sphinx/themes/basic/defindex.html:25 msgid "Search Page" msgstr "Arama Sayfası" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Taban: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "şunun takma adı: :class:`%s`" @@ -424,131 +409,125 @@ msgstr "(<<özgün girdi>> %s içinde ve %d satırında bulunuyor.)" msgid "original entry" msgstr "özgün girdi" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "[kaynak]" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "[belgeler]" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 msgid "Module code" msgstr "Modül kodu" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "<h1>%s öğesinin kaynak kodu</h1>" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "Genel bakış: modül kodu" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "<h1>Kodları mevcut bütün modüller</h1>" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Dikkat" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Uyarı" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Tehlike" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Hata" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "İpucu" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Önemli" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Not" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Ayrıca bkz." -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Tüyo" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Uyarı" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "%s sürümüyle geldi" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "%s sürümünde değişti" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "%s sürümünden beri önerilmiyor" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "anahtar kelime" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "işleç" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "nesne" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "deyim" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "gömülü fonksiyon" -#: sphinx/themes/agogo/layout.html:45 -#: sphinx/themes/basic/globaltoc.html:10 +#: sphinx/themes/agogo/layout.html:45 sphinx/themes/basic/globaltoc.html:10 #: sphinx/themes/basic/localtoc.html:11 msgid "Table Of Contents" msgstr "İçindekiler Tablosu" -#: sphinx/themes/agogo/layout.html:49 -#: sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 -#: sphinx/themes/basic/search.html:14 +#: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Ara" -#: sphinx/themes/agogo/layout.html:52 -#: sphinx/themes/basic/searchbox.html:15 +#: sphinx/themes/agogo/layout.html:52 sphinx/themes/basic/searchbox.html:15 msgid "Go" msgstr "Git" -#: sphinx/themes/agogo/layout.html:57 -#: sphinx/themes/basic/searchbox.html:20 +#: sphinx/themes/agogo/layout.html:57 sphinx/themes/basic/searchbox.html:20 msgid "Enter search terms or a module, class or function name." msgstr "Aranacak terimleri veya modül, sınıf ya da fonksiyon adını yazınız" -#: sphinx/themes/agogo/layout.html:78 -#: sphinx/themes/basic/sourcelink.html:14 +#: sphinx/themes/agogo/layout.html:78 sphinx/themes/basic/sourcelink.html:14 msgid "Show Source" msgstr "Kaynağı Göster" @@ -638,8 +617,12 @@ msgstr "Son güncelleme: %(last_updated)s." #: sphinx/themes/basic/layout.html:189 #, python-format -msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." -msgstr "<a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s ile oluşturulmuştur." +msgid "" +"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> " +"%(sphinx_version)s." +msgstr "" +"<a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s ile " +"oluşturulmuştur." #: sphinx/themes/basic/opensearch.xml:4 #, python-format @@ -662,7 +645,7 @@ msgstr "Sonraki konu" msgid "next chapter" msgstr "sonraki bölüm" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." @@ -670,7 +653,7 @@ msgstr "" "Arama işlevini kullanabilmek için lütfen JavaScript'i\n" " etkinleştirin." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -682,16 +665,16 @@ msgstr "" "otomatik olarak bütün kelimeleri arayacaktır. Eksik kelime içeren \n" "sayfalar sonuç listesinde görünmez." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "ara" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Arama Sonuçları" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Arama sonucunda herhangi bir belge bulunamadı." @@ -731,14 +714,12 @@ msgstr "C API'sindeki değişiklikler" msgid "Other changes" msgstr "Diğer değişiklikler" -#: sphinx/themes/basic/static/doctools.js:154 -#: sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Bu başlığın kalıcı bağlantısı" -#: sphinx/themes/basic/static/doctools.js:160 -#: sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Bu tanımın kalıcı bağlantısı" @@ -746,23 +727,27 @@ msgstr "Bu tanımın kalıcı bağlantısı" msgid "Hide Search Matches" msgstr "Arama Sonuçlarını Gizle" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Arıyor" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Aramaya hazırlanıyor..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", şunun içinde: " -#: sphinx/themes/basic/static/searchtools.js:491 -msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." -msgstr "Arama sonucunda hiçbir belge bulunamadı. Bütün kelimeleri doğru yazdığınızdan ve gereken kategorileri seçtiğinizden emin olun." +#: sphinx/themes/basic/static/searchtools.js:506 +msgid "" +"Your search did not match any documents. Please make sure that all words " +"are spelled correctly and that you've selected enough categories." +msgstr "" +"Arama sonucunda hiçbir belge bulunamadı. Bütün kelimeleri doğru " +"yazdığınızdan ve gereken kategorileri seçtiğinizden emin olun." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Arama sonuçlandı, aramayla eşleşen %s sayfa bulundu." @@ -772,7 +757,7 @@ msgid "Expand sidebar" msgstr "Yan çubuğu genişlet" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "Yan çubuğu daralt" @@ -780,20 +765,19 @@ msgstr "Yan çubuğu daralt" msgid "Contents" msgstr "İçindekiler" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Sürüm" -#: sphinx/writers/latex.py:572 -#: sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "Dipnotları" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "önceki sayfadan devam" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "Devamı sonraki sayfada" diff --git a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo index cfa0b8e0efe73f83c7eb1be3fdb67aec79d38246..18f4d8d6d75542f06f381cc24a8a55257c63f47d 100644 GIT binary patch delta 33 ocmaDJ`aE=lsTi+?u92C7p`n$jk+y-sW+yQlMn==k_2L=A0I@I$J^%m! delta 33 ocmaDJ`aE=lsTi-Ru92yNfq|8=iMD~!W+yQlMn;p(_2L=A0I<3VIRF3v diff --git a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po index a85eaebe4..81c21d782 100644 --- a/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Sphinx 0.6\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-12-28 23:40+0100\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Petro Sasnyk <petro@sasnyk.name>\n" "Language-Team: uk_UA <LL@li.org>\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " @@ -16,15 +16,15 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python Enhancement Proposals!PEP %s" @@ -37,67 +37,67 @@ msgstr "Вбудовані елементи" msgid "Module level" msgstr "Рівень модуля" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%b %d, %Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Загальний індекс" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "індекс" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "наступний" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "попередній" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr " (в " -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Автор секції: " -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "Автор модуля: " -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "Автор модуля: " -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "Автор: " -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "Дивись також" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "Параметри" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "Повертає" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "Тип повернення" @@ -126,12 +126,12 @@ msgstr "%s (C тип)" msgid "%s (C variable)" msgstr "%s (C змінна)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "функція" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "член" @@ -139,7 +139,7 @@ msgstr "член" msgid "macro" msgstr "макрос" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "тип" @@ -148,191 +148,201 @@ msgstr "тип" msgid "variable" msgstr "Змінна" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" msgstr "%s (C++ клас)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ тип)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ член)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ функція)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "клас" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (вбудована функція)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s метод)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (C++ клас)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s атрибут)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "Параметри" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 -#, python-format +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "атрибут" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "Змінна" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "Викликає" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (в модулі %s)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (вбудована змінна)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s (в модулі %s)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (вбудований клас)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "%s (клас в %s)" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s метод)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s статичний метод)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s статичний метод)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s метод)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s метод)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s атрибут)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "Платформи: " -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (модуль)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "Індекс модулів" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "модулі" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "Застарілий" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "виняткова ситуація" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s метод)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "статичний метод" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "модуль" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "Застарілий" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (модуль)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "модуль" @@ -372,7 +382,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "Індекс" @@ -384,12 +394,12 @@ msgstr "Індекс модулів" msgid "Search Page" msgstr "Сторінка пошуку" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr " Базовий: %s" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "синонім :class:`%s`" @@ -407,104 +417,104 @@ msgstr "(Початкове входження знаходиться в %s, р msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "модуль" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "Увага" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "Застереження" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "Небезпека" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "Помилка" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "Підказка" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "Важливо" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "Примітка" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "Дивись також" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "Порада" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "Попередження" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "Нове в версії %s" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "Змінено в версії %s" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "Застаріло починаючи з версії %s" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "ключове слово" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "оператор" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "об'єкт" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "вираз" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "вбудована функція" @@ -514,7 +524,7 @@ msgid "Table Of Contents" msgstr "Зміст" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "Пошук" @@ -644,7 +654,7 @@ msgstr "Наступна тема" msgid "next chapter" msgstr "наступний розділ" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." @@ -653,7 +663,7 @@ msgstr "" "\"\n" "\" пошук." -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -665,16 +675,16 @@ msgstr "" " пошуку автоматично шукатиме за всіма словами. Сторінки\n" " що містять менше слів не з'являться в результуючому списку." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "пошук" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "Результати пошуку" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "Ваш пошук не виявив жодних співпадінь." @@ -714,12 +724,12 @@ msgstr "зміни C API" msgid "Other changes" msgstr "Інші зміни" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "Постійне посилання на цей заголовок" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "Постійне посилання на це визначення" @@ -727,19 +737,19 @@ msgstr "Постійне посилання на це визначення" msgid "Hide Search Matches" msgstr "Приховати співпадіння пошуку" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "Шукаю" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "Підготовка до пошуку..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", в " -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." @@ -747,7 +757,7 @@ msgstr "" "Ваш пошук не виявив жодного співпадіння. Будь-ласка переконайтеся що всі " "слова набрані правильно і ви обрали достатньо категорій." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "Пошук закінчено, знайдено %s сторінок які співпали з пошуковим запитом." @@ -757,7 +767,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -765,19 +775,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "Реліз" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 #, fuzzy msgid "Continued on next page" msgstr "Повний індекс на одній сторінці" @@ -785,3 +795,4 @@ msgstr "Повний індекс на одній сторінці" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "" + diff --git a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo index b62c4c5b9daf83c1605836ce30bd5c65157e2b40..11818de5ac0e8f0b0748980d9fbd6063bd6924f8 100644 GIT binary patch delta 37 tcmdn&ve{*Wycn;Au92C7p`n$jk+y-sWF4`WOr~6u_i$Ql_7{J`1pv$R3i1E| delta 37 tcmdn&ve{*Wycn;ku92yNfq|8=iMD~!WF4`WOeS2D_i$Ql_7{J`1pv!w3he*@ diff --git a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po index c17620439..b9dce0852 100644 --- a/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po @@ -9,22 +9,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.6\n" "Report-Msgid-Bugs-To: zhutao.iscas@gmail.com\n" "POT-Creation-Date: 2009-03-09 19:46+0120\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Tower Joo<zhutao.iscas@gmail.com>\n" "Language-Team: cn <LL@li.org>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%Y 年 %m 月 %d 日" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python 建议文件!PEP %s" @@ -37,67 +37,67 @@ msgstr "内置" msgid "Module level" msgstr "模块级别" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%Y 年 %m 月 %d 日" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "总目录" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "索引" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "下一页" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "上一页" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Section 作者:" -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "模块作者:" -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "模块作者:" -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "作者:" -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "也可以参考" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "参数" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "返回" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "返回类型" @@ -126,12 +126,12 @@ msgstr "%s (C 类型)" msgid "%s (C variable)" msgstr "%s (C 变量)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "函数" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "成员" @@ -139,7 +139,7 @@ msgstr "成员" msgid "macro" msgstr "" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "" @@ -148,190 +148,201 @@ msgstr "" msgid "variable" msgstr "变量" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, fuzzy, python-format msgid "%s (C++ class)" msgstr "%s (內置类)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ 类型)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ 成员)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ 函数)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (內置函数)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s 方法)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (內置类)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s 属性)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "参数" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "属性" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "变量" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "引发" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (在 %s 模块中)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (內置变量)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s() (在 %s 模块中)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (內置类)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s 方法)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s 静态方法)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s 静态方法)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s 方法)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s 方法)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s 属性)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "平台" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (模块)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "模块索引" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "模块" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "已移除" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "例外" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s 方法)" -#: sphinx/domains/python.py:503 +#: sphinx/domains/python.py:529 msgid "static method" msgstr "静态方法" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "模块" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "已移除" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (模块)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "模块" @@ -371,7 +382,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "索引" @@ -383,12 +394,12 @@ msgstr "模块索引" msgid "Search Page" msgstr "搜索页面" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "" @@ -406,104 +417,104 @@ msgstr "(最初的入口位于%s 的第%d行" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "模块" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "注意" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "警告" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "危险" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "错误" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "提示" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "重要" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "注解" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "也可以参考" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "小技巧" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "警告" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "%s 新版功能" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "在 %s 版更改" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "%s 版后已移除" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "关键字" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "操作数" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "对象" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "语句" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "內置函数" @@ -513,7 +524,7 @@ msgid "Table Of Contents" msgstr "內容目录" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "搜索" @@ -641,13 +652,13 @@ msgstr "下一个主题" msgid "next chapter" msgstr "下一章" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "" -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -655,16 +666,16 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "在这儿,你可以对这些文档进行搜索。向搜索框中输入你所要搜索的关键字并点击\"搜索\"。注意:搜索引擎会自动搜索所有的关键字。将不会搜索到部分关键字的页面." -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "搜索" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "搜索结果" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "你的搜索没有找到任何的结果." @@ -704,12 +715,12 @@ msgstr "C API 更改" msgid "Other changes" msgstr "其他更改" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "永久链接至标题" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "永久链接至目标" @@ -717,25 +728,25 @@ msgstr "永久链接至目标" msgid "Hide Search Matches" msgstr "隐藏搜索结果" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "搜索中" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "准备搜索..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr ", 位于" -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "你的搜索没有匹配到任何文档。请确认你所搜索的关键字." -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "搜索完成, 找到了 %s 页匹配所搜索的关键字" @@ -745,7 +756,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -753,19 +764,19 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "发布" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 #, fuzzy msgid "Continued on next page" msgstr "一页的全部索引" @@ -773,3 +784,4 @@ msgstr "一页的全部索引" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[图片]" + diff --git a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo index 008ecec05d4a97dbfe0550c13da8e4635b61034d..d58c16ff08200e5d4d744384b83e65cddc0ebb29 100644 GIT binary patch delta 32 ocmaFn`pk8Ml^Cyuu92C7p`n$jk+y-sWOuPAjHa7)#CLH60IARkqyPW_ delta 32 ocmaFn`pk8Ml^Cz7u92yNfq|8=iMD~!WOuPAj3%3P#CLH60I6OHp8x;= diff --git a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po index 33dff8cae..cb2646ce8 100644 --- a/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po @@ -8,22 +8,22 @@ msgstr "" "Project-Id-Version: Sphinx 0.5\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2008-11-09 19:46+0100\n" -"PO-Revision-Date: 2010-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-08-26 11:45+0000\n" "Last-Translator: Fred Lin <gasolin@gmail.com>\n" "Language-Team: tw <LL@li.org>\n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 0.9.4\n" +"Generated-By: Babel 0.9.5\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:111 sphinx/writers/latex.py:185 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" msgstr "%Y 年 %m 月 %d 日" -#: sphinx/roles.py:174 +#: sphinx/roles.py:173 #, python-format msgid "Python Enhancement Proposals!PEP %s" msgstr "Python 建議文件!PEP %s" @@ -36,67 +36,67 @@ msgstr "" msgid "Module level" msgstr "" -#: sphinx/builders/html.py:266 +#: sphinx/builders/html.py:260 #, python-format msgid "%b %d, %Y" msgstr "%Y 年 %m 月 %d 日" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:279 sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "總索引" -#: sphinx/builders/html.py:285 +#: sphinx/builders/html.py:279 msgid "index" msgstr "索引" -#: sphinx/builders/html.py:345 +#: sphinx/builders/html.py:339 msgid "next" msgstr "下一頁" -#: sphinx/builders/html.py:354 +#: sphinx/builders/html.py:348 msgid "previous" msgstr "上一頁" -#: sphinx/builders/latex.py:151 +#: sphinx/builders/latex.py:145 msgid " (in " msgstr "" -#: sphinx/directives/other.py:127 +#: sphinx/directives/other.py:135 msgid "Section author: " msgstr "Section 作者:" -#: sphinx/directives/other.py:129 +#: sphinx/directives/other.py:137 msgid "Module author: " msgstr "模組作者:" -#: sphinx/directives/other.py:131 +#: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " msgstr "模組作者:" -#: sphinx/directives/other.py:133 +#: sphinx/directives/other.py:141 msgid "Author: " msgstr "作者:" -#: sphinx/directives/other.py:238 +#: sphinx/directives/other.py:213 msgid "See also" msgstr "" -#: sphinx/domains/__init__.py:253 +#: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" msgstr "" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" msgstr "參數" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 -#: sphinx/domains/python.py:59 +#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:127 +#: sphinx/domains/python.py:104 msgid "Returns" msgstr "返回" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 sphinx/domains/python.py:106 msgid "Return type" msgstr "返回類別" @@ -125,13 +125,12 @@ msgstr "%s (C 類別)" msgid "%s (C variable)" msgstr "%s (C 變數)" -#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031 -#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497 -#, python-format +#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1039 +#: sphinx/domains/javascript.py:161 sphinx/domains/python.py:523 msgid "function" msgstr "函式" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1040 msgid "member" msgstr "成員" @@ -139,7 +138,7 @@ msgstr "成員" msgid "macro" msgstr "巨集" -#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033 +#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" msgstr "類別" @@ -148,192 +147,201 @@ msgstr "類別" msgid "variable" msgstr "變數" -#: sphinx/domains/cpp.py:876 +#: sphinx/domains/cpp.py:883 #, fuzzy, python-format msgid "%s (C++ class)" msgstr "%s (內建類別)" -#: sphinx/domains/cpp.py:891 +#: sphinx/domains/cpp.py:898 #, python-format msgid "%s (C++ type)" msgstr "%s (C++ 類別)" -#: sphinx/domains/cpp.py:910 +#: sphinx/domains/cpp.py:917 #, python-format msgid "%s (C++ member)" msgstr "%s (C++ 成員)" -#: sphinx/domains/cpp.py:962 +#: sphinx/domains/cpp.py:969 #, python-format msgid "%s (C++ function)" msgstr "%s (C++ 函式)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 +#: sphinx/domains/python.py:525 msgid "class" msgstr "" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format msgid "%s() (built-in function)" msgstr "%s() (內建函式)" -#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285 +#: sphinx/domains/javascript.py:106 sphinx/domains/python.py:308 #, python-format msgid "%s() (%s method)" msgstr "%s() (%s 方法)" -#: sphinx/domains/javascript.py:120 +#: sphinx/domains/javascript.py:108 +#, fuzzy, python-format +msgid "%s() (class)" +msgstr "%s (內建類別)" + +#: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" msgstr "" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format msgid "%s (%s attribute)" msgstr "%s (%s 屬性)" -#: sphinx/domains/javascript.py:131 +#: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" msgstr "參數" -#: sphinx/domains/javascript.py:134 +#: sphinx/domains/javascript.py:124 msgid "Throws" msgstr "" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" msgstr "" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 -#, python-format +#: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" msgstr "屬性" -#: sphinx/domains/python.py:53 +#: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" msgstr "變數" -#: sphinx/domains/python.py:56 +#: sphinx/domains/python.py:101 msgid "Raises" msgstr "" -#: sphinx/domains/python.py:222 sphinx/domains/python.py:279 -#: sphinx/domains/python.py:291 sphinx/domains/python.py:304 +#: sphinx/domains/python.py:245 sphinx/domains/python.py:302 +#: sphinx/domains/python.py:314 sphinx/domains/python.py:327 #, python-format msgid "%s() (in module %s)" msgstr "%s() (在 %s 模組中)" -#: sphinx/domains/python.py:225 +#: sphinx/domains/python.py:248 #, python-format msgid "%s (built-in variable)" msgstr "%s (內建變數)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:249 sphinx/domains/python.py:340 #, python-format msgid "%s (in module %s)" msgstr "%s() (在 %s 模組中)" -#: sphinx/domains/python.py:242 +#: sphinx/domains/python.py:265 #, python-format msgid "%s (built-in class)" msgstr "%s (內建類別)" -#: sphinx/domains/python.py:243 +#: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" msgstr "" -#: sphinx/domains/python.py:283 +#: sphinx/domains/python.py:306 #, python-format msgid "%s() (%s.%s method)" msgstr "%s() (%s.%s 方法)" -#: sphinx/domains/python.py:295 +#: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" msgstr "%s() (%s.%s 靜態方法)" -#: sphinx/domains/python.py:298 +#: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" msgstr "%s() (%s 靜態方法)" -#: sphinx/domains/python.py:308 +#: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" msgstr "%s() (%s.%s 方法)" -#: sphinx/domains/python.py:311 +#: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" msgstr "%s() (%s 方法)" -#: sphinx/domains/python.py:321 +#: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" msgstr "%s (%s.%s 屬性)" -#: sphinx/domains/python.py:366 +#: sphinx/domains/python.py:392 msgid "Platforms: " msgstr "平台" -#: sphinx/domains/python.py:372 +#: sphinx/domains/python.py:398 #, python-format msgid "%s (module)" msgstr "%s (模組)" -#: sphinx/domains/python.py:429 +#: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" msgstr "模組索引" -#: sphinx/domains/python.py:430 +#: sphinx/domains/python.py:456 msgid "modules" msgstr "模組" -#: sphinx/domains/python.py:475 +#: sphinx/domains/python.py:501 msgid "Deprecated" msgstr "已移除" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:526 sphinx/locale/__init__.py:178 msgid "exception" msgstr "例外" -#: sphinx/domains/python.py:501 +#: sphinx/domains/python.py:527 msgid "method" msgstr "" -#: sphinx/domains/python.py:502 -#, fuzzy, python-format +#: sphinx/domains/python.py:528 +#, fuzzy msgid "class method" msgstr "%s() (%s 方法)" -#: sphinx/domains/python.py:503 -#, python-format +#: sphinx/domains/python.py:529 msgid "static method" msgstr "靜態方法" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:531 sphinx/locale/__init__.py:174 msgid "module" msgstr "模組" -#: sphinx/domains/rst.py:53 +#: sphinx/domains/python.py:657 +#, fuzzy +msgid " (deprecated)" +msgstr "已移除" + +#: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" msgstr "" -#: sphinx/domains/rst.py:55 +#: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" msgstr "%s (模組)" -#: sphinx/domains/rst.py:103 +#: sphinx/domains/rst.py:106 msgid "directive" msgstr "" -#: sphinx/domains/rst.py:104 +#: sphinx/domains/rst.py:107 #, fuzzy msgid "role" msgstr "模組" @@ -373,7 +381,7 @@ msgstr "" #: sphinx/themes/basic/genindex-split.html:14 #: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14 #: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125 -#: sphinx/writers/latex.py:173 +#: sphinx/writers/latex.py:174 msgid "Index" msgstr "索引" @@ -385,12 +393,12 @@ msgstr "模組索引" msgid "Search Page" msgstr "搜尋頁面" -#: sphinx/ext/autodoc.py:917 +#: sphinx/ext/autodoc.py:923 #, python-format msgid " Bases: %s" msgstr "" -#: sphinx/ext/autodoc.py:950 +#: sphinx/ext/autodoc.py:959 #, python-format msgid "alias of :class:`%s`" msgstr "" @@ -408,104 +416,104 @@ msgstr "" msgid "original entry" msgstr "" -#: sphinx/ext/viewcode.py:66 +#: sphinx/ext/viewcode.py:70 msgid "[source]" msgstr "" -#: sphinx/ext/viewcode.py:109 +#: sphinx/ext/viewcode.py:117 msgid "[docs]" msgstr "" -#: sphinx/ext/viewcode.py:123 +#: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" msgstr "模組" -#: sphinx/ext/viewcode.py:129 +#: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" msgstr "" -#: sphinx/ext/viewcode.py:156 +#: sphinx/ext/viewcode.py:164 msgid "Overview: module code" msgstr "" -#: sphinx/ext/viewcode.py:157 +#: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" msgstr "" -#: sphinx/locale/__init__.py:139 +#: sphinx/locale/__init__.py:155 msgid "Attention" msgstr "注意" -#: sphinx/locale/__init__.py:140 +#: sphinx/locale/__init__.py:156 msgid "Caution" msgstr "警示" -#: sphinx/locale/__init__.py:141 +#: sphinx/locale/__init__.py:157 msgid "Danger" msgstr "危險" -#: sphinx/locale/__init__.py:142 +#: sphinx/locale/__init__.py:158 msgid "Error" msgstr "錯誤" -#: sphinx/locale/__init__.py:143 +#: sphinx/locale/__init__.py:159 msgid "Hint" msgstr "提示" -#: sphinx/locale/__init__.py:144 +#: sphinx/locale/__init__.py:160 msgid "Important" msgstr "重要" -#: sphinx/locale/__init__.py:145 +#: sphinx/locale/__init__.py:161 msgid "Note" msgstr "註解" -#: sphinx/locale/__init__.py:146 +#: sphinx/locale/__init__.py:162 msgid "See Also" msgstr "" -#: sphinx/locale/__init__.py:147 +#: sphinx/locale/__init__.py:163 msgid "Tip" msgstr "小技巧" -#: sphinx/locale/__init__.py:148 +#: sphinx/locale/__init__.py:164 msgid "Warning" msgstr "警告" -#: sphinx/locale/__init__.py:152 +#: sphinx/locale/__init__.py:168 #, python-format msgid "New in version %s" msgstr "%s 版新功能" -#: sphinx/locale/__init__.py:153 +#: sphinx/locale/__init__.py:169 #, python-format msgid "Changed in version %s" msgstr "在 %s 版改變" -#: sphinx/locale/__init__.py:154 +#: sphinx/locale/__init__.py:170 #, python-format msgid "Deprecated since version %s" msgstr "%s 版後已移除" -#: sphinx/locale/__init__.py:159 +#: sphinx/locale/__init__.py:175 msgid "keyword" msgstr "關鍵字" -#: sphinx/locale/__init__.py:160 +#: sphinx/locale/__init__.py:176 msgid "operator" msgstr "運算子" -#: sphinx/locale/__init__.py:161 +#: sphinx/locale/__init__.py:177 msgid "object" msgstr "物件" -#: sphinx/locale/__init__.py:163 +#: sphinx/locale/__init__.py:179 msgid "statement" msgstr "" -#: sphinx/locale/__init__.py:164 +#: sphinx/locale/__init__.py:180 msgid "built-in function" msgstr "內建函式" @@ -515,7 +523,7 @@ msgid "Table Of Contents" msgstr "內容目錄" #: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128 -#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14 +#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:20 msgid "Search" msgstr "搜尋" @@ -644,13 +652,13 @@ msgstr "下一個主題" msgid "next chapter" msgstr "下一章" -#: sphinx/themes/basic/search.html:18 +#: sphinx/themes/basic/search.html:24 msgid "" "Please activate JavaScript to enable the search\n" " functionality." msgstr "" -#: sphinx/themes/basic/search.html:23 +#: sphinx/themes/basic/search.html:29 msgid "" "From here you can search these documents. Enter your search\n" " words into the box below and click \"search\". Note that the search\n" @@ -658,16 +666,16 @@ msgid "" " containing fewer words won't appear in the result list." msgstr "" -#: sphinx/themes/basic/search.html:30 +#: sphinx/themes/basic/search.html:36 msgid "search" msgstr "搜尋" -#: sphinx/themes/basic/search.html:34 -#: sphinx/themes/basic/static/searchtools.js:489 +#: sphinx/themes/basic/search.html:40 +#: sphinx/themes/basic/static/searchtools.js:504 msgid "Search Results" msgstr "搜尋結果" -#: sphinx/themes/basic/search.html:36 +#: sphinx/themes/basic/search.html:42 msgid "Your search did not match any results." msgstr "" @@ -707,12 +715,12 @@ msgstr "C API 改變" msgid "Other changes" msgstr "其他改變:" -#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482 -#: sphinx/writers/html.py:487 +#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:491 +#: sphinx/writers/html.py:496 msgid "Permalink to this headline" msgstr "" -#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87 +#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:88 msgid "Permalink to this definition" msgstr "" @@ -720,25 +728,25 @@ msgstr "" msgid "Hide Search Matches" msgstr "" -#: sphinx/themes/basic/static/searchtools.js:285 +#: sphinx/themes/basic/static/searchtools.js:287 msgid "Searching" msgstr "搜尋中" -#: sphinx/themes/basic/static/searchtools.js:290 +#: sphinx/themes/basic/static/searchtools.js:292 msgid "Preparing search..." msgstr "準備搜尋..." -#: sphinx/themes/basic/static/searchtools.js:364 +#: sphinx/themes/basic/static/searchtools.js:366 msgid ", in " msgstr "" -#: sphinx/themes/basic/static/searchtools.js:491 +#: sphinx/themes/basic/static/searchtools.js:506 msgid "" "Your search did not match any documents. Please make sure that all words " "are spelled correctly and that you've selected enough categories." msgstr "" -#: sphinx/themes/basic/static/searchtools.js:493 +#: sphinx/themes/basic/static/searchtools.js:508 #, python-format msgid "Search finished, found %s page(s) matching the search query." msgstr "" @@ -748,7 +756,7 @@ msgid "Expand sidebar" msgstr "" #: sphinx/themes/default/static/sidebar.js:79 -#: sphinx/themes/default/static/sidebar.js:106 +#: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" msgstr "" @@ -756,22 +764,23 @@ msgstr "" msgid "Contents" msgstr "" -#: sphinx/writers/latex.py:171 +#: sphinx/writers/latex.py:172 msgid "Release" msgstr "釋出" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:576 sphinx/writers/manpage.py:178 msgid "Footnotes" msgstr "" -#: sphinx/writers/latex.py:641 +#: sphinx/writers/latex.py:649 msgid "continued from previous page" msgstr "" -#: sphinx/writers/latex.py:646 +#: sphinx/writers/latex.py:654 msgid "Continued on next page" msgstr "" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[圖片]" + From ad759f54c19ef29aa5def6169e80bc6b6887fba8 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Thu, 26 Aug 2010 12:01:12 +0000 Subject: [PATCH 389/744] #392: deprecate "centered". --- doc/markup/para.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/markup/para.rst b/doc/markup/para.rst index 52a5019b6..42bcc33cc 100644 --- a/doc/markup/para.rst +++ b/doc/markup/para.rst @@ -112,6 +112,10 @@ units as well as normal text: .. centered:: LICENSE AGREEMENT + .. deprecated:: 1.1 + This presentation-only directive is a legacy from older versions. Use a + :rst:dir:`rst-class` directive instead and add an appropriate style. + .. rst:directive:: hlist From ef870346deece9077cc5635e2c9c764d12c92a01 Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Fri, 27 Aug 2010 01:02:49 +0200 Subject: [PATCH 390/744] Document internationalization mechanisms. --- doc/builders.rst | 6 +-- doc/config.rst | 110 +++++++++++++++++++++++++------------------- doc/intl.rst | 62 +++++++++++++++++++++++-- doc/invocation.rst | 3 ++ doc/translation.png | Bin 0 -> 40683 bytes 5 files changed, 127 insertions(+), 54 deletions(-) create mode 100644 doc/translation.png diff --git a/doc/builders.rst b/doc/builders.rst index 7d182d068..8ea46ad62 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -223,10 +223,10 @@ Note that a direct PDF builder using ReportLab is available in `rst2pdf .. module:: sphinx.builders.intl .. class:: MessageCatalogBuilder - This builder produces a message catalog file for each reST file or - subdirectory. + This builder produces gettext-style message catalos. Each top-level file or + subdirectory grows a single ``.pot`` catalog template. - See the documentation on :ref:`internationalization <intl>` for further reference. + See the documentation on :ref:`intl` for further reference. Its name is ``gettext``. diff --git a/doc/config.rst b/doc/config.rst index 3115c401c..d5f5cbe58 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -143,20 +143,6 @@ General configuration .. deprecated:: 1.0 Use :confval:`exclude_patterns` instead. -.. confval:: locale_dirs - - .. versionadded:: 0.5 - - Directories in which to search for additional Sphinx message catalogs (see - :confval:`language`), relative to the source directory. The directories on - this path are searched by the standard :mod:`gettext` module for a text - domain of ``sphinx``; so if you add the directory :file:`./locale` to this - settting, the message catalogs (compiled from ``.po`` format using - :program:`msgfmt`) must be in - :file:`./locale/{language}/LC_MESSAGES/sphinx.mo`. - - The default is ``[]``. - .. confval:: templates_path A list of paths that contain extra templates (or templates that overwrite @@ -272,40 +258,6 @@ Project information If you don't need the separation provided between :confval:`version` and :confval:`release`, just set them both to the same value. -.. confval:: language - - The code for the language the docs are written in. Any text automatically - generated by Sphinx will be in that language. Also, in the LaTeX builder, a - suitable language will be selected as an option for the *Babel* package. - Default is ``None``, which means that no translation will be done. - - .. versionadded:: 0.5 - - Currently supported languages are: - - * ``bn`` -- Bengali - * ``ca`` -- Catalan - * ``cs`` -- Czech - * ``da`` -- Danish - * ``de`` -- German - * ``en`` -- English - * ``es`` -- Spanish - * ``fi`` -- Finnish - * ``fr`` -- French - * ``hr`` -- Croatian - * ``it`` -- Italian - * ``lt`` -- Lithuanian - * ``nl`` -- Dutch - * ``pl`` -- Polish - * ``pt_BR`` -- Brazilian Portuguese - * ``ru`` -- Russian - * ``sl`` -- Slovenian - * ``sv`` -- Swedish - * ``tr`` -- Turkish - * ``uk_UA`` -- Ukrainian - * ``zh_CN`` -- Simplified Chinese - * ``zh_TW`` -- Traditional Chinese - .. confval:: today today_fmt @@ -381,6 +333,68 @@ Project information .. versionadded:: 1.0 +.. _intl-options: + +Options for internationalization +-------------------------------- + +These options influence Sphinx' *Native Language Support*. See the +documentation on :ref:`intl` for details. + +.. confval:: language + + The code for the language the docs are written in. Any text automatically + generated by Sphinx will be in that language. Also, Sphinx will try to + substitute individual paragraphs from your documents with the translation + sets obtained from :confval:`locale_dirs`. In the LaTeX builder, a suitable + language will be selected as an option for the *Babel* package. Default is + ``None``, which means that no translation will be done. + + .. versionadded:: 0.5 + + Currently supported languages by Sphinx are: + + * ``bn`` -- Bengali + * ``ca`` -- Catalan + * ``cs`` -- Czech + * ``da`` -- Danish + * ``de`` -- German + * ``en`` -- English + * ``es`` -- Spanish + * ``fi`` -- Finnish + * ``fr`` -- French + * ``hr`` -- Croatian + * ``it`` -- Italian + * ``lt`` -- Lithuanian + * ``nl`` -- Dutch + * ``pl`` -- Polish + * ``pt_BR`` -- Brazilian Portuguese + * ``ru`` -- Russian + * ``sl`` -- Slovenian + * ``sv`` -- Swedish + * ``tr`` -- Turkish + * ``uk_UA`` -- Ukrainian + * ``zh_CN`` -- Simplified Chinese + * ``zh_TW`` -- Traditional Chinese + +.. confval:: locale_dirs + + .. versionadded:: 0.5 + + Directories in which to search for additional message catalogs (see + :confval:`language`), relative to the source directory. The directories on + this path are searched by the standard :mod:`gettext` module. + + Internal messages are fetched from a text domain of ``sphinx``; so if you + add the directory :file:`./locale` to this settting, the message catalogs + (compiled from ``.po`` format using :program:`msgfmt`) must be in + :file:`./locale/{language}/LC_MESSAGES/sphinx.mo`. The text domain of + individual documents depends on their docname if they are top-level project + files and on their base directory otherwise. + + The default is ``[]``. + + .. _html-options: Options for HTML output diff --git a/doc/intl.rst b/doc/intl.rst index b60367675..b5abf9d80 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -7,7 +7,63 @@ Internationalization Complementary to translations provided for Sphinx-generated messages such as navigation bars, Sphinx provides mechanisms facilitating *document* translations -in itself. It relies on the existing configuration values :confval:`language` -and :confval:`locale_dirs`. +in itself. See the :ref:`intl-options` for details on configuration. -.. XXX write more! +.. figure:: translation.png + :width: 100% + + Workflow visualization of translations in Sphinx. [1]_ + +**gettext** [2]_ is an established standard for internationalization and +localization. It naïvely maps messages in a program to a translated string. +Sphinx uses these facilities to translate whole documents. + +Initially project maintainers have to collect all translatable strings (also +referred to as *messages*) to make them known to translators. Sphinx extracts +these through invocation of ``sphinx-build -b gettext``. + +Every single element in the doctree will end up in a single message which +results in lists being equally split into different chunks while large +paragraphs will remain as coarsely-grained as they were in the original +document. This grants seamless document updates while still providing a little +bit of context for translators in free-text passages. It is the maintainer's +task to split up paragraphs which are too large as there is no sane automated +way to do that. + +After Sphinx successfully ran the +:class:`~sphinx.builders.intl.MessageCatalogBuilder` you will find a collection +of ``.pot`` files in your output directory. These are **catalog templates** +and contain messages in your original language *only*. + +They can be delivered to translators which will transform them to ``.po`` files +--- so called **message catalogs** --- containing a mapping from the original +messages to foreign-language strings. + +Gettext compiles them into a binary format known as **binary catalogs** through +:program:`msgfmt` for efficiency reasons. If you make these files discoverable +with :confval:`locale_dirs` for your :confval:`language`, Sphinx will pick them +up automatically. + +An example: you have a document ``usage.rst`` in your Sphinx project. The +gettext builder will put its messages into ``usage.pot``. Image you have +Spanish translations [3]_ on your hands in ``usage.po`` --- for your builds to +be translated you need to follow these instructions: + +* Compile your message catalog to a locale directory, say ``translated``, so it + ends up in ``./translated/es/LC_MESSAGES/usage.mo`` in your source directory + (where ``es`` is the language code for Spanish.) :: + + msgfmt "usage.po" -o "translated/es/LC_MESSAGES/usage.mo" + +* Set :confval:`locale_dirs` to ``["translated/"]``. +* Set :confval:`language` to ``es`` (also possible via :option:`-D`). +* Run your desired build. + + +.. rubric:: Footnotes + +.. [1] The stick-figure is taken from an `XKCD comic <http://xkcd.com/779/>`_. +.. [2] See the `GNU gettext utilites + <http://www.gnu.org/software/gettext/manual/gettext.html#Introduction>`_ + for details on that software suite. +.. [3] Because nobody expects the Spanish Inquisition! diff --git a/doc/invocation.rst b/doc/invocation.rst index 6b8b9ee35..e143a3233 100644 --- a/doc/invocation.rst +++ b/doc/invocation.rst @@ -43,6 +43,9 @@ The :program:`sphinx-build` script has several options: **text** Build plain text files. + **gettext** + Build gettext-style message catalogs (``.pot`` files). + **doctest** Run all doctests in the documentation, if the :mod:`~sphinx.ext.doctest` extension is enabled. diff --git a/doc/translation.png b/doc/translation.png new file mode 100644 index 0000000000000000000000000000000000000000..aa368b67e29f1728f6aa9d63dc3b8bb78df9b471 GIT binary patch literal 40683 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 Obmr=7C(O3m{l5U?Dt|Tr literal 0 HcmV?d00001 From 3c2160e2c1c49935aaaaa67d3f5d683da347fc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Wed, 1 Sep 2010 16:27:22 +0200 Subject: [PATCH 391/744] Added Heapkeeper to the examples --- EXAMPLES | 1 + 1 file changed, 1 insertion(+) diff --git a/EXAMPLES b/EXAMPLES index 778b235e1..b5fd96af4 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -25,6 +25,7 @@ Documentation using the default theme * gevent: http://www.gevent.org/ * Google Wave API: http://wave-robot-python-client.googlecode.com/svn/trunk/pydocs/index.html * GSL Shell: http://www.nongnu.org/gsl-shell/ +* Heapkeeper: http://heapkeeper.org/ * Hedge: http://documen.tician.de/hedge/ * Kaa: http://doc.freevo.org/api/kaa/ * MeshPy: http://documen.tician.de/meshpy/ From fee6b38142e88dd7caf2074254b6117bf0f4504c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Fri, 3 Sep 2010 17:10:22 +0200 Subject: [PATCH 392/744] Remove trailing whitespace --- doc/config.rst | 2 +- doc/intl.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/config.rst b/doc/config.rst index e72bfdf46..906a9e199 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -384,7 +384,7 @@ documentation on :ref:`intl` for details. Directories in which to search for additional message catalogs (see :confval:`language`), relative to the source directory. The directories on this path are searched by the standard :mod:`gettext` module. - + Internal messages are fetched from a text domain of ``sphinx``; so if you add the directory :file:`./locale` to this settting, the message catalogs (compiled from ``.po`` format using :program:`msgfmt`) must be in diff --git a/doc/intl.rst b/doc/intl.rst index b5abf9d80..89cfcd2c8 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -37,7 +37,7 @@ and contain messages in your original language *only*. They can be delivered to translators which will transform them to ``.po`` files --- so called **message catalogs** --- containing a mapping from the original -messages to foreign-language strings. +messages to foreign-language strings. Gettext compiles them into a binary format known as **binary catalogs** through :program:`msgfmt` for efficiency reasons. If you make these files discoverable From 8f2f4db5ac87fe66aec481f345120dac9721f4c0 Mon Sep 17 00:00:00 2001 From: Martin Brochhaus <mbrochh@gmail.com> Date: Tue, 7 Sep 2010 16:06:06 +0200 Subject: [PATCH 393/744] Minor changes to make coverage extension more robust against exceptions and false positives.. --- sphinx/ext/coverage.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index f41820e2a..5bcd12b2c 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -166,7 +166,8 @@ class CoverageBuilder(Builder): if exp.match(name): break else: - if full_name not in objects: + if (full_name not in objects + and not obj.__doc__): # not documented at all classes[name] = [] continue @@ -174,16 +175,21 @@ class CoverageBuilder(Builder): attrs = [] for attr_name in dir(obj): + if attr_name not in obj.__dict__: + continue attr = getattr(obj, attr_name) - for attr_name, attr in inspect.getmembers( - obj, lambda x: inspect.ismethod(x) or \ - inspect.isfunction(x)): + if (not inspect.ismethod(attr) + and not inspect.isfunction(attr)): + continue if attr_name[0] == '_': # starts with an underscore, ignore it continue full_attr_name = '%s.%s' % (full_name, attr_name) if full_attr_name not in objects: + if not self._is_func_undocumented( + obj, attr_name): + continue attrs.append(attr_name) if attrs: @@ -192,13 +198,26 @@ class CoverageBuilder(Builder): self.py_undoc[mod_name] = {'funcs': funcs, 'classes': classes} + def _is_func_undocumented(self, obj, attr_name): + """Last check looking at the source code. Is function really not documented?""" + obj_source = inspect.getsource(obj) or '' + obj_source = obj_source.replace(' ', '').replace('\n', '') + if not "def%s" % attr_name in obj_source: + # Funktion is not defined in this class. No documentation needed. + return False + m = re.search('def%s\([^\)]*\):"""' %attr_name, obj_source) + if not m: + return True + else: + return False + def write_py_coverage(self): output_file = path.join(self.outdir, 'python.txt') op = open(output_file, 'w') failed = [] try: - write_header(op, 'Undocumented Python objects', '=') - + if self.config.coverage_write_headline: + write_header(op, 'Undocumented Python objects', '=') keys = self.py_undoc.keys() keys.sort() for name in keys: @@ -248,3 +267,4 @@ def setup(app): app.add_config_value('coverage_c_path', [], False) app.add_config_value('coverage_c_regexes', {}, False) app.add_config_value('coverage_ignore_c_items', {}, False) + app.add_config_value('coverage_write_headline', {}, False) \ No newline at end of file From b857b46be95c4eecf3f7dacb32d5c64a7d911a4e Mon Sep 17 00:00:00 2001 From: Martin Brochhaus <mbrochh@gmail.com> Date: Tue, 7 Sep 2010 16:22:34 +0200 Subject: [PATCH 394/744] Removed a rather confusing method by a simpler call to .__doc__ It turned out that at this point we can safely check for .__doc__ wihtout producing exceptions or false positives. --- sphinx/ext/coverage.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index 5bcd12b2c..8bff22487 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -187,8 +187,7 @@ class CoverageBuilder(Builder): full_attr_name = '%s.%s' % (full_name, attr_name) if full_attr_name not in objects: - if not self._is_func_undocumented( - obj, attr_name): + if len(obj.__doc__) > 0: continue attrs.append(attr_name) @@ -198,19 +197,6 @@ class CoverageBuilder(Builder): self.py_undoc[mod_name] = {'funcs': funcs, 'classes': classes} - def _is_func_undocumented(self, obj, attr_name): - """Last check looking at the source code. Is function really not documented?""" - obj_source = inspect.getsource(obj) or '' - obj_source = obj_source.replace(' ', '').replace('\n', '') - if not "def%s" % attr_name in obj_source: - # Funktion is not defined in this class. No documentation needed. - return False - m = re.search('def%s\([^\)]*\):"""' %attr_name, obj_source) - if not m: - return True - else: - return False - def write_py_coverage(self): output_file = path.join(self.outdir, 'python.txt') op = open(output_file, 'w') From 72e865e0ca5e5a00700005ece92bf8970513ad80 Mon Sep 17 00:00:00 2001 From: Jonathan Waltman <jonathan.waltman@gmail.com> Date: Thu, 16 Sep 2010 02:06:48 -0500 Subject: [PATCH 395/744] Add Texinfo builder and writer --- sphinx/builders/texinfo.py | 229 +++++++ sphinx/writers/texinfo.py | 1230 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1459 insertions(+) create mode 100644 sphinx/builders/texinfo.py create mode 100644 sphinx/writers/texinfo.py diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py new file mode 100644 index 000000000..d65a46810 --- /dev/null +++ b/sphinx/builders/texinfo.py @@ -0,0 +1,229 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.texinfo + ~~~~~~~~~~~~~~~~~~~~~~~ + + Texinfo builder. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +from os import path + +from docutils import nodes +from docutils.io import FileOutput +from docutils.utils import new_document +from docutils.frontend import OptionParser + +from sphinx import package_dir, addnodes +from sphinx.locale import _ +from sphinx.builders import Builder +from sphinx.environment import NoUri +from sphinx.util.nodes import inline_all_toctrees +from sphinx.util.osutil import SEP, copyfile +from sphinx.util.console import bold, darkgreen +from sphinx.writers.texinfo import TexinfoWriter + + +TEXINFO_MAKEFILE = '''\ +# Makefile for Sphinx Texinfo output + +infodir ?= /usr/share/info + +MAKEINFO = makeinfo --no-split +MAKEINFO_html = makeinfo --no-split --html +MAKEINFO_plaintext = makeinfo --no-split --plaintext +TEXI2PDF = texi2pdf --batch --expand +INSTALL_INFO = install-info + +ALLDOCS = $(basename $(wildcard *.texi)) + +all: info +info: $(addsuffix .info,$(ALLDOCS)) +plaintext: $(addsuffix .txt,$(ALLDOCS)) +html: $(addsuffix .html,$(ALLDOCS)) +pdf: $(addsuffix .pdf,$(ALLDOCS)) + +install-info: info +\tfor f in *.info; do \\ +\t cp -t $(infodir) "$$f" && \\ +\t $(INSTALL_INFO) --info-dir=$(infodir) "$$f" ;\\ +\tdone + +uninstall-info: info +\tfor f in *.info; do \\ +\t rm -f "$(infodir)/$$f" ;\\ +\t $(INSTALL_INFO) --delete --info-dir=$(infodir) "$$f" ;\\ +\tdone + +%.info: %.texi +\t$(MAKEINFO) -o '$@' '$<' + +%.txt: %.texi +\t$(MAKEINFO_plaintext) -o '$@' '$<' + +%.html: %.texi +\t$(MAKEINFO_html) -o '$@' '$<' + +%.pdf: %.texi +\t-$(TEXI2PDF) '$<' +\t-$(TEXI2PDF) '$<' +\t-$(TEXI2PDF) '$<' + +clean: +\t-rm -f *.info *.pdf *.txt *.html +\t-rm -f *.log *.ind *.aux *.toc *.syn *.idx *.out *.ilg *.pla *.ky *.pg +\t-rm -f *.vr *.tp *.fn *.fns *.def *.defs *.cp *.cps *.ge *.ges *.mo + +.PHONY: all info plaintext html pdf install-info uninstall-info clean +''' + + +class TexinfoBuilder(Builder): + """ + Builds Texinfo output to create Info. + """ + name = 'texinfo' + format = 'texinfo' + supported_image_types = [] + + def init(self): + self.docnames = [] + self.document_data = [] + + def get_outdated_docs(self): + return 'all documents' # for now + + def get_target_uri(self, docname, typ=None): + if docname not in self.docnames: + raise NoUri + else: + return '%' + docname + + def get_relative_uri(self, from_, to, typ=None): + # ignore source path + return self.get_target_uri(to, typ) + + def init_document_data(self): + preliminary_document_data = map(list, self.config.texinfo_documents) + if not preliminary_document_data: + self.warn('no "texinfo_documents" config value found; no documents ' + 'will be written') + return + # assign subdirs to titles + self.titles = [] + for entry in preliminary_document_data: + docname = entry[0] + if docname not in self.env.all_docs: + self.warn('"texinfo_documents" config value references unknown ' + 'document %s' % docname) + continue + self.document_data.append(entry) + if docname.endswith(SEP+'index'): + docname = docname[:-5] + self.titles.append((docname, entry[2])) + + def write(self, *ignored): + self.init_document_data() + for entry in self.document_data: + docname, targetname, title, author = entry[:4] + targetname += '.texi' + direntry = description = category = '' + if len(entry) > 6: + direntry, description, category = entry[4:7] + toctree_only = False + if len(entry) > 7: + toctree_only = entry[7] + destination = FileOutput( + destination_path=path.join(self.outdir, targetname), + encoding='utf-8') + self.info("processing " + targetname + "... ", nonl=1) + doctree = self.assemble_doctree(docname, toctree_only, + appendices=(self.config.texinfo_appendices or [])) + self.info("writing... ", nonl=1) + + # Add an Index section + if self.config.texinfo_domain_indices: + doctree.append( + nodes.section('', + nodes.title(_("Index"), + nodes.Text(_('Index'), + _('Index'))), + nodes.raw('@printindex ge\n', + nodes.Text('@printindex ge\n', + '@printindex ge\n'), + format="texinfo"))) + docwriter = TexinfoWriter(self) + settings = OptionParser( + defaults=self.env.settings, + components=(docwriter,)).get_default_values() + settings.author = author + settings.title = title + settings.texinfo_filename = targetname[:-5] + '.info' + settings.texinfo_elements = self.config.texinfo_elements + settings.texinfo_dir_entry = direntry or '' + settings.texinfo_dir_category = category or '' + settings.texinfo_dir_description = description or '' + settings.docname = docname + doctree.settings = settings + docwriter.write(doctree, destination) + self.info("done") + + def assemble_doctree(self, indexfile, toctree_only, appendices): + self.docnames = set([indexfile] + appendices) + self.info(darkgreen(indexfile) + " ", nonl=1) + tree = self.env.get_doctree(indexfile) + tree['docname'] = indexfile + if toctree_only: + # extract toctree nodes from the tree and put them in a + # fresh document + new_tree = new_document('<texinfo output>') + new_sect = nodes.section() + new_sect += nodes.title(u'<Set title in conf.py>', + u'<Set title in conf.py>') + new_tree += new_sect + for node in tree.traverse(addnodes.toctree): + new_sect += node + tree = new_tree + largetree = inline_all_toctrees(self, self.docnames, indexfile, tree, + darkgreen) + largetree['docname'] = indexfile + for docname in appendices: + appendix = self.env.get_doctree(docname) + appendix['docname'] = docname + largetree.append(appendix) + self.info() + self.info("resolving references...") + self.env.resolve_references(largetree, indexfile, self) + # TODO: add support for external :ref:s + for pendingnode in largetree.traverse(addnodes.pending_xref): + docname = pendingnode['refdocname'] + sectname = pendingnode['refsectname'] + newnodes = [nodes.emphasis(sectname, sectname)] + for subdir, title in self.titles: + if docname.startswith(subdir): + newnodes.append(nodes.Text(_(' (in '), _(' (in '))) + newnodes.append(nodes.emphasis(title, title)) + newnodes.append(nodes.Text(')', ')')) + break + else: + pass + pendingnode.replace_self(newnodes) + return largetree + + def finish(self): + self.info(bold('copying Texinfo support files... '), nonl=True) + # copy Makefile + fn = path.join(self.outdir, 'Makefile') + self.info(fn, nonl=1) + try: + mkfile = open(fn, 'w') + try: + mkfile.write(TEXINFO_MAKEFILE) + finally: + mkfile.close() + except (IOError, OSError), err: + self.warn("error writing file %s: %s" % (fn, err)) + self.info(' done') diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py new file mode 100644 index 000000000..4065bd727 --- /dev/null +++ b/sphinx/writers/texinfo.py @@ -0,0 +1,1230 @@ +# -*- coding: utf-8 -*- +""" + sphinx.writers.texinfo + ~~~~~~~~~~~~~~~~~~~~~~ + + Custom docutils writer for Texinfo. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +import os.path + +import docutils +import docutils.utils + +from docutils import nodes, writers, transforms +from docutils.transforms import writer_aux + +from sphinx import addnodes +from sphinx.locale import admonitionlabels, versionlabels + + +TEMPLATE = """\ +\\input texinfo @c -*-texinfo-*- +@c %%**start of header +@setfilename %(filename)s +@documentencoding UTF-8 +@copying +Generated by Sphinx +@end copying +@settitle %(title)s +@defindex ge +@paragraphindent %(paragraphindent)s +@exampleindent %(exampleindent)s +%(direntry)s +@c %%**end of header + +@titlepage +@title %(title)s +@author %(author)s +@end titlepage +@contents + +@c %%** start of user preamble +%(preamble)s +@c %%** end of user preamble + +@ifnottex +@node Top,,(DIR),(DIR) +@top %(title)s +@end ifnottex + +@c %%**start of body +%(body)s +@c %%**end of body +@bye +""" + + +def find_subsections(section): + """Return a list of subsections for the given ``section``.""" + result = [] + for child in section.children: + if isinstance(child, nodes.section): + result.append(child) + continue + result.extend(find_subsections(child)) + return result + + +## Escaping +# Which characters to escape depends on the context. In some cases, +# namely menus and node names, it's not possible to escape certain +# characters. + +def escape(s): + """Return a string with Texinfo command characters escaped.""" + s = s.replace('@', '@@') + s = s.replace('{', '@{') + s = s.replace('}', '@}') + # Prevent "--" from being converted to an "em dash" + # s = s.replace('-', '@w{-}') + return s + +def escape_arg(s): + """Return an escaped string suitable for use as an argument + to a Texinfo command.""" + s = escape(s) + # Commas are the argument delimeters + s = s.replace(',', '@comma{}') + # Normalize white space + s = ' '.join(s.split()).strip() + return s + +def escape_id(s): + """Return an escaped string suitable for node names, menu entries, + and xrefs anchors.""" + bad_chars = ',:.()@{}' + for bc in bad_chars: + s = s.replace(bc, ' ') + s = ' '.join(s.split()).strip() + return s + + +class TexinfoWriter(writers.Writer): + """Texinfo writer for generating Texinfo documents.""" + supported = ('texinfo', 'texi') + + settings_spec = ( + 'Texinfo Specific Options', + None, + ( + ("Name of the resulting Info file to be created by 'makeinfo'. " + "Should probably end with '.info'.", + ['--texinfo-filename'], + {'default': '', 'metavar': '<file>'}), + + ('Specify the Info dir entry category.', + ['--texinfo-dir-category'], + {'default': 'Miscellaneous', 'metavar': '<name>'}), + + ('The name to use for the Info dir entry. ' + 'If not provided, no entry will be created.', + ['--texinfo-dir-entry'], + {'default': '', 'metavar': '<name>'}), + + ('A brief description (one or two lines) to use for the ' + 'Info dir entry.', + ['--texinfo-dir-description'], + {'default': '', 'metavar': '<desc>'}), + ) + ) + + settings_defaults = {} + + output = None + + visitor_attributes = ('output', 'fragment') + + def __init__(self, builder): + writers.Writer.__init__(self) + self.builder = builder + + def get_transforms(self): + trans = writers.Writer.get_transforms(self) + return trans + [writer_aux.Admonitions] + + def translate(self): + self.visitor = visitor = TexinfoTranslator(self.document, self.builder) + self.document.walkabout(visitor) + visitor.finish() + for attr in self.visitor_attributes: + setattr(self, attr, getattr(visitor, attr)) + + +class TexinfoTranslator(nodes.NodeVisitor): + + default_elements = { + 'filename': '', + 'title': '', + 'paragraphindent': 2, + 'exampleindent': 4, + 'direntry': '', + 'preamble': '', + 'body': '', + } + + def __init__(self, document, builder): + nodes.NodeVisitor.__init__(self, document) + self.builder = builder + self.init_settings() + + self.written_ids = set() # node names and anchors in output + self.referenced_ids = set() # node names and anchors that should + # be in output + self.node_names = {} # node name --> node's name to display + self.node_menus = {} # node name --> node's menu entries + self.rellinks = {} # node name --> (next, previous, up) + + self.collect_node_names() + self.collect_node_menus() + self.collect_rellinks() + + self.short_ids = {} + self.body = [] + self.previous_section = None + self.section_level = 0 + self.seen_title = False + self.next_section_targets = [] + self.escape_newlines = 0 + self.curfilestack = [] + + def finish(self): + while self.referenced_ids: + # Handle xrefs with missing anchors + r = self.referenced_ids.pop() + if r not in self.written_ids: + self.document.reporter.info( + "Unknown cross-reference target: `%s'" % r) + self.add_text('@anchor{%s}@w{%s}\n' % (r, ' ' * 30)) + self.fragment = ''.join(self.body).strip() + '\n' + self.elements['body'] = self.fragment + self.output = TEMPLATE % self.elements + + + ## Helper routines + + def init_settings(self): + settings = self.settings = self.document.settings + elements = self.elements = self.default_elements.copy() + elements.update({ + # if empty, the title is set to the first section title + 'title': settings.title, + 'author': escape_arg(settings.author), + 'filename': settings.texinfo_filename, + }) + # Title + title = elements['title'] + if not title: + title = self.document.next_node(nodes.title) + title = (title and title.astext()) or '<untitled>' + elements['title'] = escape_id(title) or '<untitled>' + # Direntry + if settings.texinfo_dir_entry: + elements['direntry'] = ('@dircategory %s\n' + '@direntry\n' + '* %s: (%s). %s\n' + '@end direntry\n') % ( + escape_id(settings.texinfo_dir_category), + escape_id(settings.texinfo_dir_entry), + elements['filename'], + escape_arg(settings.texinfo_dir_description)) + # allow the user to override them all + elements.update(settings.texinfo_elements) + + def collect_node_names(self): + """Generates a unique id for each section. + + Assigns the attribute ``node_name`` to each section.""" + self.document['node_name'] = 'Top' + self.node_names['Top'] = 'Top' + self.written_ids.update(('Top', 'top')) + + for section in self.document.traverse(nodes.section): + title = section.next_node(nodes.Titular) + name = (title and title.astext()) or '<untitled>' + node_id = name = escape_id(name) or '<untitled>' + assert node_id and name + nth, suffix = 1, '' + while (node_id + suffix).lower() in self.written_ids: + nth += 1 + suffix = '<%s>' % nth + node_id += suffix + assert node_id not in self.node_names + assert node_id not in self.written_ids + assert node_id.lower() not in self.written_ids + section['node_name'] = node_id + self.node_names[node_id] = name + self.written_ids.update((node_id, node_id.lower())) + + def collect_node_menus(self): + """Collect the menu entries for each "node" section.""" + node_menus = self.node_menus + for node in ([self.document] + + self.document.traverse(nodes.section)): + assert 'node_name' in node and node['node_name'] + entries = tuple(s['node_name'] + for s in find_subsections(node)) + node_menus[node['node_name']] = entries + # Try to find a suitable "Top" node + title = self.document.next_node(nodes.title) + top = (title and title.parent) or self.document + assert isinstance(top, (nodes.document, nodes.section)) + if top is not self.document: + entries = node_menus[top['node_name']] + entries += node_menus['Top'][1:] + node_menus['Top'] = entries + del node_menus[top['node_name']] + top['node_name'] = 'Top' + + def collect_rellinks(self): + """Collect the relative links (next, previous, up) for each "node".""" + rellinks = self.rellinks + node_menus = self.node_menus + for id, entries in node_menus.items(): + rellinks[id] = ['', '', ''] + # Up's + for id, entries in node_menus.items(): + for e in entries: + rellinks[e][2] = id + # Next's and prev's + for id, entries in node_menus.items(): + for i, id in enumerate(entries): + # First child's prev is empty + if i != 0: + rellinks[id][1] = entries[i-1] + # Last child's next is empty + if i != len(entries) - 1: + rellinks[id][0] = entries[i+1] + # Top's next is its first child + try: + first = node_menus['Top'][0] + except IndexError: + pass + else: + rellinks['Top'][0] = first + rellinks[first][1] = 'Top' + + def add_text(self, text, fresh=False): + """Add some text to the output. + + Optional argument ``fresh`` means to insert a newline before + the text if the last character out was not a newline.""" + if fresh: + if self.body and not self.body[-1].endswith('\n'): + self.body.append('\n') + self.body.append(text) + + def rstrip(self): + """Strip trailing whitespace from the current output.""" + while self.body and not self.body[-1].strip(): + del self.body[-1] + if not self.body: + return + self.body[-1] = self.body[-1].rstrip() + + def add_menu_entries(self, entries): + for entry in entries: + name = self.node_names[entry] + if name == entry: + self.add_text('* %s::\n' % name, fresh=1) + else: + self.add_text('* %s: %s.\n' % (name, entry), fresh=1) + + def add_menu(self, section, master=False): + entries = self.node_menus[section['node_name']] + if not entries: + return + self.add_text('\n@menu\n') + self.add_menu_entries(entries) + if master: + # Write the "detailed menu" + started_detail = False + for entry in entries: + subentries = self.node_menus[entry] + if not subentries: + continue + if not started_detail: + started_detail = True + self.add_text('\n@detailmenu\n' + ' --- The Detailed Node Listing ---\n') + self.add_text('\n%s\n\n' % self.node_names[entry]) + self.add_menu_entries(subentries) + if started_detail: + self.rstrip() + self.add_text('\n@end detailmenu\n') + self.rstrip() + self.add_text('\n@end menu\n\n') + + + ## xref handling + + def get_short_id(self, id): + """Return a shorter 'id' associated with ``id``.""" + # Shorter ids improve paragraph filling in places + # that the id is hidden by Emacs. + try: + sid = self.short_ids[id] + except KeyError: + sid = hex(len(self.short_ids))[2:] + self.short_ids[id] = sid + return sid + + def add_anchor(self, id, msg_node=None): + # Anchors can be referenced by their original id + # or by the generated shortened id + id = escape_id(id).lower() + ids = (self.get_short_id(id), id) + for id in ids: + if id not in self.written_ids: + self.add_text('@anchor{%s}' % id) + self.written_ids.add(id) + + def add_xref(self, ref, name, node): + ref = self.get_short_id(escape_id(ref).lower()) + name = ' '.join(name.split()).strip() + if not name or ref == name: + self.add_text('@pxref{%s}' % ref) + else: + self.add_text('@pxref{%s,%s}' % (ref, name)) + self.referenced_ids.add(ref) + + ## Visiting + + def visit_document(self, node): + pass + def depart_document(self, node): + pass + + def visit_Text(self, node): + s = escape(node.astext()) + if self.escape_newlines: + s = s.replace('\n', ' ') + self.add_text(s) + def depart_Text(self, node): + pass + + def visit_section(self, node): + self.next_section_targets.extend(node.get('ids', [])) + if not self.seen_title: + return + if self.previous_section: + self.add_menu(self.previous_section) + else: + self.add_menu(self.document, master=True) + + node_name = node['node_name'] + pointers = tuple([node_name] + self.rellinks[node_name]) + self.add_text('\n@node %s,%s,%s,%s\n' % pointers) + if node_name != node_name.lower(): + self.add_text('@anchor{%s}' % node_name.lower()) + for id in self.next_section_targets: + self.add_anchor(id, node) + + self.next_section_targets = [] + self.previous_section = node + self.section_level += 1 + + def depart_section(self, node): + self.section_level -= 1 + + headings = ( + '@unnumbered', + '@chapter', + '@section', + '@subsection', + '@subsubsection', + ) + + rubrics = ( + '@heading', + '@subheading', + '@subsubheading', + ) + + def visit_title(self, node): + if not self.seen_title: + self.seen_title = 1 + raise nodes.SkipNode + parent = node.parent + if isinstance(parent, nodes.table): + return + if isinstance(parent, nodes.Admonition): + raise nodes.SkipNode + elif isinstance(parent, nodes.sidebar): + self.visit_rubric(node) + elif isinstance(parent, nodes.topic): + raise nodes.SkipNode + elif not isinstance(parent, nodes.section): + self.document.reporter.warning( + 'encountered title node not in section, topic, table, ' + 'admonition or sidebar', base_node=node) + self.visit_rubric(node) + else: + try: + heading = self.headings[self.section_level] + except IndexError: + heading = self.headings[-1] + self.add_text('%s ' % heading, fresh=1) + + def depart_title(self, node): + self.add_text('', fresh=1) + + def visit_rubric(self, node): + try: + rubric = self.rubrics[self.section_level] + except IndexError: + rubric = self.rubrics[-1] + self.add_text('%s ' % rubric, fresh=1) + def depart_rubric(self, node): + self.add_text('', fresh=1) + + def visit_subtitle(self, node): + self.add_text('\n\n@noindent\n') + def depart_subtitle(self, node): + self.add_text('\n\n') + + ## References + + def visit_target(self, node): + if node.get('ids'): + self.add_anchor(node['ids'][0], node) + elif node.get('refid'): + # Section targets need to go after the start of the section. + next = node.next_node(ascend=1, siblings=1) + while isinstance(next, nodes.target): + next = next.next_node(ascend=1, siblings=1) + if isinstance(next, nodes.section): + self.next_section_targets.append(node['refid']) + return + self.add_anchor(node['refid'], node) + elif node.get('refuri'): + pass + else: + self.document.reporter.error("Unknown target type: %r" % node) + + def visit_reference(self, node): + if isinstance(node.parent, nodes.title): + return + if isinstance(node[0], nodes.image): + return + if isinstance(node.parent, addnodes.desc_type): + return + name = node.get('name', node.astext()).strip() + if node.get('refid'): + self.add_xref(escape_id(node['refid']), + escape_id(name), node) + raise nodes.SkipNode + if not node.get('refuri'): + self.document.reporter.error("Unknown reference type: %s" % node) + return + uri = node['refuri'] + if uri.startswith('#'): + self.add_xref(escape_id(uri[1:]), escape_id(name), node) + elif uri.startswith('%'): + id = uri[1:] + if '#' in id: + src, id = uri[1:].split('#', 1) + assert '#' not in id + self.add_xref(escape_id(id), escape_id(name), node) + elif uri.startswith('mailto:'): + uri = escape_arg(uri[7:]) + name = escape_arg(name) + if not name or name == uri: + self.add_text('@email{%s}' % uri) + else: + self.add_text('@email{%s,%s}' % (uri, name)) + elif uri.startswith('info:'): + uri = uri[5:].replace('_', ' ') + uri = escape_arg(uri) + id = 'Top' + if '#' in uri: + uri, id = uri.split('#', 1) + id = escape_id(id) + name = escape_id(name) + if name == id: + self.add_text('@pxref{%s,,,%s}' % (id, uri)) + else: + self.add_text('@pxref{%s,,%s,%s}' % (id, name, uri)) + else: + uri = escape_arg(uri) + name = escape_arg(name) + if not name or uri == name: + self.add_text('@indicateurl{%s}' % uri) + else: + self.add_text('@uref{%s,%s}' % (uri, name)) + raise nodes.SkipNode + + def depart_reference(self, node): + pass + + def visit_title_reference(self, node): + text = node.astext() + self.add_text('@cite{%s}' % escape_arg(text)) + raise nodes.SkipNode + def visit_title_reference(self, node): + pass + + ## Blocks + + def visit_paragraph(self, node): + if 'continued' in node or isinstance(node.parent, nodes.compound): + self.add_text('@noindent\n', fresh=1) + def depart_paragraph(self, node): + self.add_text('\n\n') + + def visit_block_quote(self, node): + self.rstrip() + self.add_text('\n\n@quotation\n') + def depart_block_quote(self, node): + self.rstrip() + self.add_text('\n@end quotation\n\n') + + def visit_literal_block(self, node): + self.rstrip() + self.add_text('\n\n@example\n') + def depart_literal_block(self, node): + self.rstrip() + self.add_text('\n@end example\n\n' + '@noindent\n') + + visit_doctest_block = visit_literal_block + depart_doctest_block = depart_literal_block + + def visit_line_block(self, node): + self.add_text('@display\n', fresh=1) + def depart_line_block(self, node): + self.add_text('@end display\n', fresh=1) + + def visit_line(self, node): + self.rstrip() + self.add_text('\n') + self.escape_newlines += 1 + def depart_line(self, node): + self.add_text('@w{ }\n') + self.escape_newlines -= 1 + + ## Inline + + def visit_strong(self, node): + self.add_text('@strong{') + def depart_strong(self, node): + self.add_text('}') + + def visit_emphasis(self, node): + self.add_text('@emph{') + def depart_emphasis(self, node): + self.add_text('}') + + def visit_literal(self, node): + self.add_text('@code{') + def depart_literal(self, node): + self.add_text('}') + + def visit_superscript(self, node): + self.add_text('@w{^') + def depart_superscript(self, node): + self.add_text('}') + + def visit_subscript(self, node): + self.add_text('@w{[') + def depart_subscript(self, node): + self.add_text(']}') + + ## Footnotes + + def visit_footnote(self, node): + self.visit_block_quote(node) + def depart_footnote(self, node): + self.depart_block_quote(node) + + def visit_footnote_reference(self, node): + self.add_text('@w{(') + def depart_footnote_reference(self, node): + self.add_text(')}') + + visit_citation = visit_footnote + depart_citation = depart_footnote + + def visit_citation_reference(self, node): + self.add_text('@w{[') + def depart_citation_reference(self, node): + self.add_text(']}') + + ## Lists + + def visit_bullet_list(self, node): + bullet = node.get('bullet', '*') + self.rstrip() + self.add_text('\n\n@itemize %s\n' % bullet) + def depart_bullet_list(self, node): + self.rstrip() + self.add_text('\n@end itemize\n\n') + + def visit_enumerated_list(self, node): + # Doesn't support Roman numerals + enum = node.get('enumtype', 'arabic') + starters = {'arabic': '', + 'loweralpha': 'a', + 'upperalpha': 'A',} + start = node.get('start', starters.get(enum, '')) + self.rstrip() + self.add_text('\n\n@enumerate %s\n' % start) + def depart_enumerated_list(self, node): + self.rstrip() + self.add_text('\n@end enumerate\n\n') + + def visit_list_item(self, node): + self.rstrip() + self.add_text('\n@item\n') + def depart_list_item(self, node): + pass + + ## Option List + + def visit_option_list(self, node): + self.add_text('\n@table @option\n') + def depart_option_list(self, node): + self.rstrip() + self.add_text('\n@end table\n\n') + + def visit_option_list_item(self, node): + pass + def depart_option_list_item(self, node): + pass + + def visit_option_group(self, node): + self.at_item_x = '@item' + def depart_option_group(self, node): + pass + + def visit_option(self, node): + self.add_text(self.at_item_x + ' ', fresh=1) + self.at_item_x = '@itemx' + def depart_option(self, node): + pass + + def visit_option_string(self, node): + pass + def depart_option_string(self, node): + pass + + def visit_option_argument(self, node): + self.add_text(node.get('delimiter', ' ')) + def depart_option_argument(self, node): + pass + + def visit_description(self, node): + self.add_text('', fresh=1) + def depart_description(self, node): + pass + + ## Definitions + + def visit_definition_list(self, node): + self.add_text('\n@table @asis\n') + def depart_definition_list(self, node): + self.rstrip() + self.add_text('\n@end table\n\n') + + def visit_definition_list_item(self, node): + self.at_item_x = '@item' + def depart_definition_list_item(self, node): + pass + + def visit_term(self, node): + if node.get('ids') and node['ids'][0]: + self.add_anchor(node['ids'][0], node) + self.add_text(self.at_item_x + ' ', fresh=1) + self.at_item_x = '@itemx' + def depart_term(self, node): + pass + + def visit_classifier(self, node): + self.add_text(' : ') + def depart_classifier(self, node): + pass + + def visit_definition(self, node): + self.add_text('', fresh=1) + def depart_definition(self, node): + pass + + ## Tables + + def visit_table(self, node): + self.entry_sep = '@item' + def depart_table(self, node): + self.rstrip() + self.add_text('\n@end multitable\n\n') + + def visit_tabular_col_spec(self, node): + pass + def depart_tabular_col_spec(self, node): + pass + + def visit_colspec(self, node): + self.colwidths.append(node['colwidth']) + if len(self.colwidths) != self.n_cols: + return + self.add_text('@multitable ', fresh=1) + for i, n in enumerate(self.colwidths): + self.add_text('{%s} ' %('x' * (n+2))) + def depart_colspec(self, node): + pass + + def visit_tgroup(self, node): + self.colwidths = [] + self.n_cols = node['cols'] + def depart_tgroup(self, node): + pass + + def visit_thead(self, node): + self.entry_sep = '@headitem' + def depart_thead(self, node): + pass + + def visit_tbody(self, node): + pass + def depart_tbody(self, node): + pass + + def visit_row(self, node): + pass + def depart_row(self, node): + self.entry_sep = '@item' + + def visit_entry(self, node): + self.rstrip() + self.add_text('\n%s ' % self.entry_sep) + self.entry_sep = '@tab' + def depart_entry(self, node): + for i in xrange(node.get('morecols', 0)): + self.add_text('@tab\n', fresh=1) + self.add_text('', fresh=1) + + ## Field Lists + + def visit_field_list(self, node): + self.add_text('\n@itemize @w\n') + def depart_field_list(self, node): + self.rstrip() + self.add_text('\n@end itemize\n\n') + + def visit_field(self, node): + if not isinstance(node.parent, nodes.field_list): + self.visit_field_list(None) + def depart_field(self, node): + if not isinstance(node.parent, nodes.field_list): + self.depart_field_list(None) + + def visit_field_name(self, node): + self.add_text('@item ', fresh=1) + def depart_field_name(self, node): + self.add_text(':') + + def visit_field_body(self, node): + self.add_text('', fresh=1) + def depart_field_body(self, node): + pass + + ## Docinfo + + def visit_docinfo(self, node): + pass + def depart_docinfo(self, node): + self.add_text('\n\n') + + def visit_authors(self, node): + self.author_level = 0 + self.add_text('@*Authors: ', fresh=1) + def depart_authors(self, node): + self.add_text('@*\n') + + def visit_author(self, node): + if isinstance(node.parent, nodes.authors): + if self.author_level > 0: + self.add_text(', ') + self.author_level += 1 + else: + self.add_text('@*Author: ', fresh=1) + def depart_author(self, node): + if not isinstance(node.parent, nodes.authors): + self.add_text('\n') + + def _make_visit_docinfo_field(typ): + def visit_docinfo_field(self, node): + self.add_text('@*%s: ' % typ.capitalize(), fresh=1) + return visit_docinfo_field + + visit_organization = _make_visit_docinfo_field('organization') + visit_address = _make_visit_docinfo_field('address') + visit_contact = _make_visit_docinfo_field('contact') + visit_version = _make_visit_docinfo_field('version') + visit_revision = _make_visit_docinfo_field('revision') + visit_status = _make_visit_docinfo_field('status') + visit_date = _make_visit_docinfo_field('date') + + visit_copyright = visit_block_quote + depart_copyright = depart_block_quote + + ## Admonitions + + def visit_admonition(self, node): + title = escape(node[0].astext()) + self.add_text('\n@cartouche\n' + '@quotation %s\n' % title) + def depart_admonition(self, node): + self.rstrip() + self.add_text('\n@end quotation\n' + '@end cartouche\n\n') + + def _make_visit_admonition(typ): + def visit_admonition(self, node): + self.add_text('\n@cartouche\n' + '@quotation %s\n' % escape(typ)) + return visit_admonition + + visit_attention = _make_visit_admonition('Attention') + visit_caution = _make_visit_admonition('Caution') + visit_danger = _make_visit_admonition('Danger') + visit_error = _make_visit_admonition('Error') + visit_important = _make_visit_admonition('Important') + visit_note = _make_visit_admonition('Note') + visit_tip = _make_visit_admonition('Tip') + visit_hint = _make_visit_admonition('Hint') + visit_warning = _make_visit_admonition('Warning') + + depart_attention = depart_admonition + depart_caution = depart_admonition + depart_danger = depart_admonition + depart_error = depart_admonition + depart_important = depart_admonition + depart_note = depart_admonition + depart_tip = depart_admonition + depart_hint = depart_admonition + depart_warning = depart_admonition + + def visit_topic(self, node): + # Ignore TOC's since we have to have a "menu" anyway + if 'contents' in node.get('classes', []): + raise nodes.SkipNode + title = node[0] + self.visit_rubric(title) + self.add_text('%s\n' % escape(title.astext())) + self.visit_block_quote(node) + def depart_topic(self, node): + self.depart_block_quote(node) + + def visit_generated(self, node): + raise nodes.SkipNode + def depart_generated(self, node): + pass + + def visit_transition(self, node): + self.add_text('\n\n@noindent\n' + '@exdent @w{%s}\n\n' + '@noindent\n' % ('_' * 70)) + def depart_transition(self, node): + pass + + def visit_attribution(self, node): + self.add_text('@flushright\n', fresh=1) + def depart_attribution(self, node): + self.add_text('@end flushright\n', fresh=1) + + def visit_raw(self, node): + format = node.get('format', '').split() + if 'texinfo' in format or 'texi' in format: + self.add_text(node.astext()) + raise nodes.SkipNode + def depart_raw(self, node): + pass + + def visit_figure(self, node): + self.add_text('\n@float Figure\n') + def depart_figure(self, node): + self.rstrip() + self.add_text('\n@end float\n\n') + + def visit_caption(self, node): + if not isinstance(node.parent, nodes.figure): + self.document.reporter.warning('Caption not inside a figure.', + base_node=node) + return + self.add_text('@caption{', fresh=1) + def depart_caption(self, node): + if isinstance(node.parent, nodes.figure): + self.rstrip() + self.add_text('}\n') + + def visit_image(self, node): + self.add_text('@w{[image]}') + raise nodes.SkipNode + def depart_image(self, node): + pass + + def visit_compound(self, node): + pass + def depart_compound(self, node): + pass + + def visit_sidebar(self, node): + pass + def depart_sidebar(self, node): + pass + + def visit_label(self, node): + self.add_text('@w{(') + def depart_label(self, node): + self.add_text(')} ') + + def visit_legend(self, node): + pass + def depart_legend(self, node): + pass + + def visit_substitution_reference(self, node): + pass + def depart_substitution_reference(self, node): + pass + + def visit_substitution_definition(self, node): + raise nodes.SkipNode + def depart_substitution_definition(self, node): + pass + + def visit_system_message(self, node): + self.add_text('\n@format\n' + '---------- SYSTEM MESSAGE -----------\n') + def depart_system_message(self, node): + self.rstrip() + if node.get('backrefs'): + ref = escape_id(node['backrefs'][0]) + self.add_xref(ref, ref, node) + self.add_text('\n------------------------------------\n' + '@end format\n') + + def visit_comment(self, node): + for line in node.astext().splitlines(): + # Prevents unintended inclusion of `Local Variables:' + # comment blocks used by editors. No harm in leaving it + # but it can interfere with the ones we add. + if line.strip().lower() == 'local variables:': + line = '--IGNORED-- Local Variables' + self.add_text('@c %s\n' % line, fresh=1) + raise nodes.SkipNode + + def visit_problematic(self, node): + if node.get('ids'): + self.add_anchor(node['ids'][0], node) + self.add_text('>') + def depart_problematic(self, node): + self.add_text('<') + + def unimplemented_visit(self, node): + self.document.reporter.error("Unimplemented node type: `%s'" + % node.__class__.__name__, base_node=node) + + def unknown_visit(self, node): + self.document.reporter.error("Unknown node type: `%s'" + % node.__class__.__name__, base_node=node) + def unknown_departure(self, node): + pass + + ### Sphinx specific + + def visit_productionlist(self, node): + self.visit_literal_block(None) + names = [] + for production in node: + names.append(production['tokenname']) + maxlen = max(len(name) for name in names) + for production in node: + if production['tokenname']: + s = production['tokenname'].ljust(maxlen) + ' ::=' + lastname = production['tokenname'] + else: + s = '%s ' % (' '*len(lastname)) + self.add_text(escape(s)) + self.add_text(escape(production.astext() + '\n')) + self.depart_literal_block(None) + raise nodes.SkipNode + def depart_productionlist(self, node): + pass + + def visit_literal_emphasis(self, node): + self.add_text('@code{') + def depart_literal_emphasis(self, node): + self.add_text('}') + + def visit_module(self, node): + modname = escape_id(node['modname']) + self.add_anchor(modname, node) + + def visit_index(self, node): + # Throws off table alignment + if isinstance(node.parent, nodes.term): + return + for entry in node['entries']: + typ, text, tid, text2 = entry + text = text.replace('!', ' ').replace(';', ' ') + text = escape_id(text) + self.add_text('@geindex %s\n' % text, fresh=1) + + def visit_autosummary_table(self, node): + pass + def depart_autosummary_table(self, node): + pass + + def visit_todo_node(self, node): + self.visit_transition(node) + self.visit_admonition(node) + def depart_todo_node(self, node): + self.depart_admonition(node) + self.visit_transition(node) + + def visit_refcount(self, node): + self.add_text('\n') + def depart_refcount(self, node): + self.add_text('\n\n') + + def visit_versionmodified(self, node): + intro = versionlabels[node['type']] % node['version'] + if node.children: + intro += ': ' + else: + intro += '.' + self.add_text('%s' % escape(intro), fresh=1) + def depart_versionmodified(self, node): + self.rstrip() + self.add_text('\n\n', fresh=1) + + def visit_start_of_file(self, node): + self.curfilestack.append(node.get('docname', '')) + if node.get('docname'): + self.next_section_targets.append(node['docname']) + def depart_start_of_file(self, node): + self.curfilestack.pop() + + def visit_centered(self, node): + txt = escape_arg(node.astext()) + self.add_text('@center %s\n' % txt, fresh=1) + raise nodes.SkipNode + def depart_centered(self, node): + pass + + def visit_seealso(self, node): + pass + def depart_seealso(self, node): + pass + + def visit_meta(self, node): + raise nodes.SkipNode + def depart_meta(self, node): + pass + + def visit_glossary(self, node): + pass + def depart_glossary(self, node): + pass + + def visit_acks(self, node): + pass + def depart_acks(self, node): + pass + + def visit_highlightlang(self, node): + pass + def depart_highlightlang(self, node): + pass + + ## Desc + + desc_map = { + 'function' : 'Function', + 'class': 'Class', + 'method': 'Method', + 'classmethod': 'Class Method', + 'staticmethod': 'Static Method', + 'exception': 'Exception', + 'data': 'Data', + 'attribute': 'Attribute', + 'opcode': 'Opcode', + 'cfunction': 'C Function', + 'cmember': 'C Member', + 'cmacro': 'C Macro', + 'ctype': 'C Type', + 'cvar': 'C Variable', + 'cmdoption': 'Option', + 'describe': 'Description', + } + + def visit_desc(self, node): + self.at_deffnx = '@deffn' + def depart_desc(self, node): + self.rstrip() + self.add_text('@end deffn\n\n', fresh=1) + def visit_desc_signature(self, node): + self.desctype = node.parent['desctype'].strip() + if self.desctype != 'describe' and node['ids']: + self.add_anchor(node['ids'][0], node) + typ = self.desc_map.get(self.desctype, self.desctype) + self.add_text('%s {%s} ' % (self.at_deffnx, escape_arg(typ)), fresh=1) + self.at_deffnx = '@deffnx' + def depart_desc_signature(self, node): + self.add_text("", fresh=1) + + def visit_desc_name(self, node): + pass + def depart_desc_name(self, node): + pass + + def visit_desc_addname(self, node): + pass + def depart_desc_addname(self, node): + pass + + def visit_desc_type(self, node): + pass + def depart_desc_type(self, node): + pass + + def visit_desc_returns(self, node): + self.add_text(' -> ') + def depart_desc_returns(self, node): + pass + + def visit_desc_parameterlist(self, node): + self.add_text(' (') + self.first_param = 1 + def depart_desc_parameterlist(self, node): + self.add_text(')') + + def visit_desc_parameter(self, node): + if not self.first_param: + self.add_text(', ') + else: + self.first_param = 0 + self.add_text(escape(node.astext())) + raise nodes.SkipNode + def depart_desc_parameter(self, node): + pass + + def visit_desc_optional(self, node): + self.add_text('[') + def depart_desc_optional(self, node): + self.add_text(']') + + def visit_desc_annotation(self, node): + raise nodes.SkipNode + def depart_desc_annotation(self, node): + pass + + def visit_desc_content(self, node): + self.add_text("", fresh=1) + def depart_desc_content(self, node): + pass From 0d1b0020d56a6df1b3e044b930468ee0c4b2d893 Mon Sep 17 00:00:00 2001 From: Jonathan Waltman <jonathan.waltman@gmail.com> Date: Thu, 16 Sep 2010 02:08:31 -0500 Subject: [PATCH 396/744] Update config for Texinfo --- sphinx/builders/__init__.py | 1 + sphinx/config.py | 6 ++++++ sphinx/quickstart.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index ce04f7691..1fe474075 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -325,6 +325,7 @@ BUILTIN_BUILDERS = { 'latex': ('latex', 'LaTeXBuilder'), 'text': ('text', 'TextBuilder'), 'man': ('manpage', 'ManualPageBuilder'), + 'texinfo': ('texinfo', 'TexinfoBuilder'), 'changes': ('changes', 'ChangesBuilder'), 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'), 'websupport': ('websupport', 'WebSupportBuilder'), diff --git a/sphinx/config.py b/sphinx/config.py index 8ad260e9c..a0ba989a9 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -159,6 +159,12 @@ class Config(object): # manpage options man_pages = ([], None), + + # Texinfo options + texinfo_documents = ([], None), + texinfo_appendices = ([], None), + texinfo_elements = ({}, None), + texinfo_domain_indices = (True, None), ) def __init__(self, dirname, filename, overrides, tags): diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 864cca95d..86ca96684 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -254,6 +254,20 @@ man_pages = [ ('%(master_str)s', '%(project_manpage)s', u'%(project_doc)s', [u'%(author_str)s'], 1) ] + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('%(master_str)s', '%(project_fn)s', u'%(project_doc)s', + u'%(author_str)s', '%(project_fn)s', + 'One line description of project.', 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +texinfo_appendices = [] ''' EPUB_CONFIG = ''' @@ -364,6 +378,8 @@ help: \t@echo " latexpdf to make LaTeX files and run them through pdflatex" \t@echo " text to make text files" \t@echo " man to make manual pages" +\t@echo " texinfo to make Texinfo files" +\t@echo " info to make Texinfo files and run them through makeinfo" \t@echo " gettext to make PO message catalogs" \t@echo " changes to make an overview of all changed/added/deprecated items" \t@echo " linkcheck to check all external links for integrity" @@ -451,6 +467,19 @@ man: \t@echo \t@echo "Build finished. The manual pages are in $(BUILDDIR)/man." +texinfo: +\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo +\t@echo +\t@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." +\t@echo "Run \\`make' in that directory to run these through makeinfo" \\ +\t "(use \\`make info' here to do that automatically)." + +info: +\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo +\t@echo "Running Texinfo files through makeinfo..." +\tmake -C $(BUILDDIR)/texinfo info +\t@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + gettext: \t$(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) $(BUILDDIR)/locale \t@echo From a5343b4c5bf61fb6fb5caa7648465972d8a1911a Mon Sep 17 00:00:00 2001 From: Jonathan Waltman <jonathan.waltman@gmail.com> Date: Thu, 16 Sep 2010 02:16:27 -0500 Subject: [PATCH 397/744] Add documentation for Texinfo builder --- doc/Makefile | 15 +++++++ doc/builders.rst | 21 +++++++++ doc/conf.py | 6 +++ doc/config.rst | 73 ++++++++++++++++++++++++++++++ doc/faq.rst | 109 +++++++++++++++++++++++++++++++++++++++++++++ doc/invocation.rst | 4 ++ 6 files changed, 228 insertions(+) diff --git a/doc/Makefile b/doc/Makefile index aa3ecb61a..0199ba47c 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -29,6 +29,8 @@ help: @echo " epub to make an epub file" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run pdflatex" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @@ -131,3 +133,16 @@ linkcheck: doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo + @echo + @echo "Build finished. The Texinfo files are in _build/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) _build/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C _build/texinfo info + @echo "makeinfo finished; the Info files are in _build/texinfo." diff --git a/doc/builders.rst b/doc/builders.rst index 8ea46ad62..71e28a7ca 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -144,6 +144,27 @@ Note that a direct PDF builder using ReportLab is available in `rst2pdf .. versionadded:: 1.0 + +.. module:: sphinx.builders.texinfo +.. class:: TexinfoBuilder + + This builder produces Texinfo files that can be processed into Info + files by the :program:`makeinfo` program. You have to specify which + documents are to be included in which Texinfo files via the + :confval:`texinfo_documents` configuration value. + + The Info format is the basis of the on-line help system used by GNU + Emacs and the terminal-based program :program:`info`. See + :ref:`texinfo-faq` for more details. The Texinfo format is the + official documentation system used by the GNU project. More + information on Texinfo can be found at + `<http://www.gnu.org/software/texinfo/>`_. + + Its name is ``texinfo``. + + .. versionadded:: 1.1 + + .. currentmodule:: sphinx.builders.html .. class:: SerializingHTMLBuilder diff --git a/doc/conf.py b/doc/conf.py index b3a1cda79..dd23b0812 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -64,6 +64,12 @@ man_pages = [ 'template generator', '', 1), ] +texinfo_documents = [ + ('contents', 'sphinx', 'Sphinx Documentation', 'Georg Brandl', + 'Sphinx', 'The Sphinx documentation builder.', 'Documentation tools', + 1), +] + # We're not using intersphinx right now, but if we did, this would be part of # the mapping: intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)} diff --git a/doc/config.rst b/doc/config.rst index 906a9e199..493a7d83e 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -1040,6 +1040,79 @@ These options influence manual page output. .. versionadded:: 1.0 +.. _texinfo-options: + +Options for Texinfo output +-------------------------- + +These options influence Texinfo output. + +.. confval:: texinfo_documents + + This value determines how to group the document tree into Texinfo + source files. It must be a list of tuples ``(startdocname, + targetname, title, author, dir_entry, description, category, + toctree_only)``, where the items are: + + * *startdocname*: document name that is the "root" of the Texinfo + file. All documents referenced by it in TOC trees will be + included in the Texinfo file too. (If you want only one Texinfo + file, use your :confval:`master_doc` here.) + * *targetname*: file name (no extension) of the Texinfo file in + the output directory. + * *title*: Texinfo document title. Can be empty to use the title of the + *startdoc*. + * *author*: Author for the Texinfo document. Use ``\and`` to + separate multiple authors, as in: ``'John \and Sarah'``. + * *dir_entry*: The name that will appear in the top-level ``DIR`` + menu file. + * *description*: Descriptive text to appear in the top-level + ``DIR`` menu file. + * *category*: Specifies the section which this entry will appear in + the top-level ``DIR`` menu file. + * *toctree_only*: Must be ``True`` or ``False``. If ``True``, the + *startdoc* document itself is not included in the output, only + the documents referenced by it via TOC trees. With this option, + you can put extra stuff in the master document that shows up in + the HTML, but not the Texinfo output. + + .. versionadded:: 1.1 + + +.. confval:: texinfo_appendices + + A list of document names to append as an appendix to all manuals. + + .. versionadded:: 1.1 + + +.. confval:: texinfo_elements + + A dictionary that contains Texinfo snippets that override those Sphinx usually + puts into the generated ``.texi`` files. + + * Keys that you may want to override include: + + ``'paragraphindent'`` + Number of spaces to indent the first line of each paragraph, + default ``2``. Specify ``0`` for no indentation. + + ``'exampleindent'`` + Number of spaces to indent the lines for examples or literal blocks, default ``4``. + Specify ``0`` for no indentation. + + ``'preamble'`` + Text inserted as is near the beginning of the file. + + * Keys that are set by other options and therefore should not be overridden are: + + ``'filename'`` + ``'title'`` + ``'direntry'`` + + .. versionadded:: 1.1 + + .. rubric:: Footnotes .. [1] A note on available globbing syntax: you can use the standard shell diff --git a/doc/faq.rst b/doc/faq.rst index 5869e3af8..aba4aee8a 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -148,3 +148,112 @@ some notes: .. _Calibre: http://calibre-ebook.com/ .. _FBreader: http://www.fbreader.org/ .. _Bookworm: http://bookworm.oreilly.com/ + + +.. _texinfo-faq: + +Texinfo info +------------ + +The Texinfo builder is currently in an experimental stage but has +successfully been used to build the documentation for both Sphinx and +Python. The intended use of this builder is to generate Texinfo that +is then processed into Info files. + +There are two main programs for reading Info files, ``info`` and GNU +Emacs. The ``info`` program has less features but is available in +most Unix environments and can be quickly accessed from the terminal. +Emacs provides better font and color display and supports extensive +customization (of course). + +.. _texinfo-links: + +Displaying Links +~~~~~~~~~~~~~~~~ + +One noticeable problem you may encounter with the generated Info files +is how references are displayed. If you read the source of an Info +file, a reference to this section would look like:: + + * note Displaying Links: target-id + +In the stand-alone reader, ``info``, references are displayed just as +they appear in the source. Emacs, on the other-hand, will by default +replace ``\*note:`` with ``see`` and hide the ``target-id``. For +example: + + :ref:`texinfo-links` + +The exact behavior of how Emacs displays references is dependent on +the variable ``Info-hide-note-references``. If set to the value of +``hide``, Emacs will hide both the ``\*note:`` part and the +``target-id``. This is generally the best way to view Sphinx-based +documents since they often make frequent use of links and do not take +this limitation into account. However, changing this variable affects +how all Info documents are displayed and most due take this behavior +into account. + +If you want Emacs to display Info files produced by Sphinx using the +value ``hide`` for ``Info-hide-note-references`` and the default value +for all other Info files, try adding the following Emacs Lisp code to +your start-up file, ``~/.emacs.d/init.el``. + +:: + + (defadvice info-insert-file-contents (after + sphinx-info-insert-file-contents + activate) + "Hack to make `Info-hide-note-references' buffer-local and + automatically set to `hide' iff it can be determined that this file + was created from a Texinfo file generated by Sphinx." + (set (make-local-variable 'Info-hide-note-references) + (default-value 'Info-hide-note-references)) + (save-excursion + (save-restriction + (widen) (goto-char (point-min)) + (when (re-search-forward + "^Generated by Sphinx" + (save-excursion (search-forward "" nil t)) t) + (set (make-local-variable 'Info-hide-note-references) + 'hide))))) + + +Notes +~~~~~ + +The following notes may be helpful if you want to create Texinfo +files: + +- Each section corresponds to a different ``node`` in the Info file. + +- Some characters cannot be properly escaped in menu entries and + xrefs. The following characters are replaced by spaces in these + contexts: ``@``, ``{``, ``}``, ``.``, ``,``, and ``:``. + +- In the HTML and Tex output, the word ``see`` is automatically + inserted before all xrefs. + +- Links to external Info files can be created using the somewhat + official URI scheme ``info``. For example:: + + info:Texinfo#makeinfo_options + + which produces: + + info:Texinfo#makeinfo_options + +- Inline markup appears as follows in Info: + + * strong -- \*strong\* + * emphasis -- _emphasis_ + * literal -- \`literal' + + It is possible to change this behavior using the Texinfo command + ``@definfoenclose``. For example, to make inline markup more + closely resemble reST, add the following to your :file:`conf.py`:: + + texinfo_elements = {'preamble': """\ + @definfoenclose strong,**,** + @definfoenclose emph,*,* + @definfoenclose code,`@w{}`,`@w{}` + """} diff --git a/doc/invocation.rst b/doc/invocation.rst index e143a3233..c8e9a61fc 100644 --- a/doc/invocation.rst +++ b/doc/invocation.rst @@ -40,6 +40,10 @@ The :program:`sphinx-build` script has several options: **man** Build manual pages in groff format for UNIX systems. + **texinfo** + Build Texinfo files that can be processed into Info files using + :program:`makeinfo`. + **text** Build plain text files. From 13684aa91f6b9f4cef6a3a08ae2833a81c373128 Mon Sep 17 00:00:00 2001 From: Jonathan Waltman <jonathan.waltman@gmail.com> Date: Thu, 16 Sep 2010 02:19:06 -0500 Subject: [PATCH 398/744] Add tests for Texinfo builder --- tests/root/conf.py | 5 ++++ tests/test_build_texinfo.py | 54 +++++++++++++++++++++++++++++++++++++ tests/test_quickstart.py | 8 ++++++ 3 files changed, 67 insertions(+) create mode 100644 tests/test_build_texinfo.py diff --git a/tests/root/conf.py b/tests/root/conf.py index 89804a30e..b97ddfcc1 100644 --- a/tests/root/conf.py +++ b/tests/root/conf.py @@ -49,6 +49,11 @@ latex_documents = [ latex_additional_files = ['svgimg.svg'] +texinfo_documents = [ + ('contents', 'SphinxTests', 'Sphinx Tests', + 'Georg Brandl \\and someone else', 'Sphinx Testing', 'Miscellaneous'), +] + value_from_conf_py = 84 coverage_c_path = ['special/*.h'] diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py new file mode 100644 index 000000000..33dbcc91d --- /dev/null +++ b/tests/test_build_texinfo.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +""" + test_build_texinfo + ~~~~~~~~~~~~~~~~~~ + + Test the build process with Texinfo builder with the test root. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import sys +from StringIO import StringIO +from subprocess import Popen, PIPE + +from sphinx.writers.texinfo import TexinfoTranslator + +from util import * +from test_build_html import ENV_WARNINGS + + +def teardown_module(): + (test_root / '_build').rmtree(True) + + +texinfo_warnfile = StringIO() + +if sys.version_info >= (3, 0): + TEXINFO_WARNINGS = remove_unicode_literals(TEXINFO_WARNINGS) + + +@with_app(buildername='texinfo', warning=texinfo_warnfile, cleanenv=True) +def test_texinfo(app): + app.builder.build_all() + # now, try to run makeinfo over it + cwd = os.getcwd() + os.chdir(app.outdir) + try: + try: + p = Popen(['makeinfo', '--no-split', 'SphinxTests.texi'], + stdout=PIPE, stderr=PIPE) + except OSError: + pass # most likely makeinfo was not found + else: + stdout, stderr = p.communicate() + retcode = p.returncode + if retcode != 0: + print stdout + print stderr + del app.cleanup_trees[:] + assert False, 'makeinfo exited with return code %s' % retcode + finally: + os.chdir(cwd) diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 541959bd3..453bf536d 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -171,6 +171,14 @@ def test_quickstart_all_answers(tempdir): assert ns['man_pages'] == [ ('contents', 'stasi', u'STASI™ Documentation', [u'Wolfgang Schäuble & G\'Beckstein'], 1)] + print ns['texinfo_documents'] + print [('contents', 'STASI', u'STASI™ Documentation', + u'Wolfgang Schäuble & G\'Beckstein', 'STASI', + 'One line description of project.', 'Miscellaneous'),] + assert ns['texinfo_documents'] == [ + ('contents', 'STASI', u'STASI™ Documentation', + u'Wolfgang Schäuble & G\'Beckstein', 'STASI', + 'One line description of project.', 'Miscellaneous'),] assert (tempdir / 'build').isdir() assert (tempdir / 'source' / '.static').isdir() From 1a5f00d83cf564fff6507f17441031a57a2c4610 Mon Sep 17 00:00:00 2001 From: Jonathan Waltman <jonathan.waltman@gmail.com> Date: Thu, 16 Sep 2010 02:34:54 -0500 Subject: [PATCH 399/744] Remove left-over debugging code --- tests/test_quickstart.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 453bf536d..3e942744a 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -171,10 +171,6 @@ def test_quickstart_all_answers(tempdir): assert ns['man_pages'] == [ ('contents', 'stasi', u'STASI™ Documentation', [u'Wolfgang Schäuble & G\'Beckstein'], 1)] - print ns['texinfo_documents'] - print [('contents', 'STASI', u'STASI™ Documentation', - u'Wolfgang Schäuble & G\'Beckstein', 'STASI', - 'One line description of project.', 'Miscellaneous'),] assert ns['texinfo_documents'] == [ ('contents', 'STASI', u'STASI™ Documentation', u'Wolfgang Schäuble & G\'Beckstein', 'STASI', From 4a00029a43b897f326e2f9a745d9fde704f27a81 Mon Sep 17 00:00:00 2001 From: Doug Hellmann <doug@doughellmann.com> Date: Thu, 16 Sep 2010 09:29:04 -0400 Subject: [PATCH 400/744] add 'inline' flag to graphviz extension directives to control paragraph breaks --- doc/ext/graphviz.rst | 5 +++++ sphinx/ext/graphviz.py | 20 +++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/doc/ext/graphviz.rst b/doc/ext/graphviz.rst index de6e03e27..3a6d7c30e 100644 --- a/doc/ext/graphviz.rst +++ b/doc/ext/graphviz.rst @@ -72,6 +72,11 @@ It adds these directives: alternate text for HTML output. If not given, the alternate text defaults to the graphviz code. +.. versionadded:: 1.1 + All three directives support an ``inline`` flag that controls + paragraph breaks in the output. When set, the graph is inserted + into the current paragraph. If the flag is not given, paragraph + breaks are introduced before and after the image (the default). There are also these new config values: diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 19dcd951a..b4449e182 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -51,6 +51,7 @@ class Graphviz(Directive): final_argument_whitespace = False option_spec = { 'alt': directives.unchanged, + 'inline': directives.flag, } def run(self): @@ -84,6 +85,7 @@ class Graphviz(Directive): node['options'] = [] if 'alt' in self.options: node['alt'] = self.options['alt'] + node['inline'] = 'inline' in self.options return [node] @@ -97,6 +99,7 @@ class GraphvizSimple(Directive): final_argument_whitespace = False option_spec = { 'alt': directives.unchanged, + 'inline': directives.flag, } def run(self): @@ -106,6 +109,7 @@ class GraphvizSimple(Directive): node['options'] = [] if 'alt' in self.options: node['alt'] = self.options['alt'] + node['inline'] = 'inline' in self.options return [node] @@ -213,7 +217,12 @@ def render_dot_html(self, node, code, options, prefix='graphviz', self.builder.warn('dot code %r: ' % code + str(exc)) raise nodes.SkipNode - self.body.append(self.starttag(node, 'p', CLASS='graphviz')) + if node.get('inline', False): + wrapper = 'span' + else: + wrapper = 'p' + + self.body.append(self.starttag(node, wrapper, CLASS='graphviz')) if fname is None: self.body.append(self.encode(code)) else: @@ -240,7 +249,7 @@ def render_dot_html(self, node, code, options, prefix='graphviz', (fname, alt, mapname, imgcss)) self.body.extend(imgmap) - self.body.append('</p>\n') + self.body.append('</%s>\n' % wrapper) raise nodes.SkipNode @@ -255,8 +264,13 @@ def render_dot_latex(self, node, code, options, prefix='graphviz'): self.builder.warn('dot code %r: ' % code + str(exc)) raise nodes.SkipNode + if node.get('inline', False): + para_separator = '' + else: + para_separator = '\n' + if fname is not None: - self.body.append('\\includegraphics{%s}' % fname) + self.body.append('%s\\includegraphics{%s}%s' % (para_separator, fname, para_separator)) raise nodes.SkipNode From 464fee852332deae3313c5e96559d7623c5a5b82 Mon Sep 17 00:00:00 2001 From: Jonathan Waltman <jonathan.waltman@gmail.com> Date: Mon, 20 Sep 2010 04:04:24 -0500 Subject: [PATCH 401/744] Remove unused imports and clean up --- doc/faq.rst | 4 +- sphinx/quickstart.py | 5 +- sphinx/writers/texinfo.py | 118 ++++++++++++-------------------------- 3 files changed, 41 insertions(+), 86 deletions(-) diff --git a/doc/faq.rst b/doc/faq.rst index aba4aee8a..bd516a5fa 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -205,14 +205,14 @@ your start-up file, ``~/.emacs.d/init.el``. activate) "Hack to make `Info-hide-note-references' buffer-local and automatically set to `hide' iff it can be determined that this file - was created from a Texinfo file generated by Sphinx." + was created from a Texinfo file generated by Docutils or Sphinx." (set (make-local-variable 'Info-hide-note-references) (default-value 'Info-hide-note-references)) (save-excursion (save-restriction (widen) (goto-char (point-min)) (when (re-search-forward - "^Generated by Sphinx" + "^Generated by \\(Sphinx\\|Docutils\\)" (save-excursion (search-forward "" nil t)) t) (set (make-local-variable 'Info-hide-note-references) 'hide))))) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 86ca96684..bba3c66df 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -261,9 +261,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('%(master_str)s', '%(project_fn)s', u'%(project_doc)s', - u'%(author_str)s', '%(project_fn)s', - 'One line description of project.', 'Miscellaneous'), + ('%(master_str)s', '%(project_fn)s', u'%(project_doc)s', u'%(author_str)s', + '%(project_fn)s', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 4065bd727..c3b8fdc0c 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -9,17 +9,11 @@ :license: BSD, see LICENSE for details. """ -import re -import os.path - import docutils -import docutils.utils - -from docutils import nodes, writers, transforms -from docutils.transforms import writer_aux +from docutils import nodes, writers from sphinx import addnodes -from sphinx.locale import admonitionlabels, versionlabels +from sphinx.locale import versionlabels TEMPLATE = """\ @@ -48,7 +42,7 @@ Generated by Sphinx @c %%** end of user preamble @ifnottex -@node Top,,(DIR),(DIR) +@node Top @top %(title)s @end ifnottex @@ -134,6 +128,7 @@ class TexinfoWriter(writers.Writer): ) settings_defaults = {} + settings_default_overrides = {'docinfo_xform': 0} output = None @@ -143,10 +138,6 @@ class TexinfoWriter(writers.Writer): writers.Writer.__init__(self) self.builder = builder - def get_transforms(self): - trans = writers.Writer.get_transforms(self) - return trans + [writer_aux.Admonitions] - def translate(self): self.visitor = visitor = TexinfoTranslator(self.document, self.builder) self.document.walkabout(visitor) @@ -214,6 +205,7 @@ class TexinfoTranslator(nodes.NodeVisitor): # if empty, the title is set to the first section title 'title': settings.title, 'author': escape_arg(settings.author), + # if empty, use basename of input file 'filename': settings.texinfo_filename, }) # Title @@ -222,6 +214,12 @@ class TexinfoTranslator(nodes.NodeVisitor): title = self.document.next_node(nodes.title) title = (title and title.astext()) or '<untitled>' elements['title'] = escape_id(title) or '<untitled>' + # Filename + if not elements['filename']: + elements['filename'] = self.document.get('source') or 'untitled' + if elements['filename'][-4:] in ('.txt', '.rst'): + elements['filename'] = elements['filename'][:-4] + elements['filename'] += '.info' # Direntry if settings.texinfo_dir_entry: elements['direntry'] = ('@dircategory %s\n' @@ -272,7 +270,8 @@ class TexinfoTranslator(nodes.NodeVisitor): # Try to find a suitable "Top" node title = self.document.next_node(nodes.title) top = (title and title.parent) or self.document - assert isinstance(top, (nodes.document, nodes.section)) + if not isinstance(top, (nodes.document, nodes.section)): + top = self.document if top is not self.document: entries = node_menus[top['node_name']] entries += node_menus['Top'][1:] @@ -831,46 +830,6 @@ class TexinfoTranslator(nodes.NodeVisitor): def depart_field_body(self, node): pass - ## Docinfo - - def visit_docinfo(self, node): - pass - def depart_docinfo(self, node): - self.add_text('\n\n') - - def visit_authors(self, node): - self.author_level = 0 - self.add_text('@*Authors: ', fresh=1) - def depart_authors(self, node): - self.add_text('@*\n') - - def visit_author(self, node): - if isinstance(node.parent, nodes.authors): - if self.author_level > 0: - self.add_text(', ') - self.author_level += 1 - else: - self.add_text('@*Author: ', fresh=1) - def depart_author(self, node): - if not isinstance(node.parent, nodes.authors): - self.add_text('\n') - - def _make_visit_docinfo_field(typ): - def visit_docinfo_field(self, node): - self.add_text('@*%s: ' % typ.capitalize(), fresh=1) - return visit_docinfo_field - - visit_organization = _make_visit_docinfo_field('organization') - visit_address = _make_visit_docinfo_field('address') - visit_contact = _make_visit_docinfo_field('contact') - visit_version = _make_visit_docinfo_field('version') - visit_revision = _make_visit_docinfo_field('revision') - visit_status = _make_visit_docinfo_field('status') - visit_date = _make_visit_docinfo_field('date') - - visit_copyright = visit_block_quote - depart_copyright = depart_block_quote - ## Admonitions def visit_admonition(self, node): @@ -883,30 +842,37 @@ class TexinfoTranslator(nodes.NodeVisitor): '@end cartouche\n\n') def _make_visit_admonition(typ): - def visit_admonition(self, node): + def visit(self, node): + title = escape(typ) self.add_text('\n@cartouche\n' - '@quotation %s\n' % escape(typ)) - return visit_admonition + '@quotation %s\n' % title) + return visit visit_attention = _make_visit_admonition('Attention') - visit_caution = _make_visit_admonition('Caution') - visit_danger = _make_visit_admonition('Danger') - visit_error = _make_visit_admonition('Error') + visit_caution = _make_visit_admonition('Caution') + visit_danger = _make_visit_admonition('Danger') + visit_error = _make_visit_admonition('Error') visit_important = _make_visit_admonition('Important') - visit_note = _make_visit_admonition('Note') - visit_tip = _make_visit_admonition('Tip') - visit_hint = _make_visit_admonition('Hint') - visit_warning = _make_visit_admonition('Warning') + visit_note = _make_visit_admonition('Note') + visit_tip = _make_visit_admonition('Tip') + visit_hint = _make_visit_admonition('Hint') + visit_warning = _make_visit_admonition('Warning') depart_attention = depart_admonition - depart_caution = depart_admonition - depart_danger = depart_admonition - depart_error = depart_admonition + depart_caution = depart_admonition + depart_danger = depart_admonition + depart_error = depart_admonition depart_important = depart_admonition - depart_note = depart_admonition - depart_tip = depart_admonition - depart_hint = depart_admonition - depart_warning = depart_admonition + depart_note = depart_admonition + depart_tip = depart_admonition + depart_hint = depart_admonition + depart_warning = depart_admonition + + ## Misc + + def visit_docinfo(self, node): + # No 'docinfo_xform' + raise nodes.SkipNode def visit_topic(self, node): # Ignore TOC's since we have to have a "menu" anyway @@ -1002,25 +968,15 @@ class TexinfoTranslator(nodes.NodeVisitor): '---------- SYSTEM MESSAGE -----------\n') def depart_system_message(self, node): self.rstrip() - if node.get('backrefs'): - ref = escape_id(node['backrefs'][0]) - self.add_xref(ref, ref, node) self.add_text('\n------------------------------------\n' '@end format\n') def visit_comment(self, node): for line in node.astext().splitlines(): - # Prevents unintended inclusion of `Local Variables:' - # comment blocks used by editors. No harm in leaving it - # but it can interfere with the ones we add. - if line.strip().lower() == 'local variables:': - line = '--IGNORED-- Local Variables' self.add_text('@c %s\n' % line, fresh=1) raise nodes.SkipNode def visit_problematic(self, node): - if node.get('ids'): - self.add_anchor(node['ids'][0], node) self.add_text('>') def depart_problematic(self, node): self.add_text('<') From 436a471974d4598581849eb74352c73e8d58a6b5 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Tue, 21 Sep 2010 10:31:59 +0200 Subject: [PATCH 402/744] Add changelog entry. --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index d86c3e9ee..5c8019110 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,9 @@ Release 1.1 (in development) * #221: Add Swedish locale. +* Added ``inline`` option to graphviz directives, and fixed the + default (block-style) in LaTeX output. + Release 1.0.4 (Sep 17, 2010) ============================ From 674f04d8083f5033e1e8cff70cb2d3d51f4673f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 28 Sep 2010 15:58:10 +0200 Subject: [PATCH 403/744] Fix line length --- sphinx/ext/graphviz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index b4449e182..78dffc987 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -270,7 +270,8 @@ def render_dot_latex(self, node, code, options, prefix='graphviz'): para_separator = '\n' if fname is not None: - self.body.append('%s\\includegraphics{%s}%s' % (para_separator, fname, para_separator)) + self.body.append('%s\\includegraphics{%s}%s' % (para_separator, fname, + para_separator)) raise nodes.SkipNode From 6149c462a3da07f61ec9796790f46cfe9130be08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 28 Sep 2010 18:15:20 +0200 Subject: [PATCH 404/744] Fix file path handling in the epub builder --- sphinx/builders/epub.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index d75ec36ae..4731b92c1 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -12,6 +12,7 @@ import os import re +import sys import time import codecs import zipfile @@ -542,7 +543,8 @@ class EpubBuilder(StandaloneHTMLBuilder): epub.write(path.join(outdir, 'mimetype'), 'mimetype', \ zipfile.ZIP_STORED) for file in projectfiles: - if isinstance(file, unicode): - file = file.encode('utf-8') - epub.write(path.join(outdir, file), file, zipfile.ZIP_DEFLATED) + fp = path.join(outdir, file) + if isinstance(fp, unicode): + fp = fp.encode(sys.getfilesystemencoding()) + epub.write(fp, file, zipfile.ZIP_DEFLATED) epub.close() From ef62d317e5cd42759dc2212b50d49cd957757a05 Mon Sep 17 00:00:00 2001 From: Chris Rebert <code@rebertia.com> Date: Sat, 2 Oct 2010 00:05:59 -0700 Subject: [PATCH 405/744] autodoc's docstring/special-comment detection works for instance attributes too --- doc/ext/autodoc.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index a1d9d98fb..68837a9a9 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -183,16 +183,23 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, For module data members and class attributes, documentation can either be put into a special-formatted comment *before* the attribute definition, or in a docstring *after* the definition. This means that in the following class - definition, both attributes can be autodocumented:: + definition, all attributes can be autodocumented:: class Foo: """Docstring for class Foo.""" - #: Doc comment for attribute Foo.bar. + #: Doc comment for class attribute Foo.bar. bar = 1 baz = 2 - """Docstring for attribute Foo.baz.""" + """Docstring for class attribute Foo.baz.""" + + def __init__(self): + #: Doc comment for instance attribute qux. + self.qux = 3 + + self.spam = 4 + """Docstring for instance attribute spam.""" .. versionchanged:: 0.6 :rst:dir:`autodata` and :rst:dir:`autoattribute` can now extract docstrings. From a3a0a96c3a439af06558f4e3a67714dee6a95513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 10 Oct 2010 20:00:12 +0200 Subject: [PATCH 406/744] Mention Sphinx Contrib in the documentation. Along with that I also removed the part about finding a place to host extensions when a message about one is send to the mailinglist, as I believe we have one for every extension with Sphinx Contrib. --- doc/extensions.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/extensions.rst b/doc/extensions.rst index 492247744..5d083f130 100644 --- a/doc/extensions.rst +++ b/doc/extensions.rst @@ -59,14 +59,19 @@ These extensions are built in and can be activated by respective entries in the Third-party extensions ---------------------- +You can find several inofficial extensions in the `Sphinx Contrib`_ +repository which is open for anyone who wants to maintain an extension +publicly. + There are several extensions that are not (yet) maintained in the Sphinx distribution. The `Wiki at BitBucket`_ maintains a list of those. -If you write an extension that you think others will find useful, please write -to the project mailing list (`join here <http://groups.google.com/group/sphinx-dev>`_) -and we'll find the proper way of including or hosting it for the public. +If you write an extension that you think others will find useful or you think +should be included as a part of Sphinx, please write to the project mailing +list (`join here <http://groups.google.com/group/sphinx-dev>`_). .. _Wiki at BitBucket: http://www.bitbucket.org/birkenfeld/sphinx/wiki/Home +.. _Sphinx Contrib: http://www.bitbucket.org/birkenfeld/sphinx-contrib Where to put your own extensions? From 27dcdb4bdde1ed472531321f4f1582d3921df221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Thu, 14 Oct 2010 23:26:08 +0200 Subject: [PATCH 407/744] doctest setup-/testrunner now share stdout This was a patch pfein send my via #pocoo --- sphinx/ext/doctest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 62fbfdff4..95fcd990a 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -265,6 +265,9 @@ Doctest summary optionflags=self.opt) self.test_runner = SphinxDocTestRunner(verbose=False, optionflags=self.opt) + + self.test_runner._fakeout = self.setup_runner._fakeout + if self.config.doctest_test_doctest_blocks: def condition(node): return (isinstance(node, (nodes.literal_block, nodes.comment)) From 5caad013d1b9f229de23ba2714206bf9140e02d6 Mon Sep 17 00:00:00 2001 From: Doug Hellmann <doug@doughellmann.com> Date: Wed, 20 Oct 2010 08:10:07 -0400 Subject: [PATCH 408/744] include a blank line before a new line-block so it stands as its own paragraph --- sphinx/writers/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index de57e35a8..f6d7eb7cb 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1238,7 +1238,7 @@ class LaTeXTranslator(nodes.NodeVisitor): * inline markup is supported. * serif typeface """ - self.body.append('{\\raggedright{}') + self.body.append('\n{\\raggedright{}') self.literal_whitespace += 1 def depart_line_block(self, node): self.literal_whitespace -= 1 From bd268c71826be859f00f8052397632793bc9e378 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 10:17:35 +0200 Subject: [PATCH 409/744] Few cleanups after texinfo builder merge (#529). Add CHANGES and AUTHORS entries. To do: add node handlers for all extension nodes so that the test documents can be handled without warnings. --- AUTHORS | 1 + CHANGES | 2 + doc/builders.rst | 17 +++--- doc/config.rst | 66 ++++++++++---------- doc/faq.rst | 119 ++++++++++++++++++------------------ sphinx/builders/texinfo.py | 13 ++-- sphinx/writers/texinfo.py | 3 +- tests/test_build_texinfo.py | 11 +++- 8 files changed, 118 insertions(+), 114 deletions(-) diff --git a/AUTHORS b/AUTHORS index 2ee77739f..04987b60a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -30,6 +30,7 @@ Other contributors, listed alphabetically, are: * Antonio Valentino -- qthelp builder * Pauli Virtanen -- autodoc improvements, autosummary extension * Stefan van der Walt -- autosummary extension +* John Waltman -- Texinfo builder * Barry Warsaw -- setup command improvements * Sebastian Wiesner -- image handling, distutils support diff --git a/CHANGES b/CHANGES index 5c8019110..d57846c6d 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,8 @@ Release 1.1 (in development) * Added the ``websupport`` library. +* Added a Texinfo builder. + * #460: Allow limiting the depth of section numbers for HTML. * #138: Add an ``index`` role, to make inline index entries. diff --git a/doc/builders.rst b/doc/builders.rst index 71e28a7ca..4a95e120e 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -148,16 +148,15 @@ Note that a direct PDF builder using ReportLab is available in `rst2pdf .. module:: sphinx.builders.texinfo .. class:: TexinfoBuilder - This builder produces Texinfo files that can be processed into Info - files by the :program:`makeinfo` program. You have to specify which - documents are to be included in which Texinfo files via the - :confval:`texinfo_documents` configuration value. + This builder produces Texinfo files that can be processed into Info files by + the :program:`makeinfo` program. You have to specify which documents are to + be included in which Texinfo files via the :confval:`texinfo_documents` + configuration value. - The Info format is the basis of the on-line help system used by GNU - Emacs and the terminal-based program :program:`info`. See - :ref:`texinfo-faq` for more details. The Texinfo format is the - official documentation system used by the GNU project. More - information on Texinfo can be found at + The Info format is the basis of the on-line help system used by GNU Emacs and + the terminal-based program :program:`info`. See :ref:`texinfo-faq` for more + details. The Texinfo format is the official documentation system used by the + GNU project. More information on Texinfo can be found at `<http://www.gnu.org/software/texinfo/>`_. Its name is ``texinfo``. diff --git a/doc/config.rst b/doc/config.rst index 493a7d83e..fbf7fc40b 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -1031,9 +1031,9 @@ These options influence manual page output. well as the name of the manual page (in the NAME section). * *description*: description of the manual page. This is used in the NAME section. - * *authors*: A list of strings with authors, or a single string. Can be - an empty string or list if you do not want to automatically generate - an AUTHORS section in the manual page. + * *authors*: A list of strings with authors, or a single string. Can be an + empty string or list if you do not want to automatically generate an + AUTHORS section in the manual page. * *section*: The manual page section. Used for the output file name as well as in the manual page header. @@ -1049,32 +1049,31 @@ These options influence Texinfo output. .. confval:: texinfo_documents - This value determines how to group the document tree into Texinfo - source files. It must be a list of tuples ``(startdocname, - targetname, title, author, dir_entry, description, category, - toctree_only)``, where the items are: + This value determines how to group the document tree into Texinfo source + files. It must be a list of tuples ``(startdocname, targetname, title, + author, dir_entry, description, category, toctree_only)``, where the items + are: - * *startdocname*: document name that is the "root" of the Texinfo - file. All documents referenced by it in TOC trees will be - included in the Texinfo file too. (If you want only one Texinfo - file, use your :confval:`master_doc` here.) - * *targetname*: file name (no extension) of the Texinfo file in - the output directory. + * *startdocname*: document name that is the "root" of the Texinfo file. All + documents referenced by it in TOC trees will be included in the Texinfo + file too. (If you want only one Texinfo file, use your + :confval:`master_doc` here.) + * *targetname*: file name (no extension) of the Texinfo file in the output + directory. * *title*: Texinfo document title. Can be empty to use the title of the *startdoc*. - * *author*: Author for the Texinfo document. Use ``\and`` to - separate multiple authors, as in: ``'John \and Sarah'``. - * *dir_entry*: The name that will appear in the top-level ``DIR`` - menu file. - * *description*: Descriptive text to appear in the top-level - ``DIR`` menu file. - * *category*: Specifies the section which this entry will appear in - the top-level ``DIR`` menu file. - * *toctree_only*: Must be ``True`` or ``False``. If ``True``, the - *startdoc* document itself is not included in the output, only - the documents referenced by it via TOC trees. With this option, - you can put extra stuff in the master document that shows up in - the HTML, but not the Texinfo output. + * *author*: Author for the Texinfo document. Use ``\and`` to separate + multiple authors, as in: ``'John \and Sarah'``. + * *dir_entry*: The name that will appear in the top-level ``DIR`` menu file. + * *description*: Descriptive text to appear in the top-level ``DIR`` menu + file. + * *category*: Specifies the section which this entry will appear in the + top-level ``DIR`` menu file. + * *toctree_only*: Must be ``True`` or ``False``. If ``True``, the *startdoc* + document itself is not included in the output, only the documents + referenced by it via TOC trees. With this option, you can put extra stuff + in the master document that shows up in the HTML, but not the Texinfo + output. .. versionadded:: 1.1 @@ -1088,23 +1087,24 @@ These options influence Texinfo output. .. confval:: texinfo_elements - A dictionary that contains Texinfo snippets that override those Sphinx usually - puts into the generated ``.texi`` files. + A dictionary that contains Texinfo snippets that override those Sphinx + usually puts into the generated ``.texi`` files. * Keys that you may want to override include: ``'paragraphindent'`` - Number of spaces to indent the first line of each paragraph, - default ``2``. Specify ``0`` for no indentation. + Number of spaces to indent the first line of each paragraph, default + ``2``. Specify ``0`` for no indentation. ``'exampleindent'`` - Number of spaces to indent the lines for examples or literal blocks, default ``4``. - Specify ``0`` for no indentation. + Number of spaces to indent the lines for examples or literal blocks, + default ``4``. Specify ``0`` for no indentation. ``'preamble'`` Text inserted as is near the beginning of the file. - * Keys that are set by other options and therefore should not be overridden are: + * Keys that are set by other options and therefore should not be overridden + are: ``'filename'`` ``'title'`` diff --git a/doc/faq.rst b/doc/faq.rst index bd516a5fa..0c2d2c4f7 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -155,92 +155,89 @@ some notes: Texinfo info ------------ -The Texinfo builder is currently in an experimental stage but has -successfully been used to build the documentation for both Sphinx and -Python. The intended use of this builder is to generate Texinfo that -is then processed into Info files. +The Texinfo builder is currently in an experimental stage but has successfully +been used to build the documentation for both Sphinx and Python. The intended +use of this builder is to generate Texinfo that is then processed into Info +files. + +There are two main programs for reading Info files, ``info`` and GNU Emacs. The +``info`` program has less features but is available in most Unix environments +and can be quickly accessed from the terminal. Emacs provides better font and +color display and supports extensive customization (of course). -There are two main programs for reading Info files, ``info`` and GNU -Emacs. The ``info`` program has less features but is available in -most Unix environments and can be quickly accessed from the terminal. -Emacs provides better font and color display and supports extensive -customization (of course). .. _texinfo-links: Displaying Links ~~~~~~~~~~~~~~~~ -One noticeable problem you may encounter with the generated Info files -is how references are displayed. If you read the source of an Info -file, a reference to this section would look like:: +One noticeable problem you may encounter with the generated Info files is how +references are displayed. If you read the source of an Info file, a reference +to this section would look like:: * note Displaying Links: target-id -In the stand-alone reader, ``info``, references are displayed just as -they appear in the source. Emacs, on the other-hand, will by default -replace ``\*note:`` with ``see`` and hide the ``target-id``. For -example: +In the stand-alone reader, ``info``, references are displayed just as they +appear in the source. Emacs, on the other-hand, will by default replace +``\*note:`` with ``see`` and hide the ``target-id``. For example: :ref:`texinfo-links` -The exact behavior of how Emacs displays references is dependent on -the variable ``Info-hide-note-references``. If set to the value of -``hide``, Emacs will hide both the ``\*note:`` part and the -``target-id``. This is generally the best way to view Sphinx-based -documents since they often make frequent use of links and do not take -this limitation into account. However, changing this variable affects -how all Info documents are displayed and most due take this behavior +The exact behavior of how Emacs displays references is dependent on the variable +``Info-hide-note-references``. If set to the value of ``hide``, Emacs will hide +both the ``\*note:`` part and the ``target-id``. This is generally the best way +to view Sphinx-based documents since they often make frequent use of links and +do not take this limitation into account. However, changing this variable +affects how all Info documents are displayed and most due take this behavior into account. -If you want Emacs to display Info files produced by Sphinx using the -value ``hide`` for ``Info-hide-note-references`` and the default value -for all other Info files, try adding the following Emacs Lisp code to -your start-up file, ``~/.emacs.d/init.el``. +If you want Emacs to display Info files produced by Sphinx using the value +``hide`` for ``Info-hide-note-references`` and the default value for all other +Info files, try adding the following Emacs Lisp code to your start-up file, +``~/.emacs.d/init.el``. :: - (defadvice info-insert-file-contents (after - sphinx-info-insert-file-contents - activate) - "Hack to make `Info-hide-note-references' buffer-local and - automatically set to `hide' iff it can be determined that this file - was created from a Texinfo file generated by Docutils or Sphinx." - (set (make-local-variable 'Info-hide-note-references) - (default-value 'Info-hide-note-references)) - (save-excursion - (save-restriction - (widen) (goto-char (point-min)) - (when (re-search-forward - "^Generated by \\(Sphinx\\|Docutils\\)" - (save-excursion (search-forward "" nil t)) t) - (set (make-local-variable 'Info-hide-note-references) - 'hide))))) + (defadvice info-insert-file-contents (after + sphinx-info-insert-file-contents + activate) + "Hack to make `Info-hide-note-references' buffer-local and + automatically set to `hide' iff it can be determined that this file + was created from a Texinfo file generated by Docutils or Sphinx." + (set (make-local-variable 'Info-hide-note-references) + (default-value 'Info-hide-note-references)) + (save-excursion + (save-restriction + (widen) (goto-char (point-min)) + (when (re-search-forward + "^Generated by \\(Sphinx\\|Docutils\\)" + (save-excursion (search-forward "" nil t)) t) + (set (make-local-variable 'Info-hide-note-references) + 'hide))))) Notes ~~~~~ -The following notes may be helpful if you want to create Texinfo -files: +The following notes may be helpful if you want to create Texinfo files: - Each section corresponds to a different ``node`` in the Info file. -- Some characters cannot be properly escaped in menu entries and - xrefs. The following characters are replaced by spaces in these - contexts: ``@``, ``{``, ``}``, ``.``, ``,``, and ``:``. +- Some characters cannot be properly escaped in menu entries and xrefs. The + following characters are replaced by spaces in these contexts: ``@``, ``{``, + ``}``, ``.``, ``,``, and ``:``. -- In the HTML and Tex output, the word ``see`` is automatically - inserted before all xrefs. +- In the HTML and Tex output, the word ``see`` is automatically inserted before + all xrefs. -- Links to external Info files can be created using the somewhat - official URI scheme ``info``. For example:: +- Links to external Info files can be created using the somewhat official URI + scheme ``info``. For example:: - info:Texinfo#makeinfo_options + info:Texinfo#makeinfo_options which produces: - info:Texinfo#makeinfo_options + info:Texinfo#makeinfo_options - Inline markup appears as follows in Info: @@ -249,11 +246,11 @@ files: * literal -- \`literal' It is possible to change this behavior using the Texinfo command - ``@definfoenclose``. For example, to make inline markup more - closely resemble reST, add the following to your :file:`conf.py`:: + ``@definfoenclose``. For example, to make inline markup more closely resemble + reST, add the following to your :file:`conf.py`:: - texinfo_elements = {'preamble': """\ - @definfoenclose strong,**,** - @definfoenclose emph,*,* - @definfoenclose code,`@w{}`,`@w{}` - """} + texinfo_elements = {'preamble': """\ + @definfoenclose strong,**,** + @definfoenclose emph,*,* + @definfoenclose code,`@w{}`,`@w{}` + """} diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py index d65a46810..b08c0cd6b 100644 --- a/sphinx/builders/texinfo.py +++ b/sphinx/builders/texinfo.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import os from os import path from docutils import nodes @@ -17,12 +16,12 @@ from docutils.io import FileOutput from docutils.utils import new_document from docutils.frontend import OptionParser -from sphinx import package_dir, addnodes +from sphinx import addnodes from sphinx.locale import _ from sphinx.builders import Builder from sphinx.environment import NoUri from sphinx.util.nodes import inline_all_toctrees -from sphinx.util.osutil import SEP, copyfile +from sphinx.util.osutil import SEP from sphinx.util.console import bold, darkgreen from sphinx.writers.texinfo import TexinfoWriter @@ -49,13 +48,13 @@ pdf: $(addsuffix .pdf,$(ALLDOCS)) install-info: info \tfor f in *.info; do \\ \t cp -t $(infodir) "$$f" && \\ -\t $(INSTALL_INFO) --info-dir=$(infodir) "$$f" ;\\ +\t $(INSTALL_INFO) --info-dir=$(infodir) "$$f" ; \\ \tdone uninstall-info: info \tfor f in *.info; do \\ -\t rm -f "$(infodir)/$$f" ;\\ -\t $(INSTALL_INFO) --delete --info-dir=$(infodir) "$$f" ;\\ +\t rm -f "$(infodir)/$$f" ; \\ +\t $(INSTALL_INFO) --delete --info-dir=$(infodir) "$$f" ; \\ \tdone %.info: %.texi @@ -83,7 +82,7 @@ clean: class TexinfoBuilder(Builder): """ - Builds Texinfo output to create Info. + Builds Texinfo output to create Info documentation. """ name = 'texinfo' format = 'texinfo' diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index c3b8fdc0c..6ea3a3fe4 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import docutils from docutils import nodes, writers from sphinx import addnodes @@ -564,7 +563,7 @@ class TexinfoTranslator(nodes.NodeVisitor): text = node.astext() self.add_text('@cite{%s}' % escape_arg(text)) raise nodes.SkipNode - def visit_title_reference(self, node): + def depart_title_reference(self, node): pass ## Blocks diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 33dbcc91d..295097ece 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -10,12 +10,11 @@ """ import os +import re import sys from StringIO import StringIO from subprocess import Popen, PIPE -from sphinx.writers.texinfo import TexinfoTranslator - from util import * from test_build_html import ENV_WARNINGS @@ -26,6 +25,8 @@ def teardown_module(): texinfo_warnfile = StringIO() +TEXINFO_WARNINGS = ENV_WARNINGS + if sys.version_info >= (3, 0): TEXINFO_WARNINGS = remove_unicode_literals(TEXINFO_WARNINGS) @@ -33,6 +34,12 @@ if sys.version_info >= (3, 0): @with_app(buildername='texinfo', warning=texinfo_warnfile, cleanenv=True) def test_texinfo(app): app.builder.build_all() + texinfo_warnings = texinfo_warnfile.getvalue().replace(os.sep, '/') + texinfo_warnings_exp = TEXINFO_WARNINGS % {'root': app.srcdir} + #assert re.match(texinfo_warnings_exp + '$', texinfo_warnings), \ + # 'Warnings don\'t match:\n' + \ + # '--- Expected (regex):\n' + texinfo_warnings_exp + \ + # '--- Got:\n' + texinfo_warnings # now, try to run makeinfo over it cwd = os.getcwd() os.chdir(app.outdir) From 92f6212c98871ddba777524039d8b1950a069c53 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 11:16:23 +0200 Subject: [PATCH 410/744] Add add_node() support for texinfo and add handlers for nodes in our extensions. --- doc/ext/appapi.rst | 6 ++--- sphinx/application.py | 3 +++ sphinx/ext/autosummary/__init__.py | 6 +++-- sphinx/ext/inheritance_diagram.py | 3 ++- sphinx/ext/mathbase.py | 42 +++++++++++++++++++++--------- sphinx/ext/todo.py | 3 ++- 6 files changed, 44 insertions(+), 19 deletions(-) diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst index 8f2a779b5..bbe2070fe 100644 --- a/doc/ext/appapi.rst +++ b/doc/ext/appapi.rst @@ -76,9 +76,9 @@ the following public API: Node visitor functions for the Sphinx HTML, LaTeX, text and manpage writers can be given as keyword arguments: the keyword must be one or more of - ``'html'``, ``'latex'``, ``'text'``, ``'man'``, the value a 2-tuple of - ``(visit, depart)`` methods. ``depart`` can be ``None`` if the ``visit`` - function raises :exc:`docutils.nodes.SkipNode`. Example: + ``'html'``, ``'latex'``, ``'text'``, ``'man'``, ``'texinfo'``, the value a + 2-tuple of ``(visit, depart)`` methods. ``depart`` can be ``None`` if the + ``visit`` function raises :exc:`docutils.nodes.SkipNode`. Example: .. code-block:: python diff --git a/sphinx/application.py b/sphinx/application.py index 50f4102e3..97b1b396d 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -358,6 +358,9 @@ class Sphinx(object): elif key == 'man': from sphinx.writers.manpage import ManualPageTranslator \ as translator + elif key == 'texinfo': + from sphinx.writers.texinfo import TexinfoTranslator \ + as translator else: # ignore invalid keys for compatibility continue diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 8186a2e58..df938307c 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -481,12 +481,14 @@ def setup(app): html=(autosummary_toc_visit_html, autosummary_noop), latex=(autosummary_noop, autosummary_noop), text=(autosummary_noop, autosummary_noop), - man=(autosummary_noop, autosummary_noop)) + man=(autosummary_noop, autosummary_noop), + texinfo=(autosummary_noop, autosummary_noop)) app.add_node(autosummary_table, html=(autosummary_table_visit_html, autosummary_noop), latex=(autosummary_noop, autosummary_noop), text=(autosummary_noop, autosummary_noop), - man=(autosummary_noop, autosummary_noop)) + man=(autosummary_noop, autosummary_noop), + texinfo=(autosummary_noop, autosummary_noop)) app.add_directive('autosummary', Autosummary) app.add_role('autolink', autolink_role) app.connect('doctree-read', process_autosummary_toc) diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index 3f6f0b4d7..7623e2e89 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -357,7 +357,8 @@ def setup(app): latex=(latex_visit_inheritance_diagram, None), html=(html_visit_inheritance_diagram, None), text=(skip, None), - man=(skip, None)) + man=(skip, None), + texinfo=(skip, None)) app.add_directive('inheritance-diagram', InheritanceDiagram) app.add_config_value('inheritance_graph_attrs', {}, False), app.add_config_value('inheritance_node_attrs', {}, False), diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py index 8a5b75d34..6c67265a1 100644 --- a/sphinx/ext/mathbase.py +++ b/sphinx/ext/mathbase.py @@ -13,6 +13,7 @@ from docutils import nodes, utils from docutils.parsers.rst import directives from sphinx.util.compat import Directive +from sphinx.writers.texinfo import escape class math(nodes.Inline, nodes.TextElement): @@ -122,6 +123,20 @@ def man_visit_eqref(self, node): raise nodes.SkipNode +def texinfo_visit_math(self, node): + self.body.append('@math{' + escape(node['latex']) + '}') + raise nodes.SkipNode + +def texinfo_visit_displaymath(self, node): + self.visit_paragraph(self, node) +def texinfo_depart_displaymath(self, node): + self.depart_paragraph(self, node) + +def texinfo_visit_eqref(self, node): + self.body.append(node['target']) + raise nodes.SkipNode + + def html_visit_eqref(self, node): self.body.append('<a href="#equation-%s">' % node['target']) @@ -148,20 +163,23 @@ def number_equations(app, doctree, docname): def setup_math(app, htmlinlinevisitors, htmldisplayvisitors): app.add_node(math, - latex=(latex_visit_math, None), - text=(text_visit_math, None), - man=(man_visit_math, None), - html=htmlinlinevisitors) + latex=(latex_visit_math, None), + text=(text_visit_math, None), + man=(man_visit_math, None), + texinfo=(texinfo_visit_math, None), + html=htmlinlinevisitors) app.add_node(displaymath, - latex=(latex_visit_displaymath, None), - text=(text_visit_displaymath, None), - man=(man_visit_displaymath, man_depart_displaymath), - html=htmldisplayvisitors) + latex=(latex_visit_displaymath, None), + text=(text_visit_displaymath, None), + man=(man_visit_displaymath, man_depart_displaymath), + texinfo=(texinfo_visit_displaymath, texinfo_depart_displaymath), + html=htmldisplayvisitors) app.add_node(eqref, - latex=(latex_visit_eqref, None), - text=(text_visit_eqref, None), - man=(man_visit_eqref, None), - html=(html_visit_eqref, html_depart_eqref)) + latex=(latex_visit_eqref, None), + text=(text_visit_eqref, None), + man=(man_visit_eqref, None), + texinfo=(texinfo_visit_eqref, None), + html=(html_visit_eqref, html_depart_eqref)) app.add_role('math', math_role) app.add_role('eq', eq_role) app.add_directive('math', MathDirective) diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index a6c33202e..e6af39ec0 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -159,7 +159,8 @@ def setup(app): html=(visit_todo_node, depart_todo_node), latex=(visit_todo_node, depart_todo_node), text=(visit_todo_node, depart_todo_node), - man=(visit_todo_node, depart_todo_node)) + man=(visit_todo_node, depart_todo_node), + texinfo=(visit_todo_node, depart_todo_node)) app.add_directive('todo', Todo) app.add_directive('todolist', TodoList) From c48b67d3c9a25bf15e92627984ef220c44ec711f Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 11:20:52 +0200 Subject: [PATCH 411/744] Fix call. --- sphinx/ext/mathbase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py index 6c67265a1..4053dbe45 100644 --- a/sphinx/ext/mathbase.py +++ b/sphinx/ext/mathbase.py @@ -128,9 +128,9 @@ def texinfo_visit_math(self, node): raise nodes.SkipNode def texinfo_visit_displaymath(self, node): - self.visit_paragraph(self, node) + self.visit_paragraph(node) def texinfo_depart_displaymath(self, node): - self.depart_paragraph(self, node) + self.depart_paragraph(node) def texinfo_visit_eqref(self, node): self.body.append(node['target']) From 08c107691779bf45c39a74029bc73182d9eef692 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 11:27:33 +0200 Subject: [PATCH 412/744] Implement missing node handlers for texinfo. Enable checking warnings from texinfo build in tests. --- sphinx/ext/mathbase.py | 4 ++-- sphinx/writers/texinfo.py | 32 ++++++++++++++++++++++++++++++++ tests/test_build_texinfo.py | 8 ++++---- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py index 4053dbe45..04e940a27 100644 --- a/sphinx/ext/mathbase.py +++ b/sphinx/ext/mathbase.py @@ -12,8 +12,8 @@ from docutils import nodes, utils from docutils.parsers.rst import directives +from sphinx.writers import texinfo from sphinx.util.compat import Directive -from sphinx.writers.texinfo import escape class math(nodes.Inline, nodes.TextElement): @@ -124,7 +124,7 @@ def man_visit_eqref(self, node): def texinfo_visit_math(self, node): - self.body.append('@math{' + escape(node['latex']) + '}') + self.body.append('@math{' + texinfo.escape_arg(node['latex']) + '}') raise nodes.SkipNode def texinfo_visit_displaymath(self, node): diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 6ea3a3fe4..f7b3a9bb6 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -175,6 +175,7 @@ class TexinfoTranslator(nodes.NodeVisitor): self.short_ids = {} self.body = [] + self.context = [] self.previous_section = None self.section_level = 0 self.seen_title = False @@ -1183,3 +1184,34 @@ class TexinfoTranslator(nodes.NodeVisitor): self.add_text("", fresh=1) def depart_desc_content(self, node): pass + + def visit_inline(self, node): + # stub + pass + def depart_inline(self, node): + pass + + def visit_abbreviation(self, node): + self.add_text('@abbr{') + if node.hasattr('explanation'): + self.context.append(', %s}' % escape_arg(node['explanation'])) + else: + self.context.append('}') + def depart_abbreviation(self, node): + self.body.append(self.context.pop()) + + def visit_download_reference(self, node): + pass + def depart_download_reference(self, node): + pass + + def visit_hlist(self, node): + # stub + self.visit_bullet_list(node) + def depart_hlist(self, node): + self.depart_bullet_list(node) + + def visit_hlistcol(self, node): + pass + def depart_hlistcol(self, node): + pass diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 295097ece..2b2c8efdf 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -36,10 +36,10 @@ def test_texinfo(app): app.builder.build_all() texinfo_warnings = texinfo_warnfile.getvalue().replace(os.sep, '/') texinfo_warnings_exp = TEXINFO_WARNINGS % {'root': app.srcdir} - #assert re.match(texinfo_warnings_exp + '$', texinfo_warnings), \ - # 'Warnings don\'t match:\n' + \ - # '--- Expected (regex):\n' + texinfo_warnings_exp + \ - # '--- Got:\n' + texinfo_warnings + assert re.match(texinfo_warnings_exp + '$', texinfo_warnings), \ + 'Warnings don\'t match:\n' + \ + '--- Expected (regex):\n' + texinfo_warnings_exp + \ + '--- Got:\n' + texinfo_warnings # now, try to run makeinfo over it cwd = os.getcwd() os.chdir(app.outdir) From 092a7173a1dad1bf05383fd0f532a934aeadb31a Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 11:30:07 +0200 Subject: [PATCH 413/744] #518: update Japanese catalog. --- sphinx/locale/ja/LC_MESSAGES/sphinx.js | 2 +- sphinx/locale/ja/LC_MESSAGES/sphinx.mo | Bin 9950 -> 10110 bytes sphinx/locale/ja/LC_MESSAGES/sphinx.po | 46 ++++++++++++------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.js b/sphinx/locale/ja/LC_MESSAGES/sphinx.js index 6b63245ef..0a1355404 100644 --- a/sphinx/locale/ja/LC_MESSAGES/sphinx.js +++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.js @@ -1 +1 @@ -Documentation.addTranslations({"locale": "ja", "plural_expr": "0", "messages": {"Search Results": "\u691c\u7d22\u7d50\u679c", "Preparing search...": "\u691c\u7d22\u306e\u6e96\u5099\u4e2d...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u691c\u7d22\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u691c\u7d22\u3057\u305f\u3044\u8a00\u8449\u3092\u6b63\u3057\u3044\u3064\u3065\u308a\u3067\u5165\u529b\u3057\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307e\u305f\u3001\u6b63\u3057\u3044\u30ab\u30c6\u30b4\u30ea\u306e\u691c\u7d22\u3092\u884c\u3063\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "Search finished, found %s page(s) matching the search query.": "\u691c\u7d22\u304c\u7d42\u4e86\u3057\u3001\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30da\u30fc\u30b8\u304c %s \u500b\u307f\u3064\u304b\u308a\u307e\u3057\u305f\u3002", ", in ": "", "Expand sidebar": "", "Permalink to this headline": "\u3053\u306e\u30d8\u30c3\u30c9\u30e9\u30a4\u30f3\u3078\u306e\u30d1\u30fc\u30de\u30ea\u30f3\u30af", "Searching": "\u691c\u7d22\u4e2d", "Collapse sidebar": "", "Permalink to this definition": "\u3053\u306e\u5b9a\u7fa9\u3078\u306e\u30d1\u30fc\u30de\u30ea\u30f3\u30af", "Hide Search Matches": "\u691c\u7d22\u7d50\u679c\u3092\u96a0\u3059"}}); \ No newline at end of file +Documentation.addTranslations({"locale": "ja", "plural_expr": "0", "messages": {"Search Results": "\u691c\u7d22\u7d50\u679c", "Preparing search...": "\u691c\u7d22\u306e\u6e96\u5099\u4e2d...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u691c\u7d22\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u306f\u3042\u308a\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u691c\u7d22\u3057\u305f\u3044\u8a00\u8449\u3092\u6b63\u3057\u3044\u3064\u3065\u308a\u3067\u5165\u529b\u3057\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u307e\u305f\u3001\u6b63\u3057\u3044\u30ab\u30c6\u30b4\u30ea\u306e\u691c\u7d22\u3092\u884c\u3063\u3066\u3044\u308b\u304b\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002", "Search finished, found %s page(s) matching the search query.": "\u691c\u7d22\u304c\u7d42\u4e86\u3057\u3001\u6761\u4ef6\u306b\u4e00\u81f4\u3059\u308b\u30da\u30fc\u30b8\u304c %s \u500b\u307f\u3064\u304b\u308a\u307e\u3057\u305f\u3002", ", in ": "", "Expand sidebar": "\u30b5\u30a4\u30c9\u30d0\u30fc\u3092\u5c55\u958b", "Permalink to this headline": "\u3053\u306e\u30d8\u30c3\u30c9\u30e9\u30a4\u30f3\u3078\u306e\u30d1\u30fc\u30de\u30ea\u30f3\u30af", "Searching": "\u691c\u7d22\u4e2d", "Collapse sidebar": "\u30b5\u30a4\u30c9\u30d0\u30fc\u3092\u305f\u305f\u3080", "Permalink to this definition": "\u3053\u306e\u5b9a\u7fa9\u3078\u306e\u30d1\u30fc\u30de\u30ea\u30f3\u30af", "Hide Search Matches": "\u691c\u7d22\u7d50\u679c\u3092\u96a0\u3059"}}); \ No newline at end of file diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.mo b/sphinx/locale/ja/LC_MESSAGES/sphinx.mo index 5a8ada49e2dd86560266fd49f5cf836ea68cc232..d77e83a42bb186d4a84430d50bacc75358c02beb 100644 GIT binary patch delta 1763 zcmZwFUu;uV9Ki8Y$cEF#KXr`J4Y>$yor1R07+l88gqTIM1&unQVTr~B_tIzx4=Pgb zy>?@ku!XYXzzD^$bp^VaD*^w&Uev?~Ulxtgm?h@WKT}`)^Wc;C{k2CPoJrHqIrsd| z?{|LZ-o@U(`{v%OO9Yit{<TWAVFcIUQ`m@u$WI;MZ#}+?t#}#l#&40I`pXy~*}cp+ z<HOjGvhRqQ7qE_b5g)@}f-0dlFu1=(sZF>O>v136g!^#-ljz55*o?nn1K!MO<Uo!5 zbzuxyQm>-KAI95o0wsRN=;1!*S6UObf)B2%*@3L7ZhR64P&VdK3V(!hz^^gxQ|cSD zzMVFuU@uDC^C)q9aW%eyl4s03KVi%!7&Nhv!vN0Xow$H^VF~4i>a1F#2y%OB8%m*F zC<R_aIry-dPofn31SRk1C<pou`Kd+z<iLsR4CLzlREc5?rNB$rgKwb}_|o_zZe;#D zO5!Fy1WDY6k}!&L<=apW&}-KFQSuI$_1BU3gc@P+EDISE@GHv3RaB9W@D7v$VdEB* zcl?l<??frsZ{~YY4iY!>H;oQn=lKXeD$;c$_0?Al<YQaJTd<6B^?zUl1EJc1I#4>^ zfh?(h^L#%_{E(TSMY*wAl!JVMx8njzBO0Z_pD{pv^`{JQRk#)qMCoXQF@}<;8zrvK z%wII?apM83XZ;9D{5VSd8I*z-&3qo~nJ?hu68JrX>c@t1wRN;zJpf8z4CVP&T#3)1 zbhry8E*|lIXqfij8jEgeZ?|Hxj`m0Hi?(~CP4<fI+8x%;2iiTaomuS;RmYh&720dv zqn$bJUeNAm+8xvGx#HwVX?#xGSG9dg+q1>O38oU6_63{0=hp7<B|1BcNmtti?c}uU zY4?zJ^IcsQ(fXimk&`tm^s-gS9xk6OXnR`Q6K!g<H5j*4=PR+UVv>Dlv~yXzXRCG3 zNius#+gTE5hm;&<L_0?-`3u_NNlH5oF`W;vTeV7#S{3bFkyz*B8=JhAmauQDb}njX zQgTR71RSk)b4oi0Rr%6raqJKWE1l1(>OSfXRU3KR+tcdzH80;?{BV3Jlk)6!ft6Ni z%Bf7*9oBza`1jae&Kg`%f9-0jIGOR*Y~1aORD0ouX*OHCu-)o~^07~g?kIyBJ*3~+ zlxnEHpBXwz2Se6+d2Mq1dG)_9Fx?Z1hRVlsmHhilC%L}D(&;3PsZwUR<c(Kf44Zl3 zUOFA>@k@ERFqZ?Re+oSvSpFK*-Qm?wX!p2$5%OKkX!o>s^76#Fq}^#!spYS)bbO{V Xm8xWOsyOPDlSHv^f{!UZ7Vi582V><` delta 1392 zcmXZaU1(Eh7{Kv2sSOs>XkAllKeprArgJK$R%H~Ko5PRcw4<~Il}=)E)*eW5$~kGZ zV-PfGHx#;YWGeMWrL$5ICEF}^7czDsBBD4LSoLC|AD7yjZGyW0v%GM~@BMhr^E~Ix z`+D!QJ=f2dW<yG;;7X+oti=_$1uL-=d6dP^JD9^dyoB%KZRAk}?JInr`HC8)Hsdyw z_l9sIp2JeSk5T-iMrBnkgHnDPaTNw|2QJ1v_yu-j5P!yM{0+-+8l|Bb{1E+2mf^=J z@f|364x+^OX&oG3{&iipSa5*_Df}<;C{?f2ZUN;*3+1F-l(=J<_9^wP-v8(AVnIKj zD-AA1iF*&jxE>|XcD>%N?anf&VxtF_V+P;CVI-#-#Z`C<CD9Y)^wa`Mp>m_R9zkh* zi=OX8DQKhQ9YSg76iOZs<%F_V7|7B8g7TkCqZ9~Ibr&|HywI;bfep;hqa?nKlK2ry z!pFE2=TI8>Tkre0Fp_sM%6<b9pH;04x>)!~2mFBY;tiBbIE7N+f%Z@2?^e(B{3S|4 zA6-dY1xh2;dfud6i*HzO#jPS=9iYB)8OSC1N}EGD`cv496DSQmLiysS$fKU~Beyx+ zRE)31Ma<h!;<utS@+r!t>_%y@SMR5>ocijB4Df^wIF0f}quL3SL{lgY+(Ah+t@r=Z zF6ecCq!?d@5?_VV&}x)R+=>C*hCBZ6P;Ydj9Ic7cz)=j~_j>&-mN5Sb<xWhZ#NChN zAD3MYhN2&=+pyVaX=&TEVNEoeAE`R(_m(V)=DSwz_j#WMhx4D;8a{8Vydpezb7bbb zZ{|j?&0f8rKC#moGo5ks<8?t_X#0`=SUO?2R>C|KbMp7<mzRXw2Tf<tG7q;IDLXNc zG>y2OF!Qes%OB|MbL_)zKGo3Ut4i2$w?`eAJ3Td%8)4wu15VuZUNoL8jVEKS>m6@0 zLWx)=rV^H8#xvHS>HXdm_3l>Ic{P!pfs~o)vlCt_(h;&9tJg}$k_L@AL*BzkxUJu@ zd!1Oyu=_KXomMl)#|ycOg=3S2+*l#^Yaur=d*;ggk0<9Zj4Q|d(sayp+%%H0LuS&8 HHt+op40+j_ diff --git a/sphinx/locale/ja/LC_MESSAGES/sphinx.po b/sphinx/locale/ja/LC_MESSAGES/sphinx.po index dd5a892e8..03e7123d4 100644 --- a/sphinx/locale/ja/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/ja/LC_MESSAGES/sphinx.po @@ -170,7 +170,7 @@ msgstr "%s (C++ の関数)" #: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 #: sphinx/domains/python.py:525 msgid "class" -msgstr "" +msgstr "クラス" #: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format @@ -190,7 +190,7 @@ msgstr "%s (のクラス)" #: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" -msgstr "" +msgstr "%s (グローバル変数または定数)" #: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format @@ -204,11 +204,11 @@ msgstr "パラメタ" #: sphinx/domains/javascript.py:124 msgid "Throws" -msgstr "" +msgstr "例外" #: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" -msgstr "" +msgstr "データ" #: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" @@ -307,7 +307,7 @@ msgstr "例外" #: sphinx/domains/python.py:527 msgid "method" -msgstr "" +msgstr "メソッド" #: sphinx/domains/python.py:528 #, fuzzy @@ -330,7 +330,7 @@ msgstr "撤廃" #: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" -msgstr "" +msgstr "%s (ディレクティブ)" #: sphinx/domains/rst.py:57 #, fuzzy, python-format @@ -339,12 +339,12 @@ msgstr "%s (モジュール)" #: sphinx/domains/rst.py:106 msgid "directive" -msgstr "" +msgstr "ディレクティブ" #: sphinx/domains/rst.py:107 #, fuzzy msgid "role" -msgstr "モジュール" +msgstr "ロール" #: sphinx/domains/std.py:68 sphinx/domains/std.py:84 #, python-format @@ -358,15 +358,15 @@ msgstr "%sコマンドラインオプション; %s" #: sphinx/domains/std.py:328 msgid "glossary term" -msgstr "" +msgstr "用語集の項目" #: sphinx/domains/std.py:329 msgid "grammar token" -msgstr "" +msgstr "文法トークン" #: sphinx/domains/std.py:330 msgid "reference label" -msgstr "" +msgstr "参照ラベル" #: sphinx/domains/std.py:331 msgid "environment variable" @@ -374,7 +374,7 @@ msgstr "環境変数" #: sphinx/domains/std.py:332 msgid "program option" -msgstr "" +msgstr "プログラムオプション" #: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11 #: sphinx/themes/basic/genindex-split.html:11 @@ -410,37 +410,37 @@ msgstr "課題" #: sphinx/ext/todo.py:109 #, python-format msgid "(The <<original entry>> is located in %s, line %d.)" -msgstr "" +msgstr "(<<元のエントリ>> は、 %s の %d 行目です)" #: sphinx/ext/todo.py:117 msgid "original entry" -msgstr "" +msgstr "元のエントリ" #: sphinx/ext/viewcode.py:70 msgid "[source]" -msgstr "" +msgstr "[ソース]" #: sphinx/ext/viewcode.py:117 msgid "[docs]" -msgstr "" +msgstr "[ドキュメント]" #: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" -msgstr "モジュール" +msgstr "モジュールコード" #: sphinx/ext/viewcode.py:137 #, python-format msgid "<h1>Source code for %s</h1>" -msgstr "" +msgstr "<h1>%s のソースコード</h1>" #: sphinx/ext/viewcode.py:164 msgid "Overview: module code" -msgstr "" +msgstr "概要: モジュールコード" #: sphinx/ext/viewcode.py:165 msgid "<h1>All modules for which code is available</h1>" -msgstr "" +msgstr "<h1>全モジュールのうち、コードを読めるもの</h1>" #: sphinx/locale/__init__.py:155 msgid "Attention" @@ -754,16 +754,16 @@ msgstr "検索が終了し、条件に一致するページが %s 個みつか #: sphinx/themes/default/static/sidebar.js:66 msgid "Expand sidebar" -msgstr "" +msgstr "サイドバーを展開" #: sphinx/themes/default/static/sidebar.js:79 #: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" -msgstr "" +msgstr "サイドバーをたたむ" #: sphinx/themes/haiku/layout.html:26 msgid "Contents" -msgstr "" +msgstr "コンテンツ" #: sphinx/writers/latex.py:172 msgid "Release" From b72621c447432753d29f2d3ada69839eae34e11c Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 11:40:38 +0200 Subject: [PATCH 414/744] #521: Added :confval:`linkcheck_ignore` config value. --- CHANGES | 2 ++ doc/config.rst | 13 +++++++++++++ sphinx/builders/linkcheck.py | 6 ++++++ sphinx/config.py | 3 +++ 4 files changed, 24 insertions(+) diff --git a/CHANGES b/CHANGES index c14c570f0..d271c1b82 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,8 @@ Release 1.1 (in development) * Added ``inline`` option to graphviz directives, and fixed the default (block-style) in LaTeX output. +* #521: Added :confval:`linkcheck_ignore` config value. + Release 1.0.4 (Sep 17, 2010) ============================ diff --git a/doc/config.rst b/doc/config.rst index 4c6735726..979aff5fa 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -1113,6 +1113,19 @@ These options influence Texinfo output. .. versionadded:: 1.1 +Options for the linkcheck builder +--------------------------------- + +.. confval:: linkcheck_ignore + + A list of regular expressions that match URIs that should not be checked + when doing a ``linkcheck`` build. Example:: + + linkcheck_ignore = [r'http://localhost:\d+/'] + + .. versionadded:: 1.1 + + .. rubric:: Footnotes .. [1] A note on available globbing syntax: you can use the standard shell diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 4a0bab633..dd87d70d8 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for details. """ +import re import socket from os import path from urllib2 import build_opener, HTTPError @@ -30,6 +31,7 @@ class CheckExternalLinksBuilder(Builder): name = 'linkcheck' def init(self): + self.to_ignore = map(re.compile, self.app.config.linkcheck_ignore) self.good = set() self.broken = {} self.redirected = {} @@ -76,6 +78,10 @@ class CheckExternalLinksBuilder(Builder): if lineno: self.info('(line %3d) ' % lineno, nonl=1) + for rex in self.to_ignore: + if rex.match(uri): + self.info(uri + ' - ' + darkgray('ignored')) + return if uri[0:5] == 'http:' or uri[0:6] == 'https:': self.info(uri, nonl=1) diff --git a/sphinx/config.py b/sphinx/config.py index a0ba989a9..6f957314a 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -165,6 +165,9 @@ class Config(object): texinfo_appendices = ([], None), texinfo_elements = ({}, None), texinfo_domain_indices = (True, None), + + # linkcheck options + linkcheck_ignore = ([], None), ) def __init__(self, dirname, filename, overrides, tags): From 8c28be1cd627aa1d7cd5b948c1429037d34478ce Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 12:01:36 +0200 Subject: [PATCH 415/744] #543: Remove special treatment of None, True and False in literals. --- sphinx/writers/html.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 858681f7a..be4a97c5d 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -246,9 +246,6 @@ class HTMLTranslator(BaseTranslator): # overwritten def visit_literal(self, node): - if len(node.children) == 1 and \ - node.children[0] in ('None', 'True', 'False'): - node['classes'].append('xref') self.body.append(self.starttag(node, 'tt', '', CLASS='docutils literal')) self.protect_literal_text += 1 From 4cb330f8a603eaef68f1555188afaab035ee9457 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 13:24:17 +0200 Subject: [PATCH 416/744] #526: Add Iranian translation, thanks to Omid Raha. --- CHANGES | 2 + doc/config.rst | 1 + sphinx/locale/fa/LC_MESSAGES/sphinx.js | 1 + sphinx/locale/fa/LC_MESSAGES/sphinx.mo | Bin 0 -> 9545 bytes sphinx/locale/fa/LC_MESSAGES/sphinx.po | 596 +++++++++++++++++++++++++ 5 files changed, 600 insertions(+) create mode 100644 sphinx/locale/fa/LC_MESSAGES/sphinx.js create mode 100644 sphinx/locale/fa/LC_MESSAGES/sphinx.mo create mode 100644 sphinx/locale/fa/LC_MESSAGES/sphinx.po diff --git a/CHANGES b/CHANGES index d271c1b82..d56e3d93d 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,8 @@ Release 1.1 (in development) * #521: Added :confval:`linkcheck_ignore` config value. +* #526: Added Iranian translation. + Release 1.0.4 (Sep 17, 2010) ============================ diff --git a/doc/config.rst b/doc/config.rst index 979aff5fa..90730d60c 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -361,6 +361,7 @@ documentation on :ref:`intl` for details. * ``de`` -- German * ``en`` -- English * ``es`` -- Spanish + * ``fa`` -- Iranian * ``fi`` -- Finnish * ``fr`` -- French * ``hr`` -- Croatian diff --git a/sphinx/locale/fa/LC_MESSAGES/sphinx.js b/sphinx/locale/fa/LC_MESSAGES/sphinx.js new file mode 100644 index 000000000..c268479a3 --- /dev/null +++ b/sphinx/locale/fa/LC_MESSAGES/sphinx.js @@ -0,0 +1 @@ +Documentation.addTranslations({"locale": "fa", "plural_expr": "(n > 1)", "messages": {"module, in ": "\u0645\u0627\u0698\u0648\u0644, \u062f\u0631", "Preparing search...": "...\u0622\u0645\u0627\u062f\u0647 \u062c\u0633\u062a\u062c\u0648", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": ". \u0647\u06cc\u0686 \u0633\u0646\u062f\u06cc \u0628\u0631\u0627\u06cc \u062c\u0633\u062a\u062c\u0648\u06cc \u0634\u0645\u0627 \u06cc\u0627\u0641\u062a \u0646\u0634\u062f\u060c \u0644\u0637\u0641\u0627 \u0627\u0637\u0645\u06cc\u0646\u0627\u0646 \u062d\u0627\u0635\u0644 \u0646\u0645\u0627\u0626\u06cc\u062f \u06a9\u0647 \u0627\u0645\u0644\u0627\u06cc \u062a\u0645\u0627\u0645\u06cc \u06a9\u0644\u0645\u0627\u062a \u0635\u062d\u06cc\u062d \u0645\u06cc \u0628\u0627\u0634\u062f \u0648 \u0647\u0645\u0686\u0646\u06cc\u0646 \u0645\u0648\u0636\u0648\u0639\u0627\u062a \u06a9\u0627\u0641\u06cc \u0631\u0627 \u0628\u0631\u0627\u06cc \u0627\u06cc\u0646 \u062c\u0633\u062a\u062c\u0648 \u0627\u0646\u062a\u062e\u0627\u0628 \u0646\u0645\u0648\u062f\u0647 \u0627\u06cc\u062f", "Search finished, found %s page(s) matching the search query.": "\u062c\u0633\u062a\u062c\u0648 \u0627\u0646\u062c\u0627\u0645 \u0634\u062f \u060c %s \u0635\u0641\u062d\u0647 \u0645\u0637\u0627\u0628\u0642 \u0628\u0627 \u067e\u0631\u0633 \u0648 \u062c\u0648 \u067e\u06cc\u062f\u0627 \u0634\u062f", ", in ": ", \u062f\u0631", "Permalink to this headline": "\u0644\u06cc\u0646\u06a9 \u062b\u0627\u0628\u062a \u0628\u0647 \u0627\u06cc\u0646 \u0633\u0631 \u0645\u0642\u0627\u0644\u0647", "Searching": "\u062f\u0631 \u062d\u0627\u0644 \u062c\u0633\u062a\u062c\u0648", "Permalink to this definition": "\u0644\u06cc\u0646\u06a9 \u062b\u0627\u0628\u062a \u0628\u0647 \u0627\u06cc\u0646 \u062a\u0639\u0631\u06cc\u0641", "Hide Search Matches": "\u0639\u062f\u0645 \u0646\u0645\u0627\u06cc\u0634 \u0646\u062a\u0627\u06cc\u062c \u06cc\u0627\u0641\u062a \u0634\u062f\u0647", "Search Results": "\u0646\u062a\u0627\u06cc\u062c \u062c\u0633\u062a\u062c\u0648"}}); \ No newline at end of file diff --git a/sphinx/locale/fa/LC_MESSAGES/sphinx.mo b/sphinx/locale/fa/LC_MESSAGES/sphinx.mo new file mode 100644 index 0000000000000000000000000000000000000000..c988b75c8754b69788d6d36dc2c82728295c7538 GIT binary patch literal 9545 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 UbTP8TONBs>H|Tzj2d*9S-<HSRi2wiq literal 0 HcmV?d00001 diff --git a/sphinx/locale/fa/LC_MESSAGES/sphinx.po b/sphinx/locale/fa/LC_MESSAGES/sphinx.po new file mode 100644 index 000000000..fe75951ef --- /dev/null +++ b/sphinx/locale/fa/LC_MESSAGES/sphinx.po @@ -0,0 +1,596 @@ +msgid "" +msgstr "" +"Project-Id-Version: Sphinx 1.0.3\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2008-11-09 19:46+0100\n" +"PO-Revision-Date: \n" +"Last-Translator: Omid Raha <omidraha.com@gmail.com>\n" +"Language-Team: Omid Raha <omidraha.com@gmail.com>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1)\n" +"X-Poedit-Language: Persian\n" +"X-Poedit-Country: IRAN, ISLAMIC REPUBLIC OF\n" + +#: sphinx/builder.py:408 +#, python-format +msgid "%b %d, %Y" +msgstr "" + +#: sphinx/builder.py:427 +#: sphinx/templates/defindex.html:21 +msgid "General Index" +msgstr "فهرست کلی" + +#: sphinx/builder.py:427 +msgid "index" +msgstr "فهرست" + +#: sphinx/builder.py:429 +#: sphinx/htmlhelp.py:155 +#: sphinx/templates/defindex.html:19 +#: sphinx/templates/modindex.html:2 +#: sphinx/templates/modindex.html:13 +msgid "Global Module Index" +msgstr "فهرست کلی ماژول ها" + +#: sphinx/builder.py:429 +msgid "modules" +msgstr "ماژول ها" + +#: sphinx/builder.py:466 +msgid "next" +msgstr "بعدی" + +#: sphinx/builder.py:473 +msgid "previous" +msgstr "قبلی" + +#: sphinx/builder.py:1054 +msgid " (in " +msgstr "" + +#: sphinx/builder.py:1129 +msgid "Builtins" +msgstr "درونی سازی" + +#: sphinx/builder.py:1131 +msgid "Module level" +msgstr "در سطح ماژول" + +#: sphinx/environment.py:102 +#: sphinx/latexwriter.py:164 +#, python-format +msgid "%B %d, %Y" +msgstr "" + +#: sphinx/environment.py:290 +#: sphinx/latexwriter.py:170 +#: sphinx/templates/genindex-single.html:2 +#: sphinx/templates/genindex-split.html:2 +#: sphinx/templates/genindex-split.html:5 +#: sphinx/templates/genindex.html:2 +#: sphinx/templates/genindex.html:5 +#: sphinx/templates/genindex.html:48 +#: sphinx/templates/layout.html:130 +msgid "Index" +msgstr "فهرست" + +#: sphinx/environment.py:291 +#: sphinx/latexwriter.py:169 +msgid "Module Index" +msgstr "فهرست ماژول ها" + +#: sphinx/environment.py:292 +#: sphinx/templates/defindex.html:16 +msgid "Search Page" +msgstr "صفحه جستجو" + +#: sphinx/htmlwriter.py:79 +#: sphinx/static/doctools.js:145 +msgid "Permalink to this definition" +msgstr "لینک ثابت به این تعریف" + +#: sphinx/htmlwriter.py:402 +#: sphinx/static/doctools.js:139 +msgid "Permalink to this headline" +msgstr "لینک ثابت به این سر مقاله" + +#: sphinx/latexwriter.py:167 +msgid "Release" +msgstr "انتشار" + +#: sphinx/roles.py:53 +#: sphinx/directives/desc.py:525 +#, python-format +msgid "environment variable; %s" +msgstr "%s متغیرهای عمومی؛" + +#: sphinx/roles.py:60 +#, python-format +msgid "Python Enhancement Proposals!PEP %s" +msgstr "" + +#: sphinx/textwriter.py:166 +#, python-format +msgid "Platform: %s" +msgstr "%s:پلتفرم" + +#: sphinx/textwriter.py:422 +msgid "[image]" +msgstr "" + +#: sphinx/directives/desc.py:25 +#, python-format +msgid "%s() (built-in function)" +msgstr "%s() (توابع درونی)" + +#: sphinx/directives/desc.py:26 +#: sphinx/directives/desc.py:42 +#: sphinx/directives/desc.py:54 +#, python-format +msgid "%s() (in module %s)" +msgstr "%s() (در ماژول %s)" + +#: sphinx/directives/desc.py:29 +#, python-format +msgid "%s (built-in variable)" +msgstr "%s (متغیر درونی)" + +#: sphinx/directives/desc.py:30 +#: sphinx/directives/desc.py:66 +#, python-format +msgid "%s (in module %s)" +msgstr "%s (در ماژول %s)" + +#: sphinx/directives/desc.py:33 +#, python-format +msgid "%s (built-in class)" +msgstr "%s (کلاس درونی)" + +#: sphinx/directives/desc.py:34 +#, python-format +msgid "%s (class in %s)" +msgstr "%s (کلاس در %s)" + +#: sphinx/directives/desc.py:46 +#, python-format +msgid "%s() (%s.%s method)" +msgstr "%s() (%s.%s متد)" + +#: sphinx/directives/desc.py:48 +#, python-format +msgid "%s() (%s method)" +msgstr "%s() (%s متد)" + +#: sphinx/directives/desc.py:58 +#, python-format +msgid "%s() (%s.%s static method)" +msgstr "%s() (%s.%s متد استاتیک)" + +#: sphinx/directives/desc.py:60 +#, python-format +msgid "%s() (%s static method)" +msgstr "%s() (%s متد استاتیک)" + +#: sphinx/directives/desc.py:70 +#, python-format +msgid "%s (%s.%s attribute)" +msgstr "%s (%s.%s مشخصه)" + +#: sphinx/directives/desc.py:72 +#, python-format +msgid "%s (%s attribute)" +msgstr "%s (%s مشخصه)" + +#: sphinx/directives/desc.py:74 +#, python-format +msgid "%s (C function)" +msgstr "%s (C تابع)" + +#: sphinx/directives/desc.py:76 +#, python-format +msgid "%s (C member)" +msgstr "%s (C عضو)" + +#: sphinx/directives/desc.py:78 +#, python-format +msgid "%s (C macro)" +msgstr "%s (C ماکرو)" + +#: sphinx/directives/desc.py:80 +#, python-format +msgid "%s (C type)" +msgstr "%s (C نوع)" + +#: sphinx/directives/desc.py:82 +#, python-format +msgid "%s (C variable)" +msgstr "%s (C متغیر)" + +#: sphinx/directives/desc.py:100 +msgid "Raises" +msgstr "برانگیختن" + +#: sphinx/directives/desc.py:104 +msgid "Variable" +msgstr "متغیر" + +#: sphinx/directives/desc.py:107 +msgid "Returns" +msgstr "" + +#: sphinx/directives/desc.py:114 +msgid "Return type" +msgstr "نوع برگشتی" + +#: sphinx/directives/desc.py:141 +msgid "Parameters" +msgstr "پارامترها" + +#: sphinx/directives/desc.py:411 +#, python-format +msgid "%scommand line option; %s" +msgstr "%sگزینه خط فرمان; %s" + +#: sphinx/directives/other.py:101 +msgid "Platforms: " +msgstr ":پلتفرم ها" + +#: sphinx/directives/other.py:106 +#, python-format +msgid "%s (module)" +msgstr "%s (ماژول)" + +#: sphinx/directives/other.py:146 +msgid "Section author: " +msgstr ":نویسنده این بخش" + +#: sphinx/directives/other.py:148 +msgid "Module author: " +msgstr "نویسنده این ماژول:" + +#: sphinx/directives/other.py:150 +msgid "Author: " +msgstr ":نویسنده" + +#: sphinx/directives/other.py:246 +msgid "See also" +msgstr "همچنین ملاحظه نمائید" + +#: sphinx/ext/todo.py:32 +msgid "Todo" +msgstr "در دست انجام" + +#: sphinx/ext/todo.py:78 +#, python-format +msgid "(The original entry is located in %s, line %d and can be found " +msgstr "( ورودی اصلی در %s ، در خط %d واقع شده است و می تواند یافت بشود" + +#: sphinx/ext/todo.py:84 +msgid "here" +msgstr "اینجا" + +#: sphinx/locale/__init__.py:15 +msgid "Attention" +msgstr "دقت" + +#: sphinx/locale/__init__.py:16 +msgid "Caution" +msgstr "ملاحظه" + +#: sphinx/locale/__init__.py:17 +msgid "Danger" +msgstr "خطر" + +#: sphinx/locale/__init__.py:18 +msgid "Error" +msgstr "خطا" + +#: sphinx/locale/__init__.py:19 +msgid "Hint" +msgstr "تذکر" + +#: sphinx/locale/__init__.py:20 +msgid "Important" +msgstr "مهم" + +#: sphinx/locale/__init__.py:21 +msgid "Note" +msgstr "توجه" + +#: sphinx/locale/__init__.py:22 +msgid "See Also" +msgstr "همچنین ملاحظه نمائید" + +#: sphinx/locale/__init__.py:23 +msgid "Tip" +msgstr "نکته" + +#: sphinx/locale/__init__.py:24 +msgid "Warning" +msgstr "هشدار" + +#: sphinx/locale/__init__.py:28 +#, python-format +msgid "New in version %s" +msgstr "جدید در نسخه %s" + +#: sphinx/locale/__init__.py:29 +#, python-format +msgid "Changed in version %s" +msgstr "تغییر داده شده در نسخه %s" + +#: sphinx/locale/__init__.py:30 +#, python-format +msgid "Deprecated since version %s" +msgstr "منسوخ شده از نسخه %s" + +#: sphinx/locale/__init__.py:34 +msgid "module" +msgstr "ماژول" + +#: sphinx/locale/__init__.py:35 +msgid "keyword" +msgstr "کلمه کلیدی" + +#: sphinx/locale/__init__.py:36 +msgid "operator" +msgstr "عملگر" + +#: sphinx/locale/__init__.py:37 +msgid "object" +msgstr "شیء" + +#: sphinx/locale/__init__.py:38 +msgid "exception" +msgstr "استثناء" + +#: sphinx/locale/__init__.py:39 +msgid "statement" +msgstr "گذاره" + +#: sphinx/locale/__init__.py:40 +msgid "built-in function" +msgstr "توابع درونی" + +#: sphinx/static/doctools.js:174 +msgid "Hide Search Matches" +msgstr "عدم نمایش نتایج یافت شده" + +#: sphinx/static/searchtools.js:274 +msgid "Searching" +msgstr "در حال جستجو" + +#: sphinx/static/searchtools.js:279 +msgid "Preparing search..." +msgstr "...آماده جستجو" + +#: sphinx/static/searchtools.js:338 +msgid "module, in " +msgstr "ماژول, در" + +#: sphinx/static/searchtools.js:347 +msgid ", in " +msgstr ", در" + +#: sphinx/static/searchtools.js:447 +#: sphinx/templates/search.html:18 +msgid "Search Results" +msgstr "نتایج جستجو" + +#: sphinx/static/searchtools.js:449 +msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." +msgstr ". هیچ سندی برای جستجوی شما یافت نشد، لطفا اطمینان حاصل نمائید که املای تمامی کلمات صحیح می باشد و همچنین موضوعات کافی را برای این جستجو انتخاب نموده اید" + +#: sphinx/static/searchtools.js:451 +#, python-format +msgid "Search finished, found %s page(s) matching the search query." +msgstr "جستجو انجام شد ، %s صفحه مطابق با پرس و جو پیدا شد" + +#: sphinx/templates/defindex.html:2 +msgid "Overview" +msgstr "بررسی اجمالی" + +#: sphinx/templates/defindex.html:11 +msgid "Indices and tables:" +msgstr "ایندکس ها و جداول:" + +#: sphinx/templates/defindex.html:14 +msgid "Complete Table of Contents" +msgstr "فهرست کامل مطالب" + +#: sphinx/templates/defindex.html:15 +msgid "lists all sections and subsections" +msgstr "فهرست تمامی بخش ها و زیر مجموعه ها" + +#: sphinx/templates/defindex.html:17 +msgid "search this documentation" +msgstr "جستجو در این اسناد" + +#: sphinx/templates/defindex.html:20 +msgid "quick access to all modules" +msgstr "دسترسی سریع به تمامی متدها" + +#: sphinx/templates/defindex.html:22 +msgid "all functions, classes, terms" +msgstr "تمامی توابع ، کلاس ها ، اصطلاحات" + +#: sphinx/templates/genindex-single.html:5 +#, python-format +msgid "Index – %(key)s" +msgstr "فهرست – %(key)s" + +#: sphinx/templates/genindex-single.html:44 +#: sphinx/templates/genindex-split.html:14 +#: sphinx/templates/genindex-split.html:27 +#: sphinx/templates/genindex.html:54 +msgid "Full index on one page" +msgstr "فهرست کامل در یک صفحه" + +#: sphinx/templates/genindex-split.html:7 +msgid "Index pages by letter" +msgstr "فهرست صفحات بر اساس حروف" + +#: sphinx/templates/genindex-split.html:15 +msgid "can be huge" +msgstr "" + +#: sphinx/templates/layout.html:9 +msgid "Navigation" +msgstr "ناوبری" + +#: sphinx/templates/layout.html:40 +msgid "Table Of Contents" +msgstr "فهرست عناوین" + +#: sphinx/templates/layout.html:46 +msgid "Previous topic" +msgstr "موضوع قبلی" + +#: sphinx/templates/layout.html:47 +msgid "previous chapter" +msgstr "فصل قبلی" + +#: sphinx/templates/layout.html:50 +msgid "Next topic" +msgstr "موضوع بعدی" + +#: sphinx/templates/layout.html:51 +msgid "next chapter" +msgstr "فصل بعدی" + +#: sphinx/templates/layout.html:55 +msgid "This Page" +msgstr "صفحه فعلی" + +#: sphinx/templates/layout.html:59 +msgid "Suggest Change" +msgstr "" + +#: sphinx/templates/layout.html:60 +#: sphinx/templates/layout.html:62 +msgid "Show Source" +msgstr "نمایش سورس" + +#: sphinx/templates/layout.html:71 +msgid "Quick search" +msgstr "جستجو سریع" + +#: sphinx/templates/layout.html:71 +msgid "Keyword search" +msgstr "جستجو کلید واژه" + +#: sphinx/templates/layout.html:73 +msgid "Go" +msgstr "برو" + +#: sphinx/templates/layout.html:78 +msgid "Enter a module, class or function name." +msgstr "نام یک ماژول ، کلاس و یا تابع را وارد نمائید" + +#: sphinx/templates/layout.html:119 +#, python-format +msgid "Search within %(docstitle)s" +msgstr "جستجو در %(docstitle)s" + +#: sphinx/templates/layout.html:128 +msgid "About these documents" +msgstr "درباره این مستندات" + +#: sphinx/templates/layout.html:131 +#: sphinx/templates/search.html:2 +#: sphinx/templates/search.html:5 +msgid "Search" +msgstr "جستجو" + +#: sphinx/templates/layout.html:133 +msgid "Copyright" +msgstr "کپی رایت" + +#: sphinx/templates/layout.html:178 +#, python-format +msgid "© <a href=\"%(path)s\">Copyright</a> %(copyright)s." +msgstr "" + +#: sphinx/templates/layout.html:180 +#, python-format +msgid "© Copyright %(copyright)s." +msgstr "" + +#: sphinx/templates/layout.html:183 +#, python-format +msgid "Last updated on %(last_updated)s." +msgstr ". %(last_updated)s آخرین بروز رسانی در" + +#: sphinx/templates/layout.html:186 +#, python-format +msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s." +msgstr ". <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s ایجاد شده با" + +#: sphinx/templates/modindex.html:15 +msgid "Most popular modules:" +msgstr "متداول ترین ماژول ها:" + +#: sphinx/templates/modindex.html:24 +msgid "Show modules only available on these platforms" +msgstr "تنها ماژول هایی که در این پلتفرم در دسترس هستند را نشان بده" + +#: sphinx/templates/modindex.html:56 +msgid "Deprecated" +msgstr "منسوخ شده" + +#: sphinx/templates/opensearch.xml:4 +#, python-format +msgid "Search %(docstitle)s" +msgstr "جستجو %(docstitle)s" + +#: sphinx/templates/page.html:8 +msgid "<strong>Note:</strong> You requested an out-of-date URL from this server. We've tried to redirect you to the new location of this page, but it may not be the right one." +msgstr "" + +#: sphinx/templates/search.html:7 +msgid "" +"From here you can search these documents. Enter your search\n" +" words into the box below and click \"search\". Note that the search\n" +" function will automatically search for all of the words. Pages\n" +" containing less words won't appear in the result list." +msgstr "در اینجا شما می توانید مستندات را جستجو نمائید ، کلماتی را در کادر جستجو وارد کنید و سپس بر روی دکمه جستجو کلیک نمائید ، توجه کنید که تابع جستجو گر امر جستجو را بطور خودکار برای تمامی کلمات دنبال خواهد کرد .صفحاتی که شامل کلمات کمتری هستند ، در لیست جستجو نمایش داده نخواهند شد." + +#: sphinx/templates/search.html:14 +msgid "search" +msgstr "جستجو" + +#: sphinx/templates/search.html:20 +msgid "Your search did not match any results." +msgstr ".جستجوی شما نتیجه ایی در بر نداشت" + +#: sphinx/templates/changes/frameset.html:5 +#: sphinx/templates/changes/versionchanges.html:12 +#, python-format +msgid "Changes in Version %(version)s — %(docstitle)s" +msgstr "تغییرات در نسخه %(version)s — %(docstitle)s" + +#: sphinx/templates/changes/rstsource.html:5 +#, python-format +msgid "%(filename)s — %(docstitle)s" +msgstr "" + +#: sphinx/templates/changes/versionchanges.html:17 +#, python-format +msgid "Automatically generated list of changes in version %(version)s" +msgstr "لیست تولید شده خودکار از تغییرات در نسخه %(version)s" + +#: sphinx/templates/changes/versionchanges.html:18 +msgid "Library changes" +msgstr "تغییرات کتابخانه ایی" + +#: sphinx/templates/changes/versionchanges.html:23 +msgid "C API changes" +msgstr "C API تغییرات" + +#: sphinx/templates/changes/versionchanges.html:25 +msgid "Other changes" +msgstr "دگر تغییرات" + From 20b50f8ac4aa47826775cb4a60084ed864d02159 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 22 Oct 2010 19:28:47 +0200 Subject: [PATCH 417/744] Add texinfo target to make.bat. --- sphinx/quickstart.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 423af3ae8..b67ee054c 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -532,6 +532,7 @@ if "%%1" == "help" ( \techo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter \techo. text to make text files \techo. man to make manual pages +\techo. texinfo to make Texinfo files \techo. gettext to make PO message catalogs \techo. changes to make an overview over all changed/added/deprecated items \techo. linkcheck to check all external links for integrity @@ -646,8 +647,17 @@ if "%%1" == "man" ( \tgoto end ) +if "%%1" == "texinfo" ( +\t%%SPHINXBUILD%% -b texinfo %%ALLSPHINXOPTS%% %%BUILDDIR%%/texinfo +\tif errorlevel 1 exit /b 1 +\techo. +\techo.Build finished. The Texinfo files are in %%BUILDDIR%%/texinfo. +\tgoto end +) + if "%%1" == "gettext" ( \t%%SPHINXBUILD%% -b gettext %%ALLSPHINXOPTS%% %%BUILDDIR%%/locale +\tif errorlevel 1 exit /b 1 \techo. \techo.Build finished. The message catalogs are in %%BUILDDIR%%/locale. \tgoto end From ead89093b57b368dba3d406ddad786a720932aff Mon Sep 17 00:00:00 2001 From: Jonathan Jacobs <korpse@slipgate.za.net> Date: Sun, 24 Oct 2010 23:30:00 +0200 Subject: [PATCH 418/744] Implement "man_show_urls" configuration option to show the URL after external references in manpages. --- sphinx/config.py | 1 + sphinx/quickstart.py | 3 +++ sphinx/writers/manpage.py | 13 +++++++++++++ 3 files changed, 17 insertions(+) diff --git a/sphinx/config.py b/sphinx/config.py index 6f957314a..13d0a9900 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -159,6 +159,7 @@ class Config(object): # manpage options man_pages = ([], None), + man_show_urls = (False, None), # Texinfo options texinfo_documents = ([], None), diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index b67ee054c..016460a24 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -255,6 +255,9 @@ man_pages = [ [u'%(author_str)s'], 1) ] +# If true, show URL addresses after external links. +#man_show_urls = False + # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index 5f93c7ebb..2a2152be9 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -235,6 +235,19 @@ class ManualPageTranslator(BaseTranslator): self.body.append(self.defs['reference'][0]) self.body.append(node.astext()) self.body.append(self.defs['reference'][1]) + + uri = node.get('refuri', '') + if uri.startswith('mailto:') or uri.startswith('http:') or \ + uri.startswith('https:') or uri.startswith('ftp:'): + # if configured, put the URL after the link + if self.builder.config.man_show_urls and \ + node.astext() != uri: + if uri.startswith('mailto:'): + uri = uri[7:] + self.body.extend([ + ' <', + self.defs['strong'][0], uri, self.defs['strong'][1], + '>']) raise nodes.SkipNode def visit_centered(self, node): From 5ad1c7d03e51de4af5863ca192c2cd78ffa8014e Mon Sep 17 00:00:00 2001 From: Jonathan Jacobs <korpse@slipgate.za.net> Date: Sun, 24 Oct 2010 23:40:16 +0200 Subject: [PATCH 419/744] Update config.rst with man_show_urls option, it still needs a version directive. --- doc/config.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/config.rst b/doc/config.rst index 90730d60c..86b3b2333 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -1040,6 +1040,10 @@ These options influence manual page output. .. versionadded:: 1.0 +.. confval:: man_show_urls + + If true, add URL addresses after links. Default is ``False``. + .. _texinfo-options: From 42208cf974cca3e66e039351947189d3da0bb2cb Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Thu, 28 Oct 2010 21:23:32 +0200 Subject: [PATCH 420/744] Use dashes rather than underscores in CSS classes and IDs. --- sphinx/themes/basic/static/websupport.js | 76 ++++++++++++------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 870b0cdc0..5bec8ec13 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -39,7 +39,7 @@ } function initEvents() { - $('a.comment_close').live("click", function(event) { + $('a.comment-close').live("click", function(event) { hide($(this).attr('id').substring(2)); return false; }); @@ -51,39 +51,39 @@ openReply($(this).attr('id').substring(2)); return false; }); - $('a.close_reply').live("click", function() { + $('a.close-reply').live("click", function() { closeReply($(this).attr('id').substring(2)); return false; }); - $('a.sort_option').live("click", function(event) { + $('a.sort-option').live("click", function(event) { handleReSort($(this)); return false; }); - $('a.show_proposal').live("click", function() { + $('a.show-proposal').live("click", function() { showProposal($(this).attr('id').substring(2)); return false; }); - $('a.hide_proposal').live("click", function() { + $('a.hide-proposal').live("click", function() { hideProposal($(this).attr('id').substring(2)); return false; }); - $('a.show_propose_change').live("click", function() { + $('a.show-propose-change').live("click", function() { showProposeChange($(this).attr('id').substring(2)); return false; }); - $('a.hide_propose_change').live("click", function() { + $('a.hide-propose-change').live("click", function() { hideProposeChange($(this).attr('id').substring(2)); return false; }); - $('a.accept_comment').live("click", function() { + $('a.accept-comment').live("click", function() { acceptComment($(this).attr('id').substring(2)); return false; }); - $('a.reject_comment').live("click", function() { + $('a.reject-comment').live("click", function() { rejectComment($(this).attr('id').substring(2)); return false; }); - $('a.delete_comment').live("click", function() { + $('a.delete-comment').live("click", function() { deleteComment($(this).attr('id').substring(2)); return false; }); @@ -330,9 +330,9 @@ success: function(data, textStatus, request) { var div = $('#cd' + id); div - .find('span.user_id:first') + .find('span.user-id:first') .text('[deleted]').end() - .find('p.comment_text:first') + .find('p.comment-text:first') .text('[deleted]').end() .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) @@ -383,7 +383,7 @@ function handleReSort(link) { var classes = link.attr('class').split(/\s+/); for (var i=0; i<classes.length; i++) { - if (classes[i] != 'sort_option') { + if (classes[i] != 'sort-option') { by = classes[i]; } } @@ -393,7 +393,7 @@ expiration.setDate(expiration.getDate() + 365); document.cookie= 'sortBy=' + escape(by) + ';expires=' + expiration.toUTCString(); - $('ul.comment_ul').each(function(index, ul) { + $('ul.comment-ul').each(function(index, ul) { var comments = getChildren($(ul), true); comments = sortComments(comments); appendComments(comments, $(ul).empty()); @@ -577,7 +577,7 @@ } function showError(message) { - $(document.createElement('div')).attr({'class': 'popup_error'}) + $(document.createElement('div')).attr({'class': 'popup-error'}) .append($(document.createElement('h1')).text(message)) .appendTo('body') .fadeIn("slow") @@ -598,7 +598,7 @@ .append( $(document.createElement('a')).attr({ href: '#', - 'class': 'sphinx_comment', + 'class': 'sphinx-comment', id: 'ao' + id }) .append($(document.createElement('img')).attr({ @@ -614,7 +614,7 @@ .append( $(document.createElement('a')).attr({ href: '#', - 'class': 'sphinx_comment_close hidden', + 'class': 'sphinx-comment-close hidden', id: 'ah' + id }) .append($(document.createElement('img')).attr({ @@ -651,7 +651,7 @@ var replyTemplate = '\ <li>\ - <div class="reply_div" id="rd<%id%>">\ + <div class="reply-div" id="rd<%id%>">\ <form id="rf<%id%>">\ <textarea name="comment" cols="80"></textarea>\ <input type="submit" value="add reply" />\ @@ -681,28 +681,28 @@ </a>\ </div>\ </div>\ - <div class="comment_content">\ + <div class="comment-content">\ <p class="tagline comment">\ - <span class="user_id"><%username%></span>\ + <span class="user-id"><%username%></span>\ <span class="rating"><%pretty_rating%></span>\ <span class="delta"><%time.delta%></span>\ </p>\ - <p class="comment_text comment"><%text%></p>\ - <p class="comment_opts comment">\ + <p class="comment-text comment"><%text%></p>\ + <p class="comment-opts comment">\ <a href="#" class="reply hidden" id="rl<%id%>">reply ▹</a>\ - <a href="#" class="close_reply" id="cr<%id%>">reply ▿</a>\ - <a href="#" id="sp<%id%>" class="show_proposal">\ + <a href="#" class="close-reply" id="cr<%id%>">reply ▿</a>\ + <a href="#" id="sp<%id%>" class="show-proposal">\ proposal ▹\ </a>\ - <a href="#" id="hp<%id%>" class="hide_proposal">\ + <a href="#" id="hp<%id%>" class="hide-proposal">\ proposal ▿\ </a>\ - <a href="#" id="dc<%id%>" class="delete_comment hidden">\ + <a href="#" id="dc<%id%>" class="delete-comment hidden">\ delete\ </a>\ <span id="cm<%id%>" class="moderation hidden">\ - <a href="#" id="ac<%id%>" class="accept_comment">accept</a>\ - <a href="#" id="rc<%id%>" class="reject_comment">reject</a>\ + <a href="#" id="ac<%id%>" class="accept-comment">accept</a>\ + <a href="#" id="rc<%id%>" class="reject-comment">reject</a>\ </span>\ </p>\ <pre class="proposal" id="pr<%id%>">\ @@ -715,15 +715,15 @@ </div>'; var popupTemplate = '\ - <div class="sphinx_comments" id="sc<%id%>">\ + <div class="sphinx-comments" id="sc<%id%>">\ <h1>Comments</h1>\ - <form method="post" id="cf<%id%>" class="comment_form" action="/docs/add_comment">\ + <form method="post" id="cf<%id%>" class="comment-form" action="/docs/add_comment">\ <textarea name="comment" cols="80"></textarea>\ - <p class="propose_button">\ - <a href="#" id="pc<%id%>" class="show_propose_change">\ + <p class="propose-button">\ + <a href="#" id="pc<%id%>" class="show-propose-change">\ Propose a change ▹\ </a>\ - <a href="#" id="hc<%id%>" class="hide_propose_change">\ + <a href="#" id="hc<%id%>" class="hide-propose-change">\ Propose a change ▿\ </a>\ </p>\ @@ -731,15 +731,15 @@ <input type="submit" value="add comment" />\ <input type="hidden" name="node" value="<%id%>" />\ <input type="hidden" name="parent" value="" />\ - <p class="sort_options">\ + <p class="sort-options">\ Sort by:\ - <a href="#" class="sort_option rating">top</a>\ - <a href="#" class="sort_option ascage">newest</a>\ - <a href="#" class="sort_option age">oldest</a>\ + <a href="#" class="sort-option rating">top</a>\ + <a href="#" class="sort-option ascage">newest</a>\ + <a href="#" class="sort-option age">oldest</a>\ </p>\ </form>\ <h3 id="cn<%id%>">loading comments... <img src="<%loadingImage%>" alt="" /></h3>\ - <ul id="cl<%id%>" class="comment_ul"></ul>\ + <ul id="cl<%id%>" class="comment-ul"></ul>\ </div>'; $(document).ready(function() { From 99a74ad502ea8be719089230b0223f331236542b Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Thu, 28 Oct 2010 21:28:44 +0200 Subject: [PATCH 421/744] Add header and use docstring convention. --- sphinx/themes/basic/static/websupport.js | 129 ++++++++++++----------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 5bec8ec13..ac247c5b8 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -1,3 +1,14 @@ +/* + * websupport.js + * ~~~~~~~~~~~~~ + * + * sphinx.websupport utilties for all documentation. + * + * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + (function($) { $.fn.autogrow = function(){ return this.each(function(){ @@ -89,10 +100,10 @@ }); } - /* - Set comp, which is a comparator function used for sorting and - inserting comments into the list. - */ + /** + * Set comp, which is a comparator function used for sorting and + * inserting comments into the list. + */ function setComparator() { // If the first three letters are "asc", sort in ascending order // and remove the prefix. @@ -109,10 +120,10 @@ $('a.' + by).removeAttr('href').addClass('sel'); } - /* - Create a comp function. If the user has preferences stored in - the sortBy cookie, use those, otherwise use the default. - */ + /** + * Create a comp function. If the user has preferences stored in + * the sortBy cookie, use those, otherwise use the default. + */ function initComparator() { by = 'rating'; // Default to sort by rating. // If the sortBy cookie is set, use that instead. @@ -130,9 +141,9 @@ setComparator(); } - /* - Show a comment div. - */ + /** + * Show a comment div. + */ function show(id) { $('#ao' + id).hide(); $('#ah' + id).show(); @@ -151,9 +162,9 @@ }); } - /* - Hide a comment div. - */ + /** + * Hide a comment div. + */ function hide(id) { $('#ah' + id).hide(); $('#ao' + id).show(); @@ -163,10 +174,10 @@ }); } - /* - Perform an ajax request to get comments for a node - and insert the comments into the comments tree. - */ + /** + * Perform an ajax request to get comments for a node + * and insert the comments into the comments tree. + */ function getComments(id) { $.ajax({ type: 'GET', @@ -199,9 +210,9 @@ }); } - /* - Add a comment via ajax and insert the comment into the comment tree. - */ + /** + * Add a comment via ajax and insert the comment into the comment tree. + */ function addComment(form) { // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); @@ -242,10 +253,10 @@ }); } - /* - Recursively append comments to the main comment list and children - lists, creating the comment tree. - */ + /** + * Recursively append comments to the main comment list and children + * lists, creating the comment tree. + */ function appendComments(comments, ul) { $.each(comments, function() { var div = createCommentDiv(this); @@ -257,10 +268,10 @@ }); } - /* - After adding a new comment, it must be inserted in the correct - location in the comment tree. - */ + /** + * After adding a new comment, it must be inserted in the correct + * location in the comment tree. + */ function insertComment(comment) { var div = createCommentDiv(comment); @@ -377,9 +388,9 @@ textarea.slideUp('fast'); } - /* - Handle when the user clicks on a sort by link. - */ + /** + * Handle when the user clicks on a sort by link. + */ function handleReSort(link) { var classes = link.attr('class').split(/\s+/); for (var i=0; i<classes.length; i++) { @@ -400,9 +411,9 @@ }); } - /* - Function to process a vote when a user clicks an arrow. - */ + /** + * Function to process a vote when a user clicks an arrow. + */ function handleVote(link) { if (!opts.voting) { showError("You'll need to login to vote."); @@ -458,9 +469,9 @@ }); } - /* - Open a reply form used to reply to an existing comment. - */ + /** + * Open a reply form used to reply to an existing comment. + */ function openReply(id) { // Swap out the reply link for the hide link $('#rl' + id).hide(); @@ -480,9 +491,9 @@ div.slideDown('fast'); } - /* - Close the reply form opened with openReply. - */ + /** + * Close the reply form opened with openReply. + */ function closeReply(id) { // Remove the reply div from the DOM. $('#rd' + id).slideUp('fast', function() { @@ -494,9 +505,9 @@ $('#rl' + id).show(); } - /* - Recursively sort a tree of comments using the comp comparator. - */ + /** + * Recursively sort a tree of comments using the comp comparator. + */ function sortComments(comments) { comments.sort(comp); $.each(comments, function() { @@ -505,10 +516,10 @@ return comments; } - /* - Get the children comments from a ul. If recursive is true, - recursively include childrens' children. - */ + /** + * Get the children comments from a ul. If recursive is true, + * recursively include childrens' children. + */ function getChildren(ul, recursive) { var children = []; ul.children().children("[id^='cd']") @@ -522,9 +533,9 @@ return children; } - /* - Create a div to display a comment in. - */ + /** + * Create a div to display a comment in. + */ function createCommentDiv(comment) { // Prettify the comment rating. comment.pretty_rating = comment.rating + ' point' + @@ -555,11 +566,11 @@ return div; } - /* - A simple template renderer. Placeholders such as <%id%> are replaced - by context['id'] with items being escaped. Placeholders such as <#id#> - are not escaped. - */ + /** + * A simple template renderer. Placeholders such as <%id%> are replaced + * by context['id'] with items being escaped. Placeholders such as <#id#> + * are not escaped. + */ function renderTemplate(template, context) { var esc = $(document.createElement('div')); @@ -585,9 +596,9 @@ .fadeOut("slow"); } - /* - Add a link the user uses to open the comments popup. - */ + /** + * Add a link the user uses to open the comments popup. + */ $.fn.comment = function() { return this.each(function() { var id = $(this).attr('id').substring(1); From f0f9f7b1b2d54246b7ec77547f3cade6afa20967 Mon Sep 17 00:00:00 2001 From: Doug Hellmann <doug@doughellmann.com> Date: Thu, 28 Oct 2010 22:10:24 -0400 Subject: [PATCH 422/744] add caption option to graphviz nodes and render the caption in latex output --- sphinx/ext/graphviz.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index b4449e182..f44e51366 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -52,6 +52,7 @@ class Graphviz(Directive): option_spec = { 'alt': directives.unchanged, 'inline': directives.flag, + 'caption': directives.unchanged, } def run(self): @@ -85,6 +86,8 @@ class Graphviz(Directive): node['options'] = [] if 'alt' in self.options: node['alt'] = self.options['alt'] + if 'caption' in self.options: + node['caption'] = self.options['caption'] node['inline'] = 'inline' in self.options return [node] @@ -100,6 +103,7 @@ class GraphvizSimple(Directive): option_spec = { 'alt': directives.unchanged, 'inline': directives.flag, + 'caption': directives.unchanged, } def run(self): @@ -109,6 +113,8 @@ class GraphvizSimple(Directive): node['options'] = [] if 'alt' in self.options: node['alt'] = self.options['alt'] + if 'caption' in self.options: + node['caption'] = self.options['caption'] node['inline'] = 'inline' in self.options return [node] @@ -264,13 +270,24 @@ def render_dot_latex(self, node, code, options, prefix='graphviz'): self.builder.warn('dot code %r: ' % code + str(exc)) raise nodes.SkipNode - if node.get('inline', False): + inline = node.get('inline', False) + if inline: para_separator = '' else: para_separator = '\n' if fname is not None: - self.body.append('%s\\includegraphics{%s}%s' % (para_separator, fname, para_separator)) + caption = node.get('caption') + if caption: + self.body.append('\n\\begin{figure}[h!]') + self.body.append('\n\\begin{center}') + self.body.append('\n\\caption{%s}' % caption) + self.body.append('\n\\label{figure:%s}' % caption) + self.body.append('\n\\includegraphics{%s}' % fname) + self.body.append('\n\\end{center}') + self.body.append('\n\\end{figure}\n') + else: + self.body.append('%s\\includegraphics{%s}' % (para_separator, fname, para_separator)) raise nodes.SkipNode From 51edd7e8109781e33bb2c27e1b6ee8ab74b8b62a Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 29 Oct 2010 07:04:13 +0200 Subject: [PATCH 423/744] Nits. --- sphinx/themes/basic/static/websupport.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index ac247c5b8..51a5a7d7c 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -10,8 +10,8 @@ */ (function($) { - $.fn.autogrow = function(){ - return this.each(function(){ + $.fn.autogrow = function() { + return this.each(function() { var textarea = this; $.fn.autogrow.resize(textarea); @@ -582,7 +582,7 @@ return escape ? esc.text(cur || "").html() : cur; } - return template.replace(/<([%#])([\w\.]*)\1>/g, function(){ + return template.replace(/<([%#])([\w\.]*)\1>/g, function() { return handle(arguments[2], arguments[1] == '%' ? true : false); }); } From 65505907f1f09ccc2385d21758254f9516abb5de Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 29 Oct 2010 07:17:14 +0200 Subject: [PATCH 424/744] Fix loading JS searchindex data in Py3k. --- sphinx/builders/html.py | 6 +++++- sphinx/util/jsdump.py | 4 +++- sphinx/util/pycompat.py | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index cdeb334a2..b85e94423 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -614,7 +614,11 @@ class StandaloneHTMLBuilder(Builder): def load_indexer(self, docnames): keep = set(self.env.all_docs) - set(docnames) try: - f = open(path.join(self.outdir, self.searchindex_filename), 'rb') + searchindexfn = path.join(self.outdir, self.searchindex_filename) + if self.indexer_dumps_unicode: + f = codecs.open(searchindexfn, 'r', encoding='utf-8') + else: + f = open(searchindexfn, 'rb') try: self.indexer.load(f, self.indexer_format) finally: diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py index e357128c2..bfa1895f4 100644 --- a/sphinx/util/jsdump.py +++ b/sphinx/util/jsdump.py @@ -12,6 +12,8 @@ import re +from sphinx.util.pycompat import u + _str_re = re.compile(r'"(\\\\|\\"|[^"])*"') _int_re = re.compile(r'\d+') _name_re = re.compile(r'[a-zA-Z]\w*') @@ -50,7 +52,7 @@ def encode_string(s): return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"' def decode_string(s): - return ESCAPED.sub(lambda m: eval('u"'+m.group()+'"'), s) + return ESCAPED.sub(lambda m: eval(u + '"' + m.group() + '"'), s) reswords = set("""\ diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 319312a75..5834b7b72 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -23,6 +23,8 @@ if sys.version_info >= (3, 0): def b(s): return s.encode('utf-8') bytes = bytes + # prefix for Unicode strings + u = '' # support for running 2to3 over config files def convert_with_2to3(filepath): from lib2to3.refactor import RefactoringTool, get_fixers_from_package @@ -45,6 +47,7 @@ else: class_types = (type, ClassType) b = str bytes = str + u = 'u' # no need to refactor on 2.x versions convert_with_2to3 = None From c62a19bca5b262ab46dbc7737157352805424f96 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 30 Oct 2010 16:47:05 +0000 Subject: [PATCH 425/744] Make Theme class usable without a builder object. --- sphinx/builders/changes.py | 3 ++- sphinx/builders/html.py | 3 ++- sphinx/theming.py | 11 ++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py index 980ed7606..488bc0374 100644 --- a/sphinx/builders/changes.py +++ b/sphinx/builders/changes.py @@ -30,7 +30,8 @@ class ChangesBuilder(Builder): def init(self): self.create_template_bridge() - Theme.init_themes(self) + Theme.init_themes(self.confdir, self.config.html_theme_path, + warn=self.warn) self.theme = Theme('default') self.templates.init(self, self.theme) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index b85e94423..3d4370530 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -117,7 +117,8 @@ class StandaloneHTMLBuilder(Builder): return self.config.html_theme, self.config.html_theme_options def init_templates(self): - Theme.init_themes(self) + Theme.init_themes(self.confdir, self.config.html_theme_path, + warn=self.warn) themename, themeoptions = self.get_theme_config() self.theme = Theme(themename) self.theme_options = themeoptions.copy() diff --git a/sphinx/theming.py b/sphinx/theming.py index 92e63f31c..42bc44f63 100644 --- a/sphinx/theming.py +++ b/sphinx/theming.py @@ -30,13 +30,13 @@ class Theme(object): themes = {} @classmethod - def init_themes(cls, builder): + def init_themes(cls, confdir, theme_path, warn=None): """Search all theme paths for available themes.""" - cls.themepath = list(builder.config.html_theme_path) + cls.themepath = list(theme_path) cls.themepath.append(path.join(package_dir, 'themes')) for themedir in cls.themepath[::-1]: - themedir = path.join(builder.confdir, themedir) + themedir = path.join(confdir, themedir) if not path.isdir(themedir): continue for theme in os.listdir(themedir): @@ -48,8 +48,9 @@ class Theme(object): tname = theme[:-4] tinfo = zfile except Exception: - builder.warn('file %r on theme path is not a valid ' - 'zipfile or contains no theme' % theme) + if warn: + warn('file %r on theme path is not a valid ' + 'zipfile or contains no theme' % theme) continue else: if not path.isfile(path.join(themedir, theme, THEMECONF)): From 7f254f1a77ca8d6ac7a1020203fd3f76f16ec6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Sun, 31 Oct 2010 13:04:13 +0100 Subject: [PATCH 426/744] Fixed a typo. Thanks to Thomas Waldmann for finding it. --- doc/markup/para.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/markup/para.rst b/doc/markup/para.rst index 42bcc33cc..302d9c1ba 100644 --- a/doc/markup/para.rst +++ b/doc/markup/para.rst @@ -52,7 +52,7 @@ units as well as normal text: Similar to :rst:dir:`versionadded`, but describes when and what changed in the named feature in some way (new parameters, changed side effects, etc.). -.. rst:directive:: .. deprecated:: vesion +.. rst:directive:: .. deprecated:: version Similar to :rst:dir:`versionchanged`, but describes when the feature was deprecated. An explanation can also be given, for example to inform the From 34707fefacbecc95daa5e7fc54bbf5adb2727a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= <ich@danielneuhaeuser.de> Date: Tue, 2 Nov 2010 13:15:18 +0100 Subject: [PATCH 427/744] Ignore files generated when building the test docs This is really helpful if you want to take a look at the HTML output --- .hgignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgignore b/.hgignore index b68cccf91..45ecebc31 100644 --- a/.hgignore +++ b/.hgignore @@ -17,3 +17,5 @@ ~$ ^utils/.*3\.py$ ^distribute- +^tests/root/_build/* +^tests/root/generated/* From 6aba1ba781fcc6ccb40c5047a18192a3b41cc889 Mon Sep 17 00:00:00 2001 From: Jonathan Waltman <jonathan.waltman@gmail.com> Date: Sun, 7 Nov 2010 00:26:40 -0500 Subject: [PATCH 428/744] Improve Texinfo xref appearance in non Info output --- sphinx/writers/texinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index f7b3a9bb6..83dbe7248 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -388,7 +388,7 @@ class TexinfoTranslator(nodes.NodeVisitor): if not name or ref == name: self.add_text('@pxref{%s}' % ref) else: - self.add_text('@pxref{%s,%s}' % (ref, name)) + self.add_text('@pxref{%s,,%s}' % (ref, name)) self.referenced_ids.add(ref) ## Visiting From 58e8e4911131103bceafd9e24b00fff9720cd3e6 Mon Sep 17 00:00:00 2001 From: Jonathan Waltman <jonathan.waltman@gmail.com> Date: Sun, 7 Nov 2010 00:41:47 -0500 Subject: [PATCH 429/744] Implement image handling for Texinfo builder --- sphinx/builders/texinfo.py | 15 +++++++++++-- sphinx/writers/texinfo.py | 42 ++++++++++++++++++++++++++++++++++--- tests/test_build_texinfo.py | 7 ++++++- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py index b08c0cd6b..e12a35665 100644 --- a/sphinx/builders/texinfo.py +++ b/sphinx/builders/texinfo.py @@ -21,7 +21,7 @@ from sphinx.locale import _ from sphinx.builders import Builder from sphinx.environment import NoUri from sphinx.util.nodes import inline_all_toctrees -from sphinx.util.osutil import SEP +from sphinx.util.osutil import SEP, copyfile from sphinx.util.console import bold, darkgreen from sphinx.writers.texinfo import TexinfoWriter @@ -86,7 +86,8 @@ class TexinfoBuilder(Builder): """ name = 'texinfo' format = 'texinfo' - supported_image_types = [] + supported_image_types = ['application/pdf', 'image/png', + 'image/gif', 'image/jpeg'] def init(self): self.docnames = [] @@ -154,6 +155,7 @@ class TexinfoBuilder(Builder): nodes.Text('@printindex ge\n', '@printindex ge\n'), format="texinfo"))) + self.post_process_images(doctree) docwriter = TexinfoWriter(self) settings = OptionParser( defaults=self.env.settings, @@ -213,6 +215,15 @@ class TexinfoBuilder(Builder): return largetree def finish(self): + # copy image files + if self.images: + self.info(bold('copying images...'), nonl=1) + for src, dest in self.images.iteritems(): + self.info(' '+src, nonl=1) + copyfile(path.join(self.srcdir, src), + path.join(self.outdir, dest)) + self.info() + self.info(bold('copying Texinfo support files... '), nonl=True) # copy Makefile fn = path.join(self.outdir, 'Makefile') diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 83dbe7248..8768c4867 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -9,6 +9,9 @@ :license: BSD, see LICENSE for details. """ +import re +from os import path + from docutils import nodes, writers from sphinx import addnodes @@ -27,6 +30,7 @@ Generated by Sphinx @defindex ge @paragraphindent %(paragraphindent)s @exampleindent %(exampleindent)s +@afourlatex %(direntry)s @c %%**end of header @@ -96,7 +100,6 @@ def escape_id(s): s = ' '.join(s.split()).strip() return s - class TexinfoWriter(writers.Writer): """Texinfo writer for generating Texinfo documents.""" supported = ('texinfo', 'texi') @@ -147,6 +150,8 @@ class TexinfoWriter(writers.Writer): class TexinfoTranslator(nodes.NodeVisitor): + ignore_missing_images = False + default_elements = { 'filename': '', 'title': '', @@ -359,6 +364,21 @@ class TexinfoTranslator(nodes.NodeVisitor): self.add_text('\n@end menu\n\n') + def tex_image_length(self, width_str): + match = re.match('(\d*\.?\d*)\s*(\S*)', width_str) + if not match: + # fallback + return width_str + res = width_str + amount, unit = match.groups()[:2] + if not unit or unit == "px": + # pixels: let TeX alone + return '' + elif unit == "%": + # a4paper: textwidth=418.25368pt + res = "%d.0pt" % (float(amount) * 4.1825368) + return res + ## xref handling def get_short_id(self, id): @@ -928,8 +948,24 @@ class TexinfoTranslator(nodes.NodeVisitor): self.add_text('}\n') def visit_image(self, node): - self.add_text('@w{[image]}') - raise nodes.SkipNode + if node['uri'] in self.builder.images: + uri = self.builder.images[node['uri']] + else: + # missing image! + if self.ignore_missing_images: + return + uri = node['uri'] + if uri.find('://') != -1: + # ignore remote images + return + name, ext = path.splitext(uri) + attrs = node.attributes + # ignored in non-tex output + width = self.tex_image_length(attrs.get('width', '')) + height = self.tex_image_length(attrs.get('height', '')) + alt = escape_arg(attrs.get('alt', '')) + self.add_text('\n\n@image{%s,%s,%s,%s,%s}\n\n' % + (name, width, height, alt, ext[1:])) def depart_image(self, node): pass diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 2b2c8efdf..4785abebd 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -15,6 +15,8 @@ import sys from StringIO import StringIO from subprocess import Popen, PIPE +from sphinx.writers.texinfo import TexinfoTranslator + from util import * from test_build_html import ENV_WARNINGS @@ -25,7 +27,9 @@ def teardown_module(): texinfo_warnfile = StringIO() -TEXINFO_WARNINGS = ENV_WARNINGS +TEXINFO_WARNINGS = ENV_WARNINGS + """\ +None:None: WARNING: no matching candidate for image URI u'foo.\\*' +""" if sys.version_info >= (3, 0): TEXINFO_WARNINGS = remove_unicode_literals(TEXINFO_WARNINGS) @@ -33,6 +37,7 @@ if sys.version_info >= (3, 0): @with_app(buildername='texinfo', warning=texinfo_warnfile, cleanenv=True) def test_texinfo(app): + TexinfoTranslator.ignore_missing_images = True app.builder.build_all() texinfo_warnings = texinfo_warnfile.getvalue().replace(os.sep, '/') texinfo_warnings_exp = TEXINFO_WARNINGS % {'root': app.srcdir} From 7df688ea6c5ca7eb0ff67750aef7ed35beec35a1 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Fri, 12 Nov 2010 08:28:47 +0100 Subject: [PATCH 430/744] Style fix. --- sphinx/websupport/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 303031323..a0a6787ae 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -357,7 +357,7 @@ class WebSupport(object): """ self.base_comment_opts = {} - if self.docroot is not '': + if self.docroot != '': comment_urls = [ ('addCommentURL', 'add_comment'), ('getCommentsURL', 'get_comments'), From b151aa56e1b755125f0236b736ef2038f9f3a6eb Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Sat, 13 Nov 2010 12:49:39 +0100 Subject: [PATCH 431/744] Clean up coding style for better comprehensibility. --- sphinx/environment.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 0efdcdd34..7274a76db 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -201,15 +201,13 @@ class Locale(Transform): # fetch translations dirs = [path.join(env.srcdir, x) for x in env.config.locale_dirs] - catalog, empty = init_locale(dirs, env.config.language, section) - if not empty: + catalog, has_catalog = init_locale(dirs, env.config.language, section) + if not has_catalog: return parser = RSTParser() for node, msg in extract_messages(self.document): - # XXX ctx not used - #ctx = node.parent patch = new_document(source, settings) msgstr = catalog.gettext(msg) # XXX add marker to untranslated parts From f411dbce2393a057665295fe01ebe94c07a6fb16 Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Sat, 13 Nov 2010 12:50:52 +0100 Subject: [PATCH 432/744] Ignored translator errors in non-paragraphs for now. --- sphinx/environment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 7274a76db..3e69baa1b 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -215,7 +215,9 @@ class Locale(Transform): continue parser.parse(msgstr, patch) patch = patch[0] - assert isinstance(patch, nodes.paragraph) + #XXX doctest and other block markup + if not isinstance(patch, nodes.paragraph): + continue # skip for now for child in patch.children: # update leaves child.parent = node node.children = patch.children From b72015643701d4d5a3750aafd050439edc032e14 Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Sat, 13 Nov 2010 12:59:12 +0100 Subject: [PATCH 433/744] Tested section grouping for translations. --- tests/test_build_gettext.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index ba2440fd4..2004958bc 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -82,6 +82,9 @@ def setup_patch(): p = Popen(['msgfmt', test_root / 'bom.po', '-o', test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo'], stdout=PIPE, stderr=PIPE) + p = Popen(['msgfmt', test_root / 'subdir.po', '-o', + test_root / 'xx' / 'LC_MESSAGES' / 'subdir.mo'], + stdout=PIPE, stderr=PIPE) except OSError: return # most likely msgfmt was not found else: @@ -99,12 +102,14 @@ def teardown_patch(): @with_app(buildername='text', confoverrides={'language': 'xx', 'locale_dirs': ['.']}) def test_patch(app): - app.builder.build(['bom']) + app.builder.build(['bom', 'subdir/includes']) result = (app.outdir / 'bom.txt').text(encoding='utf-8') expect = (u"\nDatei mit UTF-8" u"\n***************\n" # underline matches new translation u"\nThis file has umlauts: äöü.\n") assert result == expect + result = (app.outdir / 'subdir' / 'includes.txt').text(encoding='utf-8') + assert result.startswith(u"\ntranslation\n***********\n\n") test_patch.setup = setup_patch test_patch.teardown = teardown_patch From 3d006f827d8234090a015cd092baaae4ac8fc48c Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Sat, 13 Nov 2010 13:14:35 +0100 Subject: [PATCH 434/744] Transform false positives into skipped tests. --- tests/test_build_gettext.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index 2004958bc..be35c5695 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -14,6 +14,7 @@ import os from subprocess import Popen, PIPE from util import * +from util import SkipTest def teardown_module(): @@ -41,7 +42,7 @@ def test_gettext(app): '--locale', 'en_US'], stdout=PIPE, stderr=PIPE) except OSError: - return # most likely msginit was not found + raise SkipTest # most likely msginit was not found else: stdout, stderr = p.communicate() if p.returncode != 0: @@ -55,7 +56,7 @@ def test_gettext(app): os.path.join('en', 'LC_MESSAGES', 'test_root.mo')], stdout=PIPE, stderr=PIPE) except OSError: - return # most likely msgfmt was not found + raise SkipTest # most likely msgfmt was not found else: stdout, stderr = p.communicate() if p.returncode != 0: @@ -86,7 +87,7 @@ def setup_patch(): test_root / 'xx' / 'LC_MESSAGES' / 'subdir.mo'], stdout=PIPE, stderr=PIPE) except OSError: - return # most likely msgfmt was not found + raise SkipTest # most likely msgfmt was not found else: stdout, stderr = p.communicate() if p.returncode != 0: From 3ab3400c1247dce2c86e753cb7cd93f5a62a149a Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Sat, 13 Nov 2010 13:15:15 +0100 Subject: [PATCH 435/744] Remove generated catalogs properly after tests. --- tests/test_build_gettext.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index be35c5695..ca47909b3 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -19,6 +19,7 @@ from util import SkipTest def teardown_module(): (test_root / '_build').rmtree(True) + (test_root / 'xx').rmtree(True) @with_app(buildername='gettext') From f055c1593f0dd2e3b716283b9e2a3d8dad5cbaf8 Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Sun, 14 Nov 2010 19:42:50 +0100 Subject: [PATCH 436/744] Split up sphinx-i18n tests into build and patch phase. --- tests/test_build_gettext.py | 56 +++++++------------------------------ tests/test_intl.py | 54 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 46 deletions(-) create mode 100644 tests/test_intl.py diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py index ca47909b3..ab68289e3 100644 --- a/tests/test_build_gettext.py +++ b/tests/test_build_gettext.py @@ -19,17 +19,24 @@ from util import SkipTest def teardown_module(): (test_root / '_build').rmtree(True) - (test_root / 'xx').rmtree(True) + + +@with_app(buildername='gettext') +def test_all(app): + # Generic build; should fail only when the builder is horribly broken. + app.builder.build_all() @with_app(buildername='gettext') def test_build(app): + # Do messages end up in the correct location? app.builder.build(['extapi', 'subdir/includes']) - # documents end up in a message catalog + # top-level documents end up in a message catalog assert (app.outdir / 'extapi.pot').isfile() - # ..and are grouped into sections + # directory items are grouped into sections assert (app.outdir / 'subdir.pot').isfile() + @with_app(buildername='gettext') def test_gettext(app): app.builder.build(['markup']) @@ -72,46 +79,3 @@ def test_gettext(app): _ = gettext.translation('test_root', app.outdir, languages=['en']).gettext assert _("Testing various markup") == u"Testing various markup" - -@with_app(buildername='gettext') -def test_all(app): - app.builder.build_all() - - -def setup_patch(): - (test_root / 'xx' / 'LC_MESSAGES').makedirs() - try: - p = Popen(['msgfmt', test_root / 'bom.po', '-o', - test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo'], - stdout=PIPE, stderr=PIPE) - p = Popen(['msgfmt', test_root / 'subdir.po', '-o', - test_root / 'xx' / 'LC_MESSAGES' / 'subdir.mo'], - stdout=PIPE, stderr=PIPE) - except OSError: - raise SkipTest # most likely msgfmt was not found - else: - stdout, stderr = p.communicate() - if p.returncode != 0: - print stdout - print stderr - assert False, 'msgfmt exited with return code %s' % p.returncode - assert (test_root / 'xx' / 'LC_MESSAGES' / 'bom.mo').isfile(), \ - 'msgfmt failed' - -def teardown_patch(): - (test_root / 'xx').rmtree() - -@with_app(buildername='text', - confoverrides={'language': 'xx', 'locale_dirs': ['.']}) -def test_patch(app): - app.builder.build(['bom', 'subdir/includes']) - result = (app.outdir / 'bom.txt').text(encoding='utf-8') - expect = (u"\nDatei mit UTF-8" - u"\n***************\n" # underline matches new translation - u"\nThis file has umlauts: äöü.\n") - assert result == expect - result = (app.outdir / 'subdir' / 'includes.txt').text(encoding='utf-8') - assert result.startswith(u"\ntranslation\n***********\n\n") - -test_patch.setup = setup_patch -test_patch.teardown = teardown_patch diff --git a/tests/test_intl.py b/tests/test_intl.py new file mode 100644 index 000000000..546f6711b --- /dev/null +++ b/tests/test_intl.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +""" + test_intl + ~~~~~~~~~ + + Test message patching for internationalization purposes. Runs the text + builder in the test root. + + :copyright: Copyright 2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from subprocess import Popen, PIPE + +from util import * +from util import SkipTest + + +def setup_module(): + (test_root / 'xx' / 'LC_MESSAGES').makedirs() + # Compile all required catalogs into binary format (*.mo). + for catalog in 'bom', 'subdir': + try: + p = Popen(['msgfmt', test_root / '%s.po' % catalog, '-o', + test_root / 'xx' / 'LC_MESSAGES' / '%s.mo' % catalog], + stdout=PIPE, stderr=PIPE) + except OSError: + raise SkipTest # most likely msgfmt was not found + else: + stdout, stderr = p.communicate() + if p.returncode != 0: + print stdout + print stderr + assert False, 'msgfmt exited with return code %s' % p.returncode + assert (test_root / 'xx' / 'LC_MESSAGES' / ('%s.mo' % catalog) + ).isfile(), 'msgfmt failed' + + +def teardown_module(): + (test_root / '_build').rmtree(True) + (test_root / 'xx').rmtree(True) + + +@with_app(buildername='text', + confoverrides={'language': 'xx', 'locale_dirs': ['.']}) +def test_patch(app): + app.builder.build(['bom', 'subdir/includes']) + result = (app.outdir / 'bom.txt').text(encoding='utf-8') + expect = (u"\nDatei mit UTF-8" + u"\n***************\n" # underline matches new translation + u"\nThis file has umlauts: äöü.\n") + assert result == expect + result = (app.outdir / 'subdir' / 'includes.txt').text(encoding='utf-8') + assert result.startswith(u"\ntranslation\n***********\n\n") From 84298debb7b395b730ee790e6143e607af1df5e4 Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Sun, 14 Nov 2010 19:43:28 +0100 Subject: [PATCH 437/744] Supply missing PO file for tests. --- tests/root/subdir.po | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/root/subdir.po diff --git a/tests/root/subdir.po b/tests/root/subdir.po new file mode 100644 index 000000000..f515f2207 --- /dev/null +++ b/tests/root/subdir.po @@ -0,0 +1,9 @@ +#, fuzzy +msgid "" +msgstr "" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Including in subdir" +msgstr "translation" From 3de93d83d61a34f3cbad63d04359e9ea1ad3680e Mon Sep 17 00:00:00 2001 From: Robert Lehmann <mail@robertlehmann.de> Date: Sun, 14 Nov 2010 19:50:30 +0100 Subject: [PATCH 438/744] Split up tests for locale patching. --- tests/test_intl.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_intl.py b/tests/test_intl.py index 546f6711b..9459a1b76 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -43,12 +43,18 @@ def teardown_module(): @with_app(buildername='text', confoverrides={'language': 'xx', 'locale_dirs': ['.']}) -def test_patch(app): - app.builder.build(['bom', 'subdir/includes']) +def test_simple(app): + app.builder.build(['bom']) result = (app.outdir / 'bom.txt').text(encoding='utf-8') expect = (u"\nDatei mit UTF-8" u"\n***************\n" # underline matches new translation u"\nThis file has umlauts: äöü.\n") assert result == expect + + +@with_app(buildername='text', + confoverrides={'language': 'xx', 'locale_dirs': ['.']}) +def test_subdir(app): + app.builder.build(['subdir/includes']) result = (app.outdir / 'subdir' / 'includes.txt').text(encoding='utf-8') assert result.startswith(u"\ntranslation\n***********\n\n") From 55e70715235e77ea3c968e54d1ab419c67638ea0 Mon Sep 17 00:00:00 2001 From: Michael Jones <m.pricejones@gmail.com> Date: Thu, 18 Nov 2010 08:51:43 +1300 Subject: [PATCH 439/744] Update cpp domain identifier regex to match destructors Previously the regex would fail to match c++ destructors, as the presence of a "~" would mean that the "\b" would no longer match the start of a word. Now we try to find the optional "~" first then continue with the word as normal. --- sphinx/domains/cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index d45fd5721..ffe5e9f26 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -23,7 +23,7 @@ from sphinx.util.nodes import make_refnode from sphinx.util.compat import Directive -_identifier_re = re.compile(r'\b(~?[a-zA-Z_][a-zA-Z0-9_]*)\b') +_identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b') _whitespace_re = re.compile(r'\s+(?u)') _string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) From e36ca00744902e4b83fd0be9d84a2a6f0dcdc358 Mon Sep 17 00:00:00 2001 From: Michael Jones <m.pricejones@gmail.com> Date: Thu, 18 Nov 2010 08:56:39 +1300 Subject: [PATCH 440/744] Account for arguments with only a type and no parameter name Previously, the code was parsing the type into the argname variable and then when it found something (not "," or ")") following the type it swapped the name into the type and parsed next part as the name. However, when no name is provided, as it allowed in c++, if you're not planning on using the parameter, or it is in a function declaration, then the actual type was being left in the name variable and the type variable was empty. As a result function signatures for references were being generated without knowledge of the type, which is the important factor in disambiguating overloaded functions. --- sphinx/domains/cpp.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index ffe5e9f26..ffa76f4eb 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -697,14 +697,13 @@ class DefinitionParser(object): self.fail('expected comma between arguments') self.skip_ws() - argname = self._parse_type() - argtype = default = None + argtype = self._parse_type() + argname = default = None self.skip_ws() if self.skip_string('='): self.pos += 1 default = self._parse_default_expr() elif self.current_char not in ',)': - argtype = argname argname = self._parse_name() self.skip_ws() if self.skip_string('='): From de0cf8f665c6e12704a58c4856e0da191aa3bc34 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 20 Nov 2010 09:45:05 +0100 Subject: [PATCH 441/744] Fix calling all serializers with pickle.HIGHEST_PROTOCOL argument. --- sphinx/builders/html.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 3d4370530..6e8279f7c 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -931,6 +931,8 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): #: (pickle, simplejson etc.) implementation = None implementation_dumps_unicode = False + #: additional arguments for dump() + additional_dump_args = () #: the filename for the global context file globalcontext_filename = None @@ -959,8 +961,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): else: f = open(filename, 'wb') try: - # XXX: the third argument is pickle-specific! - self.implementation.dump(context, f, 2) + self.implementation.dump(context, f, *self.additional_dump_args) finally: f.close() @@ -1011,6 +1012,7 @@ class PickleHTMLBuilder(SerializingHTMLBuilder): """ implementation = pickle implementation_dumps_unicode = False + additional_dump_args = (pickle.HIGHEST_PROTOCOL,) indexer_format = pickle indexer_dumps_unicode = False name = 'pickle' From 47bc250f19e80ce705a6435184c22d40e4a3b8c7 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 20 Nov 2010 11:25:37 +0100 Subject: [PATCH 442/744] Avoid "import *" and remove unused import/variable. --- sphinx/builders/websupport.py | 3 +-- sphinx/websupport/__init__.py | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 303adfe64..7970f8bd7 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -11,7 +11,6 @@ import cPickle as pickle from os import path -from cgi import escape import posixpath import shutil @@ -83,7 +82,7 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin): return '/' + posixpath.join(self.app.staticdir, otheruri) ctx['pathto'] = pathto ctx['hasdoc'] = lambda name: name in self.env.all_docs - ctx['encoding'] = encoding = self.config.html_output_encoding + ctx['encoding'] = self.config.html_output_encoding ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw) self.add_sidebars(pagename, ctx) ctx.update(addctx) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index a0a6787ae..f410e10d3 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -19,9 +19,9 @@ from jinja2 import Environment, FileSystemLoader from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir from sphinx.util.jsonimpl import dumps as dump_json +from sphinx.websupport import errors from sphinx.websupport.search import BaseSearch, SEARCH_ADAPTERS from sphinx.websupport.storage import StorageBackend -from sphinx.websupport.errors import * class WebSupportApp(Sphinx): @@ -103,7 +103,7 @@ class WebSupport(object): It will also save node data to the database. """ if not self.srcdir: - raise SrcdirNotSpecifiedError( \ + raise errors.SrcdirNotSpecifiedError( \ 'No srcdir associated with WebSupport object') doctreedir = path.join(self.outdir, 'doctrees') app = WebSupportApp(self.srcdir, self.srcdir, @@ -158,7 +158,7 @@ class WebSupport(object): try: f = open(infilename, 'rb') except IOError: - raise DocumentNotFoundError( + raise errors.DocumentNotFoundError( 'The document "%s" could not be found' % docname) document = pickle.load(f) @@ -334,7 +334,7 @@ class WebSupport(object): :param moderator: Whether the user making the request is a moderator. """ if not moderator: - raise UserNotAuthorizedError() + raise errors.UserNotAuthorizedError() self.storage.accept_comment(comment_id) def reject_comment(self, comment_id, moderator=False): @@ -347,7 +347,7 @@ class WebSupport(object): :param moderator: Whether the user making the request is a moderator. """ if not moderator: - raise UserNotAuthorizedError() + raise errors.UserNotAuthorizedError() self.storage.reject_comment(comment_id) def _make_base_comment_options(self): From 2ab934b2322050f3554c5f28ef4827ebecd90571 Mon Sep 17 00:00:00 2001 From: Georg Brandl <georg@python.org> Date: Sat, 20 Nov 2010 17:41:20 +0100 Subject: [PATCH 443/744] Make websupport builder inherit from serializing builder, remove separate WebSupportApp. --- sphinx/builders/versioning.py | 2 +- sphinx/builders/websupport.py | 95 +++++++++----------- sphinx/themes/basic/layout.html | 45 ++++++---- sphinx/themes/basic/searchresults.html | 20 ++--- sphinx/themes/basic/static/websupport.js | 51 ++++++----- sphinx/websupport/__init__.py | 106 ++++++++++++++--------- sphinx/websupport/errors.py | 7 -- sphinx/writers/websupport.py | 2 +- tests/test_websupport.py | 2 +- 9 files changed, 175 insertions(+), 155 deletions(-) diff --git a/sphinx/builders/versioning.py b/sphinx/builders/versioning.py index 6c2bccca4..f0646a829 100644 --- a/sphinx/builders/versioning.py +++ b/sphinx/builders/versioning.py @@ -7,7 +7,7 @@ :license: BSD, see LICENSE for details. """ import os -import pickle +import cPickle as pickle from docutils.utils import Reporter diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 7970f8bd7..9ca9f3bb7 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -9,31 +9,43 @@ :license: BSD, see LICENSE for details. """ -import cPickle as pickle from os import path import posixpath import shutil from docutils.io import StringOutput +from sphinx.jinja2glue import BuiltinTemplateLoader from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile -from sphinx.util.jsonimpl import dumps as dump_json from sphinx.util.websupport import is_commentable -from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.builders.html import PickleHTMLBuilder from sphinx.builders.versioning import VersioningBuilderMixin from sphinx.writers.websupport import WebSupportTranslator -class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin): +class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): """ Builds documents for the web support package. """ name = 'websupport' - out_suffix = '.fpickle' def init(self): - StandaloneHTMLBuilder.init(self) + PickleHTMLBuilder.init(self) VersioningBuilderMixin.init(self) + # templates are needed for this builder, but the serializing + # builder does not initialize them + self.init_templates() + if not isinstance(self.templates, BuiltinTemplateLoader): + raise RuntimeError('websupport builder must be used with ' + 'the builtin templates') + # add our custom JS + self.script_files.append('_static/websupport.js') + + def set_webinfo(self, staticdir, virtual_staticdir, search, storage): + self.staticdir = staticdir + self.virtual_staticdir = virtual_staticdir + self.search = search + self.storage = storage def init_translator_class(self): self.translator_class = WebSupportTranslator @@ -46,9 +58,9 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin): self.cur_docname = docname self.secnumbers = self.env.toc_secnumbers.get(docname, {}) - self.imgpath = '/' + posixpath.join(self.app.staticdir, '_images') + self.imgpath = '/' + posixpath.join(self.virtual_staticdir, '_images') self.post_process_images(doctree) - self.dlpath = '/' + posixpath.join(self.app.staticdir, '_downloads') + self.dlpath = '/' + posixpath.join(self.virtual_staticdir, '_downloads') self.docwriter.write(doctree, destination) self.docwriter.assemble_parts() body = self.docwriter.parts['fragment'] @@ -58,11 +70,8 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin): self.index_page(docname, doctree, ctx.get('title', '')) self.handle_page(docname, ctx, event_arg=doctree) - def get_target_uri(self, docname, typ=None): - return docname - def load_indexer(self, docnames): - self.indexer = self.app.search + self.indexer = self.search self.indexer.init_indexing(changed=docnames) def handle_page(self, pagename, addctx, templatename='page.html', @@ -75,11 +84,13 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin): def pathto(otheruri, resource=False, baseuri=self.get_target_uri(pagename)): - if not resource: + if resource and '://' in otheruri: + return otheruri + elif not resource: otheruri = self.get_target_uri(otheruri) return relative_uri(baseuri, otheruri) or '#' else: - return '/' + posixpath.join(self.app.staticdir, otheruri) + return '/' + posixpath.join(self.virtual_staticdir, otheruri) ctx['pathto'] = pathto ctx['hasdoc'] = lambda name: name in self.env.all_docs ctx['encoding'] = self.config.html_output_encoding @@ -90,47 +101,41 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin): self.app.emit('html-page-context', pagename, templatename, ctx, event_arg) - # Create a dict that will be pickled and used by webapps. - css = '<link rel="stylesheet" href="%s" type=text/css />' % \ - pathto('_static/pygments.css', 1) - doc_ctx = {'body': ctx.get('body', ''), - 'title': ctx.get('title', ''), - 'css': css, - 'js': self._make_js(ctx)} - # Partially render the html template to proved a more useful ctx. + # create a dict that will be pickled and used by webapps + doc_ctx = { + 'body': ctx.get('body', ''), + 'title': ctx.get('title', ''), + } + # partially render the html template to get at interesting macros template = self.templates.environment.get_template(templatename) template_module = template.make_module(ctx) - if hasattr(template_module, 'sidebar'): - doc_ctx['sidebar'] = template_module.sidebar() - if hasattr(template_module, 'relbar'): - doc_ctx['relbar'] = template_module.relbar() + for item in ['sidebar', 'relbar', 'script', 'css']: + if hasattr(template_module, item): + doc_ctx[item] = getattr(template_module, item)() if not outfilename: outfilename = path.join(self.outdir, 'pickles', os_path(pagename) + self.out_suffix) - ensuredir(path.dirname(outfilename)) - f = open(outfilename, 'wb') - try: - pickle.dump(doc_ctx, f, pickle.HIGHEST_PROTOCOL) - finally: - f.close() + self.dump_context(doc_ctx, outfilename) # if there is a source file, copy the source file for the # "show source" link if ctx.get('sourcename'): - source_name = path.join(self.app.builddir, self.app.staticdir, + source_name = path.join(self.staticdir, '_sources', os_path(ctx['sourcename'])) ensuredir(path.dirname(source_name)) copyfile(self.env.doc2path(pagename), source_name) def handle_finish(self): - StandaloneHTMLBuilder.handle_finish(self) + PickleHTMLBuilder.handle_finish(self) VersioningBuilderMixin.finish(self) + + # move static stuff over to separate directory directories = ['_images', '_static'] for directory in directories: src = path.join(self.outdir, directory) - dst = path.join(self.app.builddir, self.app.staticdir, directory) + dst = path.join(self.staticdir, directory) if path.isdir(src): if path.isdir(dst): shutil.rmtree(dst) @@ -138,23 +143,3 @@ class WebSupportBuilder(StandaloneHTMLBuilder, VersioningBuilderMixin): def dump_search_index(self): self.indexer.finish_indexing() - - def _make_js(self, ctx): - def make_script(file): - path = ctx['pathto'](file, 1) - return '<script type="text/javascript" src="%s"></script>' % path - - opts = { - 'URL_ROOT': ctx.get('url_root', ''), - 'VERSION': ctx['release'], - 'COLLAPSE_INDEX': False, - 'FILE_SUFFIX': '', - 'HAS_SOURCE': ctx['has_source'] - } - scripts = [make_script(file) for file in ctx['script_files']] - scripts.append(make_script('_static/websupport.js')) - return '\n'.join([ - '<script type="text/javascript">' - 'var DOCUMENTATION_OPTIONS = %s;' % dump_json(opts), - '</script>' - ] + scripts) diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html index e31e85443..82b40e1af 100644 --- a/sphinx/themes/basic/layout.html +++ b/sphinx/themes/basic/layout.html @@ -16,7 +16,13 @@ {%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and (sidebars != []) %} {%- set url_root = pathto('', 1) %} +{# XXX necessary? #} {%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} {%- macro relbar() %} <div class="related"> @@ -78,24 +84,7 @@ {%- endif %} {%- endmacro %} -<html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" /> - {{ metatags }} - {%- if not embedded and docstitle %} - {%- set titlesuffix = " — "|safe + docstitle|e %} - {%- else %} - {%- set titlesuffix = "" %} - {%- endif %} - {%- block htmltitle %} - <title>{{ title|striptags|e }}{{ titlesuffix }} - {%- endblock %} - - - {%- for cssfile in css_files %} - - {%- endfor %} - {%- if not embedded %} +{%- macro script() %} {%- endfor %} +{%- endmacro %} + +{%- macro css() %} + + + {%- for cssfile in css_files %} + + {%- endfor %} +{%- endmacro %} + + + + + {{ metatags }} + {%- block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {%- endblock %} + {{ css() }} + {%- if not embedded %} + {{ script() }} {%- if use_opensearch %} -{% if search_performed %} -

    Search Results

    -{% if not search_results %} -

    Your search did not match any results.

    -{% endif %} -{% endif %} +{%- if search_performed %} +

    Search Results

    + {%- if not search_results %} +

    Your search did not match any results.

    + {%- endif %} +{%- endif %}
    - {% if search_results %} + {%- if search_results %} - {% endif %} + {%- endif %}
    diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 51a5a7d7c..f545b5716 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -214,10 +214,18 @@ * Add a comment via ajax and insert the comment into the comment tree. */ function addComment(form) { - // Disable the form that is being submitted. - form.find('textarea,input').attr('disabled', 'disabled'); var node_id = form.find('input[name="node"]').val(); var parent_id = form.find('input[name="parent"]').val(); + var text = form.find('textarea[name="comment"]').val(); + var proposal = form.find('textarea[name="proposal"]').val(); + + if (text == '') { + showError('Please enter a comment.'); + return; + } + + // Disable the form that is being submitted. + form.find('textarea,input').attr('disabled', 'disabled'); // Send the comment to the server. $.ajax({ @@ -227,8 +235,8 @@ data: { node: node_id, parent: parent_id, - text: form.find('textarea[name="comment"]').val(), - proposal: form.find('textarea[name="proposal"]').val() + text: text, + proposal: proposal }, success: function(data, textStatus, error) { // Reset the form. @@ -311,7 +319,7 @@ $('#cm' + id).fadeOut('fast'); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem accepting the comment."); + showError('Oops, there was a problem accepting the comment.'); } }); } @@ -328,7 +336,7 @@ }); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem rejecting the comment."); + showError('Oops, there was a problem rejecting the comment.'); } }); } @@ -354,7 +362,7 @@ div.data('comment', comment); }, error: function(request, textStatus, error) { - showError("Oops, there was a problem deleting the comment."); + showError('Oops, there was a problem deleting the comment.'); } }); } @@ -395,7 +403,7 @@ var classes = link.attr('class').split(/\s+/); for (var i=0; i Date: Sat, 20 Nov 2010 18:00:11 +0100 Subject: [PATCH 444/744] For now, do not allow comments on literals. --- sphinx/util/websupport.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/util/websupport.py b/sphinx/util/websupport.py index 510ecbe0e..446093ff7 100644 --- a/sphinx/util/websupport.py +++ b/sphinx/util/websupport.py @@ -8,4 +8,5 @@ """ def is_commentable(node): - return node.__class__.__name__ in ('paragraph', 'literal_block') + #return node.__class__.__name__ in ('paragraph', 'literal_block') + return node.__class__.__name__ == 'paragraph' From dae3933e87edad48dd0b03cb308345daba2db6a2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 20 Nov 2010 18:00:23 +0100 Subject: [PATCH 445/744] Add a "nocomment" class to be able to hide bubbles. --- sphinx/themes/basic/static/websupport.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index f545b5716..ac87c3e2c 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -614,11 +614,12 @@ var count = COMMENT_METADATA[id]; var title = count + ' comment' + (count == 1 ? '' : 's'); var image = count > 0 ? opts.commentBrightImage : opts.commentImage; + var addcls = count == 0 ? ' nocomment' : ''; $(this) .append( $(document.createElement('a')).attr({ href: '#', - 'class': 'sphinx-comment', + 'class': 'sphinx-comment' + addcls, id: 'ao' + id }) .append($(document.createElement('img')).attr({ From 7e3f9d866a8d0d57c13a6e66329c17509e26229a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 08:25:43 +0100 Subject: [PATCH 446/744] Fix a few smaller issues. Add JS and CSS for all pages. --- sphinx/builders/websupport.py | 8 ++++++ sphinx/themes/basic/static/websupport.js | 16 ++++++++---- sphinx/websupport/__init__.py | 31 +++++++++++++----------- sphinx/writers/websupport.py | 2 +- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 9ca9f3bb7..d4c08c295 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -50,6 +50,10 @@ class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): def init_translator_class(self): self.translator_class = WebSupportTranslator + def prepare_writing(self, docnames): + PickleHTMLBuilder.prepare_writing(self, docnames) + self.globalcontext['no_search_suffix'] = True + def write_doc(self, docname, doctree): destination = StringOutput(encoding='utf-8') doctree.settings = self.docsettings @@ -112,6 +116,10 @@ class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): for item in ['sidebar', 'relbar', 'script', 'css']: if hasattr(template_module, item): doc_ctx[item] = getattr(template_module, item)() + if 'script' not in self.globalcontext: + self.globalcontext['script'] = doc_ctx['script'] + if 'css' not in self.globalcontext: + self.globalcontext['css'] = doc_ctx['css'] if not outfilename: outfilename = path.join(self.outdir, 'pickles', diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index ac87c3e2c..d04336eaa 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -253,6 +253,8 @@ ul.data('empty', false); } insertComment(data.comment); + var ao = $('#ao' + (node_id || parent_id)); + ao.find('img').attr({'src': opts.commentBrightImage}); }, error: function(request, textStatus, error) { form.find('textarea,input').removeAttr('disabled'); @@ -598,7 +600,7 @@ function showError(message) { $(document.createElement('div')).attr({'class': 'popup-error'}) .append($(document.createElement('div')) - .attr({'class': 'error-header'}).text(message)) + .attr({'class': 'error-message'}).text(message)) .appendTo('body') .fadeIn("slow") .delay(2000) @@ -651,7 +653,7 @@ }); }; - var opts = jQuery.extend({ + var opts = { processVoteURL: '/_process_vote', addCommentURL: '/_add_comment', getCommentsURL: '/_get_comments', @@ -668,7 +670,11 @@ downArrowPressed: '/static/_static/down-pressed.png', voting: false, moderator: false - }, COMMENT_OPTIONS); + }; + + if (typeof COMMENT_OPTIONS != "undefined") { + opts = jQuery.extend(opts, COMMENT_OPTIONS); + } var replyTemplate = '\
  • \ @@ -683,7 +689,7 @@
  • '; var commentTemplate = '\ -
    \ +
    \
    \
    \ \ @@ -769,7 +775,7 @@ })(jQuery); $(document).ready(function() { - $('.spxcmt').comment(); + $('.sphinx-has-comment').comment(); /** Highlight search words in search results. */ $("div.context").each(function() { diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 611c262d5..a97a589dc 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -172,6 +172,10 @@ class WebSupport(object): docpath = path.join(self.datadir, 'pickles', docname) if path.isdir(docpath): infilename = docpath + '/index.fpickle' + if not docname: + docname = 'index' + else: + docname += '/index' else: infilename = docpath + '.fpickle' @@ -186,11 +190,10 @@ class WebSupport(object): f.close() comment_opts = self._make_comment_options(username, moderator) - comment_metadata = self.storage.get_metadata(docname, moderator) + comment_meta = self._make_metadata( + self.storage.get_metadata(docname, moderator)) - document['script'] = '\n'.join([comment_opts, - self._make_metadata(comment_metadata), - document['script']]) + document['script'] = comment_opts + comment_meta + document['script'] return document def get_search_results(self, q): @@ -424,15 +427,15 @@ class WebSupport(object): 'username': username, 'moderator': moderator, }) - return '\n'.join([ - '' - ]) + return '''\ + + ''' % dump_json(rv) def _make_metadata(self, data): - return '\n'.join([ - '' - ]) + return '''\ + + ''' % dump_json(data) diff --git a/sphinx/writers/websupport.py b/sphinx/writers/websupport.py index 224329d7e..1214ba015 100644 --- a/sphinx/writers/websupport.py +++ b/sphinx/writers/websupport.py @@ -20,7 +20,7 @@ class WebSupportTranslator(HTMLTranslator): def __init__(self, builder, *args, **kwargs): HTMLTranslator.__init__(self, builder, *args, **kwargs) - self.comment_class = 'spxcmt' + self.comment_class = 'sphinx-has-comment' def dispatch_visit(self, node): if is_commentable(node): From e05137417a2a61cdc3ed4b62142c460d8942e565 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 11:13:11 +0100 Subject: [PATCH 447/744] Tweak the commenting interface a bit. --- sphinx/builders/websupport.py | 19 ++-- sphinx/themes/basic/static/websupport.js | 131 +++++++++++------------ sphinx/websupport/__init__.py | 9 +- sphinx/websupport/storage/differ.py | 2 +- 4 files changed, 83 insertions(+), 78 deletions(-) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index d4c08c295..c883fdf17 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -78,8 +78,7 @@ class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): self.indexer = self.search self.indexer.init_indexing(changed=docnames) - def handle_page(self, pagename, addctx, templatename='page.html', - outfilename=None, event_arg=None): + def _render_page(self, pagename, addctx, templatename, event_arg=None): # This is mostly copied from StandaloneHTMLBuilder. However, instead # of rendering the template and saving the html, create a context # dict and pickle it. @@ -116,10 +115,13 @@ class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): for item in ['sidebar', 'relbar', 'script', 'css']: if hasattr(template_module, item): doc_ctx[item] = getattr(template_module, item)() - if 'script' not in self.globalcontext: - self.globalcontext['script'] = doc_ctx['script'] - if 'css' not in self.globalcontext: - self.globalcontext['css'] = doc_ctx['css'] + + return ctx, doc_ctx + + def handle_page(self, pagename, addctx, templatename='page.html', + outfilename=None, event_arg=None): + ctx, doc_ctx = self._render_page(pagename, addctx, + templatename, event_arg) if not outfilename: outfilename = path.join(self.outdir, 'pickles', @@ -136,6 +138,11 @@ class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): copyfile(self.env.doc2path(pagename), source_name) def handle_finish(self): + # get global values for css and script files + _, doc_ctx = self._render_page('tmp', {}, 'page.html') + self.globalcontext['css'] = doc_ctx['css'] + self.globalcontext['script'] = doc_ctx['script'] + PickleHTMLBuilder.handle_finish(self) VersioningBuilderMixin.finish(self) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index d04336eaa..94123c534 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -431,6 +431,10 @@ } var id = link.attr('id'); + if (!id) { + // Didn't click on one of the voting arrows. + return; + } // If it is an unvote, the new vote value is 0, // Otherwise it's 1 for an upvote, or -1 for a downvote. var value = 0; @@ -535,21 +539,18 @@ ul.children().children("[id^='cd']") .each(function() { var comment = $(this).data('comment'); - if (recursive) { + if (recursive) comment.children = getChildren($(this).find('#cl' + comment.id), true); - } children.push(comment); }); return children; } - /** - * Create a div to display a comment in. - */ + /** Create a div to display a comment in. */ function createCommentDiv(comment) { // Prettify the comment rating. comment.pretty_rating = comment.rating + ' point' + - (comment.rating == 1 ? '' : 's'); + (comment.rating == 1 ? '' : 's'); // Create a div for this comment. var context = $.extend({}, opts, comment); var div = $(renderTemplate(commentTemplate, context)); @@ -563,15 +564,12 @@ if (comment.text != '[deleted]') { div.find('a.reply').show(); - if (comment.proposal_diff) { + if (comment.proposal_diff) div.find('#sp' + comment.id).show(); - } - if (opts.moderator && !comment.displayed) { + if (opts.moderator && !comment.displayed) div.find('#cm' + comment.id).show(); - } - if (opts.moderator || (opts.username == comment.username)) { + if (opts.moderator || (opts.username == comment.username)) div.find('#dc' + comment.id).show(); - } } return div; } @@ -597,6 +595,7 @@ }); } + /** Flash an error message briefly. */ function showError(message) { $(document.createElement('div')).attr({'class': 'popup-error'}) .append($(document.createElement('div')) @@ -607,9 +606,7 @@ .fadeOut("slow"); } - /** - * Add a link the user uses to open the comments popup. - */ + /** Add a link the user uses to open the comments popup. */ $.fn.comment = function() { return this.each(function() { var id = $(this).attr('id').substring(1); @@ -621,7 +618,7 @@ .append( $(document.createElement('a')).attr({ href: '#', - 'class': 'sphinx-comment' + addcls, + 'class': 'sphinx-comment-open' + addcls, id: 'ao' + id }) .append($(document.createElement('img')).attr({ @@ -676,34 +673,53 @@ opts = jQuery.extend(opts, COMMENT_OPTIONS); } - var replyTemplate = '\ -
  • \ -
    \ -
    \ - \ - \ - \ - \ -
    \ -
    \ -
  • '; + var popupTemplate = '\ +
    \ +

    \ + Sort by:\ + top\ + newest\ + oldest\ +

    \ +
    Comments
    \ +
    \ + loading comments...
    \ +
      \ +

      Add a comment:

      \ +
      \ + \ +

      \ + \ + Propose a change ▹\ + \ + \ + Propose a change ▿\ + \ +

      \ + \ + \ + \ + \ +
      \ +
      '; var commentTemplate = '\ -
      \ +
      \
      \ \ \ @@ -718,15 +734,9 @@

      \ \ reply ▿\ - \ - proposal ▹\ - \ - \ - proposal ▿\ - \ - \ + proposal ▹\ + proposal ▿\ + \

      \
      '; - var popupTemplate = '\ -
      \ -
      Comments
      \ -
      \ - \ -

      \ - \ - Propose a change ▹\ - \ - \ - Propose a change ▿\ - \ -

      \ - \ - \ - \ - \ -

      \ - Sort by:\ - top\ - newest\ - oldest\ -

      \ -
      \ -
      loading comments...
      \ -
        \ -
        '; + var replyTemplate = '\ +
      • \ +
        \ +
        \ + \ + \ + \ + \ +
        \ +
        \ +
      • '; $(document).ready(function() { init(); @@ -775,6 +769,7 @@ })(jQuery); $(document).ready(function() { + /** Add comment anchors for all paragraphs that are commentable. */ $('.sphinx-has-comment').comment(); /** Highlight search words in search results. */ diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index a97a589dc..9f8d9e304 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -213,9 +213,12 @@ class WebSupport(object): 'search_results': results, 'docroot': '../', # XXX } - document = self.get_document('search') - document['body'] = self.results_template.render(ctx) - document['title'] = 'Search Results' + document = { + 'body': self.results_template.render(ctx), + 'title': 'Search Results', + 'sidebar': '', + 'relbar': '' + } return document def get_data(self, node_id, username=None, moderator=False): diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py index d52250718..8e430a137 100644 --- a/sphinx/websupport/storage/differ.py +++ b/sphinx/websupport/storage/differ.py @@ -58,7 +58,7 @@ class CombinedHtmlDiff(object): if next is not None and next[0] == '?': tag = 'ins' if prefix == '+' else 'del' text = self._highlight_text(text, next, tag) - css_class = 'prop_added' if prefix == '+' else 'prop_removed' + css_class = 'prop-added' if prefix == '+' else 'prop-removed' return '%s\n' % (css_class, text.rstrip()) From 69713fb26d028dcbd43708f9bd156edf8ba705b2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 12:46:31 +0100 Subject: [PATCH 448/744] Allow rst markup in the comment text. --- sphinx/themes/basic/static/websupport.js | 24 ++++++++++++++++-------- sphinx/websupport/__init__.py | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 94123c534..3881a42bc 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -98,6 +98,9 @@ deleteComment($(this).attr('id').substring(2)); return false; }); + $('a.comment-markup').live("click", function() { + toggleCommentMarkupBox($(this).attr('id').substring(2)); + }); } /** @@ -271,7 +274,7 @@ $.each(comments, function() { var div = createCommentDiv(this); ul.append($(document.createElement('li')).html(div)); - appendComments(this.children, div.find('ul.children')); + appendComments(this.children, div.find('ul.comment-children')); // To avoid stagnating data, don't store the comments children in data. this.children = null; div.data('comment', this); @@ -353,7 +356,7 @@ div .find('span.user-id:first') .text('[deleted]').end() - .find('p.comment-text:first') + .find('div.comment-text:first') .text('[deleted]').end() .find('#cm' + id + ', #dc' + id + ', #ac' + id + ', #rc' + id + ', #sp' + id + ', #hp' + id + ', #cr' + id + ', #rl' + id) @@ -398,9 +401,11 @@ textarea.slideUp('fast'); } - /** - * Handle when the user clicks on a sort by link. - */ + function toggleCommentMarkupBox(id) { + $('#mb' + id).toggle(); + } + + /** Handle when the user clicks on a sort by link. */ function handleReSort(link) { var classes = link.attr('class').split(/\s+/); for (var i=0; i\ loading comments...
        \
          \ -

          Add a comment:

          \ +

          Add a comment\ + (markup):

          \ +
          \ + Comment markup:
          \
          \ \

          \ @@ -730,7 +738,7 @@ <%pretty_rating%>\ <%time.delta%>\

          \ -

          <%text%>

          \ +
          <#text#>
          \

          \ \ reply ▿\ @@ -745,7 +753,7 @@

          \
           <#proposal_diff#>\
                   
          \ -
            \ +
              \
              \
              \
              \ diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 9f8d9e304..9a476ad98 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for details. """ +import cgi import sys import cPickle as pickle import posixpath @@ -16,6 +17,8 @@ from os import path from jinja2 import Environment, FileSystemLoader +from docutils.core import publish_parts + from sphinx.application import Sphinx from sphinx.util.osutil import ensuredir from sphinx.util.jsonimpl import dumps as dump_json @@ -308,6 +311,7 @@ class WebSupport(object): :param username: the username of the user making the comment. :param time: the time the comment was created, defaults to now. """ + text = self._parse_comment_text(text) comment = self.storage.add_comment(text, displayed, username, time, proposal, node_id, parent_id, moderator) @@ -442,3 +446,13 @@ class WebSupport(object): var COMMENT_METADATA = %s; ''' % dump_json(data) + + def _parse_comment_text(self, text): + settings = {'file_insertion_enabled': False, + 'output_encoding': 'unicode'} + try: + ret = publish_parts(text, writer_name='html', + settings_overrides=settings)['fragment'] + except Exception: + ret = cgi.escape(text) + return ret From bdf27419e46bc005de470207fdecd4a07a1c8e57 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 13:17:11 +0100 Subject: [PATCH 449/744] Support complete comment deletion. --- sphinx/themes/basic/static/websupport.js | 18 ++++++++++--- sphinx/websupport/__init__.py | 17 +++++++------ sphinx/websupport/storage/sqlalchemy_db.py | 3 ++- .../websupport/storage/sqlalchemystorage.py | 25 ++++++++++++------- 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 3881a42bc..29f0a5dd8 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -353,6 +353,14 @@ data: {id: id}, success: function(data, textStatus, request) { var div = $('#cd' + id); + if (data == 'delete') { + // Moderator mode: remove the comment and all children immediately + div.slideUp('fast', function() { + div.remove(); + }); + return; + } + // User mode: only mark the comment as deleted div .find('span.user-id:first') .text('[deleted]').end() @@ -507,7 +515,9 @@ addComment($('#rf' + id)); closeReply(id); }); - div.slideDown('fast'); + div.slideDown('fast', function() { + $('#rf' + id).find('textarea').focus(); + }); } /** @@ -567,7 +577,7 @@ div.find('#' + direction + 'u' + comment.id).show(); } - if (comment.text != '[deleted]') { + if (opts.moderator || comment.text != '[deleted]') { div.find('a.reply').show(); if (comment.proposal_diff) div.find('#sp' + comment.id).show(); @@ -693,7 +703,9 @@

              Add a comment\ (markup):

              \
              \ - Comment markup:
              \ + reStructured text markup: *emph*, **strong**, \ + ``code``, \ + code blocks: :: and an indented block after blank line
              \ \ \

              \ diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 9a476ad98..d483a1ff8 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -266,13 +266,16 @@ class WebSupport(object): return self.storage.get_data(node_id, username, moderator) def delete_comment(self, comment_id, username='', moderator=False): - """Delete a comment. Doesn't actually delete the comment, but - instead replaces the username and text files with "[deleted]" so - as not to leave any comments orphaned. + """Delete a comment. - If `moderator` is True, the comment will always be deleted. If - `moderator` is False, the comment will only be deleted if the - `username` matches the `username` on the comment. + If `moderator` is True, the comment and all descendants will be deleted + from the database, and the function returns ``True``. + + If `moderator` is False, the comment will be marked as deleted (but not + removed from the database so as not to leave any comments orphaned), but + only if the `username` matches the `username` on the comment. The + username and text files are replaced with "[deleted]" . In this case, + the function returns ``False``. This raises :class:`~sphinx.websupport.errors.UserNotAuthorizedError` if moderator is False and `username` doesn't match username on the @@ -282,7 +285,7 @@ class WebSupport(object): :param username: the username requesting the deletion. :param moderator: whether the requestor is a moderator. """ - self.storage.delete_comment(comment_id, username, moderator) + return self.storage.delete_comment(comment_id, username, moderator) def add_comment(self, text, node_id='', parent_id='', displayed=True, username=None, time=None, proposal=None, diff --git a/sphinx/websupport/storage/sqlalchemy_db.py b/sphinx/websupport/storage/sqlalchemy_db.py index 4e2757a9e..dd4bf8822 100644 --- a/sphinx/websupport/storage/sqlalchemy_db.py +++ b/sphinx/websupport/storage/sqlalchemy_db.py @@ -33,7 +33,7 @@ class Node(Base): def nested_comments(self, username, moderator): """Create a tree of comments. First get all comments that are - descendents of this node, then convert them to a tree form. + descendants of this node, then convert them to a tree form. :param username: the name of the user to get comments for. :param moderator: whether the user is moderator. @@ -56,6 +56,7 @@ class Node(Base): # Filter out all comments not descending from this node. q = q.filter(Comment.path.like(str(self.id) + '.%')) + # Filter out all comments that are not moderated yet. if not moderator: q = q.filter(Comment.displayed == True) diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index 6f13c91b6..a83c7623f 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -88,12 +88,23 @@ class SQLAlchemyStorage(StorageBackend): session = Session() comment = session.query(Comment).\ filter(Comment.id == comment_id).one() - if moderator or comment.username == username: + if moderator: + # moderator mode: delete the comment and all descendants + # find descendants via path + session.query(Comment).filter( + Comment.path.like(comment.path + '.%')).delete(False) + session.delete(comment) + session.commit() + session.close() + return True + elif comment.username == username: + # user mode: do not really delete, but remove text and proposal comment.username = '[deleted]' comment.text = '[deleted]' comment.proposal = '' session.commit() session.close() + return False else: session.close() raise UserNotAuthorizedError() @@ -154,20 +165,16 @@ class SQLAlchemyStorage(StorageBackend): def accept_comment(self, comment_id): session = Session() - - # XXX assignment to "comment" needed? - comment = session.query(Comment).filter( - Comment.id == comment_id).update( - {Comment.displayed: True}) + session.query(Comment).filter(Comment.id == comment_id).update( + {Comment.displayed: True} + ) session.commit() session.close() def reject_comment(self, comment_id): session = Session() - - comment = session.query(Comment).\ - filter(Comment.id == comment_id).one() + comment = session.query(Comment).filter(Comment.id == comment_id).one() session.delete(comment) session.commit() From 53d237a4eb609aa330e25b7d8ebbacdfd5eb26db Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 13:41:19 +0100 Subject: [PATCH 450/744] Allow configuration whether to allow anonymous comments. --- sphinx/websupport/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index d483a1ff8..35696c22c 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -42,6 +42,7 @@ class WebSupport(object): status=sys.stdout, warning=sys.stderr, moderation_callback=None, + allow_anonymous_comments=True, docroot='', staticroot='static', ): @@ -59,6 +60,7 @@ class WebSupport(object): self.status = status self.warning = warning self.moderation_callback = moderation_callback + self.allow_anonymous_comments = allow_anonymous_comments self._init_templating() self._init_search(search) @@ -314,6 +316,11 @@ class WebSupport(object): :param username: the username of the user making the comment. :param time: the time the comment was created, defaults to now. """ + if username is None: + if self.allow_anonymous_comments: + username = 'Anonymous' + else: + raise errors.UserNotAuthorizedError() text = self._parse_comment_text(text) comment = self.storage.add_comment(text, displayed, username, time, proposal, node_id, From fc9549767c77be08e8a2aa25389282f516fdd1c5 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 14:02:03 +0100 Subject: [PATCH 451/744] Tolerate the lack of a sidebar. --- sphinx/themes/default/static/sidebar.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sphinx/themes/default/static/sidebar.js b/sphinx/themes/default/static/sidebar.js index 731851711..233c755ea 100644 --- a/sphinx/themes/default/static/sidebar.js +++ b/sphinx/themes/default/static/sidebar.js @@ -29,6 +29,9 @@ $(function() { var sidebar = $('.sphinxsidebar'); var sidebarwrapper = $('.sphinxsidebarwrapper'); + // for some reason, the document has no sidebar; do not run into errors + if (!sidebar.length) return; + // original margin-left of the bodywrapper and width of the sidebar // with the sidebar expanded var bw_margin_expanded = bodywrapper.css('margin-left'); From 9295e3ce1a70dc7d853b228ebfe46d3ea12f7f16 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 20:02:23 +0100 Subject: [PATCH 452/744] Fix deletion of comments with votes. --- sphinx/websupport/storage/sqlalchemy_db.py | 36 ++++++++++--------- .../websupport/storage/sqlalchemystorage.py | 1 + 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/sphinx/websupport/storage/sqlalchemy_db.py b/sphinx/websupport/storage/sqlalchemy_db.py index dd4bf8822..cbecc03e8 100644 --- a/sphinx/websupport/storage/sqlalchemy_db.py +++ b/sphinx/websupport/storage/sqlalchemy_db.py @@ -98,6 +98,22 @@ class Node(Base): self.source = source +class CommentVote(Base): + """A vote a user has made on a Comment.""" + __tablename__ = db_prefix + 'commentvote' + + username = Column(String(64), primary_key=True) + comment_id = Column(Integer, ForeignKey(db_prefix + 'comments.id'), + primary_key=True) + # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted. + value = Column(Integer, nullable=False) + + def __init__(self, comment_id, username, value): + self.comment_id = comment_id + self.username = username + self.value = value + + class Comment(Base): """An individual Comment being stored.""" __tablename__ = db_prefix + 'comments' @@ -115,6 +131,9 @@ class Comment(Base): node_id = Column(String, ForeignKey(db_prefix + 'nodes.id')) node = relation(Node, backref="comments") + votes = relation(CommentVote, backref="comment", + cascade="all, delete-orphan") + def __init__(self, text, displayed, username, rating, time, proposal, proposal_diff): self.text = text @@ -187,20 +206,3 @@ class Comment(Base): dt = (days, 'day') return '%s %s ago' % dt if dt[0] == 1 else '%s %ss ago' % dt - - -class CommentVote(Base): - """A vote a user has made on a Comment.""" - __tablename__ = db_prefix + 'commentvote' - - username = Column(String(64), primary_key=True) - comment_id = Column(Integer, ForeignKey(db_prefix + 'comments.id'), - primary_key=True) - comment = relation(Comment, backref="votes") - # -1 if downvoted, +1 if upvoted, 0 if voted then unvoted. - value = Column(Integer, nullable=False) - - def __init__(self, comment_id, username, value): - self.comment_id = comment_id - self.username = username - self.value = value diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index a83c7623f..78991639d 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -81,6 +81,7 @@ class SQLAlchemyStorage(StorageBackend): comment.set_path(node_id, parent_id) session.commit() d = comment.serializable() + d['document'] = comment.node.document session.close() return d From 6d6f7997101df6ff65a9a3f7c5e053b9111feed2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 20:43:20 +0100 Subject: [PATCH 453/744] Remove comment form once comment is submitted, and allow directly opening a comment popup with an anchor. --- sphinx/themes/basic/static/websupport.js | 19 ++++++++++++++++++- sphinx/websupport/storage/sqlalchemy_db.py | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 29f0a5dd8..198795f00 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -256,8 +256,13 @@ ul.data('empty', false); } insertComment(data.comment); - var ao = $('#ao' + (node_id || parent_id)); + var ao = $('#ao' + node_id); ao.find('img').attr({'src': opts.commentBrightImage}); + if (node_id) { + // if this was a "root" comment, remove the commenting box + // (the user can get it back by reopening the comment popup) + $('#ca' + node_id).slideUp(); + } }, error: function(request, textStatus, error) { form.find('textarea,input').removeAttr('disabled'); @@ -563,6 +568,10 @@ /** Create a div to display a comment in. */ function createCommentDiv(comment) { + if (!comment.displayed) { + return $('

              Thank you! Your comment will show up once it is has ' + + ' been approved by a moderator.
              '); + } // Prettify the comment rating. comment.pretty_rating = comment.rating + ' point' + (comment.rating == 1 ? '' : 's'); @@ -700,6 +709,7 @@
              \ loading comments...
              \
                \ +
                \

                Add a comment\ (markup):

                \
                \ @@ -722,6 +732,7 @@ \ \ \ +
                \
                '; var commentTemplate = '\ @@ -801,4 +812,10 @@ $(document).ready(function() { result.highlightText(this.toLowerCase(), 'highlighted'); }); }); + + // directly open comment window if requested + var anchor = document.location.hash; + if (anchor.substring(0, 9) == '#comment-') { + $('#ao' + anchor.substring(9)).click(); + } }); diff --git a/sphinx/websupport/storage/sqlalchemy_db.py b/sphinx/websupport/storage/sqlalchemy_db.py index cbecc03e8..02ff6aaab 100644 --- a/sphinx/websupport/storage/sqlalchemy_db.py +++ b/sphinx/websupport/storage/sqlalchemy_db.py @@ -175,7 +175,7 @@ class Comment(Base): 'delta': self.pretty_delta(delta)} path = self.path.split('.') - node = path[0] if len(path) == 2 else None + node = path[0] parent = path[-2] if len(path) > 2 else None return {'text': self.text, From f9705fc5b7bbfb30870751da40c5f59218d77859 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 20:47:32 +0100 Subject: [PATCH 454/744] Remove comment rejection; delete is fine for that now. --- sphinx/themes/basic/static/websupport.js | 27 ++----------------- sphinx/websupport/__init__.py | 13 --------- .../websupport/storage/sqlalchemystorage.py | 8 ------ 3 files changed, 2 insertions(+), 46 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index 198795f00..bf8a85d58 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -90,10 +90,6 @@ acceptComment($(this).attr('id').substring(2)); return false; }); - $('a.reject-comment').live("click", function() { - rejectComment($(this).attr('id').substring(2)); - return false; - }); $('a.delete-comment').live("click", function() { deleteComment($(this).attr('id').substring(2)); return false; @@ -334,23 +330,6 @@ }); } - function rejectComment(id) { - $.ajax({ - type: 'POST', - url: opts.rejectCommentURL, - data: {id: id}, - success: function(data, textStatus, request) { - var div = $('#cd' + id); - div.slideUp('fast', function() { - div.remove(); - }); - }, - error: function(request, textStatus, error) { - showError('Oops, there was a problem rejecting the comment.'); - } - }); - } - function deleteComment(id) { $.ajax({ type: 'POST', @@ -568,7 +547,7 @@ /** Create a div to display a comment in. */ function createCommentDiv(comment) { - if (!comment.displayed) { + if (!comment.displayed && !opts.moderator) { return $('
                Thank you! Your comment will show up once it is has ' + ' been approved by a moderator.
                '); } @@ -579,7 +558,7 @@ var context = $.extend({}, opts, comment); var div = $(renderTemplate(commentTemplate, context)); - // If the user has voted on this comment, highlight the correct arrow. + // If the user has voted on this comment, highblight the correct arrow. if (comment.vote) { var direction = (comment.vote == 1) ? 'u' : 'd'; div.find('#' + direction + 'v' + comment.id).hide(); @@ -679,7 +658,6 @@ addCommentURL: '/_add_comment', getCommentsURL: '/_get_comments', acceptCommentURL: '/_accept_comment', - rejectCommentURL: '/_reject_comment', deleteCommentURL: '/_delete_comment', commentImage: '/static/_static/comment.png', closeCommentImage: '/static/_static/comment-close.png', @@ -770,7 +748,6 @@ \ \

                \
                \
                diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py
                index 35696c22c..26392b984 100644
                --- a/sphinx/websupport/__init__.py
                +++ b/sphinx/websupport/__init__.py
                @@ -383,19 +383,6 @@ class WebSupport(object):
                             raise errors.UserNotAuthorizedError()
                         self.storage.accept_comment(comment_id)
                 
                -    def reject_comment(self, comment_id, moderator=False):
                -        """Reject a comment that is pending moderation.
                -
                -        This raises :class:`~sphinx.websupport.errors.UserNotAuthorizedError`
                -        if moderator is False.
                -
                -        :param comment_id: The id of the comment that was accepted.
                -        :param moderator: Whether the user making the request is a moderator.
                -        """
                -        if not moderator:
                -            raise errors.UserNotAuthorizedError()
                -        self.storage.reject_comment(comment_id)
                -
                     def _make_base_comment_options(self):
                         """Helper method to create the part of the COMMENT_OPTIONS javascript
                         that remains the same throughout the lifetime of the
                diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py
                index 78991639d..b58b1198e 100644
                --- a/sphinx/websupport/storage/sqlalchemystorage.py
                +++ b/sphinx/websupport/storage/sqlalchemystorage.py
                @@ -172,11 +172,3 @@ class SQLAlchemyStorage(StorageBackend):
                 
                         session.commit()
                         session.close()
                -
                -    def reject_comment(self, comment_id):
                -        session = Session()
                -        comment = session.query(Comment).filter(Comment.id == comment_id).one()
                -        session.delete(comment)
                -
                -        session.commit()
                -        session.close()
                
                From 8e074332561110527f27de5e885c8c3649e5b70f Mon Sep 17 00:00:00 2001
                From: Georg Brandl 
                Date: Sun, 21 Nov 2010 20:49:51 +0100
                Subject: [PATCH 455/744] Remove more mentions of reject_comment.
                
                ---
                 doc/web/quickstart.rst                | 13 +++----------
                 doc/web/storagebackends.rst           |  2 --
                 sphinx/websupport/__init__.py         |  1 -
                 sphinx/websupport/storage/__init__.py |  8 --------
                 4 files changed, 3 insertions(+), 21 deletions(-)
                
                diff --git a/doc/web/quickstart.rst b/doc/web/quickstart.rst
                index 0627c9c33..bd0f71a73 100644
                --- a/doc/web/quickstart.rst
                +++ b/doc/web/quickstart.rst
                @@ -228,8 +228,8 @@ pass the `displayed` keyword argument::
                                                  username=username, proposal=proposal,
                                                  displayed=False)
                 
                -You can then create two new views to handle the moderation of comments.  The
                -first will be called when a moderator decides a comment should be accepted and
                +You can then create a new view to handle the moderation of comments.  It
                +will be called when a moderator decides a comment should be accepted and
                 displayed::
                 
                    @app.route('/docs/accept_comment', methods=['POST'])
                @@ -239,14 +239,7 @@ displayed::
                        support.accept_comment(comment_id, moderator=moderator)
                        return 'OK'
                 
                -The next is very similar, but used when rejecting a comment::
                -
                -   @app.route('/docs/reject_comment', methods=['POST'])
                -   def reject_comment():
                -       moderator = g.user.moderator if g.user else False
                -       comment_id = request.form.get('id')
                -       support.reject_comment(comment_id, moderator=moderator)
                -       return 'OK'
                +Rejecting comments happens via comment deletion.
                 
                 To perform a custom action (such as emailing a moderator) when a new comment is
                 added but not displayed, you can pass callable to the :class:`~.WebSupport`
                diff --git a/doc/web/storagebackends.rst b/doc/web/storagebackends.rst
                index a46ea9e55..d191b43e5 100644
                --- a/doc/web/storagebackends.rst
                +++ b/doc/web/storagebackends.rst
                @@ -42,5 +42,3 @@ StorageBackend Methods
                 .. automethod:: StorageBackend.update_username
                 
                 .. automethod:: StorageBackend.accept_comment
                -
                -.. automethod:: StorageBackend.reject_comment
                diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py
                index 26392b984..d5510dcac 100644
                --- a/sphinx/websupport/__init__.py
                +++ b/sphinx/websupport/__init__.py
                @@ -396,7 +396,6 @@ class WebSupport(object):
                                 ('getCommentsURL', '_get_comments'),
                                 ('processVoteURL', '_process_vote'),
                                 ('acceptCommentURL', '_accept_comment'),
                -                ('rejectCommentURL', '_reject_comment'),
                                 ('deleteCommentURL', '_delete_comment')
                             ]
                             for key, value in comment_urls:
                diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py
                index 3d8a9ab58..55eb00c87 100644
                --- a/sphinx/websupport/storage/__init__.py
                +++ b/sphinx/websupport/storage/__init__.py
                @@ -113,11 +113,3 @@ class StorageBackend(object):
                         :param comment_id: The id of the comment being accepted.
                         """
                         raise NotImplementedError()
                -
                -    def reject_comment(self, comment_id):
                -        """Called when a moderator rejects a comment. The comment should
                -        then be deleted.
                -
                -        :param comment_id: The id of the comment being accepted.
                -        """
                -        raise NotImplementedError()
                
                From 27f937867068703d15242d5b65e82d755575cbd2 Mon Sep 17 00:00:00 2001
                From: Georg Brandl 
                Date: Sun, 21 Nov 2010 20:56:13 +0100
                Subject: [PATCH 456/744] Fix comment voting again.
                
                ---
                 sphinx/websupport/storage/sqlalchemy_db.py | 2 +-
                 1 file changed, 1 insertion(+), 1 deletion(-)
                
                diff --git a/sphinx/websupport/storage/sqlalchemy_db.py b/sphinx/websupport/storage/sqlalchemy_db.py
                index 02ff6aaab..bf1844ce4 100644
                --- a/sphinx/websupport/storage/sqlalchemy_db.py
                +++ b/sphinx/websupport/storage/sqlalchemy_db.py
                @@ -132,7 +132,7 @@ class Comment(Base):
                     node = relation(Node, backref="comments")
                 
                     votes = relation(CommentVote, backref="comment",
                -                     cascade="all, delete-orphan")
                +                     cascade="all")
                 
                     def __init__(self, text, displayed, username, rating, time,
                                  proposal, proposal_diff):
                
                From 9a3a3df917ce1fd513b874e974f0e4f21bff6229 Mon Sep 17 00:00:00 2001
                From: Georg Brandl 
                Date: Sun, 21 Nov 2010 21:07:20 +0100
                Subject: [PATCH 457/744] Highligh comments to moderate.
                
                ---
                 sphinx/themes/basic/static/websupport.js | 8 +++++---
                 1 file changed, 5 insertions(+), 3 deletions(-)
                
                diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js
                index bf8a85d58..dc4cf6ac9 100644
                --- a/sphinx/themes/basic/static/websupport.js
                +++ b/sphinx/themes/basic/static/websupport.js
                @@ -548,12 +548,14 @@
                   /** Create a div to display a comment in. */
                   function createCommentDiv(comment) {
                     if (!comment.displayed && !opts.moderator) {
                -      return $('
                Thank you! Your comment will show up once it is has ' - + ' been approved by a moderator.
                '); + return $('
                Thank you! Your comment will show up ' + + 'once it is has been approved by a moderator.
                '); } // Prettify the comment rating. comment.pretty_rating = comment.rating + ' point' + (comment.rating == 1 ? '' : 's'); + // Make a class (for displaying not yet moderated comments differently) + comment.css_class = comment.displayed ? '' : ' moderate'; // Create a div for this comment. var context = $.extend({}, opts, comment); var div = $(renderTemplate(commentTemplate, context)); @@ -714,7 +716,7 @@
                '; var commentTemplate = '\ -
                \ +
                \
                \
                \ \ From 9481a557c1ae732f2b194e27a54880cfe8b8737d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 21 Nov 2010 21:18:57 +0100 Subject: [PATCH 458/744] Add a text-based diff view e.g. for display in emails. --- sphinx/websupport/__init__.py | 5 +++-- sphinx/websupport/storage/differ.py | 18 ++++++++++++------ sphinx/websupport/storage/sqlalchemystorage.py | 7 +++++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index d5510dcac..dfcd868b2 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -321,10 +321,11 @@ class WebSupport(object): username = 'Anonymous' else: raise errors.UserNotAuthorizedError() - text = self._parse_comment_text(text) - comment = self.storage.add_comment(text, displayed, username, + parsed = self._parse_comment_text(text) + comment = self.storage.add_comment(parsed, displayed, username, time, proposal, node_id, parent_id, moderator) + comment['original_text'] = text if not displayed and self.moderation_callback: self.moderation_callback(comment) return comment diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py index 8e430a137..10b76b345 100644 --- a/sphinx/websupport/storage/differ.py +++ b/sphinx/websupport/storage/differ.py @@ -20,19 +20,25 @@ class CombinedHtmlDiff(object): """ highlight_regex = re.compile(r'([\+\-\^]+)') - def make_html(self, source, proposal): + def __init__(self, source, proposal): + proposal = escape(proposal) + + differ = Differ() + self.diff = list(differ.compare(source.splitlines(1), + proposal.splitlines(1))) + + def make_text(self): + return '\n'.join(self.diff) + + def make_html(self): """Return the HTML representation of the differences between `source` and `proposal`. :param source: the original text :param proposal: the proposed text """ - proposal = escape(proposal) - - differ = Differ() - diff = list(differ.compare(source.splitlines(1), - proposal.splitlines(1))) html = [] + diff = self.diff[:] line = diff.pop(0) next = diff.pop(0) while True: diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py index b58b1198e..fa1d0e3e4 100644 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ b/sphinx/websupport/storage/sqlalchemystorage.py @@ -60,11 +60,13 @@ class SQLAlchemyStorage(StorageBackend): proposal, node_id, parent_id, moderator): session = Session() proposal_diff = None + proposal_diff_text = None if node_id and proposal: node = session.query(Node).filter(Node.id == node_id).one() - differ = CombinedHtmlDiff() - proposal_diff = differ.make_html(node.source, proposal) + differ = CombinedHtmlDiff(node.source, proposal) + proposal_diff = differ.make_html() + proposal_diff_text = differ.make_text() elif parent_id: parent = session.query(Comment.displayed).\ filter(Comment.id == parent_id).one() @@ -82,6 +84,7 @@ class SQLAlchemyStorage(StorageBackend): session.commit() d = comment.serializable() d['document'] = comment.node.document + d['proposal_diff_text'] = proposal_diff_text session.close() return d From b72e41832e2fa3f8f1f4bc355dc6e4ee4c2f48fa Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 24 Nov 2010 09:17:11 +0100 Subject: [PATCH 459/744] Small tweaks. --- sphinx/themes/basic/static/websupport.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index dc4cf6ac9..eaea0de7b 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -54,7 +54,7 @@ hide($(this).attr('id').substring(2)); return false; }); - $('.vote').live("click", function() { + $('a.vote').live("click", function() { handleVote($(this)); return false; }); @@ -96,6 +96,7 @@ }); $('a.comment-markup').live("click", function() { toggleCommentMarkupBox($(this).attr('id').substring(2)); + return false; }); } @@ -323,6 +324,7 @@ data: {id: id}, success: function(data, textStatus, request) { $('#cm' + id).fadeOut('fast'); + $('#cd' + id).removeClass('moderate'); }, error: function(request, textStatus, error) { showError('Oops, there was a problem accepting the comment.'); @@ -681,7 +683,7 @@
                \

                \ Sort by:\ - top\ + best rated\ newest\ oldest\

                \ @@ -779,10 +781,10 @@ })(jQuery); $(document).ready(function() { - /** Add comment anchors for all paragraphs that are commentable. */ + // add comment anchors for all paragraphs that are commentable $('.sphinx-has-comment').comment(); - /** Highlight search words in search results. */ + // highlight search words in search results $("div.context").each(function() { var params = $.getQueryParameters(); var terms = (params.q) ? params.q[0].split(/\s+/) : []; @@ -796,5 +798,6 @@ $(document).ready(function() { var anchor = document.location.hash; if (anchor.substring(0, 9) == '#comment-') { $('#ao' + anchor.substring(9)).click(); + document.location.hash = '#s' + anchor.substring(9); } }); From 80c3fc600019f3f0442530ccf7e31ce493114394 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 24 Nov 2010 17:12:05 +0100 Subject: [PATCH 460/744] Fix typo. --- doc/theming.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/theming.rst b/doc/theming.rst index 716eb50a7..552a0eaf8 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -206,7 +206,7 @@ name), containing the following: * A :file:`theme.conf` file, see below. * HTML templates, if needed. * A ``static/`` directory containing any static files that will be copied to the - output statid directory on build. These can be images, styles, script files. + output static directory on build. These can be images, styles, script files. The :file:`theme.conf` file is in INI format [1]_ (readable by the standard Python :mod:`ConfigParser` module) and has the following structure: From 6c843451b2198803e45e9be07ad85fc956318817 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 24 Nov 2010 21:31:26 +0100 Subject: [PATCH 461/744] Fix event handling and class names for comment sort links. --- sphinx/themes/basic/static/websupport.js | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index eaea0de7b..be37718ec 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -51,52 +51,52 @@ function initEvents() { $('a.comment-close').live("click", function(event) { + event.preventDefault(); hide($(this).attr('id').substring(2)); - return false; }); - $('a.vote').live("click", function() { + $('a.vote').live("click", function(event) { + event.preventDefault(); handleVote($(this)); - return false; }); - $('a.reply').live("click", function() { + $('a.reply').live("click", function(event) { + event.preventDefault(); openReply($(this).attr('id').substring(2)); - return false; }); - $('a.close-reply').live("click", function() { + $('a.close-reply').live("click", function(event) { + event.preventDefault(); closeReply($(this).attr('id').substring(2)); - return false; }); $('a.sort-option').live("click", function(event) { + event.preventDefault(); handleReSort($(this)); - return false; }); - $('a.show-proposal').live("click", function() { + $('a.show-proposal').live("click", function(event) { + event.preventDefault(); showProposal($(this).attr('id').substring(2)); - return false; }); - $('a.hide-proposal').live("click", function() { + $('a.hide-proposal').live("click", function(event) { + event.preventDefault(); hideProposal($(this).attr('id').substring(2)); - return false; }); - $('a.show-propose-change').live("click", function() { + $('a.show-propose-change').live("click", function(event) { + event.preventDefault(); showProposeChange($(this).attr('id').substring(2)); - return false; }); - $('a.hide-propose-change').live("click", function() { + $('a.hide-propose-change').live("click", function(event) { + event.preventDefault(); hideProposeChange($(this).attr('id').substring(2)); - return false; }); - $('a.accept-comment').live("click", function() { + $('a.accept-comment').live("click", function(event) { + event.preventDefault(); acceptComment($(this).attr('id').substring(2)); - return false; }); - $('a.delete-comment').live("click", function() { + $('a.delete-comment').live("click", function(event) { + event.preventDefault(); deleteComment($(this).attr('id').substring(2)); - return false; }); - $('a.comment-markup').live("click", function() { + $('a.comment-markup').live("click", function(event) { + event.preventDefault(); toggleCommentMarkupBox($(this).attr('id').substring(2)); - return false; }); } @@ -117,7 +117,7 @@ // Reset link styles and format the selected sort option. $('a.sel').attr('href', '#').removeClass('sel'); - $('a.' + by).removeAttr('href').addClass('sel'); + $('a.by' + by).removeAttr('href').addClass('sel'); } /** @@ -150,7 +150,7 @@ var context = $.extend({id: id}, opts); var popup = $(renderTemplate(popupTemplate, context)).hide(); popup.find('textarea[name="proposal"]').hide(); - popup.find('a.' + by).addClass('sel'); + popup.find('a.by' + by).addClass('sel'); var form = popup.find('#cf' + id); form.submit(function(event) { event.preventDefault(); From 4a2ce98cd807e7dfd9c65b05ba1676ae246e399c Mon Sep 17 00:00:00 2001 From: Robert Lehmann Date: Thu, 9 Dec 2010 10:44:31 +0100 Subject: [PATCH 462/744] Recommend best practice instead of sphinx-to-github. Reported by the original author, Michael Jones, on sphinx-dev. http://groups.google.com/group/sphinx-dev/msg/1faa6adbe1bf0819 --- doc/faq.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/faq.rst b/doc/faq.rst index 0c2d2c4f7..c92067f0c 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -60,10 +60,11 @@ PyPI Sphinx documentation to the PyPI package documentation area at http://packages.python.org/. -github pages - You can use `Michael Jones' sphinx-to-github tool - `_ to prepare - Sphinx HTML output. +GitHub Pages + Directories starting with underscores are ignored by default which breaks + static files in Sphinx. GitHub's preprocessor can be `disabled + `_ to support + Sphinx HTML output properly. Google Analytics You can use a custom ``layout.html`` template, like this: From 537db42eced3c8fd359a5154172fe1e90301b860 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 24 Nov 2010 22:08:26 +0100 Subject: [PATCH 463/744] Add cancel button to comment reply form. --- sphinx/themes/basic/static/websupport.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index be37718ec..f1b64a0db 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -500,6 +500,10 @@ event.preventDefault(); addComment($('#rf' + id)); closeReply(id); + }) + .find('input[type=button]') + .click(function() { + closeReply(id); }); div.slideDown('fast', function() { $('#rf' + id).find('textarea').focus(); @@ -769,6 +773,7 @@
                \ \ \ + \ \ \
                \ From dd955b1f94d404fd45a343de322f3f1175dcb26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Wed, 1 Dec 2010 22:54:54 +0100 Subject: [PATCH 464/744] #559 Add `html_permalink_text` confval --- CHANGES | 2 ++ doc/config.rst | 8 ++++++++ sphinx/config.py | 1 + sphinx/writers/html.py | 17 +++++++++++------ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 0a7ae7ea9..ce712009d 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,8 @@ Release 1.1 (in development) * #526: Added Iranian translation. +* #559: Added :confval:`html_permalink_text`. + Release 1.0.6 (in development) ============================== diff --git a/doc/config.rst b/doc/config.rst index 90730d60c..503a357c7 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -506,6 +506,14 @@ that use Sphinx' HTMLWriter class. .. versionadded:: 0.6 Previously, this was always activated. +.. confval:: html_permalink_text + + A string specifying the text which should be used for "permalinks". + Default: ``u'\u00B6'``. + + .. versionadded:: 1.1 + Previously, this was always the current default value. + .. confval:: html_sidebars Custom sidebar templates, must be a dictionary that maps document names to diff --git a/sphinx/config.py b/sphinx/config.py index 6f957314a..abde73cea 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -93,6 +93,7 @@ class Config(object): html_use_modindex = (True, 'html'), # deprecated html_domain_indices = (True, 'html'), html_add_permalinks = (True, 'html'), + html_permalink_text = (u'\u00B6', 'html'), html_use_index = (True, 'html'), html_split_index = (False, 'html'), html_copy_source = (True, 'html'), diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index be4a97c5d..28d61a436 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -60,6 +60,7 @@ class HTMLTranslator(BaseTranslator): self.highlightlinenothreshold = sys.maxint self.protect_literal_text = 0 self.add_permalinks = builder.config.html_add_permalinks + self.permalink_text = builder.config.html_permalink_text self.secnumber_suffix = builder.config.html_secnumber_suffix def visit_start_of_file(self, node): @@ -84,8 +85,10 @@ class HTMLTranslator(BaseTranslator): if node['ids'] and self.add_permalinks and self.builder.add_permalinks: self.body.append(u'\u00B6' % - _('Permalink to this definition')) + u'title="%s">%s' % ( + _('Permalink to this definition'), + self.permalink_text) + ) self.body.append('\n') def visit_desc_addname(self, node): @@ -486,13 +489,15 @@ class HTMLTranslator(BaseTranslator): # add permalink anchor if close_tag.startswith('\u00B6' % - _('Permalink to this headline')) + u'title="%s">%s' % ( + _('Permalink to this headline'), + self.permalink_text)) elif close_tag.startswith('\u00B6' % - _('Permalink to this headline')) + u'title="%s">%s' % ( + _('Permalink to this headline'), + self.permalink_text)) BaseTranslator.depart_title(self, node) From e38f19a829862cf2880d27efaf431c875eb355e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Wed, 1 Dec 2010 23:13:31 +0100 Subject: [PATCH 465/744] "Fixed" the CombinedHTMLDiff test The output should probably be validated but I just wanted to make sure it runs without an Exception first --- tests/test_websupport.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_websupport.py b/tests/test_websupport.py index 84d6f9e0f..c09334361 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -271,9 +271,9 @@ def test_moderation(support): def test_differ(): - differ = CombinedHtmlDiff() source = 'Lorem ipsum dolor sit amet,\nconsectetur adipisicing elit,\n' \ 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' prop = 'Lorem dolor sit amet,\nconsectetur nihil adipisicing elit,\n' \ 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.' - differ.make_html(source, prop) + differ = CombinedHtmlDiff(source, prop) + differ.make_html() From b3ee43700cd6a88881c440fa845995d17dddf2b6 Mon Sep 17 00:00:00 2001 From: mcantor Date: Wed, 1 Dec 2010 21:45:47 -0500 Subject: [PATCH 466/744] added a :private-members: option to autodoc, which will instruct directives to include members whose names start with an underscore --- sphinx/ext/autodoc.py | 7 ++++--- tests/test_autodoc.py | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 696e5f1d0..2c6746100 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -541,7 +541,7 @@ class Documenter(object): if want_all and membername.startswith('_'): # ignore members whose name starts with _ by default - skip = True + skip = not self.options.private_members elif (namespace, membername) in attr_docs: # keep documented attributes skip = False @@ -713,6 +713,7 @@ class ModuleDocumenter(Documenter): 'show-inheritance': bool_option, 'synopsis': identity, 'platform': identity, 'deprecated': bool_option, 'member-order': identity, 'exclude-members': members_set_option, + 'private-members': bool_option, } @classmethod @@ -866,7 +867,7 @@ class ClassDocumenter(ModuleLevelDocumenter): 'members': members_option, 'undoc-members': bool_option, 'noindex': bool_option, 'inherited-members': bool_option, 'show-inheritance': bool_option, 'member-order': identity, - 'exclude-members': members_set_option, + 'exclude-members': members_set_option, 'private-members': bool_option, } @classmethod @@ -1134,7 +1135,7 @@ class AutoDirective(Directive): # flags that can be given in autodoc_default_flags _default_flags = set(['members', 'undoc-members', 'inherited-members', - 'show-inheritance']) + 'show-inheritance', 'private-members']) # standard docutils directive settings has_content = True diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 8f983471c..f2aa92208 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -38,6 +38,7 @@ def setup_module(): members = [], member_order = 'alphabetic', exclude_members = set(), + private_members = False ) directive = Struct( From ec85695e7cf90ece1d121998c2c0194d049b8301 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 3 Dec 2010 21:04:57 +0100 Subject: [PATCH 467/744] Allow giving root dir to quickstart as an argument. --- sphinx/quickstart.py | 14 ++++++++++++-- sphinx/themes/basic/static/websupport.js | 18 ++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index b67ee054c..d79c555d3 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -784,14 +784,24 @@ def inner_main(args): if not color_terminal(): nocolor() + if len(args) > 3: + print 'Usage: sphinx-quickstart [root]' + sys.exit(1) + elif len(args) == 2: + d['path'] = args[1] + print bold('Welcome to the Sphinx %s quickstart utility.') % __version__ print ''' Please enter values for the following settings (just press Enter to accept a default value, if one is given in brackets).''' - print ''' + if 'path' in d: + print bold(''' +Selected root path: %s''' % d['path']) + else: + print ''' Enter the root path for documentation.''' - do_prompt(d, 'path', 'Root path for the documentation', '.', is_path) + do_prompt(d, 'path', 'Root path for the documentation', '.', is_path) while path.isfile(path.join(d['path'], 'conf.py')) or \ path.isfile(path.join(d['path'], 'source', 'conf.py')): diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index f1b64a0db..aff5ef414 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -156,7 +156,11 @@ event.preventDefault(); addComment(form); }); + if (COMMENT_METADATA[id] == 0) + $('#cn' + id).hide(); $('#s' + id).after(popup); +// Recaptcha.create("6LfFAr8SAAAAADQ4xx8foVRW51EnF1fe4Apyivxf", +// "cap" + id, { theme: "clean" }); popup.slideDown('fast', function() { getComments(id); }); @@ -227,6 +231,10 @@ // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); + // Solve CAPTCHA. + var challenge = Recaptcha.get_challenge(); + var response = Recaptcha.get_response(); + // Send the comment to the server. $.ajax({ type: "POST", @@ -236,7 +244,9 @@ node: node_id, parent: parent_id, text: text, - proposal: proposal + proposal: proposal, + challenge: challenge, + response: response }, success: function(data, textStatus, error) { // Reset the form. @@ -714,6 +724,7 @@

                \ \ +
                \ \ \ \ @@ -787,7 +798,10 @@ $(document).ready(function() { // add comment anchors for all paragraphs that are commentable - $('.sphinx-has-comment').comment(); + //$('.sphinx-has-comment').comment(); + $('.sphinx-has-comment').mouseenter(function() { + $(this).unbind('mouseenter'); + $(this).comment();}); // highlight search words in search results $("div.context").each(function() { From 266df2317f10d1770c4687ddd6e0cefcc60593f2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 11 Dec 2010 09:25:35 +0100 Subject: [PATCH 468/744] Revert accident. --- sphinx/themes/basic/static/websupport.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js index aff5ef414..f1b64a0db 100644 --- a/sphinx/themes/basic/static/websupport.js +++ b/sphinx/themes/basic/static/websupport.js @@ -156,11 +156,7 @@ event.preventDefault(); addComment(form); }); - if (COMMENT_METADATA[id] == 0) - $('#cn' + id).hide(); $('#s' + id).after(popup); -// Recaptcha.create("6LfFAr8SAAAAADQ4xx8foVRW51EnF1fe4Apyivxf", -// "cap" + id, { theme: "clean" }); popup.slideDown('fast', function() { getComments(id); }); @@ -231,10 +227,6 @@ // Disable the form that is being submitted. form.find('textarea,input').attr('disabled', 'disabled'); - // Solve CAPTCHA. - var challenge = Recaptcha.get_challenge(); - var response = Recaptcha.get_response(); - // Send the comment to the server. $.ajax({ type: "POST", @@ -244,9 +236,7 @@ node: node_id, parent: parent_id, text: text, - proposal: proposal, - challenge: challenge, - response: response + proposal: proposal }, success: function(data, textStatus, error) { // Reset the form. @@ -724,7 +714,6 @@

                \ \ -
                \ \ \ \ @@ -798,10 +787,7 @@ $(document).ready(function() { // add comment anchors for all paragraphs that are commentable - //$('.sphinx-has-comment').comment(); - $('.sphinx-has-comment').mouseenter(function() { - $(this).unbind('mouseenter'); - $(this).comment();}); + $('.sphinx-has-comment').comment(); // highlight search words in search results $("div.context").each(function() { From 29d6167125fdc6bad19252deafdca2b2e96d615e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 11 Dec 2010 09:25:59 +0100 Subject: [PATCH 469/744] Remove html_permalink_text again and give meaning to html_add_permalinks. --- CHANGES | 3 ++- doc/config.rst | 18 ++++++++---------- sphinx/config.py | 3 +-- sphinx/writers/html.py | 14 ++++++++------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/CHANGES b/CHANGES index ce712009d..c3bd34357 100644 --- a/CHANGES +++ b/CHANGES @@ -25,7 +25,8 @@ Release 1.1 (in development) * #526: Added Iranian translation. -* #559: Added :confval:`html_permalink_text`. +* #559: :confval:`html_add_permalinks` is now a string giving the + text to display in permalinks. Release 1.0.6 (in development) diff --git a/doc/config.rst b/doc/config.rst index 503a357c7..65601d8cf 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -499,20 +499,18 @@ that use Sphinx' HTMLWriter class. .. confval:: html_add_permalinks - If true, Sphinx will add "permalinks" for each heading and description - environment as paragraph signs that become visible when the mouse hovers over - them. Default: ``True``. + Sphinx will add "permalinks" for each heading and description environment as + paragraph signs that become visible when the mouse hovers over them. + + This value determines the text for the permalink; it defaults to ``"¶"``. + Set it to ``None`` or the empty string to disable permalinks. .. versionadded:: 0.6 Previously, this was always activated. -.. confval:: html_permalink_text - - A string specifying the text which should be used for "permalinks". - Default: ``u'\u00B6'``. - - .. versionadded:: 1.1 - Previously, this was always the current default value. + .. versionchanged:: 1.1 + This can now be a string to select the actual text of the link. + Previously, only boolean values were accepted. .. confval:: html_sidebars diff --git a/sphinx/config.py b/sphinx/config.py index abde73cea..922af803a 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -92,8 +92,7 @@ class Config(object): html_additional_pages = ({}, 'html'), html_use_modindex = (True, 'html'), # deprecated html_domain_indices = (True, 'html'), - html_add_permalinks = (True, 'html'), - html_permalink_text = (u'\u00B6', 'html'), + html_add_permalinks = (u'\u00B6', 'html'), html_use_index = (True, 'html'), html_split_index = (False, 'html'), html_copy_source = (True, 'html'), diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 28d61a436..1cc3bbe55 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -59,8 +59,11 @@ class HTMLTranslator(BaseTranslator): self.highlightlang = builder.config.highlight_language self.highlightlinenothreshold = sys.maxint self.protect_literal_text = 0 - self.add_permalinks = builder.config.html_add_permalinks - self.permalink_text = builder.config.html_permalink_text + self.permalink_text = builder.config.html_add_permalinks + # support backwards-compatible setting to a bool + if not isinstance(self.permalink_text, basestring): + self.permalink_text = self.permalink_text and u'\u00B6' or '' + self.permalink_text = self.encode(self.permalink_text) self.secnumber_suffix = builder.config.html_secnumber_suffix def visit_start_of_file(self, node): @@ -82,13 +85,12 @@ class HTMLTranslator(BaseTranslator): and node['ids'] and node['first']: self.body.append('' % node['ids'][0]) def depart_desc_signature(self, node): - if node['ids'] and self.add_permalinks and self.builder.add_permalinks: + if node['ids'] and self.permalink_text and self.builder.add_permalinks: self.body.append(u'%s' % ( _('Permalink to this definition'), - self.permalink_text) - ) + self.permalink_text)) self.body.append('\n') def visit_desc_addname(self, node): @@ -483,7 +485,7 @@ class HTMLTranslator(BaseTranslator): def depart_title(self, node): close_tag = self.context[-1] - if (self.add_permalinks and self.builder.add_permalinks and + if (self.permalink_text and self.builder.add_permalinks and node.parent.hasattr('ids') and node.parent['ids']): aname = node.parent['ids'][0] # add permalink anchor From 2f4f5bb30da305c2b6eb401890d45007aa389abd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 18 Dec 2010 09:42:27 +0100 Subject: [PATCH 470/744] Use better-readable code markup in text output. --- sphinx/writers/text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index b28b23792..1f1700396 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -651,9 +651,9 @@ class TextTranslator(nodes.NodeVisitor): self.add_text('*') def visit_literal(self, node): - self.add_text('``') + self.add_text('"') def depart_literal(self, node): - self.add_text('``') + self.add_text('"') def visit_subscript(self, node): self.add_text('_') From 685a9085a9e966b1b1b45ee8904925cf110d9355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kandulski?= Date: Wed, 22 Dec 2010 08:44:16 +0100 Subject: [PATCH 471/744] Updated polish i18n. --- sphinx/locale/pl/LC_MESSAGES/sphinx.mo | Bin 9370 -> 11024 bytes sphinx/locale/pl/LC_MESSAGES/sphinx.po | 102 ++++++++++++------------- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/sphinx/locale/pl/LC_MESSAGES/sphinx.mo b/sphinx/locale/pl/LC_MESSAGES/sphinx.mo index 949564e0ebb767a50c8c98abacf1ce5153d4850a..132e7da33128d3017e757e149307b01054edfaf3 100644 GIT binary patch delta 4901 zcmZve3vg7`8OP5;9^}DG9_9h$k`M?nMj{Z}5Tc=qA}DH+D1!86_nK_>>E2!ArsygK zANcmF2r8h}`kLzKcAS=R)ESaGZAXUMsdk(x*3ovR{S_$xRW9)jBM2xPyU`8$;=I==8HlroE|nE|!IJg9}8d~|?Sa2i|>XTm`^ z1?HgE-2pAQ4{H6(Q2W0Ib)wM?uy4Mhav~j5*rgV>Kp9vG>)-}h1FwKO`A&a) z7u5WxU?&`d4e%?t1kS>`JaqwF1p8n;yb;cTkH8A-n**xAQ8)n}f<5pJzdiGW;>{ai zIsGR?ZLk!|&>DaIT8M3P8`Qz~LG5<{>c$75?7Rur!FOS_)_fma|-Gq3sWccBdb1L}bPg!0HZPEFNR&BXs& zIF}A(e;btRRzn#G!Idxxb+9~?CtiU~@O8icOQ-`*B9Y|LdZ$mu9*9%S znK^&LMyQQ1huts(6^iGeTy_X5wts@U!TUZxhBELksGFCQshVF4b&w`F9=1YxqTL@~ z40XWV3MxCPoC)W{!%z!9^Cx@_Ww4A?*NG>?*>E0|p_NdE*FqiSGN{-NL3tzz<=LyD z4zLGm-9E^EIr9V+Y?v4Qfj6KGz6}+#4}5+OwQ(8o(aEc!et@P!^)G`u_zItA_*?@u z|3at(Y=k=4mclskk5Z9A7b*l>{r1gJF2CFFe-tVtzko{0!%!Rk18 z$-2N4sCjds>@>j_cs*@!n%@8W_(z8S4K=YE|LSH_p%yIhxdQ5@tDp|B7V?D6CaCoZ zsGD5_6^fgol6#jw{;EvO%c58xuGUm z1L|NsP&d2)YQsUuKa=F64ex@Nzz3k_e+uQH3gTI~C_IgJJ5 z9>h!LMXn{+{}~pN3uiJy^b)XEK4{wCJXzm#*iscwo zQoRnf;oqPPe++fAKyz_k71X?0P_Au*ntv+PydJ2SpY6AU5GR-zR0w_x3A=d{mcnnK43u&W&8ze|6>7s8sCgEYtLOXU zT~LO5q4qt;@81YD&u+#4O0ok>S44S-GTJJlgL3TI^em5 z8h?D?c}N-lkl!aLjA?WMx(nH8E0X=fFOupf>qAk}>_uJZXGo<3or*42gF;G}pQ6P` zu2mUA{e>ET3*Zm^y5;i}_#?C$$!i%T`-T7eZ-feg;-#_<-Cw8`v$v1>qi8P5qHRcJ zC%PAf)u3F8&P2DO^N|w!PNWh;*PtFXiX~^xqB0ICQ=8E;bT-;3jV zxWlgt4QQ3$u7?}Y9<&5iA|BGW%5B&;x1&x(@*XRC6{YA5bQyBdW9Si-LXRVr@1xCV zyBd^vMVMXi9KU`QT!YF{BU*~0=ya63na@4w1XPEfKoik@=m$vU3N#yq&;zI)%|Q`# zwA|!Z7Wk}ymmeLpENi9hI&PQM>>8^jiBAj%8=Ckm|DX*Gy~~ zUlL4&oc!J?y@6@xXX%ey=O#j#sAH{;hn%bON2bmXRBW(QVY@%-xcTwZmIkVhJ7&If zdMHq$dw9ENoK}8iaiaJ7j2)9YPCdC}nbonlYjNjEi#ojU%msC^aB$F$wnywZ7Tieq z^nsWij!(-d6B<~<% z^(Syq)>~0uQ@%dsjNLzaLw<98Sz!8wiIMNj%3m=n7^pm}7^JKf_Wn5gqbi+2fganI z@kZzL)t#RjaR#EJJ3==3(j}9}h8Hz_Sfjj+5QV~ugLxcONNYP^Z*>J4`m(8!Owcxc zZp0p8G-8|eiBQ69EaZygxpVIeSYB0QZ+)bAxy|O%M1RU2fyW%-*^RMDwmCk~-r>f^ z>bPT$rJ(n9g$vT&pk#R?C<7Q+1iKsP@vSVaVB9Lp)TIc4i1g(_OkQlPVnD}`43<0-75e9 delta 3126 zcmYk+e{dA#9mnzK2p}jVcZq?7ofxbV<_DAM& zuYGo(=lgu0@AK^Ky?uXo@x98)KQjD1%>P{eFHBbJe}C^zH)aOSKj0KRgY)qm@-bDH zPnbYGUx+bm#Vhd!tiW}sIQ=*izlB#AQ#8e}!yYW>!9%D3KMU_4K@B*LRrn6h!m}8~ zDMZn@S?FLAHU1XlWA5Nf{rYh>j-cY)i*uOYJjO*0H;#l4-o#qkf5ydFM%wkb1hv8q z*p7E$9QR>0K832tF`S0K#btOhY*(`iEu@aG%diEjncpnpViA5Bm1z;@;9gXqL#P!$ zi<;myT#awyR7|pbieXw%33Xy8u0Mg3aT`}$4U4> z*glOq7Uxj`l%)b+j#_y%v;j4KDbB++$Pkl5iePqz?fb*_fhhG?=1+1%d-go4{TtMT zCs2vKjjF_H)P(0y{iZTVhcOufoHqgx-%)f0ghPZfH+F zLj^b=wlAxkcwT`z&9hL?uSNyz#HDx(s`R^16*_?AXnu-H{DshCsPV^0%E;Qf_ zDv`fo8JOKBsEHmzZOy^ZXHWrOMkVws)bGUa!uy}168=2&;)F#r zg$>n!8K?xVMD0y2YCr>O!d6tt7l-YYs1o;t_rHPKf-LIL?M98?k9wUSLoMtuD)BL# ztoQ#7E{^g#yoFTD?B`cPi9C&3K?#+>$iNy6aceQE_dmsjGRoulYl2kA+>d%a5299j7!}|+I(RC)e*rboOqQvBwWznD8MUC5 zsI#yh>1qn7g!f`mHy-CgzjQ~i9zR1>po%E%Sc58UH)^1Vs=!v%*6qOS@%yL}zk=h5 zqE>ziDX#ewD&7TDqOEc2uN5vMx=P%O`r-IGD$sXvJ?=(L`~hmEpQ92lec>#!vy{VY>rWnd?!%V4G0= z@5ZHgFOs`?6*ciksDA%Mou#?X#8!2LcA?H%aXA-SX-{Y`sZjiT8YcGw)6$A{@~H#TD(@1O>BqXMi! z?d=BSjWa2vtJ#h^jE|sx>3)Jr?0MAqSCJx{*HMT298TB!|937_>Px6IP)*%5!8NFW z*P&K)V`vX5;6_xbQ`mw5YAYW_P4EP&-=WZFQ1O0&>UR{YnBV+5>~IP-@gGr#=Hu}G zzffBcSukD(LsnNXK=4ZWp z+i<3IF&>H7^2BX3lR;ZDaNK-uTi;0D4kf-aDec)85`EJO&S1t3XzZ!zvZ=bbJy2IO zJ;=NHZK=_$YtPoj6HO)=G&VVn6EXh#%o+0Xe!s1(pKq7ff2XX)DcIiF>dMs)LkFCG ze{f{T%jVtGfZgu=JbII#$+*LT=LG3~ug}fdHS=nw-Q;KU^a)D-ys44$m0O2d$A3DM zelR~3iFDh4Ty@U=b;0fSidl(Cxo!5oM3O1QL{l1(#>J4%g_#?TLXP<2xQW36cnm;4&n*Mao zOXbtw_Uxvn74}T66CVsrFt*b#_*7^lHR=~UQ`k0?_QvjU>^n^hD^fY%4Eq_+P=^5u z5p7;-cg&e-H#En~hjaesoIB+B!}+wIwY!=d+j8DFy_}a#c}~Xd^D<`b=*XbEg^\n" +"PO-Revision-Date: 2010-12-17 13:36+0100\n" +"Last-Translator: Michał Kandulski \n" "Language-Team: \n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && " "(n%100<10 || n%100>=20) ? 1 : 2)\n" @@ -69,7 +69,7 @@ msgstr "Autor modułu: " #: sphinx/directives/other.py:139 #, fuzzy msgid "Code author: " -msgstr "Autor modułu: " +msgstr "Autor kodu: " #: sphinx/directives/other.py:141 msgid "Author: " @@ -82,7 +82,7 @@ msgstr "Zobacz także" #: sphinx/domains/__init__.py:242 #, python-format msgid "%s %s" -msgstr "" +msgstr "%s %s" #: sphinx/domains/c.py:51 sphinx/domains/python.py:94 msgid "Parameters" @@ -133,7 +133,7 @@ msgstr "pole" #: sphinx/domains/c.py:173 msgid "macro" -msgstr "" +msgstr "makro" #: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1041 msgid "type" @@ -142,12 +142,12 @@ msgstr "typ" #: sphinx/domains/c.py:175 #, fuzzy msgid "variable" -msgstr "Zmienna" +msgstr "zmienna" #: sphinx/domains/cpp.py:883 #, python-format msgid "%s (C++ class)" -msgstr "%s (klasie C++)" +msgstr "%s (klasa C++)" #: sphinx/domains/cpp.py:898 #, python-format @@ -167,7 +167,7 @@ msgstr "%s (funkcja C++)" #: sphinx/domains/cpp.py:1038 sphinx/domains/javascript.py:162 #: sphinx/domains/python.py:525 msgid "class" -msgstr "klasie" +msgstr "klasa" #: sphinx/domains/javascript.py:105 sphinx/domains/python.py:244 #, python-format @@ -182,12 +182,12 @@ msgstr "%s() (%s metoda)" #: sphinx/domains/javascript.py:108 #, fuzzy, python-format msgid "%s() (class)" -msgstr "%s (klasie C++)" +msgstr "%s() (klasa)" #: sphinx/domains/javascript.py:110 #, python-format msgid "%s (global variable or constant)" -msgstr "" +msgstr "%s (zmienna lub stała globalna)" #: sphinx/domains/javascript.py:112 sphinx/domains/python.py:346 #, python-format @@ -197,15 +197,15 @@ msgstr "%s (%s atrybut)" #: sphinx/domains/javascript.py:121 #, fuzzy msgid "Arguments" -msgstr "Parametry" +msgstr "Argumenty" #: sphinx/domains/javascript.py:124 msgid "Throws" -msgstr "" +msgstr "Wyrzuca" #: sphinx/domains/javascript.py:163 sphinx/domains/python.py:524 msgid "data" -msgstr "" +msgstr "dane" #: sphinx/domains/javascript.py:164 sphinx/domains/python.py:530 msgid "attribute" @@ -214,7 +214,7 @@ msgstr "atrybut" #: sphinx/domains/python.py:98 #, fuzzy msgid "Variables" -msgstr "Zmienna" +msgstr "Zmienne" #: sphinx/domains/python.py:101 msgid "Raises" @@ -244,7 +244,7 @@ msgstr "%s (klasa wbudowana)" #: sphinx/domains/python.py:266 #, python-format msgid "%s (class in %s)" -msgstr "%s (w klasie %s)" +msgstr "%s (klasa w module %s)" #: sphinx/domains/python.py:306 #, python-format @@ -254,27 +254,27 @@ msgstr "%s() (%s.%s metoda)" #: sphinx/domains/python.py:318 #, python-format msgid "%s() (%s.%s static method)" -msgstr "%s() (%s.%s statyczna metoda)" +msgstr "%s() (%s.%s metoda statyczna)" #: sphinx/domains/python.py:321 #, python-format msgid "%s() (%s static method)" -msgstr "%s() (%s statyczna metoda)" +msgstr "%s() (%s metoda statyczna)" #: sphinx/domains/python.py:331 #, fuzzy, python-format msgid "%s() (%s.%s class method)" -msgstr "%s() (%s.%s metoda)" +msgstr "%s() (%s.%s metoda klasy)" #: sphinx/domains/python.py:334 #, fuzzy, python-format msgid "%s() (%s class method)" -msgstr "%s() (%s metoda)" +msgstr "%s() (%s metoda klasy)" #: sphinx/domains/python.py:344 #, python-format msgid "%s (%s.%s attribute)" -msgstr "%s (%s.%s atrybut)" +msgstr "%s (atrybut %s.%s)" #: sphinx/domains/python.py:392 msgid "Platforms: " @@ -288,7 +288,7 @@ msgstr "%s (moduł)" #: sphinx/domains/python.py:455 #, fuzzy msgid "Python Module Index" -msgstr "Indeks modułów" +msgstr "Indeks modułów pythona" #: sphinx/domains/python.py:456 msgid "modules" @@ -304,12 +304,12 @@ msgstr "wyjątek" #: sphinx/domains/python.py:527 msgid "method" -msgstr "" +msgstr "metoda" #: sphinx/domains/python.py:528 #, fuzzy msgid "class method" -msgstr "%s() (%s metoda)" +msgstr "metoda klasy" #: sphinx/domains/python.py:529 msgid "static method" @@ -322,26 +322,26 @@ msgstr "moduł" #: sphinx/domains/python.py:657 #, fuzzy msgid " (deprecated)" -msgstr "Niezalecane" +msgstr " (niezalecane)" #: sphinx/domains/rst.py:55 #, python-format msgid "%s (directive)" -msgstr "" +msgstr "%s (dyrektywa)" #: sphinx/domains/rst.py:57 #, fuzzy, python-format msgid "%s (role)" -msgstr "%s (moduł)" +msgstr "%s (rola)" #: sphinx/domains/rst.py:106 msgid "directive" -msgstr "" +msgstr "dyrektywa" #: sphinx/domains/rst.py:107 #, fuzzy msgid "role" -msgstr "moduł" +msgstr "rola" #: sphinx/domains/std.py:68 sphinx/domains/std.py:84 #, python-format @@ -355,15 +355,15 @@ msgstr "%sopcja linii komend; %s" #: sphinx/domains/std.py:328 msgid "glossary term" -msgstr "" +msgstr "termin glosariusza" #: sphinx/domains/std.py:329 msgid "grammar token" -msgstr "" +msgstr "symbol gramatyki" #: sphinx/domains/std.py:330 msgid "reference label" -msgstr "" +msgstr "etykieta odsyłacza" #: sphinx/domains/std.py:331 msgid "environment variable" @@ -371,7 +371,7 @@ msgstr "zmienna środowiskowa" #: sphinx/domains/std.py:332 msgid "program option" -msgstr "" +msgstr "opcja programu" #: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11 #: sphinx/themes/basic/genindex-split.html:11 @@ -402,44 +402,42 @@ msgstr "alias klasy :class:`%s`" #: sphinx/ext/todo.py:41 msgid "Todo" -msgstr "Do zrobienia" +msgstr "Todo" #: sphinx/ext/todo.py:109 #, fuzzy, python-format msgid "(The <> is located in %s, line %d.)" -msgstr "" -"(Oryginalny wpis znajduje się w pliku %s, w linii %d i może być " -"odnaleziony " +msgstr "(<> znajduje się w pliku %s, w linii %d.)" #: sphinx/ext/todo.py:117 msgid "original entry" -msgstr "" +msgstr "oryginalny wpis" #: sphinx/ext/viewcode.py:70 msgid "[source]" -msgstr "" +msgstr "[źródło]" #: sphinx/ext/viewcode.py:117 msgid "[docs]" -msgstr "" +msgstr "[dokumenty]" #: sphinx/ext/viewcode.py:131 #, fuzzy msgid "Module code" -msgstr "moduł" +msgstr "Kod modułu" #: sphinx/ext/viewcode.py:137 #, python-format msgid "

                Source code for %s

                " -msgstr "" +msgstr "

                Kod źródłowy modułu %s

                " #: sphinx/ext/viewcode.py:164 msgid "Overview: module code" -msgstr "" +msgstr "Przeglądanie: kod modułu" #: sphinx/ext/viewcode.py:165 msgid "

                All modules for which code is available

                " -msgstr "" +msgstr "

                Wszystkie moduły, dla których jest dostępny kod

                " #: sphinx/locale/__init__.py:155 msgid "Attention" @@ -552,15 +550,15 @@ msgstr "Kompletny spis treści" #: sphinx/themes/basic/defindex.html:24 msgid "lists all sections and subsections" -msgstr "wymień wszystkie rozdziały i podrozdziały" +msgstr "wszystkie rozdziały i podrozdziały" #: sphinx/themes/basic/defindex.html:26 msgid "search this documentation" -msgstr "wyszukaj w dokumentacji" +msgstr "przyszukaj tę dokumentację" #: sphinx/themes/basic/defindex.html:28 msgid "Global Module Index" -msgstr "Indeks modułów" +msgstr "Globalny indeks modułów" #: sphinx/themes/basic/defindex.html:29 msgid "quick access to all modules" @@ -674,7 +672,7 @@ msgstr "" #: sphinx/themes/basic/search.html:36 msgid "search" -msgstr "Szukaj" +msgstr "szukaj" #: sphinx/themes/basic/search.html:40 #: sphinx/themes/basic/static/searchtools.js:504 @@ -752,7 +750,7 @@ msgid "" "are spelled correctly and that you've selected enough categories." msgstr "" "Nie znaleziono żadnych pasujących dokumentów. Upewnij się, że wszystkie " -"słowa są poprawnie wpisane i że wybrałeś wystarczającąliczbę kategorii." +"słowa są poprawnie wpisane i że wybrałeś wystarczającą liczbę kategorii." #: sphinx/themes/basic/static/searchtools.js:508 #, python-format @@ -761,16 +759,16 @@ msgstr "Przeszukiwanie zakończone, znaleziono %s pasujących stron." #: sphinx/themes/default/static/sidebar.js:66 msgid "Expand sidebar" -msgstr "" +msgstr "Rozwiń pasek boczny" #: sphinx/themes/default/static/sidebar.js:79 #: sphinx/themes/default/static/sidebar.js:107 msgid "Collapse sidebar" -msgstr "" +msgstr "Zwiń pasek boczny" #: sphinx/themes/haiku/layout.html:26 msgid "Contents" -msgstr "" +msgstr "Treść" #: sphinx/writers/latex.py:172 msgid "Release" @@ -790,5 +788,5 @@ msgstr "Kontynuacja na następnej stronie" #: sphinx/writers/text.py:422 msgid "[image]" -msgstr "[obrazek]" +msgstr "[obraz]" From 7401622583248fe26bfcf8da283f068126aea7f4 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 3 Jan 2011 21:20:29 +0100 Subject: [PATCH 472/744] #553: Added :rst:dir:`testcleanup` blocks in the doctest extension. --- CHANGES | 2 ++ doc/ext/doctest.rst | 16 +++++++++ sphinx/ext/doctest.py | 78 ++++++++++++++++++++++++++++++------------ tests/root/doctest.txt | 6 ++++ tests/test_doctest.py | 10 ++++++ 5 files changed, 91 insertions(+), 21 deletions(-) diff --git a/CHANGES b/CHANGES index 5cd04b793..ea7489af6 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,8 @@ Release 1.1 (in development) * #559: :confval:`html_add_permalinks` is now a string giving the text to display in permalinks. +* #553: Added :rst:dir:`testcleanup` blocks in the doctest extension. + Release 1.0.6 (in development) ============================== diff --git a/doc/ext/doctest.rst b/doc/ext/doctest.rst index 20e747de1..c884a10d8 100644 --- a/doc/ext/doctest.rst +++ b/doc/ext/doctest.rst @@ -45,6 +45,14 @@ names. but executed before the doctests of the group(s) it belongs to. +.. rst:directive:: .. testcleanup:: [group] + + A cleanup code block. This code is not shown in the output for other + builders, but executed after the doctests of the group(s) it belongs to. + + .. versionadded:: 1.1 + + .. rst:directive:: .. doctest:: [group] A doctest-style code block. You can use standard :mod:`doctest` flags for @@ -181,6 +189,14 @@ There are also these config values for customizing the doctest extension: .. versionadded:: 0.6 +.. confval:: doctest_global_cleanup + + Python code that is treated like it were put in a ``testcleanup`` directive + for *every* file that is tested, and for every group. You can use this to + e.g. remove any temporary files that the tests leave behind. + + .. versionadded:: 1.1 + .. confval:: doctest_test_doctest_blocks If this is a nonempty string (the default is ``'default'``), standard reST diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 95fcd990a..fb87df99a 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -56,7 +56,7 @@ class TestDirective(Directive): test = code code = doctestopt_re.sub('', code) nodetype = nodes.literal_block - if self.name == 'testsetup' or 'hide' in self.options: + if self.name in ('testsetup', 'testcleanup') or 'hide' in self.options: nodetype = nodes.comment if self.arguments: groups = [x.strip() for x in self.arguments[0].split(',')] @@ -86,6 +86,9 @@ class TestDirective(Directive): class TestsetupDirective(TestDirective): option_spec = {} +class TestcleanupDirective(TestDirective): + option_spec = {} + class DoctestDirective(TestDirective): option_spec = { 'hide': directives.flag, @@ -113,6 +116,7 @@ class TestGroup(object): self.name = name self.setup = [] self.tests = [] + self.cleanup = [] def add_code(self, code, prepend=False): if code.type == 'testsetup': @@ -120,6 +124,8 @@ class TestGroup(object): self.setup.insert(0, code) else: self.setup.append(code) + elif code.type == 'testcleanup': + self.cleanup.append(code) elif code.type == 'doctest': self.tests.append([code]) elif code.type == 'testcode': @@ -131,8 +137,8 @@ class TestGroup(object): raise RuntimeError('invalid TestCode type') def __repr__(self): - return 'TestGroup(name=%r, setup=%r, tests=%r)' % ( - self.name, self.setup, self.tests) + return 'TestGroup(name=%r, setup=%r, cleanup=%r, tests=%r)' % ( + self.name, self.setup, self.cleanup, self.tests) class TestCode(object): @@ -204,6 +210,8 @@ class DocTestBuilder(Builder): self.total_tries = 0 self.setup_failures = 0 self.setup_tries = 0 + self.cleanup_failures = 0 + self.cleanup_tries = 0 date = time.strftime('%Y-%m-%d %H:%M:%S') @@ -240,12 +248,14 @@ Doctest summary %5d test%s %5d failure%s in tests %5d failure%s in setup code +%5d failure%s in cleanup code ''' % (self.total_tries, s(self.total_tries), self.total_failures, s(self.total_failures), - self.setup_failures, s(self.setup_failures))) + self.setup_failures, s(self.setup_failures), + self.cleanup_failures, s(self.cleanup_failures))) self.outfile.close() - if self.total_failures or self.setup_failures: + if self.total_failures or self.setup_failures or self.cleanup_failures: self.app.statuscode = 1 def write(self, build_docnames, updated_docnames, method='update'): @@ -265,8 +275,11 @@ Doctest summary optionflags=self.opt) self.test_runner = SphinxDocTestRunner(verbose=False, optionflags=self.opt) + self.cleanup_runner = SphinxDocTestRunner(verbose=False, + optionflags=self.opt) self.test_runner._fakeout = self.setup_runner._fakeout + self.cleanup_runner._fakeout = self.setup_runner._fakeout if self.config.doctest_test_doctest_blocks: def condition(node): @@ -301,6 +314,11 @@ Doctest summary 'testsetup', lineno=0) for group in groups.itervalues(): group.add_code(code, prepend=True) + if self.config.doctest_global_cleanup: + code = TestCode(self.config.doctest_global_cleanup, + 'testcleanup', lineno=0) + for group in groups.itervalues(): + group.add_code(code) if not groups: return @@ -316,29 +334,42 @@ Doctest summary res_f, res_t = self.test_runner.summarize(self._out, verbose=True) self.total_failures += res_f self.total_tries += res_t + if self.cleanup_runner.tries: + res_f, res_t = self.cleanup_runner.summarize(self._out, verbose=True) + self.cleanup_failures += res_f + self.cleanup_tries += res_t def compile(self, code, name, type, flags, dont_inherit): return compile(code, name, self.type, flags, dont_inherit) def test_group(self, group, filename): ns = {} - setup_examples = [] - for setup in group.setup: - setup_examples.append(doctest.Example(setup.code, '', - lineno=setup.lineno)) - if setup_examples: - # simulate a doctest with the setup code - setup_doctest = doctest.DocTest(setup_examples, {}, - '%s (setup code)' % group.name, - filename, 0, None) - setup_doctest.globs = ns - old_f = self.setup_runner.failures - self.type = 'exec' # the snippet may contain multiple statements - self.setup_runner.run(setup_doctest, out=self._warn_out, - clear_globs=False) - if self.setup_runner.failures > old_f: - # don't run the group + + def run_setup_cleanup(runner, testcodes, what): + examples = [] + for testcode in testcodes: + examples.append(doctest.Example(testcode.code, '', + lineno=testcode.lineno)) + if not examples: return + # simulate a doctest with the code + sim_doctest = doctest.DocTest(examples, {}, + '%s (%s code)' % (group.name, what), + filename, 0, None) + sim_doctest.globs = ns + old_f = runner.failures + self.type = 'exec' # the snippet may contain multiple statements + runner.run(sim_doctest, out=self._warn_out, clear_globs=False) + if runner.failures > old_f: + return False + return True + + # run the setup code + if not run_setup_cleanup(self.setup_runner, group.setup, 'setup'): + # if setup failed, don't run the group + return + + # run the tests for code in group.tests: if len(code) == 1: # ordinary doctests (code/output interleaved) @@ -376,9 +407,13 @@ Doctest summary # also don't clear the globs namespace after running the doctest self.test_runner.run(test, out=self._warn_out, clear_globs=False) + # run the cleanup + run_setup_cleanup(self.cleanup_runner, group.cleanup, 'cleanup') + def setup(app): app.add_directive('testsetup', TestsetupDirective) + app.add_directive('testcleanup', TestcleanupDirective) app.add_directive('doctest', DoctestDirective) app.add_directive('testcode', TestcodeDirective) app.add_directive('testoutput', TestoutputDirective) @@ -387,3 +422,4 @@ def setup(app): app.add_config_value('doctest_path', [], False) app.add_config_value('doctest_test_doctest_blocks', 'default', False) app.add_config_value('doctest_global_setup', '', False) + app.add_config_value('doctest_global_cleanup', '', False) diff --git a/tests/root/doctest.txt b/tests/root/doctest.txt index ba9a72c52..d029cd880 100644 --- a/tests/root/doctest.txt +++ b/tests/root/doctest.txt @@ -121,3 +121,9 @@ Special directives .. testoutput:: group2 16 + + +.. testcleanup:: * + + import test_doctest + test_doctest.cleanup_call() diff --git a/tests/test_doctest.py b/tests/test_doctest.py index b4b6bbe21..aa7556791 100644 --- a/tests/test_doctest.py +++ b/tests/test_doctest.py @@ -15,10 +15,20 @@ import StringIO from util import * status = StringIO.StringIO() +cleanup_called = 0 @with_app(buildername='doctest', status=status) def test_build(app): + global cleanup_called + cleanup_called = 0 app.builder.build_all() if app.statuscode != 0: print >>sys.stderr, status.getvalue() assert False, 'failures in doctests' + # in doctest.txt, there are two named groups and the default group, + # so the cleanup function must be called three times + assert cleanup_called == 3, 'testcleanup did not get executed enough times' + +def cleanup_call(): + global cleanup_called + cleanup_called += 1 From 92cb77a1315065dc206c880c65c83b87c8576070 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 3 Jan 2011 21:48:37 +0100 Subject: [PATCH 473/744] #176: Provide ``private-members`` option for autodoc directives. --- CHANGES | 2 ++ doc/ext/autodoc.rst | 30 ++++++++++++++++++++---------- sphinx/ext/autodoc.py | 12 +++++++----- tests/test_autodoc.py | 1 + 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/CHANGES b/CHANGES index ea7489af6..544db77ea 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,8 @@ Release 1.1 (in development) * #460: Allow limiting the depth of section numbers for HTML. +* #176: Provide ``private-members`` option for autodoc directives. + * #138: Add an ``index`` role, to make inline index entries. * #443: Allow referencing external graphviz files. diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index 68837a9a9..ee58c0602 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -93,8 +93,8 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, .. autoclass:: Noodle :members: eat, slurp - * If you want to make the ``members`` option the default, see - :confval:`autodoc_default_flags`. + * If you want to make the ``members`` option (or other flag options described + below) the default, see :confval:`autodoc_default_flags`. * Members without docstrings will be left out, unless you give the ``undoc-members`` flag option:: @@ -103,6 +103,15 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, :members: :undoc-members: + * Private members will be included if the ``private-members`` flag option is + given:: + + .. autoclass:: my.Class + :members: + :private-members: + + .. versionadded:: 1.1 + * For classes and exceptions, members inherited from base classes will be left out, unless you give the ``inherited-members`` flag option, in addition to ``members``:: @@ -152,8 +161,8 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, .. versionadded:: 0.5 - * :rst:dir:`automodule` and :rst:dir:`autoclass` also has an ``member-order`` option - that can be used to override the global value of + * :rst:dir:`automodule` and :rst:dir:`autoclass` also has an ``member-order`` + option that can be used to override the global value of :confval:`autodoc_member_order` for one directive. .. versionadded:: 0.6 @@ -173,9 +182,9 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, .. rst:directive:: autofunction - autodata - automethod - autoattribute + autodata + automethod + autoattribute These work exactly like :rst:dir:`autoclass` etc., but do not offer the options used for automatic member documentation. @@ -193,11 +202,11 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, baz = 2 """Docstring for class attribute Foo.baz.""" - + def __init__(self): #: Doc comment for instance attribute qux. self.qux = 3 - + self.spam = 4 """Docstring for instance attribute spam.""" @@ -253,7 +262,8 @@ There are also new config values that you can set: This value is a list of autodoc directive flags that should be automatically applied to all autodoc directives. The supported flags are ``'members'``, - ``'undoc-members'``, ``'inherited-members'`` and ``'show-inheritance'``. + ``'undoc-members'``, ``'private-members'``, ``'inherited-members'`` and + ``'show-inheritance'``. If you set one of these flags in this config value, you can use a negated form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once. diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 3b378001f..64b3d2d49 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -518,8 +518,9 @@ class Documenter(object): Members are skipped if - - they are private (except if given explicitly) - - they are undocumented (except if undoc-members is given) + - they are private (except if given explicitly or the private-members + option is set) + - they are undocumented (except if the undoc-members option is set) The user can override the skipping decision by connecting to the ``autodoc-skip-member`` event. @@ -541,7 +542,7 @@ class Documenter(object): if want_all and membername.startswith('_'): # ignore members whose name starts with _ by default - skip = True + skip = not self.options.private_members elif (namespace, membername) in attr_docs: # keep documented attributes skip = False @@ -713,6 +714,7 @@ class ModuleDocumenter(Documenter): 'show-inheritance': bool_option, 'synopsis': identity, 'platform': identity, 'deprecated': bool_option, 'member-order': identity, 'exclude-members': members_set_option, + 'private-members': bool_option, } @classmethod @@ -866,7 +868,7 @@ class ClassDocumenter(ModuleLevelDocumenter): 'members': members_option, 'undoc-members': bool_option, 'noindex': bool_option, 'inherited-members': bool_option, 'show-inheritance': bool_option, 'member-order': identity, - 'exclude-members': members_set_option, + 'exclude-members': members_set_option, 'private-members': bool_option, } @classmethod @@ -1134,7 +1136,7 @@ class AutoDirective(Directive): # flags that can be given in autodoc_default_flags _default_flags = set(['members', 'undoc-members', 'inherited-members', - 'show-inheritance']) + 'show-inheritance', 'private-members']) # standard docutils directive settings has_content = True diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 8f983471c..7ab9f0559 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -30,6 +30,7 @@ def setup_module(): options = Struct( inherited_members = False, undoc_members = False, + private_members = False, show_inheritance = False, noindex = False, synopsis = '', From 594c1a90f4742cfc685089d905242806f2916500 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 3 Jan 2011 21:56:41 +0100 Subject: [PATCH 474/744] #520: Provide ``special-members`` option for autodoc directives. --- CHANGES | 2 ++ doc/ext/autodoc.rst | 16 ++++++++++++---- sphinx/ext/autodoc.py | 19 ++++++++++++++----- tests/test_autodoc.py | 1 + 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 544db77ea..297c8c947 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,8 @@ Release 1.1 (in development) * #176: Provide ``private-members`` option for autodoc directives. +* #520: Provide ``special-members`` option for autodoc directives. + * #138: Add an ``index`` role, to make inline index entries. * #443: Allow referencing external graphviz files. diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index ee58c0602..8026972ce 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -103,12 +103,20 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, :members: :undoc-members: - * Private members will be included if the ``private-members`` flag option is - given:: + * "Private" members (that is, those named like ``_private`` or ``__private``) + will be included if the ``private-members`` flag option is given. + + .. versionadded:: 1.1 + + * Python "special" members (that is, those named like ``__special__``) will + be included if the ``special-members`` flag option is given:: .. autoclass:: my.Class :members: :private-members: + :special-members: + + would document both "private" and "special" members of the class. .. versionadded:: 1.1 @@ -262,8 +270,8 @@ There are also new config values that you can set: This value is a list of autodoc directive flags that should be automatically applied to all autodoc directives. The supported flags are ``'members'``, - ``'undoc-members'``, ``'private-members'``, ``'inherited-members'`` and - ``'show-inheritance'``. + ``'undoc-members'``, ``'private-members'``, ``'special-members'``, + ``'inherited-members'`` and ``'show-inheritance'``. If you set one of these flags in this config value, you can use a negated form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once. diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 64b3d2d49..993f690ab 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -520,6 +520,8 @@ class Documenter(object): - they are private (except if given explicitly or the private-members option is set) + - they are special methods (except if given explicitly or the + special-members option is set) - they are undocumented (except if the undoc-members option is set) The user can override the skipping decision by connecting to the @@ -540,7 +542,11 @@ class Documenter(object): # if isattr is True, the member is documented as an attribute isattr = False - if want_all and membername.startswith('_'): + if want_all and membername.startswith('__') and \ + membername.endswith('__') and len(membername) > 4: + # special __methods__ + skip = not self.options.special_members + elif want_all and membername.startswith('_'): # ignore members whose name starts with _ by default skip = not self.options.private_members elif (namespace, membername) in attr_docs: @@ -714,7 +720,7 @@ class ModuleDocumenter(Documenter): 'show-inheritance': bool_option, 'synopsis': identity, 'platform': identity, 'deprecated': bool_option, 'member-order': identity, 'exclude-members': members_set_option, - 'private-members': bool_option, + 'private-members': bool_option, 'special-members': bool_option, } @classmethod @@ -868,7 +874,8 @@ class ClassDocumenter(ModuleLevelDocumenter): 'members': members_option, 'undoc-members': bool_option, 'noindex': bool_option, 'inherited-members': bool_option, 'show-inheritance': bool_option, 'member-order': identity, - 'exclude-members': members_set_option, 'private-members': bool_option, + 'exclude-members': members_set_option, + 'private-members': bool_option, 'special-members': bool_option, } @classmethod @@ -1135,8 +1142,10 @@ class AutoDirective(Directive): _special_attrgetters = {} # flags that can be given in autodoc_default_flags - _default_flags = set(['members', 'undoc-members', 'inherited-members', - 'show-inheritance', 'private-members']) + _default_flags = set([ + 'members', 'undoc-members', 'inherited-members', 'show-inheritance', + 'private-members', 'special-members', + ]) # standard docutils directive settings has_content = True diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 7ab9f0559..d70fa2606 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -31,6 +31,7 @@ def setup_module(): inherited_members = False, undoc_members = False, private_members = False, + special_members = False, show_inheritance = False, noindex = False, synopsis = '', From f6cb763ff8bdbe897d0a13bf0af2fd8f528f69b1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 3 Jan 2011 22:51:33 +0100 Subject: [PATCH 475/744] #564: Add :confval:`autodoc_docstring_signature` which retrieves the signature from the first line of the docstring, if it is found there. --- CHANGES | 4 ++++ doc/ext/autodoc.rst | 13 ++++++++++ sphinx/ext/autodoc.py | 55 +++++++++++++++++++++++++++++++++++++++---- tests/test_autodoc.py | 16 +++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 297c8c947..ccce082bf 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,10 @@ Release 1.1 (in development) * #460: Allow limiting the depth of section numbers for HTML. +* #564: Add :confval:`autodoc_docstring_signature` which retrieves + the signature from the first line of the docstring, if it is + found there. + * #176: Provide ``private-members`` option for autodoc directives. * #520: Provide ``special-members`` option for autodoc directives. diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index 8026972ce..483b30fab 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -285,6 +285,19 @@ There are also new config values that you can set: .. versionadded:: 1.0 +.. confval:: autodoc_docstring_signature + + Functions imported from C modules cannot be introspected, and therefore the + signature for such functions cannot be automatically determined. However, it + is a well- convention + + If this boolean value is set to ``True`` (which is the default), autodoc will + look at the first line of the docstring for functions and methods, and if it + looks like a signature, use the line as the signature and remove it from the + docstring content. + + .. versionadded:: 1.1 + Docstring preprocessing ----------------------- diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 993f690ab..851fcf87c 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -827,7 +827,53 @@ class ClassLevelDocumenter(Documenter): return modname, parents + [base] -class FunctionDocumenter(ModuleLevelDocumenter): +class DocstringSignatureMixin(object): + """ + Mixin for FunctionDocumenter and MethodDocumenter to provide the + feature of reading the signature from the docstring. + """ + + def _find_signature(self, encoding=None): + docstrings = Documenter.get_doc(self, encoding) + if len(docstrings) != 1: + return + doclines = docstrings[0] + setattr(self, '__new_doclines', doclines) + if not doclines: + return + # match first line of docstring against signature RE + match = py_ext_sig_re.match(doclines[0]) + if not match: + return + exmod, path, base, args, retann = match.groups() + # the base name must match ours + if not self.objpath or base != self.objpath[-1]: + return + # ok, now jump over remaining empty lines and set the remaining + # lines as the new doclines + i = 1 + while i < len(doclines) and not doclines[i].strip(): + i += 1 + setattr(self, '__new_doclines', doclines[i:]) + return args, retann + + def get_doc(self, encoding=None): + lines = getattr(self, '__new_doclines', None) + if lines is not None: + return [lines] + return Documenter.get_doc(self, encoding) + + def format_signature(self): + if self.args is None and self.env.config.autodoc_docstring_signature: + # only act if a signature is not explicitly given already, and if + # the feature is enabled + result = self._find_signature() + if result is not None: + self.args, self.retann = result + return Documenter.format_signature(self) + + +class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for functions. """ @@ -841,8 +887,8 @@ class FunctionDocumenter(ModuleLevelDocumenter): def format_args(self): if inspect.isbuiltin(self.object) or \ inspect.ismethoddescriptor(self.object): - # can never get arguments of a C function or method - return None + # cannot introspect arguments of a C function or method + pass try: argspec = inspect.getargspec(self.object) except TypeError: @@ -1008,7 +1054,7 @@ class DataDocumenter(ModuleLevelDocumenter): pass -class MethodDocumenter(ClassLevelDocumenter): +class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for methods (normal, static and class). """ @@ -1235,6 +1281,7 @@ def setup(app): app.add_config_value('autoclass_content', 'class', True) app.add_config_value('autodoc_member_order', 'alphabetic', True) app.add_config_value('autodoc_default_flags', [], True) + app.add_config_value('autodoc_docstring_signature', True, True) app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-signature') app.add_event('autodoc-skip-member') diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index d70fa2606..74e69a68c 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -492,6 +492,13 @@ def test_generate(): ], 'class', 'Class', member_order='bysource', all_members=True) + # test autodoc_docstring_signature + assert_result_contains( + '.. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', 'method', + 'test_autodoc.DocstringSig.meth') + assert_result_contains( + ' rest of docstring', 'method', 'test_autodoc.DocstringSig.meth') + # --- generate fodder ------------ @@ -582,3 +589,12 @@ class Outer(object): # should be documented as an alias factory = dict + + +class DocstringSig(object): + def meth(self): + """ + meth(FOO, BAR=1) -> BAZ + + rest of docstring + """ From a304c8d247458c47d81636319f8307860464c409 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 3 Jan 2011 23:50:30 +0100 Subject: [PATCH 476/744] Fix-up half-sentence. --- doc/ext/autodoc.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index 483b30fab..6c4a147ac 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -289,7 +289,8 @@ There are also new config values that you can set: Functions imported from C modules cannot be introspected, and therefore the signature for such functions cannot be automatically determined. However, it - is a well- convention + is an often-used convention to put the signature into the first line of the + function's docstring. If this boolean value is set to ``True`` (which is the default), autodoc will look at the first line of the docstring for functions and methods, and if it From 8fd5bd1e1909ac5c5e857049dafcdcc2975eb90d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 4 Jan 2011 00:04:04 +0100 Subject: [PATCH 477/744] Fix docstring preparation with included signature: ignore indentation of two lines when looking for the signature. --- sphinx/ext/autodoc.py | 14 +++++++------- sphinx/util/docstrings.py | 25 ++++++++++++++----------- tests/test_autodoc.py | 4 ++-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 851fcf87c..b61e606aa 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -412,15 +412,15 @@ class Documenter(object): # etc. don't support a prepended module name self.add_line(u' :module: %s' % self.modname, '') - def get_doc(self, encoding=None): + def get_doc(self, encoding=None, ignore=1): """Decode and return lines of the docstring(s) for the object.""" docstring = self.get_attr(self.object, '__doc__', None) # make sure we have Unicode docstrings, then sanitize and split # into lines if isinstance(docstring, unicode): - return [prepare_docstring(docstring)] + return [prepare_docstring(docstring, ignore)] elif docstring: - return [prepare_docstring(force_decode(docstring, encoding))] + return [prepare_docstring(force_decode(docstring, encoding), ignore)] return [] def process_doc(self, docstrings): @@ -834,7 +834,7 @@ class DocstringSignatureMixin(object): """ def _find_signature(self, encoding=None): - docstrings = Documenter.get_doc(self, encoding) + docstrings = Documenter.get_doc(self, encoding, 2) if len(docstrings) != 1: return doclines = docstrings[0] @@ -857,11 +857,11 @@ class DocstringSignatureMixin(object): setattr(self, '__new_doclines', doclines[i:]) return args, retann - def get_doc(self, encoding=None): + def get_doc(self, encoding=None, ignore=1): lines = getattr(self, '__new_doclines', None) if lines is not None: return [lines] - return Documenter.get_doc(self, encoding) + return Documenter.get_doc(self, encoding, ignore) def format_signature(self): if self.args is None and self.env.config.autodoc_docstring_signature: @@ -978,7 +978,7 @@ class ClassDocumenter(ModuleLevelDocumenter): self.add_line(_(u' Bases: %s') % ', '.join(bases), '') - def get_doc(self, encoding=None): + def get_doc(self, encoding=None, ignore=1): content = self.env.config.autoclass_content docstrings = [] diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py index d1a2ff8db..143482fe1 100644 --- a/sphinx/util/docstrings.py +++ b/sphinx/util/docstrings.py @@ -12,26 +12,29 @@ import sys -def prepare_docstring(s): - """Convert a docstring into lines of parseable reST. +def prepare_docstring(s, ignore=1): + """Convert a docstring into lines of parseable reST. Remove common leading + indentation, where the indentation of a given number of lines (usually just + one) is ignored. - Return it as a list of lines usable for inserting into a docutils ViewList - (used as argument of nested_parse().) An empty line is added to act as a - separator between this docstring and following content. + Return the docstring as a list of lines usable for inserting into a docutils + ViewList (used as argument of nested_parse().) An empty line is added to + act as a separator between this docstring and following content. """ lines = s.expandtabs().splitlines() - # Find minimum indentation of any non-blank lines after first line. + # Find minimum indentation of any non-blank lines after ignored lines. margin = sys.maxint - for line in lines[1:]: + for line in lines[ignore:]: content = len(line.lstrip()) if content: indent = len(line) - content margin = min(margin, indent) - # Remove indentation. - if lines: - lines[0] = lines[0].lstrip() + # Remove indentation from ignored lines. + for i in range(ignore): + if i < len(lines): + lines[i] = lines[i].lstrip() if margin < sys.maxint: - for i in range(1, len(lines)): lines[i] = lines[i][margin:] + for i in range(ignore, len(lines)): lines[i] = lines[i][margin:] # Remove any leading blank lines. while lines and not lines[0]: lines.pop(0) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 74e69a68c..063df861b 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -593,8 +593,8 @@ class Outer(object): class DocstringSig(object): def meth(self): - """ - meth(FOO, BAR=1) -> BAZ + """meth(FOO, BAR=1) -> BAZ +First line of docstring rest of docstring """ From 51852c0e8732085bc3f933667a3394320c1e98f2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 4 Jan 2011 11:27:42 +0100 Subject: [PATCH 478/744] #472: linkcheck builder: Check links in parallel, use HTTP HEAD requests and allow configuring the timeout. New config values: :confval:`linkcheck_timeout` and :confval:`linkcheck_workers`. --- CHANGES | 4 + doc/config.rst | 15 +++ sphinx/builders/linkcheck.py | 181 ++++++++++++++++++++--------------- sphinx/config.py | 2 + 4 files changed, 127 insertions(+), 75 deletions(-) diff --git a/CHANGES b/CHANGES index 174998567..3fe98cfc1 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,10 @@ Release 1.1 (in development) * #443: Allow referencing external graphviz files. +* #472: linkcheck builder: Check links in parallel, use HTTP HEAD + requests and allow configuring the timeout. New config values: + :confval:`linkcheck_timeout` and :confval:`linkcheck_workers`. + * #221: Add Swedish locale. * Added ``inline`` option to graphviz directives, and fixed the diff --git a/doc/config.rst b/doc/config.rst index 65601d8cf..992a714ef 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -1132,6 +1132,21 @@ Options for the linkcheck builder .. versionadded:: 1.1 +.. confval:: linkcheck_timeout + + A timeout value, in seconds, for the linkcheck builder. **Only works in + Python 2.6 and higher.** The default is to use Python's global socket + timeout. + + .. versionadded:: 1.1 + +.. confval:: linkcheck_workers + + The number of worker threads to use when checking links. Default is 5 + threads. + + .. versionadded:: 1.1 + .. rubric:: Footnotes diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index dd87d70d8..e40919861 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -10,9 +10,12 @@ """ import re +import sys +import Queue import socket +import threading from os import path -from urllib2 import build_opener, HTTPError +from urllib2 import build_opener, Request from docutils import nodes @@ -24,6 +27,12 @@ opener = build_opener() opener.addheaders = [('User-agent', 'Mozilla/5.0')] +class HeadRequest(Request): + """Subclass of urllib2.Request that sends a HEAD request.""" + def get_method(self): + return 'HEAD' + + class CheckExternalLinksBuilder(Builder): """ Checks for broken external links. @@ -40,6 +49,83 @@ class CheckExternalLinksBuilder(Builder): # create output file open(path.join(self.outdir, 'output.txt'), 'w').close() + # create queues and worker threads + self.wqueue = Queue.Queue() + self.rqueue = Queue.Queue() + self.workers = [] + for i in range(self.app.config.linkcheck_workers): + thread = threading.Thread(target=self.check_thread) + thread.setDaemon(True) + thread.start() + self.workers.append(thread) + + def check_thread(self): + kwargs = {} + if sys.version_info > (2, 5) and self.app.config.linkcheck_timeout: + kwargs['timeout'] = self.app.config.linkcheck_timeout + + def check(): + # check for various conditions without bothering the network + if len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:': + return 'unchecked', '' + elif not (uri[0:5] == 'http:' or uri[0:6] == 'https:'): + return 'local', '' + elif uri in self.good: + return 'working', '' + elif uri in self.broken: + return 'broken', self.broken[uri] + elif uri in self.redirected: + return 'redirected', self.redirected[uri] + for rex in self.to_ignore: + if rex.match(uri): + return 'ignored', '' + + # need to actually check the URI + try: + f = opener.open(HeadRequest(uri), **kwargs) + f.close() + except Exception, err: + self.broken[uri] = str(err) + return 'broken', str(err) + if f.url.rstrip('/') == uri.rstrip('/'): + self.good.add(uri) + return 'working', 'new' + else: + self.redirected[uri] = f.url + return 'redirected', f.url + + while True: + uri, docname, lineno = self.wqueue.get() + if uri is None: + break + status, info = check() + self.rqueue.put((uri, docname, lineno, status, info)) + + def process_result(self, result): + uri, docname, lineno, status, info = result + if status == 'unchecked': + return + if status == 'working' and info != 'new': + return + if lineno: + self.info('(line %3d) ' % lineno, nonl=1) + if status == 'ignored': + self.info(uri + ' - ' + darkgray('ignored')) + elif status == 'local': + self.info(uri + ' - ' + darkgray('local')) + self.write_entry('local', docname, lineno, uri) + elif status == 'working': + self.info(uri + ' - ' + darkgreen('working')) + elif status == 'broken': + self.info(uri + ' - ' + red('broken: ') + info) + self.write_entry('broken', docname, lineno, uri + ': ' + info) + if self.app.quiet: + self.warn('broken link: %s' % uri, + '%s:%s' % (self.env.doc2path(docname), lineno)) + elif status == 'redirected': + self.info(uri + ' - ' + purple('redirected') + ' to ' + info) + self.write_entry('redirected', docname, lineno, uri + ' to ' + info) + def get_target_uri(self, docname, typ=None): return '' @@ -51,65 +137,25 @@ class CheckExternalLinksBuilder(Builder): def write_doc(self, docname, doctree): self.info() + n = 0 for node in doctree.traverse(nodes.reference): - try: - self.check(node, docname) - except KeyError: + if 'refuri' not in node: continue - - def check(self, node, docname): - uri = node['refuri'] - - if '#' in uri: - uri = uri.split('#')[0] - - if uri in self.good: - return - - lineno = None - while lineno is None: - node = node.parent - if node is None: - break - lineno = node.line - - if len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:': - return - - if lineno: - self.info('(line %3d) ' % lineno, nonl=1) - for rex in self.to_ignore: - if rex.match(uri): - self.info(uri + ' - ' + darkgray('ignored')) - return - if uri[0:5] == 'http:' or uri[0:6] == 'https:': - self.info(uri, nonl=1) - - if uri in self.broken: - (r, s) = self.broken[uri] - elif uri in self.redirected: - (r, s) = self.redirected[uri] - else: - (r, s) = self.resolve(uri) - - if r == 0: - self.info(' - ' + darkgreen('working')) - self.good.add(uri) - elif r == 2: - self.info(' - ' + red('broken: ') + s) - self.write_entry('broken', docname, lineno, uri + ': ' + s) - self.broken[uri] = (r, s) - if self.app.quiet: - self.warn('broken link: %s' % uri, - '%s:%s' % (self.env.doc2path(docname), lineno)) - else: - self.info(' - ' + purple('redirected') + ' to ' + s) - self.write_entry('redirected', docname, - lineno, uri + ' to ' + s) - self.redirected[uri] = (r, s) - else: - self.info(uri + ' - ' + darkgray('local')) - self.write_entry('local', docname, lineno, uri) + uri = node['refuri'] + if '#' in uri: + uri = uri.split('#')[0] + lineno = None + while lineno is None: + node = node.parent + if node is None: + break + lineno = node.line + self.wqueue.put((uri, docname, lineno), False) + n += 1 + done = 0 + while done < n: + self.process_result(self.rqueue.get()) + done += 1 if self.broken: self.app.statuscode = 1 @@ -120,21 +166,6 @@ class CheckExternalLinksBuilder(Builder): line, what, uri)) output.close() - def resolve(self, uri): - try: - f = opener.open(uri) - f.close() - except HTTPError, err: - #if err.code == 403 and uri.startswith('http://en.wikipedia.org/'): - # # Wikipedia blocks requests from urllib User-Agent - # return (0, 0) - return (2, str(err)) - except Exception, err: - return (2, str(err)) - if f.url.rstrip('/') == uri.rstrip('/'): - return (0, 0) - else: - return (1, f.url) - def finish(self): - return + for worker in self.workers: + self.wqueue.put((None, None, None), False) diff --git a/sphinx/config.py b/sphinx/config.py index 922af803a..b461c94bd 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -168,6 +168,8 @@ class Config(object): # linkcheck options linkcheck_ignore = ([], None), + linkcheck_timeout = (None, None), + linkcheck_workers = (5, None), ) def __init__(self, dirname, filename, overrides, tags): From c5b5c16cb31f4fedeaabf443cb4a5f5f96f8f046 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 4 Jan 2011 11:37:56 +0100 Subject: [PATCH 479/744] Fix some broken URLs. --- EXAMPLES | 5 ++--- doc/theming.rst | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/EXAMPLES b/EXAMPLES index 0dab0046e..54178a2f8 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -20,7 +20,7 @@ Documentation using the default theme * Cython: http://docs.cython.org/ * C\\C++ Python language binding project: http://language-binding.net/index.html * Director: http://packages.python.org/director/ -* F2py: http://www.f2py.org/html/ +* F2py: http://f2py.sourceforge.net/docs/ * GeoDjango: http://geodjango.org/docs/ * gevent: http://www.gevent.org/ * Google Wave API: http://wave-robot-python-client.googlecode.com/svn/trunk/pydocs/index.html @@ -70,7 +70,6 @@ Documentation using a customized version of the default theme * NOC: http://trac.nocproject.org/trac/wiki/NocGuide * NumPy: http://docs.scipy.org/doc/numpy/reference/ * Peach^3: http://peach3.nl/doc/latest/userdoc/ -* Py on Windows: http://timgolden.me.uk/python-on-windows/ * PyLit: http://pylit.berlios.de/ * Sage: http://sagemath.org/doc/ * SciPy: http://docs.scipy.org/doc/scipy/reference/ @@ -108,7 +107,7 @@ Documentation using another builtin theme * C/C++ Development with Eclipse: http://book.dehlia.in/c-cpp-eclipse/ (agogo) * Distribute: http://packages.python.org/distribute/ (nature) -* Jinja: http://jinja.pocoo.org/2/documentation/ (scrolls) +* Jinja: http://jinja.pocoo.org/ (scrolls) * pip: http://pip.openplans.org/ (nature) * Programmieren mit PyGTK und Glade (German): http://www.florian-diesch.de/doc/python-und-glade/online/ (agogo) diff --git a/doc/theming.rst b/doc/theming.rst index 552a0eaf8..ee6900d41 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -144,8 +144,7 @@ These themes are: on the right side. There are currently no options beyond *nosidebar*. * **scrolls** -- A more lightweight theme, based on `the Jinja documentation - `_. The following color options are - available: + `_. The following color options are available: - **headerbordercolor** - **subheadlinecolor** From 7beb8533b56c71b07a040ecb71ca1e13a0b52932 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 4 Jan 2011 12:40:19 +0100 Subject: [PATCH 480/744] #273: Add an API for adding full-text search support for languages other than English. Add support for Japanese. Based on the implementation by SHIBUKAWA Yoshiki in https://bitbucket.org/shibu/sphinx/. --- AUTHORS | 1 + CHANGES | 3 + doc/config.rst | 32 ++ doc/ext/appapi.rst | 9 + sphinx/application.py | 5 + sphinx/builders/html.py | 9 +- sphinx/config.py | 2 + sphinx/{search.py => search/__init__.py} | 149 +++++++--- sphinx/search/en.py | 242 ++++++++++++++++ sphinx/search/ja.py | 273 ++++++++++++++++++ .../{searchtools.js => searchtools.js_t} | 197 +------------ 11 files changed, 681 insertions(+), 241 deletions(-) rename sphinx/{search.py => search/__init__.py} (61%) create mode 100644 sphinx/search/en.py create mode 100644 sphinx/search/ja.py rename sphinx/themes/basic/static/{searchtools.js => searchtools.js_t} (68%) diff --git a/AUTHORS b/AUTHORS index 04987b60a..3d6d478e7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -27,6 +27,7 @@ Other contributors, listed alphabetically, are: * Benjamin Peterson -- unittests * T. Powers -- HTML output improvements * Stefan Seefeld -- toctree improvements +* Shibukawa Yoshiki -- pluggable search API and Japanese search * Antonio Valentino -- qthelp builder * Pauli Virtanen -- autodoc improvements, autosummary extension * Stefan van der Walt -- autosummary extension diff --git a/CHANGES b/CHANGES index abe2bdd2b..b03f1df54 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,9 @@ Release 1.1 (in development) requests and allow configuring the timeout. New config values: :confval:`linkcheck_timeout` and :confval:`linkcheck_workers`. +* #273: Add an API for adding full-text search support for languages + other than English. Add support for Japanese. + * #221: Add Swedish locale. * Added ``inline`` option to graphviz directives, and fixed the diff --git a/doc/config.rst b/doc/config.rst index 992a714ef..153d39507 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -694,6 +694,38 @@ that use Sphinx' HTMLWriter class. .. versionadded:: 1.0 +.. confval:: html_search_language + + Language to be used for generating the HTML full-text search index. This + defaults to the global language selected with :confval:`language`. If there + is no support for this language, ``"en"`` is used which selects the English + language. + + Support is present for these languages: + + * ``en`` -- English + * ``ja`` -- Japanese + + .. versionadded:: 1.1 + +.. confval:: html_search_options + + A dictionary with options for the search language support, empty by default. + The meaning of these options depends on the language selected. + + The English support has no options. + + The Japanese support has these options: + + * ``type`` -- ``'mecab'`` or ``'default'`` (selects either MeCab or + TinySegmenter word splitter algorithm) + * ``dic_enc`` -- the encoding for the MeCab algorithm + * ``dict`` -- the dictionary to use for the MeCab algorithm + * ``lib`` -- the library name for finding the MeCab library via ctypes if the + Python binding is not installed + + .. versionadded:: 1.1 + .. confval:: htmlhelp_basename Output file base name for HTML help builder. Default is ``'pydoc'``. diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst index bbe2070fe..aa314584c 100644 --- a/doc/ext/appapi.rst +++ b/doc/ext/appapi.rst @@ -286,6 +286,15 @@ the following public API: .. versionadded:: 0.6 +.. method:: Sphinx.add_search_language(cls) + + Add *cls*, which must be a subclass of :class:`sphinx.search.SearchLanguage`, + as a support language for building the HTML full-text search index. The + class must have a *lang* attribute that indicates the language it should be + used for. See :confval:`html_search_language`. + + .. versionadded:: 1.1 + .. method:: Sphinx.connect(event, callback) Register *callback* to be called when *event* is emitted. For details on diff --git a/sphinx/application.py b/sphinx/application.py index 7042c439f..0d9326f0b 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -487,6 +487,11 @@ class Sphinx(object): from sphinx.ext import autodoc autodoc.AutoDirective._special_attrgetters[type] = getter + def add_search_language(self, cls): + from sphinx.search import languages, SearchLanguage + assert isinstance(cls, SearchLanguage) + languages[cls.lang] = cls + class TemplateBridge(object): """ diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index f28014995..ace4dd015 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -539,13 +539,18 @@ class StandaloneHTMLBuilder(Builder): if jsfile: copyfile(jsfile, path.join(self.outdir, '_static', 'translations.js')) + + # add context items for search function used in searchtools.js_t + ctx = self.globalcontext.copy() + ctx.update(self.indexer.globalcontext_for_searchtool()) + # then, copy over theme-supplied static files if self.theme: themeentries = [path.join(themepath, 'static') for themepath in self.theme.get_dirchain()[::-1]] for entry in themeentries: copy_static_entry(entry, path.join(self.outdir, '_static'), - self, self.globalcontext) + self, ctx) # then, copy over all user-supplied static files staticentries = [path.join(self.confdir, spath) for spath in self.config.html_static_path] @@ -558,7 +563,7 @@ class StandaloneHTMLBuilder(Builder): self.warn('html_static_path entry %r does not exist' % entry) continue copy_static_entry(entry, path.join(self.outdir, '_static'), self, - self.globalcontext, exclude_matchers=matchers) + ctx, exclude_matchers=matchers) # copy logo and favicon files if not already in static path if self.config.html_logo: logobase = path.basename(self.config.html_logo) diff --git a/sphinx/config.py b/sphinx/config.py index febc0a600..e86acaea5 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -106,6 +106,8 @@ class Config(object): html_output_encoding = ('utf-8', 'html'), html_compact_lists = (True, 'html'), html_secnumber_suffix = ('. ', 'html'), + html_search_language = (None, 'html'), + html_search_options = ({}, 'html'), # HTML help only options htmlhelp_basename = (lambda self: make_filename(self.project), None), diff --git a/sphinx/search.py b/sphinx/search/__init__.py similarity index 61% rename from sphinx/search.py rename to sphinx/search/__init__.py index 51f997c2d..ff3124542 100644 --- a/sphinx/search.py +++ b/sphinx/search/__init__.py @@ -3,7 +3,7 @@ sphinx.search ~~~~~~~~~~~~~ - Create a search index for offline search. + Create a full-text search index for offline search. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. @@ -14,28 +14,90 @@ import cPickle as pickle from docutils.nodes import comment, Text, NodeVisitor, SkipNode from sphinx.util import jsdump, rpartition -try: - # http://bitbucket.org/methane/porterstemmer/ - from porterstemmer import Stemmer as CStemmer - CSTEMMER = True -except ImportError: - from sphinx.util.stemmer import PorterStemmer - CSTEMMER = False -word_re = re.compile(r'\w+(?u)') +class SearchLanguage(object): + """ + This class is the base class for search natural language preprocessors. If + you want to add support for a new language, you should override the methods + of this class. -stopwords = set(""" -a and are as at -be but by -for -if in into is it -near no not -of on or -such -that the their then there these they this to -was will with -""".split()) + You should override `lang` class property too (e.g. 'en', 'fr' and so on). + + .. attribute:: stopwords + + This is a set of stop words of the target language. Default `stopwords` + is empty. This word is used for building index and embedded in JS. + + .. attribute:: js_stemmer_code + + Return stemmer class of JavaScript version. This class' name should be + ``Stemmer`` and this class must have ``stemWord`` method. This string is + embedded as-is in searchtools.js. + + This class is used to preprocess search word which Sphinx HTML readers + type, before searching index. Default implementation does nothing. + """ + lang = None + stopwords = set() + js_stemmer_code = """ +/** + * Dummy stemmer for languages without stemming rules. + */ +var Stemmer = function() { + this.stemWord = function(w) { + return w; + } +} +""" + + _word_re = re.compile(r'\w+(?u)') + + def __init__(self, options): + self.options = options + self.init(options) + + def init(self, options): + """ + Initialize the class with the options the user has given. + """ + + def split(self, input): + """ + This method splits a sentence into words. Default splitter splits input + at white spaces, which should be enough for most languages except CJK + languages. + """ + return self._word_re.findall(input) + + def stem(self, word): + """ + This method implements stemming algorithm of the Python version. + + Default implementation does nothing. You should implement this if the + language has any stemming rules. + + This class is used to preprocess search words before registering them in + the search index. The stemming of the Python version and the JS version + (given in the js_stemmer_code attribute) must be compatible. + """ + return word + + def word_filter(self, word): + """ + Return true if the target word should be registered in the search index. + This method is called after stemming. + """ + return not (((len(word) < 3) and (12353 < ord(word[0]) < 12436)) or + (ord(word[0]) < 256 and (len(word) < 3 or word in self.stopwords or + word.isdigit()))) + +from sphinx.search import en, ja + +languages = { + 'en': en.SearchEnglish, + 'ja': ja.SearchJapanese, +} class _JavaScriptIndex(object): @@ -67,39 +129,21 @@ class _JavaScriptIndex(object): js_index = _JavaScriptIndex() -if CSTEMMER: - class Stemmer(CStemmer): - - def stem(self, word): - return self(word.lower()) - -else: - class Stemmer(PorterStemmer): - """ - All those porter stemmer implementations look hideous. - make at least the stem method nicer. - """ - - def stem(self, word): - word = word.lower() - return PorterStemmer.stem(self, word, 0, len(word) - 1) - - - class WordCollector(NodeVisitor): """ A special visitor that collects words for the `IndexBuilder`. """ - def __init__(self, document): + def __init__(self, document, lang): NodeVisitor.__init__(self, document) self.found_words = [] + self.lang = lang def dispatch_visit(self, node): if node.__class__ is comment: raise SkipNode if node.__class__ is Text: - self.found_words.extend(word_re.findall(node.astext())) + self.found_words.extend(self.lang.split(node.astext())) class IndexBuilder(object): @@ -114,7 +158,6 @@ class IndexBuilder(object): def __init__(self, env): self.env = env - self._stemmer = Stemmer() # filename -> title self._titles = {} # stemmed word -> set(filenames) @@ -123,6 +166,11 @@ class IndexBuilder(object): self._objtypes = {} # objtype index -> objname (localized) self._objnames = {} + # add language-specific SearchLanguage instance + search_language = env.config.html_search_language or env.config.language + if not search_language or search_language not in languages: + search_language = 'en' + self.lang = languages[search_language](env.config.html_search_options) def load(self, stream, format): """Reconstruct from frozen data.""" @@ -215,17 +263,22 @@ class IndexBuilder(object): """Feed a doctree to the index.""" self._titles[filename] = title - visitor = WordCollector(doctree) + visitor = WordCollector(doctree, self.lang) doctree.walk(visitor) - def add_term(word, stem=self._stemmer.stem): + def add_term(word, stem=self.lang.stem): word = stem(word) - if len(word) < 3 or word in stopwords or word.isdigit(): - return - self._mapping.setdefault(word, set()).add(filename) + if self.lang.word_filter(word): + self._mapping.setdefault(word, set()).add(filename) - for word in word_re.findall(title): + for word in self.lang.split(title): add_term(word) for word in visitor.found_words: add_term(word) + + def globalcontext_for_searchtool(self): + return dict( + search_language_stemming_code = self.lang.js_stemmer_code, + search_language_stop_words = jsdump.dumps(self.lang.stopwords), + ) diff --git a/sphinx/search/en.py b/sphinx/search/en.py new file mode 100644 index 000000000..16e93e891 --- /dev/null +++ b/sphinx/search/en.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +""" + sphinx.search_languages.en + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + English search language: includes the JS porter stemmer. + + :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from sphinx.search import SearchLanguage + +try: + # http://bitbucket.org/methane/porterstemmer/ + from porterstemmer import Stemmer as CStemmer + CSTEMMER = True +except ImportError: + from sphinx.util.stemmer import PorterStemmer + CSTEMMER = False + + +english_stopwords = set(""" +a and are as at +be but by +for +if in into is it +near no not +of on or +such +that the their then there these they this to +was will with +""".split()) + +js_porter_stemmer = """ +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} +""" + + +class SearchEnglish(SearchLanguage): + lang = 'en' + js_stemmer_code = js_porter_stemmer + stopwords = english_stopwords + + def init(self, options): + if CSTEMMER: + class Stemmer(CStemmer): + def stem(self, word): + return self(word.lower()) + else: + class Stemmer(PorterStemmer): + """All those porter stemmer implementations look hideous; + make at least the stem method nicer. + """ + def stem(self, word): + word = word.lower() + return PorterStemmer.stem(self, word, 0, len(word) - 1) + + self.stemmer = Stemmer() + + def stem(self, word): + return self.stemmer.stem(word) diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py new file mode 100644 index 000000000..18d21bf01 --- /dev/null +++ b/sphinx/search/ja.py @@ -0,0 +1,273 @@ +# -*- coding: utf-8 -*- +""" + sphinx.search_languages.ja + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Japanese search language: includes routine to split words. + + :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +# Python Version of TinySegmenter +# (http://chasen.org/~taku/software/TinySegmenter/) +# TinySegmenter is super compact Japanese tokenizer. +# +# TinySegmenter was originally developed by Taku Kudo . +# Python Version was developed by xnights . +# For details, see http://programming-magic.com/?id=170 + +import os +import re +import sys + +try: + import MeCab + native_module = True +except ImportError: + native_module = False + +from sphinx.search import SearchLanguage + + +class MecabBinder(object): + def __init__(self, options): + self.ctypes_libmecab = None + self.ctypes_mecab = None + if not native_module: + self.init_ctypes(options) + else: + self.init_native(options) + self.dict_encode = options.get('dic_enc', 'utf-8') + + def split(self, input): + input2 = input.encode(self.dict_encode) + if native_module: + result = self.native.parse(input2) + else: + result = self.ctypes_libmecab.mecab_sparse_tostr( + self.ctypes_mecab, input) + return result.decode(self.dict_encode).split(' ') + + def init_native(self, options): + param = '-Owakati' + dict = options.get('dict') + if dict: + param += ' -d %s' % dict + self.native = MeCab.Tagger(param) + + def init_ctypes(self, options): + import ctypes.util + + lib = options.get('lib') + + if lib is None: + if sys.platform.startswith('win'): + libname = 'libmecab.dll' + else: + libname = 'mecab' + libpath = ctypes.util.find_library(libname) + elif os.path.basename(lib) == lib: + libpath = ctypes.util.find_library(lib) + else: + libpath = None + if os.path.exists(lib): + libpath = lib + if libpath is None: + raise RuntimeError('MeCab dynamic library is not available') + + param = 'mecab -Owakati' + dict = options.get('dict') + if dict: + param += ' -d %s' % dict + + self.ctypes_libmecab = ctypes.CDLL(libpath) + self.ctypes_libmecab.mecab_sparse_tostr.restype = ctypes.c_char_p + self.ctypes_mecab = self.libmecab.mecab_new2(param) + + def __del__(self): + if self.ctypes_libmecab: + self.ctypes_libmecab.mecab_destroy(self.ctypes_mecab) + + +class TinySegmenter(object): + patterns_ = dict([(re.compile(pattern), value) for pattern, value in { + u'[一二三四五六七八九十百千万億兆]': u'M', + u'[一-龠々〆ヵヶ]': u'H', + u'[ぁ-ん]': u'I', + u'[ァ-ヴーア-ン゙ー]': u'K', + u'[a-zA-Za-zA-Z]': u'A', + u'[0-90-9]': u'N', + }.iteritems()]) + BIAS__ = -332 + BC1__ = {u'HH':6,u'II':2461,u'KH':406,u'OH':-1378} + BC2__ = {u'AA':-3267,u'AI':2744,u'AN':-878,u'HH':-4070,u'HM':-1711,u'HN':4012,u'HO':3761,u'IA':1327,u'IH':-1184,u'II':-1332,u'IK':1721,u'IO':5492,u'KI':3831,u'KK':-8741,u'MH':-3132,u'MK':3334,u'OO':-2920} + BC3__ = {u'HH':996,u'HI':626,u'HK':-721,u'HN':-1307,u'HO':-836,u'IH':-301,u'KK':2762,u'MK':1079,u'MM':4034,u'OA':-1652,u'OH':266} + BP1__ = {u'BB':295,u'OB':304,u'OO':-125,u'UB':352} + BP2__ = {u'BO':60,u'OO':-1762} + BQ1__ = {u'BHH':1150,u'BHM':1521,u'BII':-1158,u'BIM':886,u'BMH':1208,u'BNH':449,u'BOH':-91,u'BOO':-2597,u'OHI':451,u'OIH':-296,u'OKA':1851,u'OKH':-1020,u'OKK':904,u'OOO':2965} + BQ2__ = {u'BHH':118,u'BHI':-1159,u'BHM':466,u'BIH':-919,u'BKK':-1720,u'BKO':864,u'OHH':-1139,u'OHM':-181,u'OIH':153,u'UHI':-1146} + BQ3__ = {u'BHH':-792,u'BHI':2664,u'BII':-299,u'BKI':419,u'BMH':937,u'BMM':8335,u'BNN':998,u'BOH':775,u'OHH':2174,u'OHM':439,u'OII':280,u'OKH':1798,u'OKI':-793,u'OKO':-2242,u'OMH':-2402,u'OOO':11699} + BQ4__ = {u'BHH':-3895,u'BIH':3761,u'BII':-4654,u'BIK':1348,u'BKK':-1806,u'BMI':-3385,u'BOO':-12396,u'OAH':926,u'OHH':266,u'OHK':-2036,u'ONN':-973} + BW1__ = {u',と':660,u',同':727,u'B1あ':1404,u'B1同':542,u'、と':660,u'、同':727,u'」と':1682,u'あっ':1505,u'いう':1743,u'いっ':-2055,u'いる':672,u'うし':-4817,u'うん':665,u'から':3472,u'がら':600,u'こう':-790,u'こと':2083,u'こん':-1262,u'さら':-4143,u'さん':4573,u'した':2641,u'して':1104,u'すで':-3399,u'そこ':1977,u'それ':-871,u'たち':1122,u'ため':601,u'った':3463,u'つい':-802,u'てい':805,u'てき':1249,u'でき':1127,u'です':3445,u'では':844,u'とい':-4915,u'とみ':1922,u'どこ':3887,u'ない':5713,u'なっ':3015,u'など':7379,u'なん':-1113,u'にし':2468,u'には':1498,u'にも':1671,u'に対':-912,u'の一':-501,u'の中':741,u'ませ':2448,u'まで':1711,u'まま':2600,u'まる':-2155,u'やむ':-1947,u'よっ':-2565,u'れた':2369,u'れで':-913,u'をし':1860,u'を見':731,u'亡く':-1886,u'京都':2558,u'取り':-2784,u'大き':-2604,u'大阪':1497,u'平方':-2314,u'引き':-1336,u'日本':-195,u'本当':-2423,u'毎日':-2113,u'目指':-724,u'B1あ':1404,u'B1同':542,u'」と':1682} + BW2__ = {u'..':-11822,u'11':-669,u'――':-5730,u'−−':-13175,u'いう':-1609,u'うか':2490,u'かし':-1350,u'かも':-602,u'から':-7194,u'かれ':4612,u'がい':853,u'がら':-3198,u'きた':1941,u'くな':-1597,u'こと':-8392,u'この':-4193,u'させ':4533,u'され':13168,u'さん':-3977,u'しい':-1819,u'しか':-545,u'した':5078,u'して':972,u'しな':939,u'その':-3744,u'たい':-1253,u'たた':-662,u'ただ':-3857,u'たち':-786,u'たと':1224,u'たは':-939,u'った':4589,u'って':1647,u'っと':-2094,u'てい':6144,u'てき':3640,u'てく':2551,u'ては':-3110,u'ても':-3065,u'でい':2666,u'でき':-1528,u'でし':-3828,u'です':-4761,u'でも':-4203,u'とい':1890,u'とこ':-1746,u'とと':-2279,u'との':720,u'とみ':5168,u'とも':-3941,u'ない':-2488,u'なが':-1313,u'など':-6509,u'なの':2614,u'なん':3099,u'にお':-1615,u'にし':2748,u'にな':2454,u'によ':-7236,u'に対':-14943,u'に従':-4688,u'に関':-11388,u'のか':2093,u'ので':-7059,u'のに':-6041,u'のの':-6125,u'はい':1073,u'はが':-1033,u'はず':-2532,u'ばれ':1813,u'まし':-1316,u'まで':-6621,u'まれ':5409,u'めて':-3153,u'もい':2230,u'もの':-10713,u'らか':-944,u'らし':-1611,u'らに':-1897,u'りし':651,u'りま':1620,u'れた':4270,u'れて':849,u'れば':4114,u'ろう':6067,u'われ':7901,u'を通':-11877,u'んだ':728,u'んな':-4115,u'一人':602,u'一方':-1375,u'一日':970,u'一部':-1051,u'上が':-4479,u'会社':-1116,u'出て':2163,u'分の':-7758,u'同党':970,u'同日':-913,u'大阪':-2471,u'委員':-1250,u'少な':-1050,u'年度':-8669,u'年間':-1626,u'府県':-2363,u'手権':-1982,u'新聞':-4066,u'日新':-722,u'日本':-7068,u'日米':3372,u'曜日':-601,u'朝鮮':-2355,u'本人':-2697,u'東京':-1543,u'然と':-1384,u'社会':-1276,u'立て':-990,u'第に':-1612,u'米国':-4268,u'11':-669} + BW3__ = {u'あた':-2194,u'あり':719,u'ある':3846,u'い.':-1185,u'い。':-1185,u'いい':5308,u'いえ':2079,u'いく':3029,u'いた':2056,u'いっ':1883,u'いる':5600,u'いわ':1527,u'うち':1117,u'うと':4798,u'えと':1454,u'か.':2857,u'か。':2857,u'かけ':-743,u'かっ':-4098,u'かに':-669,u'から':6520,u'かり':-2670,u'が,':1816,u'が、':1816,u'がき':-4855,u'がけ':-1127,u'がっ':-913,u'がら':-4977,u'がり':-2064,u'きた':1645,u'けど':1374,u'こと':7397,u'この':1542,u'ころ':-2757,u'さい':-714,u'さを':976,u'し,':1557,u'し、':1557,u'しい':-3714,u'した':3562,u'して':1449,u'しな':2608,u'しま':1200,u'す.':-1310,u'す。':-1310,u'する':6521,u'ず,':3426,u'ず、':3426,u'ずに':841,u'そう':428,u'た.':8875,u'た。':8875,u'たい':-594,u'たの':812,u'たり':-1183,u'たる':-853,u'だ.':4098,u'だ。':4098,u'だっ':1004,u'った':-4748,u'って':300,u'てい':6240,u'てお':855,u'ても':302,u'です':1437,u'でに':-1482,u'では':2295,u'とう':-1387,u'とし':2266,u'との':541,u'とも':-3543,u'どう':4664,u'ない':1796,u'なく':-903,u'など':2135,u'に,':-1021,u'に、':-1021,u'にし':1771,u'にな':1906,u'には':2644,u'の,':-724,u'の、':-724,u'の子':-1000,u'は,':1337,u'は、':1337,u'べき':2181,u'まし':1113,u'ます':6943,u'まっ':-1549,u'まで':6154,u'まれ':-793,u'らし':1479,u'られ':6820,u'るる':3818,u'れ,':854,u'れ、':854,u'れた':1850,u'れて':1375,u'れば':-3246,u'れる':1091,u'われ':-605,u'んだ':606,u'んで':798,u'カ月':990,u'会議':860,u'入り':1232,u'大会':2217,u'始め':1681,u'市':965,u'新聞':-5055,u'日,':974,u'日、':974,u'社会':2024,u'カ月':990} + TC1__ = {u'AAA':1093,u'HHH':1029,u'HHM':580,u'HII':998,u'HOH':-390,u'HOM':-331,u'IHI':1169,u'IOH':-142,u'IOI':-1015,u'IOM':467,u'MMH':187,u'OOI':-1832} + TC2__ = {u'HHO':2088,u'HII':-1023,u'HMM':-1154,u'IHI':-1965,u'KKH':703,u'OII':-2649} + TC3__ = {u'AAA':-294,u'HHH':346,u'HHI':-341,u'HII':-1088,u'HIK':731,u'HOH':-1486,u'IHH':128,u'IHI':-3041,u'IHO':-1935,u'IIH':-825,u'IIM':-1035,u'IOI':-542,u'KHH':-1216,u'KKA':491,u'KKH':-1217,u'KOK':-1009,u'MHH':-2694,u'MHM':-457,u'MHO':123,u'MMH':-471,u'NNH':-1689,u'NNO':662,u'OHO':-3393} + TC4__ = {u'HHH':-203,u'HHI':1344,u'HHK':365,u'HHM':-122,u'HHN':182,u'HHO':669,u'HIH':804,u'HII':679,u'HOH':446,u'IHH':695,u'IHO':-2324,u'IIH':321,u'III':1497,u'IIO':656,u'IOO':54,u'KAK':4845,u'KKA':3386,u'KKK':3065,u'MHH':-405,u'MHI':201,u'MMH':-241,u'MMM':661,u'MOM':841} + TQ1__ = {u'BHHH':-227,u'BHHI':316,u'BHIH':-132,u'BIHH':60,u'BIII':1595,u'BNHH':-744,u'BOHH':225,u'BOOO':-908,u'OAKK':482,u'OHHH':281,u'OHIH':249,u'OIHI':200,u'OIIH':-68} + TQ2__ = {u'BIHH':-1401,u'BIII':-1033,u'BKAK':-543,u'BOOO':-5591} + TQ3__ = {u'BHHH':478,u'BHHM':-1073,u'BHIH':222,u'BHII':-504,u'BIIH':-116,u'BIII':-105,u'BMHI':-863,u'BMHM':-464,u'BOMH':620,u'OHHH':346,u'OHHI':1729,u'OHII':997,u'OHMH':481,u'OIHH':623,u'OIIH':1344,u'OKAK':2792,u'OKHH':587,u'OKKA':679,u'OOHH':110,u'OOII':-685} + TQ4__ = {u'BHHH':-721,u'BHHM':-3604,u'BHII':-966,u'BIIH':-607,u'BIII':-2181,u'OAAA':-2763,u'OAKK':180,u'OHHH':-294,u'OHHI':2446,u'OHHO':480,u'OHIH':-1573,u'OIHH':1935,u'OIHI':-493,u'OIIH':626,u'OIII':-4007,u'OKAK':-8156} + TW1__ = {u'につい':-4681,u'東京都':2026} + TW2__ = {u'ある程':-2049,u'いった':-1256,u'ころが':-2434,u'しょう':3873,u'その後':-4430,u'だって':-1049,u'ていた':1833,u'として':-4657,u'ともに':-4517,u'もので':1882,u'一気に':-792,u'初めて':-1512,u'同時に':-8097,u'大きな':-1255,u'対して':-2721,u'社会党':-3216} + TW3__ = {u'いただ':-1734,u'してい':1314,u'として':-4314,u'につい':-5483,u'にとっ':-5989,u'に当た':-6247,u'ので,':-727,u'ので、':-727,u'のもの':-600,u'れから':-3752,u'十二月':-2287} + TW4__ = {u'いう.':8576,u'いう。':8576,u'からな':-2348,u'してい':2958,u'たが,':1516,u'たが、':1516,u'ている':1538,u'という':1349,u'ました':5543,u'ません':1097,u'ようと':-4258,u'よると':5865} + UC1__ = {u'A':484,u'K':93,u'M':645,u'O':-505} + UC2__ = {u'A':819,u'H':1059,u'I':409,u'M':3987,u'N':5775,u'O':646} + UC3__ = {u'A':-1370,u'I':2311} + UC4__ = {u'A':-2643,u'H':1809,u'I':-1032,u'K':-3450,u'M':3565,u'N':3876,u'O':6646} + UC5__ = {u'H':313,u'I':-1238,u'K':-799,u'M':539,u'O':-831} + UC6__ = {u'H':-506,u'I':-253,u'K':87,u'M':247,u'O':-387} + UP1__ = {u'O':-214} + UP2__ = {u'B':69,u'O':935} + UP3__ = {u'B':189} + UQ1__ = {u'BH':21,u'BI':-12,u'BK':-99,u'BN':142,u'BO':-56,u'OH':-95,u'OI':477,u'OK':410,u'OO':-2422} + UQ2__ = {u'BH':216,u'BI':113,u'OK':1759} + UQ3__ = {u'BA':-479,u'BH':42,u'BI':1913,u'BK':-7198,u'BM':3160,u'BN':6427,u'BO':14761,u'OI':-827,u'ON':-3212} + UW1__ = {u',':156,u'、':156,u'「':-463,u'あ':-941,u'う':-127,u'が':-553,u'き':121,u'こ':505,u'で':-201,u'と':-547,u'ど':-123,u'に':-789,u'の':-185,u'は':-847,u'も':-466,u'や':-470,u'よ':182,u'ら':-292,u'り':208,u'れ':169,u'を':-446,u'ん':-137,u'・':-135,u'主':-402,u'京':-268,u'区':-912,u'午':871,u'国':-460,u'大':561,u'委':729,u'市':-411,u'日':-141,u'理':361,u'生':-408,u'県':-386,u'都':-718,u'「':-463,u'・':-135} + UW2__ = {u',':-829,u'、':-829,u'〇':892,u'「':-645,u'」':3145,u'あ':-538,u'い':505,u'う':134,u'お':-502,u'か':1454,u'が':-856,u'く':-412,u'こ':1141,u'さ':878,u'ざ':540,u'し':1529,u'す':-675,u'せ':300,u'そ':-1011,u'た':188,u'だ':1837,u'つ':-949,u'て':-291,u'で':-268,u'と':-981,u'ど':1273,u'な':1063,u'に':-1764,u'の':130,u'は':-409,u'ひ':-1273,u'べ':1261,u'ま':600,u'も':-1263,u'や':-402,u'よ':1639,u'り':-579,u'る':-694,u'れ':571,u'を':-2516,u'ん':2095,u'ア':-587,u'カ':306,u'キ':568,u'ッ':831,u'三':-758,u'不':-2150,u'世':-302,u'中':-968,u'主':-861,u'事':492,u'人':-123,u'会':978,u'保':362,u'入':548,u'初':-3025,u'副':-1566,u'北':-3414,u'区':-422,u'大':-1769,u'天':-865,u'太':-483,u'子':-1519,u'学':760,u'実':1023,u'小':-2009,u'市':-813,u'年':-1060,u'強':1067,u'手':-1519,u'揺':-1033,u'政':1522,u'文':-1355,u'新':-1682,u'日':-1815,u'明':-1462,u'最':-630,u'朝':-1843,u'本':-1650,u'東':-931,u'果':-665,u'次':-2378,u'民':-180,u'気':-1740,u'理':752,u'発':529,u'目':-1584,u'相':-242,u'県':-1165,u'立':-763,u'第':810,u'米':509,u'自':-1353,u'行':838,u'西':-744,u'見':-3874,u'調':1010,u'議':1198,u'込':3041,u'開':1758,u'間':-1257,u'「':-645,u'」':3145,u'ッ':831,u'ア':-587,u'カ':306,u'キ':568} + UW3__ = {u',':4889,u'1':-800,u'−':-1723,u'、':4889,u'々':-2311,u'〇':5827,u'」':2670,u'〓':-3573,u'あ':-2696,u'い':1006,u'う':2342,u'え':1983,u'お':-4864,u'か':-1163,u'が':3271,u'く':1004,u'け':388,u'げ':401,u'こ':-3552,u'ご':-3116,u'さ':-1058,u'し':-395,u'す':584,u'せ':3685,u'そ':-5228,u'た':842,u'ち':-521,u'っ':-1444,u'つ':-1081,u'て':6167,u'で':2318,u'と':1691,u'ど':-899,u'な':-2788,u'に':2745,u'の':4056,u'は':4555,u'ひ':-2171,u'ふ':-1798,u'へ':1199,u'ほ':-5516,u'ま':-4384,u'み':-120,u'め':1205,u'も':2323,u'や':-788,u'よ':-202,u'ら':727,u'り':649,u'る':5905,u'れ':2773,u'わ':-1207,u'を':6620,u'ん':-518,u'ア':551,u'グ':1319,u'ス':874,u'ッ':-1350,u'ト':521,u'ム':1109,u'ル':1591,u'ロ':2201,u'ン':278,u'・':-3794,u'一':-1619,u'下':-1759,u'世':-2087,u'両':3815,u'中':653,u'主':-758,u'予':-1193,u'二':974,u'人':2742,u'今':792,u'他':1889,u'以':-1368,u'低':811,u'何':4265,u'作':-361,u'保':-2439,u'元':4858,u'党':3593,u'全':1574,u'公':-3030,u'六':755,u'共':-1880,u'円':5807,u'再':3095,u'分':457,u'初':2475,u'別':1129,u'前':2286,u'副':4437,u'力':365,u'動':-949,u'務':-1872,u'化':1327,u'北':-1038,u'区':4646,u'千':-2309,u'午':-783,u'協':-1006,u'口':483,u'右':1233,u'各':3588,u'合':-241,u'同':3906,u'和':-837,u'員':4513,u'国':642,u'型':1389,u'場':1219,u'外':-241,u'妻':2016,u'学':-1356,u'安':-423,u'実':-1008,u'家':1078,u'小':-513,u'少':-3102,u'州':1155,u'市':3197,u'平':-1804,u'年':2416,u'広':-1030,u'府':1605,u'度':1452,u'建':-2352,u'当':-3885,u'得':1905,u'思':-1291,u'性':1822,u'戸':-488,u'指':-3973,u'政':-2013,u'教':-1479,u'数':3222,u'文':-1489,u'新':1764,u'日':2099,u'旧':5792,u'昨':-661,u'時':-1248,u'曜':-951,u'最':-937,u'月':4125,u'期':360,u'李':3094,u'村':364,u'東':-805,u'核':5156,u'森':2438,u'業':484,u'氏':2613,u'民':-1694,u'決':-1073,u'法':1868,u'海':-495,u'無':979,u'物':461,u'特':-3850,u'生':-273,u'用':914,u'町':1215,u'的':7313,u'直':-1835,u'省':792,u'県':6293,u'知':-1528,u'私':4231,u'税':401,u'立':-960,u'第':1201,u'米':7767,u'系':3066,u'約':3663,u'級':1384,u'統':-4229,u'総':1163,u'線':1255,u'者':6457,u'能':725,u'自':-2869,u'英':785,u'見':1044,u'調':-562,u'財':-733,u'費':1777,u'車':1835,u'軍':1375,u'込':-1504,u'通':-1136,u'選':-681,u'郎':1026,u'郡':4404,u'部':1200,u'金':2163,u'長':421,u'開':-1432,u'間':1302,u'関':-1282,u'雨':2009,u'電':-1045,u'非':2066,u'駅':1620,u'1':-800,u'」':2670,u'・':-3794,u'ッ':-1350,u'ア':551,u'グ':1319,u'ス':874,u'ト':521,u'ム':1109,u'ル':1591,u'ロ':2201,u'ン':278} + UW4__ = {u',':3930,u'.':3508,u'―':-4841,u'、':3930,u'。':3508,u'〇':4999,u'「':1895,u'」':3798,u'〓':-5156,u'あ':4752,u'い':-3435,u'う':-640,u'え':-2514,u'お':2405,u'か':530,u'が':6006,u'き':-4482,u'ぎ':-3821,u'く':-3788,u'け':-4376,u'げ':-4734,u'こ':2255,u'ご':1979,u'さ':2864,u'し':-843,u'じ':-2506,u'す':-731,u'ず':1251,u'せ':181,u'そ':4091,u'た':5034,u'だ':5408,u'ち':-3654,u'っ':-5882,u'つ':-1659,u'て':3994,u'で':7410,u'と':4547,u'な':5433,u'に':6499,u'ぬ':1853,u'ね':1413,u'の':7396,u'は':8578,u'ば':1940,u'ひ':4249,u'び':-4134,u'ふ':1345,u'へ':6665,u'べ':-744,u'ほ':1464,u'ま':1051,u'み':-2082,u'む':-882,u'め':-5046,u'も':4169,u'ゃ':-2666,u'や':2795,u'ょ':-1544,u'よ':3351,u'ら':-2922,u'り':-9726,u'る':-14896,u'れ':-2613,u'ろ':-4570,u'わ':-1783,u'を':13150,u'ん':-2352,u'カ':2145,u'コ':1789,u'セ':1287,u'ッ':-724,u'ト':-403,u'メ':-1635,u'ラ':-881,u'リ':-541,u'ル':-856,u'ン':-3637,u'・':-4371,u'ー':-11870,u'一':-2069,u'中':2210,u'予':782,u'事':-190,u'井':-1768,u'人':1036,u'以':544,u'会':950,u'体':-1286,u'作':530,u'側':4292,u'先':601,u'党':-2006,u'共':-1212,u'内':584,u'円':788,u'初':1347,u'前':1623,u'副':3879,u'力':-302,u'動':-740,u'務':-2715,u'化':776,u'区':4517,u'協':1013,u'参':1555,u'合':-1834,u'和':-681,u'員':-910,u'器':-851,u'回':1500,u'国':-619,u'園':-1200,u'地':866,u'場':-1410,u'塁':-2094,u'士':-1413,u'多':1067,u'大':571,u'子':-4802,u'学':-1397,u'定':-1057,u'寺':-809,u'小':1910,u'屋':-1328,u'山':-1500,u'島':-2056,u'川':-2667,u'市':2771,u'年':374,u'庁':-4556,u'後':456,u'性':553,u'感':916,u'所':-1566,u'支':856,u'改':787,u'政':2182,u'教':704,u'文':522,u'方':-856,u'日':1798,u'時':1829,u'最':845,u'月':-9066,u'木':-485,u'来':-442,u'校':-360,u'業':-1043,u'氏':5388,u'民':-2716,u'気':-910,u'沢':-939,u'済':-543,u'物':-735,u'率':672,u'球':-1267,u'生':-1286,u'産':-1101,u'田':-2900,u'町':1826,u'的':2586,u'目':922,u'省':-3485,u'県':2997,u'空':-867,u'立':-2112,u'第':788,u'米':2937,u'系':786,u'約':2171,u'経':1146,u'統':-1169,u'総':940,u'線':-994,u'署':749,u'者':2145,u'能':-730,u'般':-852,u'行':-792,u'規':792,u'警':-1184,u'議':-244,u'谷':-1000,u'賞':730,u'車':-1481,u'軍':1158,u'輪':-1433,u'込':-3370,u'近':929,u'道':-1291,u'選':2596,u'郎':-4866,u'都':1192,u'野':-1100,u'銀':-2213,u'長':357,u'間':-2344,u'院':-2297,u'際':-2604,u'電':-878,u'領':-1659,u'題':-792,u'館':-1984,u'首':1749,u'高':2120,u'「':1895,u'」':3798,u'・':-4371,u'ッ':-724,u'ー':-11870,u'カ':2145,u'コ':1789,u'セ':1287,u'ト':-403,u'メ':-1635,u'ラ':-881,u'リ':-541,u'ル':-856,u'ン':-3637} + UW5__ = {u',':465,u'.':-299,u'1':-514,u'E2':-32768,u']':-2762,u'、':465,u'。':-299,u'「':363,u'あ':1655,u'い':331,u'う':-503,u'え':1199,u'お':527,u'か':647,u'が':-421,u'き':1624,u'ぎ':1971,u'く':312,u'げ':-983,u'さ':-1537,u'し':-1371,u'す':-852,u'だ':-1186,u'ち':1093,u'っ':52,u'つ':921,u'て':-18,u'で':-850,u'と':-127,u'ど':1682,u'な':-787,u'に':-1224,u'の':-635,u'は':-578,u'べ':1001,u'み':502,u'め':865,u'ゃ':3350,u'ょ':854,u'り':-208,u'る':429,u'れ':504,u'わ':419,u'を':-1264,u'ん':327,u'イ':241,u'ル':451,u'ン':-343,u'中':-871,u'京':722,u'会':-1153,u'党':-654,u'務':3519,u'区':-901,u'告':848,u'員':2104,u'大':-1296,u'学':-548,u'定':1785,u'嵐':-1304,u'市':-2991,u'席':921,u'年':1763,u'思':872,u'所':-814,u'挙':1618,u'新':-1682,u'日':218,u'月':-4353,u'査':932,u'格':1356,u'機':-1508,u'氏':-1347,u'田':240,u'町':-3912,u'的':-3149,u'相':1319,u'省':-1052,u'県':-4003,u'研':-997,u'社':-278,u'空':-813,u'統':1955,u'者':-2233,u'表':663,u'語':-1073,u'議':1219,u'選':-1018,u'郎':-368,u'長':786,u'間':1191,u'題':2368,u'館':-689,u'1':-514,u'E2':-32768,u'「':363,u'イ':241,u'ル':451,u'ン':-343} + UW6__ = {u',':227,u'.':808,u'1':-270,u'E1':306,u'、':227,u'。':808,u'あ':-307,u'う':189,u'か':241,u'が':-73,u'く':-121,u'こ':-200,u'じ':1782,u'す':383,u'た':-428,u'っ':573,u'て':-1014,u'で':101,u'と':-105,u'な':-253,u'に':-149,u'の':-417,u'は':-236,u'も':-206,u'り':187,u'る':-135,u'を':195,u'ル':-673,u'ン':-496,u'一':-277,u'中':201,u'件':-800,u'会':624,u'前':302,u'区':1792,u'員':-1212,u'委':798,u'学':-960,u'市':887,u'広':-695,u'後':535,u'業':-697,u'相':753,u'社':-507,u'福':974,u'空':-822,u'者':1811,u'連':463,u'郎':1082,u'1':-270,u'E1':306,u'ル':-673,u'ン':-496} + + # ctype_ + def ctype_(self, char): + for pattern, value in self.patterns_.iteritems(): + if pattern.match(char): + return value + return u'O' + # ts_ + def ts_(self, dict, key): + if key in dict: + return dict[key] + return 0 + + # segment + def split(self, input): + if not input: + return [] + + result = [] + seg = [u'B3',u'B2',u'B1'] + ctype = [u'O',u'O',u'O'] + for t in input: + seg.append(t) + ctype.append(self.ctype_(t)) + seg.append(u'E1') + seg.append(u'E2') + seg.append(u'E3') + ctype.append(u'O') + ctype.append(u'O') + ctype.append(u'O') + word = seg[3] + p1 = u'U' + p2 = u'U' + p3 = u'U' + + for i in range(4, len(seg) - 3): + score = self.BIAS__ + w1 = seg[i-3] + w2 = seg[i-2] + w3 = seg[i-1] + w4 = seg[i] + w5 = seg[i+1] + w6 = seg[i+2] + c1 = ctype[i-3] + c2 = ctype[i-2] + c3 = ctype[i-1] + c4 = ctype[i] + c5 = ctype[i+1] + c6 = ctype[i+2] + score += self.ts_(self.UP1__, p1) + score += self.ts_(self.UP2__, p2) + score += self.ts_(self.UP3__, p3) + score += self.ts_(self.BP1__, p1 + p2) + score += self.ts_(self.BP2__, p2 + p3) + score += self.ts_(self.UW1__, w1) + score += self.ts_(self.UW2__, w2) + score += self.ts_(self.UW3__, w3) + score += self.ts_(self.UW4__, w4) + score += self.ts_(self.UW5__, w5) + score += self.ts_(self.UW6__, w6) + score += self.ts_(self.BW1__, w2 + w3) + score += self.ts_(self.BW2__, w3 + w4) + score += self.ts_(self.BW3__, w4 + w5) + score += self.ts_(self.TW1__, w1 + w2 + w3) + score += self.ts_(self.TW2__, w2 + w3 + w4) + score += self.ts_(self.TW3__, w3 + w4 + w5) + score += self.ts_(self.TW4__, w4 + w5 + w6) + score += self.ts_(self.UC1__, c1) + score += self.ts_(self.UC2__, c2) + score += self.ts_(self.UC3__, c3) + score += self.ts_(self.UC4__, c4) + score += self.ts_(self.UC5__, c5) + score += self.ts_(self.UC6__, c6) + score += self.ts_(self.BC1__, c2 + c3) + score += self.ts_(self.BC2__, c3 + c4) + score += self.ts_(self.BC3__, c4 + c5) + score += self.ts_(self.TC1__, c1 + c2 + c3) + score += self.ts_(self.TC2__, c2 + c3 + c4) + score += self.ts_(self.TC3__, c3 + c4 + c5) + score += self.ts_(self.TC4__, c4 + c5 + c6) +# score += self.ts_(self.TC5__, c4 + c5 + c6) + score += self.ts_(self.UQ1__, p1 + c1) + score += self.ts_(self.UQ2__, p2 + c2) + score += self.ts_(self.UQ1__, p3 + c3) + score += self.ts_(self.BQ1__, p2 + c2 + c3) + score += self.ts_(self.BQ2__, p2 + c3 + c4) + score += self.ts_(self.BQ3__, p3 + c2 + c3) + score += self.ts_(self.BQ4__, p3 + c3 + c4) + score += self.ts_(self.TQ1__, p2 + c1 + c2 + c3) + score += self.ts_(self.TQ2__, p2 + c2 + c3 + c4) + score += self.ts_(self.TQ3__, p3 + c1 + c2 + c3) + score += self.ts_(self.TQ4__, p3 + c2 + c3 + c4) + p = u'O' + if score > 0: + result.append(word) + word = u'' + p = u'B' + p1 = p2 + p2 = p3 + p3 = p + word += seg[i] + + result.append(word) + return result + + +class SearchJapanese(SearchLanguage): + """ + Japanese search implementation: uses no stemmer, but word splitting is quite + complicated. + """ + lang = 'ja' + + def init(self, options): + type = options.get('type', 'default') + if type not in ('mecab', 'default'): + raise ValueError(("Japanese tokenizer's type should be 'mecab'" + " or 'default'")) + self.libmecab = None + if type == 'mecab': + self.splitter = MecabBinder(options) + else: + self.splitter = TinySegmenter() + + def split(self, input): + return self.splitter.split(input) + + def word_filter(self, stemmed_word): + return len(stemmed_word) > 1 diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js_t similarity index 68% rename from sphinx/themes/basic/static/searchtools.js rename to sphinx/themes/basic/static/searchtools.js_t index dae92b5e5..6be7489fb 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js_t @@ -1,6 +1,6 @@ /* - * searchtools.js - * ~~~~~~~~~~~~~~ + * searchtools.js_t + * ~~~~~~~~~~~~~~~~ * * Sphinx JavaScript utilties for the full-text search. * @@ -36,188 +36,7 @@ jQuery.makeSearchSummary = function(text, keywords, hlwords) { return rv; } -/** - * Porter Stemmer - */ -var PorterStemmer = function() { - - var step2list = { - ational: 'ate', - tional: 'tion', - enci: 'ence', - anci: 'ance', - izer: 'ize', - bli: 'ble', - alli: 'al', - entli: 'ent', - eli: 'e', - ousli: 'ous', - ization: 'ize', - ation: 'ate', - ator: 'ate', - alism: 'al', - iveness: 'ive', - fulness: 'ful', - ousness: 'ous', - aliti: 'al', - iviti: 'ive', - biliti: 'ble', - logi: 'log' - }; - - var step3list = { - icate: 'ic', - ative: '', - alize: 'al', - iciti: 'ic', - ical: 'ic', - ful: '', - ness: '' - }; - - var c = "[^aeiou]"; // consonant - var v = "[aeiouy]"; // vowel - var C = c + "[^aeiouy]*"; // consonant sequence - var V = v + "[aeiou]*"; // vowel sequence - - var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - +{{ search_language_stemming_code|safe }} /** * Search Module @@ -300,14 +119,10 @@ var Search = { }, query : function(query) { - var stopwords = ['and', 'then', 'into', 'it', 'as', 'are', 'in', - 'if', 'for', 'no', 'there', 'their', 'was', 'is', - 'be', 'to', 'that', 'but', 'they', 'not', 'such', - 'with', 'by', 'a', 'on', 'these', 'of', 'will', - 'this', 'near', 'the', 'or', 'at']; + var stopwords = {{ search_language_stop_words }}; - // stem the searchterms and add them to the correct list - var stemmer = new PorterStemmer(); + // Stem the searchterms and add them to the correct list + var stemmer = new Stemmer(); var searchterms = []; var excluded = []; var hlterms = []; From cfe85b4e8ba0f79ca028798af21feec9b772c9e5 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 4 Jan 2011 12:47:49 +0100 Subject: [PATCH 481/744] Determine search language in builder, and add missing method in websupport indexer implementations. --- sphinx/builders/html.py | 13 +++++++++---- sphinx/search/__init__.py | 9 +++------ sphinx/websupport/search/__init__.py | 5 +++++ tests/test_search.py | 2 +- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index ace4dd015..6aa4fda4d 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -232,10 +232,15 @@ class StandaloneHTMLBuilder(Builder): return pub.writer.parts def prepare_writing(self, docnames): - from sphinx.search import IndexBuilder - - self.indexer = IndexBuilder(self.env) + # create the search indexer + from sphinx.search import IndexBuilder, languages + lang = self.config.html_search_language or self.config.language + if not lang or lang not in languages: + lang = 'en' + self.indexer = IndexBuilder(self.env, lang, + self.config.html_search_options) self.load_indexer(docnames) + self.docwriter = HTMLWriter(self) self.docsettings = OptionParser( defaults=self.env.settings, @@ -542,7 +547,7 @@ class StandaloneHTMLBuilder(Builder): # add context items for search function used in searchtools.js_t ctx = self.globalcontext.copy() - ctx.update(self.indexer.globalcontext_for_searchtool()) + ctx.update(self.indexer.context_for_searchtool()) # then, copy over theme-supplied static files if self.theme: diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index ff3124542..645ebfc81 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -156,7 +156,7 @@ class IndexBuilder(object): 'pickle': pickle } - def __init__(self, env): + def __init__(self, env, lang, options): self.env = env # filename -> title self._titles = {} @@ -167,10 +167,7 @@ class IndexBuilder(object): # objtype index -> objname (localized) self._objnames = {} # add language-specific SearchLanguage instance - search_language = env.config.html_search_language or env.config.language - if not search_language or search_language not in languages: - search_language = 'en' - self.lang = languages[search_language](env.config.html_search_options) + self.lang = languages[lang](options) def load(self, stream, format): """Reconstruct from frozen data.""" @@ -277,7 +274,7 @@ class IndexBuilder(object): for word in visitor.found_words: add_term(word) - def globalcontext_for_searchtool(self): + def context_for_searchtool(self): return dict( search_language_stemming_code = self.lang.js_stemmer_code, search_language_stop_words = jsdump.dumps(self.lang.stopwords), diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index 58773fa01..9410973ca 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -113,6 +113,11 @@ class BaseSearch(object): except TypeError: return context + def context_for_searchtool(self): + """Required by the HTML builder.""" + return {} + + # The built-in search adapters. SEARCH_ADAPTERS = { 'xapian': ('xapiansearch', 'XapianSearch'), diff --git a/tests/test_search.py b/tests/test_search.py index 08ba40e57..d9dcfb81b 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -36,7 +36,7 @@ def test_wordcollector(): doc['file'] = 'dummy' parser.parse(FILE_CONTENTS, doc) - ix = IndexBuilder(None) + ix = IndexBuilder(None, 'en', {}) ix.feed('filename', 'title', doc) assert 'boson' not in ix._mapping assert 'fermion' in ix._mapping From 442229dd97160535e388473760002c1f1c495bf8 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 4 Jan 2011 17:49:59 +0100 Subject: [PATCH 482/744] Add ``pyramid`` theme. --- CHANGES | 2 + doc/themes/fullsize/pyramid.png | Bin 0 -> 131965 bytes doc/themes/pyramid.png | Bin 0 -> 49630 bytes doc/theming.rst | 8 +- sphinx/highlighting.py | 22 +- sphinx/pygments_styles.py | 96 ++++++ sphinx/themes/pyramid/layout.html | 24 ++ sphinx/themes/pyramid/static/dialog-note.png | Bin 0 -> 1582 bytes .../themes/pyramid/static/dialog-seealso.png | Bin 0 -> 1502 bytes sphinx/themes/pyramid/static/dialog-topic.png | Bin 0 -> 1910 bytes .../themes/pyramid/static/dialog-warning.png | Bin 0 -> 1391 bytes sphinx/themes/pyramid/static/epub.css | 310 +++++++++++++++++ sphinx/themes/pyramid/static/footerbg.png | Bin 0 -> 333 bytes sphinx/themes/pyramid/static/headerbg.png | Bin 0 -> 203 bytes sphinx/themes/pyramid/static/ie6.css | 7 + sphinx/themes/pyramid/static/middlebg.png | Bin 0 -> 2797 bytes sphinx/themes/pyramid/static/pyramid.css_t | 323 ++++++++++++++++++ sphinx/themes/pyramid/static/transparent.gif | Bin 0 -> 49 bytes sphinx/themes/pyramid/theme.conf | 4 + 19 files changed, 773 insertions(+), 23 deletions(-) create mode 100644 doc/themes/fullsize/pyramid.png create mode 100644 doc/themes/pyramid.png create mode 100644 sphinx/pygments_styles.py create mode 100644 sphinx/themes/pyramid/layout.html create mode 100644 sphinx/themes/pyramid/static/dialog-note.png create mode 100644 sphinx/themes/pyramid/static/dialog-seealso.png create mode 100644 sphinx/themes/pyramid/static/dialog-topic.png create mode 100644 sphinx/themes/pyramid/static/dialog-warning.png create mode 100644 sphinx/themes/pyramid/static/epub.css create mode 100644 sphinx/themes/pyramid/static/footerbg.png create mode 100644 sphinx/themes/pyramid/static/headerbg.png create mode 100644 sphinx/themes/pyramid/static/ie6.css create mode 100644 sphinx/themes/pyramid/static/middlebg.png create mode 100644 sphinx/themes/pyramid/static/pyramid.css_t create mode 100644 sphinx/themes/pyramid/static/transparent.gif create mode 100644 sphinx/themes/pyramid/theme.conf diff --git a/CHANGES b/CHANGES index b03f1df54..d38e53086 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,8 @@ Release 1.1 (in development) * #443: Allow referencing external graphviz files. +* Add ``pyramid`` theme. + * #472: linkcheck builder: Check links in parallel, use HTTP HEAD requests and allow configuring the timeout. New config values: :confval:`linkcheck_timeout` and :confval:`linkcheck_workers`. diff --git a/doc/themes/fullsize/pyramid.png b/doc/themes/fullsize/pyramid.png new file mode 100644 index 0000000000000000000000000000000000000000..429a8b7e0b8b6770ade5263c654871e7833ceff3 GIT binary patch literal 131965 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 Smpvo^fS2;BawX4A0{;gB>mL{Z literal 0 HcmV?d00001 diff --git a/doc/themes/pyramid.png b/doc/themes/pyramid.png new file mode 100644 index 0000000000000000000000000000000000000000..b16095c99e19f861778dffba4e9fc59b7fd5c2a4 GIT binary patch literal 49630 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ⅈ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�TNnONWcK{6 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. The following options are supported: diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index aa093d80e..e84238e8a 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -30,34 +30,14 @@ try: from pygments.lexers import get_lexer_by_name, guess_lexer from pygments.formatters import HtmlFormatter, LatexFormatter from pygments.filters import ErrorToken - from pygments.style import Style from pygments.styles import get_style_by_name - from pygments.styles.friendly import FriendlyStyle - from pygments.token import Generic, Comment, Number from pygments.util import ClassNotFound + from sphinx.pygments_styles import SphinxStyle, NoneStyle except ImportError: pygments = None lexers = None HtmlFormatter = LatexFormatter = None else: - class SphinxStyle(Style): - """ - Like friendly, but a bit darker to enhance contrast on the green - background. - """ - - background_color = '#eeffcc' - default_style = '' - - styles = FriendlyStyle.styles - styles.update({ - Generic.Output: '#333', - Comment: 'italic #408090', - Number: '#208050', - }) - - class NoneStyle(Style): - """Style without any styling.""" lexers = dict( none = TextLexer(), diff --git a/sphinx/pygments_styles.py b/sphinx/pygments_styles.py new file mode 100644 index 000000000..44740b31e --- /dev/null +++ b/sphinx/pygments_styles.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +""" + sphinx.pygments_styles + ~~~~~~~~~~~~~~~~~~~~~~ + + Sphinx theme specific highlighting styles. + + :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.style import Style +from pygments.styles.friendly import FriendlyStyle +from pygments.token import Generic, Comment, Number, Whitespace, Keyword, \ + Operator, Name, String, Error + + +class NoneStyle(Style): + """Style without any styling.""" + + +class SphinxStyle(Style): + """ + Like friendly, but a bit darker to enhance contrast on the green + background. + """ + + background_color = '#eeffcc' + default_style = '' + + styles = FriendlyStyle.styles + styles.update({ + Generic.Output: '#333', + Comment: 'italic #408090', + Number: '#208050', + }) + + +class PyramidStyle(Style): + """ + Pylons/pyramid pygments style based on friendly style, by Blaise Laflamme. + """ + + # work in progress... + + background_color = "#f8f8f8" + default_style = "" + + styles = { + Whitespace: "#bbbbbb", + Comment: "italic #60a0b0", + Comment.Preproc: "noitalic #007020", + Comment.Special: "noitalic bg:#fff0f0", + + Keyword: "bold #007020", + Keyword.Pseudo: "nobold", + Keyword.Type: "nobold #902000", + + Operator: "#666666", + Operator.Word: "bold #007020", + + Name.Builtin: "#007020", + Name.Function: "#06287e", + Name.Class: "bold #0e84b5", + Name.Namespace: "bold #0e84b5", + Name.Exception: "#007020", + Name.Variable: "#bb60d5", + Name.Constant: "#60add5", + Name.Label: "bold #002070", + Name.Entity: "bold #d55537", + Name.Attribute: "#0e84b5", + Name.Tag: "bold #062873", + Name.Decorator: "bold #555555", + + String: "#4070a0", + String.Doc: "italic", + String.Interpol: "italic #70a0d0", + String.Escape: "bold #4070a0", + String.Regex: "#235388", + String.Symbol: "#517918", + String.Other: "#c65d09", + Number: "#40a070", + + Generic.Heading: "bold #000080", + Generic.Subheading: "bold #800080", + Generic.Deleted: "#A00000", + Generic.Inserted: "#00A000", + Generic.Error: "#FF0000", + Generic.Emph: "italic", + Generic.Strong: "bold", + Generic.Prompt: "bold #c65d09", + Generic.Output: "#888", + Generic.Traceback: "#04D", + + Error: "#a40000 bg:#fbe3e4" + } diff --git a/sphinx/themes/pyramid/layout.html b/sphinx/themes/pyramid/layout.html new file mode 100644 index 000000000..1887361ee --- /dev/null +++ b/sphinx/themes/pyramid/layout.html @@ -0,0 +1,24 @@ +{% extends "basic/layout.html" %} + +{%- block extrahead %} + + + +{% endblock %} + +{% block header %} +{%- if logo %} +
                  + +
                  +{%- endif %} +{% endblock %} + +{%- block sidebarlogo %}{%- endblock %} +{%- block sidebarsourcelink %}{%- endblock %} diff --git a/sphinx/themes/pyramid/static/dialog-note.png b/sphinx/themes/pyramid/static/dialog-note.png new file mode 100644 index 0000000000000000000000000000000000000000..263fbd5866aef1ce3645d768fbf55def9a091905 GIT binary patch literal 1582 zcmV+}2GRM6P)k@WH7pQF=3=faW zkFSqR=8AIgXBSA;Hc`EO&K^N)42Y`_9UkcY+JOPZzW)_SXY=A^lj%j;c6NwBgJ_tV zm_2r=oma1nQ4OegJG6y4*V7pt+<)-viajx2cgoi@dv1pQz5%6oPs>nSm~*w?TqQVv zob8dII{c&Oe%S1c$Jb4}_O0hohn`**8Ge(}Coiu}Aq2|y9sKoAe_L3{Is>OKFbSRv z1pSn5A0ELYKmLQ6BwuOzH&f_kC zpw7uXeVu-psxJ`U5$EUY>gJ?Py7zS&L7kH>03>~lEiK4GzOF#Bu=x@I$Wn<&a}!AV z0JO@7l0!?lsTn6_uHD-u=x*nv?jh4tOM8aU5Jm$S4XgGI1^n1jw1Qj)a0G^`sW^46 z`zk{v@<5WOf#ro&h@# znjak7C0?~s9=SM_n9NYjv(wK7=2M5YtS{PKY$ zvq{93_SMB}%_$-0$B^l1M4*|BZ#$YIXIuaXZY|uu(-Vrempj=|${=$lGCK?Q%G0#? z`m+#hhFpCs04oQ|EN0RVf?FVz z;UBke#-&y%!v?064Rok zOzr0t(XWi*Ji3QBXXf%JRoe>fIM6Q~a~|x{=CxG~y=Q>o+w;yR!_$*li|?MfjH_0w z4(~z?Yj=n@ipBh~`I%&-^WdPS`V7dV>N~21K#jH|clB`PgWJ~4*uv-kzRU4neaIZ( zs_AL~J|J*?3i;mzZ}f)kj*(jv-9BB_qC@+I5-iVl$9ByRp&N*X5L#OY%C2rk literal 0 HcmV?d00001 diff --git a/sphinx/themes/pyramid/static/dialog-seealso.png b/sphinx/themes/pyramid/static/dialog-seealso.png new file mode 100644 index 0000000000000000000000000000000000000000..3eb7b05c84809454c095bcb61b27d83585540bb2 GIT binary patch literal 1502 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 literal 0 HcmV?d00001 diff --git a/sphinx/themes/pyramid/static/dialog-warning.png b/sphinx/themes/pyramid/static/dialog-warning.png new file mode 100644 index 0000000000000000000000000000000000000000..7233d45d8e6e41ef8fcb318c76303a9b6f23997e GIT binary patch literal 1391 zcmV-#1(5oQP)}piCG~<@Tj_&{ee%V|SZW{gB8?y>5~HGEkRU`Q5xgR16NK8$#%7av&gnyv zB_>9ji%*39&LbK3qFu zI7(%ZZ%;}2Gqh=wf?-g1;X+qD8r}8{{PEcV%0X!tqH%6yq(DC>&`hf#DjvXW3(1702fiEY= zF5vsm0&oGwD!P6+uzb1JdigTStXXLJ`Pjl?)VXtsUA;>7;>9+ps^67D`lthR!lCrM zJiGbuAzhIO`u_b0*{$vea+<+#n+u)TG0)=^W&Y8i0fonJZU{&O3K7Vlni6`w*i02KV;P{rfRu zG0etBGUm-QC7c=+fU_{mF@F3e|CA|e+x6?%3JOrCPscC}T3cI*Mx&UfiIS0lW6Kua z*4E+=1XR`QT~P`vMhu|BsqFlGD_&EBWm!0O?*~ty__PKSBsxE>~io%jH4{flNukRa8X# z{rjXQCrg{pH?0i59u|NFLJ0B8)HQ2(TUCW{yU};B)sYgKfwF=i#fQ*|T%prYYO&>#^_Jg-l9HIC3}~D2jrjDCoMLxDnFn#I<)X9Z#O% zl~TASP4c~kT|)vm3aV6;lT*TB*>>d$((gwvEKE2_WWLjh)9HL~UI>A6!v?geQ)#cO zBP%CIQ>6UickujYfDG8-Td-iF{q<|n@$@N&XN+4a)1MXii!#xt%)zy89UYGz zG12e0NnI~2gPDT@C~$aEUY=^+y^GP-hO?w((CCB^XqtxXDMlUN#ETcC z1p+4hp6CQXDP-AvzM{13Y_09qE$nO8qUGca8odyL^73*nUc5M{b0Gw_%uGDnx6}FT z856u-Rdc(;WsuwJSM3x1@yun*tghR)G2?Oc_3NRv6|2V>LcBLlv~F)$efs`;+#5F% zJ#z;0`E&f~>DJ4JhLgbjz5vpKd|!698oPE4fasPj{qtqSV!U}nQhK`b970DyKML_) z0Ovbsj@-OC=8MgnrPMUAEG)wS)5J22?y+HDnPzw2Z#-bF|D0)J#^W@
                ') + raise nodes.SkipNode + + parts = [prt for prt in node['latex'].split('\n\n') if prt.strip()] + for i, part in enumerate(parts): + part = self.encode(part) + if i == 0: + # necessary to e.g. set the id property correctly + if node['number']: + self.body.append('(%s)' % + node['number']) + if '&' in part or '\\\\' in part: + self.body.append(self.builder.config.mathjax_display[0] + + '\\begin{split}' + part + '\\end{split}' + + self.builder.config.mathjax_display[1]) + else: + self.body.append(self.builder.config.mathjax_display[0] + part + + self.builder.config.mathjax_display[1]) + self.body.append('
                \n') + raise nodes.SkipNode + +def builder_inited(app): + if not app.config.mathjax_path: + raise ExtensionError('mathjax_path config value must be set for the ' + 'mathjax extension to work') + app.add_javascript(app.config.mathjax_path) + + +def setup(app): + mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None)) + app.add_config_value('mathjax_path', '', False) + app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html') + app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html') + app.connect('builder-inited', builder_inited) + From 772c94ef69c3028b61a3a2d56665053be9a53526 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Jan 2011 18:20:57 +0100 Subject: [PATCH 491/744] Remove latex-unsupported footnote in caption. --- doc/intl.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/intl.rst b/doc/intl.rst index 89cfcd2c8..3a9e32f29 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -12,9 +12,10 @@ in itself. See the :ref:`intl-options` for details on configuration. .. figure:: translation.png :width: 100% - Workflow visualization of translations in Sphinx. [1]_ + Workflow visualization of translations in Sphinx. (The stick-figure is taken + from an `XKCD comic `_.) -**gettext** [2]_ is an established standard for internationalization and +**gettext** [1]_ is an established standard for internationalization and localization. It naïvely maps messages in a program to a translated string. Sphinx uses these facilities to translate whole documents. @@ -46,7 +47,7 @@ up automatically. An example: you have a document ``usage.rst`` in your Sphinx project. The gettext builder will put its messages into ``usage.pot``. Image you have -Spanish translations [3]_ on your hands in ``usage.po`` --- for your builds to +Spanish translations [2]_ on your hands in ``usage.po`` --- for your builds to be translated you need to follow these instructions: * Compile your message catalog to a locale directory, say ``translated``, so it @@ -62,8 +63,7 @@ be translated you need to follow these instructions: .. rubric:: Footnotes -.. [1] The stick-figure is taken from an `XKCD comic `_. -.. [2] See the `GNU gettext utilites +.. [1] See the `GNU gettext utilites `_ for details on that software suite. -.. [3] Because nobody expects the Spanish Inquisition! +.. [2] Because nobody expects the Spanish Inquisition! From 549c800f0bd8f5fff331db1319c496fb9f487f85 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Jan 2011 18:21:24 +0100 Subject: [PATCH 492/744] Remove control character. --- doc/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/faq.rst b/doc/faq.rst index c92067f0c..a0f008559 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -212,7 +212,7 @@ Info files, try adding the following Emacs Lisp code to your start-up file, (widen) (goto-char (point-min)) (when (re-search-forward "^Generated by \\(Sphinx\\|Docutils\\)" - (save-excursion (search-forward "" nil t)) t) + (save-excursion (search-forward "^_" nil t)) t) (set (make-local-variable 'Info-hide-note-references) 'hide))))) From 92a5db16d5fd694e7027c3a4d5f6e5b6e552bd1c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Jan 2011 18:29:37 +0100 Subject: [PATCH 493/744] #516: Added new value of the :confval:`latex_show_urls` option to show the URLs in footnotes. --- CHANGES | 3 +++ doc/conf.py | 1 + doc/config.rst | 12 ++++++++++-- sphinx/config.py | 2 +- sphinx/roles.py | 1 + sphinx/writers/latex.py | 24 ++++++++++++++++++++---- 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 04b43f679..6835f35dd 100644 --- a/CHANGES +++ b/CHANGES @@ -46,6 +46,9 @@ Release 1.1 (in development) * #521: Added :confval:`linkcheck_ignore` config value. +* #516: Added new value of the :confval:`latex_show_urls` option to + show the URLs in footnotes. + * #526: Added Iranian translation. * #559: :confval:`html_add_permalinks` is now a string giving the diff --git a/doc/conf.py b/doc/conf.py index 6322f794e..170e1bc26 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -44,6 +44,7 @@ latex_logo = '_static/sphinx.png' latex_elements = { 'fontpkg': '\\usepackage{palatino}', } +latex_show_urls = 'footnote' autodoc_member_order = 'groupwise' todo_include_todos = True diff --git a/doc/config.rst b/doc/config.rst index ad711ee11..a1f550ddf 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -933,10 +933,18 @@ These options influence LaTeX output. .. confval:: latex_show_urls - If true, add URL addresses after links. This is very useful for printed - copies of the manual. Default is ``False``. + Control whether to display URL addresses. This is very useful for printed + copies of the manual. The setting can have the following values: + + * ``'no'`` -- do not display URLs (default) + * ``'footnote'`` -- display URLs in footnotes + * ``'inline'`` -- display URLs inline in parentheses .. versionadded:: 1.0 + .. versionchanged:: 1.1 + This value is now a string; previously it was a boolean value, and a true + value selected the ``'inline'`` display. For backwards compatibility, + ``True`` is still accepted. .. confval:: latex_elements diff --git a/sphinx/config.py b/sphinx/config.py index 3c46b0ef2..90c4b5627 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -143,7 +143,7 @@ class Config(object): latex_use_parts = (False, None), latex_use_modindex = (True, None), # deprecated latex_domain_indices = (True, None), - latex_show_urls = (False, None), + latex_show_urls = ('no', None), latex_show_pagerefs = (False, None), # paper_size and font_size are still separate values # so that you can give them easily on the command line diff --git a/sphinx/roles.py b/sphinx/roles.py index 6b22d20ac..a960f00a7 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -285,6 +285,7 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): entries = [('single', target, targetid, target)] indexnode = addnodes.index() indexnode['entries'] = entries + indexnode['inline'] = True textnode = nodes.Text(title, title) return [indexnode, targetnode, textnode], [] diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 98dc0ee86..8150b6b97 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -242,6 +242,8 @@ class LaTeXTranslator(nodes.NodeVisitor): self.verbatim = None self.in_title = 0 self.in_production_list = 0 + self.in_footnote = 0 + self.in_caption = 0 self.first_document = 1 self.this_is_the_title = 1 self.literal_whitespace = 0 @@ -594,9 +596,11 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_collected_footnote(self, node): + self.in_footnote += 1 self.body.append('\\footnote{') def depart_collected_footnote(self, node): self.body.append('}') + self.in_footnote -= 1 def visit_label(self, node): if isinstance(node.parent, nodes.citation): @@ -815,7 +819,7 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_paragraph(self, node): self.body.append('\n') def depart_paragraph(self, node): - self.body.append('\n\n') + self.body.append('\n') def visit_centered(self, node): self.body.append('\n\\begin{center}') @@ -946,9 +950,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(self.context.pop()) def visit_caption(self, node): + self.in_caption += 1 self.body.append('\\caption{') def depart_caption(self, node): self.body.append('}') + self.in_caption -= 1 def visit_legend(self, node): self.body.append('{\\small ') @@ -1091,11 +1097,17 @@ class LaTeXTranslator(nodes.NodeVisitor): uri.startswith('https:') or uri.startswith('ftp:'): self.body.append('\\href{%s}{' % self.encode_uri(uri)) # if configured, put the URL after the link - if self.builder.config.latex_show_urls and \ - node.astext() != uri: + show_urls = self.builder.config.latex_show_urls + if node.astext() != uri and show_urls and show_urls != 'no': if uri.startswith('mailto:'): uri = uri[7:] - self.context.append('} (%s)' % self.encode_uri(uri)) + if show_urls == 'footnote' and not \ + (self.in_footnote or self.in_caption): + # obviously, footnotes in footnotes are not going to work + self.context.append( + r'}\footnote{%s}' % self.encode_uri(uri)) + else: # all other true values (b/w compat) + self.context.append('} (%s)' % self.encode_uri(uri)) else: self.context.append('}') elif uri.startswith('#'): @@ -1221,6 +1233,10 @@ class LaTeXTranslator(nodes.NodeVisitor): if used: self.body.append('\\footnotemark[%s]' % num) else: + if self.in_caption: + raise UnsupportedError('%s:%s: footnotes in float captions ' + 'are not supported by LaTeX' % + (self.curfilestack[-1], node.line)) footnode.walkabout(self) self.footnotestack[-1][num][1] = True raise nodes.SkipChildren From e9e4a372d7e1f6cf7f3ec542bb62c713cf382112 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Jan 2011 19:00:50 +0100 Subject: [PATCH 494/744] #259: HTML table rows now have even/odd CSS classes to enable "Zebra styling". --- CHANGES | 3 +++ sphinx/writers/html.py | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/CHANGES b/CHANGES index d8883adc7..da1113a91 100644 --- a/CHANGES +++ b/CHANGES @@ -61,6 +61,9 @@ Release 1.1 (in development) * Added :confval:`man_show_urls` config value. +* #259: HTML table rows now have even/odd CSS classes to enable + "Zebra styling". + Release 1.0.7 (in development) ============================== diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 57d7a7d5c..f62b4e256 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -503,6 +503,33 @@ class HTMLTranslator(BaseTranslator): BaseTranslator.depart_title(self, node) + # overwritten to add even/odd classes + + def visit_table(self, node): + self._table_row_index = 0 + return BaseTranslator.visit_table(self, node) + + def visit_row(self, node): + self._table_row_index += 1 + if self._table_row_index % 2 == 0: + node['classes'].append('row-even') + else: + node['classes'].append('row-odd') + self.body.append(self.starttag(node, 'tr', '')) + node.column = 0 + + def visit_field_list(self, node): + self._fieldlist_row_index = 0 + return BaseTranslator.visit_field_list(self, node) + + def visit_field(self, node): + self._fieldlist_row_index += 1 + if self._fieldlist_row_index % 2 == 0: + node['classes'].append('field-even') + else: + node['classes'].append('field-odd') + self.body.append(self.starttag(node, 'tr', '', CLASS='field')) + def unknown_visit(self, node): raise NotImplementedError('Unknown node: ' + node.__class__.__name__) From 3020799a1ea577b1088b3dd39dd4d18fea5c814e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Jan 2011 19:42:37 +0100 Subject: [PATCH 495/744] Add explicit hl language. --- doc/glossary.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/glossary.rst b/doc/glossary.rst index 2a82e20f2..8bc393eb7 100644 --- a/doc/glossary.rst +++ b/doc/glossary.rst @@ -23,7 +23,9 @@ Glossary A reStructuredText markup element that allows marking a block of content with special meaning. Directives are supplied not only by docutils, but Sphinx and custom extensions can add their own. The basic directive - syntax looks like this:: + syntax looks like this: + + .. sourcecode:: rst .. directivename:: argument ... :option: value From 0d6c738b02c7d41838b9f3a0bbe53229ee9177cf Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Jan 2011 20:34:37 +0100 Subject: [PATCH 496/744] Fix test after config default change. --- tests/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index 5e14f0ccf..b07a71bf1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -39,7 +39,7 @@ def test_core_config(app): # complex default values assert 'html_title' not in cfg.__dict__ - assert cfg.html_title == 'Sphinx v0.6alpha1 documentation' + assert cfg.html_title == 'Sphinx 0.6alpha1 documentation' # complex default values mustn't raise for valuename in cfg.config_values: From f925de6aa07077d50e0d2d895c59039fb2d23327 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Jan 2011 20:34:41 +0100 Subject: [PATCH 497/744] #586: Implemented improved glossary markup which allows multiple terms per definition. --- CHANGES | 3 ++ doc/markup/para.rst | 21 ++++++-- sphinx/addnodes.py | 3 ++ sphinx/domains/std.py | 108 ++++++++++++++++++++++++++++++-------- sphinx/roles.py | 1 - sphinx/writers/html.py | 4 ++ sphinx/writers/latex.py | 6 ++- sphinx/writers/manpage.py | 4 ++ sphinx/writers/texinfo.py | 3 ++ sphinx/writers/text.py | 4 ++ tests/root/markup.txt | 14 ++++- 11 files changed, 143 insertions(+), 28 deletions(-) diff --git a/CHANGES b/CHANGES index da1113a91..58a4a5082 100644 --- a/CHANGES +++ b/CHANGES @@ -51,6 +51,9 @@ Release 1.1 (in development) * #526: Added Iranian translation. +* #586: Implemented improved glossary markup which allows multiple terms per + definition. + * #559: :confval:`html_add_permalinks` is now a string giving the text to display in permalinks. diff --git a/doc/markup/para.rst b/doc/markup/para.rst index 302d9c1ba..ced18d818 100644 --- a/doc/markup/para.rst +++ b/doc/markup/para.rst @@ -153,9 +153,9 @@ Glossary .. rst:directive:: .. glossary:: - This directive must contain a reST definition list with terms and - definitions. The definitions will then be referencable with the :rst:role:`term` - role. Example:: + This directive must contain a reST definition-list-like markup with terms and + definitions. The definitions will then be referencable with the + :rst:role:`term` role. Example:: .. glossary:: @@ -169,10 +169,25 @@ Glossary The directory which, including its subdirectories, contains all source files for one Sphinx project. + In contrast to regular definition lists, *multiple* terms per entry are + allowed, and inline markup is allowed in terms. You can link to all of the + terms. For example:: + + .. glossary:: + + term 1 + term 2 + Definition of both terms. + + (When the glossary is sorted, the first term determines the sort order.) + .. versionadded:: 0.6 You can now give the glossary directive a ``:sorted:`` flag that will automatically sort the entries alphabetically. + .. versionchanged:: 1.1 + Now supports multiple terms and inline markup in terms. + Grammar production displays --------------------------- diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index ec6f2f6df..94f1d615f 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -171,6 +171,9 @@ class literal_emphasis(nodes.emphasis): class abbreviation(nodes.Inline, nodes.TextElement): """Node for abbreviations with explanations.""" +class termsep(nodes.Structural, nodes.Element): + """Separates two terms within a node.""" + # make the new nodes known to docutils; needed because the HTML writer will # choke at some point if these are not added diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 0625a4516..606c88cd6 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -14,6 +14,7 @@ import unicodedata from docutils import nodes from docutils.parsers.rst import directives +from docutils.statemachine import ViewList from sphinx import addnodes from sphinx.roles import XRefRole @@ -206,8 +207,8 @@ class OptionXRefRole(XRefRole): class Glossary(Directive): """ - Directive to create a glossary with cross-reference targets - for :term: roles. + Directive to create a glossary with cross-reference targets for :term: + roles. """ has_content = True @@ -224,37 +225,100 @@ class Glossary(Directive): gloss_entries = env.temp_data.setdefault('gloss_entries', set()) node = addnodes.glossary() node.document = self.state.document - self.state.nested_parse(self.content, self.content_offset, node) - # the content should be definition lists - dls = [child for child in node - if isinstance(child, nodes.definition_list)] - # now, extract definition terms to enable cross-reference creation - new_dl = nodes.definition_list() - new_dl['classes'].append('glossary') + # This directive implements a custom format of the reST definition list + # that allows multiple lines of terms before the definition. This is + # easy to parse since we know that the contents of the glossary *must + # be* a definition list. + + # first, collect single entries + entries = [] + in_definition = True + was_empty = True + messages = [] + for (source, lineno, line) in self.content.xitems(): + # empty line -> add to last definition + if not line: + if in_definition and entries: + entries[-1][1].append('', source, lineno) + was_empty = True + continue + # unindented line -> a term + if line and not line[0].isspace(): + # first term of definition + if in_definition: + if not was_empty: + messages.append(self.state.reporter.system_message( + 2, 'glossary term must be preceded by empty line', + source=source, line=lineno)) + entries.append(([(line, source, lineno)], ViewList())) + in_definition = False + # second term and following + else: + if was_empty: + messages.append(self.state.reporter.system_message( + 2, 'glossary terms must not be separated by empty ' + 'lines', source=source, line=lineno)) + entries[-1][0].append((line, source, lineno)) + else: + if not in_definition: + # first line of definition, determines indentation + in_definition = True + indent_len = len(line) - len(line.lstrip()) + entries[-1][1].append(line[indent_len:], source, lineno) + was_empty = False + + # now, parse all the entries into a big definition list items = [] - for dl in dls: - for li in dl.children: - if not li.children or not isinstance(li[0], nodes.term): - continue - termtext = li.children[0].astext() + for terms, definition in entries: + termtexts = [] + termnodes = [] + system_messages = [] + ids = [] + for line, source, lineno in terms: + # parse the term with inline markup + res = self.state.inline_text(line, lineno) + system_messages.extend(res[1]) + + # get a text-only representation of the term and register it + # as a cross-reference target + tmp = nodes.paragraph('', '', *res[0]) + termtext = tmp.astext() new_id = 'term-' + nodes.make_id(termtext) if new_id in gloss_entries: new_id = 'term-' + str(len(gloss_entries)) gloss_entries.add(new_id) - li[0]['names'].append(new_id) - li[0]['ids'].append(new_id) + ids.append(new_id) objects['term', termtext.lower()] = env.docname, new_id + termtexts.append(termtext) # add an index entry too indexnode = addnodes.index() indexnode['entries'] = [('single', termtext, new_id, termtext)] - li.insert(0, indexnode) - items.append((termtext, li)) + termnodes += indexnode + termnodes.extend(res[0]) + termnodes.append(addnodes.termsep()) + # make a single "term" node with all the terms, separated by termsep + # nodes (remove the dangling trailing separator) + term = nodes.term('', '', *termnodes[:-1]) + term['ids'].extend(ids) + term['names'].extend(ids) + term += system_messages + + defnode = nodes.definition() + self.state.nested_parse(definition, definition.items[0][1], defnode) + + items.append((termtexts, + nodes.definition_list_item('', term, defnode))) + if 'sorted' in self.options: - items.sort(key=lambda x: unicodedata.normalize('NFD', x[0].lower())) - new_dl.extend(item[1] for item in items) - node.children = [new_dl] - return [node] + items.sort(key=lambda x: + unicodedata.normalize('NFD', x[0][0].lower())) + + dlist = nodes.definition_list() + dlist['classes'].append('glossary') + dlist.extend(item[1] for item in items) + node += dlist + return messages + [node] token_re = re.compile('`([a-z_][a-z0-9_]*)`') diff --git a/sphinx/roles.py b/sphinx/roles.py index a960f00a7..6b22d20ac 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -285,7 +285,6 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): entries = [('single', target, targetid, target)] indexnode = addnodes.index() indexnode['entries'] = entries - indexnode['inline'] = True textnode = nodes.Text(title, title) return [indexnode, targetnode, textnode], [] diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index f62b4e256..5719aca23 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -483,6 +483,10 @@ class HTMLTranslator(BaseTranslator): def depart_abbreviation(self, node): self.body.append('') + def visit_termsep(self, node): + self.body.append('
                ') + raise nodes.SkipNode + def depart_title(self, node): close_tag = self.context[-1] if (self.permalink_text and self.builder.add_permalinks and diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 63faa202a..a6dce7cb3 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -788,6 +788,10 @@ class LaTeXTranslator(nodes.NodeVisitor): def depart_term(self, node): self.body.append(self.context.pop()) + def visit_termsep(self, node): + self.body.append(', ') + raise nodes.SkipNode + def visit_classifier(self, node): self.body.append('{[}') def depart_classifier(self, node): @@ -1059,7 +1063,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\n\\end{flushright}\n') def visit_index(self, node, scre=re.compile(r';\s*')): - if not node.get('inline'): + if not node.get('inline', True): self.body.append('\n') entries = node['entries'] for type, string, tid, _ in entries: diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index 7c8c4653a..e6bccca78 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -161,6 +161,10 @@ class ManualPageTranslator(BaseTranslator): def depart_versionmodified(self, node): self.depart_paragraph(node) + def visit_termsep(self, node): + self.body.append(', ') + raise nodes.SkipNode + # overwritten -- we don't want source comments to show up def visit_comment(self, node): raise nodes.SkipNode diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index b35df47db..c5c3aabe2 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -762,6 +762,9 @@ class TexinfoTranslator(nodes.NodeVisitor): def depart_term(self, node): pass + def visit_termsep(self, node): + self.add_text(self.at_item_x + ' ', fresh=1) + def visit_classifier(self, node): self.add_text(' : ') def depart_classifier(self, node): diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index b79a8536d..1a7d2a7df 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -483,6 +483,10 @@ class TextTranslator(nodes.NodeVisitor): if not self._li_has_classifier: self.end_state(end=None) + def visit_termsep(self, node): + self.add_text(', ') + raise nodes.SkipNode + def visit_classifier(self, node): self.add_text(' : ') def depart_classifier(self, node): diff --git a/tests/root/markup.txt b/tests/root/markup.txt index fab6d78c0..601485341 100644 --- a/tests/root/markup.txt +++ b/tests/root/markup.txt @@ -239,13 +239,25 @@ This tests :CLASS:`role names in uppercase`. * Monty Python .. glossary:: + :sorted: boson Particle with integer spin. - fermion + *fermion* Particle with half-integer spin. + tauon + myon + electron + Examples for fermions. + + über + Gewisse + + änhlich + Dinge + .. productionlist:: try_stmt: `try1_stmt` | `try2_stmt` try1_stmt: "try" ":" `suite` From e2a249d729370c9ef0d88f868401972d44bd4a89 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 7 Jan 2011 15:49:01 +0100 Subject: [PATCH 498/744] C++ domain now supports array definitions. --- CHANGES | 2 ++ sphinx/domains/cpp.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/CHANGES b/CHANGES index 923369024..d9fe9efdd 100644 --- a/CHANGES +++ b/CHANGES @@ -67,6 +67,8 @@ Release 1.1 (in development) * #259: HTML table rows now have even/odd CSS classes to enable "Zebra styling". +* C++ domain now supports array definitions. + Release 1.0.7 (in development) ============================== diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index e712e01e2..4688691df 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -28,6 +28,7 @@ _whitespace_re = re.compile(r'\s+(?u)') _string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) _visibility_re = re.compile(r'\b(public|private|protected)\b') +_array_def_re = re.compile(r'\[\s*(.+?)?\s*\]') _operator_re = re.compile(r'''(?x) \[\s*\] | \(\s*\) @@ -273,6 +274,22 @@ class PtrDefExpr(WrappingDefExpr): return u'%s*' % self.typename +class ArrayDefExpr(WrappingDefExpr): + + def __init__(self, typename, size_hint=None): + WrappingDefExpr.__init__(self, typename) + self.size_hint = size_hint + + def get_id(self): + return self.typename.get_id() + u'A' + + def __unicode__(self): + return u'%s[%s]' % ( + self.typename, + self.size_hint is not None and unicode(self.size_hint) or u'' + ) + + class RefDefExpr(WrappingDefExpr): def get_id(self): @@ -562,6 +579,8 @@ class DefinitionParser(object): expr = ConstDefExpr(expr) elif self.skip_string('*'): expr = PtrDefExpr(expr) + elif self.match(_array_def_re): + expr = ArrayDefExpr(expr, self.last_match.group(1)) elif self.skip_string('&'): expr = RefDefExpr(expr) else: From 98d884da6ebb5b26f97c72aa55b01711a53040a1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 7 Jan 2011 16:41:44 +0100 Subject: [PATCH 499/744] #306: Added :event:`env-get-outdated` event. --- CHANGES | 2 ++ doc/ext/appapi.rst | 9 +++++++++ sphinx/application.py | 1 + sphinx/environment.py | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/CHANGES b/CHANGES index d9fe9efdd..151edd6c0 100644 --- a/CHANGES +++ b/CHANGES @@ -67,6 +67,8 @@ Release 1.1 (in development) * #259: HTML table rows now have even/odd CSS classes to enable "Zebra styling". +* #306: Added :event:`env-get-outdated` event. + * C++ domain now supports array definitions. diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst index aa314584c..5337f4cc8 100644 --- a/doc/ext/appapi.rst +++ b/doc/ext/appapi.rst @@ -352,6 +352,15 @@ registered event handlers. Emitted when the builder object has been created. It is available as ``app.builder``. +.. event:: env-get-outdated (app, env, added, changed, removed) + + Emitted when the environment determines which source files have changed and + should be re-read. *added*, *changed* and *removed* are sets of docnames + that the environment has determined. You can return a list of docnames to + re-read in addition to these. + + .. versionadded:: 1.1 + .. event:: env-purge-doc (app, env, docname) Emitted when all traces of a source file should be cleaned from the diff --git a/sphinx/application.py b/sphinx/application.py index 0d9326f0b..fb100b2f3 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -40,6 +40,7 @@ from sphinx.util.console import bold # List of all known core events. Maps name to arguments description. events = { 'builder-inited': '', + 'env-get-outdated': 'env, added, changed, removed', 'env-purge-doc': 'env, docname', 'source-read': 'docname, source text', 'doctree-read': 'the doctree before being pickled', diff --git a/sphinx/environment.py b/sphinx/environment.py index d47562cd7..e11a981a9 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -548,6 +548,10 @@ class BuildEnvironment: added, changed, removed = self.get_outdated_files(config_changed) + # allow user intervention as well + for docs in app.emit('env-get-outdated', self, added, changed, removed): + changed.update(set(docs) & self.found_docs) + # if files were added or removed, all documents with globbed toctrees # must be reread if added or removed: From bf767d5222b6259e11306594e3d64602f4bd93ca Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 7 Jan 2011 19:00:29 +0100 Subject: [PATCH 500/744] #454: Add more index markup capabilities: marking see/seealso entries, and main entries for a given key. --- CHANGES | 3 + doc/markup/misc.rst | 20 +++++++ sphinx/builders/epub.py | 11 ++-- sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- sphinx/domains/javascript.py | 2 +- sphinx/domains/python.py | 4 +- sphinx/domains/rst.py | 2 +- sphinx/domains/std.py | 14 ++--- sphinx/environment.py | 76 +++++++++++------------- sphinx/locale/__init__.py | 1 + sphinx/roles.py | 15 +++-- sphinx/texinputs/sphinx.sty | 17 ------ sphinx/themes/basic/genindex-single.html | 49 ++++++++++----- sphinx/themes/basic/genindex.html | 62 ++++++++++++------- sphinx/util/__init__.py | 8 +++ sphinx/util/nodes.py | 19 ++++-- sphinx/writers/latex.py | 48 ++++++++------- tests/root/markup.txt | 7 +++ tests/test_build_html.py | 10 +++- tests/test_build_latex.py | 1 + 21 files changed, 228 insertions(+), 145 deletions(-) diff --git a/CHANGES b/CHANGES index 151edd6c0..459000f89 100644 --- a/CHANGES +++ b/CHANGES @@ -46,6 +46,9 @@ Release 1.1 (in development) * #521: Added :confval:`linkcheck_ignore` config value. +* #454: Add more index markup capabilities: marking see/seealso entries, + and main entries for a given key. + * #516: Added new value of the :confval:`latex_show_urls` option to show the URLs in footnotes. diff --git a/doc/markup/misc.rst b/doc/markup/misc.rst index 55370de7e..95899ef32 100644 --- a/doc/markup/misc.rst +++ b/doc/markup/misc.rst @@ -113,11 +113,28 @@ mainly contained in information units, such as the language reference. Likewise, ``triple: module; search; path`` is a shortcut that creates three index entries, which are ``module; search path``, ``search; path, module`` and ``path; module search``. + see + ``see: entry; other`` creates an index entry that refers from ``entry`` to + ``other``. + seealso + Like ``see``, but inserts "see also" instead of "see". module, keyword, operator, object, exception, statement, builtin These all create two index entries. For example, ``module: hashlib`` creates the entries ``module; hashlib`` and ``hashlib; module``. (These are Python-specific and therefore deprecated.) + You can mark up "main" index entries by prefixing them with an exclamation + mark. The references to "main" entries are emphasized in the generated + index. For example, if two pages contain :: + + .. index:: Python + + and one page contains :: + + .. index:: ! Python + + then the backlink to the latter page is emphasized among the three backlinks. + For index directives containing only "single" entries, there is a shorthand notation:: @@ -125,6 +142,9 @@ mainly contained in information units, such as the language reference. This creates four index entries. + .. versionchanged:: 1.1 + Added ``see`` and ``seealso`` types, as well as marking main entries. + .. rst:role:: index While the :rst:dir:`index` directive is a block-level markup and links to the diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index 128965033..ddc6fb5c6 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -289,16 +289,17 @@ class EpubBuilder(StandaloneHTMLBuilder): # Logic modeled from themes/basic/genindex.html for key, columns in tree: for entryname, (links, subitems) in columns: - for (i, link) in enumerate(links): + for (i, (ismain, link)) in enumerate(links): m = _refuri_re.match(link) if m: - links[i] = self.fix_fragment(m.group(1), m.group(2)) + links[i] = (ismain, + self.fix_fragment(m.group(1), m.group(2))) for subentryname, subentrylinks in subitems: - for (i, link) in enumerate(subentrylinks): + for (i, (ismain, link)) in enumerate(subentrylinks): m = _refuri_re.match(link) if m: - subentrylinks[i] = \ - self.fix_fragment(m.group(1), m.group(2)) + subentrylinks[i] = (ismain, + self.fix_fragment(m.group(1), m.group(2))) def handle_page(self, pagename, addctx, templatename='page.html', outfilename=None, event_arg=None): diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 48fbb36f2..b0dd332b5 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -168,7 +168,7 @@ class CObject(ObjectDescription): indextext = self.get_index_text(name) if indextext: - self.indexnode['entries'].append(('single', indextext, name, name)) + self.indexnode['entries'].append(('single', indextext, name, '')) def before_content(self): self.typename_set = False diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 4688691df..4e40dde7d 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -852,7 +852,7 @@ class CPPObject(ObjectDescription): indextext = self.get_index_text(name) if indextext: - self.indexnode['entries'].append(('single', indextext, name, name)) + self.indexnode['entries'].append(('single', indextext, theid, '')) def before_content(self): lastname = self.names and self.names[-1] diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 07a54e4dd..57cee8f9c 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -96,7 +96,7 @@ class JSObject(ObjectDescription): indextext = self.get_index_text(objectname, name_obj) if indextext: self.indexnode['entries'].append(('single', indextext, - fullname, fullname)) + fullname, '')) def get_index_text(self, objectname, name_obj): name, obj = name_obj diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index f9aef0500..aa3375df9 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -221,7 +221,7 @@ class PyObject(ObjectDescription): indextext = self.get_index_text(modname, name_cls) if indextext: self.indexnode['entries'].append(('single', indextext, - fullname, fullname)) + fullname, '')) def before_content(self): # needed for automatic qualification of members (reset in subclasses) @@ -399,7 +399,7 @@ class PyModule(Directive): if not noindex: indextext = _('%s (module)') % modname inode = addnodes.index(entries=[('single', indextext, - 'module-' + modname, modname)]) + 'module-' + modname, '')]) ret.append(inode) return ret diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index fa3b4f1a0..e67f827e0 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -48,7 +48,7 @@ class ReSTMarkup(ObjectDescription): indextext = self.get_index_text(self.objtype, name) if indextext: self.indexnode['entries'].append(('single', indextext, - targetname, targetname)) + targetname, '')) def get_index_text(self, objectname, name): if self.objtype == 'directive': diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 606c88cd6..7dcec6168 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -61,7 +61,7 @@ class GenericObject(ObjectDescription): indextype = 'single' indexentry = self.indextemplate % (name,) self.indexnode['entries'].append((indextype, indexentry, - targetname, targetname)) + targetname, '')) self.env.domaindata['std']['objects'][self.objtype, name] = \ self.env.docname, targetname @@ -82,8 +82,8 @@ class EnvVarXRefRole(XRefRole): tgtid = 'index-%s' % env.new_serialno('index') indexnode = addnodes.index() indexnode['entries'] = [ - ('single', varname, tgtid, varname), - ('single', _('environment variable; %s') % varname, tgtid, varname) + ('single', varname, tgtid, ''), + ('single', _('environment variable; %s') % varname, tgtid, '') ] targetnode = nodes.target('', '', ids=[tgtid]) document.note_explicit_target(targetnode) @@ -118,7 +118,7 @@ class Target(Directive): indextype = indexentry[:colon].strip() indexentry = indexentry[colon+1:].strip() inode = addnodes.index(entries=[(indextype, indexentry, - targetname, targetname)]) + targetname, '')]) ret.insert(0, inode) name = self.name if ':' in self.name: @@ -161,7 +161,7 @@ class Cmdoption(ObjectDescription): self.indexnode['entries'].append( ('pair', _('%scommand line option; %s') % ((currprogram and currprogram + ' ' or ''), sig), - targetname, targetname)) + targetname, '')) self.env.domaindata['std']['progoptions'][currprogram, name] = \ self.env.docname, targetname @@ -293,8 +293,8 @@ class Glossary(Directive): termtexts.append(termtext) # add an index entry too indexnode = addnodes.index() - indexnode['entries'] = [('single', termtext, new_id, termtext)] - termnodes += indexnode + indexnode['entries'] = [('single', termtext, new_id, 'main')] + termnodes.append(indexnode) termnodes.extend(res[0]) termnodes.append(addnodes.termsep()) # make a single "term" node with all the terms, separated by termsep diff --git a/sphinx/environment.py b/sphinx/environment.py index e11a981a9..583234cce 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -37,7 +37,7 @@ from docutils.transforms import Transform from docutils.transforms.parts import ContentsFilter from sphinx import addnodes -from sphinx.util import url_re, get_matching_docs, docname_join, \ +from sphinx.util import url_re, get_matching_docs, docname_join, split_into, \ FilenameUniqDict from sphinx.util.nodes import clean_astext, make_refnode, extract_messages from sphinx.util.osutil import movefile, SEP, ustrftime @@ -1484,56 +1484,50 @@ class BuildEnvironment: """Create the real index from the collected index entries.""" new = {} - def add_entry(word, subword, dic=new): + def add_entry(word, subword, link=True, dic=new): entry = dic.get(word) if not entry: dic[word] = entry = [[], {}] if subword: - add_entry(subword, '', dic=entry[1]) - else: + add_entry(subword, '', link=link, dic=entry[1]) + elif link: try: - entry[0].append(builder.get_relative_uri('genindex', fn) - + '#' + tid) + uri = builder.get_relative_uri('genindex', fn) + '#' + tid except NoUri: pass + else: + entry[0].append((main, uri)) for fn, entries in self.indexentries.iteritems(): # new entry types must be listed in directives/other.py! - for type, value, tid, alias in entries: - if type == 'single': - try: - entry, subentry = value.split(';', 1) - except ValueError: - entry, subentry = value, '' - if not entry: - self.warn(fn, 'invalid index entry %r' % value) - continue - add_entry(entry.strip(), subentry.strip()) - elif type == 'pair': - try: - first, second = map(lambda x: x.strip(), - value.split(';', 1)) - if not first or not second: - raise ValueError - except ValueError: - self.warn(fn, 'invalid pair index entry %r' % value) - continue - add_entry(first, second) - add_entry(second, first) - elif type == 'triple': - try: - first, second, third = map(lambda x: x.strip(), - value.split(';', 2)) - if not first or not second or not third: - raise ValueError - except ValueError: - self.warn(fn, 'invalid triple index entry %r' % value) - continue - add_entry(first, second+' '+third) - add_entry(second, third+', '+first) - add_entry(third, first+' '+second) - else: - self.warn(fn, 'unknown index entry type %r' % type) + for type, value, tid, main in entries: + try: + if type == 'single': + try: + entry, subentry = split_into(2, 'single', value) + except ValueError: + entry, = split_into(1, 'single', value) + subentry = '' + add_entry(entry, subentry) + elif type == 'pair': + first, second = split_into(2, 'pair', value) + add_entry(first, second) + add_entry(second, first) + elif type == 'triple': + first, second, third = split_into(3, 'triple', value) + add_entry(first, second+' '+third) + add_entry(second, third+', '+first) + add_entry(third, first+' '+second) + elif type == 'see': + first, second = split_into(2, 'see', value) + add_entry(first, _('see %s') % second, link=False) + elif type == 'seealso': + first, second = split_into(2, 'see', value) + add_entry(first, _('see also %s') % second, link=False) + else: + self.warn(fn, 'unknown index entry type %r' % type) + except ValueError, err: + self.warn(fn, str(err)) # sort the index entries; put all symbols at the front, even those # following the letters in ASCII, this is where the chr(127) comes from diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index 5174f4952..3aca780cf 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -170,6 +170,7 @@ versionlabels = { 'deprecated': l_('Deprecated since version %s'), } +# XXX Python specific pairindextypes = { 'module': l_('module'), 'keyword': l_('keyword'), diff --git a/sphinx/roles.py b/sphinx/roles.py index 6b22d20ac..1d791f6d2 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -170,8 +170,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, inliner.document.note_explicit_target(targetnode) if typ == 'pep': indexnode['entries'] = [ - ('single', _('Python Enhancement Proposals!PEP %s') % text, - targetid, 'PEP %s' % text)] + ('single', _('Python Enhancement Proposals; PEP %s') % text, + targetid, '')] anchor = '' anchorindex = text.find('#') if anchorindex > 0: @@ -190,8 +190,7 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, rn += sn return [indexnode, targetnode, rn], [] elif typ == 'rfc': - indexnode['entries'] = [('single', 'RFC; RFC %s' % text, - targetid, 'RFC %s' % text)] + indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, '')] anchor = '' anchorindex = text.find('#') if anchorindex > 0: @@ -282,7 +281,13 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): entries = process_index_entry(target, targetid) # otherwise we just create a "single" entry else: - entries = [('single', target, targetid, target)] + # but allow giving main entry + main = '' + if target.startswith('!'): + target = target[1:] + title = title[1:] + main = 'main' + entries = [('single', target, targetid, main)] indexnode = addnodes.index() indexnode['entries'] = entries textnode = nodes.Text(title, title) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 7d2820822..bac435cc7 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -175,23 +175,6 @@ } -% Index-entry generation support. -% - -% Command to generate two index entries (using subentries) -\newcommand{\indexii}[2]{\index{#1!#2}\index{#2!#1}} - -% And three entries (using only one level of subentries) -\newcommand{\indexiii}[3]{\index{#1!#2 #3}\index{#2!#3, #1}\index{#3!#1 #2}} - -% And four (again, using only one level of subentries) -\newcommand{\indexiv}[4]{ -\index{#1!#2 #3 #4} -\index{#2!#3 #4, #1} -\index{#3!#4, #1 #2} -\index{#4!#1 #2 #3} -} - % \moduleauthor{name}{email} \newcommand{\moduleauthor}[2]{} diff --git a/sphinx/themes/basic/genindex-single.html b/sphinx/themes/basic/genindex-single.html index 225abfa8f..b6fa6a855 100644 --- a/sphinx/themes/basic/genindex-single.html +++ b/sphinx/themes/basic/genindex-single.html @@ -7,31 +7,48 @@ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. #} +{% macro indexentries(firstname, links) %} +
                + {%- if links -%} + + {%- if links[0][0] %}{% endif -%} + {{ firstname|e }} + {%- if links[0][0] %}{% endif -%} + + + {%- for ismain, link in links[1:] -%} + , {% if ismain %}{% endif -%} + [{{ loop.index }}] + {%- if ismain %}{% endif -%} + + {%- endfor %} + {%- else %} + {{ firstname|e }} + {%- endif %} +
                +{% endmacro %} + {% extends "layout.html" %} {% set title = _('Index') %} {% block body %} -

                {% trans key=key %}Index – {{ key }}{% endtrans %}

                +

                {% trans key=key %}Index – {{ key }}{% endtrans %}

                {%- for column in entries|slice(2) if column %} -{%- endfor %} + + {%- endfor %}
                - {%- for entryname, (links, subitems) in column %} -
                {% if links %}{{ entryname|e }} - {%- for link in links[1:] %}, [{{ loop.index }}]{% endfor %} - {%- else %}{{ entryname|e }}{% endif %}
                - {%- if subitems %} -
                - {%- for subentryname, subentrylinks in subitems %} -
                {{ subentryname|e }} - {%- for link in subentrylinks[1:] %}, [{{ loop.index }}]{% endfor -%} -
                + {%- for entryname, (links, subitems) in column %} + {{ indexentries(entryname, links) }} + {%- if subitems %} +
                + {%- for subentryname, subentrylinks in subitems %} + {{ indexentries(subentryname, subentrylinks) }} + {%- endfor %} +
                + {%- endif -%} {%- endfor %} -
                - {%- endif -%} -{%- endfor %} -
                {% endblock %} diff --git a/sphinx/themes/basic/genindex.html b/sphinx/themes/basic/genindex.html index 4af472528..536f09639 100644 --- a/sphinx/themes/basic/genindex.html +++ b/sphinx/themes/basic/genindex.html @@ -7,39 +7,57 @@ :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. #} +{% macro indexentries(firstname, links) %} +
                + {%- if links -%} + + {%- if links[0][0] %}{% endif -%} + {{ firstname|e }} + {%- if links[0][0] %}{% endif -%} + + + {%- for ismain, link in links[1:] -%} + , {% if ismain %}{% endif -%} + [{{ loop.index }}] + {%- if ismain %}{% endif -%} + + {%- endfor %} + {%- else %} + {{ firstname|e }} + {%- endif %} +
                +{% endmacro %} + {% extends "layout.html" %} {% set title = _('Index') %} {% block body %} -

                {{ _('Index') }}

                +

                {{ _('Index') }}

                -
                - {% for key, dummy in genindexentries -%} - {{ key }} {% if not loop.last %}| {% endif %} - {%- endfor %} -
                +
                + {% for key, dummy in genindexentries -%} + {{ key }} + {% if not loop.last %}| {% endif %} + {%- endfor %} +
                - {%- for key, entries in genindexentries %} +{%- for key, entries in genindexentries %}

                {{ key }}

                {%- for column in entries|slice(2) if column %} -{%- endfor %} + + {%- endfor %}
                - {%- for entryname, (links, subitems) in column %} -
                {% if links %}{{ entryname|e }} - {%- for link in links[1:] %}, [{{ loop.index }}]{% endfor %} - {%- else %}{{ entryname|e }}{% endif %}
                - {%- if subitems %} -
                - {%- for subentryname, subentrylinks in subitems %} -
                {{ subentryname|e }} - {%- for link in subentrylinks[1:] %}, [{{ loop.index }}]{% endfor -%} -
                + {%- for entryname, (links, subitems) in column %} + {{ indexentries(entryname, links) }} + {%- if subitems %} +
                + {%- for subentryname, subentrylinks in subitems %} + {{ indexentries(subentryname, subentrylinks) }} + {%- endfor %} +
                + {%- endif -%} {%- endfor %} -
                - {%- endif -%} -{%- endfor %} -
                {% endfor %} diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index d51f02083..becaff0bd 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -285,6 +285,14 @@ def rpartition(s, t): return '', s +def split_into(n, type, value): + """Split an index entry into a given number of parts at semicolons.""" + parts = map(lambda x: x.strip(), value.split(';', n-1)) + if sum(1 for part in parts if part) < n: + raise ValueError('invalid %s index entry %r' % (type, value)) + return parts + + def format_exception_cut_frames(x=1): """Format an exception with traceback, but only the last x frames.""" typ, val, tb = sys.exc_info() diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 09ab8b880..a241f5742 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -74,17 +74,22 @@ def split_explicit_title(text): indextypes = [ - 'single', 'pair', 'double', 'triple', + 'single', 'pair', 'double', 'triple', 'see', 'seealso', ] def process_index_entry(entry, targetid): indexentries = [] entry = entry.strip() + oentry = entry + main = '' + if entry.startswith('!'): + main = 'main' + entry = entry[1:].lstrip() for type in pairindextypes: if entry.startswith(type+':'): value = entry[len(type)+1:].strip() value = pairindextypes[type] + '; ' + value - indexentries.append(('pair', value, targetid, value)) + indexentries.append(('pair', value, targetid, main)) break else: for type in indextypes: @@ -92,15 +97,19 @@ def process_index_entry(entry, targetid): value = entry[len(type)+1:].strip() if type == 'double': type = 'pair' - indexentries.append((type, value, targetid, value)) + indexentries.append((type, value, targetid, main)) break # shorthand notation for single entries else: - for value in entry.split(','): + for value in oentry.split(','): value = value.strip() + main = '' + if value.startswith('!'): + main = 'main' + value = value[1:].lstrip() if not value: continue - indexentries.append(('single', value, targetid, value)) + indexentries.append(('single', value, targetid, main)) return indexentries diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index a6dce7cb3..c557101be 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -23,6 +23,7 @@ from sphinx import addnodes from sphinx import highlighting from sphinx.errors import SphinxError from sphinx.locale import admonitionlabels, versionlabels, _ +from sphinx.util import split_into from sphinx.util.osutil import ustrftime from sphinx.util.pycompat import any from sphinx.util.texescape import tex_escape_map, tex_replace_map @@ -1066,26 +1067,33 @@ class LaTeXTranslator(nodes.NodeVisitor): if not node.get('inline', True): self.body.append('\n') entries = node['entries'] - for type, string, tid, _ in entries: - if type == 'single': - self.body.append(r'\index{%s}' % - scre.sub('!', self.encode(string))) - elif type == 'pair': - parts = tuple(self.encode(x.strip()) - for x in string.split(';', 1)) - try: - self.body.append(r'\indexii{%s}{%s}' % parts) - except TypeError: - self.builder.warn('invalid pair index entry %r' % string) - elif type == 'triple': - parts = tuple(self.encode(x.strip()) - for x in string.split(';', 2)) - try: - self.body.append(r'\indexiii{%s}{%s}{%s}' % parts) - except TypeError: - self.builder.warn('invalid triple index entry %r' % string) - else: - self.builder.warn('unknown index entry type %s found' % type) + for type, string, tid, ismain in entries: + m = '' + if ismain: + m = '|textbf' + try: + if type == 'single': + p = scre.sub('!', self.encode(string)) + self.body.append(r'\index{%s%s}' % (p, m)) + elif type == 'pair': + p1, p2 = map(self.encode, split_into(2, 'pair', string)) + self.body.append(r'\index{%s!%s%s}\index{%s!%s%s}' % + (p1, p2, m, p2, p1, m)) + elif type == 'triple': + p1, p2, p3 = map(self.encode, split_into(3, 'triple', string)) + self.body.append( + r'\index{%s!%s %s%s}\index{%s!%s, %s%s}\index{%s!%s %s%s}' % + (p1, p2, p3, m, p2, p3, p1, m, p3, p1, p2, m)) + elif type == 'see': + p1, p2 = map(self.encode, split_into(2, 'see', string)) + self.body.append(r'\index{%s|see{%s}}' % (p1, p2)) + elif type == 'seealso': + p1, p2 = map(self.encode, split_into(2, 'seealso', string)) + self.body.append(r'\index{%s|see{%s}}' % (p1, p2)) + else: + self.builder.warn('unknown index entry type %s found' % type) + except ValueError, err: + self.builder.warn(str(err)) raise nodes.SkipNode def visit_raw(self, node): diff --git a/tests/root/markup.txt b/tests/root/markup.txt index 601485341..4c6b10862 100644 --- a/tests/root/markup.txt +++ b/tests/root/markup.txt @@ -277,6 +277,8 @@ Index markup double: entry; double triple: index; entry; triple keyword: with + see: from; to + seealso: fromalso; toalso Invalid index markup... @@ -285,6 +287,11 @@ Invalid index markup... pair: keyword: +.. index:: + !Main, !Other + !single: entry; pair + +:index:`!Main` .. _ölabel: diff --git a/tests/test_build_html.py b/tests/test_build_html.py index f89df08c4..7e68f8dbb 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -49,7 +49,7 @@ http://sphinx.pocoo.org/domains.html HTML_WARNINGS = ENV_WARNINGS + """\ %(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' -%(root)s/markup.txt:: WARNING: invalid index entry u'' +%(root)s/markup.txt:: WARNING: invalid single index entry u'' %(root)s/markup.txt:: WARNING: invalid pair index entry u'' %(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; ' """ @@ -232,6 +232,14 @@ HTML_XPATH = { '_static/statictmpl.html': [ (".//project", 'Sphinx '), ], + 'genindex.html': [ + # index entries + (".//a/strong", "Main"), + (".//a/strong", "[1]"), + (".//a/strong", "Other"), + (".//a", "entry"), + (".//dt/a", "double"), + ] } if pygments: diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 5fc6e0afc..68a8c07c6 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -30,6 +30,7 @@ latex_warnfile = StringIO() LATEX_WARNINGS = ENV_WARNINGS + """\ None:None: WARNING: no matching candidate for image URI u'foo.\\*' WARNING: invalid pair index entry u'' +WARNING: invalid pair index entry u'keyword; ' """ if sys.version_info >= (3, 0): From 30f85dab6ef9419c71ce306551a439ccfa0aeefe Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 7 Jan 2011 22:35:57 +0100 Subject: [PATCH 501/744] #367: Added automatic exclusion of hidden members in inheritance diagrams, and an option to selectively enable it. --- CHANGES | 3 +++ doc/ext/inheritance.rst | 7 +++++++ sphinx/ext/inheritance_diagram.py | 18 +++++++++++++----- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 04d9d4efd..253088f06 100644 --- a/CHANGES +++ b/CHANGES @@ -70,6 +70,9 @@ Release 1.1 (in development) * #259: HTML table rows now have even/odd CSS classes to enable "Zebra styling". +* #367: Added automatic exclusion of hidden members in inheritance + diagrams, and an option to selectively enable it. + * #306: Added :event:`env-get-outdated` event. * C++ domain now supports array definitions. diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst index cdd017917..5e0a76fcc 100644 --- a/doc/ext/inheritance.rst +++ b/doc/ext/inheritance.rst @@ -30,6 +30,13 @@ It adds this directive: ``lib.``, you can give ``:parts: 1`` to remove that prefix from the displayed node names.) + It also supports a ``private-bases`` flag option; if given, private base + classes (those whose name starts with ``_``) will be included. + + .. versionchanged:: 1.1 + Added ``private-bases`` option; previously, all bases were always + included. + New config values are: diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index 231b37625..29439f7cd 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -66,7 +66,8 @@ class InheritanceGraph(object): from all the way to the root "object", and then is able to generate a graphviz dot graph from them. """ - def __init__(self, class_names, currmodule, show_builtins=False, parts=0): + def __init__(self, class_names, currmodule, show_builtins=False, + private_bases=False, parts=0): """*class_names* is a list of child classes to show bases from. If *show_builtins* is True, then Python builtins will be shown @@ -74,7 +75,8 @@ class InheritanceGraph(object): """ self.class_names = class_names classes = self._import_classes(class_names, currmodule) - self.class_info = self._class_info(classes, show_builtins, parts) + self.class_info = self._class_info(classes, show_builtins, + private_bases, parts) if not self.class_info: raise InheritanceException('No classes found for ' 'inheritance diagram') @@ -131,7 +133,7 @@ class InheritanceGraph(object): classes.extend(self._import_class_or_module(name, currmodule)) return classes - def _class_info(self, classes, show_builtins, parts): + def _class_info(self, classes, show_builtins, private_bases, parts): """Return name and bases for all classes that are ancestors of *classes*. @@ -144,6 +146,8 @@ class InheritanceGraph(object): def recurse(cls): if not show_builtins and cls in builtins: return + if not private_bases and cls.__name__.startswith('_'): + return nodename = self.class_name(cls, parts) fullname = self.class_name(cls, 0) @@ -152,7 +156,9 @@ class InheritanceGraph(object): all_classes[cls] = (nodename, fullname, baselist) for base in cls.__bases__: if not show_builtins and base in builtins: - return + continue + if not private_bases and base.__name__.startswith('_'): + continue baselist.append(self.class_name(base, parts)) if base not in all_classes: recurse(base) @@ -268,6 +274,7 @@ class InheritanceDiagram(Directive): final_argument_whitespace = True option_spec = { 'parts': directives.nonnegative_int, + 'private-bases': directives.flag, } def run(self): @@ -284,7 +291,8 @@ class InheritanceDiagram(Directive): try: graph = InheritanceGraph( class_names, env.temp_data.get('py:module'), - parts=node['parts']) + parts=node['parts'], + private_bases='private-bases' in self.options) except InheritanceException, err: return [node.document.reporter.warning(err.args[0], line=self.lineno)] From 071546601ed2d7e6c91dd9b7e40f6132d101c89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 Jan 2011 00:06:02 +0100 Subject: [PATCH 502/744] Handle versioning in the environment --- sphinx/builders/intl.py | 10 +---- sphinx/builders/versioning.py | 72 ----------------------------------- sphinx/builders/websupport.py | 8 +--- sphinx/environment.py | 21 ++++++++++ 4 files changed, 23 insertions(+), 88 deletions(-) delete mode 100644 sphinx/builders/versioning.py diff --git a/sphinx/builders/intl.py b/sphinx/builders/intl.py index 447a20cf2..74ba03b54 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/intl.py @@ -17,7 +17,6 @@ from collections import defaultdict from docutils import nodes from sphinx.builders import Builder -from sphinx.builders.versioning import VersioningBuilderMixin from sphinx.util.nodes import extract_messages from sphinx.util.osutil import SEP, copyfile from sphinx.util.console import darkgreen @@ -44,7 +43,7 @@ msgstr "" """[1:] -class I18nBuilder(Builder, VersioningBuilderMixin): +class I18nBuilder(Builder): """ General i18n builder. """ @@ -52,7 +51,6 @@ class I18nBuilder(Builder, VersioningBuilderMixin): def init(self): Builder.init(self) - VersioningBuilderMixin.init(self) self.catalogs = defaultdict(dict) def get_target_uri(self, docname, typ=None): @@ -67,15 +65,9 @@ class I18nBuilder(Builder, VersioningBuilderMixin): def write_doc(self, docname, doctree): catalog = self.catalogs[docname.split(SEP, 1)[0]] - self.handle_versioning(docname, doctree, nodes.TextElement) - for node, msg in extract_messages(doctree): catalog.setdefault(node.uid, msg) - def finish(self): - Builder.finish(self) - VersioningBuilderMixin.finish(self) - class MessageCatalogBuilder(I18nBuilder): """ diff --git a/sphinx/builders/versioning.py b/sphinx/builders/versioning.py deleted file mode 100644 index 8c6438c7a..000000000 --- a/sphinx/builders/versioning.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.builders.versioning - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - - :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" -import os -import cPickle as pickle - -from docutils.utils import Reporter - -from sphinx.util.osutil import copyfile -from sphinx.environment import WarningStream -from sphinx.versioning import add_uids, merge_doctrees - - -class VersioningBuilderMixin(object): - def walk_doctree_files(self): - for root, dirs, files in os.walk(self.doctreedir): - for fn in files: - yield os.path.join(root, fn) - - def init(self): - for fp in self.walk_doctree_files(): - if fp.endswith('.doctree'): - copyfile(fp, fp + '.old') - - def get_old_doctree(self, docname): - fp = self.env.doc2path(docname, self.doctreedir, '.doctree.old') - try: - f = open(fp, 'rb') - try: - doctree = pickle.load(f) - finally: - f.close() - except IOError: - return None - doctree.settings.env = self.env - doctree.reporter = Reporter(self.env.doc2path(docname), 2, 5, - stream=WarningStream(self.env._warnfunc)) - return doctree - - def resave_doctree(self, docname, doctree): - reporter = doctree.reporter - doctree.reporter = None - doctree.settings.warning_stream = None - doctree.settings.env = None - doctree.settings.record_dependencies = None - - fp = self.env.doc2path(docname, self.doctreedir, '.doctree') - f = open(fp, 'wb') - try: - pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) - finally: - f.close() - - doctree.reporter = reporter - - def handle_versioning(self, docname, doctree, condition): - old_doctree = self.get_old_doctree(docname) - if old_doctree: - list(merge_doctrees(old_doctree, doctree, condition)) - else: - list(add_uids(doctree, condition)) - self.resave_doctree(docname, doctree) - - def finish(self): - for fp in self.walk_doctree_files(): - if fp.endswith('.doctree.old'): - os.remove(fp) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index 5165bc192..e8f6aef35 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -17,13 +17,11 @@ from docutils.io import StringOutput from sphinx.jinja2glue import BuiltinTemplateLoader from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile -from sphinx.util.websupport import is_commentable from sphinx.builders.html import PickleHTMLBuilder -from sphinx.builders.versioning import VersioningBuilderMixin from sphinx.writers.websupport import WebSupportTranslator -class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): +class WebSupportBuilder(PickleHTMLBuilder): """ Builds documents for the web support package. """ @@ -31,7 +29,6 @@ class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): def init(self): PickleHTMLBuilder.init(self) - VersioningBuilderMixin.init(self) # templates are needed for this builder, but the serializing # builder does not initialize them self.init_templates() @@ -58,8 +55,6 @@ class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): destination = StringOutput(encoding='utf-8') doctree.settings = self.docsettings - self.handle_versioning(docname, doctree, is_commentable) - self.cur_docname = docname self.secnumbers = self.env.toc_secnumbers.get(docname, {}) self.imgpath = '/' + posixpath.join(self.virtual_staticdir, '_images') @@ -144,7 +139,6 @@ class WebSupportBuilder(PickleHTMLBuilder, VersioningBuilderMixin): self.globalcontext['script'] = doc_ctx['script'] PickleHTMLBuilder.handle_finish(self) - VersioningBuilderMixin.finish(self) # move static stuff over to separate directory directories = ['_images', '_static'] diff --git a/sphinx/environment.py b/sphinx/environment.py index 2236c53a5..59debcd88 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -45,6 +45,7 @@ from sphinx.util.matching import compile_matchers from sphinx.util.pycompat import all, class_types from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _, init as init_locale +from sphinx.versioning import add_uids, merge_doctrees fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() @@ -753,6 +754,26 @@ class BuildEnvironment: # store time of build, for outdated files detection self.all_docs[docname] = time.time() + # get old doctree + old_doctree_path = self.doc2path(docname, self.doctreedir, '.doctree') + try: + f = open(old_doctree_path, 'rb') + try: + old_doctree = pickle.load(f) + finally: + f.close() + old_doctree.settings.env = self + old_doctree.reporter = Reporter(self.doc2path(docname), 2, 5, + stream=WarningStream(self._warnfunc)) + except EnvironmentError: + old_doctree = None + + # add uids for versioning + if old_doctree is None: + list(add_uids(doctree, nodes.TextElement)) + else: + list(merge_doctrees(old_doctree, doctree, nodes.TextElement)) + # make it picklable doctree.reporter = None doctree.transformer = None From c6028c440542f6662fd53232629a5d5b1b398691 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 10:07:42 +0100 Subject: [PATCH 503/744] #478: Added :rst:dir:`py:decorator` directive to describe decorators. --- CHANGES | 2 ++ doc/domains.rst | 39 +++++++++++++++++++++++++++++ sphinx/domains/python.py | 54 ++++++++++++++++++++++++++++++++-------- 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 9d7d8473d..26c63f8b9 100644 --- a/CHANGES +++ b/CHANGES @@ -70,6 +70,8 @@ Release 1.1 (in development) * #259: HTML table rows now have even/odd CSS classes to enable "Zebra styling". +* #478: Added :rst:dir:`py:decorator` directive to describe decorators. + * #367: Added automatic exclusion of hidden members in inheritance diagrams, and an option to selectively enable it. diff --git a/doc/domains.rst b/doc/domains.rst index 6e6a9b26a..4cd77b9e2 100644 --- a/doc/domains.rst +++ b/doc/domains.rst @@ -231,6 +231,45 @@ The following directives are provided for module and class contents: .. versionadded:: 0.6 +.. rst:directive:: .. py:decorator:: name + .. py:decorator:: name(signature) + + Describes a decorator function. The signature should *not* represent the + signature of the actual function, but the usage as a decorator. For example, + given the functions + + .. code-block:: python + + def removename(func): + func.__name__ = '' + return func + + def setnewname(name): + def decorator(func): + func.__name__ = name + return func + return decorator + + the descriptions should look like this:: + + .. py:decorator:: removename + + Remove name of the decorated function. + + .. py:decorator:: setnewname(name) + + Set name of the decorated function to *name*. + + There is no ``py:deco`` role to link to a decorator that is marked up with + this directive; rather, use the :rst:role:`py:func` role. + +.. rst:directive:: .. py:decoratormethod:: name + .. py:decoratormethod:: name(signature) + + Same as :rst:dir:`py:decorator`, but for decorators that are methods. + + Refer to a decorator method using the :rst:role:`py:meth` role. + .. _signatures: diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index aa3375df9..0dbd883c7 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -357,6 +357,38 @@ class PyClassmember(PyObject): self.clsname_set = True +class PyDecoratorMixin(object): + """ + Mixin for decorator directives. + """ + def handle_signature(self, sig, signode): + ret = super(PyDecoratorMixin, self).handle_signature(sig, signode) + signode.insert(0, addnodes.desc_addname('@', '@')) + return ret + + def needs_arglist(self): + return False + + +class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel): + """ + Directive to mark functions meant to be used as decorators. + """ + def run(self): + # a decorator function is a function after all + self.name = 'py:function' + return PyModulelevel.run(self) + + +class PyDecoratorMethod(PyDecoratorMixin, PyClassmember): + """ + Directive to mark methods meant to be used as decorators. + """ + def run(self): + self.name = 'py:method' + return PyClassmember.run(self) + + class PyModule(Directive): """ Directive to mark description of a new module. @@ -534,16 +566,18 @@ class PythonDomain(Domain): } directives = { - 'function': PyModulelevel, - 'data': PyModulelevel, - 'class': PyClasslike, - 'exception': PyClasslike, - 'method': PyClassmember, - 'classmethod': PyClassmember, - 'staticmethod': PyClassmember, - 'attribute': PyClassmember, - 'module': PyModule, - 'currentmodule': PyCurrentModule, + 'function': PyModulelevel, + 'data': PyModulelevel, + 'class': PyClasslike, + 'exception': PyClasslike, + 'method': PyClassmember, + 'classmethod': PyClassmember, + 'staticmethod': PyClassmember, + 'attribute': PyClassmember, + 'module': PyModule, + 'currentmodule': PyCurrentModule, + 'decorator': PyDecoratorFunction, + 'decoratormethod': PyDecoratorMethod, } roles = { 'data': PyXRefRole(), From e48f7eb09afbca2f52745b5efed2a1e3e25d66ee Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 15:39:35 +0100 Subject: [PATCH 504/744] Fix interpolation error, encode inserted caption strings and add the caption in HTML output too. --- AUTHORS | 1 + CHANGES | 2 ++ doc/ext/graphviz.rst | 13 +++++++++---- sphinx/ext/graphviz.py | 14 +++++++++----- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/AUTHORS b/AUTHORS index 09574fbee..44bbbcd42 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,6 +15,7 @@ Other contributors, listed alphabetically, are: * Josip Dzolonga -- coverage builder * Horst Gutmann -- internationalization support * Martin Hans -- autodoc improvements +* Doug Hellmann -- graphviz improvements * Dave Kuhlman -- original LaTeX writer * Blaise Laflamme -- pyramid theme * Thomas Lamb -- linkcheck builder diff --git a/CHANGES b/CHANGES index 26c63f8b9..1cadd7420 100644 --- a/CHANGES +++ b/CHANGES @@ -77,6 +77,8 @@ Release 1.1 (in development) * #306: Added :event:`env-get-outdated` event. +* #590: Added ``caption`` option to graphviz directives. + * C++ domain now supports array definitions. diff --git a/doc/ext/graphviz.rst b/doc/ext/graphviz.rst index 3a6d7c30e..9b34b48fe 100644 --- a/doc/ext/graphviz.rst +++ b/doc/ext/graphviz.rst @@ -73,10 +73,15 @@ It adds these directives: the graphviz code. .. versionadded:: 1.1 - All three directives support an ``inline`` flag that controls - paragraph breaks in the output. When set, the graph is inserted - into the current paragraph. If the flag is not given, paragraph - breaks are introduced before and after the image (the default). + All three directives support an ``inline`` flag that controls paragraph + breaks in the output. When set, the graph is inserted into the current + paragraph. If the flag is not given, paragraph breaks are introduced before + and after the image (the default). + +.. versionadded:: 1.1 + All three directives support a ``caption`` option that can be used to give a + caption to the diagram. Naturally, diagrams marked as "inline" cannot have a + caption. There are also these new config values: diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 8f7744b21..45f3169c4 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -223,7 +223,8 @@ def render_dot_html(self, node, code, options, prefix='graphviz', self.builder.warn('dot code %r: ' % code + str(exc)) raise nodes.SkipNode - if node.get('inline', False): + inline = node.get('inline', False) + if inline: wrapper = 'span' else: wrapper = 'p' @@ -254,6 +255,9 @@ def render_dot_html(self, node, code, options, prefix='graphviz', self.body.append('%s\n' % (fname, alt, mapname, imgcss)) self.body.extend(imgmap) + if node.get('caption') and not inline: + self.body.append('

                \n

                ') + self.body.append(self.encode(node['caption'])) self.body.append('\n' % wrapper) raise nodes.SkipNode @@ -278,16 +282,16 @@ def render_dot_latex(self, node, code, options, prefix='graphviz'): if fname is not None: caption = node.get('caption') - if caption: + if caption and not inline: self.body.append('\n\\begin{figure}[h!]') self.body.append('\n\\begin{center}') - self.body.append('\n\\caption{%s}' % caption) - self.body.append('\n\\label{figure:%s}' % caption) + self.body.append('\n\\caption{%s}' % self.encode(caption)) + self.body.append('\n\\label{figure:%s}' % self.encode(caption)) self.body.append('\n\\includegraphics{%s}' % fname) self.body.append('\n\\end{center}') self.body.append('\n\\end{figure}\n') else: - self.body.append('%s\\includegraphics{%s}' % + self.body.append('%s\\includegraphics{%s}%s' % (para_separator, fname, para_separator)) raise nodes.SkipNode From 7139a1c882df653662570f88690bd52040974c73 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 16:09:49 +0100 Subject: [PATCH 505/744] The :rst:dir:`py:module` directive doesn't output its ``platform`` option value anymore. (It was the only thing that the directive did output, and therefore quite inconsistent.) --- CHANGES | 4 ++++ sphinx/domains/python.py | 11 ++--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index e9db668e0..1389934c5 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,10 @@ Release 1.1 (in development) * Added a Texinfo builder. +* Incompatibility: The :rst:dir:`py:module` directive doesn't output + its ``platform`` option value anymore. (It was the only thing that + the directive did output, and therefore quite inconsistent.) + * Added i18n support for content, a ``gettext`` builder and related utilities. diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 0dbd883c7..2387f1e0f 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -419,15 +419,8 @@ class PyModule(Directive): targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] - # XXX this behavior of the module directive is a mess... - if 'platform' in self.options: - platform = self.options['platform'] - node = nodes.paragraph() - node += nodes.emphasis('', _('Platforms: ')) - node += nodes.Text(platform, platform) - ret.append(node) - # the synopsis isn't printed; in fact, it is only used in the - # modindex currently + # the platform and synopsis aren't printed; in fact, they are only used + # in the modindex currently if not noindex: indextext = _('%s (module)') % modname inode = addnodes.index(entries=[('single', indextext, From c266128c6d0f06f4f11c34ddc6e533fd2c425ccc Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:32:32 +0100 Subject: [PATCH 506/744] Rename "intl" module to "gettext", to make it easier to find. Distinguish environments with different versioning methods and always give the gettext builder its own doctree dir. --- doc/Makefile | 7 +-- doc/builders.rst | 2 +- doc/intl.rst | 2 +- sphinx/builders/__init__.py | 5 ++- sphinx/builders/{intl.py => gettext.py} | 5 ++- sphinx/builders/websupport.py | 1 + sphinx/environment.py | 60 ++++++++++++++++++------- sphinx/quickstart.py | 9 ++-- 8 files changed, 63 insertions(+), 28 deletions(-) rename sphinx/builders/{intl.py => gettext.py} (97%) diff --git a/doc/Makefile b/doc/Makefile index 0199ba47c..47951316a 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -8,8 +8,9 @@ PAPER = PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \ - $(SPHINXOPTS) $(O) . +ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \ + $(SPHINXOPTS) $(O) . +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(O) . .PHONY: help clean html dirhtml singlehtml text man pickle json htmlhelp \ qthelp devhelp epub latex latexpdf changes linkcheck doctest @@ -116,7 +117,7 @@ latexpdf: @echo "pdflatex finished; the PDF files are in _build/latex." gettext: - $(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) _build/locale + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) _build/locale @echo @echo "Build finished. The message catalogs are in _build/locale." diff --git a/doc/builders.rst b/doc/builders.rst index 4a95e120e..b44245e97 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -240,7 +240,7 @@ Note that a direct PDF builder using ReportLab is available in `rst2pdf .. versionadded:: 0.5 -.. module:: sphinx.builders.intl +.. module:: sphinx.builders.gettext .. class:: MessageCatalogBuilder This builder produces gettext-style message catalos. Each top-level file or diff --git a/doc/intl.rst b/doc/intl.rst index 3a9e32f29..6a5471c4f 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -32,7 +32,7 @@ task to split up paragraphs which are too large as there is no sane automated way to do that. After Sphinx successfully ran the -:class:`~sphinx.builders.intl.MessageCatalogBuilder` you will find a collection +:class:`~sphinx.builders.gettext.MessageCatalogBuilder` you will find a collection of ``.pot`` files in your output directory. These are **catalog templates** and contain messages in your original language *only*. diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 339540335..5240a1c73 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -31,9 +31,12 @@ class Builder(object): name = '' # builder's output format, or '' if no document output is produced format = '' + # doctree versioning method + versioning_method = 'none' def __init__(self, app): self.env = app.env + self.env.set_versioning_method(self.versioning_method) self.srcdir = app.srcdir self.confdir = app.confdir self.outdir = app.outdir @@ -330,5 +333,5 @@ BUILTIN_BUILDERS = { 'changes': ('changes', 'ChangesBuilder'), 'linkcheck': ('linkcheck', 'CheckExternalLinksBuilder'), 'websupport': ('websupport', 'WebSupportBuilder'), - 'gettext': ('intl', 'MessageCatalogBuilder'), + 'gettext': ('gettext', 'MessageCatalogBuilder'), } diff --git a/sphinx/builders/intl.py b/sphinx/builders/gettext.py similarity index 97% rename from sphinx/builders/intl.py rename to sphinx/builders/gettext.py index 74ba03b54..1ff92360f 100644 --- a/sphinx/builders/intl.py +++ b/sphinx/builders/gettext.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - sphinx.builders.intl - ~~~~~~~~~~~~~~~~~~~~ + sphinx.builders.gettext + ~~~~~~~~~~~~~~~~~~~~~~~ The MessageCatalogBuilder class. @@ -48,6 +48,7 @@ class I18nBuilder(Builder): General i18n builder. """ name = 'i18n' + versioning_method = 'text' def init(self): Builder.init(self) diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py index e8f6aef35..b77573095 100644 --- a/sphinx/builders/websupport.py +++ b/sphinx/builders/websupport.py @@ -26,6 +26,7 @@ class WebSupportBuilder(PickleHTMLBuilder): Builds documents for the web support package. """ name = 'websupport' + versioning_method = 'commentable' def init(self): PickleHTMLBuilder.init(self) diff --git a/sphinx/environment.py b/sphinx/environment.py index 32cc44a8b..75292299c 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -43,6 +43,7 @@ from sphinx.util.nodes import clean_astext, make_refnode, extract_messages from sphinx.util.osutil import movefile, SEP, ustrftime from sphinx.util.matching import compile_matchers from sphinx.util.pycompat import all, class_types +from sphinx.util.websupport import is_commentable from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _, init as init_locale from sphinx.versioning import add_uids, merge_doctrees @@ -79,6 +80,12 @@ default_substitutions = set([ dummy_reporter = Reporter('', 4, 4) +versioning_methods = { + 'none': False, + 'text': nodes.TextElement, + 'commentable': is_commentable, +} + class WarningStream(object): def __init__(self, warnfunc): @@ -313,6 +320,9 @@ class BuildEnvironment: self.srcdir = srcdir self.config = config + # the method of doctree versioning; see set_versioning_method + self.versioning_method = None + # the application object; only set while update() runs self.app = None @@ -380,6 +390,23 @@ class BuildEnvironment: self._warnfunc = func self.settings['warning_stream'] = WarningStream(func) + def set_versioning_method(self, method): + """This sets the doctree versioning method for this environment. + + Versioning methods are a builder property; only builders with the same + versioning method can share the same doctree directory. Therefore, we + raise an exception if the user tries to use an environment with an + incompatible versioning method. + """ + if method not in versioning_methods: + raise ValueError('invalid versioning method: %r' % method) + method = versioning_methods[method] + if self.versioning_method not in (None, method): + raise SphinxError('This environment is incompatible with the ' + 'selected builder, please choose another ' + 'doctree directory.') + self.versioning_method = method + def warn(self, docname, msg, lineno=None): # strange argument order is due to backwards compatibility self._warnfunc(msg, (docname, lineno)) @@ -754,25 +781,24 @@ class BuildEnvironment: # store time of build, for outdated files detection self.all_docs[docname] = time.time() - # get old doctree - old_doctree_path = self.doc2path(docname, self.doctreedir, '.doctree') - try: - f = open(old_doctree_path, 'rb') + if self.versioning_method: + # get old doctree try: - old_doctree = pickle.load(f) - finally: - f.close() - old_doctree.settings.env = self - old_doctree.reporter = Reporter(self.doc2path(docname), 2, 5, - stream=WarningStream(self._warnfunc)) - except EnvironmentError: - old_doctree = None + f = open(self.doc2path(docname, + self.doctreedir, '.doctree'), 'rb') + try: + old_doctree = pickle.load(f) + finally: + f.close() + except EnvironmentError: + old_doctree = None - # add uids for versioning - if old_doctree is None: - list(add_uids(doctree, nodes.TextElement)) - else: - list(merge_doctrees(old_doctree, doctree, nodes.TextElement)) + # add uids for versioning + if old_doctree is None: + list(add_uids(doctree, nodes.TextElement)) + else: + list(merge_doctrees( + old_doctree, doctree, self.versioning_method)) # make it picklable doctree.reporter = None diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 4d7e2db3f..0818ad0a3 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -361,6 +361,8 @@ PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) %(rsrcdir)s +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) %(rsrcdir)s .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp \ epub latex latexpdf text man changes linkcheck doctest gettext @@ -483,7 +485,7 @@ info: \t@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: -\t$(SPHINXBUILD) -b gettext $(ALLSPHINXOPTS) $(BUILDDIR)/locale +\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale \t@echo \t@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." @@ -514,8 +516,10 @@ if "%%SPHINXBUILD%%" == "" ( ) set BUILDDIR=%(rbuilddir)s set ALLSPHINXOPTS=-d %%BUILDDIR%%/doctrees %%SPHINXOPTS%% %(rsrcdir)s +set I18NSPHINXOPTS=%%SPHINXOPTS%% %(rsrcdir)s if NOT "%%PAPER%%" == "" ( \tset ALLSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%ALLSPHINXOPTS%% +\tset I18NSPHINXOPTS=-D latex_paper_size=%%PAPER%% %%I18NSPHINXOPTS%% ) if "%%1" == "" goto help @@ -659,7 +663,7 @@ if "%%1" == "texinfo" ( ) if "%%1" == "gettext" ( -\t%%SPHINXBUILD%% -b gettext %%ALLSPHINXOPTS%% %%BUILDDIR%%/locale +\t%%SPHINXBUILD%% -b gettext %%I18NSPHINXOPTS%% %%BUILDDIR%%/locale \tif errorlevel 1 exit /b 1 \techo. \techo.Build finished. The message catalogs are in %%BUILDDIR%%/locale. @@ -991,4 +995,3 @@ def main(argv=sys.argv): print print '[Interrupted.]' return - From 661101663ae1d3a9838fd28b2811e03ff113b31b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:33:02 +0100 Subject: [PATCH 507/744] Bump env version after addition of versioning_method. --- sphinx/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 75292299c..a87db0e07 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -69,7 +69,7 @@ default_settings = { # This is increased every time an environment attribute is added # or changed to properly invalidate pickle files. -ENV_VERSION = 39 +ENV_VERSION = 40 default_substitutions = set([ From 2524f31069838a9bfa71b728258f2add3e8b3297 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:36:00 +0100 Subject: [PATCH 508/744] Rename attribute to better fit the purpose. --- sphinx/environment.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index a87db0e07..1ffbac64e 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -80,7 +80,7 @@ default_substitutions = set([ dummy_reporter = Reporter('', 4, 4) -versioning_methods = { +versioning_conditions = { 'none': False, 'text': nodes.TextElement, 'commentable': is_commentable, @@ -321,7 +321,7 @@ class BuildEnvironment: self.config = config # the method of doctree versioning; see set_versioning_method - self.versioning_method = None + self.versioning_condition = None # the application object; only set while update() runs self.app = None @@ -398,14 +398,14 @@ class BuildEnvironment: raise an exception if the user tries to use an environment with an incompatible versioning method. """ - if method not in versioning_methods: + if method not in versioning_conditions: raise ValueError('invalid versioning method: %r' % method) - method = versioning_methods[method] - if self.versioning_method not in (None, method): + condition = versioning_conditions[method] + if self.versioning_condition not in (None, condition): raise SphinxError('This environment is incompatible with the ' 'selected builder, please choose another ' 'doctree directory.') - self.versioning_method = method + self.versioning_condition = condition def warn(self, docname, msg, lineno=None): # strange argument order is due to backwards compatibility @@ -781,7 +781,7 @@ class BuildEnvironment: # store time of build, for outdated files detection self.all_docs[docname] = time.time() - if self.versioning_method: + if self.versioning_condition: # get old doctree try: f = open(self.doc2path(docname, @@ -795,10 +795,10 @@ class BuildEnvironment: # add uids for versioning if old_doctree is None: - list(add_uids(doctree, nodes.TextElement)) + list(add_uids(doctree, self.versioning_condition)) else: list(merge_doctrees( - old_doctree, doctree, self.versioning_method)) + old_doctree, doctree, self.versioning_condition)) # make it picklable doctree.reporter = None From a20b68cd3e416d72f236cc1abd797c51e3a1ef08 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:55:34 +0100 Subject: [PATCH 509/744] Refactor warning-emission for unknown references. --- sphinx/environment.py | 57 ++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 1ffbac64e..43e0262bf 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1411,7 +1411,6 @@ class BuildEnvironment: typ = node['reftype'] target = node['reftarget'] refdoc = node.get('refdoc', fromdocname) - warned = False domain = None try: @@ -1428,11 +1427,7 @@ class BuildEnvironment: # directly reference to document by source name; # can be absolute or relative docname = docname_join(refdoc, target) - if docname not in self.all_docs: - self.warn(refdoc, - 'unknown document: %s' % docname, node.line) - warned = True - else: + if docname in self.all_docs: if node['refexplicit']: # reference with explicit title caption = node.astext() @@ -1445,11 +1440,7 @@ class BuildEnvironment: newnode.append(innernode) elif typ == 'citation': docname, labelid = self.citations.get(target, ('', '')) - if not docname: - self.warn(refdoc, - 'citation not found: %s' % target, node.line) - warned = True - else: + if docname: newnode = make_refnode(builder, fromdocname, docname, labelid, contnode) # no new node found? try the missing-reference event @@ -1457,21 +1448,40 @@ class BuildEnvironment: newnode = builder.app.emit_firstresult( 'missing-reference', self, node, contnode) # still not found? warn if in nit-picky mode - if newnode is None and not warned and \ - (self.config.nitpicky or node.get('refwarn')): - if domain and typ in domain.dangling_warnings: - msg = domain.dangling_warnings[typ] - elif node.get('refdomain') != 'std': - msg = '%s:%s reference target not found: ' \ - '%%(target)s' % (node['refdomain'], typ) - else: - msg = '%s reference target not found: ' \ - '%%(target)s' % typ - self.warn(refdoc, msg % {'target': target}, node.line) + if newnode is None: + self._warn_missing_reference( + fromdocname, typ, target, node, domain) except NoUri: newnode = contnode node.replace_self(newnode or contnode) + # remove only-nodes that do not belong to our builder + self.process_only_nodes(doctree, fromdocname, builder) + + # allow custom references to be resolved + builder.app.emit('doctree-resolved', doctree, fromdocname) + + def _warn_missing_reference(self, fromdoc, typ, target, node, domain): + warn = node.get('refwarn') + if self.config.nitpicky: + warn = True # XXX process exceptions here + if not warn: + return + refdoc = node.get('refdoc', fromdoc) + if domain and typ in domain.dangling_warnings: + msg = domain.dangling_warnings[typ] + elif typ == 'doc': + msg = 'unknown document: %(target)s' + elif typ == 'citation': + msg = 'citation not found: %(target)s' + elif node.get('refdomain', 'std') != 'std': + msg = '%s:%s reference target not found: %%(target)s' % \ + (node['refdomain'], typ) + else: + msg = '%s reference target not found: %%(target)s' % typ + self.warn(refdoc, msg % {'target': target}, node.line) + + def process_only_nodes(self, doctree, fromdocname, builder): for node in doctree.traverse(addnodes.only): try: ret = builder.tags.eval_condition(node['expr']) @@ -1487,9 +1497,6 @@ class BuildEnvironment: # if there is a target node before the only node node.replace_self(nodes.comment()) - # allow custom references to be resolved - builder.app.emit('doctree-resolved', doctree, fromdocname) - def assign_section_numbers(self): """Assign a section number to each heading under a numbered toctree.""" # a list of all docnames whose section numbers changed From 8814e973897d6bdaa13279997e016b95111ee8be Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 17:59:44 +0100 Subject: [PATCH 510/744] Always warn on missing :doc: or citation references. --- sphinx/environment.py | 3 ++- sphinx/roles.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/environment.py b/sphinx/environment.py index 43e0262bf..1f8e00f56 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -189,7 +189,8 @@ class CitationReferences(Transform): for citnode in self.document.traverse(nodes.citation_reference): cittext = citnode.astext() refnode = addnodes.pending_xref(cittext, reftype='citation', - reftarget=cittext) + reftarget=cittext, refwarn=True) + refnode.line = citnode.line or citnode.parent.line refnode += nodes.Text('[' + cittext + ']') citnode.parent.replace(citnode, refnode) diff --git a/sphinx/roles.py b/sphinx/roles.py index 1511e35f9..307439146 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -300,7 +300,7 @@ specific_docroles = { # links to download references 'download': XRefRole(nodeclass=addnodes.download_reference), # links to documents - 'doc': XRefRole(), + 'doc': XRefRole(warn_dangling=True), 'pep': indexmarkup_role, 'rfc': indexmarkup_role, From a2934e33b8f051569e7d4f421502e6a184742e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 Jan 2011 18:03:17 +0100 Subject: [PATCH 511/744] Replace unneeded defaultdict with dict --- sphinx/versioning.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index f50f80b0d..74355efed 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -11,7 +11,6 @@ """ from uuid import uuid4 from operator import itemgetter -from collections import defaultdict from sphinx.util.pycompat import product, zip_longest @@ -49,7 +48,7 @@ def merge_doctrees(old, new, condition): new_iter = new.traverse(condition) old_nodes = [] new_nodes = [] - ratios = defaultdict(list) + ratios = {} seen = set() # compare the nodes each doctree in order for old_node, new_node in zip_longest(old_iter, new_iter): From 48d64cc4cb14a540b7318e10de7f07fa8f159229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Sat, 8 Jan 2011 18:06:24 +0100 Subject: [PATCH 512/744] Support for autodocumenting partial functions. --- sphinx/ext/autodoc.py | 40 +++++++++++++++++++++++++++++++++++----- tests/test_autodoc.py | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 044a181f7..7ea3dd803 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -902,15 +902,15 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # cannot introspect arguments of a C function or method pass try: - argspec = inspect.getargspec(self.object) + argspec = getargspec(self.object) except TypeError: # if a class should be documented as function (yay duck # typing) we try to use the constructor signature as function # signature without the first argument. try: - argspec = inspect.getargspec(self.object.__new__) + argspec = getargspec(self.object.__new__) except TypeError: - argspec = inspect.getargspec(self.object.__init__) + argspec = getargspec(self.object.__init__) if argspec[0]: del argspec[0][0] args = inspect.formatargspec(*argspec) @@ -960,7 +960,7 @@ class ClassDocumenter(ModuleLevelDocumenter): (inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): return None try: - argspec = inspect.getargspec(initmeth) + argspec = getargspec(initmeth) except TypeError: # still not possible: happens e.g. for old-style classes # with __init__ in C @@ -1117,7 +1117,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): inspect.ismethoddescriptor(self.object): # can never get arguments of a C function or method return None - argspec = inspect.getargspec(self.object) + argspec = getargspec(self.object) if argspec[0] and argspec[0][0] in ('cls', 'self'): del argspec[0][0] return inspect.formatargspec(*argspec) @@ -1284,6 +1284,36 @@ def add_documenter(cls): AutoDirective._registry[cls.objtype] = cls +if sys.version_info >= (2, 5): + from functools import partial + def getargspec(func): + """Like inspect.getargspec but supports functools.partial as well.""" + if inspect.ismethod(func): + func = func.im_func + parts = 0, () + if type(func) is partial: + parts = len(func.args), func.keywords.keys() + func = func.func + if not inspect.isfunction(func): + raise TypeError('{!r} is not a Python function'.format(func)) + args, varargs, varkw = inspect.getargs(func.func_code) + func_defaults = func.func_defaults + if func_defaults: + func_defaults = list(func_defaults) + if parts[0]: + args = args[parts[0]:] + if parts[1]: + for arg in parts[1]: + i = args.index(arg) - len(args) + del args[i] + try: + del func_defaults[i] + except IndexError: + pass + return inspect.ArgSpec(args, varargs, varkw, func_defaults) +else: + getargspec = inspect.getargspec + def setup(app): app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ClassDocumenter) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 3e9b4f25c..b0da900eb 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -10,6 +10,9 @@ :license: BSD, see LICENSE for details. """ +import sys +from StringIO import StringIO + from util import * from docutils.statemachine import ViewList @@ -17,7 +20,6 @@ from docutils.statemachine import ViewList from sphinx.ext.autodoc import AutoDirective, add_documenter, \ ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL -from StringIO import StringIO def setup_module(): global app, lid, options, directive @@ -422,12 +424,14 @@ def test_generate(): ('attribute', 'test_autodoc.Class.udocattr'), ('attribute', 'test_autodoc.Class.mdocattr'), ('attribute', 'test_autodoc.Class.inst_attr_comment'), - ('attribute', 'test_autodoc.Class.inst_attr_string') + ('attribute', 'test_autodoc.Class.inst_attr_string'), + ('method', 'test_autodoc.Class.moore'), ]) options.members = ALL assert_processes(should, 'class', 'Class') options.undoc_members = True - should.append(('method', 'test_autodoc.Class.undocmeth')) + should.extend((('method', 'test_autodoc.Class.undocmeth'), + ('method', 'test_autodoc.Class.roger'))) assert_processes(should, 'class', 'Class') options.inherited_members = True should.append(('method', 'test_autodoc.Class.inheritedmeth')) @@ -490,6 +494,8 @@ def test_generate(): ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', + ' .. py:classmethod:: Class.roger(a, e=5, f=6)', + ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_string', ' .. py:method:: Class.inheritedmeth()', @@ -509,6 +515,9 @@ def test_generate(): 'test_autodoc.DocstringSig.meth') assert_result_contains( ' rest of docstring', 'method', 'test_autodoc.DocstringSig.meth') + assert_result_contains( + '.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method', + 'test_autodoc.Class.moore') # --- generate fodder ------------ @@ -534,6 +543,21 @@ class CustomDataDescriptor(object): return self return 42 +def _funky_classmethod(name, b, c, d, docstring=None): + """Generates a classmethod for a class from a template by filling out + some arguments.""" + def template(cls, a, b, c, d=4, e=5, f=6): + return a, b, c, d, e, f + if sys.version_info >= (2, 5): + from functools import partial + function = partial(template, b=b, c=c, d=d) + else: + def function(cls, a, e=5, f=6): + return template(a, b, c, d, e, f) + function.__name__ = name + function.__doc__ = docstring + return classmethod(function) + class Base(object): def inheritedmeth(self): """Inherited function.""" @@ -576,6 +600,11 @@ class Class(Base): mdocattr = StringIO() """should be documented as well - süß""" + roger = _funky_classmethod("roger", 2, 3, 4) + + moore = _funky_classmethod("moore", 9, 8, 7, + docstring="moore(a, e, f) -> happiness") + def __init__(self, arg): #: a documented instance attribute self.inst_attr_comment = None From 1961a4aaa83914d317d32efb23cc3d6ca297377a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 18:06:38 +0100 Subject: [PATCH 513/744] Fix typo in name. --- doc/theming.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/theming.rst b/doc/theming.rst index 216118c1b..4892299f0 100644 --- a/doc/theming.rst +++ b/doc/theming.rst @@ -179,7 +179,7 @@ These themes are: *nosidebar*. * **pyramid** -- A theme from the Pyramid web framework project, designed by - Blais Laflamme. THere are currently no options beyond *nosidebar*. + Blaise Laflamme. THere are currently no options beyond *nosidebar*. * **haiku** -- A theme without sidebar inspired by the `Haiku OS user guide `_. The following From 332fb3eaea219324a062d6106782f22be3e4853b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Sat, 8 Jan 2011 18:24:50 +0100 Subject: [PATCH 514/744] Import all from pycompat --- sphinx/versioning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 74355efed..d45ed1d3f 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -12,7 +12,7 @@ from uuid import uuid4 from operator import itemgetter -from sphinx.util.pycompat import product, zip_longest +from sphinx.util.pycompat import product, zip_longest, all # anything below that ratio is considered equal/changed From f5ecd62d05fa5502795e31ac5cc8778c570269cd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 18:36:52 +0100 Subject: [PATCH 515/744] #537: Added :confval:`nitpick_ignore`. --- CHANGES | 2 ++ doc/config.rst | 8 ++++++++ sphinx/config.py | 1 + sphinx/environment.py | 11 +++++++++-- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 52a962d2d..07b543783 100644 --- a/CHANGES +++ b/CHANGES @@ -83,6 +83,8 @@ Release 1.1 (in development) * #590: Added ``caption`` option to graphviz directives. +* #537: Added :confval:`nitpick_ignore`. + * C++ domain now supports array definitions. diff --git a/doc/config.rst b/doc/config.rst index a1f550ddf..368674692 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -232,6 +232,14 @@ General configuration .. versionadded:: 1.0 +.. confval:: nitpick_ignore + + A list of ``(type, target)`` tuples (by default empty) that should be ignored + when generating warnings in "nitpicky mode". Note that ``type`` should + include the domain name. An example entry would be ``('py:func', 'int')``. + + .. versionadded:: 1.1 + Project information ------------------- diff --git a/sphinx/config.py b/sphinx/config.py index 90c4b5627..f4c0946b5 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -71,6 +71,7 @@ class Config(object): primary_domain = ('py', 'env'), needs_sphinx = (None, None), nitpicky = (False, 'env'), + nitpick_ignore = ([], 'env'), # HTML options html_theme = ('default', 'html'), diff --git a/sphinx/environment.py b/sphinx/environment.py index 1f8e00f56..0867eb74e 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -69,7 +69,7 @@ default_settings = { # This is increased every time an environment attribute is added # or changed to properly invalidate pickle files. -ENV_VERSION = 40 +ENV_VERSION = 41 default_substitutions = set([ @@ -340,6 +340,9 @@ class BuildEnvironment: # this is to invalidate old pickles self.version = ENV_VERSION + # make this a set for faster testing + self._nitpick_ignore = set(self.config.nitpick_ignore) + # All "docnames" here are /-separated and relative and exclude # the source suffix. @@ -1465,7 +1468,11 @@ class BuildEnvironment: def _warn_missing_reference(self, fromdoc, typ, target, node, domain): warn = node.get('refwarn') if self.config.nitpicky: - warn = True # XXX process exceptions here + warn = True + if self._nitpick_ignore: + dtype = domain and '%s:%s' % (domain.name, typ) or typ + if (dtype, target) in self._nitpick_ignore: + warn = False if not warn: return refdoc = node.get('refdoc', fromdoc) From 87dc9b302e4d3e03851b3d94ca2a86000280f99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Sat, 8 Jan 2011 18:37:49 +0100 Subject: [PATCH 516/744] Removed unnecessary \ --- sphinx/ext/autodoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 928a943f3..61b08273a 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -27,7 +27,7 @@ from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.application import ExtensionError from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.compat import Directive -from sphinx.util.inspect import (getargspec, isdescriptor, safe_getmembers, \ +from sphinx.util.inspect import (getargspec, isdescriptor, safe_getmembers, safe_getattr) from sphinx.util.pycompat import base_exception, class_types from sphinx.util.docstrings import prepare_docstring From 7e61f4c4d526b24843636f199f1f75874b8f9a53 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 18:45:07 +0100 Subject: [PATCH 517/744] Add changelog entry and credits. --- AUTHORS | 1 + CHANGES | 3 +++ 2 files changed, 4 insertions(+) diff --git a/AUTHORS b/AUTHORS index 44bbbcd42..acee46279 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,6 +19,7 @@ Other contributors, listed alphabetically, are: * Dave Kuhlman -- original LaTeX writer * Blaise Laflamme -- pyramid theme * Thomas Lamb -- linkcheck builder +* Łukasz Langa -- partial support for autodoc * Robert Lehmann -- gettext builder (GSOC project) * Dan MacKinlay -- metadata fixes * Martin Mahner -- nature theme diff --git a/CHANGES b/CHANGES index 07b543783..20d90d57e 100644 --- a/CHANGES +++ b/CHANGES @@ -85,6 +85,9 @@ Release 1.1 (in development) * #537: Added :confval:`nitpick_ignore`. +* autodoc now supports documenting the signatures of ``functools.partial`` + objects. + * C++ domain now supports array definitions. From 7bb0fa5006d36a182fb84f52957fd67397b858ae Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 18:53:34 +0100 Subject: [PATCH 518/744] #160: separate latex rows with horizontal lines like in other builders too. --- sphinx/writers/latex.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index c557101be..2b24a35ac 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -701,21 +701,21 @@ class LaTeXTranslator(nodes.NodeVisitor): if self.next_table_colspec: self.table.colspec = '{%s}\n' % self.next_table_colspec self.next_table_colspec = None -# self.body.append('\\hline\n') -# self.table.had_head = True + # self.body.append('\\hline\n') + # self.table.had_head = True def depart_thead(self, node): - self.body.append('\\hline\n') + pass def visit_tbody(self, node): if not self.table.had_head: self.visit_thead(node) def depart_tbody(self, node): - self.body.append('\\hline\n') + pass def visit_row(self, node): self.table.col = 0 def depart_row(self, node): - self.body.append('\\\\\n') + self.body.append('\\\\\\hline\n') self.table.rowcount += 1 def visit_entry(self, node): From 9088c2ffc278a5a763a5d1227466baaec4e05c28 Mon Sep 17 00:00:00 2001 From: Herbert Griebel Date: Tue, 15 Jun 2010 22:51:04 +0200 Subject: [PATCH 519/744] add option pngmath_use_tooltips This option will show the latex formula code in the tooltips if enabled, default is True. --- sphinx/ext/pngmath.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py index 015a4904d..37b4d3239 100644 --- a/sphinx/ext/pngmath.py +++ b/sphinx/ext/pngmath.py @@ -178,6 +178,11 @@ def cleanup_tempdir(app, exc): except Exception: pass +def get_tooltip(self, node): + if self.builder.config.pngmath_use_tooltips : + return ' alt="%s"' % self.encode(node['latex']).strip() + return '' + def html_visit_math(self, node): try: fname, depth = render_math(self, '$'+node['latex']+'$') @@ -193,15 +198,12 @@ def html_visit_math(self, node): self.body.append('%s' % self.encode(node['latex']).strip()) else: - if depth is None: - self.body.append( - '%s' % - (fname, self.encode(node['latex']).strip())) - else: - self.body.append( - '%s' % - (fname, self.encode(node['latex']).strip(), -depth)) + c = '%s

                \n
                ' % self.encode(node['latex']).strip()) else: - self.body.append('%s

                \n
                ' % - (fname, self.encode(node['latex']).strip())) + c = ' Date: Fri, 2 Jul 2010 11:33:36 +0200 Subject: [PATCH 520/744] #462: another qthelp namespace fix. --- sphinx/builders/qthelp.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 89bda0dcf..51785c9d6 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -164,6 +164,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder): nspace = 'org.sphinx.%s.%s' % (outname, self.config.version) nspace = re.sub('[^a-zA-Z0-9.]', '', nspace) nspace = re.sub(r'\.+', '.', nspace).strip('.') + nspace = nspace.lower() # write the project file f = codecs.open(path.join(outdir, outname+'.qhp'), 'w', 'utf-8') From 611887a4f9df8e1c6418811ded1ccd50f0ab20f0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 26 Jul 2010 19:31:07 +0200 Subject: [PATCH 521/744] Add docutils 0.7 target. --- tox.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 95a877274..2d9d18f85 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=du06,du05 +envlist=du07,du06,du05 [testenv] deps=nose @@ -12,3 +12,6 @@ deps=docutils==0.5 [testenv:du06] deps=docutils==0.6 + +[testenv:du07] +deps=docutils==0.7 From bf71b0b2fad1a802d0cb4a23aa9dfa568fbd9393 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 26 Jul 2010 19:32:44 +0200 Subject: [PATCH 522/744] Fix some naming issues in intersphinx when given an explicit prefix. --- sphinx/ext/intersphinx.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 4fb3805f9..32226c2ac 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -191,10 +191,12 @@ def missing_reference(app, env, node, contnode): return objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes] to_try = [(env.intersphinx_inventory, target)] + in_set = None if ':' in target: # first part may be the foreign doc set name setname, newtarget = target.split(':', 1) if setname in env.intersphinx_named_inventory: + in_set = setname to_try.append((env.intersphinx_named_inventory[setname], newtarget)) for inventory, target in to_try: for objtype in objtypes: @@ -204,10 +206,13 @@ def missing_reference(app, env, node, contnode): newnode = nodes.reference('', '', internal=False, refuri=uri, reftitle='(in %s v%s)' % (proj, version)) if dispname == '-': - newnode.append(contnode) - else: - newnode.append(contnode.__class__(dispname, dispname)) + dispname = target + newnode.append(contnode.__class__(dispname, dispname)) return newnode + # at least get rid of the ':' in the target + if in_set is not None: + if len(contnode) and isinstance(contnode[0], nodes.Text): + contnode[0] = nodes.Text(newtarget, contnode[0].rawsource) def setup(app): From 0aafe2ac1ddd3078ff541a98fa44b3926072a3b0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 13:20:58 +0200 Subject: [PATCH 523/744] Further fix for intersphinx labels, add test cases for that. --- CHANGES | 3 +++ sphinx/ext/intersphinx.py | 4 ++-- tests/test_intersphinx.py | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index c89621035..608dcaa25 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.1 (in development) ============================== +* Fix display names for objects linked to by intersphinx with + explicit targets. + * Fix building with the JSON builder. * Fix hyperrefs in object descriptions for LaTeX. diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 32226c2ac..0a210879a 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -209,8 +209,8 @@ def missing_reference(app, env, node, contnode): dispname = target newnode.append(contnode.__class__(dispname, dispname)) return newnode - # at least get rid of the ':' in the target - if in_set is not None: + # at least get rid of the ':' in the target if no explicit title given + if in_set is not None and not node.get('refexplicit', True): if len(contnode) and isinstance(contnode[0], nodes.Text): contnode[0] = nodes.Text(newtarget, contnode[0].rawsource) diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py index 622243e60..8b6547e54 100644 --- a/tests/test_intersphinx.py +++ b/tests/test_intersphinx.py @@ -80,7 +80,10 @@ def test_read_inventory_v2(): def test_missing_reference(tempdir, app): inv_file = tempdir / 'inventory' write_file(inv_file, inventory_v2) - app.config.intersphinx_mapping = {'http://docs.python.org/': inv_file} + app.config.intersphinx_mapping = { + 'http://docs.python.org/': inv_file, + 'py3k': ('http://docs.python.org/py3k/', inv_file), + } app.config.intersphinx_cache_limit = 0 # load the inventory and check if it's done correctly @@ -91,7 +94,7 @@ def test_missing_reference(tempdir, app): ('foo', '2.0', 'http://docs.python.org/foo.html#module-module2', '-') # create fake nodes and check referencing - contnode = nodes.emphasis('foo') + contnode = nodes.emphasis('foo', 'foo') refnode = addnodes.pending_xref('') refnode['reftarget'] = 'module1.func' refnode['reftype'] = 'func' @@ -101,7 +104,7 @@ def test_missing_reference(tempdir, app): assert isinstance(rn, nodes.reference) assert rn['refuri'] == 'http://docs.python.org/sub/foo.html#module1.func' assert rn['reftitle'] == '(in foo v2.0)' - assert rn[0] is contnode + assert rn[0].astext() == 'module1.func' # create unresolvable nodes and check None return value refnode['reftype'] = 'foo' @@ -110,3 +113,27 @@ def test_missing_reference(tempdir, app): refnode['reftype'] = 'function' refnode['reftarget'] = 'foo.func' assert missing_reference(app, app.env, refnode, contnode) is None + + # check handling of prefixes + + # prefix given, target found: prefix is stripped + refnode['reftype'] = 'mod' + refnode['reftarget'] = 'py3k:module2' + rn = missing_reference(app, app.env, refnode, contnode) + assert rn[0].astext() == 'module2' + + # prefix given, target not found and nonexplicit title: prefix is stripped + refnode['reftarget'] = 'py3k:unknown' + refnode['refexplicit'] = False + contnode[0] = nodes.Text('py3k:unknown') + rn = missing_reference(app, app.env, refnode, contnode) + assert rn is None + assert contnode[0].astext() == 'unknown' + + # prefix given, target not found and explicit title: nothing is changed + refnode['reftarget'] = 'py3k:unknown' + refnode['refexplicit'] = True + contnode[0] = nodes.Text('py3k:unknown') + rn = missing_reference(app, app.env, refnode, contnode) + assert rn is None + assert contnode[0].astext() == 'py3k:unknown' From 8c0c728d1bee97e3c286784db062067175a24269 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 13:27:00 +0200 Subject: [PATCH 524/744] Fix building with SingleHTMLBuilder when there is no toctree. --- CHANGES | 2 ++ sphinx/builders/html.py | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 608dcaa25..6fbe98ebc 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Release 1.0.1 (in development) ============================== +* Fix building with SingleHTMLBuilder when there is no toctree. + * Fix display names for objects linked to by intersphinx with explicit targets. diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index bbfe67e22..8d96c146c 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -847,8 +847,14 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): def get_doc_context(self, docname, body, metatags): # no relation links... toc = self.env.get_toctree_for(self.config.master_doc, self, False) - self.fix_refuris(toc) - toc = self.render_partial(toc)['fragment'] + # if there is no toctree, toc is None + if toc: + self.fix_refuris(toc) + toc = self.render_partial(toc)['fragment'] + display_toc = True + else: + toc = '' + display_toc = False return dict( parents = [], prev = None, @@ -861,7 +867,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): rellinks = [], sourcename = '', toc = toc, - display_toc = True, + display_toc = display_toc, ) def write(self, *ignored): From 954e8d0220866151ca2e57cf90885c008fc4ae35 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 14:28:31 +0200 Subject: [PATCH 525/744] #474: Fix javascript domain parsing of object names. --- CHANGES | 4 +++- sphinx/domains/javascript.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 6fbe98ebc..883d1e0bc 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,9 @@ Release 1.0.1 (in development) ============================== -* Fix building with SingleHTMLBuilder when there is no toctree. +* #473: Fix a bug in parsing JavaScript object names. + +* #474: Fix building with SingleHTMLBuilder when there is no toctree. * Fix display names for objects linked to by intersphinx with explicit targets. diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 890af8e14..9934dba3a 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -31,6 +31,7 @@ class JSObject(ObjectDescription): display_prefix = None def handle_signature(self, sig, signode): + import pdb; pdb.set_trace() sig = sig.strip() if '(' in sig and sig[-1:] == ')': prefix, arglist = sig.split('(', 1) @@ -56,7 +57,7 @@ class JSObject(ObjectDescription): else: # just a function or constructor objectname = '' - fullname = '' + fullname = name signode['object'] = objectname signode['fullname'] = fullname From 2380957682d2bff6495ef7bca59f96d9ef62ed7c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 14:36:47 +0200 Subject: [PATCH 526/744] #266: Add Bengali locale, thanks to Nasimul Haque. --- CHANGES | 2 + doc/config.rst | 1 + sphinx/locale/bn/LC_MESSAGES/sphinx.js | 1 + sphinx/locale/bn/LC_MESSAGES/sphinx.mo | Bin 0 -> 12509 bytes sphinx/locale/bn/LC_MESSAGES/sphinx.po | 698 +++++++++++++++++++++++++ 5 files changed, 702 insertions(+) create mode 100644 sphinx/locale/bn/LC_MESSAGES/sphinx.js create mode 100644 sphinx/locale/bn/LC_MESSAGES/sphinx.mo create mode 100644 sphinx/locale/bn/LC_MESSAGES/sphinx.po diff --git a/CHANGES b/CHANGES index 883d1e0bc..8781d0e2a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Release 1.0.1 (in development) ============================== +* #266: Add Bengali language. + * #473: Fix a bug in parsing JavaScript object names. * #474: Fix building with SingleHTMLBuilder when there is no toctree. diff --git a/doc/config.rst b/doc/config.rst index 8084580c4..bf8ad3c2d 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -283,6 +283,7 @@ Project information Currently supported languages are: + * ``bn`` -- Bengali * ``ca`` -- Catalan * ``cs`` -- Czech * ``da`` -- Danish diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.js b/sphinx/locale/bn/LC_MESSAGES/sphinx.js new file mode 100644 index 000000000..277cd3d03 --- /dev/null +++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.js @@ -0,0 +1 @@ +Documentation.addTranslations({"locale": "bn", "plural_expr": "(n != 1)", "messages": {"Search Results": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09ab\u09b2\u09be\u09ab\u09b2", "Preparing search...": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09aa\u09cd\u09b0\u09b8\u09cd\u09a4\u09c1\u09a4\u09bf \u099a\u09b2\u099b\u09c7...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7 \u0995\u09c7\u09be\u09a8 \u09ab\u09b2\u09be\u09ab\u09b2 \u09aa\u09be\u0993\u09df\u09be \u09af\u09be\u09df\u09a8\u09bf\u0964 \u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09b6\u09ac\u09cd\u09a6\u0997\u09c1\u09b2\u09c7\u09be\u09b0 \u09b8\u09a0\u09bf\u0995 \u09ac\u09be\u09a8\u09be\u09a8 \u0993 \u09ac\u09bf\u09ad\u09be\u0997 \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09a8 \u09a8\u09bf\u09b6\u09cd\u099a\u09bf\u09a4 \u0995\u09b0\u09c1\u09a8\u0964", "Search finished, found %s page(s) matching the search query.": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u09b6\u09c7\u09b7 \u09b9\u09df\u09c7\u099b\u09c7, \u09ab\u09b2\u09be\u09ab\u09b2\u09c7 %s-\u099f\u09bf \u09aa\u09be\u09a4\u09be \u09aa\u09be\u0993\u09df\u09be \u0997\u09c7\u099b\u09c7\u0964", ", in ": ", -", "Permalink to this headline": "\u098f\u0987 \u09b6\u09bf\u09b0\u09c7\u09be\u09a8\u09be\u09ae\u09c7\u09b0 \u09aa\u09be\u09b0\u09cd\u09ae\u09be\u09b2\u09bf\u0999\u09cd\u0995", "Searching": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u099a\u09b2\u099b\u09c7", "Permalink to this definition": "\u098f\u0987 \u09b8\u0982\u099c\u09cd\u099e\u09be\u09b0 \u09aa\u09be\u09b0\u09cd\u09ae\u09be\u09b2\u09bf\u0999\u09cd\u0995", "Hide Search Matches": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09ae\u09cd\u09af\u09be\u099a\u0997\u09c1\u09b2\u09c7\u09be \u09b2\u09c1\u0995\u09be\u09a8"}}); \ No newline at end of file diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.mo b/sphinx/locale/bn/LC_MESSAGES/sphinx.mo new file mode 100644 index 0000000000000000000000000000000000000000..9b60397e2f33f760d5a6e098877fe7eab7663ef8 GIT binary patch literal 12509 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 literal 0 HcmV?d00001 diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.po b/sphinx/locale/bn/LC_MESSAGES/sphinx.po new file mode 100644 index 000000000..d5141c797 --- /dev/null +++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.po @@ -0,0 +1,698 @@ +# Translations template for Sphinx. +# Copyright (C) 2009 ORGANIZATION +# This file is distributed under the same license as the Sphinx project. +# FIRST AUTHOR , 2009. +# +msgid "" +msgstr "" +"Project-Id-Version: Sphinx 1.0pre/[?1034h2e1ab15e035e\n" +"Report-Msgid-Bugs-To: nasim.haque@gmail.com\n" +"POT-Creation-Date: 2009-11-08 16:28+0100\n" +"PO-Revision-Date: 2009-11-10 13:42+0100\n" +"Last-Translator: Nasimul Haque \n" +"Language-Team: Nasimul Haque \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.4\n" + +#: sphinx/environment.py:130 +#: sphinx/writers/latex.py:184 +#, python-format +msgid "%B %d, %Y" +msgstr "%B %d, %Y" + +#: sphinx/environment.py:348 +#: sphinx/themes/basic/genindex-single.html:2 +#: sphinx/themes/basic/genindex-split.html:2 +#: sphinx/themes/basic/genindex-split.html:5 +#: sphinx/themes/basic/genindex.html:2 +#: sphinx/themes/basic/genindex.html:5 +#: sphinx/themes/basic/genindex.html:48 +#: sphinx/themes/basic/layout.html:134 +#: sphinx/writers/latex.py:190 +msgid "Index" +msgstr "ইনডেক্স" + +#: sphinx/environment.py:349 +#: sphinx/writers/latex.py:189 +msgid "Module Index" +msgstr "মডিউল ইনডেক্স" + +#: sphinx/environment.py:350 +#: sphinx/themes/basic/defindex.html:16 +msgid "Search Page" +msgstr "অনুসন্ধান পাতা" + +#: sphinx/roles.py:167 +#, python-format +msgid "Python Enhancement Proposals!PEP %s" +msgstr "পাইথন উন্নয়ন পরামর্শ!PEP %s" + +#: sphinx/builders/changes.py:70 +msgid "Builtins" +msgstr "বিল্টইন সমূহ" + +#: sphinx/builders/changes.py:72 +msgid "Module level" +msgstr "মডিউল লেভেল" + +#: sphinx/builders/html.py:224 +#, python-format +msgid "%b %d, %Y" +msgstr "%b %d, %Y" + +#: sphinx/builders/html.py:243 +#: sphinx/themes/basic/defindex.html:21 +msgid "General Index" +msgstr "সাধারণ ইনডেক্স" + +#: sphinx/builders/html.py:243 +msgid "index" +msgstr "ইনডেক্স" + +#: sphinx/builders/html.py:247 +#: sphinx/builders/htmlhelp.py:220 +#: sphinx/builders/qthelp.py:133 +#: sphinx/themes/basic/defindex.html:19 +#: sphinx/themes/basic/modindex.html:2 +#: sphinx/themes/basic/modindex.html:13 +#: sphinx/themes/scrolls/modindex.html:2 +#: sphinx/themes/scrolls/modindex.html:13 +msgid "Global Module Index" +msgstr "গ্লোবাল মডিউল ইনডেক্স" + +#: sphinx/builders/html.py:248 +msgid "modules" +msgstr "মডিউল সমূহ" + +#: sphinx/builders/html.py:304 +msgid "next" +msgstr "পরবর্তী" + +#: sphinx/builders/html.py:313 +msgid "previous" +msgstr "পূর্ববর্তী" + +#: sphinx/builders/latex.py:162 +msgid " (in " +msgstr "(-" + +#: sphinx/directives/__init__.py:78 +#: sphinx/directives/__init__.py:79 +#: sphinx/directives/__init__.py:80 +#: sphinx/directives/__init__.py:81 +msgid "Raises" +msgstr "রেইজেস" + +#: sphinx/directives/__init__.py:82 +#: sphinx/directives/__init__.py:83 +#: sphinx/directives/__init__.py:84 +msgid "Variable" +msgstr "ভ্যারিয়েবল" + +#: sphinx/directives/__init__.py:85 +#: sphinx/directives/__init__.py:86 +#: sphinx/directives/__init__.py:92 +#: sphinx/directives/__init__.py:93 +msgid "Returns" +msgstr "রিটার্নস" + +#: sphinx/directives/__init__.py:94 +msgid "Return type" +msgstr "রিটার্ন টাইপ" + +#: sphinx/directives/__init__.py:169 +msgid "Parameter" +msgstr "প্যারামিটার" + +#: sphinx/directives/__init__.py:173 +msgid "Parameters" +msgstr "প্যারামিটার" + +#: sphinx/directives/other.py:127 +msgid "Section author: " +msgstr "অনুচ্ছেদ লেখক:" + +#: sphinx/directives/other.py:129 +msgid "Module author: " +msgstr "মডিউল লেখক:" + +#: sphinx/directives/other.py:131 +msgid "Author: " +msgstr "লেখক:" + +#: sphinx/directives/other.py:233 +msgid "See also" +msgstr "আরও দেখুন" + +#: sphinx/domains/c.py:124 +#, python-format +msgid "%s (C function)" +msgstr "%s (C ফাংশন)" + +#: sphinx/domains/c.py:126 +#, python-format +msgid "%s (C member)" +msgstr "%s (C মেম্বার)" + +#: sphinx/domains/c.py:128 +#, python-format +msgid "%s (C macro)" +msgstr "%s (C ম্যাক্রো)" + +#: sphinx/domains/c.py:130 +#, python-format +msgid "%s (C type)" +msgstr "%s (C টাইপ)" + +#: sphinx/domains/c.py:132 +#, python-format +msgid "%s (C variable)" +msgstr "%s (C ভ্যারিয়েবল)" + +#: sphinx/domains/c.py:162 +msgid "C function" +msgstr "C ফাংশন" + +#: sphinx/domains/c.py:163 +msgid "C member" +msgstr "C মেম্বার" + +#: sphinx/domains/c.py:164 +msgid "C macro" +msgstr "C ম্যাক্রো" + +#: sphinx/domains/c.py:165 +msgid "C type" +msgstr "C টাইপ" + +#: sphinx/domains/c.py:166 +msgid "C variable" +msgstr "C ভ্যারিয়েবল" + +#: sphinx/domains/python.py:186 +#, python-format +msgid "%s() (built-in function)" +msgstr "%s() (বিল্ট-ইন ফাংশন)" + +#: sphinx/domains/python.py:187 +#: sphinx/domains/python.py:244 +#: sphinx/domains/python.py:256 +#: sphinx/domains/python.py:269 +#, python-format +msgid "%s() (in module %s)" +msgstr "%s() (%s মডিউলে)" + +#: sphinx/domains/python.py:190 +#, python-format +msgid "%s (built-in variable)" +msgstr "%s (বিল্ট-ইন ভ্যারিয়েবল)" + +#: sphinx/domains/python.py:191 +#: sphinx/domains/python.py:282 +#, python-format +msgid "%s (in module %s)" +msgstr "%s (%s মডিউলে)" + +#: sphinx/domains/python.py:207 +#, python-format +msgid "%s (built-in class)" +msgstr "%s (বিল্ট-ইন ক্লাস)" + +#: sphinx/domains/python.py:208 +#, python-format +msgid "%s (class in %s)" +msgstr "%s (%s ক্লাসে)" + +#: sphinx/domains/python.py:248 +#, python-format +msgid "%s() (%s.%s method)" +msgstr "%s (%s.%s মেথড)" + +#: sphinx/domains/python.py:250 +#, python-format +msgid "%s() (%s method)" +msgstr "%s() (%s মেথড)" + +#: sphinx/domains/python.py:260 +#, python-format +msgid "%s() (%s.%s static method)" +msgstr "%s (%s.%s স্ট্যাটিক মেথড)" + +#: sphinx/domains/python.py:263 +#, python-format +msgid "%s() (%s static method)" +msgstr "%s() (%s স্ট্যাটিক মেথড)" + +#: sphinx/domains/python.py:273 +#, python-format +msgid "%s() (%s.%s class method)" +msgstr "%s() (%s.%s ক্লাস মেথড)" + +#: sphinx/domains/python.py:276 +#, python-format +msgid "%s() (%s class method)" +msgstr "%s() (%s ক্লাস মেথড)" + +#: sphinx/domains/python.py:286 +#, python-format +msgid "%s (%s.%s attribute)" +msgstr "%s (%s.%s এ্যট্রিবিউট)" + +#: sphinx/domains/python.py:288 +#, python-format +msgid "%s (%s attribute)" +msgstr "%s (%s এ্যট্রিবিউট)" + +#: sphinx/domains/python.py:334 +msgid "Platforms: " +msgstr "প্লাটফরম:" + +#: sphinx/domains/python.py:340 +#, python-format +msgid "%s (module)" +msgstr "%s (মডিউল)" + +#: sphinx/domains/python.py:396 +msgid "function" +msgstr "ফাংশন" + +#: sphinx/domains/python.py:397 +msgid "data" +msgstr "ডাটা" + +#: sphinx/domains/python.py:398 +msgid "class" +msgstr "ক্লাস" + +#: sphinx/domains/python.py:399 +#: sphinx/locale/__init__.py:161 +msgid "exception" +msgstr "এক্সেপশন" + +#: sphinx/domains/python.py:400 +msgid "method" +msgstr "মেথড" + +#: sphinx/domains/python.py:401 +msgid "attribute" +msgstr "এ্যট্রিবিউট" + +#: sphinx/domains/python.py:402 +#: sphinx/locale/__init__.py:157 +msgid "module" +msgstr "মডিউল" + +#: sphinx/domains/std.py:67 +#: sphinx/domains/std.py:83 +#, python-format +msgid "environment variable; %s" +msgstr "এনভায়রনমেন্ট ভ্যারিয়েবল; %s" + +#: sphinx/domains/std.py:156 +#, python-format +msgid "%scommand line option; %s" +msgstr "%sকমান্ড লাইন অপশন; %s" + +#: sphinx/domains/std.py:324 +msgid "glossary term" +msgstr "শব্দকোষ" + +#: sphinx/domains/std.py:325 +msgid "grammar token" +msgstr "ব্যকরণ টোকেন" + +#: sphinx/domains/std.py:326 +msgid "environment variable" +msgstr "এনভায়রনমেন্ট ভ্যারিয়েবল" + +#: sphinx/domains/std.py:327 +msgid "program option" +msgstr "প্রোগ্রাম অপশন" + +#: sphinx/ext/autodoc.py:892 +#, python-format +msgid " Bases: %s" +msgstr "বেস: %s" + +#: sphinx/ext/autodoc.py:925 +#, python-format +msgid "alias of :class:`%s`" +msgstr ":class:`%s` এর উপনাম" + +#: sphinx/ext/todo.py:40 +msgid "Todo" +msgstr "অসমাপ্ত কাজ" + +#: sphinx/ext/todo.py:98 +#, python-format +msgid "(The original entry is located in %s, line %d and can be found " +msgstr "(%s, %d লাইনে মূল অন্তর্ভুক্তিটি রয়েছে, যা পাওয়া যাবে" + +#: sphinx/ext/todo.py:104 +msgid "here" +msgstr "এখানে" + +#: sphinx/locale/__init__.py:138 +msgid "Attention" +msgstr "দৃষ্টি আকর্ষণ" + +#: sphinx/locale/__init__.py:139 +msgid "Caution" +msgstr "সতর্কীকরণ" + +#: sphinx/locale/__init__.py:140 +msgid "Danger" +msgstr "বিপজ্জনক" + +#: sphinx/locale/__init__.py:141 +msgid "Error" +msgstr "ভুল (এরর)" + +#: sphinx/locale/__init__.py:142 +msgid "Hint" +msgstr "আভাস" + +#: sphinx/locale/__init__.py:143 +msgid "Important" +msgstr "গুরুত্বপূর্ণ" + +#: sphinx/locale/__init__.py:144 +msgid "Note" +msgstr "নোট" + +#: sphinx/locale/__init__.py:145 +msgid "See Also" +msgstr "আরও দেখুন" + +#: sphinx/locale/__init__.py:146 +msgid "Tip" +msgstr "পরামর্শ" + +#: sphinx/locale/__init__.py:147 +msgid "Warning" +msgstr "সতর্কতা" + +#: sphinx/locale/__init__.py:151 +#, python-format +msgid "New in version %s" +msgstr "%s ভার্সনে নতুন" + +#: sphinx/locale/__init__.py:152 +#, python-format +msgid "Changed in version %s" +msgstr "%s ভার্সনে পরিবর্তিত" + +#: sphinx/locale/__init__.py:153 +#, python-format +msgid "Deprecated since version %s" +msgstr "%s ভার্সন থেকে ডেপ্রিকেটেড" + +#: sphinx/locale/__init__.py:158 +msgid "keyword" +msgstr "কিওয়ার্ড" + +#: sphinx/locale/__init__.py:159 +msgid "operator" +msgstr "অপারেটর" + +#: sphinx/locale/__init__.py:160 +msgid "object" +msgstr "অবজেক্ট" + +#: sphinx/locale/__init__.py:162 +msgid "statement" +msgstr "স্ট্যাটমেন্ট" + +#: sphinx/locale/__init__.py:163 +msgid "built-in function" +msgstr "বিল্ট-ইন ফাংশন" + +#: sphinx/themes/basic/defindex.html:2 +msgid "Overview" +msgstr "ভুমিকা" + +#: sphinx/themes/basic/defindex.html:11 +msgid "Indices and tables:" +msgstr "ইনডেক্স ও টেবিল সমূহ:" + +#: sphinx/themes/basic/defindex.html:14 +msgid "Complete Table of Contents" +msgstr "পূর্ণাঙ্গ সূচীপত্র" + +#: sphinx/themes/basic/defindex.html:15 +msgid "lists all sections and subsections" +msgstr "সকল অনুচ্ছেদ সমূহের তালিকা" + +#: sphinx/themes/basic/defindex.html:17 +msgid "search this documentation" +msgstr "এই সহায়িকাতে অনুসন্ধা করুন" + +#: sphinx/themes/basic/defindex.html:20 +msgid "quick access to all modules" +msgstr "সকল মডিউলে দ্রুত প্রবেশ" + +#: sphinx/themes/basic/defindex.html:22 +msgid "all functions, classes, terms" +msgstr "সকল ফাংশন, ক্লাস, টার্ম" + +#: sphinx/themes/basic/genindex-single.html:5 +#, python-format +msgid "Index – %(key)s" +msgstr "ইনডেক্স – %(key)s" + +#: sphinx/themes/basic/genindex-single.html:44 +#: sphinx/themes/basic/genindex-split.html:14 +#: sphinx/themes/basic/genindex-split.html:27 +#: sphinx/themes/basic/genindex.html:54 +msgid "Full index on one page" +msgstr "এক পাতায় সম্পূর্ণ ইনডেক্স" + +#: sphinx/themes/basic/genindex-split.html:7 +msgid "Index pages by letter" +msgstr "বর্ণানুসারে ইনডেক্স পাতা" + +#: sphinx/themes/basic/genindex-split.html:15 +msgid "can be huge" +msgstr "খুব বড় হতে পারে" + +#: sphinx/themes/basic/layout.html:10 +msgid "Navigation" +msgstr "নেভিগেশন" + +#: sphinx/themes/basic/layout.html:42 +msgid "Table Of Contents" +msgstr "সূচীপত্র" + +#: sphinx/themes/basic/layout.html:48 +msgid "Previous topic" +msgstr "পূর্ববর্তী টপিক" + +#: sphinx/themes/basic/layout.html:50 +msgid "previous chapter" +msgstr "পূর্ববর্তী অধ্যায়" + +#: sphinx/themes/basic/layout.html:53 +msgid "Next topic" +msgstr "পরবর্তী টপিক" + +#: sphinx/themes/basic/layout.html:55 +msgid "next chapter" +msgstr "পরবর্তী অধ্যায়" + +#: sphinx/themes/basic/layout.html:60 +msgid "This Page" +msgstr "এই পাতা" + +#: sphinx/themes/basic/layout.html:63 +msgid "Show Source" +msgstr "সোর্স দেখুন" + +#: sphinx/themes/basic/layout.html:73 +msgid "Quick search" +msgstr "দ্রুত অনুসন্ধান" + +#: sphinx/themes/basic/layout.html:76 +msgid "Go" +msgstr "যান" + +#: sphinx/themes/basic/layout.html:81 +msgid "Enter search terms or a module, class or function name." +msgstr "অনুসন্ধানের জন্য টার্ম, মডিউল, ক্লাস অথবা ফাংশনের নাম দিন।" + +#: sphinx/themes/basic/layout.html:122 +#, python-format +msgid "Search within %(docstitle)s" +msgstr "%(docstitle)s এর মধ্যে খুঁজুন" + +#: sphinx/themes/basic/layout.html:131 +msgid "About these documents" +msgstr "এই ডকুমেন্ট সম্পর্কে" + +#: sphinx/themes/basic/layout.html:137 +#: sphinx/themes/basic/search.html:2 +#: sphinx/themes/basic/search.html:5 +msgid "Search" +msgstr "অনুসন্ধান" + +#: sphinx/themes/basic/layout.html:140 +msgid "Copyright" +msgstr "কপিরাইট" + +#: sphinx/themes/basic/layout.html:187 +#: sphinx/themes/scrolls/layout.html:83 +#, python-format +msgid "© Copyright %(copyright)s." +msgstr "© কপিরাইট %(copyright)s." + +#: sphinx/themes/basic/layout.html:189 +#: sphinx/themes/scrolls/layout.html:85 +#, python-format +msgid "© Copyright %(copyright)s." +msgstr "© কপিরাইট %(copyright)s." + +#: sphinx/themes/basic/layout.html:193 +#: sphinx/themes/scrolls/layout.html:89 +#, python-format +msgid "Last updated on %(last_updated)s." +msgstr "%(last_updated)s সর্বশেষ পরিবর্তন করা হয়েছে।" + +#: sphinx/themes/basic/layout.html:196 +#: sphinx/themes/scrolls/layout.html:92 +#, python-format +msgid "Created using Sphinx %(sphinx_version)s." +msgstr "Sphinx %(sphinx_version)s দিয়ে তৈরী।" + +#: sphinx/themes/basic/modindex.html:36 +#: sphinx/themes/scrolls/modindex.html:37 +msgid "Deprecated" +msgstr "ডেপ্রিকেটেড" + +#: sphinx/themes/basic/opensearch.xml:4 +#, python-format +msgid "Search %(docstitle)s" +msgstr "%(docstitle)s-এ খুঁজুন" + +#: sphinx/themes/basic/search.html:9 +msgid "" +"Please activate JavaScript to enable the search\n" +" functionality." +msgstr "" +"অনুসন্ধান করার জন্য অনুগ্রহপূর্বক জাভাস্ক্রিপ্ট \n" +" সক্রিয় করুন।" + +#: sphinx/themes/basic/search.html:14 +msgid "" +"From here you can search these documents. Enter your search\n" +" words into the box below and click \"search\". Note that the search\n" +" function will automatically search for all of the words. Pages\n" +" containing fewer words won't appear in the result list." +msgstr "" +"এখান থেকে এই নথিগুলোতে আপনি অনুসন্ধান করতে পারবেন। \n" +" আপনার কাঙ্ক্ষিত শব্দসমূহ নিচের বাক্সে লিখুন এবং \"অনুসন্ধান\" বাটনে ক্লিক করুন।\n" +" উল্লেখ্য, সকল শব্দসমূহের উপস্থিতি নিয়ে অনুসন্ধান করা হবে। যেসব পাতায় সকল\n" +" শব্দ নেই সেগুলো বাদ দেয়া হবে।" + +#: sphinx/themes/basic/search.html:21 +msgid "search" +msgstr "খুঁজুন" + +#: sphinx/themes/basic/search.html:25 +#: sphinx/themes/basic/static/searchtools.js:473 +msgid "Search Results" +msgstr "অনুসন্ধানের ফলাফল" + +#: sphinx/themes/basic/search.html:27 +msgid "Your search did not match any results." +msgstr "আপনার অনুসন্ধানে কোন ফলাফল পাওয়া যায়নি।" + +#: sphinx/themes/basic/changes/frameset.html:5 +#: sphinx/themes/basic/changes/versionchanges.html:12 +#, python-format +msgid "Changes in Version %(version)s — %(docstitle)s" +msgstr "%(version)s — %(docstitle)s-এ পরিবর্তন সমূহ" + +#: sphinx/themes/basic/changes/rstsource.html:5 +#, python-format +msgid "%(filename)s — %(docstitle)s" +msgstr "%(filename)s — %(docstitle)s" + +#: sphinx/themes/basic/changes/versionchanges.html:17 +#, python-format +msgid "Automatically generated list of changes in version %(version)s" +msgstr "স্বয়ংক্রিয়ভাবে তৈরী %(version)s-এ পরিবর্তন সমূহের তালিকা।" + +#: sphinx/themes/basic/changes/versionchanges.html:18 +msgid "Library changes" +msgstr "লাইব্রেরির পরিবর্তন" + +#: sphinx/themes/basic/changes/versionchanges.html:23 +msgid "C API changes" +msgstr "C API পরিবর্তন" + +#: sphinx/themes/basic/changes/versionchanges.html:25 +msgid "Other changes" +msgstr "অন্যান্য পরিবর্তন" + +#: sphinx/themes/basic/static/doctools.js:138 +#: sphinx/writers/html.py:462 +#: sphinx/writers/html.py:467 +msgid "Permalink to this headline" +msgstr "এই শিরোনামের পার্মালিঙ্ক" + +#: sphinx/themes/basic/static/doctools.js:144 +#: sphinx/writers/html.py:80 +msgid "Permalink to this definition" +msgstr "এই সংজ্ঞার পার্মালিঙ্ক" + +#: sphinx/themes/basic/static/doctools.js:173 +msgid "Hide Search Matches" +msgstr "অনুসন্ধানের ম্যাচগুলো লুকান" + +#: sphinx/themes/basic/static/searchtools.js:274 +msgid "Searching" +msgstr "অনুসন্ধান চলছে" + +#: sphinx/themes/basic/static/searchtools.js:279 +msgid "Preparing search..." +msgstr "অনুসন্ধানের প্রস্তুতি চলছে..." + +#: sphinx/themes/basic/static/searchtools.js:352 +msgid ", in " +msgstr ", -" + +#: sphinx/themes/basic/static/searchtools.js:475 +msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." +msgstr "আপনার অনুসন্ধানে কোন ফলাফল পাওয়া যায়নি। আপনার অনুসন্ধানের শব্দগুলোর সঠিক বানান ও বিভাগ নির্বাচন নিশ্চিত করুন।" + +#: sphinx/themes/basic/static/searchtools.js:477 +#, python-format +msgid "Search finished, found %s page(s) matching the search query." +msgstr "অনুসন্ধান শেষ হয়েছে, ফলাফলে %s-টি পাতা পাওয়া গেছে।" + +#: sphinx/writers/latex.py:187 +msgid "Release" +msgstr "রিলিজ" + +#: sphinx/writers/latex.py:579 +msgid "Footnotes" +msgstr "পাদটীকা" + +#: sphinx/writers/latex.py:647 +msgid "continued from previous page" +msgstr "পূর্ববর্তী পাতা হতে চলমান" + +#: sphinx/writers/latex.py:652 +msgid "Continued on next page" +msgstr "পরবর্তী পাতাতে চলমান" + +#: sphinx/writers/text.py:166 +#, python-format +msgid "Platform: %s" +msgstr "প্লাটফরম: %s" + +#: sphinx/writers/text.py:428 +msgid "[image]" +msgstr "[ছবি]" + From 9c568c68aac9a84e5115d10764f460008e77096a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 14:41:45 +0200 Subject: [PATCH 527/744] Remove debug statement. --- sphinx/domains/javascript.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 9934dba3a..582e2adc6 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -31,7 +31,6 @@ class JSObject(ObjectDescription): display_prefix = None def handle_signature(self, sig, signode): - import pdb; pdb.set_trace() sig = sig.strip() if '(' in sig and sig[-1:] == ')': prefix, arglist = sig.split('(', 1) From 3a61b58b3f8621a02f53ac30d00e7272b6aca595 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 14:45:17 +0200 Subject: [PATCH 528/744] #470: Fix generated target names for reST domain objects; they are not in the same namespace. --- CHANGES | 3 +++ sphinx/domains/rst.py | 7 ++++--- sphinx/environment.py | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 8781d0e2a..1da078655 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.1 (in development) ============================== +* #470: Fix generated target names for reST domain objects; they + are not in the same namespace. + * #266: Add Bengali language. * #473: Fix a bug in parsing JavaScript object names. diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 04092eab0..98cab2241 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -29,8 +29,9 @@ class ReSTMarkup(ObjectDescription): def add_target_and_index(self, name, sig, signode): if name not in self.state.document.ids: - signode['names'].append(name) - signode['ids'].append(name) + targetname = name + '-' + self.objtype + signode['names'].append(targetname) + signode['ids'].append(targetname) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) @@ -47,7 +48,7 @@ class ReSTMarkup(ObjectDescription): indextext = self.get_index_text(self.objtype, name) if indextext: self.indexnode['entries'].append(('single', indextext, - name, name)) + targetname, targetname)) def get_index_text(self, objectname, name): if self.objtype == 'directive': diff --git a/sphinx/environment.py b/sphinx/environment.py index 972bfa3ef..5edcb4d90 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -63,7 +63,7 @@ default_settings = { # This is increased every time an environment attribute is added # or changed to properly invalidate pickle files. -ENV_VERSION = 36 +ENV_VERSION = 37 default_substitutions = set([ From 93f531ae1a6e05f6d67e830b26620c70cb53c452 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 14:57:32 +0200 Subject: [PATCH 529/744] Fix fix. --- sphinx/domains/rst.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 98cab2241..61809f330 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -28,8 +28,8 @@ class ReSTMarkup(ObjectDescription): """ def add_target_and_index(self, name, sig, signode): - if name not in self.state.document.ids: - targetname = name + '-' + self.objtype + targetname = name + '-' + self.objtype + if targetname not in self.state.document.ids: signode['names'].append(targetname) signode['ids'].append(targetname) signode['first'] = (not self.names) From 4a400034d9d255d0c8da7d824d8038c2d90a71b2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 17:52:57 +0200 Subject: [PATCH 530/744] This apparently fixes a failing test in Gentoo. --- tests/test_build_html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index bafee243a..41ebf0556 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -37,7 +37,7 @@ ENV_WARNINGS = """\ http://www.python.org/logo.png %(root)s/includes.txt:\\d*: \\(WARNING/2\\) Encoding 'utf-8-sig' used for \ reading included file u'wrongenc.inc' seems to be wrong, try giving an \ -:encoding: option +:encoding: option\n? %(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png %(root)s/objects.txt:79: WARNING: using old C markup; please migrate to \ new-style markup \(e.g. c:function instead of cfunction\), see \ From fb0e13e0ecaa170c33ea277b3ba8c6892863d459 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 17:58:01 +0200 Subject: [PATCH 531/744] Fix handling of non-text field types. --- sphinx/util/docfields.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 1ae49c6c5..9eafde0ac 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -141,9 +141,12 @@ class TypedField(GroupedField): par = nodes.paragraph() par += self.make_xref(self.rolename, domain, fieldarg, nodes.strong) if fieldarg in types: - typename = u''.join(n.astext() for n in types[fieldarg]) par += nodes.Text(' (') - par += self.make_xref(self.typerolename, domain, typename) + if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text): + typename = u''.join(n.astext() for n in types[fieldarg]) + par += self.make_xref(self.typerolename, domain, typename) + else: + par += fieldtype par += nodes.Text(')') par += nodes.Text(' -- ') par += content From e8f2d8f8e4a3301cdfd57f338b967f89323c9636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuh=C3=A4user?= Date: Mon, 26 Jul 2010 05:16:00 +0200 Subject: [PATCH 532/744] Changed documentation URL by request --- EXAMPLES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXAMPLES b/EXAMPLES index 52fce0e8b..44c511e9e 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -12,7 +12,7 @@ interesting examples. Documentation using the default theme ------------------------------------- -* APSW: http://apsw.googlecode.com/svn/publish/index.html +* APSW: http://apidoc.apsw.googlecode.com/hg/index.html * ASE: https://wiki.fysik.dtu.dk/ase/ * boostmpi: http://documen.tician.de/boostmpi/ * Calibre: http://calibre.kovidgoyal.net/user_manual/ From 753a266fffc3382cd7d5d4d9be435d203452b776 Mon Sep 17 00:00:00 2001 From: Roland Meister Date: Mon, 26 Jul 2010 22:33:32 +0200 Subject: [PATCH 533/744] Use fix_fragment everywhere. --- sphinx/builders/epub.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index 0a647fb7e..3058cd8bc 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -221,10 +221,10 @@ class EpubBuilder(StandaloneHTMLBuilder): 'text': ssp(self.esc(text)) }) - def fix_fragment(self, match): - """Return a href attribute with colons replaced by hyphens. + def fix_fragment(self, prefix, fragment): + """Return a href/id attribute with colons replaced by hyphens. """ - return match.group(1) + match.group(2).replace(':', '-') + return prefix + fragment.replace(':', '-') def fix_ids(self, tree): """Replace colons with hyphens in href and id attributes. @@ -235,14 +235,14 @@ class EpubBuilder(StandaloneHTMLBuilder): if 'refuri' in node: m = _refuri_re.match(node['refuri']) if m: - node['refuri'] = self.fix_fragment(m) + node['refuri'] = self.fix_fragment(m.group(1), m.group(2)) if 'refid' in node: - node['refid'] = node['refid'].replace(':', '-') + node['refid'] = self.fix_fragment('', node['refid']) for node in tree.traverse(addnodes.desc_signature): ids = node.attributes['ids'] newids = [] for id in ids: - newids.append(id.replace(':', '-')) + newids.append(self.fix_fragment('', id)) node.attributes['ids'] = newids def add_visible_links(self, tree): @@ -278,12 +278,13 @@ class EpubBuilder(StandaloneHTMLBuilder): for (i, link) in enumerate(links): m = _refuri_re.match(link) if m: - links[i] = self.fix_fragment(m) + links[i] = self.fix_fragment(m.group(1), m.group(2)) for subentryname, subentrylinks in subitems: for (i, link) in enumerate(subentrylinks): m = _refuri_re.match(link) if m: - subentrylinks[i] = self.fix_fragment(m) + subentrylinks[i] = \ + self.fix_fragment(m.group(1), m.group(2)) def handle_page(self, pagename, addctx, templatename='page.html', outfilename=None, event_arg=None): From 3b64ec2a2e6468cbf1b6b3cc56035f7fdb0249cb Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 19:07:51 +0200 Subject: [PATCH 534/744] Now that the expected warnings are regexes, diffing them with the actual output makes no sense anymore. --- tests/test_build_html.py | 5 ++--- tests/test_build_latex.py | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 41ebf0556..5f4dea0ff 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -11,7 +11,6 @@ import os import re -import difflib import htmlentitydefs from StringIO import StringIO @@ -285,8 +284,8 @@ def test_html(app): html_warnings_exp = HTML_WARNINGS % {'root': re.escape(app.srcdir)} assert re.match(html_warnings_exp + '$', html_warnings), \ 'Warnings don\'t match:\n' + \ - '\n'.join(difflib.ndiff(html_warnings_exp.splitlines(), - html_warnings.splitlines())) + '--- Expected (regex):\n' + html_warnings_exp + \ + '--- Got:\n' + html_warnings for fname, paths in HTML_XPATH.iteritems(): parser = NslessParser() diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 6a20746b6..4405395a0 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -12,7 +12,6 @@ import os import re import sys -import difflib from StringIO import StringIO from subprocess import Popen, PIPE @@ -42,8 +41,9 @@ def test_latex(app): latex_warnings_exp = LATEX_WARNINGS % {'root': app.srcdir} assert re.match(latex_warnings_exp + '$', latex_warnings), \ 'Warnings don\'t match:\n' + \ - '\n'.join(difflib.ndiff(latex_warnings_exp.splitlines(), - latex_warnings.splitlines())) + '--- Expected (regex):\n' + latex_warnings_exp + \ + '--- Got:\n' + latex_warnings + # file from latex_additional_files assert (app.outdir / 'svgimg.svg').isfile() From e081cf4d3b0f9fb55aff0bacc6e06c28add77a74 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 19:08:09 +0200 Subject: [PATCH 535/744] Add a missing assignment. --- sphinx/util/docfields.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 9eafde0ac..c8e58f48a 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -142,6 +142,7 @@ class TypedField(GroupedField): par += self.make_xref(self.rolename, domain, fieldarg, nodes.strong) if fieldarg in types: par += nodes.Text(' (') + fieldtype = types[fieldarg] if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text): typename = u''.join(n.astext() for n in types[fieldarg]) par += self.make_xref(self.typerolename, domain, typename) From 5a28ae4686116a7814909d825aabb5512c407358 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 19:19:29 +0200 Subject: [PATCH 536/744] Add some tests for docfields. --- tests/root/objects.txt | 5 +++++ tests/test_build_html.py | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/root/objects.txt b/tests/root/objects.txt index ca3d0eb76..334827de2 100644 --- a/tests/root/objects.txt +++ b/tests/root/objects.txt @@ -43,6 +43,11 @@ Testing object descriptions .. class:: TimeInt + :param moo: |test| + :type moo: |test| + +.. |test| replace:: Moo + .. class:: Time(hour, minute, isdst) :param hour: The year. diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 5f4dea0ff..4dee513ae 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -36,9 +36,9 @@ ENV_WARNINGS = """\ http://www.python.org/logo.png %(root)s/includes.txt:\\d*: \\(WARNING/2\\) Encoding 'utf-8-sig' used for \ reading included file u'wrongenc.inc' seems to be wrong, try giving an \ -:encoding: option\n? +:encoding: option\\n? %(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png -%(root)s/objects.txt:79: WARNING: using old C markup; please migrate to \ +%(root)s/objects.txt:84: WARNING: using old C markup; please migrate to \ new-style markup \(e.g. c:function instead of cfunction\), see \ http://sphinx.pocoo.org/domains.html """ @@ -50,6 +50,16 @@ HTML_WARNINGS = ENV_WARNINGS + """\ %(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; ' """ +def tail_check(check): + rex = re.compile(check) + def checker(nodes): + for node in nodes: + if node.tail and rex.search(node.tail): + return True + assert False, '%r not found in tail of any nodes %s' % (check, nodes) + return checker + + HTML_XPATH = { 'images.html': [ (".//img[@src='_images/img.png']", ''), @@ -171,6 +181,10 @@ HTML_XPATH = { 'Testing various markup'), # custom sidebar (".//h4", 'Custom sidebar'), + # docfields + (".//td[@class='field-body']/ul/li/strong", '^moo$'), + (".//td[@class='field-body']/ul/li/strong", + tail_check(r'\(Moo\) .* Moo')), ], 'contents.html': [ (".//meta[@name='hc'][@content='hcval']", ''), From 6262c275a750d36402ba430f3f01ffaca18b3098 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 19:40:19 +0200 Subject: [PATCH 537/744] Prepare 1.0.1 release. --- CHANGES | 4 ++-- sphinx/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 1da078655..4d02c7a52 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,5 @@ -Release 1.0.1 (in development) -============================== +Release 1.0.1 (Jul 27, 2010) +============================ * #470: Fix generated target names for reST domain objects; they are not in the same namespace. diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 8c1ebed56..289410075 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -12,8 +12,8 @@ import sys from os import path -__version__ = '1.0+' -__released__ = '1.0' # used when Sphinx builds its own docs +__version__ = '1.0.1' +__released__ = '1.0.1' # used when Sphinx builds its own docs package_dir = path.abspath(path.dirname(__file__)) From 3d6dd4d1e7a56921943c191ed87316bd5aded7e9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 19:52:25 +0200 Subject: [PATCH 539/744] Post-release updates. --- CHANGES | 3 +++ sphinx/__init__.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 4d02c7a52..48d03e785 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +Release 1.0.2 (in development) +============================== + Release 1.0.1 (Jul 27, 2010) ============================ diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 289410075..50aa28cd6 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -12,7 +12,7 @@ import sys from os import path -__version__ = '1.0.1' +__version__ = '1.0.1+' __released__ = '1.0.1' # used when Sphinx builds its own docs package_dir = path.abspath(path.dirname(__file__)) From a1fd9830e57944b980629ad3bf5b11771dc0af32 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 27 Jul 2010 20:44:32 +0200 Subject: [PATCH 540/744] Dont fail when no json impl is found. --- sphinx/util/jsonimpl.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py index 21f285d30..b83661a7e 100644 --- a/sphinx/util/jsonimpl.py +++ b/sphinx/util/jsonimpl.py @@ -39,5 +39,8 @@ def dumps(obj, *args, **kwds): kwds['cls'] = SphinxJSONEncoder return json.dumps(obj, *args, **kwds) -load = json.load -loads = json.loads +def load(*args, **kwds): + return json.load(*args, **kwds) + +def loads(*args, **kwds): + return json.loads(*args, **kwds) From 13017eecc8fc203695b5587afcac6a7dbf5b6cb6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 28 Jul 2010 14:53:39 +0200 Subject: [PATCH 541/744] Go to #pocoo. --- doc/_templates/indexsidebar.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html index a268ec6ef..85b8fba95 100644 --- a/doc/_templates/indexsidebar.html +++ b/doc/_templates/indexsidebar.html @@ -23,6 +23,6 @@ are also available.

                -

                or come to the #python-docs channel on FreeNode.

                +

                or come to the #pocoo channel on FreeNode.

                You can also open an issue at the tracker.

                From cb26c7807943853a3393a7e8deba57131762342f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 29 Jul 2010 09:27:46 +0200 Subject: [PATCH 542/744] Describe the "deprecated" directive. --- doc/markup/para.rst | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/doc/markup/para.rst b/doc/markup/para.rst index be06f6365..ecc6b4a6b 100644 --- a/doc/markup/para.rst +++ b/doc/markup/para.rst @@ -42,15 +42,25 @@ units as well as normal text: Example:: .. versionadded:: 2.5 - The `spam` parameter. + The *spam* parameter. Note that there must be no blank line between the directive head and the explanation; this is to make these blocks visually continuous in the markup. .. rst:directive:: .. versionchanged:: version - Similar to :rst:dir:`versionadded`, but describes when and what changed in the named - feature in some way (new parameters, changed side effects, etc.). + Similar to :rst:dir:`versionadded`, but describes when and what changed in + the named feature in some way (new parameters, changed side effects, etc.). + +.. rst:directive:: .. deprecated:: vesion + + Similar to :rst:dir:`versionchanged`, but describes when the feature was + deprecated. An explanation can also be given, for example to inform the + reader what should be used instead. Example:: + + .. deprecated:: 3.1 + Use :func:`spam` instead. + -------------- From 1e010aa858ba7e53f26cdbc0ef7b6f6fb5de4302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Mon, 2 Aug 2010 13:30:59 +0200 Subject: [PATCH 543/744] Fix ASCII diagram --- sphinx/ext/inheritance_diagram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index b930d8cab..a12bad256 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -""" +r""" sphinx.ext.inheritance_diagram ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From ee7161319f0ada18ce8f7b16639c75f305dce6a0 Mon Sep 17 00:00:00 2001 From: Roland Meister Date: Wed, 4 Aug 2010 23:15:01 +0200 Subject: [PATCH 544/744] Fix creation of content.opf and toc.ncx if master_doc is in a subdir. --- sphinx/builders/epub.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index 3058cd8bc..111045190 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -202,6 +202,11 @@ class EpubBuilder(StandaloneHTMLBuilder): doctree = self.env.get_and_resolve_doctree(self.config.master_doc, self, prune_toctrees=False) self.refnodes = self.get_refnodes(doctree, []) + master_dir = os.path.dirname(self.config.master_doc) + if master_dir: + master_dir += '/' # XXX or os.sep? + for item in self.refnodes: + item['refuri'] = master_dir + item['refuri'] self.refnodes.insert(0, { 'level': 1, 'refuri': self.esc(self.config.master_doc + '.html'), From a936e90977c2033aece4d5065bb7f8f535280a26 Mon Sep 17 00:00:00 2001 From: Roland Meister Date: Wed, 4 Aug 2010 23:40:44 +0200 Subject: [PATCH 545/744] Added dc:date metadata field to content.opf. --- sphinx/builders/epub.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index 111045190..b40c8280e 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -15,6 +15,7 @@ import codecs from os import path import zipfile import re +import time from docutils import nodes @@ -84,6 +85,7 @@ _content_template = u'''\ %(publisher)s %(copyright)s %(id)s + %(date)s @@ -350,6 +352,7 @@ class EpubBuilder(StandaloneHTMLBuilder): metadata['copyright'] = self.esc(self.config.epub_copyright) metadata['scheme'] = self.esc(self.config.epub_scheme) metadata['id'] = self.esc(self.config.epub_identifier) + metadata['date'] = self.esc(time.strftime('%Y-%m-%d')) metadata['files'] = files metadata['spine'] = spine return metadata From bf159679697f9940298ca99582b71314b4d144a1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 10:14:10 +0200 Subject: [PATCH 546/744] #486: Fix removal of ``!`` for all cross-reference roles. --- CHANGES | 3 +++ sphinx/roles.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 48d03e785..dce6e713a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.2 (in development) ============================== +* #486: Fix removal of ``!`` for all cross-reference roles. + + Release 1.0.1 (Jul 27, 2010) ============================ diff --git a/sphinx/roles.py b/sphinx/roles.py index d3f3c67e3..bacdad5b6 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -105,9 +105,9 @@ class XRefRole(object): classes = ['xref', domain, '%s-%s' % (domain, role)] # if the first character is a bang, don't cross-reference at all if text[0:1] == '!': - text = utils.unescape(text) + text = utils.unescape(text)[1:] if self.fix_parens: - text, tgt = self._fix_parens(env, False, text[1:], "") + text, tgt = self._fix_parens(env, False, text, "") innernode = self.innernodeclass(rawtext, text, classes=classes) return self.result_nodes(inliner.document, env, innernode, is_ref=False) From 01c501054e4e42e2c614c7a244f8b769938909f8 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 11:58:43 +0200 Subject: [PATCH 547/744] #480: Fix handling of target naming in intersphinx. --- CHANGES | 2 ++ doc/conf.py | 2 ++ doc/ext/intersphinx.rst | 6 ++-- sphinx/ext/intersphinx.py | 17 +++++++++-- tests/test_intersphinx.py | 60 +++++++++++++++++++++++---------------- 5 files changed, 57 insertions(+), 30 deletions(-) diff --git a/CHANGES b/CHANGES index dce6e713a..cb80d72c6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Release 1.0.2 (in development) ============================== +* #480: Fix handling of target naming in intersphinx. + * #486: Fix removal of ``!`` for all cross-reference roles. diff --git a/doc/conf.py b/doc/conf.py index 299f321ac..21e8d2f54 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -8,6 +8,8 @@ import sphinx extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.autosummary', 'sphinx.ext.extlinks'] +#intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)} + master_doc = 'contents' templates_path = ['_templates'] exclude_patterns = ['_build'] diff --git a/doc/ext/intersphinx.rst b/doc/ext/intersphinx.rst index bb2a5a8c4..7997472a7 100644 --- a/doc/ext/intersphinx.rst +++ b/doc/ext/intersphinx.rst @@ -84,7 +84,7 @@ linking: To add links to modules and objects in the Python standard library documentation, use:: - intersphinx_mapping = {'python': ('http://docs.python.org/', None)} + intersphinx_mapping = {'python': ('http://docs.python.org/3.2', None)} This will download the corresponding :file:`objects.inv` file from the Internet and generate links to the pages under the given URI. The downloaded @@ -94,12 +94,12 @@ linking: A second example, showing the meaning of a non-``None`` value of the second tuple item:: - intersphinx_mapping = {'python': ('http://docs.python.org/', + intersphinx_mapping = {'python': ('http://docs.python.org/3.2', 'python-inv.txt')} This will read the inventory from :file:`python-inv.txt` in the source directory, but still generate links to the pages under - ``http://docs.python.org/``. It is up to you to update the inventory file as + ``http://docs.python.org/3.2``. It is up to you to update the inventory file as new objects are added to the Python documentation. .. confval:: intersphinx_cache_limit diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 0a210879a..055fc9cd9 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -205,9 +205,20 @@ def missing_reference(app, env, node, contnode): proj, version, uri, dispname = inventory[objtype][target] newnode = nodes.reference('', '', internal=False, refuri=uri, reftitle='(in %s v%s)' % (proj, version)) - if dispname == '-': - dispname = target - newnode.append(contnode.__class__(dispname, dispname)) + if node.get('refexplicit'): + # use whatever title was given + newnode.append(contnode) + elif dispname == '-': + # use whatever title was given, but strip prefix + title = contnode.astext() + if in_set and title.startswith(in_set+':'): + newnode.append(contnode.__class__(title[len(in_set)+1:], + title[len(in_set)+1:])) + else: + newnode.append(contnode) + else: + # else use the given display name (used for :ref:) + newnode.append(contnode.__class__(dispname, dispname)) return newnode # at least get rid of the ':' in the target if no explicit title given if in_set is not None and not node.get('refexplicit', True): diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py index 8b6547e54..3b50cc78c 100644 --- a/tests/test_intersphinx.py +++ b/tests/test_intersphinx.py @@ -94,46 +94,58 @@ def test_missing_reference(tempdir, app): ('foo', '2.0', 'http://docs.python.org/foo.html#module-module2', '-') # create fake nodes and check referencing - contnode = nodes.emphasis('foo', 'foo') - refnode = addnodes.pending_xref('') - refnode['reftarget'] = 'module1.func' - refnode['reftype'] = 'func' - refnode['refdomain'] = 'py' - rn = missing_reference(app, app.env, refnode, contnode) + def fake_node(domain, type, target, content, **attrs): + contnode = nodes.emphasis(content, content) + node = addnodes.pending_xref('') + node['reftarget'] = target + node['reftype'] = type + node['refdomain'] = domain + node.attributes.update(attrs) + node += contnode + return node, contnode + + def reference_check(*args, **kwds): + node, contnode = fake_node(*args, **kwds) + return missing_reference(app, app.env, node, contnode) + + # check resolution when a target is found + rn = reference_check('py', 'func', 'module1.func', 'foo') assert isinstance(rn, nodes.reference) assert rn['refuri'] == 'http://docs.python.org/sub/foo.html#module1.func' assert rn['reftitle'] == '(in foo v2.0)' - assert rn[0].astext() == 'module1.func' + assert rn[0].astext() == 'foo' # create unresolvable nodes and check None return value - refnode['reftype'] = 'foo' - assert missing_reference(app, app.env, refnode, contnode) is None - - refnode['reftype'] = 'function' - refnode['reftarget'] = 'foo.func' - assert missing_reference(app, app.env, refnode, contnode) is None + assert reference_check('py', 'foo', 'module1.func', 'foo') is None + assert reference_check('py', 'func', 'foo', 'foo') is None + assert reference_check('py', 'func', 'foo', 'foo') is None # check handling of prefixes # prefix given, target found: prefix is stripped - refnode['reftype'] = 'mod' - refnode['reftarget'] = 'py3k:module2' - rn = missing_reference(app, app.env, refnode, contnode) + rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2') assert rn[0].astext() == 'module2' + # prefix given, but not in title: nothing stripped + rn = reference_check('py', 'mod', 'py3k:module2', 'module2') + assert rn[0].astext() == 'module2' + + # prefix given, but explicit: nothing stripped + rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2', + refexplicit=True) + assert rn[0].astext() == 'py3k:module2' + # prefix given, target not found and nonexplicit title: prefix is stripped - refnode['reftarget'] = 'py3k:unknown' - refnode['refexplicit'] = False - contnode[0] = nodes.Text('py3k:unknown') - rn = missing_reference(app, app.env, refnode, contnode) + node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown', + refexplicit=False) + rn = missing_reference(app, app.env, node, contnode) assert rn is None assert contnode[0].astext() == 'unknown' # prefix given, target not found and explicit title: nothing is changed - refnode['reftarget'] = 'py3k:unknown' - refnode['refexplicit'] = True - contnode[0] = nodes.Text('py3k:unknown') - rn = missing_reference(app, app.env, refnode, contnode) + node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown', + refexplicit=True) + rn = missing_reference(app, app.env, node, contnode) assert rn is None assert contnode[0].astext() == 'py3k:unknown' From 29547f798172a34e00fded1da2f9dc0b0f31bc09 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 12:02:27 +0200 Subject: [PATCH 548/744] #488: Fix crash when json-py is installed, which provides a ``json`` module but is incompatible to simplejson. --- CHANGES | 3 +++ sphinx/util/jsonimpl.py | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index cb80d72c6..60c55e7a9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.2 (in development) ============================== +* #488: Fix crash when json-py is installed, which provides a + ``json`` module but is incompatible to simplejson. + * #480: Fix handling of target naming in intersphinx. * #486: Fix removal of ``!`` for all cross-reference roles. diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py index b83661a7e..fda85b5e3 100644 --- a/sphinx/util/jsonimpl.py +++ b/sphinx/util/jsonimpl.py @@ -13,8 +13,10 @@ import UserString try: import json + # json-py's json module has not JSONEncoder; this will raise AttributeError + # if json-py is imported instead of the built-in json module JSONEncoder = json.JSONEncoder -except ImportError: +except (ImportError, AttributeError): try: import simplejson as json JSONEncoder = json.JSONEncoder From eedef65aa6d3673b81ac6fcd6c9b05ec74a2567e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 12:03:45 +0200 Subject: [PATCH 549/744] #487: Fix setting the default role to one provided by the ``oldcmarkup`` extension. --- CHANGES | 3 +++ sphinx/ext/oldcmarkup.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 60c55e7a9..bc122e9c9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.2 (in development) ============================== +* #487: Fix setting the default role to one provided by the + ``oldcmarkup`` extension. + * #488: Fix crash when json-py is installed, which provides a ``json`` module but is incompatible to simplejson. diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py index 84ae61dd2..00ac37495 100644 --- a/sphinx/ext/oldcmarkup.py +++ b/sphinx/ext/oldcmarkup.py @@ -42,6 +42,8 @@ class OldCDirective(Directive): def old_crole(typ, rawtext, text, lineno, inliner, options={}, content=[]): env = inliner.document.settings.env + if not typ: + typ = env.config.default_role if not env.app._oldcmarkup_warned: env.warn(env.docname, WARNING_MSG) env.app._oldcmarkup_warned = True From 8c91fb78ce0ac364870c3cd1df2149397f46bd8a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 12:53:05 +0200 Subject: [PATCH 550/744] #484: Fix crash when duplicating a parameter in an info field list. Problem was that the :type: info nodes were inserted twice into the doctree, which led to inconsistencies when reference nodes were resolved. --- CHANGES | 2 ++ sphinx/util/docfields.py | 7 +++++-- tests/root/objects.txt | 2 ++ tests/test_build_html.py | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index bc122e9c9..f9b3b9a49 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Release 1.0.2 (in development) ============================== +* #484: Fix crash when duplicating a parameter in an info field list. + * #487: Fix setting the default role to one provided by the ``oldcmarkup`` extension. diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index c8e58f48a..6ce6d82bf 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -142,9 +142,12 @@ class TypedField(GroupedField): par += self.make_xref(self.rolename, domain, fieldarg, nodes.strong) if fieldarg in types: par += nodes.Text(' (') - fieldtype = types[fieldarg] + # NOTE: using .pop() here to prevent a single type node to be + # inserted twice into the doctree, which leads to + # inconsistencies later when references are resolved + fieldtype = types.pop(fieldarg) if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text): - typename = u''.join(n.astext() for n in types[fieldarg]) + typename = u''.join(n.astext() for n in fieldtype) par += self.make_xref(self.typerolename, domain, typename) else: par += fieldtype diff --git a/tests/root/objects.txt b/tests/root/objects.txt index 334827de2..d6b8bdf64 100644 --- a/tests/root/objects.txt +++ b/tests/root/objects.txt @@ -62,6 +62,8 @@ Testing object descriptions :ivar int hour: like *hour* :ivar minute: like *minute* :vartype minute: int + :param hour: Duplicate param. Should not lead to crashes. + :type hour: Duplicate type. C items diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 4dee513ae..813c962fe 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -38,7 +38,7 @@ http://www.python.org/logo.png reading included file u'wrongenc.inc' seems to be wrong, try giving an \ :encoding: option\\n? %(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png -%(root)s/objects.txt:84: WARNING: using old C markup; please migrate to \ +%(root)s/objects.txt:86: WARNING: using old C markup; please migrate to \ new-style markup \(e.g. c:function instead of cfunction\), see \ http://sphinx.pocoo.org/domains.html """ From 580e1c90d36c9d126f0a4301e0b492606b755e46 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 13:39:23 +0200 Subject: [PATCH 551/744] #481, #482: fix ``.name`` reference matching. #482: When doing a non-exact search, match only the given type of object. #481: Apply non-exact search for Python reference targets with ``.name`` for modules too. --- CHANGES | 6 +++ doc/builders.rst | 8 ++-- doc/conf.py | 6 ++- doc/config.rst | 21 +++------ doc/domains.rst | 19 +++++--- doc/ext/appapi.rst | 4 +- doc/ext/autodoc.rst | 40 +++++++++-------- doc/ext/inheritance.rst | 2 +- doc/ext/math.rst | 8 ++-- doc/markup/inline.rst | 2 +- doc/markup/toctree.rst | 2 +- doc/templating.rst | 12 ++--- sphinx/domains/python.py | 94 +++++++++++++++++++++++----------------- sphinx/domains/rst.py | 2 +- sphinx/environment.py | 2 +- 15 files changed, 124 insertions(+), 104 deletions(-) diff --git a/CHANGES b/CHANGES index f9b3b9a49..33a3cca2c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,12 @@ Release 1.0.2 (in development) ============================== +* #482: When doing a non-exact search, match only the given type + of object. + +* #481: Apply non-exact search for Python reference targets with + ``.name`` for modules too. + * #484: Fix crash when duplicating a parameter in an info field list. * #487: Fix setting the default role to one provided by the diff --git a/doc/builders.rst b/doc/builders.rst index 6e90ccc62..80203e759 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -255,11 +255,11 @@ All serialization builders outputs one file per source file and a few special files. They also copy the reST source files in the directory ``_sources`` under the output directory. -The :class:`PickleHTMLBuilder` is a builtin subclass that implements the pickle +The :class:`.PickleHTMLBuilder` is a builtin subclass that implements the pickle serialization interface. The files per source file have the extensions of -:attr:`~SerializingHTMLBuilder.out_suffix`, and are arranged in directories +:attr:`~.SerializingHTMLBuilder.out_suffix`, and are arranged in directories just as the source files are. They unserialize to a dictionary (or dictionary like structure) with these keys: @@ -290,7 +290,7 @@ like structure) with these keys: The special files are located in the root output directory. They are: -:attr:`SerializingHTMLBuilder.globalcontext_filename` +:attr:`.SerializingHTMLBuilder.globalcontext_filename` A pickled dict with these keys: ``project``, ``copyright``, ``release``, ``version`` @@ -309,7 +309,7 @@ The special files are located in the root output directory. They are: ``titles`` A dictionary of all documents' titles, as HTML strings. -:attr:`SerializingHTMLBuilder.searchindex_filename` +:attr:`.SerializingHTMLBuilder.searchindex_filename` An index that can be used for searching the documentation. It is a pickled list with these entries: diff --git a/doc/conf.py b/doc/conf.py index 21e8d2f54..b3a1cda79 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -8,8 +8,6 @@ import sphinx extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.autosummary', 'sphinx.ext.extlinks'] -#intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)} - master_doc = 'contents' templates_path = ['_templates'] exclude_patterns = ['_build'] @@ -66,6 +64,10 @@ man_pages = [ 'template generator', '', 1), ] +# We're not using intersphinx right now, but if we did, this would be part of +# the mapping: +intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)} + # -- Extension interface ------------------------------------------------------- diff --git a/doc/config.rst b/doc/config.rst index bf8ad3c2d..e0fbeb46e 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -346,12 +346,12 @@ Project information A boolean that decides whether module names are prepended to all :term:`object` names (for object types where a "module" of some kind is - defined), e.g. for :rst:dir:`function` directives. Default is ``True``. + defined), e.g. for :rst:dir:`py:function` directives. Default is ``True``. .. confval:: show_authors - A boolean that decides whether :rst:dir:`moduleauthor` and :rst:dir:`sectionauthor` - directives produce any output in the built files. + A boolean that decides whether :rst:dir:`codeauthor` and + :rst:dir:`sectionauthor` directives produce any output in the built files. .. confval:: modindex_common_prefix @@ -388,6 +388,8 @@ Options for HTML output These options influence HTML as well as HTML Help output, and other builders that use Sphinx' HTMLWriter class. +.. XXX document html_context + .. confval:: html_theme The "theme" that the HTML output should use. See the :doc:`section about @@ -553,19 +555,6 @@ that use Sphinx' HTMLWriter class. This will render the template ``customdownload.html`` as the page ``download.html``. - .. note:: - - Earlier versions of Sphinx had a value called :confval:`html_index` which - was a clumsy way of controlling the content of the "index" document. If - you used this feature, migrate it by adding an ``'index'`` key to this - setting, with your custom template as the value, and in your custom - template, use :: - - {% extend "defindex.html" %} - {% block tables %} - ... old template content ... - {% endblock %} - .. confval:: html_domain_indices If true, generate domain-specific indices in addition to the general index. diff --git a/doc/domains.rst b/doc/domains.rst index c64930a24..6bbe12150 100644 --- a/doc/domains.rst +++ b/doc/domains.rst @@ -138,11 +138,12 @@ declarations: .. rst:directive:: .. py:currentmodule:: name This directive tells Sphinx that the classes, functions etc. documented from - here are in the given module (like :rst:dir:`py:module`), but it will not create - index entries, an entry in the Global Module Index, or a link target for - :rst:role:`mod`. This is helpful in situations where documentation for things in - a module is spread over multiple files or sections -- one location has the - :rst:dir:`py:module` directive, the others only :rst:dir:`py:currentmodule`. + here are in the given module (like :rst:dir:`py:module`), but it will not + create index entries, an entry in the Global Module Index, or a link target + for :rst:role:`py:mod`. This is helpful in situations where documentation + for things in a module is spread over multiple files or sections -- one + location has the :rst:dir:`py:module` directive, the others only + :rst:dir:`py:currentmodule`. The following directives are provided for module and class contents: @@ -363,6 +364,9 @@ dot, this order is reversed. For example, in the documentation of Python's :mod:`codecs` module, ``:py:func:`open``` always refers to the built-in function, while ``:py:func:`.open``` refers to :func:`codecs.open`. +A similar heuristic is used to determine whether the name is an attribute of the +currently documented class. + Also, if the name is prefixed with a dot, and no exact match is found, the target is taken as a suffix and all object names with that suffix are searched. For example, ``:py:meth:`.TarFile.close``` references the @@ -370,8 +374,9 @@ searched. For example, ``:py:meth:`.TarFile.close``` references the ``tarfile``. Since this can get ambiguous, if there is more than one possible match, you will get a warning from Sphinx. -A similar heuristic is used to determine whether the name is an attribute of the -currently documented class. +Note that you can combine the ``~`` and ``.`` prefixes: +``:py:meth:`~.TarFile.close``` will reference the ``tarfile.TarFile.close()`` +method, but the visible link caption will only be ``close()``. .. _c-domain: diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst index 402dd72f0..2717c6a8e 100644 --- a/doc/ext/appapi.rst +++ b/doc/ext/appapi.rst @@ -210,7 +210,7 @@ the following public API: standard Sphinx roles (see :ref:`xref-syntax`). This method is also available under the deprecated alias - :meth:`add_description_unit`. + ``add_description_unit``. .. method:: Sphinx.add_crossref_type(directivename, rolename, indextemplate='', ref_nodeclass=None, objname='') @@ -272,6 +272,8 @@ the following public API: This allows to auto-document new types of objects. See the source of the autodoc module for examples on how to subclass :class:`Documenter`. + .. XXX add real docs for Documenter and subclassing + .. versionadded:: 0.6 .. method:: Sphinx.add_autodoc_attrgetter(type, getter) diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index bd725cfaa..813331015 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -27,20 +27,21 @@ two locations for documentation, while at the same time avoiding auto-generated-looking pure API documentation. :mod:`autodoc` provides several directives that are versions of the usual -:rst:dir:`module`, :rst:dir:`class` and so forth. On parsing time, they import the -corresponding module and extract the docstring of the given objects, inserting -them into the page source under a suitable :rst:dir:`module`, :rst:dir:`class` etc. -directive. +:rst:dir:`py:module`, :rst:dir:`py:class` and so forth. On parsing time, they +import the corresponding module and extract the docstring of the given objects, +inserting them into the page source under a suitable :rst:dir:`py:module`, +:rst:dir:`py:class` etc. directive. .. note:: - Just as :rst:dir:`class` respects the current :rst:dir:`module`, :rst:dir:`autoclass` - will also do so, and likewise with :rst:dir:`method` and :rst:dir:`class`. + Just as :rst:dir:`py:class` respects the current :rst:dir:`py:module`, + :rst:dir:`autoclass` will also do so. Likewise, :rst:dir:`automethod` will + respect the current :rst:dir:`py:class`. .. rst:directive:: automodule - autoclass - autoexception + autoclass + autoexception Document a module, class or exception. All three directives will by default only insert the docstring of the object itself:: @@ -127,23 +128,24 @@ directive. .. versionadded:: 0.4 - * The :rst:dir:`automodule`, :rst:dir:`autoclass` and :rst:dir:`autoexception` directives - also support a flag option called ``show-inheritance``. When given, a list - of base classes will be inserted just below the class signature (when used - with :rst:dir:`automodule`, this will be inserted for every class that is - documented in the module). + * The :rst:dir:`automodule`, :rst:dir:`autoclass` and + :rst:dir:`autoexception` directives also support a flag option called + ``show-inheritance``. When given, a list of base classes will be inserted + just below the class signature (when used with :rst:dir:`automodule`, this + will be inserted for every class that is documented in the module). .. versionadded:: 0.4 * All autodoc directives support the ``noindex`` flag option that has the - same effect as for standard :rst:dir:`function` etc. directives: no index - entries are generated for the documented object (and all autodocumented - members). + same effect as for standard :rst:dir:`py:function` etc. directives: no + index entries are generated for the documented object (and all + autodocumented members). .. versionadded:: 0.4 * :rst:dir:`automodule` also recognizes the ``synopsis``, ``platform`` and - ``deprecated`` options that the standard :rst:dir:`module` directive supports. + ``deprecated`` options that the standard :rst:dir:`py:module` directive + supports. .. versionadded:: 0.5 @@ -213,8 +215,8 @@ There are also new config values that you can set: ``"class"`` Only the class' docstring is inserted. This is the default. You can - still document ``__init__`` as a separate method using :rst:dir:`automethod` - or the ``members`` option to :rst:dir:`autoclass`. + still document ``__init__`` as a separate method using + :rst:dir:`automethod` or the ``members`` option to :rst:dir:`autoclass`. ``"both"`` Both the class' and the ``__init__`` method's docstring are concatenated and inserted. diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst index 76388a94c..cdd017917 100644 --- a/doc/ext/inheritance.rst +++ b/doc/ext/inheritance.rst @@ -17,7 +17,7 @@ It adds this directive: This directive has one or more arguments, each giving a module or class name. Class names can be unqualified; in that case they are taken to exist - in the currently described module (see :rst:dir:`module`). + in the currently described module (see :rst:dir:`py:module`). For each given class, and each class in each given module, the base classes are determined. Then, from all classes and their base classes, a graph is diff --git a/doc/ext/math.rst b/doc/ext/math.rst index b9f6ab12b..f2896c39c 100644 --- a/doc/ext/math.rst +++ b/doc/ext/math.rst @@ -17,15 +17,15 @@ if possible, reuse that support too. .. note:: - :mod:`sphinx.ext.mathbase` is not meant to be added to the - :confval:`extensions` config value, instead, use either - :mod:`sphinx.ext.pngmath` or :mod:`sphinx.ext.jsmath` as described below. + :mod:`.mathbase` is not meant to be added to the :confval:`extensions` config + value, instead, use either :mod:`sphinx.ext.pngmath` or + :mod:`sphinx.ext.jsmath` as described below. The input language for mathematics is LaTeX markup. This is the de-facto standard for plain-text math notation and has the added advantage that no further translation is necessary when building LaTeX output. -:mod:`mathbase` defines these new markup elements: +:mod:`.mathbase` defines these new markup elements: .. rst:role:: math diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst index bb1ed68ec..4453ab00a 100644 --- a/doc/markup/inline.rst +++ b/doc/markup/inline.rst @@ -260,7 +260,7 @@ in a different style: .. rst:role:: samp A piece of literal text, such as code. Within the contents, you can use - curly braces to indicate a "variable" part, as in :rst:dir:`file`. For + curly braces to indicate a "variable" part, as in :rst:role:`file`. For example, in ``:samp:`print 1+{variable}```, the part ``variable`` would be emphasized. diff --git a/doc/markup/toctree.rst b/doc/markup/toctree.rst index 474427d72..2c0a418a2 100644 --- a/doc/markup/toctree.rst +++ b/doc/markup/toctree.rst @@ -151,7 +151,7 @@ The special document names (and pages generated for them) are: :ref:`object descriptions `, and from :rst:dir:`index` directives. - The module index contains one entry per :rst:dir:`module` directive. + The Python module index contains one entry per :rst:dir:`py:module` directive. The search page contains a form that uses the generated JSON search index and JavaScript to full-text search the generated documents for search words; it diff --git a/doc/templating.rst b/doc/templating.rst index 6880663d3..193a90bd9 100644 --- a/doc/templating.rst +++ b/doc/templating.rst @@ -21,10 +21,10 @@ No. You have several other options: configuration value accordingly. * You can :ref:`write a custom builder ` that derives from - :class:`~sphinx.builders.StandaloneHTMLBuilder` and calls your template engine - of choice. + :class:`~sphinx.builders.html.StandaloneHTMLBuilder` and calls your template + engine of choice. -* You can use the :class:`~sphinx.builders.PickleHTMLBuilder` that produces +* You can use the :class:`~sphinx.builders.html.PickleHTMLBuilder` that produces pickle files with the page contents, and postprocess them using a custom tool, or use them in your Web application. @@ -261,9 +261,9 @@ in the future. .. data:: file_suffix - The value of the builder's :attr:`out_suffix` attribute, i.e. the file name - extension that the output files will get. For a standard HTML builder, this - is usually ``.html``. + The value of the builder's :attr:`~.SerializingHTMLBuilder.out_suffix` + attribute, i.e. the file name extension that the output files will get. For + a standard HTML builder, this is usually ``.html``. .. data:: has_source diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index fc8086995..cd87bfbda 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -356,6 +356,9 @@ class PyModule(Directive): env.domaindata['py']['modules'][modname] = \ (env.docname, self.options.get('synopsis', ''), self.options.get('platform', ''), 'deprecated' in self.options) + # make a duplicate entry in 'objects' to facilitate searching for the + # module in PythonDomain.find_obj() + env.domaindata['py']['objects'][modname] = (env.docname, 'module') targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] @@ -544,7 +547,7 @@ class PythonDomain(Domain): if fn == docname: del self.data['modules'][modname] - def find_obj(self, env, modname, classname, name, type, searchorder=0): + def find_obj(self, env, modname, classname, name, type, searchmode=0): """ Find a Python object for "name", perhaps using the given module and/or classname. Returns a list of (name, object entry) tuples. @@ -560,22 +563,31 @@ class PythonDomain(Domain): matches = [] newname = None - if searchorder == 1: - if modname and classname and \ - modname + '.' + classname + '.' + name in objects: - newname = modname + '.' + classname + '.' + name - elif modname and modname + '.' + name in objects: - newname = modname + '.' + name - elif name in objects: - newname = name - else: - # "fuzzy" searching mode - searchname = '.' + name - matches = [(name, objects[name]) for name in objects - if name.endswith(searchname)] + if searchmode == 1: + objtypes = self.objtypes_for_role(type) + if modname and classname: + fullname = modname + '.' + classname + '.' + name + if fullname in objects and objects[fullname][1] in objtypes: + newname = fullname + if not newname: + if modname and modname + '.' + name in objects and \ + objects[modname + '.' + name][1] in objtypes: + newname = modname + '.' + name + elif name in objects and objects[name][1] in objtypes: + newname = name + else: + # "fuzzy" searching mode + searchname = '.' + name + matches = [(name, objects[name]) for name in objects + if name.endswith(searchname) + and objects[name][1] in objtypes] else: + # NOTE: searching for exact match, object type is not considered if name in objects: newname = name + elif type == 'mod': + # only exact matches allowed for modules + return [] elif classname and classname + '.' + name in objects: newname = classname + '.' + name elif modname and modname + '.' + name in objects: @@ -597,33 +609,35 @@ class PythonDomain(Domain): def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): - if (type == 'mod' or - type == 'obj' and target in self.data['modules']): - docname, synopsis, platform, deprecated = \ - self.data['modules'].get(target, ('','','', '')) - if not docname: - return None - else: - title = '%s%s%s' % ((platform and '(%s) ' % platform), - synopsis, - (deprecated and ' (deprecated)' or '')) - return make_refnode(builder, fromdocname, docname, - 'module-' + target, contnode, title) + modname = node.get('py:module') + clsname = node.get('py:class') + searchmode = node.hasattr('refspecific') and 1 or 0 + matches = self.find_obj(env, modname, clsname, target, + type, searchmode) + if not matches: + return None + elif len(matches) > 1: + env.warn(fromdocname, + 'more than one target found for cross-reference ' + '%r: %s' % (target, + ', '.join(match[0] for match in matches)), + node.line) + name, obj = matches[0] + + if obj[1] == 'module': + # get additional info for modules + docname, synopsis, platform, deprecated = self.data['modules'][name] + assert docname == obj[0] + title = name + if synopsis: + title += ': ' + synopsis + if deprecated: + title += _(' (deprecated)') + if platform: + title += ' (' + platform + ')' + return make_refnode(builder, fromdocname, docname, + 'module-' + name, contnode, title) else: - modname = node.get('py:module') - clsname = node.get('py:class') - searchorder = node.hasattr('refspecific') and 1 or 0 - matches = self.find_obj(env, modname, clsname, target, - type, searchorder) - if not matches: - return None - elif len(matches) > 1: - env.warn(fromdocname, - 'more than one target found for cross-reference ' - '%r: %s' % (target, - ', '.join(match[0] for match in matches)), - node.line) - name, obj = matches[0] return make_refnode(builder, fromdocname, obj[0], name, contnode, name) diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 61809f330..98f40d845 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -28,7 +28,7 @@ class ReSTMarkup(ObjectDescription): """ def add_target_and_index(self, name, sig, signode): - targetname = name + '-' + self.objtype + targetname = self.objtype + '-' + name if targetname not in self.state.document.ids: signode['names'].append(targetname) signode['ids'].append(targetname) diff --git a/sphinx/environment.py b/sphinx/environment.py index 5edcb4d90..19ff62ca1 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -63,7 +63,7 @@ default_settings = { # This is increased every time an environment attribute is added # or changed to properly invalidate pickle files. -ENV_VERSION = 37 +ENV_VERSION = 38 default_substitutions = set([ From 0d8dd33b4d752277c8f9632b726c517ff167ef07 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 15:07:44 +0200 Subject: [PATCH 552/744] #471: Fix LaTeX references to figures. --- CHANGES | 2 ++ sphinx/writers/latex.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 33a3cca2c..41f0b1c4d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Release 1.0.2 (in development) ============================== +* #471: Fix LaTeX references to figures. + * #482: When doing a non-exact search, match only the given type of object. diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 5674b388c..03c90fe27 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -224,6 +224,7 @@ class LaTeXTranslator(nodes.NodeVisitor): else: self.top_sectionlevel = 1 self.next_section_ids = set() + self.next_figure_ids = set() # flags self.verbatim = None self.in_title = 0 @@ -887,11 +888,15 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def visit_figure(self, node): + ids = '' + for id in self.next_figure_ids: + ids += self.hypertarget(id, anchor=False) + self.next_figure_ids.clear() if node.has_key('width') and node.get('align', '') in ('left', 'right'): self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' % (node['align'] == 'right' and 'r' or 'l', node['width'])) - self.context.append('\\end{wrapfigure}\n') + self.context.append(ids + '\\end{wrapfigure}\n') else: if (not node.attributes.has_key('align') or node.attributes['align'] == 'center'): @@ -903,7 +908,7 @@ class LaTeXTranslator(nodes.NodeVisitor): align = '\\begin{flush%s}' % node.attributes['align'] align_end = '\\end{flush%s}' % node.attributes['align'] self.body.append('\\begin{figure}[htbp]%s\n' % align) - self.context.append('%s\\end{figure}\n' % align_end) + self.context.append(ids + align_end + '\\end{figure}\n') def depart_figure(self, node): self.body.append(self.context.pop()) @@ -983,6 +988,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.next_section_ids.add(node['refid']) self.next_section_ids.update(node['ids']) return + elif isinstance(next, nodes.figure): + if node.get('refid'): + self.next_figure_ids.add(node['refid']) + self.next_figure_ids.update(node['ids']) + return except IndexError: pass if 'refuri' in node: From f3c2220ad1007a3a57db868206968b98dcb5abce Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 15:16:48 +0200 Subject: [PATCH 553/744] Include hypcap package, to get better references to floats. --- sphinx/texinputs/sphinx.sty | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index be8a6c15d..b5381392a 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -446,6 +446,7 @@ linkcolor=InnerLinkColor,filecolor=OuterLinkColor, menucolor=OuterLinkColor,urlcolor=OuterLinkColor, citecolor=InnerLinkColor]{hyperref} +\RequirePackage[figure,table]{hypcap} % From docutils.writers.latex2e \providecommand{\DUspan}[2]{% From 5b2b0ecfd3dda77ad3a110286eea24fd484b42dc Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 15:17:12 +0200 Subject: [PATCH 554/744] Better references to tables, as well. --- sphinx/writers/latex.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 03c90fe27..1bb534a0b 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -225,6 +225,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.top_sectionlevel = 1 self.next_section_ids = set() self.next_figure_ids = set() + self.next_table_ids = set() # flags self.verbatim = None self.in_title = 0 @@ -634,7 +635,10 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('{|' + ('L|' * self.table.colcount) + '}\n') if self.table.longtable and self.table.caption is not None: self.body.append(u'\\caption{%s} \\\\\n' % self.table.caption) - + if self.table.caption is not None: + for id in self.next_table_ids: + self.body.append(self.hypertarget(id, anchor=False)) + self.next_table_ids.clear() if self.table.longtable: self.body.append('\\hline\n') self.body.append('\\endfirsthead\n\n') @@ -989,10 +993,19 @@ class LaTeXTranslator(nodes.NodeVisitor): self.next_section_ids.update(node['ids']) return elif isinstance(next, nodes.figure): + # labels for figures go in the figure body, not before if node.get('refid'): self.next_figure_ids.add(node['refid']) self.next_figure_ids.update(node['ids']) return + elif isinstance(next, nodes.table): + # same for tables, but only if they have a caption + for n in node: + if isinstance(n, nodes.title): + if node.get('refid'): + self.next_table_ids.add(node['refid']) + self.next_table_ids.update(node['ids']) + return except IndexError: pass if 'refuri' in node: From b9529a21a5c9252c7298836467034b0f2c3e9d07 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 15:17:23 +0200 Subject: [PATCH 555/744] Fix references to reST domain items. --- sphinx/domains/rst.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 98f40d845..d3ffc6bdc 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -130,8 +130,9 @@ class ReSTDomain(Domain): if (objtype, target) in objects: return make_refnode(builder, fromdocname, objects[objtype, target], - target, contnode, target) + objtype + '-' + target, + contnode, target + ' ' + objtype) def get_objects(self): for (typ, name), docname in self.data['objects'].iteritems(): - yield name, name, typ, docname, name, 1 + yield name, name, typ, docname, typ + '-' + name, 1 From 279d025ffe93a05ac3be7b1432409d77e9bbb7a0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 15:29:45 +0200 Subject: [PATCH 556/744] Add Tau. --- EXAMPLES | 1 + 1 file changed, 1 insertion(+) diff --git a/EXAMPLES b/EXAMPLES index 44c511e9e..778b235e1 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -97,6 +97,7 @@ Documentation using the sphinxdoc theme * Satchmo: http://www.satchmoproject.com/docs/svn/ * Sphinx: http://sphinx.pocoo.org/ * Sqlkit: http://sqlkit.argolinux.org/ +* Tau: http://www.tango-controls.org/static/tau/latest/doc/html/index.html * Total Open Station: http://tops.berlios.de/ * WebFaction: http://docs.webfaction.com/ From 92142bbdb68e98515112c34ec70ca18fd6f3ea6e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 15:42:15 +0200 Subject: [PATCH 557/744] Allow references to PEPs and RFCs with explicit anchors. --- CHANGES | 2 ++ doc/markup/inline.rst | 6 ++++-- sphinx/roles.py | 14 ++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 41f0b1c4d..ad51ffeff 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Release 1.0.2 (in development) ============================== +* Allow references to PEPs and RFCs with explicit anchors. + * #471: Fix LaTeX references to figures. * #482: When doing a non-exact search, match only the given type diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst index 4453ab00a..4b704228f 100644 --- a/doc/markup/inline.rst +++ b/doc/markup/inline.rst @@ -274,13 +274,15 @@ The following roles generate external links: A reference to a Python Enhancement Proposal. This generates appropriate index entries. The text "PEP *number*\ " is generated; in the HTML output, - this text is a hyperlink to an online copy of the specified PEP. + this text is a hyperlink to an online copy of the specified PEP. You can + link to a specific section by saying ``:pep:`number#anchor```. .. rst:role:: rfc A reference to an Internet Request for Comments. This generates appropriate index entries. The text "RFC *number*\ " is generated; in the HTML output, - this text is a hyperlink to an online copy of the specified RFC. + this text is a hyperlink to an online copy of the specified RFC. You can + link to a specific section by saying ``:rfc:`number#anchor```. Note that there are no special roles for including hyperlinks as you can use diff --git a/sphinx/roles.py b/sphinx/roles.py index bacdad5b6..0ea0ec485 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -173,6 +173,10 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, indexnode['entries'] = [ ('single', _('Python Enhancement Proposals!PEP %s') % text, targetid, 'PEP %s' % text)] + anchor = '' + anchorindex = text.find('#') + if anchorindex > 0: + text, anchor = text[:anchorindex], text[anchorindex:] try: pepnum = int(text) except ValueError: @@ -182,12 +186,17 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, return [prb], [msg] ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum sn = nodes.strong('PEP '+text, 'PEP '+text) - rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ]) + rn = nodes.reference('', '', internal=False, refuri=ref+anchor, + classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] elif typ == 'rfc': indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, 'RFC %s' % text)] + anchor = '' + anchorindex = text.find('#') + if anchorindex > 0: + text, anchor = text[:anchorindex], text[anchorindex:] try: rfcnum = int(text) except ValueError: @@ -197,7 +206,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, return [prb], [msg] ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum sn = nodes.strong('RFC '+text, 'RFC '+text) - rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ]) + rn = nodes.reference('', '', internal=False, refuri=ref+anchor, + classes=[typ]) rn += sn return [indexnode, targetnode, rn], [] From 5b09e2b41a2c40522f2c6dc4063526ea3a4fc594 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 15:47:10 +0200 Subject: [PATCH 558/744] Fix unwanted styling of C domain references (because of a namespace clash with Pygments styles). --- CHANGES | 3 +++ sphinx/highlighting.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index ad51ffeff..0e06938df 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.2 (in development) ============================== +* Fix unwanted styling of C domain references (because of a + namespace clash with Pygments styles). + * Allow references to PEPs and RFCs with explicit anchors. * #471: Fix LaTeX references to figures. diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index f5ea859cb..0dcbc0219 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -240,7 +240,7 @@ class PygmentsBridge(object): # no HTML styles needed return '' if self.dest == 'html': - return self.fmter[0].get_style_defs() + return self.fmter[0].get_style_defs('.highlight') else: styledefs = self.fmter[0].get_style_defs() # workaround for Pygments < 0.12 From df107e70f8f684c7fd4475f596ffbc3151478f41 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 16:03:36 +0200 Subject: [PATCH 559/744] Allow breaking long signatures, continuing with backlash-escaped newlines. --- CHANGES | 7 +++++-- doc/domains.rst | 11 ++++++++++- sphinx/directives/__init__.py | 7 +++++-- tests/root/objects.txt | 4 ++++ tests/test_build_html.py | 4 +++- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 0e06938df..a158267b0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,8 +1,11 @@ Release 1.0.2 (in development) ============================== -* Fix unwanted styling of C domain references (because of a - namespace clash with Pygments styles). +* Allow breaking long signatures, continuing with backlash-escaped + newlines. + +* Fix unwanted styling of C domain references (because of a namespace + clash with Pygments styles). * Allow references to PEPs and RFCs with explicit anchors. diff --git a/doc/domains.rst b/doc/domains.rst index 6bbe12150..8cd7a0c7d 100644 --- a/doc/domains.rst +++ b/doc/domains.rst @@ -52,10 +52,19 @@ flag ``:noindex:``. An example using a Python domain directive:: .. py:function:: spam(eggs) ham(eggs) - :noindex: Spam or ham the foo. +This describes the two Python functions ``spam`` and ``ham``. (Note that when +signatures become too long, you can break them if you add a backslash to lines +that are continued in the next line. Example:: + + .. py:function:: filterwarnings(action, message='', category=Warning, \ + module='', lineno=0, append=False) + :noindex: + +(This example also shows how to use the ``:noindex:`` flag.) + The domains also provide roles that link back to these object descriptions. For example, to link to one of the functions described in the example above, you could say :: diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 6c03b8e5f..48c44178d 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -32,6 +32,7 @@ except AttributeError: # RE to strip backslash escapes +nl_escape_re = re.compile(r'\\\n') strip_backslash_re = re.compile(r'\\(?=[^\\])') @@ -57,10 +58,12 @@ class ObjectDescription(Directive): """ Retrieve the signatures to document from the directive arguments. By default, signatures are given as arguments, one per line. + + Backslash-escaping of newlines is supported. """ + lines = nl_escape_re.sub('', self.arguments[0]).split('\n') # remove backslashes to support (dummy) escapes; helps Vim highlighting - return [strip_backslash_re.sub('', sig.strip()) - for sig in self.arguments[0].split('\n')] + return [strip_backslash_re.sub('', line.strip()) for line in lines] def handle_signature(self, sig, signode): """ diff --git a/tests/root/objects.txt b/tests/root/objects.txt index d6b8bdf64..e62f6d962 100644 --- a/tests/root/objects.txt +++ b/tests/root/objects.txt @@ -41,6 +41,10 @@ Testing object descriptions .. function:: func_without_module2() -> annotation +.. object:: long(parameter, \ + list) + another one + .. class:: TimeInt :param moo: |test| diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 813c962fe..6857d3309 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -38,7 +38,7 @@ http://www.python.org/logo.png reading included file u'wrongenc.inc' seems to be wrong, try giving an \ :encoding: option\\n? %(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png -%(root)s/objects.txt:86: WARNING: using old C markup; please migrate to \ +%(root)s/objects.txt:\\d*: WARNING: using old C markup; please migrate to \ new-style markup \(e.g. c:function instead of cfunction\), see \ http://sphinx.pocoo.org/domains.html """ @@ -161,6 +161,8 @@ HTML_XPATH = { 'objects.html': [ (".//dt[@id='mod.Cls.meth1']", ''), (".//dt[@id='errmod.Error']", ''), + (".//dt/tt", r'long\(parameter,\s* list\)'), + (".//dt/tt", 'another one'), (".//a[@href='#mod.Cls'][@class='reference internal']", ''), (".//dl[@class='userdesc']", ''), (".//dt[@id='userdesc-myobj']", ''), From 7518d194376994f4efa3234a12a0435b62dd4df0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 18:59:04 +0200 Subject: [PATCH 560/744] #469: document __all__ ordering in members. --- doc/ext/autodoc.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst index 813331015..a1d9d98fb 100644 --- a/doc/ext/autodoc.rst +++ b/doc/ext/autodoc.rst @@ -84,6 +84,9 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, will document all non-private member functions and properties (that is, those whose name doesn't start with ``_``). + For modules, ``__all__`` will be respected when looking for members; the + order of the members will also be the order in ``__all__``. + You can also give an explicit list of members; only these will then be documented:: From 0ed09e9c5314290db52de09fe59a688ed3c44009 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 5 Aug 2010 22:35:12 +0200 Subject: [PATCH 561/744] #258: get a bit smarter about closing double quotes. --- sphinx/util/smartypants.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py index 75888ea4d..f83f5689b 100644 --- a/sphinx/util/smartypants.py +++ b/sphinx/util/smartypants.py @@ -83,6 +83,7 @@ def sphinx_smarty_pants(t): # Constants for quote education. punct_class = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]""" +end_of_word_class = r"""[\s.,;:!?)]""" close_class = r"""[^\ \t\r\n\[\{\(\-]""" dec_dashes = r"""–|—""" @@ -117,8 +118,8 @@ opening_double_quotes_regex = re.compile(r""" closing_double_quotes_regex = re.compile(r""" #(%s)? # character that indicates the quote should be closing " - (?=\s) - """ % (close_class,), re.VERBOSE) + (?=%s) + """ % (close_class, end_of_word_class), re.VERBOSE) closing_double_quotes_regex_2 = re.compile(r""" (%s) # character that indicates the quote should be closing From 85a63e297eee3e2cd5c2dda6f4bdc7b405e3fe94 Mon Sep 17 00:00:00 2001 From: Roland Meister Date: Thu, 5 Aug 2010 23:08:55 +0200 Subject: [PATCH 562/744] Empty titles in epub_pre/post_files add no entry in toc.ncx. --- doc/config.rst | 5 +++-- sphinx/builders/epub.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/config.rst b/doc/config.rst index 5326387de..32a219351 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -757,7 +757,7 @@ the `Dublin Core metadata `_. Additional files that should be inserted before the text generated by Sphinx. It is a list of tuples containing the file name and the title. - Example:: + If the title is empty, no entry is added to :file:`toc.ncx`. Example:: epub_pre_files = [ ('index.html', 'Welcome'), @@ -769,7 +769,8 @@ the `Dublin Core metadata `_. Additional files that should be inserted after the text generated by Sphinx. It is a list of tuples containing the file name and the title. This option - can be used to add an appendix. The default value is ``[]``. + can be used to add an appendix. If the title is empty, no entry is added + to :file:`toc.ncx`. The default value is ``[]``. .. confval:: epub_exclude_files diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index b40c8280e..21b674a6e 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -439,6 +439,8 @@ class EpubBuilder(StandaloneHTMLBuilder): level = 1 lastnode = None for node in nodes: + if not node['text']: + continue file = node['refuri'].split('#')[0] if file in self.ignored_files: continue From caca655a332ed8ef55c29b601efdaf9b897b7298 Mon Sep 17 00:00:00 2001 From: Roland Meister Date: Sat, 7 Aug 2010 21:44:05 +0200 Subject: [PATCH 563/744] Fix UnicodeError for unicode filenames while writing the zipfile. --- sphinx/builders/epub.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index 21b674a6e..66b507d20 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -509,5 +509,7 @@ class EpubBuilder(StandaloneHTMLBuilder): epub.write(path.join(outdir, 'mimetype'), 'mimetype', \ zipfile.ZIP_STORED) for file in projectfiles: + if isinstance(file, unicode): + file = file.encode('utf-8') epub.write(path.join(outdir, file), file, zipfile.ZIP_DEFLATED) epub.close() From 8a17f7a84fdb2bc62a64068875dac65faf223572 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 10 Aug 2010 17:18:46 +0200 Subject: [PATCH 564/744] Prepare for 1.0.2. --- CHANGES | 4 ++-- sphinx/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index a158267b0..da9a80d75 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,5 @@ -Release 1.0.2 (in development) -============================== +Release 1.0.2 (Aug 05, 2010) +============================ * Allow breaking long signatures, continuing with backlash-escaped newlines. diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 50aa28cd6..c193d095c 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -12,8 +12,8 @@ import sys from os import path -__version__ = '1.0.1+' -__released__ = '1.0.1' # used when Sphinx builds its own docs +__version__ = '1.0.2' +__released__ = '1.0.2' # used when Sphinx builds its own docs package_dir = path.abspath(path.dirname(__file__)) From a484c6f793e4dc352b98d0202b6d3154664eac2c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 14 Aug 2010 16:51:04 +0200 Subject: [PATCH 565/744] #490: Fix cross-references to objects of types added by the :func:`~.Sphinx.add_object_type` API function. --- CHANGES | 3 +++ sphinx/domains/std.py | 8 +++++++- tests/test_build_html.py | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index da9a80d75..bd368e17c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.2 (Aug 05, 2010) ============================ +* #490: Fix cross-references to objects of types added by the + :func:`~.Sphinx.add_object_type` API function. + * Allow breaking long signatures, continuing with backlash-escaped newlines. diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 63a3bf6dc..6194ace9a 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -484,7 +484,13 @@ class StandardDomain(Domain): return make_refnode(builder, fromdocname, docname, labelid, contnode) else: - docname, labelid = self.data['objects'].get((typ, target), ('', '')) + objtypes = self.objtypes_for_role(typ) or [] + for objtype in objtypes: + if (objtype, target) in self.data['objects']: + docname, labelid = self.data['objects'][objtype, target] + break + else: + docname, labelid = '', '' if not docname: if typ == 'term': env.warn(node.get('refdoc', fromdocname), diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 6857d3309..912a0b426 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -166,7 +166,7 @@ HTML_XPATH = { (".//a[@href='#mod.Cls'][@class='reference internal']", ''), (".//dl[@class='userdesc']", ''), (".//dt[@id='userdesc-myobj']", ''), - (".//a[@href='#userdesc-myobj']", ''), + (".//a[@href='#userdesc-myobj'][@class='reference internal']", ''), # C references (".//span[@class='pre']", 'CFunction()'), (".//a[@href='#Sphinx_DoSomething']", ''), From 572e6019817685b811c637deb0be9293732536ab Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 14 Aug 2010 17:04:25 +0200 Subject: [PATCH 566/744] Fix handling of doc field types for different directive types. --- CHANGES | 2 ++ sphinx/directives/__init__.py | 1 - sphinx/util/docfields.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index bd368e17c..89f33dd27 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,8 @@ Release 1.0.2 (Aug 05, 2010) * #490: Fix cross-references to objects of types added by the :func:`~.Sphinx.add_object_type` API function. +* Fix handling of doc field types for different directive types. + * Allow breaking long signatures, continuing with backlash-escaped newlines. diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 48c44178d..b7adbcafd 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -162,7 +162,6 @@ class ObjectDescription(Directive): self.env.temp_data['object'] = self.names[0] self.before_content() self.state.nested_parse(self.content, self.content_offset, contentnode) - #self.handle_doc_fields(contentnode) DocFieldTransformer(self).transform_all(contentnode) self.env.temp_data['object'] = None self.after_content() diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 6ce6d82bf..89f81e8cb 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -167,7 +167,7 @@ class DocFieldTransformer(object): def __init__(self, directive): self.domain = directive.domain - if not hasattr(directive, '_doc_field_type_map'): + if '_doc_field_type_map' not in directive.__class__.__dict__: directive.__class__._doc_field_type_map = \ self.preprocess_fieldtypes(directive.__class__.doc_field_types) self.typemap = directive._doc_field_type_map From d1cc9be1d00bfe88887aeec74ec34f81edefb9fa Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 14 Aug 2010 17:09:23 +0200 Subject: [PATCH 567/744] Update release date. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 89f33dd27..a640ea60b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -Release 1.0.2 (Aug 05, 2010) +Release 1.0.2 (Aug 14, 2010) ============================ * #490: Fix cross-references to objects of types added by the From 9cc47218b64cb5feac88fbc88c5fed66f4475850 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 17 Aug 2010 05:15:14 +0200 Subject: [PATCH 569/744] Fix missing print statement. --- sphinx/setup_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py index e2d83bc6c..8974b9888 100644 --- a/sphinx/setup_command.py +++ b/sphinx/setup_command.py @@ -144,7 +144,7 @@ class BuildDoc(Command): except Exception, err: from docutils.utils import SystemMessage if isinstance(err, SystemMessage): - sys.stderr, darkred('reST markup error:') + print >>sys.stderr, darkred('reST markup error:') print >>sys.stderr, err.args[0].encode('ascii', 'backslashreplace') else: From 9d7b75aa5dcab31810d755e3c8204a4e9615cef0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 19 Aug 2010 00:02:26 +0200 Subject: [PATCH 570/744] Clarify example. --- doc/rest.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/rest.rst b/doc/rest.rst index 2b6eee6b4..693075728 100644 --- a/doc/rest.rst +++ b/doc/rest.rst @@ -325,14 +325,15 @@ directives.) Looking at this example, :: .. function:: foo(x) foo(y, z) - :bar: no + :module: some.module.name Return a line of text input from the user. ``function`` is the directive name. It is given two arguments here, the -remainder of the first line and the second line, as well as one option ``bar`` -(as you can see, options are given in the lines immediately following the -arguments and indicated by the colons). +remainder of the first line and the second line, as well as one option +``module`` (as you can see, options are given in the lines immediately following +the arguments and indicated by the colons). Options must be indented to the +same level as the directive content. The directive content follows after a blank line and is indented relative to the directive start. From 3deb263c2ab85664beedee867dd56cb40dd88270 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 23 Aug 2010 13:14:27 +0000 Subject: [PATCH 571/744] #502: only display
                  items with bullets. --- sphinx/themes/haiku/static/haiku.css_t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/themes/haiku/static/haiku.css_t b/sphinx/themes/haiku/static/haiku.css_t index 93007dfb9..61f14e0d8 100644 --- a/sphinx/themes/haiku/static/haiku.css_t +++ b/sphinx/themes/haiku/static/haiku.css_t @@ -292,7 +292,7 @@ li { line-height: 1.3; } -div.content li { +div.content ul > li { -moz-background-clip:border; -moz-background-inline-policy:continuous; -moz-background-origin:padding; From 80ad3d7d696e1ca0dfcbf214f9e99ac94f2df9c2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 23 Aug 2010 13:17:56 +0000 Subject: [PATCH 572/744] #503: put agogo rellinks in correct order. --- sphinx/themes/agogo/layout.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/themes/agogo/layout.html b/sphinx/themes/agogo/layout.html index 4a7e263ba..719d2d5ab 100644 --- a/sphinx/themes/agogo/layout.html +++ b/sphinx/themes/agogo/layout.html @@ -22,7 +22,7 @@

                  {{ shorttitle|e }}

                  {%- endblock %}
                  - {%- for rellink in rellinks %} + {%- for rellink in rellinks|reverse %} {{ rellink[3] }} {%- if not loop.last %}{{ reldelim2 }}{% endif %} @@ -67,7 +67,7 @@