diff options
| author | Takayuki Shimizukawa <shimizukawa+bitbucket@gmail.com> | 2014-05-28 00:08:48 +0900 |
|---|---|---|
| committer | Takayuki Shimizukawa <shimizukawa+bitbucket@gmail.com> | 2014-05-28 00:08:48 +0900 |
| commit | 2bea449254abb21a56f4d7193ece32afb2da82f8 (patch) | |
| tree | 209431e7c8f4ff28f2cbc9e579aeeb570418f41a /sphinx | |
| parent | 362225b5cf5642e50990d633eae241721ed640b4 (diff) | |
| parent | 88fe3b0b04f3fff0fce6c9b99d5da73dd5dda880 (diff) | |
| download | sphinx-2bea449254abb21a56f4d7193ece32afb2da82f8.tar.gz | |
Merged in vitaut/sphinx/cpp-variadic (pull request #241)
Add support for variadic templates in C++ domain
Diffstat (limited to 'sphinx')
66 files changed, 512 insertions, 481 deletions
diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 712bfeca..950eb5ff 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -43,7 +43,7 @@ if '+' in __version__ or 'pre' in __version__: def main(argv=sys.argv): """Sphinx build "main" command-line entry.""" if (sys.version_info[:3] < (2, 6, 0) or - (3, 0, 0) <= sys.version_info[:3] < (3, 2, 0)): + (3, 0, 0) <= sys.version_info[:3] < (3, 3, 0)): sys.stderr.write('Error: Sphinx requires at least Python 2.6 to run.\n') return 1 try: diff --git a/sphinx/application.py b/sphinx/application.py index 89d1d786..6d1c334b 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -18,8 +18,9 @@ import types import posixpath import traceback from os import path -from cStringIO import StringIO +from six import iteritems, itervalues +from six.moves import cStringIO from docutils import nodes from docutils.parsers.rst import convert_directive_function, \ directives, roles @@ -84,14 +85,14 @@ class Sphinx(object): self.parallel = parallel if status is None: - self._status = StringIO() + self._status = cStringIO() self.quiet = True else: self._status = status self.quiet = False if warning is None: - self._warning = StringIO() + self._warning = cStringIO() else: self._warning = warning self._warncount = 0 @@ -388,7 +389,7 @@ class Sphinx(object): def disconnect(self, listener_id): self.debug('[app] disconnecting event: [id=%s]', listener_id) - for event in self._listeners.itervalues(): + for event in itervalues(self._listeners): event.pop(listener_id, None) def emit(self, event, *args): @@ -399,7 +400,7 @@ class Sphinx(object): pass results = [] if event in self._listeners: - for _, callback in self._listeners[event].iteritems(): + for _, callback in iteritems(self._listeners[event]): results.append(callback(self, *args)) return results @@ -443,7 +444,7 @@ class Sphinx(object): def add_node(self, node, **kwds): self.debug('[app] adding node: %r', (node, kwds)) nodes._add_node_class_names([node.__name__]) - for key, val in kwds.iteritems(): + for key, val in iteritems(kwds): try: visit, depart = val except ValueError: diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py index 3a52c713..abc18388 100644 --- a/sphinx/builders/changes.py +++ b/sphinx/builders/changes.py @@ -12,6 +12,8 @@ import codecs from os import path +from six import iteritems + from sphinx import package_dir from sphinx.util import copy_static_entry from sphinx.locale import _ @@ -93,9 +95,9 @@ class ChangesBuilder(Builder): 'version': version, 'docstitle': self.config.html_title, 'shorttitle': self.config.html_short_title, - 'libchanges': sorted(libchanges.iteritems()), + 'libchanges': sorted(iteritems(libchanges)), 'apichanges': sorted(apichanges), - 'otherchanges': sorted(otherchanges.iteritems()), + 'otherchanges': sorted(iteritems(otherchanges)), 'show_copyright': self.config.html_show_copyright, 'show_sphinx': self.config.html_show_sphinx, } @@ -142,7 +144,7 @@ class ChangesBuilder(Builder): finally: f.close() themectx = dict(('theme_' + key, val) for (key, val) in - self.theme.get_options({}).iteritems()) + iteritems(self.theme.get_options({}))) copy_static_entry(path.join(package_dir, 'themes', 'default', 'static', 'default.css_t'), self.outdir, self, themectx) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index e25e780a..f36d0202 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -10,6 +10,7 @@ """ from __future__ import with_statement +from __future__ import unicode_literals from os import path, walk from codecs import open @@ -18,6 +19,8 @@ from datetime import datetime, tzinfo, timedelta from collections import defaultdict from uuid import uuid4 +from six import iteritems + from sphinx.builders import Builder from sphinx.util import split_index_msg from sphinx.util.nodes import extract_messages, traverse_translatable_index @@ -25,7 +28,7 @@ from sphinx.util.osutil import safe_relpath, ensuredir, find_catalog, SEP from sphinx.util.console import darkgreen, purple, bold from sphinx.locale import pairindextypes -POHEADER = ur""" +POHEADER = r""" # SOME DESCRIPTIVE TITLE. # Copyright (C) %(copyright)s # This file is distributed under the same license as the %(project)s package. @@ -186,7 +189,7 @@ class MessageCatalogBuilder(I18nBuilder): timestamp, ltz).strftime('%Y-%m-%d %H:%M%z'), ) for textdomain, catalog in self.status_iterator( - self.catalogs.iteritems(), "writing message catalogs... ", + iteritems(self.catalogs), "writing message catalogs... ", darkgreen, len(self.catalogs), lambda textdomain__: textdomain__[0]): # noop if config.gettext_compact is set @@ -202,19 +205,19 @@ class MessageCatalogBuilder(I18nBuilder): if self.config.gettext_location: # generate "#: file1:line1\n#: file2:line2 ..." - pofile.write(u"#: %s\n" % "\n#: ".join("%s:%s" % + pofile.write("#: %s\n" % "\n#: ".join("%s:%s" % (safe_relpath(source, self.outdir), line) for source, line, _ in positions)) if self.config.gettext_uuid: # generate "# uuid1\n# uuid2\n ..." - pofile.write(u"# %s\n" % "\n# ".join( + pofile.write("# %s\n" % "\n# ".join( uid for _, _, uid in positions)) # message contains *one* line of text ready for translation - message = message.replace(u'\\', ur'\\'). \ - replace(u'"', ur'\"'). \ - replace(u'\n', u'\\n"\n"') - pofile.write(u'msgid "%s"\nmsgstr ""\n\n' % message) + message = message.replace('\\', r'\\'). \ + replace('"', r'\"'). \ + replace('\n', '\\n"\n"') + pofile.write('msgid "%s"\nmsgstr ""\n\n' % message) finally: pofile.close() diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 035c734c..eeda6031 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -14,10 +14,11 @@ import sys import zlib import codecs import posixpath -import cPickle as pickle from os import path from hashlib import md5 +from six import iteritems, itervalues, text_type, string_types +from six.moves import cPickle as pickle from docutils import nodes from docutils.io import DocTreeInput, StringOutput from docutils.core import Publisher @@ -31,7 +32,6 @@ 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 b from sphinx.locale import _ from sphinx.search import js_index from sphinx.theming import Theme @@ -58,7 +58,7 @@ def get_stable_hash(obj): return get_stable_hash(list(obj.items())) elif isinstance(obj, (list, tuple)): obj = sorted(get_stable_hash(o) for o in obj) - return md5(unicode(obj).encode('utf8')).hexdigest() + return md5(text_type(obj).encode('utf8')).hexdigest() class StandaloneHTMLBuilder(Builder): @@ -163,7 +163,7 @@ class StandaloneHTMLBuilder(Builder): def get_outdated_docs(self): cfgdict = dict((name, self.config[name]) - for (name, desc) in self.config.values.iteritems() + for (name, desc) in iteritems(self.config.values) if desc[1] == 'html') self.config_hash = get_stable_hash(cfgdict) self.tags_hash = get_stable_hash(sorted(self.tags)) @@ -220,7 +220,7 @@ class StandaloneHTMLBuilder(Builder): """Utility: Render a lone doctree node.""" if node is None: return {'fragment': ''} - doc = new_document(b('<partial node>')) + doc = new_document(b'<partial node>') doc.append(node) if self._publisher is None: @@ -264,7 +264,7 @@ class StandaloneHTMLBuilder(Builder): # html_domain_indices can be False/True or a list of index names indices_config = self.config.html_domain_indices if indices_config: - for domain in self.env.domains.itervalues(): + for domain in itervalues(self.env.domains): for indexcls in domain.indices: indexname = '%s-%s' % (domain.name, indexcls.name) if isinstance(indices_config, list): @@ -295,7 +295,7 @@ class StandaloneHTMLBuilder(Builder): if favicon and os.path.splitext(favicon)[1] != '.ico': self.warn('html_favicon is not an .ico file') - if not isinstance(self.config.html_use_opensearch, basestring): + if not isinstance(self.config.html_use_opensearch, string_types): self.warn('html_use_opensearch config value must now be a string') self.relations = self.env.collect_relations() @@ -345,7 +345,7 @@ class StandaloneHTMLBuilder(Builder): if self.theme: self.globalcontext.update( ('theme_' + key, val) for (key, val) in - self.theme.get_options(self.theme_options).iteritems()) + iteritems(self.theme.get_options(self.theme_options))) self.globalcontext.update(self.config.html_context) def get_doc_context(self, docname, body, metatags): @@ -696,7 +696,7 @@ class StandaloneHTMLBuilder(Builder): sidebars = None matched = None customsidebar = None - for pattern, patsidebars in self.config.html_sidebars.iteritems(): + for pattern, patsidebars in iteritems(self.config.html_sidebars): if patmatch(pagename, pattern): if matched: if has_wildcard(pattern): @@ -713,7 +713,7 @@ class StandaloneHTMLBuilder(Builder): if sidebars is None: # keep defaults pass - elif isinstance(sidebars, basestring): + elif isinstance(sidebars, string_types): # 0.x compatible mode: insert custom sidebar before searchbox customsidebar = sidebars sidebars = None @@ -798,7 +798,7 @@ class StandaloneHTMLBuilder(Builder): % (self.config.project, self.config.version) ).encode('utf-8')) compressor = zlib.compressobj(9) - for domainname, domain in self.env.domains.iteritems(): + for domainname, domain in iteritems(self.env.domains): for name, dispname, type, docname, anchor, prio in \ domain.get_objects(): if anchor.endswith(name): diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index f5453305..bf7991cf 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -12,6 +12,7 @@ import os from os import path +from six import iteritems from docutils import nodes from docutils.io import FileOutput from docutils.utils import new_document @@ -56,7 +57,7 @@ class LaTeXBuilder(Builder): return self.get_target_uri(to, typ) def init_document_data(self): - preliminary_document_data = map(list, self.config.latex_documents) + preliminary_document_data = [list(x) for x in self.config.latex_documents] if not preliminary_document_data: self.warn('no "latex_documents" config value found; no documents ' 'will be written') @@ -152,7 +153,7 @@ class LaTeXBuilder(Builder): # copy image files if self.images: self.info(bold('copying images...'), nonl=1) - for src, dest in self.images.iteritems(): + for src, dest in iteritems(self.images): self.info(' '+src, nonl=1) copyfile(path.join(self.srcdir, src), path.join(self.outdir, dest)) diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index e46c8379..4d8970ef 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -10,14 +10,15 @@ """ import re -import Queue import socket import threading from os import path -from urllib2 import build_opener, unquote, Request, \ - HTTPError, HTTPRedirectHandler -from HTMLParser import HTMLParser, HTMLParseError +from six.moves import queue +from six.moves.urllib.request import build_opener, Request, HTTPRedirectHandler +from six.moves.urllib.parse import unquote +from six.moves.urllib.error import HTTPError +from six.moves.html_parser import HTMLParser, HTMLParseError from docutils import nodes from sphinx.builders import Builder @@ -88,7 +89,7 @@ class CheckExternalLinksBuilder(Builder): name = 'linkcheck' def init(self): - self.to_ignore = map(re.compile, self.app.config.linkcheck_ignore) + self.to_ignore = [re.compile(x) for x in self.app.config.linkcheck_ignore] self.good = set() self.broken = {} self.redirected = {} @@ -98,8 +99,8 @@ class CheckExternalLinksBuilder(Builder): open(path.join(self.outdir, 'output.txt'), 'w').close() # create queues and worker threads - self.wqueue = Queue.Queue() - self.rqueue = Queue.Queue() + self.wqueue = queue.Queue() + self.rqueue = queue.Queue() self.workers = [] for i in range(self.app.config.linkcheck_workers): thread = threading.Thread(target=self.check_thread) diff --git a/sphinx/builders/manpage.py b/sphinx/builders/manpage.py index 4b360cde..dfbf8986 100644 --- a/sphinx/builders/manpage.py +++ b/sphinx/builders/manpage.py @@ -11,11 +11,11 @@ from os import path +from six import string_types from docutils.io import FileOutput from docutils.frontend import OptionParser from sphinx import addnodes -from sphinx.errors import SphinxError from sphinx.builders import Builder from sphinx.environment import NoUri from sphinx.util.nodes import inline_all_toctrees @@ -55,7 +55,7 @@ class ManualPageBuilder(Builder): for info in self.config.man_pages: docname, name, description, authors, section = info - if isinstance(authors, basestring): + if isinstance(authors, string_types): if authors: authors = [authors] else: diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 1863fb49..c0fff2a6 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -15,6 +15,7 @@ import codecs import posixpath from os import path +from six import text_type from docutils import nodes from sphinx import addnodes @@ -136,7 +137,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder): # they are all unicode strings before joining them new_sections = [] for section in sections: - if not isinstance(section, unicode): + if not isinstance(section, text_type): new_sections.append(force_decode(section, None)) else: new_sections.append(section) diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py index cc38eb17..53463f3c 100644 --- a/sphinx/builders/texinfo.py +++ b/sphinx/builders/texinfo.py @@ -11,6 +11,7 @@ from os import path +from six import iteritems from docutils import nodes from docutils.io import FileOutput from docutils.utils import new_document @@ -107,7 +108,7 @@ class TexinfoBuilder(Builder): return self.get_target_uri(to, typ) def init_document_data(self): - preliminary_document_data = map(list, self.config.texinfo_documents) + preliminary_document_data = [list(x) for x in self.config.texinfo_documents] if not preliminary_document_data: self.warn('no "texinfo_documents" config value found; no documents ' 'will be written') @@ -207,7 +208,7 @@ class TexinfoBuilder(Builder): # copy image files if self.images: self.info(bold('copying images...'), nonl=1) - for src, dest in self.images.iteritems(): + for src, dest in iteritems(self.images): self.info(' '+src, nonl=1) copyfile(path.join(self.srcdir, src), path.join(self.outdir, dest)) diff --git a/sphinx/cmdline.py b/sphinx/cmdline.py index 07551e67..6e94ffbe 100644 --- a/sphinx/cmdline.py +++ b/sphinx/cmdline.py @@ -16,6 +16,7 @@ import getopt import traceback from os import path +from six import text_type, binary_type from docutils.utils import SystemMessage from sphinx import __version__ @@ -24,7 +25,7 @@ from sphinx.application import Sphinx from sphinx.util import Tee, format_exception_cut_frames, save_traceback from sphinx.util.console import red, nocolor, color_terminal from sphinx.util.osutil import abspath, fs_encoding -from sphinx.util.pycompat import terminal_safe, bytes +from sphinx.util.pycompat import terminal_safe def usage(argv, msg=None): @@ -88,7 +89,7 @@ def main(argv): try: opts, args = getopt.getopt(argv[1:], 'ab:t:d:c:CD:A:nNEqQWw:PThvj:', ['help', 'version']) - except getopt.error, err: + except getopt.error as err: usage(argv, 'Error: %s' % err) return 1 @@ -183,7 +184,7 @@ def main(argv): print('Error: -D option argument must be in the form name=value.', file=sys.stderr) return 1 - if likely_encoding and isinstance(val, bytes): + if likely_encoding and isinstance(val, binary_type): try: val = val.decode(likely_encoding) except UnicodeError: @@ -199,7 +200,7 @@ def main(argv): try: val = int(val) except ValueError: - if likely_encoding and isinstance(val, bytes): + if likely_encoding and isinstance(val, binary_type): try: val = val.decode(likely_encoding) except UnicodeError: @@ -271,10 +272,10 @@ def main(argv): print(terminal_safe(err.args[0]), file=error) elif isinstance(err, SphinxError): print(red('%s:' % err.category), file=error) - print(terminal_safe(unicode(err)), file=error) + print(terminal_safe(text_type(err)), file=error) elif isinstance(err, UnicodeError): print(red('Encoding error:'), file=error) - print(terminal_safe(unicode(err)), file=error) + print(terminal_safe(text_type(err)), file=error) tbpath = save_traceback(app) print(red('The full traceback has been saved in %s, if you want ' 'to report the issue to the developers.' % tbpath), diff --git a/sphinx/config.py b/sphinx/config.py index 527ea453..1b67f89a 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -11,18 +11,19 @@ import os import re -import sys from os import path +from six import PY3, iteritems, string_types, binary_type, integer_types + from sphinx.errors import ConfigError from sphinx.locale import l_ from sphinx.util.osutil import make_filename -from sphinx.util.pycompat import bytes, b, execfile_ +from sphinx.util.pycompat import execfile_ -nonascii_re = re.compile(b(r'[\x80-\xff]')) +nonascii_re = re.compile(br'[\x80-\xff]') CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s" -if sys.version_info >= (3, 0): +if PY3: CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?" class Config(object): @@ -213,7 +214,7 @@ class Config(object): self.values = Config.config_values.copy() config = {} if 'extensions' in overrides: - if isinstance(overrides['extensions'], (str, unicode)): + if isinstance(overrides['extensions'], string_types): config['extensions'] = overrides.pop('extensions').split(',') else: config['extensions'] = overrides.pop('extensions') @@ -242,8 +243,8 @@ class Config(object): def check_unicode(self, warn): # 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, bytes) and nonascii_re.search(value): + for name, value in iteritems(self._raw_config): + if isinstance(value, binary_type) and nonascii_re.search(value): warn('the config value %r is set to a string with non-ASCII ' 'characters; this can lead to Unicode errors occurring. ' 'Please use Unicode strings, e.g. %r.' % (name, u'Content') @@ -251,7 +252,7 @@ class Config(object): def init_values(self, warn): config = self._raw_config - for valname, value in self.overrides.iteritems(): + for valname, value in iteritems(self.overrides): if '.' in valname: realvalname, key = valname.split('.', 1) config.setdefault(realvalname, {})[key] = value @@ -260,7 +261,7 @@ class Config(object): warn('unknown config value %r in override, ignoring' % valname) continue defvalue = self.values[valname][0] - if isinstance(value, (str, unicode)): + if isinstance(value, string_types): if isinstance(defvalue, dict): warn('cannot override dictionary config setting %r, ' 'ignoring (use %r to set individual elements)' % @@ -268,13 +269,13 @@ class Config(object): continue elif isinstance(defvalue, list): config[valname] = value.split(',') - elif isinstance(defvalue, (int, long)): + elif isinstance(defvalue, integer_types): try: config[valname] = int(value) except ValueError: warn('invalid number %r for config value %r, ignoring' % (value, valname)) - elif defvalue is not None and not isinstance(defvalue, (str, unicode)): + elif defvalue is not None and not isinstance(defvalue, string_types): warn('cannot override config setting %r with unsupported type, ' 'ignoring' % valname) else: diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 250a013e..3ac22202 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -11,6 +11,7 @@ import re +from six import itervalues from docutils.parsers.rst import Directive, directives from sphinx import addnodes @@ -178,7 +179,7 @@ class DefaultDomain(Directive): domain_name = self.arguments[0].lower() # if domain_name not in env.domains: # # try searching by label - # for domain in env.domains.itervalues(): + # for domain in itervalues(env.domains): # if domain.label.lower() == domain_name: # domain_name = domain.name # break diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index d28c00fb..01c8c012 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -7,6 +7,7 @@ :license: BSD, see LICENSE for details. """ +from six.moves import range from docutils import nodes from docutils.parsers.rst import Directive, directives from docutils.parsers.rst.directives.admonitions import BaseAdmonition @@ -368,7 +369,7 @@ class Only(Directive): # be placed in the doctree. n_sects_to_raise = current_depth - nested_depth + 1 parent = self.state.parent - for i in xrange(n_sects_to_raise): + for i in range(n_sects_to_raise): if parent.parent: parent = parent.parent parent.append(node) diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index 200fd515..51b886fd 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -10,6 +10,8 @@ :license: BSD, see LICENSE for details. """ +from six import iteritems + from sphinx.errors import SphinxError from sphinx.locale import _ @@ -153,7 +155,7 @@ class Domain(object): self._role_cache = {} self._directive_cache = {} self._role2type = {} - for name, obj in self.object_types.iteritems(): + for name, obj in iteritems(self.object_types): for rolename in obj.roles: self._role2type.setdefault(rolename, []).append(name) self.objtypes_for_role = self._role2type.get diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index d9d5ef24..0ac1b7ab 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -12,6 +12,7 @@ import re import string +from six import iteritems from docutils import nodes from sphinx import addnodes @@ -234,7 +235,7 @@ class CDomain(Domain): } def clear_doc(self, docname): - for fullname, (fn, _) in self.data['objects'].items(): + for fullname, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] @@ -249,5 +250,5 @@ class CDomain(Domain): contnode, target) def get_objects(self): - for refname, (docname, type) in self.data['objects'].iteritems(): + for refname, (docname, type) in list(self.data['objects'].items()): yield (refname, refname, type, docname, 'c.' + refname, 1) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 71cc104a..c8505dc8 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -12,6 +12,7 @@ import re from copy import deepcopy +from six import iteritems, text_type from docutils import nodes from sphinx import addnodes @@ -21,6 +22,7 @@ from sphinx.domains import Domain, ObjType from sphinx.directives import ObjectDescription from sphinx.util.nodes import make_refnode from sphinx.util.compat import Directive +from sphinx.util.pycompat import UnicodeMixin from sphinx.util.docfields import Field, GroupedField @@ -103,25 +105,22 @@ _id_shortwords = { } -class DefinitionError(Exception): +class DefinitionError(UnicodeMixin, Exception): def __init__(self, description): self.description = description - def __str__(self): - return unicode(self).encode('utf-8') - def __unicode__(self): return self.description -class DefExpr(object): +class DefExpr(UnicodeMixin): def __eq__(self, other): if type(self) is not type(other): return False try: - for key, value in self.__dict__.iteritems(): + for key, value in iteritems(self.__dict__): if value != getattr(other, key): return False except AttributeError: @@ -161,9 +160,6 @@ class DefExpr(object): """Prefix a name node (a node returned by :meth:`get_name`).""" raise NotImplementedError() - def __str__(self): - return unicode(self).encode('utf-8') - def __unicode__(self): raise NotImplementedError() @@ -196,7 +192,7 @@ class NameDefExpr(PrimaryDefExpr): return self.name.replace(u' ', u'-') def __unicode__(self): - return unicode(self.name) + return text_type(self.name) class PathDefExpr(PrimaryDefExpr): @@ -221,10 +217,10 @@ class PathDefExpr(PrimaryDefExpr): return PathDefExpr([prefix] + self.path) def __unicode__(self): - return u'::'.join(map(unicode, self.path)) + return u'::'.join(map(text_type, self.path)) -class ArrayTypeSuffixDefExpr(object): +class ArrayTypeSuffixDefExpr(UnicodeMixin): def __init__(self, size_hint=None): self.size_hint = size_hint @@ -234,7 +230,7 @@ class ArrayTypeSuffixDefExpr(object): def __unicode__(self): return u'[%s]' % ( - self.size_hint is not None and unicode(self.size_hint) or u'', + self.size_hint is not None and text_type(self.size_hint) or u'', ) @@ -253,7 +249,7 @@ class TemplateDefExpr(PrimaryDefExpr): u'.'.join(x.get_id() for x in self.args)) def __unicode__(self): - return u'%s<%s>' % (self.typename, u', '.join(map(unicode, self.args))) + return u'%s<%s>' % (self.typename, u', '.join(map(text_type, self.args))) class ConstantTemplateArgExpr(PrimaryDefExpr): @@ -265,7 +261,7 @@ class ConstantTemplateArgExpr(PrimaryDefExpr): return self.arg.replace(u' ', u'-') def __unicode__(self): - return unicode(self.arg) + return text_type(self.arg) class WrappingDefExpr(DefExpr): @@ -284,13 +280,13 @@ class ModifierDefExpr(WrappingDefExpr): self.modifiers = modifiers def get_id(self): - pieces = [_id_shortwords.get(unicode(x), unicode(x)) + pieces = [_id_shortwords.get(text_type(x), text_type(x)) for x in self.modifiers] pieces.append(self.typename.get_id()) return u'-'.join(pieces) def __unicode__(self): - return u' '.join(map(unicode, list(self.modifiers) + [self.typename])) + return u' '.join(map(text_type, list(self.modifiers) + [self.typename])) class PtrDefExpr(WrappingDefExpr): @@ -373,7 +369,7 @@ class ArgumentDefExpr(DefExpr): if self.default is not None: buf.append('=%s' % self.default) for suffix in self.type_suffixes: - buf.append(unicode(suffix)) + buf.append(text_type(suffix)) return u''.join(buf) @@ -415,12 +411,12 @@ class TypeObjDefExpr(NamedDefExpr): def __unicode__(self): buf = self.get_modifiers() if self.typename is None: - buf.append(unicode(self.name)) + buf.append(text_type(self.name)) else: - buf.extend(map(unicode, (self.typename, self.name))) + buf.extend(map(text_type, (self.typename, self.name))) buf = [u' '.join(buf)] for suffix in self.type_suffixes: - buf.append(unicode(suffix)) + buf.append(text_type(suffix)) return u''.join(buf) @@ -441,10 +437,10 @@ class MemberObjDefExpr(NamedDefExpr): def __unicode__(self): buf = self.get_modifiers() - buf.extend((unicode(self.typename), unicode(self.name))) + buf.extend((text_type(self.typename), text_type(self.name))) buf = [u' '.join(buf)] for suffix in self.type_suffixes: - buf.append(unicode(suffix)) + buf.append(text_type(suffix)) if self.value is not None: buf.append(u' = %s' % self.value) return u''.join(buf) @@ -485,9 +481,9 @@ class FuncDefExpr(NamedDefExpr): if self.constexpr: buf.append(u'constexpr') if self.rv is not None: - buf.append(unicode(self.rv)) + buf.append(text_type(self.rv)) buf.append(u'%s(%s)' % (self.name, u', '.join( - map(unicode, self.signature)))) + map(text_type, self.signature)))) if self.const: buf.append(u'const') if self.volatile: @@ -520,7 +516,7 @@ class ClassDefExpr(NamedDefExpr): def _tostring(self, visibility='public'): buf = self.get_modifiers(visibility) - buf.append(unicode(self.name)) + buf.append(text_type(self.name)) if self.bases: buf.append(u':') buf.append(u', '.join(base._tostring('private') @@ -1001,19 +997,19 @@ class CPPObject(ObjectDescription): def attach_name(self, node, name): owner, name = name.split_owner() - varname = unicode(name) + varname = text_type(name) if owner is not None: - owner = unicode(owner) + '::' + owner = text_type(owner) + '::' node += addnodes.desc_addname(owner, owner) node += addnodes.desc_name(varname, varname) def attach_type_suffixes(self, node, suffixes): for suffix in suffixes: - node += nodes.Text(unicode(suffix)) + node += nodes.Text(text_type(suffix)) def attach_type(self, node, type): # XXX: link to c? - text = unicode(type) + text = text_type(type) pnode = addnodes.pending_xref( '', refdomain='cpp', reftype='type', reftarget=text, modname=None, classname=None) @@ -1035,7 +1031,7 @@ class CPPObject(ObjectDescription): def add_target_and_index(self, sigobj, sig, signode): theid = sigobj.get_id() - name = unicode(sigobj.name) + name = text_type(sigobj.name) if theid not in self.state.document.ids: signode['names'].append(theid) signode['ids'].append(theid) @@ -1101,8 +1097,8 @@ class CPPClassObject(CPPObject): signode += nodes.Text(' : ') for base in cls.bases: self.attach_modifiers(signode, base, 'private') - signode += nodes.emphasis(unicode(base.name), - unicode(base.name)) + signode += nodes.emphasis(text_type(base.name), + text_type(base.name)) signode += nodes.Text(', ') signode.pop() # remove the trailing comma @@ -1152,7 +1148,7 @@ class CPPFunctionObject(CPPObject): def attach_function(self, node, func): owner, name = func.name.split_owner() if owner is not None: - owner = unicode(owner) + '::' + owner = text_type(owner) + '::' node += addnodes.desc_addname(owner, owner) # cast operator is special. in this case the return value @@ -1162,7 +1158,7 @@ class CPPFunctionObject(CPPObject): node += nodes.Text(u' ') self.attach_type(node, name.typename) else: - funcname = unicode(name) + funcname = text_type(name) node += addnodes.desc_name(funcname, funcname) paramlist = addnodes.desc_parameterlist() @@ -1171,10 +1167,10 @@ class CPPFunctionObject(CPPObject): if arg.type is not None: self.attach_type(param, arg.type) param += nodes.Text(u' ') - param += nodes.emphasis(unicode(arg.name), unicode(arg.name)) + param += nodes.emphasis(text_type(arg.name), text_type(arg.name)) self.attach_type_suffixes(param, arg.type_suffixes) if arg.default is not None: - def_ = u'=' + unicode(arg.default) + def_ = u'=' + text_type(arg.default) param += nodes.emphasis(def_, def_) paramlist += param @@ -1280,14 +1276,14 @@ class CPPDomain(Domain): } def clear_doc(self, docname): - for fullname, (fn, _, _) in self.data['objects'].items(): + for fullname, (fn, _, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _create_refnode(expr): - name = unicode(expr) + name = text_type(expr) if name not in self.data['objects']: return None obj = self.data['objects'][name] @@ -1321,5 +1317,5 @@ class CPPDomain(Domain): return _create_refnode(expr.prefix(parent)) def get_objects(self): - for refname, (docname, type, theid) in self.data['objects'].iteritems(): + for refname, (docname, type, theid) in iteritems(self.data['objects']): yield (refname, refname, type, docname, refname, 1) diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 9b7777f4..8cc49b09 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -9,6 +9,8 @@ :license: BSD, see LICENSE for details. """ +from six import iteritems + from sphinx import addnodes from sphinx.domains import Domain, ObjType from sphinx.locale import l_, _ @@ -183,7 +185,7 @@ class JavaScriptDomain(Domain): } def clear_doc(self, docname): - for fullname, (fn, _) in self.data['objects'].items(): + for fullname, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] @@ -215,6 +217,6 @@ class JavaScriptDomain(Domain): name.replace('$', '_S_'), contnode, name) def get_objects(self): - for refname, (docname, type) in self.data['objects'].iteritems(): + for refname, (docname, type) in list(self.data['objects'].items()): yield refname, refname, type, docname, \ refname.replace('$', '_S_'), 1 diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 9e083a12..1780b4a1 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -11,6 +11,7 @@ import re +from six import iteritems from docutils import nodes from docutils.parsers.rst import directives @@ -514,7 +515,7 @@ class PythonModuleIndex(Index): ignores = self.domain.env.config['modindex_common_prefix'] ignores = sorted(ignores, key=len, reverse=True) # list of all modules, sorted by module name - modules = sorted(self.domain.data['modules'].iteritems(), + modules = sorted(iteritems(self.domain.data['modules']), key=lambda x: x[0].lower()) # sort out collapsable modules prev_modname = '' @@ -564,7 +565,7 @@ class PythonModuleIndex(Index): collapse = len(modules) - num_toplevels < num_toplevels # sort by first letter - content = sorted(content.iteritems()) + content = sorted(iteritems(content)) return content, collapse @@ -619,10 +620,10 @@ class PythonDomain(Domain): ] def clear_doc(self, docname): - for fullname, (fn, _) in self.data['objects'].items(): + for fullname, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] - for modname, (fn, _, _, _) in self.data['modules'].items(): + for modname, (fn, _, _, _) in list(self.data['modules'].items()): if fn == docname: del self.data['modules'][modname] @@ -720,8 +721,8 @@ class PythonDomain(Domain): contnode, name) def get_objects(self): - for modname, info in self.data['modules'].iteritems(): + for modname, info in iteritems(self.data['modules']): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) - for refname, (docname, type) in self.data['objects'].iteritems(): + for refname, (docname, type) in iteritems(self.data['objects']): if type != 'module': # modules are already handled yield (refname, refname, type, docname, refname, 1) diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index c51c85fe..e213211a 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -11,6 +11,8 @@ import re +from six import iteritems + from sphinx import addnodes from sphinx.domains import Domain, ObjType from sphinx.locale import l_, _ @@ -117,7 +119,7 @@ class ReSTDomain(Domain): } def clear_doc(self, docname): - for (typ, name), doc in self.data['objects'].items(): + for (typ, name), doc in list(self.data['objects'].items()): if doc == docname: del self.data['objects'][typ, name] @@ -133,5 +135,5 @@ class ReSTDomain(Domain): contnode, target + ' ' + objtype) def get_objects(self): - for (typ, name), docname in self.data['objects'].iteritems(): + for (typ, name), docname in iteritems(self.data['objects']): yield name, name, typ, docname, typ + '-' + name, 1 diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 07b39417..f4e22b4b 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -12,6 +12,7 @@ import re import unicodedata +from six import iteritems from docutils import nodes from docutils.parsers.rst import directives from docutils.statemachine import ViewList @@ -508,22 +509,22 @@ class StandardDomain(Domain): } def clear_doc(self, docname): - for key, (fn, _) in self.data['progoptions'].items(): + for key, (fn, _) in list(self.data['progoptions'].items()): if fn == docname: del self.data['progoptions'][key] - for key, (fn, _) in self.data['objects'].items(): + for key, (fn, _) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][key] - for key, (fn, _, _) in self.data['labels'].items(): + for key, (fn, _, _) in list(self.data['labels'].items()): if fn == docname: del self.data['labels'][key] - for key, (fn, _) in self.data['anonlabels'].items(): + for key, (fn, _) in list(self.data['anonlabels'].items()): if fn == docname: del self.data['anonlabels'][key] def process_doc(self, env, docname, document): labels, anonlabels = self.data['labels'], self.data['anonlabels'] - for name, explicit in document.nametypes.iteritems(): + for name, explicit in iteritems(document.nametypes): if not explicit: continue labelid = document.nameids[name] @@ -621,16 +622,16 @@ class StandardDomain(Domain): labelid, contnode) def get_objects(self): - for (prog, option), info in self.data['progoptions'].iteritems(): + for (prog, option), info in iteritems(self.data['progoptions']): yield (option, option, 'option', info[0], info[1], 1) - for (type, name), info in self.data['objects'].iteritems(): + for (type, name), info in iteritems(self.data['objects']): yield (name, name, type, info[0], info[1], self.object_types[type].attrs['searchprio']) - for name, info in self.data['labels'].iteritems(): + for name, info in iteritems(self.data['labels']): yield (name, info[2], 'label', info[0], info[1], -1) # add anonymous-only labels as well non_anon_labels = set(self.data['labels']) - for name, info in self.data['anonlabels'].iteritems(): + for name, info in iteritems(self.data['anonlabels']): if name not in non_anon_labels: yield (name, name, 'label', info[0], info[1], -1) diff --git a/sphinx/environment.py b/sphinx/environment.py index a0fb87da..ec65e5d2 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -18,11 +18,12 @@ import codecs import imghdr import string import unicodedata -import cPickle as pickle from os import path from glob import glob -from itertools import izip, groupby +from itertools import groupby +from six import iteritems, itervalues, text_type, class_types +from six.moves import cPickle as pickle, zip from docutils import nodes from docutils.io import FileInput, NullOutput from docutils.core import Publisher @@ -39,7 +40,6 @@ from sphinx.util import url_re, get_matching_docs, docname_join, split_into, \ from sphinx.util.nodes import clean_astext, make_refnode, WarningStream from sphinx.util.osutil import SEP, fs_encoding, find_catalog_files from sphinx.util.matching import compile_matchers -from sphinx.util.pycompat import class_types from sphinx.util.websupport import is_commentable from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _ @@ -136,7 +136,7 @@ class BuildEnvironment: del self.domains picklefile = open(filename, 'wb') # remove potentially pickling-problematic values from config - for key, val in vars(self.config).items(): + for key, val in list(vars(self.config).items()): if key.startswith('_') or \ isinstance(val, types.ModuleType) or \ isinstance(val, types.FunctionType) or \ @@ -278,11 +278,11 @@ class BuildEnvironment: self.images.purge_doc(docname) self.dlfiles.purge_doc(docname) - for subfn, fnset in self.files_to_rebuild.items(): + for subfn, fnset in list(self.files_to_rebuild.items()): fnset.discard(docname) if not fnset: del self.files_to_rebuild[subfn] - for key, (fn, _) in self.citations.items(): + for key, (fn, _) in list(self.citations.items()): if fn == docname: del self.citations[key] for version, changes in self.versionchanges.items(): @@ -421,7 +421,7 @@ class BuildEnvironment: else: # check if a config value was changed that affects how # doctrees are read - for key, descr in config.values.iteritems(): + for key, descr in iteritems(config.values): if descr[1] != 'env': continue if self.config[key] != config[key]: @@ -593,7 +593,7 @@ class BuildEnvironment: FileInput.__init__(self_, *args, **kwds) def decode(self_, data): - if isinstance(data, unicode): + if isinstance(data, text_type): return data return data.decode(self_.encoding, 'sphinx') @@ -632,7 +632,7 @@ class BuildEnvironment: self.note_indexentries_from(docname, doctree) self.note_citations_from(docname, doctree) self.build_toc_from(docname, doctree) - for domain in self.domains.itervalues(): + for domain in itervalues(self.domains): domain.process_doc(self, docname, doctree) # allow extension-specific post-processing @@ -818,7 +818,7 @@ class BuildEnvironment: candidates['*'] = rel_imgpath # map image paths to unique image names (so that they can be put # into a single directory) - for imgpath in candidates.itervalues(): + for imgpath in itervalues(candidates): self.dependencies.setdefault(docname, set()).add(imgpath) if not os.access(path.join(self.srcdir, imgpath), os.R_OK): self.warn_node('image file not readable: %s' % imgpath, @@ -1508,7 +1508,7 @@ class BuildEnvironment: # Force the word to be unicode if it's a ASCII bytestring. # This will solve problems with unicode normalization later. # For instance the RFC role will add bytestrings at the moment - word = unicode(word) + word = text_type(word) entry = dic.get(word) if not entry: dic[word] = entry = [[], {}] @@ -1522,7 +1522,7 @@ class BuildEnvironment: else: entry[0].append((main, uri)) - for fn, entries in self.indexentries.iteritems(): + for fn, entries in iteritems(self.indexentries): # new entry types must be listed in directives/other.py! for type, value, tid, main in entries: try: @@ -1560,8 +1560,7 @@ class BuildEnvironment: if lckey[0:1] in lcletters: return chr(127) + lckey return lckey - newlist = new.items() - newlist.sort(key=keyfunc) + newlist = sorted(new.items(), key=keyfunc) if group_entries: # fixup entries: transform @@ -1597,7 +1596,7 @@ class BuildEnvironment: 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()) + v[1] = sorted((si, se) for (si, (se, void)) in iteritems(v[1])) # now calculate the key letter = unicodedata.normalize('NFD', k[0])[0].upper() if letter in letters: @@ -1649,7 +1648,7 @@ class BuildEnvironment: # else it will stay None # same for children if includes: - for subindex, args in enumerate(izip(includes, + for subindex, args in enumerate(zip(includes, [None] + includes, includes[1:] + [None])): collect([(docname, subindex)] + parents, diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index a79849ae..ab081097 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -17,6 +17,7 @@ import inspect import traceback from types import FunctionType, BuiltinFunctionType, MethodType +from six import PY3, iteritems, itervalues, text_type, class_types from docutils import nodes from docutils.utils import assemble_option_dict from docutils.statemachine import ViewList @@ -29,7 +30,6 @@ from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.compat import Directive from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \ safe_getattr, safe_repr, is_builtin_class_method -from sphinx.util.pycompat import class_types from sphinx.util.docstrings import prepare_docstring @@ -54,9 +54,10 @@ class DefDict(dict): return dict.__getitem__(self, key) except KeyError: return self.default - def __nonzero__(self): + def __bool__(self): # docutils check "if option_spec" return True + __nonzero__ = __bool__ # for python2 compatibility identity = lambda x: x @@ -264,7 +265,7 @@ class Documenter(object): @staticmethod def get_attr(obj, name, *defargs): """getattr() override for types such as Zope interfaces.""" - for typ, func in AutoDirective._special_attrgetters.iteritems(): + for typ, func in iteritems(AutoDirective._special_attrgetters): if isinstance(obj, typ): return func(obj, name, *defargs) return safe_getattr(obj, name, *defargs) @@ -480,7 +481,7 @@ class Documenter(object): docstring = self.get_attr(self.object, '__doc__', None) # make sure we have Unicode docstrings, then sanitize and split # into lines - if isinstance(docstring, unicode): + if isinstance(docstring, text_type): return [prepare_docstring(docstring, ignore)] elif isinstance(docstring, str): # this will not trigger on Py3 return [prepare_docstring(force_decode(docstring, encoding), @@ -504,9 +505,9 @@ class Documenter(object): # set sourcename and add content from attribute documentation if self.analyzer: # prevent encoding errors when the file name is non-ASCII - if not isinstance(self.analyzer.srcname, unicode): - filename = unicode(self.analyzer.srcname, - sys.getfilesystemencoding(), 'replace') + if not isinstance(self.analyzer.srcname, text_type): + filename = text_type(self.analyzer.srcname, + sys.getfilesystemencoding(), 'replace') else: filename = self.analyzer.srcname sourcename = u'%s:docstring of %s' % (filename, self.fullname) @@ -550,7 +551,7 @@ class Documenter(object): if self.analyzer: attr_docs = self.analyzer.find_attr_docs() namespace = '.'.join(self.objpath) - for item in attr_docs.iteritems(): + for item in iteritems(attr_docs): if item[0][0] == namespace: analyzed_member_names.add(item[0][1]) if not want_all: @@ -691,7 +692,7 @@ class Documenter(object): # document non-skipped members memberdocumenters = [] for (mname, member, isattr) in self.filter_members(members, want_all): - classes = [cls for cls in AutoDirective._registry.itervalues() + classes = [cls for cls in itervalues(AutoDirective._registry) if cls.can_document_member(member, mname, isattr, self)] if not classes: # don't know how to document this member @@ -1128,7 +1129,7 @@ class ClassDocumenter(ModuleLevelDocumenter): docstrings.append(initdocstring) doc = [] for docstring in docstrings: - if not isinstance(docstring, unicode): + if not isinstance(docstring, text_type): docstring = force_decode(docstring, encoding) doc.append(prepare_docstring(docstring)) return doc @@ -1212,7 +1213,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): return inspect.isroutine(member) and \ not isinstance(parent, ModuleDocumenter) - if sys.version_info >= (3, 0): + if PY3: def import_object(self): ret = ClassLevelDocumenter.import_object(self) obj_from_parent = self.parent.__dict__.get(self.object_name) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 5bee387e..8ba76a58 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -59,6 +59,7 @@ import sys import inspect import posixpath +from six import text_type from docutils.parsers.rst import directives from docutils.statemachine import ViewList from docutils import nodes @@ -116,7 +117,7 @@ def autosummary_table_visit_html(self, node): par = col1_entry[0] for j, subnode in enumerate(list(par)): if isinstance(subnode, nodes.Text): - new_text = unicode(subnode.astext()) + new_text = text_type(subnode.astext()) new_text = new_text.replace(u" ", u"\u00a0") par[j] = nodes.Text(new_text) except IndexError: diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 5daf95f5..7b7c0707 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -110,14 +110,11 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', # read items = find_autosummary_in_files(sources) - # remove possible duplicates - items = dict([(item, True) for item in items]).keys() - # keep track of new files new_files = [] # write - for name, path, template_name in sorted(items, key=str): + for name, path, template_name in sorted(set(items), key=str): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index f1ac6cec..3d2da66d 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -13,9 +13,11 @@ import re import glob import inspect -import cPickle as pickle from os import path +from six import iteritems +from six.moves import cPickle as pickle + from sphinx.builders import Builder @@ -52,7 +54,7 @@ class CoverageBuilder(Builder): self.warn('invalid regex %r in coverage_c_regexes' % exp) self.c_ignorexps = {} - for (name, exps) in self.config.coverage_ignore_c_items.iteritems(): + for (name, exps) in iteritems(self.config.coverage_ignore_c_items): self.c_ignorexps[name] = compile_regex_list( 'coverage_ignore_c_items', exps, self.warn) self.mod_ignorexps = compile_regex_list( @@ -109,7 +111,7 @@ class CoverageBuilder(Builder): write_header(op, 'Undocumented C API elements', '=') op.write('\n') - for filename, undoc in self.c_undoc.iteritems(): + for filename, undoc in iteritems(self.c_undoc): write_header(op, filename) for typ, name in undoc: op.write(' * %-50s [%9s]\n' % (name, typ)) @@ -211,8 +213,7 @@ class CoverageBuilder(Builder): try: if self.config.coverage_write_headline: write_header(op, 'Undocumented Python objects', '=') - keys = self.py_undoc.keys() - keys.sort() + keys = sorted(self.py_undoc.keys()) for name in keys: undoc = self.py_undoc[name] if 'error' in undoc: @@ -229,7 +230,7 @@ class CoverageBuilder(Builder): if undoc['classes']: op.write('Classes:\n') for name, methods in sorted( - undoc['classes'].iteritems()): + iteritems(undoc['classes'])): if not methods: op.write(' * %s\n' % name) else: diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index ef27dc11..b7e247ec 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -14,11 +14,11 @@ import re import sys import time import codecs -import StringIO from os import path # circumvent relative import doctest = __import__('doctest') +from six import itervalues, StringIO, binary_type from docutils import nodes from docutils.parsers.rst import directives @@ -27,7 +27,6 @@ from sphinx.util import force_decode from sphinx.util.nodes import set_source_info from sphinx.util.compat import Directive from sphinx.util.console import bold -from sphinx.util.pycompat import bytes blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE) doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE) @@ -158,7 +157,7 @@ class TestCode(object): class SphinxDocTestRunner(doctest.DocTestRunner): def summarize(self, out, verbose=None): - string_io = StringIO.StringIO() + string_io = StringIO() old_stdout = sys.stdout sys.stdout = string_io try: @@ -233,7 +232,7 @@ Results of doctest builder run on %s self.info(text, nonl=True) if self.app.quiet: self.warn(text) - if isinstance(text, bytes): + if isinstance(text, binary_type): text = force_decode(text, None) self.outfile.write(text) @@ -312,24 +311,24 @@ Doctest summary groups[groupname] = TestGroup(groupname) groups[groupname].add_code(code) for code in add_to_all_groups: - for group in groups.itervalues(): + for group in itervalues(groups): group.add_code(code) if self.config.doctest_global_setup: code = TestCode(self.config.doctest_global_setup, 'testsetup', lineno=0) - for group in groups.itervalues(): + for group in itervalues(groups): group.add_code(code, prepend=True) if self.config.doctest_global_cleanup: code = TestCode(self.config.doctest_global_cleanup, 'testcleanup', lineno=0) - for group in groups.itervalues(): + for group in itervalues(groups): group.add_code(code) if not groups: return self._out('\nDocument: %s\n----------%s\n' % (docname, '-'*len(docname))) - for group in groups.itervalues(): + for group in itervalues(groups): self.test_group(group, self.env.doc2path(docname, base=None)) # Separately count results from setup code res_f, res_t = self.setup_runner.summarize(self._out, verbose=False) diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py index 18da573e..2a54761a 100644 --- a/sphinx/ext/extlinks.py +++ b/sphinx/ext/extlinks.py @@ -24,6 +24,7 @@ :license: BSD, see LICENSE for details. """ +from six import iteritems from docutils import nodes, utils from sphinx.util.nodes import split_explicit_title @@ -51,7 +52,7 @@ def make_link_role(base_url, prefix): return role def setup_link_roles(app): - for name, (base_url, prefix) in app.config.extlinks.iteritems(): + for name, (base_url, prefix) in iteritems(app.config.extlinks): app.add_role(name, make_link_role(base_url, prefix)) def setup(app): diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index abfb6ac6..3fbded34 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -20,6 +20,7 @@ try: except ImportError: from sha import sha +from six import text_type from docutils import nodes from docutils.parsers.rst import directives @@ -145,7 +146,7 @@ def render_dot(self, code, options, format, prefix='graphviz'): ensuredir(path.dirname(outfn)) # graphviz expects UTF-8 by default - if isinstance(code, unicode): + if isinstance(code, text_type): code = code.encode('utf-8') dot_args = [self.builder.config.graphviz_dot] diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index e2c44b58..6dcd3ea1 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -27,27 +27,27 @@ import time import zlib import codecs -import urllib2 import posixpath from os import path import re +from six import iteritems +from six.moves.urllib import request from docutils import nodes from docutils.utils import relative_path from sphinx.locale import _ from sphinx.builders.html import INVENTORY_FILENAME -from sphinx.util.pycompat import b -handlers = [urllib2.ProxyHandler(), urllib2.HTTPRedirectHandler(), - urllib2.HTTPHandler()] +handlers = [request.ProxyHandler(), request.HTTPRedirectHandler(), + request.HTTPHandler()] try: - handlers.append(urllib2.HTTPSHandler) + handlers.append(request.HTTPSHandler) except AttributeError: pass -urllib2.install_opener(urllib2.build_opener(*handlers)) +request.install_opener(request.build_opener(*handlers)) UTF8StreamReader = codecs.lookup('utf-8')[2] @@ -55,9 +55,9 @@ UTF8StreamReader = codecs.lookup('utf-8')[2] def read_inventory_v1(f, uri, join): f = UTF8StreamReader(f) invdata = {} - line = f.next() + line = next(f) projname = line.rstrip()[11:] - line = f.next() + line = next(f) version = line.rstrip()[11:] for line in f: name, type, location = line.rstrip().split(None, 2) @@ -85,19 +85,19 @@ def read_inventory_v2(f, uri, join, bufsize=16*1024): def read_chunks(): decompressor = zlib.decompressobj() - for chunk in iter(lambda: f.read(bufsize), b('')): + for chunk in iter(lambda: f.read(bufsize), b''): yield decompressor.decompress(chunk) yield decompressor.flush() def split_lines(iter): - buf = b('') + buf = b'' for chunk in iter: buf += chunk - lineend = buf.find(b('\n')) + lineend = buf.find(b'\n') while lineend != -1: yield buf[:lineend].decode('utf-8') buf = buf[lineend+1:] - lineend = buf.find(b('\n')) + lineend = buf.find(b'\n') assert not buf for line in split_lines(read_chunks()): @@ -129,7 +129,7 @@ def fetch_inventory(app, uri, inv): join = localuri and path.join or posixpath.join try: if inv.find('://') != -1: - f = urllib2.urlopen(inv) + f = request.urlopen(inv) else: f = open(path.join(app.srcdir, inv), 'rb') except Exception as err: @@ -167,7 +167,7 @@ def load_mappings(app): env.intersphinx_named_inventory = {} cache = env.intersphinx_cache update = False - for key, value in app.config.intersphinx_mapping.iteritems(): + for key, value in iteritems(app.config.intersphinx_mapping): if isinstance(value, tuple): # new format name, (uri, inv) = key, value @@ -202,13 +202,13 @@ def load_mappings(app): # add the unnamed inventories last. This means that the # unnamed inventories will shadow the named ones but the named # ones can still be accessed when the name is specified. - cached_vals = list(cache.itervalues()) + cached_vals = list(cache.values()) named_vals = sorted(v for v in cached_vals if v[0]) unnamed_vals = [v for v in cached_vals if not v[0]] for name, _, invdata in named_vals + unnamed_vals: if name: env.intersphinx_named_inventory[name] = invdata - for type, objects in invdata.iteritems(): + for type, objects in iteritems(invdata): env.intersphinx_inventory.setdefault( type, {}).update(objects) diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index 96ca81a5..b67ea79c 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -10,6 +10,9 @@ """ import sys + +from six import PY2, iteritems + from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring @@ -214,9 +217,9 @@ class Config(object): } def __init__(self, **settings): - for name, (default, rebuild) in self._config_values.iteritems(): + for name, (default, rebuild) in iteritems(self._config_values): setattr(self, name, default) - for name, value in settings.iteritems(): + for name, value in iteritems(settings): setattr(self, name, value) @@ -249,7 +252,7 @@ def setup(app): app.connect('autodoc-process-docstring', _process_docstring) app.connect('autodoc-skip-member', _skip_member) - for name, (default, rebuild) in Config._config_values.iteritems(): + for name, (default, rebuild) in iteritems(Config._config_values): app.add_config_value(name, default, rebuild) @@ -346,12 +349,12 @@ def _skip_member(app, what, name, obj, skip, options): if name != '__weakref__' and name != '__init__' and has_doc and is_member: cls_is_owner = False if what == 'class' or what == 'exception': - if sys.version_info[0] < 3: + if PY2: cls = getattr(obj, 'im_class', getattr(obj, '__objclass__', None)) cls_is_owner = (cls and hasattr(cls, name) and name in cls.__dict__) - elif sys.version_info[1] >= 3: + elif sys.version_info >= (3, 3): qualname = getattr(obj, '__qualname__', '') cls_path, _, _ = qualname.rpartition('.') if cls_path: diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 2819ffe3..1a8d88c6 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -14,13 +14,12 @@ import collections import inspect import re -import sys -from sphinx.ext.napoleon.iterators import modify_iter +from six import string_types +from six.moves import range -if sys.version_info[0] >= 3: - basestring = str - xrange = range +from sphinx.ext.napoleon.iterators import modify_iter +from sphinx.util.pycompat import UnicodeMixin _directive_regex = re.compile(r'\.\. \S+::') @@ -28,7 +27,7 @@ _google_untyped_arg_regex = re.compile(r'\s*(\w+)\s*:\s*(.*)') _google_typed_arg_regex = re.compile(r'\s*(\w+)\s*\(\s*(.+?)\s*\)\s*:\s*(.*)') -class GoogleDocstring(object): +class GoogleDocstring(UnicodeMixin): """Parse Google style docstrings. Convert Google style docstrings to reStructuredText. @@ -116,7 +115,7 @@ class GoogleDocstring(object): self._name = name self._obj = obj self._opt = options - if isinstance(docstring, basestring): + if isinstance(docstring, string_types): docstring = docstring.splitlines() self._lines = docstring self._line_iter = modify_iter(docstring, modifier=lambda s: s.rstrip()) @@ -151,20 +150,6 @@ class GoogleDocstring(object): } self._parse() - def __str__(self): - """Return the parsed docstring in reStructuredText format. - - Returns - ------- - str - UTF-8 encoded version of the docstring. - - """ - if sys.version_info[0] >= 3: - return self.__unicode__() - else: - return self.__unicode__().encode('utf8') - def __unicode__(self): """Return the parsed docstring in reStructuredText format. @@ -192,7 +177,7 @@ class GoogleDocstring(object): line = self._line_iter.peek() while(not self._is_section_break() and (not line or self._is_indented(line, indent))): - lines.append(self._line_iter.next()) + lines.append(next(self._line_iter)) line = self._line_iter.peek() return lines @@ -201,19 +186,19 @@ class GoogleDocstring(object): while (self._line_iter.has_next() and self._line_iter.peek() and not self._is_section_header()): - lines.append(self._line_iter.next()) + lines.append(next(self._line_iter)) return lines def _consume_empty(self): lines = [] line = self._line_iter.peek() while self._line_iter.has_next() and not line: - lines.append(self._line_iter.next()) + lines.append(next(self._line_iter)) line = self._line_iter.peek() return lines def _consume_field(self, parse_type=True, prefer_type=False): - line = self._line_iter.next() + line = next(self._line_iter) match = None _name, _type, _desc = line.strip(), '', '' @@ -270,7 +255,7 @@ class GoogleDocstring(object): return [] def _consume_section_header(self): - section = self._line_iter.next() + section = next(self._line_iter) stripped_section = section.strip(':') if stripped_section.lower() in self._sections: section = stripped_section @@ -280,7 +265,7 @@ class GoogleDocstring(object): self._consume_empty() lines = [] while not self._is_section_break(): - lines.append(self._line_iter.next()) + lines.append(next(self._line_iter)) return lines + self._consume_empty() def _dedent(self, lines, full=False): @@ -597,7 +582,7 @@ class GoogleDocstring(object): if start == -1: lines = [] end = -1 - for i in reversed(xrange(len(lines))): + for i in reversed(range(len(lines))): line = lines[i] if line: end = i @@ -710,7 +695,7 @@ class NumpyDocstring(GoogleDocstring): name, obj, options) def _consume_field(self, parse_type=True, prefer_type=False): - line = self._line_iter.next() + line = next(self._line_iter) if parse_type: _name, _, _type = line.partition(':') else: @@ -727,10 +712,10 @@ class NumpyDocstring(GoogleDocstring): return self._consume_fields(prefer_type=True) def _consume_section_header(self): - section = self._line_iter.next() + section = next(self._line_iter) if not _directive_regex.match(section): # Consume the header underline - self._line_iter.next() + next(self._line_iter) return section def _is_section_break(self): @@ -745,7 +730,7 @@ class NumpyDocstring(GoogleDocstring): def _is_section_header(self): section, underline = self._line_iter.peek(2) section = section.lower() - if section in self._sections and isinstance(underline, basestring): + if section in self._sections and isinstance(underline, string_types): pattern = r'[=\-`:\'"~^_*+#<>]{' + str(len(section)) + r'}$' return bool(re.match(pattern, underline)) elif self._directive_sections: diff --git a/sphinx/ext/napoleon/iterators.py b/sphinx/ext/napoleon/iterators.py index 2f1904da..90a0a1ac 100644 --- a/sphinx/ext/napoleon/iterators.py +++ b/sphinx/ext/napoleon/iterators.py @@ -12,10 +12,11 @@ """ import collections -import sys +from six import PY3 -if sys.version_info[0] >= 3: + +if PY3: callable = lambda o: hasattr(o, '__call__') @@ -75,7 +76,7 @@ class peek_iter(object): n = 1 try: while len(self._cache) < n: - self._cache.append(self._iterable.next()) + self._cache.append(next(self._iterable)) except StopIteration: while len(self._cache) < n: self._cache.append(self.sentinel) @@ -238,7 +239,7 @@ class modify_iter(peek_iter): n = 1 try: while len(self._cache) < n: - self._cache.append(self.modifier(self._iterable.next())) + self._cache.append(self.modifier(next(self._iterable))) except StopIteration: while len(self._cache) < n: self._cache.append(self.sentinel) diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py index 6bfe644c..e0fed6e0 100644 --- a/sphinx/ext/pngmath.py +++ b/sphinx/ext/pngmath.py @@ -21,12 +21,13 @@ try: except ImportError: from sha import sha +from six import text_type from docutils import nodes from sphinx.errors import SphinxError from sphinx.util.png import read_png_depth, write_png_depth from sphinx.util.osutil import ensuredir, ENOENT -from sphinx.util.pycompat import b, sys_encoding +from sphinx.util.pycompat import sys_encoding from sphinx.ext.mathbase import setup_math as mathbase_setup, wrap_displaymath class MathExtError(SphinxError): @@ -66,7 +67,7 @@ DOC_BODY_PREVIEW = r''' \end{document} ''' -depth_re = re.compile(b(r'\[\d+ depth=(-?\d+)\]')) +depth_re = re.compile(br'\[\d+ depth=(-?\d+)\]') def render_math(self, math): """Render the LaTeX math expression *math* using latex and dvipng. @@ -191,7 +192,7 @@ def html_visit_math(self, node): try: fname, depth = render_math(self, '$'+node['latex']+'$') except MathExtError as exc: - msg = unicode(exc) + msg = text_type(exc) sm = nodes.system_message(msg, type='WARNING', level=2, backrefs=[], source=node['latex']) sm.walkabout(self) diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index 74a00463..4a62bf6d 100644 --- a/sphinx/ext/viewcode.py +++ b/sphinx/ext/viewcode.py @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for details. """ +from six import iteritems, text_type from docutils import nodes from sphinx import addnodes @@ -29,7 +30,7 @@ def doctree_read(app, doctree): except Exception: env._viewcode_modules[modname] = False return - if not isinstance(analyzer.code, unicode): + if not isinstance(analyzer.code, text_type): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code @@ -91,7 +92,7 @@ def collect_pages(app): app.builder.info(' (%d module code pages)' % len(env._viewcode_modules), nonl=1) - for modname, entry in env._viewcode_modules.iteritems(): + for modname, entry in iteritems(env._viewcode_modules): if not entry: continue code, tags, used = entry @@ -109,7 +110,7 @@ def collect_pages(app): # the collected tags (HACK: this only works if the tag boundaries are # properly nested!) maxindex = len(lines) - 1 - for name, docname in used.iteritems(): + for name, docname in iteritems(used): type, start, end = tags[name] backlink = urito(pagename, docname) + '#' + modname + '.' + name lines[start] = ( diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index b5779ab4..599a76a9 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import sys import re import textwrap @@ -19,6 +18,8 @@ except ImportError: # parser is not available on Jython parser = None +from six import PY2, text_type + from sphinx.util.pycompat import htmlescape from sphinx.util.texescape import tex_hl_escape_map_new from sphinx.ext import doctest @@ -131,7 +132,7 @@ class PygmentsBridge(object): # lines beginning with "..." are probably placeholders for suite src = re.sub(r"(?m)^(\s*)" + mark + "(.)", r"\1"+ mark + r"# \2", src) - if sys.version_info < (3, 0) and isinstance(src, unicode): + if PY2 and isinstance(src, text_type): # 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 @@ -150,7 +151,7 @@ class PygmentsBridge(object): return True def highlight_block(self, source, lang, warn=None, force=False, **kwargs): - if not isinstance(source, unicode): + if not isinstance(source, text_type): source = source.decode() if not pygments: return self.unhighlighted(source) @@ -207,7 +208,7 @@ class PygmentsBridge(object): if self.dest == 'html': return hlsource else: - if not isinstance(hlsource, unicode): # Py2 / Pygments < 1.6 + if not isinstance(hlsource, text_type): # Py2 / Pygments < 1.6 hlsource = hlsource.decode() return hlsource.translate(tex_hl_escape_map_new) diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py index f4a5a815..b161b427 100644 --- a/sphinx/jinja2glue.py +++ b/sphinx/jinja2glue.py @@ -12,6 +12,7 @@ from os import path from pprint import pformat +from six import string_types from jinja2 import FileSystemLoader, BaseLoader, TemplateNotFound, \ contextfunction from jinja2.utils import open_if_exists @@ -22,7 +23,7 @@ from sphinx.util.osutil import mtimes_of_files def _tobool(val): - if isinstance(val, basestring): + if isinstance(val, string_types): return val.lower() in ('true', '1', 'yes', 'on') return bool(val) @@ -113,7 +114,7 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader): self.pathchain = pathchain # make the paths into loaders - self.loaders = map(SphinxFileSystemLoader, loaderchain) + self.loaders = [SphinxFileSystemLoader(x) for x in loaderchain] use_i18n = builder.app.translator is not None extensions = use_i18n and ['jinja2.ext.i18n'] or [] diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index b76aab1f..9332f474 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -9,12 +9,13 @@ :license: BSD, see LICENSE for details. """ -import sys import gettext -import UserString +from six import PY3, text_type +from six.moves import UserString -class _TranslationProxy(UserString.UserString, object): + +class _TranslationProxy(UserString, object): """ Class for proxy strings from gettext translations. This is a helper for the lazy_* functions from this module. @@ -32,7 +33,7 @@ class _TranslationProxy(UserString.UserString, object): def __new__(cls, func, *args): if not args: # not called with "function" and "arguments", but a plain string - return unicode(func) + return text_type(func) return object.__new__(cls) def __getnewargs__(self): @@ -59,11 +60,12 @@ class _TranslationProxy(UserString.UserString, object): def __contains__(self, key): return key in self.data - def __nonzero__(self): + def __bool__(self): return bool(self.data) + __nonzero__ = __bool__ # for python2 compatibility def __dir__(self): - return dir(unicode) + return dir(text_type) def __iter__(self): return iter(self.data) @@ -75,7 +77,7 @@ class _TranslationProxy(UserString.UserString, object): return str(self.data) def __unicode__(self): - return unicode(self.data) + return text_type(self.data) def __add__(self, other): return self.data + other @@ -132,7 +134,7 @@ class _TranslationProxy(UserString.UserString, object): def __repr__(self): try: - return 'i' + repr(unicode(self.data)) + return 'i' + repr(text_type(self.data)) except: return '<%s broken>' % self.__class__.__name__ @@ -183,7 +185,7 @@ pairindextypes = { translators = {} -if sys.version_info >= (3, 0): +if PY3: def _(message): return translators['sphinx'].gettext(message) else: diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index b735fb31..461eea05 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -13,12 +13,14 @@ from __future__ import print_function import sys from os import path +from six import iteritems, text_type, BytesIO, StringIO + from sphinx import package_dir 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, detect_encoding -from sphinx.util.pycompat import StringIO, BytesIO, TextIOWrapper +from sphinx.util.pycompat import TextIOWrapper from sphinx.util.docstrings import prepare_docstring, prepare_commentdoc @@ -30,9 +32,9 @@ pydriver = driver.Driver(pygrammar, convert=nodes.convert) # an object with attributes corresponding to token and symbol names class sym: pass -for k, v in pygrammar.symbol2number.iteritems(): +for k, v in iteritems(pygrammar.symbol2number): setattr(sym, k, v) -for k, v in token.tok_name.iteritems(): +for k, v in iteritems(token.tok_name): setattr(sym, v, k) # a dict mapping terminal and nonterminal numbers to their names @@ -98,7 +100,7 @@ class AttrDocVisitor(nodes.NodeVisitor): continue # skip over semicolon if parent[idx].type == sym.NEWLINE: prefix = parent[idx].get_prefix() - if not isinstance(prefix, unicode): + if not isinstance(prefix, text_type): prefix = prefix.decode(self.encoding) docstring = prepare_commentdoc(prefix) if docstring: @@ -116,7 +118,7 @@ class AttrDocVisitor(nodes.NodeVisitor): if not pnode or pnode.type not in (token.INDENT, token.DEDENT): break prefix = pnode.get_prefix() - if not isinstance(prefix, unicode): + if not isinstance(prefix, text_type): prefix = prefix.decode(self.encoding) docstring = prepare_commentdoc(prefix) self.add_docstring(node, docstring) @@ -339,7 +341,7 @@ if __name__ == '__main__': x1 = time.time() ma.parse() x2 = time.time() - #for (ns, name), doc in ma.find_attr_docs().iteritems(): + #for (ns, name), doc in iteritems(ma.find_attr_docs()): # print '>>', ns, name # print '\n'.join(doc) pprint.pprint(ma.find_tags()) diff --git a/sphinx/pycode/pgen2/literals.py b/sphinx/pycode/pgen2/literals.py index ce4a0ebc..25e09b62 100644 --- a/sphinx/pycode/pgen2/literals.py +++ b/sphinx/pycode/pgen2/literals.py @@ -8,6 +8,9 @@ from __future__ import print_function import re +from six import text_type + + simple_escapes = {"a": "\a", "b": "\b", "f": "\f", @@ -67,7 +70,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 and not isinstance(s, unicode): + if encoding and not isinstance(s, text_type): s = s.decode(encoding) if s.startswith('u') or s.startswith('U'): regex = uni_escape_re diff --git a/sphinx/pycode/pgen2/pgen.py b/sphinx/pycode/pgen2/pgen.py index ab990cef..e199ed8a 100644 --- a/sphinx/pycode/pgen2/pgen.py +++ b/sphinx/pycode/pgen2/pgen.py @@ -3,6 +3,8 @@ from __future__ import print_function +from six import iteritems + # Pgen imports from sphinx.pycode.pgen2 import grammar, token, tokenize @@ -29,7 +31,7 @@ class ParserGenerator(object): def make_grammar(self): c = PgenGrammar() - names = self.dfas.keys() + names = list(self.dfas.keys()) names.sort() names.remove(self.startsymbol) names.insert(0, self.startsymbol) @@ -42,7 +44,7 @@ class ParserGenerator(object): states = [] for state in dfa: arcs = [] - for label, next in state.arcs.iteritems(): + for label, next in iteritems(state.arcs): arcs.append((self.make_label(c, label), dfa.index(next))) if state.isfinal: arcs.append((0, dfa.index(state))) @@ -108,7 +110,7 @@ class ParserGenerator(object): return ilabel def addfirstsets(self): - names = self.dfas.keys() + names = list(self.dfas.keys()) names.sort() for name in names: if name not in self.first: @@ -121,7 +123,7 @@ class ParserGenerator(object): state = dfa[0] totalset = {} overlapcheck = {} - for label, next in state.arcs.iteritems(): + for label, next in iteritems(state.arcs): if label in self.dfas: if label in self.first: fset = self.first[label] @@ -136,7 +138,7 @@ class ParserGenerator(object): totalset[label] = 1 overlapcheck[label] = {label: 1} inverse = {} - for label, itsfirst in overlapcheck.iteritems(): + for label, itsfirst in iteritems(overlapcheck): for symbol in itsfirst: if symbol in inverse: raise ValueError("rule %s is ambiguous; %s is in the" @@ -195,7 +197,7 @@ class ParserGenerator(object): for label, next in nfastate.arcs: if label is not None: addclosure(next, arcs.setdefault(label, {})) - for label, nfaset in arcs.iteritems(): + for label, nfaset in iteritems(arcs): for st in states: if st.nfaset == nfaset: break @@ -225,7 +227,7 @@ class ParserGenerator(object): print("Dump of DFA for", name) for i, state in enumerate(dfa): print(" State", i, state.isfinal and "(final)" or "") - for label, next in state.arcs.iteritems(): + for label, next in iteritems(state.arcs): print(" %s -> %d" % (label, dfa.index(next))) def simplify_dfa(self, dfa): @@ -322,9 +324,9 @@ class ParserGenerator(object): return value def gettoken(self): - tup = self.generator.next() + tup = next(self.generator) while tup[0] in (tokenize.COMMENT, tokenize.NL): - tup = self.generator.next() + tup = next(self.generator) self.type, self.value, self.begin, self.end, self.line = tup #print token.tok_name[self.type], repr(self.value) @@ -333,7 +335,7 @@ class ParserGenerator(object): try: msg = msg % args except: - msg = " ".join([msg] + map(str, args)) + msg = " ".join([msg] + [str(x) for x in args]) raise SyntaxError(msg, (self.filename, self.end[0], self.end[1], self.line)) @@ -351,7 +353,7 @@ class DFAState(object): def __init__(self, nfaset, final): assert isinstance(nfaset, dict) - assert isinstance(iter(nfaset).next(), NFAState) + assert isinstance(next(iter(nfaset)), NFAState) assert isinstance(final, NFAState) self.nfaset = nfaset self.isfinal = final in nfaset @@ -364,7 +366,7 @@ class DFAState(object): self.arcs[label] = next def unifystate(self, old, new): - for label, next in self.arcs.iteritems(): + for label, next in iteritems(self.arcs): if next is old: self.arcs[label] = new @@ -377,7 +379,7 @@ class DFAState(object): # would invoke this method recursively, with cycles... if len(self.arcs) != len(other.arcs): return False - for label, next in self.arcs.iteritems(): + for label, next in iteritems(self.arcs): if next is not other.arcs.get(label): return False return True diff --git a/sphinx/pycode/pgen2/token.py b/sphinx/pycode/pgen2/token.py index 56a40ce7..55bf5e8d 100755 --- a/sphinx/pycode/pgen2/token.py +++ b/sphinx/pycode/pgen2/token.py @@ -68,7 +68,7 @@ NT_OFFSET = 256 #--end constants-- tok_name = {} -for _name, _value in globals().items(): +for _name, _value in list(globals().items()): if type(_value) is type(0): tok_name[_value] = _name diff --git a/sphinx/pycode/pgen2/tokenize.py b/sphinx/pycode/pgen2/tokenize.py index 4e94fa5c..f516f78b 100644 --- a/sphinx/pycode/pgen2/tokenize.py +++ b/sphinx/pycode/pgen2/tokenize.py @@ -97,8 +97,9 @@ ContStr = group(r"[uUbB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" + PseudoExtras = group(r'\\\r?\n', Comment, Triple) PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) -tokenprog, pseudoprog, single3prog, double3prog = map( - re.compile, (Token, PseudoToken, Single3, Double3)) +tokenprog, pseudoprog, single3prog, double3prog = [ + re.compile(x) for x in (Token, PseudoToken, Single3, Double3) +] endprogs = {"'": re.compile(Single), '"': re.compile(Double), "'''": single3prog, '"""': double3prog, "r'''": single3prog, 'r"""': double3prog, diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index e480923d..acf26df7 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -26,6 +26,8 @@ try: except ImportError: pass +from six import PY2, PY3, text_type +from six.moves import input from docutils.utils import column_width from sphinx import __version__ @@ -35,16 +37,12 @@ from sphinx.util.console import purple, bold, red, turquoise, \ from sphinx.util import texescape # function to get input from terminal -- overridden by the test suite -try: - # this raw_input is not converted by 2to3 - term_input = raw_input -except NameError: - term_input = input +term_input = input PROMPT_PREFIX = '> ' -if sys.version_info >= (3, 0): +if PY3: # prevents that the file is checked for being written in Python 2.x syntax QUICKSTART_CONF = u'#!/usr/bin/env python3\n' else: @@ -997,7 +995,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty): prompt = PROMPT_PREFIX + '%s [%s]: ' % (text, default) else: prompt = PROMPT_PREFIX + text + ': ' - if sys.version_info < (3, 0): + if PY2: # for Python 2.x, try to get a Unicode string out of it if prompt.encode('ascii', 'replace').decode('ascii', 'replace') \ != prompt: @@ -1015,7 +1013,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty): x = term_input(prompt).strip() if default and not x: x = default - if not isinstance(x, unicode): + if not isinstance(x, text_type): # 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: @@ -1037,7 +1035,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty): d[key] = x -if sys.version_info >= (3, 0): +if PY3: # remove Unicode literal prefixes def _convert_python_source(source, rex=re.compile(r"[uU]('.*?')")): return rex.sub('\\1', source) @@ -1241,10 +1239,10 @@ def generate(d, overwrite=True, silent=False): else: d['extensions'] = extensions d['copyright'] = time.strftime('%Y') + ', ' + d['author'] - d['author_texescaped'] = unicode(d['author']).\ + d['author_texescaped'] = text_type(d['author']).\ translate(texescape.tex_escape_map) d['project_doc'] = d['project'] + ' Documentation' - d['project_doc_texescaped'] = unicode(d['project'] + ' Documentation').\ + d['project_doc_texescaped'] = text_type(d['project'] + ' Documentation').\ translate(texescape.tex_escape_map) # escape backslashes and single quotes in strings that are put into diff --git a/sphinx/roles.py b/sphinx/roles.py index f0b8cedc..2778759a 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -11,6 +11,7 @@ import re +from six import iteritems from docutils import nodes, utils from docutils.parsers.rst import roles @@ -34,7 +35,7 @@ generic_docroles = { 'regexp' : nodes.literal, } -for rolename, nodeclass in generic_docroles.iteritems(): +for rolename, nodeclass in iteritems(generic_docroles): generic = roles.GenericRole(rolename, nodeclass) role = roles.CustomRole(rolename, generic, {'classes': [rolename]}) roles.register_local_role(rolename, role) @@ -313,5 +314,5 @@ specific_docroles = { 'index': index_role, } -for rolename, func in specific_docroles.iteritems(): +for rolename, func in iteritems(specific_docroles): roles.register_local_role(rolename, func) diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index b4f5a799..43e3e404 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -11,8 +11,9 @@ from __future__ import with_statement import re -import cPickle as pickle +from six import iteritems, itervalues, text_type, string_types +from six.moves import cPickle as pickle from docutils.nodes import raw, comment, title, Text, NodeVisitor, SkipNode from sphinx.util import jsdump, rpartition @@ -237,7 +238,7 @@ class IndexBuilder(object): def load(self, stream, format): """Reconstruct from frozen data.""" - if isinstance(format, basestring): + if isinstance(format, string_types): format = self.formats[format] frozen = format.load(stream) # if an old index is present, we treat it as not existing. @@ -249,7 +250,7 @@ class IndexBuilder(object): def load_terms(mapping): rv = {} - for k, v in mapping.iteritems(): + for k, v in iteritems(mapping): if isinstance(v, int): rv[k] = set([index2fn[v]]) else: @@ -262,7 +263,7 @@ class IndexBuilder(object): def dump(self, stream, format): """Dump the frozen index to a stream.""" - if isinstance(format, basestring): + if isinstance(format, string_types): format = self.formats[format] format.dump(self.freeze(), stream) @@ -270,7 +271,7 @@ class IndexBuilder(object): rv = {} otypes = self._objtypes onames = self._objnames - for domainname, domain in self.env.domains.iteritems(): + for domainname, domain in iteritems(self.env.domains): for fullname, dispname, type, docname, anchor, prio in \ domain.get_objects(): # XXX use dispname? @@ -289,7 +290,7 @@ class IndexBuilder(object): if otype: # use unicode() to fire translation proxies onames[typeindex] = (domainname, type, - unicode(domain.get_type_name(otype))) + text_type(domain.get_type_name(otype))) else: onames[typeindex] = (domainname, type, type) if anchor == fullname: @@ -304,7 +305,7 @@ class IndexBuilder(object): def get_terms(self, fn2index): rvs = {}, {} for rv, mapping in zip(rvs, (self._mapping, self._title_mapping)): - for k, v in mapping.iteritems(): + for k, v in iteritems(mapping): if len(v) == 1: fn, = v if fn in fn2index: @@ -315,14 +316,14 @@ class IndexBuilder(object): def freeze(self): """Create a usable data structure for serializing.""" - filenames = self._titles.keys() - titles = self._titles.values() + filenames = list(self._titles.keys()) + titles = list(self._titles.values()) fn2index = dict((f, i) for (i, f) in enumerate(filenames)) terms, title_terms = self.get_terms(fn2index) objects = self.get_objects(fn2index) # populates _objtypes objtypes = dict((v, k[0] + ':' + k[1]) - for (k, v) in self._objtypes.iteritems()) + for (k, v) in iteritems(self._objtypes)) objnames = self._objnames return dict(filenames=filenames, titles=titles, terms=terms, objects=objects, objtypes=objtypes, objnames=objnames, @@ -338,9 +339,9 @@ class IndexBuilder(object): if filename in self._titles: new_titles[filename] = self._titles[filename] self._titles = new_titles - for wordnames in self._mapping.itervalues(): + for wordnames in itervalues(self._mapping): wordnames.intersection_update(filenames) - for wordnames in self._title_mapping.itervalues(): + for wordnames in itervalues(self._title_mapping): wordnames.intersection_update(filenames) def feed(self, filename, title, doctree): diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py index 6084a067..bc308b67 100644 --- a/sphinx/search/ja.py +++ b/sphinx/search/ja.py @@ -21,6 +21,8 @@ import os import re import sys +from six import iteritems + try: import MeCab native_module = True @@ -91,14 +93,14 @@ class MecabBinder(object): class TinySegmenter(object): - patterns_ = dict([(re.compile(pattern), value) for pattern, value in { + patterns_ = dict([(re.compile(pattern), value) for pattern, value in iteritems({ u'[一二三四五六七八九十百千万億兆]': u'M', u'[一-龠々〆ヵヶ]': u'H', u'[ぁ-ん]': u'I', u'[ァ-ヴーア-ン゙ー]': u'K', u'[a-zA-Za-zA-Z]': u'A', u'[0-90-9]': u'N', - }.iteritems()]) + })]) BIAS__ = -332 BC1__ = {u'HH':6,u'II':2461,u'KH':406,u'OH':-1378} BC2__ = {u'AA':-3267,u'AI':2744,u'AN':-878,u'HH':-4070,u'HM':-1711,u'HN':4012,u'HO':3761,u'IA':1327,u'IH':-1184,u'II':-1332,u'IK':1721,u'IO':5492,u'KI':3831,u'KK':-8741,u'MH':-3132,u'MK':3334,u'OO':-2920} @@ -145,7 +147,7 @@ class TinySegmenter(object): # ctype_ def ctype_(self, char): - for pattern, value in self.patterns_.iteritems(): + for pattern, value in iteritems(self.patterns_): if pattern.match(char): return value return u'O' diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py index deb8ceab..8ed17113 100644 --- a/sphinx/setup_command.py +++ b/sphinx/setup_command.py @@ -15,11 +15,11 @@ from __future__ import print_function import sys import os -import types -from StringIO import StringIO from distutils.cmd import Command from distutils.errors import DistutilsOptionError +from six import StringIO, string_types + from sphinx.application import Sphinx from sphinx.util.console import darkred, nocolor, color_terminal from sphinx.util.osutil import abspath @@ -109,7 +109,7 @@ class BuildDoc(Command): if val is None: setattr(self, option, default) return default - elif not isinstance(val, types.StringTypes): + elif not isinstance(val, string_types): raise DistutilsOptionError("'%s' must be a %s (got `%s`)" % (option, what, val)) return val diff --git a/sphinx/theming.py b/sphinx/theming.py index abc09c17..41cbcae9 100644 --- a/sphinx/theming.py +++ b/sphinx/theming.py @@ -13,9 +13,11 @@ import os import shutil import zipfile import tempfile -import ConfigParser from os import path +from six import string_types, iteritems +from six.moves import configparser + try: import pkg_resources except ImportError: @@ -100,12 +102,12 @@ class Theme(object): fp.write(tinfo.read(name)) fp.close() - self.themeconf = ConfigParser.RawConfigParser() + self.themeconf = configparser.RawConfigParser() self.themeconf.read(path.join(self.themedir, THEMECONF)) try: inherit = self.themeconf.get('theme', 'inherit') - except ConfigParser.NoOptionError: + except configparser.NoOptionError: raise ThemeError('theme %r doesn\'t have "inherit" setting' % name) if inherit == 'none': self.base = None @@ -121,7 +123,7 @@ class Theme(object): """ try: return self.themeconf.get(section, name) - except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + except (configparser.NoOptionError, configparser.NoSectionError): if self.base is not None: return self.base.get_confstr(section, name, default) if default is NODEFAULT: @@ -141,9 +143,9 @@ class Theme(object): for conf in reversed(chain): try: options.update(conf.items('options')) - except ConfigParser.NoSectionError: + except configparser.NoSectionError: pass - for option, value in overrides.iteritems(): + for option, value in iteritems(overrides): if option not in options: raise ThemeError('unsupported theme option %r given' % option) options[option] = value @@ -188,7 +190,7 @@ def load_theme_plugins(): except: path = func_or_path - if isinstance(path, basestring): + if isinstance(path, string_types): theme_paths.append(path) else: raise ThemeError('Plugin %r does not response correctly.' % diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 06c6ce24..2c6731a2 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -12,7 +12,6 @@ import os import re import sys -import shutil import fnmatch import tempfile import posixpath @@ -22,6 +21,8 @@ from os import path from codecs import open, BOM_UTF8 from collections import deque +from six import iteritems, text_type, binary_type +from six.moves import range import docutils from docutils.utils import relative_path @@ -29,7 +30,6 @@ import jinja2 import sphinx from sphinx.errors import PycodeError -from sphinx.util.pycompat import bytes # import other utilities; partly for backwards compatibility, so don't # prune unused ones indiscriminately @@ -54,7 +54,7 @@ def docname_join(basedocname, docname): def path_stabilize(filepath): "normalize path separater and unicode string" newpath = filepath.replace(os.path.sep, SEP) - if isinstance(newpath, unicode): + if isinstance(newpath, text_type): newpath = unicodedata.normalize('NFC', newpath) return newpath @@ -122,7 +122,7 @@ class FilenameUniqDict(dict): return uniquename def purge_doc(self, docname): - for filename, (docs, unique) in self.items(): + for filename, (docs, unique) in list(self.items()): docs.discard(docname) if not docs: del self[filename] @@ -190,7 +190,7 @@ def save_traceback(app): docutils.__version__, docutils.__version_details__, jinja2.__version__)).encode('utf-8')) if app is not None: - for extname, extmod in app._extensions.iteritems(): + for extname, extmod in iteritems(app._extensions): os.write(fd, ('# %s from %s\n' % ( extname, getattr(extmod, '__file__', 'unknown')) ).encode('utf-8')) @@ -328,7 +328,7 @@ def parselinenos(spec, total): else: start = (begend[0] == '') and 0 or int(begend[0])-1 end = (begend[1] == '') and total or int(begend[1]) - items.extend(xrange(start, end)) + items.extend(range(start, end)) except Exception: raise ValueError('invalid line number spec: %r' % spec) return items @@ -336,7 +336,7 @@ def parselinenos(spec, total): def force_decode(string, encoding): """Forcibly get a unicode string out of a bytestring.""" - if isinstance(string, bytes): + if isinstance(string, binary_type): try: if encoding: string = string.decode(encoding) @@ -368,7 +368,7 @@ def rpartition(s, t): def split_into(n, type, value): """Split an index entry into a given number of parts at semicolons.""" - parts = map(lambda x: x.strip(), value.split(';', n-1)) + parts = [x.strip() for x in value.split(';', n-1)] if sum(1 for part in parts if part) < n: raise ValueError('invalid %s index entry %r' % (type, value)) return parts @@ -420,11 +420,13 @@ class PeekableIterator(object): def __iter__(self): return self - def next(self): + def __next__(self): """Return the next item from the iterator.""" if self.remaining: return self.remaining.popleft() - return self._iterator.next() + return next(self._iterator) + + next = __next__ # Python 2 compatibility def push(self, item): """Push the `item` on the internal stack, it will be returned on the @@ -434,6 +436,6 @@ class PeekableIterator(object): def peek(self): """Return the next item without changing the state of the iterator.""" - item = self.next() + item = next(self) self.push(item) return item diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index e7132874..f82f1f45 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -9,17 +9,17 @@ :license: BSD, see LICENSE for details. """ -import sys - # this imports the standard library inspect module without resorting to # relatively import this module inspect = __import__('inspect') +from six import PY3, binary_type +from six.moves import builtins + from sphinx.util import force_decode -from sphinx.util.pycompat import bytes, builtins -if sys.version_info >= (3, 0): +if PY3: from functools import partial def getargspec(func): """Like inspect.getargspec but supports functools.partial as well.""" @@ -129,7 +129,7 @@ def safe_repr(object): s = repr(object) except Exception: raise ValueError - if isinstance(s, bytes): + if isinstance(s, binary_type): return force_decode(s, None).replace('\n', ' ') return s.replace('\n', ' ') diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py index 85845a72..ede4ae7d 100644 --- a/sphinx/util/jsdump.py +++ b/sphinx/util/jsdump.py @@ -12,6 +12,8 @@ import re +from six import iteritems, integer_types, string_types + from sphinx.util.pycompat import u _str_re = re.compile(r'"(\\\\|\\"|[^"])*"') @@ -74,7 +76,7 @@ double in super""".split()) def dumps(obj, key=False): if key: - if not isinstance(obj, basestring): + if not isinstance(obj, string_types): obj = str(obj) if _nameonly_re.match(obj) and obj not in reswords: return obj # return it as a bare word @@ -84,16 +86,16 @@ def dumps(obj, key=False): return 'null' elif obj is True or obj is False: return obj and 'true' or 'false' - elif isinstance(obj, (int, long, float)): + elif isinstance(obj, integer_types + (float,)): return str(obj) elif isinstance(obj, dict): return '{%s}' % ','.join('%s:%s' % ( dumps(key, True), dumps(value) - ) for key, value in obj.iteritems()) + ) for key, value in iteritems(obj)) elif isinstance(obj, (tuple, list, set)): return '[%s]' % ','.join(dumps(x) for x in obj) - elif isinstance(obj, basestring): + elif isinstance(obj, string_types): return encode_string(obj) raise TypeError(type(obj)) diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py index 05a6ec6e..ac5c54ae 100644 --- a/sphinx/util/jsonimpl.py +++ b/sphinx/util/jsonimpl.py @@ -9,15 +9,17 @@ :license: BSD, see LICENSE for details. """ -import UserString import json +from six import text_type +from six.moves import UserString + class SphinxJSONEncoder(json.JSONEncoder): """JSONEncoder subclass that forces translation proxies.""" def default(self, obj): - if isinstance(obj, UserString.UserString): - return unicode(obj) + if isinstance(obj, UserString): + return text_type(obj) return json.JSONEncoder.default(self, obj) diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 496a2a41..9b5f58b7 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -20,6 +20,8 @@ import shutil import gettext from os import path +from six import PY2, text_type + # Errnos that we need. EEXIST = getattr(errno, 'EEXIST', 0) ENOENT = getattr(errno, 'ENOENT', 0) @@ -147,13 +149,13 @@ no_fn_re = re.compile(r'[^a-zA-Z0-9_-]') def make_filename(string): return no_fn_re.sub('', string) or 'sphinx' -if sys.version_info < (3, 0): +if PY2: # strftime for unicode strings def ustrftime(format, *args): # if a locale is set, the time strings are encoded in the encoding # given by LC_TIME; if that is available, use it enc = locale.getlocale(locale.LC_TIME)[1] or 'utf-8' - return time.strftime(unicode(format).encode(enc), *args).decode(enc) + return time.strftime(text_type(format).encode(enc), *args).decode(enc) else: ustrftime = time.strftime @@ -187,12 +189,6 @@ def find_catalog_files(docname, srcdir, locale_dirs, lang, compaction): fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() -if sys.version_info < (3, 0): - bytes = str -else: - bytes = bytes - - def abspath(pathdir): pathdir = path.abspath(pathdir) if isinstance(pathdir, bytes): diff --git a/sphinx/util/png.py b/sphinx/util/png.py index 65fc4d8d..397adb24 100644 --- a/sphinx/util/png.py +++ b/sphinx/util/png.py @@ -12,14 +12,13 @@ import struct import binascii -from sphinx.util.pycompat import b LEN_IEND = 12 LEN_DEPTH = 22 DEPTH_CHUNK_LEN = struct.pack('!i', 10) -DEPTH_CHUNK_START = b('tEXtDepth\x00') -IEND_CHUNK = b('\x00\x00\x00\x00IEND\xAE\x42\x60\x82') +DEPTH_CHUNK_START = b'tEXtDepth\x00' +IEND_CHUNK = b'\x00\x00\x00\x00IEND\xAE\x42\x60\x82' def read_png_depth(filename): diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 8cc7c5de..5031dd9b 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -12,20 +12,16 @@ import sys import codecs +from six import PY3, text_type, exec_ + # ------------------------------------------------------------------------------ # Python 2/3 compatibility -if sys.version_info >= (3, 0): +if PY3: # Python 3 - class_types = (type,) - # the ubiquitous "bytes" helper functions - def b(s): - return s.encode('utf-8') - bytes = bytes # prefix for Unicode strings u = '' - # StringIO/BytesIO classes - from io import StringIO, BytesIO, TextIOWrapper + from io import TextIOWrapper # safely encode a string for printing to the terminal def terminal_safe(s): return s.encode('ascii', 'backslashreplace').decode('ascii') @@ -46,19 +42,19 @@ if sys.version_info >= (3, 0): 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) - from itertools import zip_longest # Python 3 name - import builtins + return text_type(tree) + from html import escape as htmlescape # >= Python 3.2 + + class UnicodeMixin: + """Mixin class to handle defining the proper __str__/__unicode__ + methods in Python 2 or 3.""" + + def __str__(self): + return self.__unicode__() else: # Python 2 - from types import ClassType - class_types = (type, ClassType) - b = str - bytes = str u = 'u' - from StringIO import StringIO - BytesIO = StringIO # no need to refactor on 2.x versions convert_with_2to3 = None def TextIOWrapper(stream, encoding): @@ -68,11 +64,16 @@ else: return s.encode('ascii', 'backslashreplace') # some kind of default system encoding; should be used with a lenient # error handler - import locale - sys_encoding = locale.getpreferredencoding() + sys_encoding = __import__('locale').getpreferredencoding() # use Python 3 name - from itertools import izip_longest as zip_longest - import __builtin__ as builtins + from cgi import escape as htmlescape # 2.6, 2.7 + + class UnicodeMixin(object): + """Mixin class to handle defining the proper __str__/__unicode__ + methods in Python 2 or 3.""" + + def __str__(self): + return self.__unicode__().encode('utf8') def execfile_(filepath, _globals): @@ -87,7 +88,7 @@ def execfile_(filepath, _globals): # py26 accept only LF eol instead of CRLF if sys.version_info[:2] == (2, 6): - source = source.replace(b('\r\n'), b('\n')) + source = source.replace(b'\r\n', b'\n') # compile to a code object, handle syntax errors filepath_enc = filepath.encode(fs_encoding) @@ -101,10 +102,4 @@ def execfile_(filepath, _globals): code = compile(source, filepath_enc, 'exec') else: raise - exec code in _globals - - -if sys.version_info >= (3, 2): - from html import escape as htmlescape # >= Python 3.2 -else: # 2.6, 2.7, 3.1 - from cgi import escape as htmlescape + exec_(code, _globals) diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py index 2a9b2a02..d5141587 100644 --- a/sphinx/util/tags.py +++ b/sphinx/util/tags.py @@ -35,9 +35,9 @@ class BooleanParser(Parser): node = nodes.Const(None, lineno=token.lineno) else: node = nodes.Name(token.value, 'load', lineno=token.lineno) - self.stream.next() + next(self.stream) elif token.type == 'lparen': - self.stream.next() + next(self.stream) node = self.parse_expression() self.stream.expect('rparen') else: diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py index c0619f46..6fcf9ff0 100644 --- a/sphinx/util/texescape.py +++ b/sphinx/util/texescape.py @@ -9,93 +9,95 @@ :license: BSD, see LICENSE for details. """ +from __future__ import unicode_literals + tex_replacements = [ # map TeX special chars - (u'$', ur'\$'), - (u'%', ur'\%'), - (u'&', ur'\&'), - (u'#', ur'\#'), - (u'_', ur'\_'), - (u'{', ur'\{'), - (u'}', ur'\}'), - (u'[', ur'{[}'), - (u']', ur'{]}'), - (u'`', ur'{}`'), - (u'\\',ur'\textbackslash{}'), - (u'~', ur'\textasciitilde{}'), - (u'<', ur'\textless{}'), - (u'>', ur'\textgreater{}'), - (u'^', ur'\textasciicircum{}'), + ('$', r'\$'), + ('%', r'\%'), + ('&', r'\&'), + ('#', r'\#'), + ('_', r'\_'), + ('{', r'\{'), + ('}', r'\}'), + ('[', r'{[}'), + (']', r'{]}'), + ('`', r'{}`'), + ('\\',r'\textbackslash{}'), + ('~', r'\textasciitilde{}'), + ('<', r'\textless{}'), + ('>', r'\textgreater{}'), + ('^', r'\textasciicircum{}'), # map special Unicode characters to TeX commands - (u'¶', ur'\P{}'), - (u'§', ur'\S{}'), - (u'€', ur'\texteuro{}'), - (u'∞', ur'\(\infty\)'), - (u'±', ur'\(\pm\)'), - (u'→', ur'\(\rightarrow\)'), - (u'‣', ur'\(\rightarrow\)'), + ('¶', r'\P{}'), + ('§', r'\S{}'), + ('€', r'\texteuro{}'), + ('∞', r'\(\infty\)'), + ('±', r'\(\pm\)'), + ('→', r'\(\rightarrow\)'), + ('‣', r'\(\rightarrow\)'), # used to separate -- in options - (u'', ur'{}'), + ('', r'{}'), # map some special Unicode characters to similar ASCII ones - (u'─', ur'-'), - (u'⎽', ur'\_'), - (u'╲', ur'\textbackslash{}'), - (u'|', ur'\textbar{}'), - (u'│', ur'\textbar{}'), - (u'ℯ', ur'e'), - (u'ⅈ', ur'i'), - (u'₁', ur'1'), - (u'₂', ur'2'), + ('─', r'-'), + ('⎽', r'\_'), + ('╲', r'\textbackslash{}'), + ('|', r'\textbar{}'), + ('│', r'\textbar{}'), + ('ℯ', r'e'), + ('ⅈ', r'i'), + ('₁', r'1'), + ('₂', r'2'), # map Greek alphabet - (u'α', ur'\(\alpha\)'), - (u'β', ur'\(\beta\)'), - (u'γ', ur'\(\gamma\)'), - (u'δ', ur'\(\delta\)'), - (u'ε', ur'\(\epsilon\)'), - (u'ζ', ur'\(\zeta\)'), - (u'η', ur'\(\eta\)'), - (u'θ', ur'\(\theta\)'), - (u'ι', ur'\(\iota\)'), - (u'κ', ur'\(\kappa\)'), - (u'λ', ur'\(\lambda\)'), - (u'μ', ur'\(\mu\)'), - (u'ν', ur'\(\nu\)'), - (u'ξ', ur'\(\xi\)'), - (u'ο', ur'o'), - (u'π', ur'\(\pi\)'), - (u'ρ', ur'\(\rho\)'), - (u'σ', ur'\(\sigma\)'), - (u'τ', ur'\(\tau\)'), - (u'υ', u'\\(\\upsilon\\)'), - (u'φ', ur'\(\phi\)'), - (u'χ', ur'\(\chi\)'), - (u'ψ', ur'\(\psi\)'), - (u'ω', ur'\(\omega\)'), - (u'Α', ur'A'), - (u'Β', ur'B'), - (u'Γ', ur'\(\Gamma\)'), - (u'Δ', ur'\(\Delta\)'), - (u'Ε', ur'E'), - (u'Ζ', ur'Z'), - (u'Η', ur'H'), - (u'Θ', ur'\(\Theta\)'), - (u'Ι', ur'I'), - (u'Κ', ur'K'), - (u'Λ', ur'\(\Lambda\)'), - (u'Μ', ur'M'), - (u'Ν', ur'N'), - (u'Ξ', ur'\(\Xi\)'), - (u'Ο', ur'O'), - (u'Π', ur'\(\Pi\)'), - (u'Ρ', ur'P'), - (u'Σ', ur'\(\Sigma\)'), - (u'Τ', ur'T'), - (u'Υ', u'\\(\\Upsilon\\)'), - (u'Φ', ur'\(\Phi\)'), - (u'Χ', ur'X'), - (u'Ψ', ur'\(\Psi\)'), - (u'Ω', ur'\(\Omega\)'), - (u'Ω', ur'\(\Omega\)'), + ('α', r'\(\alpha\)'), + ('β', r'\(\beta\)'), + ('γ', r'\(\gamma\)'), + ('δ', r'\(\delta\)'), + ('ε', r'\(\epsilon\)'), + ('ζ', r'\(\zeta\)'), + ('η', r'\(\eta\)'), + ('θ', r'\(\theta\)'), + ('ι', r'\(\iota\)'), + ('κ', r'\(\kappa\)'), + ('λ', r'\(\lambda\)'), + ('μ', r'\(\mu\)'), + ('ν', r'\(\nu\)'), + ('ξ', r'\(\xi\)'), + ('ο', r'o'), + ('π', r'\(\pi\)'), + ('ρ', r'\(\rho\)'), + ('σ', r'\(\sigma\)'), + ('τ', r'\(\tau\)'), + ('υ', '\\(\\upsilon\\)'), + ('φ', r'\(\phi\)'), + ('χ', r'\(\chi\)'), + ('ψ', r'\(\psi\)'), + ('ω', r'\(\omega\)'), + ('Α', r'A'), + ('Β', r'B'), + ('Γ', r'\(\Gamma\)'), + ('Δ', r'\(\Delta\)'), + ('Ε', r'E'), + ('Ζ', r'Z'), + ('Η', r'H'), + ('Θ', r'\(\Theta\)'), + ('Ι', r'I'), + ('Κ', r'K'), + ('Λ', r'\(\Lambda\)'), + ('Μ', r'M'), + ('Ν', r'N'), + ('Ξ', r'\(\Xi\)'), + ('Ο', r'O'), + ('Π', r'\(\Pi\)'), + ('Ρ', r'P'), + ('Σ', r'\(\Sigma\)'), + ('Τ', r'T'), + ('Υ', '\\(\\Upsilon\\)'), + ('Φ', r'\(\Phi\)'), + ('Χ', r'X'), + ('Ψ', r'\(\Psi\)'), + ('Ω', r'\(\Omega\)'), + ('Ω', r'\(\Omega\)'), ] tex_escape_map = {} @@ -105,8 +107,8 @@ tex_hl_escape_map_new = {} def init(): for a, b in tex_replacements: tex_escape_map[ord(a)] = b - tex_replace_map[ord(a)] = u'_' + tex_replace_map[ord(a)] = '_' for a, b in tex_replacements: - if a in u'[]{}\\': continue + if a in '[]{}\\': continue tex_hl_escape_map_new[ord(a)] = b diff --git a/sphinx/versioning.py b/sphinx/versioning.py index a41de0f0..8d34802e 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -13,7 +13,8 @@ from uuid import uuid4 from operator import itemgetter from itertools import product -from sphinx.util.pycompat import zip_longest +from six import iteritems +from six.moves import range, zip_longest # anything below that ratio is considered equal/changed @@ -81,7 +82,7 @@ def merge_doctrees(old, new, condition): # choose the old node with the best ratio for each new node and set the uid # as long as the ratio is under a certain value, in which case we consider # them not changed but different - ratios = sorted(ratios.iteritems(), key=itemgetter(1)) + ratios = sorted(iteritems(ratios), key=itemgetter(1)) for (old_node, new_node), ratio in ratios: if new_node in seen: continue @@ -116,7 +117,7 @@ def levenshtein_distance(a, b): a, b = b, a if not a: return len(b) - previous_row = xrange(len(b) + 1) + previous_row = range(len(b) + 1) for i, column1 in enumerate(a): current_row = [i + 1] for j, column2 in enumerate(b): diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py index 0e9131d0..4cdedc0d 100644 --- a/sphinx/websupport/__init__.py +++ b/sphinx/websupport/__init__.py @@ -10,12 +10,11 @@ """ import sys -import cPickle as pickle import posixpath from os import path +from six.moves import cPickle as pickle from jinja2 import Environment, FileSystemLoader - from docutils.core import publish_parts from sphinx.application import Sphinx diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py index 45068d29..f2a67b4d 100644 --- a/sphinx/websupport/search/__init__.py +++ b/sphinx/websupport/search/__init__.py @@ -11,6 +11,8 @@ import re +from six import text_type + class BaseSearch(object): def __init__(self, path): @@ -109,7 +111,7 @@ class BaseSearch(object): context_end < len(text) and '...' or '']) try: - return unicode(context, errors='ignore') + return text_type(context, errors='ignore') except TypeError: return context diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py index 6d1f9de4..17adf058 100644 --- a/sphinx/websupport/search/whooshsearch.py +++ b/sphinx/websupport/search/whooshsearch.py @@ -14,6 +14,8 @@ from whoosh.fields import Schema, ID, TEXT from whoosh.qparser import QueryParser from whoosh.analysis import StemmingAnalyzer +from six import text_type + from sphinx.util.osutil import ensuredir from sphinx.websupport.search import BaseSearch @@ -43,7 +45,7 @@ class WhooshSearch(BaseSearch): self.index_writer.commit() def add_document(self, pagename, title, text): - self.index_writer.add_document(path=unicode(pagename), + self.index_writer.add_document(path=text_type(pagename), title=title, text=text) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 13dd5d42..2d336692 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -14,6 +14,7 @@ import posixpath import os import copy +from six import string_types from docutils import nodes from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator @@ -74,7 +75,7 @@ class HTMLTranslator(BaseTranslator): self.protect_literal_text = 0 self.permalink_text = builder.config.html_add_permalinks # support backwards-compatible setting to a bool - if not isinstance(self.permalink_text, basestring): + if not isinstance(self.permalink_text, string_types): self.permalink_text = self.permalink_text and u'\u00B6' or '' self.permalink_text = self.encode(self.permalink_text) self.secnumber_suffix = builder.config.html_secnumber_suffix diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 9d3c0179..9eeedae3 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -16,6 +16,7 @@ import re import sys from os import path +from six import itervalues, text_type from docutils import nodes, writers from docutils.writers.latex2e import Babel @@ -306,7 +307,7 @@ class LaTeXTranslator(nodes.NodeVisitor): return '\\autopageref*{%s}' % self.idescape(id) def idescape(self, id): - return unicode(id).translate(tex_replace_map).\ + return text_type(id).translate(tex_replace_map).\ encode('ascii', 'backslashreplace').decode('ascii').\ replace('\\', '_') @@ -319,7 +320,7 @@ class LaTeXTranslator(nodes.NodeVisitor): if i > 0: ret.append('\\indexspace\n') ret.append('\\bigletter{%s}\n' % - unicode(letter).translate(tex_escape_map)) + text_type(letter).translate(tex_escape_map)) for entry in entries: if not entry[3]: continue @@ -335,7 +336,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # latex_domain_indices can be False/True or a list of index names indices_config = self.builder.config.latex_domain_indices if indices_config: - for domain in self.builder.env.domains.itervalues(): + for domain in itervalues(self.builder.env.domains): for indexcls in domain.indices: indexname = '%s-%s' % (domain.name, indexcls.name) if isinstance(indices_config, list): @@ -710,7 +711,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\n\\hline\n') self.body.extend(self.tableheaders) self.body.append('\\endhead\n\n') - self.body.append(ur'\hline \multicolumn{%s}{|r|}{{\textsf{%s}}} \\ \hline' + self.body.append(r'\hline \multicolumn{%s}{|r|}{{\textsf{%s}}} \\ \hline' % (self.table.colcount, _('Continued on next page'))) self.body.append('\n\\endfoot\n\n') @@ -1136,21 +1137,21 @@ class LaTeXTranslator(nodes.NodeVisitor): p = scre.sub('!', self.encode(string)) self.body.append(r'\index{%s%s}' % (p, m)) elif type == 'pair': - p1, p2 = map(self.encode, split_into(2, 'pair', string)) + p1, p2 = [self.encode(x) for x in split_into(2, 'pair', string)] self.body.append(r'\index{%s!%s%s}\index{%s!%s%s}' % (p1, p2, m, p2, p1, m)) elif type == 'triple': - p1, p2, p3 = map(self.encode, - split_into(3, 'triple', string)) + p1, p2, p3 = [self.encode(x) + for x in split_into(3, 'triple', string)] self.body.append( r'\index{%s!%s %s%s}\index{%s!%s, %s%s}' r'\index{%s!%s %s%s}' % (p1, p2, p3, m, p2, p3, p1, m, p3, p1, p2, m)) elif type == 'see': - p1, p2 = map(self.encode, split_into(2, 'see', string)) + p1, p2 = [self.encode(x) for x in split_into(2, 'see', string)] self.body.append(r'\index{%s|see{%s}}' % (p1, p2)) elif type == 'seealso': - p1, p2 = map(self.encode, split_into(2, 'seealso', string)) + p1, p2 = [self.encode(x) for x in split_into(2, 'seealso', string)] self.body.append(r'\index{%s|see{%s}}' % (p1, p2)) else: self.builder.warn( @@ -1512,7 +1513,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # text handling def encode(self, text): - text = unicode(text).translate(tex_escape_map) + text = text_type(text).translate(tex_escape_map) if self.literal_whitespace: # Insert a blank before the newline, to avoid # ! LaTeX Error: There's no line here to end. diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 76f28a4f..d8da901e 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -10,10 +10,11 @@ """ import re -import string import textwrap from os import path +from six import itervalues +from six.moves import range from docutils import nodes, writers from sphinx import addnodes, __version__ @@ -457,7 +458,7 @@ class TexinfoTranslator(nodes.NodeVisitor): indices_config = self.builder.config.texinfo_domain_indices if indices_config: - for domain in self.builder.env.domains.itervalues(): + for domain in itervalues(self.builder.env.domains): for indexcls in domain.indices: indexname = '%s-%s' % (domain.name, indexcls.name) if isinstance(indices_config, list): @@ -974,7 +975,7 @@ class TexinfoTranslator(nodes.NodeVisitor): self.body.append('\n%s\n' % self.entry_sep) self.entry_sep = '@tab' def depart_entry(self, node): - for i in xrange(node.get('morecols', 0)): + for i in range(node.get('morecols', 0)): self.body.append('\n@tab\n') ## Field Lists diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 62d3791f..3f2edd14 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -488,7 +488,7 @@ class TextTranslator(nodes.NodeVisitor): for i, cell in enumerate(line): par = my_wrap(cell, width=colwidths[i]) if par: - maxwidth = max(map(column_width, par)) + maxwidth = max(column_width(x) for x in par) else: maxwidth = 0 realwidths[i] = max(realwidths[i], maxwidth) |
