diff options
| author | DasIch <dasdasich@gmail.com> | 2010-08-15 11:55:46 +0200 |
|---|---|---|
| committer | DasIch <dasdasich@gmail.com> | 2010-08-15 11:55:46 +0200 |
| commit | 7926331c6fa1d80e08ae4d6c19bc1cfdce9e16c8 (patch) | |
| tree | c31e72f4dc43e03502213c08a6d036492ea98856 /sphinx | |
| parent | c8f16b61cc6682fbbe47a5902b3c92e4b9336be0 (diff) | |
| parent | c0e116c026aebb10854d8e083e702f6290403116 (diff) | |
| download | sphinx-7926331c6fa1d80e08ae4d6c19bc1cfdce9e16c8.tar.gz | |
merge with lehmannro/sphinx-i18n
Diffstat (limited to 'sphinx')
49 files changed, 1731 insertions, 423 deletions
diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 37b6ecef..1ea2e7bf 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -9,11 +9,14 @@ :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 -__version__ = '1.0b2+' -__released__ = '1.0b2' # used when Sphinx builds its own docs +__version__ = '1.1pre' +__released__ = '1.1 (hg)' # used when Sphinx builds its own docs package_dir = path.abspath(path.dirname(__file__)) @@ -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) diff --git a/sphinx/application.py b/sphinx/application.py index 3ffd86c2..b3d2aebc 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': '', @@ -109,7 +106,9 @@ class Sphinx(object): if self.confdir is None: self.confdir = self.srcdir - # load all extension modules + # backwards compatibility: activate old C markup + self.setup_extension('sphinx.ext.oldcmarkup') + # load all user-given extension modules for extension in self.config.extensions: self.setup_extension(extension) # the config file itself can be an extension diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index 9767391e..aea07d4d 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -11,15 +11,17 @@ """ import os +import re import codecs -from os import path import zipfile +from os import path from docutils import nodes -from docutils.transforms import Transform +from sphinx import addnodes from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.util.osutil import EEXIST +from sphinx.util.smartypants import sphinx_smarty_pants as ssp # (Fragment) templates from which the metainfo files content.opf, toc.ncx, @@ -119,29 +121,10 @@ _media_types = { '.ttf': 'application/x-font-ttf', } - -# The transform to show link targets - -class VisibleLinksTransform(Transform): - """ - Add the link target of referances to the text, unless it is already - present in the description. - """ - - # This transform must run after the references transforms - default_priority = 680 - - def apply(self): - for ref in self.document.traverse(nodes.reference): - uri = ref.get('refuri', '') - if ( uri.startswith('http:') or uri.startswith('https:') or \ - uri.startswith('ftp:') ) and uri not in ref.astext(): - uri = _link_target_template % {'uri': uri} - if uri: - idx = ref.parent.index(ref) + 1 - link = nodes.inline(uri, uri) - link['classes'].append(_css_link_target_class) - ref.parent.insert(idx, link) +# Regular expression to match colons only in local fragment identifiers. +# If the URI contains a colon before the #, +# it is an external link that should not change. +_refuri_re = re.compile("([^#:]*#)(.*)") # The epub publisher @@ -170,7 +153,6 @@ class EpubBuilder(StandaloneHTMLBuilder): # the output files for epub must be .html only self.out_suffix = '.html' self.playorder = 0 - self.app.add_transform(VisibleLinksTransform) def get_theme_config(self): return self.config.epub_theme, {} @@ -194,17 +176,20 @@ class EpubBuilder(StandaloneHTMLBuilder): """Collect section titles, their depth in the toc and the refuri.""" # XXX: is there a better way than checking the attribute # toctree-l[1-8] on the parent node? - if isinstance(doctree, nodes.reference): + if isinstance(doctree, nodes.reference) and doctree.has_key('refuri'): + refuri = doctree['refuri'] + if refuri.startswith('http://') or refuri.startswith('https://') \ + or refuri.startswith('irc:') or refuri.startswith('mailto:'): + return result classes = doctree.parent.attributes['classes'] - level = 1 - for l in range(8, 0, -1): # or range(1, 8)? - if (_toctree_template % l) in classes: - level = l - result.append({ - 'level': level, - 'refuri': self.esc(doctree['refuri']), - 'text': self.esc(doctree.astext()) - }) + for level in range(8, 0, -1): # or range(1, 8)? + if (_toctree_template % level) in classes: + result.append({ + 'level': level, + 'refuri': self.esc(refuri), + 'text': ssp(self.esc(doctree.astext())) + }) + break else: for elem in doctree.children: result = self.get_refnodes(elem, result) @@ -220,21 +205,97 @@ class EpubBuilder(StandaloneHTMLBuilder): self.refnodes.insert(0, { 'level': 1, 'refuri': self.esc(self.config.master_doc + '.html'), - 'text': self.esc(self.env.titles[self.config.master_doc].astext()) + 'text': ssp(self.esc( + self.env.titles[self.config.master_doc].astext())) }) for file, text in reversed(self.config.epub_pre_files): self.refnodes.insert(0, { 'level': 1, - 'refuri': self.esc(file + '.html'), - 'text': self.esc(text) + 'refuri': self.esc(file), + 'text': ssp(self.esc(text)) }) for file, text in self.config.epub_post_files: self.refnodes.append({ 'level': 1, - 'refuri': self.esc(file + '.html'), - 'text': self.esc(text) + 'refuri': self.esc(file), + 'text': ssp(self.esc(text)) }) + def fix_fragment(self, match): + """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. + """ + for node in tree.traverse(nodes.reference): + if 'refuri' in node: + m = _refuri_re.match(node['refuri']) + if m: + node['refuri'] = self.fix_fragment(m) + if 'refid' in node: + node['refid'] = node['refid'].replace(':', '-') + for node in tree.traverse(addnodes.desc_signature): + ids = node.attributes['ids'] + newids = [] + for id in ids: + newids.append(id.replace(':', '-')) + node.attributes['ids'] = newids + + def add_visible_links(self, tree): + """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 + uri.startswith('ftp:')) and uri not in node.astext(): + uri = _link_target_template % {'uri': uri} + if uri: + idx = node.parent.index(node) + 1 + link = nodes.inline(uri, uri) + link['classes'].append(_css_link_target_class) + node.parent.insert(idx, link) + + 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. + """ + self.fix_ids(doctree) + self.add_visible_links(doctree) + return StandaloneHTMLBuilder.write_doc(self, docname, doctree) + + def fix_genindex(self, tree): + """Fix href attributes for genindex pages. + """ + # XXX: modifies tree inline + # Logic modeled from themes/basic/genindex.html + for key, columns in tree: + for entryname, (links, subitems) in columns: + for (i, link) in enumerate(links): + m = _refuri_re.match(link) + if m: + links[i] = self.fix_fragment(m) + for subentryname, subentrylinks in subitems: + for (i, link) in enumerate(subentrylinks): + m = _refuri_re.match(link) + if m: + subentrylinks[i] = self.fix_fragment(m) + + 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. + """ + if pagename.startswith('genindex'): + self.fix_genindex(addctx['genindexentries']) + StandaloneHTMLBuilder.handle_page(self, pagename, addctx, templatename, + outfilename, event_arg) + # Finish by building the epub file def handle_finish(self): @@ -380,7 +441,7 @@ class EpubBuilder(StandaloneHTMLBuilder): navstack.append(navlist) navlist = [] level += 1 - if lastnode: + if lastnode and self.config.epub_tocdup: # Insert starting point in subtoc with same playOrder navlist.append(self.new_navpoint(lastnode, level, False)) navlist.append(self.new_navpoint(node, level)) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 6b3eada7..5a7d49cd 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -30,12 +30,12 @@ from docutils.frontend import OptionParser from docutils.readers.doctree import Reader as DoctreeReader from sphinx import package_dir, __version__ -from sphinx.util import copy_static_entry +from sphinx.util import jsonimpl, copy_static_entry 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 @@ -47,14 +47,6 @@ from sphinx.util.console import bold, darkgreen, brown from sphinx.writers.html import HTMLWriter, HTMLTranslator, \ SmartyPantsHTMLTranslator -try: - import json -except ImportError: - try: - import simplejson as json - except ImportError: - json = None - #: the filename for the inventory of objects INVENTORY_FILENAME = 'objects.inv' #: the filename for the "last build" file (for serializing builders) @@ -71,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' @@ -99,7 +92,7 @@ class StandaloneHTMLBuilder(Builder): self.init_templates() self.init_highlighter() self.init_translator_class() - if self.config.html_file_suffix: + if self.config.html_file_suffix is not None: self.out_suffix = self.config.html_file_suffix if self.config.html_link_suffix is not None: @@ -154,8 +147,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('utf-8')).hexdigest() + self.tags_hash = md5(unicode(sorted(self.tags)).encode('utf-8')) \ + .hexdigest() old_config_hash = old_tags_hash = '' try: fp = open(path.join(self.outdir, '.buildinfo')) @@ -207,7 +201,7 @@ class StandaloneHTMLBuilder(Builder): """Utility: Render a lone doctree node.""" if node is None: return {'fragment': ''} - doc = new_document('<partial node>') + doc = new_document(b('<partial node>')) doc.append(node) if self._publisher is None: @@ -735,10 +729,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 \ @@ -750,11 +746,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() @@ -766,7 +760,10 @@ class StandaloneHTMLBuilder(Builder): 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: @@ -855,8 +852,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, @@ -869,7 +872,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): rellinks = [], sourcename = '', toc = toc, - display_toc = True, + display_toc = display_toc, ) def write(self, *ignored): @@ -917,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 @@ -939,6 +943,17 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): return docname[:-5] # up to sep return docname + SEP + def dump_context(self, context, filename): + 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() + def handle_page(self, pagename, ctx, templatename='page.html', outfilename=None, event_arg=None): ctx['current_page_name'] = pagename @@ -952,11 +967,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 @@ -969,11 +980,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) @@ -993,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' @@ -1007,15 +1016,17 @@ class JSONHTMLBuilder(SerializingHTMLBuilder): """ A builder that dumps the generated HTML into JSON files. """ - implementation = json - indexer_format = json + implementation = jsonimpl + implementation_dumps_unicode = True + indexer_format = jsonimpl + indexer_dumps_unicode = True name = 'json' out_suffix = '.fjson' globalcontext_filename = 'globalcontext.json' searchindex_filename = 'searchindex.json' def init(self): - if json is None: + if jsonimpl.json is None: raise SphinxError( 'The module simplejson (or json in Python >= 2.6) ' 'is not available. The JSONHTMLBuilder builder will not work.') diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 538f4c84..e3a58e72 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 = ' <param name="%s" value="%s">\n' % (name, value) - f.write(item.encode('ascii', 'xmlcharrefreplace')) + f.write(item.encode('ascii', 'xmlcharrefreplace') + .decode('ascii')) title = cgi.escape(title) f.write('<LI> <OBJECT type="text/sitemap">\n') write_param('Keyword', title) diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index c9bc363d..4a0bab63 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -16,7 +16,7 @@ from urllib2 import build_opener, HTTPError from docutils import nodes from sphinx.builders import Builder -from sphinx.util.console import purple, red, darkgreen +from sphinx.util.console import purple, red, darkgreen, darkgray # create an opener that will simulate a browser user-agent opener = build_opener() @@ -71,9 +71,12 @@ class CheckExternalLinksBuilder(Builder): 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) if uri[0:5] == 'http:' or uri[0:6] == 'https:': - if lineno: - self.info('(line %3d) ' % lineno, nonl=1) self.info(uri, nonl=1) if uri in self.broken: @@ -98,15 +101,9 @@ class CheckExternalLinksBuilder(Builder): self.write_entry('redirected', docname, lineno, uri + ' to ' + s) self.redirected[uri] = (r, s) - elif len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:': - return else: - self.warn(uri + ' - ' + red('malformed!')) - self.write_entry('malformed', docname, lineno, uri) - if self.app.quiet: - self.warn('malformed link: %s' % uri, - '%s:%s' % (self.env.doc2path(docname), lineno)) - self.app.statuscode = 1 + self.info(uri + ' - ' + darkgray('local')) + self.write_entry('local', docname, lineno, uri) if self.broken: self.app.statuscode = 1 diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index ffc52334..e86f1921 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 = [] @@ -230,7 +238,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: diff --git a/sphinx/config.py b/sphinx/config.py index e1075ff6..6c27f85f 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -11,13 +11,18 @@ import os import re +import sys from os import path from sphinx.errors import ConfigError from sphinx.util.osutil import make_filename +from sphinx.util.pycompat import bytes, b, convert_with_2to3 -nonascii_re = re.compile(r'[\x80-\xff]') +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.""" @@ -124,6 +129,7 @@ class Config(object): epub_post_files = ([], 'env'), epub_exclude_files = ([], 'env'), epub_tocdepth = (3, 'env'), + epub_tocdup = (True, 'env'), # LaTeX options latex_documents = ([], None), @@ -162,12 +168,30 @@ 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) - execfile(config['__file__'], 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('There is a syntax error in your ' - 'configuration file: ' + str(err)) + raise ConfigError(CONFIG_SYNTAX_ERROR % err) finally: os.chdir(olddir) @@ -181,10 +205,11 @@ 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/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 6c03b8e5..48c44178 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/sphinx/directives/code.py b/sphinx/directives/code.py index aff4395f..1808cdab 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -102,7 +102,7 @@ class LiteralInclude(Directive): rel_fn = filename[1:] else: docdir = path.dirname(env.doc2path(env.docname, base=None)) - rel_fn = path.normpath(path.join(docdir, filename)) + rel_fn = path.join(docdir, filename) try: fn = path.join(env.srcdir, rel_fn) except UnicodeDecodeError: @@ -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, 'rb'), codec_info[2], codec_info[3], 'strict') lines = f.readlines() f.close() diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 5ce1ce1a..332c4084 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -215,12 +215,7 @@ class VersionChange(Directive): else: ret = [node] env = self.state.document.settings.env - env.versionchanges.setdefault(node['version'], []).append( - (node['type'], env.temp_data['docname'], self.lineno, - # XXX: python domain specific - env.temp_data.get('py:module'), - env.temp_data.get('object'), - node.astext())) + env.note_versionchange(node['type'], node['version'], node, self.lineno) return ret diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 4dac8925..8df89459 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): @@ -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) diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 890af8e1..582e2adc 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -56,7 +56,7 @@ class JSObject(ObjectDescription): else: # just a function or constructor objectname = '' - fullname = '' + fullname = name signode['object'] = objectname signode['fullname'] = fullname diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index fc808699..cd87bfbd 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 04092eab..d3ffc6bd 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -28,9 +28,10 @@ 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 = self.objtype + '-' + name + if targetname not in self.state.document.ids: + 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': @@ -129,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 diff --git a/sphinx/environment.py b/sphinx/environment.py index b03e4625..6339675b 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 @@ -40,10 +41,11 @@ from sphinx.util import url_re, get_matching_docs, docname_join, \ 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 +from sphinx.util.pycompat import all, class_types from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _, init as init_locale +fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() orig_role_function = roles.role orig_directive_function = directives.directive @@ -64,7 +66,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 = 38 default_substitutions = set([ @@ -81,7 +83,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): @@ -289,7 +291,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) @@ -421,14 +423,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): """ @@ -666,6 +668,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_): @@ -687,7 +691,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(fs_encoding)) pub.set_destination(None, None) try: pub.publish() @@ -771,6 +775,12 @@ class BuildEnvironment: def note_dependency(self, filename): self.dependencies.setdefault(self.docname, set()).add(filename) + def note_versionchange(self, type, version, node, lineno): + self.versionchanges.setdefault(version, []).append( + (type, self.temp_data['docname'], lineno, + self.temp_data.get('py:module'), + self.temp_data.get('object'), node.astext())) + # post-processing of read doctrees def filter_messages(self, doctree): @@ -1521,8 +1531,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/ext/autodoc.py b/sphinx/ext/autodoc.py index adf08bcd..eef181de 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 @@ -256,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 @@ -321,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, @@ -416,9 +418,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 [] @@ -438,8 +442,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() @@ -866,7 +873,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) @@ -939,9 +946,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: @@ -972,7 +982,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) @@ -1004,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 \ diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index 4924d30b..f41820e2 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/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index 9d681f90..62fbfdff 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, diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 106de7a6..257ff1b6 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'): diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index b930d8ca..a12bad25 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 4fb3805f..251f5c55 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -26,6 +26,7 @@ import time import zlib +import codecs import urllib2 import posixpath from os import path @@ -33,19 +34,26 @@ from os import path from docutils import nodes from sphinx.builders.html import INVENTORY_FILENAME +from sphinx.util.pycompat import b + 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)) +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: @@ -68,25 +76,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()): @@ -109,13 +117,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) @@ -191,10 +199,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: @@ -203,11 +213,25 @@ 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 == '-': + 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): + if len(contnode) and isinstance(contnode[0], nodes.Text): + contnode[0] = nodes.Text(newtarget, contnode[0].rawsource) def setup(app): diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py index 62f5ee28..571d82a5 100644 --- a/sphinx/ext/oldcmarkup.py +++ b/sphinx/ext/oldcmarkup.py @@ -13,6 +13,10 @@ from docutils.parsers.rst import directives from sphinx.util.compat import Directive +_warned_oldcmarkup = False +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 @@ -26,6 +30,10 @@ 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:] newdir = env.lookup_domain_element('directive', newname)[0] return newdir(newname, self.arguments, self.options, @@ -35,12 +43,18 @@ 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 newtyp = 'c:' + typ[1:] newrole = env.lookup_domain_element('role', newtyp)[0] return newrole(newtyp, rawtext, text, lineno, inliner, options, content) def setup(app): + app._oldcmarkup_warned = False app.add_directive('cfunction', OldCDirective) app.add_directive('cmember', OldCDirective) app.add_directive('cmacro', OldCDirective) @@ -50,3 +64,4 @@ def setup(app): app.add_role('cfunc', old_crole) app.add_role('cmacro', old_crole) app.add_role('ctype', old_crole) + app.add_role('cmember', old_crole) diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index 80b887c0..b9bb9d77 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 diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index f5ea859c..6d710919 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 @@ -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) @@ -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 diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index 02583ff8..48116991 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -8,6 +8,8 @@ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ + +import sys import gettext import UserString @@ -178,8 +180,12 @@ pairindextypes = { translators = {} -def _(message): - return translators['sphinx'].ugettext(message) +if sys.version_info >= (3, 0): + def _(message): + return translators['sphinx'].gettext(message) +else: + def _(message): + return translators['sphinx'].ugettext(message) def init(locale_dirs, language, catalog='sphinx'): """ diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.js b/sphinx/locale/bn/LC_MESSAGES/sphinx.js new file mode 100644 index 00000000..277cd3d0 --- /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 Binary files differnew file mode 100644 index 00000000..9b60397e --- /dev/null +++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.po b/sphinx/locale/bn/LC_MESSAGES/sphinx.po new file mode 100644 index 00000000..d5141c79 --- /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 <EMAIL@ADDRESS>, 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 <nasim.haque@gmail.com>\n" +"Language-Team: Nasimul Haque <nasim.haque@gmail.com>\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 "© <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 +#, 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 <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 "ডেপ্রিকেটেড" + +#: 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 "[ছবি]" + diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js index dbfaab85..312d0fc7 100644 --- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js +++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js @@ -1 +1 @@ -Documentation.addTranslations({"locale": "pt_BR", "plural_expr": "(n > 1)", "messages": {"Search Results": "Resultados da Pesquisa", "Preparing search...": "Preparando pesquisa...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Sua pesquisa n\u00e3o encontrou nenhum documento. Por favor assegure-se de que todas as palavras foram digitadas corretamente e de que voc\u00ea tenha selecionado o m\u00ednimo de categorias.", "Search finished, found %s page(s) matching the search query.": "Pesquisa finalizada, foram encontrada(s) %s p\u00e1gina(s) que conferem com o crit\u00e9rio de pesquisa.", ", in ": ", em ", "Expand sidebar": "", "Permalink to this headline": "Link permanente para este t\u00edtulo", "Searching": "Pesquisando", "Collapse sidebar": "", "Permalink to this definition": "Link permanente para esta defini\u00e7\u00e3o", "Hide Search Matches": "Esconder Resultados da Pesquisa"}});
\ No newline at end of file +Documentation.addTranslations({"locale": "pt_BR", "plural_expr": "(n > 1)", "messages": {"Search Results": "Resultados da Pesquisa", "Preparing search...": "Preparando pesquisa...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Sua pesquisa n\u00e3o encontrou nenhum documento. Por favor assegure-se de que todas as palavras foram digitadas corretamente e de que voc\u00ea tenha selecionado o m\u00ednimo de categorias.", "Search finished, found %s page(s) matching the search query.": "Pesquisa finalizada, foram encontrada(s) %s p\u00e1gina(s) que conferem com o crit\u00e9rio de pesquisa.", ", in ": ", em ", "Expand sidebar": "Expandir painel lateral", "Permalink to this headline": "Link permanente para este t\u00edtulo", "Searching": "Pesquisando", "Collapse sidebar": "Recolher painel lateral", "Permalink to this definition": "Link permanente para esta defini\u00e7\u00e3o", "Hide Search Matches": "Esconder Resultados da Pesquisa"}});
\ No newline at end of file diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo Binary files differindex b644a114..67c1ce54 100644 --- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po index 44e2053a..7df9013e 100644 --- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po @@ -8,7 +8,7 @@ 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-05-24 23:54+0200\n" +"PO-Revision-Date: 2010-06-20 18:34-0300\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" @@ -17,7 +17,8 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.4\n" -#: sphinx/environment.py:106 sphinx/writers/latex.py:184 +#: sphinx/environment.py:106 +#: sphinx/writers/latex.py:184 #: sphinx/writers/manpage.py:67 #, python-format msgid "%B %d, %Y" @@ -41,7 +42,8 @@ msgstr "Módulo" msgid "%b %d, %Y" msgstr "%d/%m/%Y" -#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30 +#: sphinx/builders/html.py:285 +#: sphinx/themes/basic/defindex.html:30 msgid "General Index" msgstr "Índice Geral" @@ -70,9 +72,8 @@ msgid "Module author: " msgstr "Autor do módulo: " #: sphinx/directives/other.py:131 -#, fuzzy msgid "Code author: " -msgstr "Autor do módulo: " +msgstr "Autor do código: " #: sphinx/directives/other.py:133 msgid "Author: " @@ -85,18 +86,21 @@ msgstr "Veja também" #: sphinx/domains/__init__.py:253 #, python-format msgid "%s %s" -msgstr "" +msgstr "%s %s" -#: sphinx/domains/c.py:51 sphinx/domains/python.py:49 +#: sphinx/domains/c.py:51 +#: sphinx/domains/python.py:49 msgid "Parameters" msgstr "Parâmetros" -#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137 +#: sphinx/domains/c.py:54 +#: sphinx/domains/javascript.py:137 #: sphinx/domains/python.py:59 msgid "Returns" msgstr "Retorna" -#: sphinx/domains/c.py:56 sphinx/domains/python.py:61 +#: sphinx/domains/c.py:56 +#: sphinx/domains/python.py:61 msgid "Return type" msgstr "Tipo de retorno" @@ -125,12 +129,15 @@ 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:1031 +#: sphinx/domains/javascript.py:166 +#: sphinx/domains/python.py:497 msgid "function" msgstr "função" -#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032 +#: sphinx/domains/c.py:172 +#: sphinx/domains/cpp.py:1032 msgid "member" msgstr "membro" @@ -138,14 +145,14 @@ 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:1033 msgid "type" msgstr "tipo" #: sphinx/domains/c.py:175 -#, fuzzy msgid "variable" -msgstr "Variável" +msgstr "variável" #: sphinx/domains/cpp.py:876 #, python-format @@ -167,16 +174,19 @@ msgstr "%s (membro C++)" msgid "%s (C++ function)" msgstr "%s (função C++)" -#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499 +#: sphinx/domains/cpp.py:1030 +#: sphinx/domains/python.py:499 msgid "class" msgstr "classe" -#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221 +#: sphinx/domains/javascript.py:117 +#: sphinx/domains/python.py:221 #, 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:118 +#: sphinx/domains/python.py:285 #, python-format msgid "%s() (%s method)" msgstr "%s() (método %s)" @@ -184,41 +194,44 @@ msgstr "%s() (método %s)" #: sphinx/domains/javascript.py:120 #, python-format msgid "%s (global variable or constant)" -msgstr "" +msgstr "%s (variável global ou constante)" -#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323 +#: sphinx/domains/javascript.py:122 +#: sphinx/domains/python.py:323 #, python-format msgid "%s (%s attribute)" msgstr "%s (atributo %s)" #: sphinx/domains/javascript.py:131 -#, fuzzy msgid "Arguments" msgstr "Parâmetros" #: sphinx/domains/javascript.py:134 msgid "Throws" -msgstr "" +msgstr "Gera" -#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498 +#: sphinx/domains/javascript.py:167 +#: sphinx/domains/python.py:498 msgid "data" -msgstr "" +msgstr "dado" -#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504 +#: sphinx/domains/javascript.py:168 +#: sphinx/domains/python.py:504 msgid "attribute" msgstr "atributo" #: sphinx/domains/python.py:53 -#, fuzzy msgid "Variables" -msgstr "Variável" +msgstr "Variáveis" #: sphinx/domains/python.py:56 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: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() (no módulo %s)" @@ -228,7 +241,8 @@ msgstr "%s() (no módulo %s)" msgid "%s (built-in variable)" msgstr "%s (variável interna)" -#: sphinx/domains/python.py:226 sphinx/domains/python.py:317 +#: sphinx/domains/python.py:226 +#: sphinx/domains/python.py:317 #, python-format msgid "%s (in module %s)" msgstr "%s (no módulo %s)" @@ -259,14 +273,14 @@ msgid "%s() (%s static method)" msgstr "%s() (método estático %s)" #: sphinx/domains/python.py:308 -#, fuzzy, python-format +#, python-format msgid "%s() (%s.%s class method)" -msgstr "%s() (método %s.%s)" +msgstr "%s() (método de classe %s.%s)" #: sphinx/domains/python.py:311 -#, fuzzy, python-format +#, python-format msgid "%s() (%s class method)" -msgstr "%s() (método %s)" +msgstr "%s() (método de classe %s)" #: sphinx/domains/python.py:321 #, python-format @@ -283,9 +297,8 @@ msgid "%s (module)" msgstr "%s (módulo)" #: sphinx/domains/python.py:429 -#, fuzzy msgid "Python Module Index" -msgstr "Índice do Módulo" +msgstr "Índice de Módulos do Python" #: sphinx/domains/python.py:430 msgid "modules" @@ -295,47 +308,49 @@ msgstr "módulos" msgid "Deprecated" msgstr "Obsoleto" -#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162 +#: sphinx/domains/python.py:500 +#: sphinx/locale/__init__.py:162 msgid "exception" msgstr "exceção" #: sphinx/domains/python.py:501 msgid "method" -msgstr "" +msgstr "método" #: sphinx/domains/python.py:502 -#, fuzzy, python-format +#, python-format msgid "class method" -msgstr "%s() (método %s)" +msgstr "método de classe" #: sphinx/domains/python.py:503 msgid "static method" msgstr "método estático" -#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158 +#: sphinx/domains/python.py:505 +#: sphinx/locale/__init__.py:158 msgid "module" msgstr "módulo" #: sphinx/domains/rst.py:53 #, python-format msgid "%s (directive)" -msgstr "" +msgstr "%s (diretiva)" #: sphinx/domains/rst.py:55 -#, fuzzy, python-format +#, python-format msgid "%s (role)" -msgstr "%s (módulo)" +msgstr "%s (papel)" #: sphinx/domains/rst.py:103 msgid "directive" -msgstr "" +msgstr "diretiva" #: sphinx/domains/rst.py:104 -#, fuzzy msgid "role" -msgstr "módulo" +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" @@ -347,15 +362,15 @@ msgstr "%sopção de linha de comando; %s" #: sphinx/domains/std.py:328 msgid "glossary term" -msgstr "" +msgstr "Termo de glossário" #: sphinx/domains/std.py:329 msgid "grammar token" -msgstr "" +msgstr "token de gramática" #: sphinx/domains/std.py:330 msgid "reference label" -msgstr "" +msgstr "rótulo de referência" #: sphinx/domains/std.py:331 msgid "environment variable" @@ -363,13 +378,16 @@ msgstr "váriavel de ambiente" #: sphinx/domains/std.py:332 msgid "program option" -msgstr "" +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/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 "Índice" @@ -378,19 +396,20 @@ 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 #, python-format msgid " Bases: %s" -msgstr "" +msgstr " Bases: %s" #: sphinx/ext/autodoc.py:950 #, python-format msgid "alias of :class:`%s`" -msgstr "" +msgstr "apelido de :class:`%s`" #: sphinx/ext/todo.py:41 msgid "Todo" @@ -407,29 +426,28 @@ msgstr "entrada original" #: sphinx/ext/viewcode.py:66 msgid "[source]" -msgstr "" +msgstr "[código fonte]" #: sphinx/ext/viewcode.py:109 msgid "[docs]" -msgstr "" +msgstr "[documentos]" #: sphinx/ext/viewcode.py:123 -#, fuzzy msgid "Module code" -msgstr "módulo" +msgstr "Código do módulo" #: sphinx/ext/viewcode.py:129 #, python-format msgid "<h1>Source code for %s</h1>" -msgstr "" +msgstr "<h1>Código fonte de %s</h1>" #: sphinx/ext/viewcode.py:156 msgid "Overview: module code" -msgstr "" +msgstr "Visão geral: código do módulo" #: sphinx/ext/viewcode.py:157 msgid "<h1>All modules for which code is available</h1>" -msgstr "" +msgstr "<h1>Todos os módulos onde este código está disponível</h1>" #: sphinx/locale/__init__.py:139 msgid "Attention" @@ -506,26 +524,31 @@ msgstr "comando" 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:14 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 -#, fuzzy +#: sphinx/themes/agogo/layout.html:57 +#: sphinx/themes/basic/searchbox.html:20 msgid "Enter search terms or a module, class or function name." -msgstr "Informe o nome de um módulo, classe ou função." +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" @@ -615,12 +638,8 @@ 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 @@ -647,10 +666,9 @@ msgstr "próximo capítulo" msgid "" "Please activate JavaScript to enable the search\n" " functionality." -msgstr "" +msgstr "Por favor ative o JavaScript para habilitar a funcionalidade de pesquisa." #: sphinx/themes/basic/search.html:23 -#, fuzzy 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,11 +676,9 @@ 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 msgid "search" @@ -713,12 +729,14 @@ 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/themes/basic/static/doctools.js:154 +#: sphinx/writers/html.py:482 #: sphinx/writers/html.py:487 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:87 msgid "Permalink to this definition" msgstr "Link permanente para esta definição" @@ -739,51 +757,45 @@ 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." +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 #, 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 "" +msgstr "Expandir painel lateral" #: sphinx/themes/default/static/sidebar.js:79 #: sphinx/themes/default/static/sidebar.js:106 msgid "Collapse sidebar" -msgstr "" +msgstr "Recolher painel lateral" #: sphinx/themes/haiku/layout.html:26 msgid "Contents" -msgstr "" +msgstr "Conteúdo" #: sphinx/writers/latex.py:171 msgid "Release" msgstr "Versão" -#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178 +#: sphinx/writers/latex.py:572 +#: sphinx/writers/manpage.py:178 msgid "Footnotes" -msgstr "" +msgstr "Notas de rodapé" #: sphinx/writers/latex.py:641 msgid "continued from previous page" -msgstr "" +msgstr "continuação da página anterior" #: sphinx/writers/latex.py:646 -#, fuzzy msgid "Continued on next page" -msgstr "Índice completo em uma página" +msgstr "Continua na próxima página" #: sphinx/writers/text.py:422 msgid "[image]" msgstr "[imagem]" + diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index b8e2fded..ef92297c 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 @@ -98,7 +99,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) @@ -278,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/pycode/nodes.py b/sphinx/pycode/nodes.py index e7184677..fc6eb93a 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: diff --git a/sphinx/pycode/pgen2/literals.py b/sphinx/pycode/pgen2/literals.py index 31900291..d4893702 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 diff --git a/sphinx/pycode/pgen2/tokenize.py b/sphinx/pycode/pgen2/tokenize.py index 4489db89..7ad9f012 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/sphinx/quickstart.py b/sphinx/quickstart.py index e656d218..fdac4cbe 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -9,8 +9,9 @@ :license: BSD, see LICENSE for details. """ -import sys, os, time +import sys, os, time, re from os import path +from codecs import open TERM_ENCODING = getattr(sys.stdin, 'encoding', None) @@ -20,10 +21,23 @@ 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 +try: + # this raw_input is not converted by 2to3 + term_input = raw_input +except NameError: + term_input = input + 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 @@ -42,7 +56,7 @@ import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) +#sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- @@ -186,8 +200,8 @@ html_static_path = ['%(dot)sstatic'] # base URL from which the finished HTML is served. #html_use_opensearch = '' -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = '%(project_fn)sdoc' @@ -279,6 +293,9 @@ epub_copyright = u'%(copyright_str)s' # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True ''' INTERSPHINX_CONFIG = ''' @@ -667,20 +684,22 @@ 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 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') + 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) + 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: @@ -690,6 +709,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() @@ -845,28 +876,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 diff --git a/sphinx/roles.py b/sphinx/roles.py index 0164d757..0ea0ec48 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) @@ -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], [] @@ -205,8 +215,9 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner, _amp_re = re.compile(r'(?<!&)&(?![&\s])') def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): + text = utils.unescape(text) if typ == 'menuselection': - text = utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}') + text = text.replace('-->', u'\N{TRIANGULAR BULLET}') spans = _amp_re.split(text) node = nodes.emphasis(rawtext=rawtext) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index be8a6c15..b5381392 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]{% diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html index 1dac6693..e31e8544 100644 --- a/sphinx/themes/basic/layout.html +++ b/sphinx/themes/basic/layout.html @@ -14,7 +14,7 @@ {%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} {%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and - (not sidebars == []) %} + (sidebars != []) %} {%- set url_root = pathto('', 1) %} {%- if url_root == '#' %}{% set url_root = '' %}{% endif %} diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 8d1298cd..a434f3a8 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -18,6 +18,8 @@ import tempfile import posixpath import traceback from os import path +from codecs import open +from collections import deque import docutils from docutils.utils import relative_path @@ -140,8 +142,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() @@ -162,17 +164,23 @@ 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' % 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, (_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 @@ -290,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/util/docfields.py b/sphinx/util/docfields.py index c975b9b5..6ce6d82b 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -141,9 +141,16 @@ 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) + # 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 fieldtype) + par += self.make_xref(self.typerolename, domain, typename) + else: + par += fieldtype par += nodes.Text(')') par += nodes.Text(' -- ') par += content @@ -222,7 +229,10 @@ class DocFieldTransformer(object): if is_typefield: # filter out only inline nodes; others will result in invalid # markup being written out - content = filter(lambda n: isinstance(n, nodes.Inline), content) + content = filter( + lambda n: isinstance(n, nodes.Inline) or + isinstance(n, nodes.Text), + content) if content: types.setdefault(typename, {})[fieldarg] = content continue diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py new file mode 100644 index 00000000..fda85b5e --- /dev/null +++ b/sphinx/util/jsonimpl.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +""" + sphinx.util.jsonimpl + ~~~~~~~~~~~~~~~~~~~~ + + JSON serializer implementation wrapper. + + :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +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, AttributeError): + try: + import simplejson as json + JSONEncoder = json.JSONEncoder + except ImportError: + json = None + JSONEncoder = object + + +class SphinxJSONEncoder(JSONEncoder): + """JSONEncoder subclass that forces translation proxies.""" + def default(self, obj): + if isinstance(obj, UserString.UserString): + return unicode(obj) + return JSONEncoder.default(self, obj) + + +def dump(obj, fp, *args, **kwds): + kwds['cls'] = SphinxJSONEncoder + return json.dump(obj, fp, *args, **kwds) + +def dumps(obj, *args, **kwds): + kwds['cls'] = SphinxJSONEncoder + return json.dumps(obj, *args, **kwds) + +def load(*args, **kwds): + return json.load(*args, **kwds) + +def loads(*args, **kwds): + return json.loads(*args, **kwds) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 3728eac9..c2f96f07 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 @@ -129,7 +129,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/osutil.py b/sphinx/util/osutil.py index beab38cb..9943b207 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -11,6 +11,7 @@ import os import re +import sys import time import errno import shutil @@ -124,7 +125,10 @@ 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/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index bdd9507d..5f23bbe1 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -12,6 +12,65 @@ import sys import codecs import encodings +import re + +try: + from types import ClassType + class_types = (type, ClassType) +except ImportError: + # Python 3 + class_types = (type,) + + +# the ubiquitous "bytes" helper function +if sys.version_info >= (3, 0): + 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: + 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') + refactoring_tool = RefactoringTool(fixers) + source = refactoring_tool._read_python_source(filepath)[0] + 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: + base_exception = BaseException +except NameError: + 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: diff --git a/sphinx/versioning.py b/sphinx/versioning.py new file mode 100644 index 00000000..d0ea18a7 --- /dev/null +++ b/sphinx/versioning.py @@ -0,0 +1,148 @@ +# -*- 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 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 + +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 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 + 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 + +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 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. + """ + 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/sphinx/writers/latex.py b/sphinx/writers/latex.py index ea12f003..de0bbdf2 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -224,6 +224,8 @@ class LaTeXTranslator(nodes.NodeVisitor): else: 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 @@ -250,7 +252,7 @@ class LaTeXTranslator(nodes.NodeVisitor): '\\label{%s}' % self.idescape(id) def hyperlink(self, id): - return '\\hyperref[%s]{' % (self.idescape(id)) + return '{\\hyperref[%s]{' % (self.idescape(id)) def hyperpageref(self, id): return '\\autopageref*{%s}' % (self.idescape(id)) @@ -314,7 +316,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 @@ -633,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') @@ -694,7 +699,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 +756,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 +838,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 +892,17 @@ class LaTeXTranslator(nodes.NodeVisitor): pass def visit_figure(self, node): - if node.has_key('width') and node.get('align', '') in ('left', 'right'): + ids = '' + for id in self.next_figure_ids: + ids += self.hypertarget(id, anchor=False) + self.next_figure_ids.clear() + 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') + self.context.append(ids + '\\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' @@ -903,7 +912,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()) @@ -963,8 +972,11 @@ class LaTeXTranslator(nodes.NodeVisitor): def add_target(id): # indexing uses standard LaTeX index markup, so the targets # will be generated differently - if not id.startswith('index-'): - self.body.append(self.hypertarget(id)) + if id.startswith('index-'): + return + # do not generate \phantomsection in \section{} + anchor = not self.in_title + self.body.append(self.hypertarget(id, anchor=anchor)) # postpone the labels until after the sectioning command parindex = node.parent.index(node) @@ -980,6 +992,20 @@ class LaTeXTranslator(nodes.NodeVisitor): self.next_section_ids.add(node['refid']) 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: @@ -1048,9 +1074,9 @@ class LaTeXTranslator(nodes.NodeVisitor): id = self.curfilestack[-1] + ':' + uri[1:] self.body.append(self.hyperlink(id)) if self.builder.config.latex_show_pagerefs: - self.context.append('} (%s)' % self.hyperpageref(id)) + self.context.append('}} (%s)' % self.hyperpageref(id)) else: - self.context.append('}') + self.context.append('}}') elif uri.startswith('%'): # references to documents or labels inside documents hashindex = uri.find('#') @@ -1064,12 +1090,12 @@ class LaTeXTranslator(nodes.NodeVisitor): if len(node) and hasattr(node[0], 'attributes') and \ 'std-term' in node[0].get('classes', []): # don't add a pageref for glossary terms - self.context.append('}') + self.context.append('}}') else: if self.builder.config.latex_show_pagerefs: - self.context.append('} (%s)' % self.hyperpageref(id)) + self.context.append('}} (%s)' % self.hyperpageref(id)) else: - self.context.append('}') + self.context.append('}}') elif uri.startswith('@token'): if self.in_production_list: self.body.append('\\token{') @@ -1151,7 +1177,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) @@ -1180,10 +1206,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 diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 98528d5b..b28b2379 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): |
