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 | |
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
100 files changed, 713 insertions, 717 deletions
@@ -4,7 +4,7 @@ Release 1.3 (in development) Incompatible changes -------------------- -* Dropped support for Python 2.5 and 3.1. +* Dropped support for Python 2.5, 3.1 and 3.2. * Dropped support for docutils versions up to 0.9. * Removed the ``sphinx.ext.oldcmarkup`` extension. * The deprecated config values ``exclude_trees``, ``exclude_dirnames`` and @@ -16,6 +16,7 @@ Incompatible changes New features ------------ +* Add support for Python 3.4. * Added ``sphinx.ext.napoleon`` extension for NumPy and Google style docstring support. * PR#214: Added stemming support for 14 languages, so that the built-in document diff --git a/custom_fixers/__init__.py b/custom_fixers/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/custom_fixers/__init__.py +++ /dev/null diff --git a/custom_fixers/fix_alt_unicode.py b/custom_fixers/fix_alt_unicode.py deleted file mode 100644 index 55175e90..00000000 --- a/custom_fixers/fix_alt_unicode.py +++ /dev/null @@ -1,12 +0,0 @@ -from lib2to3.fixer_base import BaseFix -from lib2to3.fixer_util import Name - -class FixAltUnicode(BaseFix): - PATTERN = """ - func=funcdef< 'def' name='__unicode__' - parameters< '(' NAME ')' > any+ > - """ - - def transform(self, node, results): - name = results['name'] - name.replace(Name('__str__', prefix=name.prefix)) @@ -41,24 +41,13 @@ Among its features are the following: * Setuptools integration ''' -if sys.version_info < (2, 6) or (3, 0) <= sys.version_info < (3, 2): - print('ERROR: Sphinx requires at least Python 2.6 or 3.2 to run.') +if sys.version_info < (2, 6) or (3, 0) <= sys.version_info < (3, 3): + print('ERROR: Sphinx requires at least Python 2.6 or 3.3 to run.') sys.exit(1) -requires = ['Pygments>=1.2', 'docutils>=0.10', 'snowballstemmer>=1.1'] - -if (3, 0) <= sys.version_info < (3, 3): - requires.append('Jinja2>=2.3,<2.7') -else: # 2.6, 2.7, 3.3 or later - requires.append('Jinja2>=2.3') - -# tell distribute to use 2to3 with our own fixers -extra = {} -if sys.version_info >= (3, 0): - extra.update( - use_2to3=True, - use_2to3_fixers=['custom_fixers'] - ) +requires = [ + 'six', 'Jinja2>=2.3', 'Pygments>=1.2', 'docutils>=0.10', 'snowballstemmer>=1.1' +] # Provide a "compile_catalog" command that also creates the translated # JavaScript files if Babel is available. @@ -181,7 +170,7 @@ setup( 'Topic :: Utilities', ], platforms='any', - packages=find_packages(exclude=['custom_fixers', 'test']), + packages=find_packages(exclude=['test']), include_package_data=True, entry_points={ 'console_scripts': [ @@ -196,5 +185,4 @@ setup( }, install_requires=requires, cmdclass=cmdclass, - **extra ) 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) diff --git a/tests/coverage.py b/tests/coverage.py index 95063b67..f9341d8b 100755 --- a/tests/coverage.py +++ b/tests/coverage.py @@ -69,15 +69,11 @@ import sys import atexit import threading import token -import types import zipimport from socket import gethostname -# Python version compatibility -try: - strclass = basestring # new to 2.3 -except: - strclass = str +from six import string_types + # 2. IMPLEMENTATION # @@ -405,7 +401,7 @@ class coverage: if settings.get('collect'): self.collect() if not args: - args = self.cexecuted.keys() + args = list(self.cexecuted.keys()) ignore_errors = settings.get('ignore-errors') show_missing = settings.get('show-missing') @@ -747,10 +743,8 @@ class coverage: visitor = StatementFindingAstVisitor(statements, excluded, suite_spots) compiler.walk(ast, visitor, walker=visitor) - lines = statements.keys() - lines.sort() - excluded_lines = excluded.keys() - excluded_lines.sort() + lines = sorted(statements.keys()) + excluded_lines = sorted(excluded.keys()) return lines, excluded_lines, suite_spots # format_lines(statements, lines). Format a list of line numbers @@ -845,7 +839,7 @@ class coverage: # On windows, the shell doesn't expand wildcards. Do it here. globbed = [] for morf in morfs: - if isinstance(morf, strclass): + if isinstance(morf, string_types): globbed.extend(glob.glob(morf)) else: globbed.append(morf) @@ -854,7 +848,7 @@ class coverage: morfs = self.filter_by_prefix(morfs, omit_prefixes) morfs.sort(self.morf_name_compare) - max_name = max([5,] + map(len, map(self.morf_name, morfs))) + max_name = max(5, *map(len, map(self.morf_name, morfs))) fmt_name = "%%- %ds " % max_name fmt_err = fmt_name + "%s: %s" header = fmt_name % "Name" + " Stmts Exec Cover" diff --git a/tests/etree13/ElementPath.py b/tests/etree13/ElementPath.py index b097d816..d26a0d7a 100644 --- a/tests/etree13/ElementPath.py +++ b/tests/etree13/ElementPath.py @@ -177,7 +177,7 @@ class _SelectorContext: def find(elem, path): try: - return findall(elem, path).next() + return next(findall(elem, path)) except StopIteration: return None @@ -194,17 +194,17 @@ def findall(elem, path): if path[:1] == "/": raise SyntaxError("cannot use absolute path on element") stream = iter(xpath_tokenizer(path)) - next = stream.next; token = next() + next_ = lambda: next(stream); token = next_() selector = [] while 1: try: - selector.append(ops[token[0]](next, token)) + selector.append(ops[token[0]](next_, token)) except StopIteration: raise SyntaxError("invalid path") try: - token = next() + token = next_() if token[0] == "/": - token = next() + token = next_() except StopIteration: break _cache[path] = selector @@ -220,7 +220,7 @@ def findall(elem, path): def findtext(elem, path, default=None): try: - elem = findall(elem, path).next() + elem = next(findall(elem, path)) return elem.text except StopIteration: return default diff --git a/tests/etree13/ElementTree.py b/tests/etree13/ElementTree.py index 892b08a0..0dd12ddb 100644 --- a/tests/etree13/ElementTree.py +++ b/tests/etree13/ElementTree.py @@ -81,6 +81,9 @@ from __future__ import generators from __future__ import absolute_import +from six import string_types + + __all__ = [ # public symbols "Comment", @@ -243,7 +246,7 @@ class Element(object): def __len__(self): return len(self._children) - def __nonzero__(self): + def __bool__(self): import warnings warnings.warn( "The behavior of this method will change in future versions. " @@ -251,6 +254,7 @@ class Element(object): FutureWarning ) return len(self._children) != 0 # emulate old behaviour + __nonzero__ = __bool__ # for python2 compatibility ## # Returns the given subelement. @@ -828,7 +832,7 @@ def _namespaces(elem, encoding, default_namespace=None): tag = elem.tag if isinstance(tag, QName) and tag.text not in qnames: add_qname(tag.text) - elif isinstance(tag, basestring): + elif isinstance(tag, string_types): if tag not in qnames: add_qname(tag) elif tag is not None and tag is not Comment and tag is not PI: @@ -863,7 +867,7 @@ def _serialize_xml(write, elem, encoding, qnames, namespaces): write("<" + tag) items = elem.items() if items or namespaces: - items.sort() # lexical order + items = sorted(items) # lexical order for k, v in items: if isinstance(k, QName): k = k.text @@ -874,7 +878,7 @@ def _serialize_xml(write, elem, encoding, qnames, namespaces): write(" %s=\"%s\"" % (qnames[k], v)) if namespaces: items = namespaces.items() - items.sort(key=lambda x: x[1]) # sort on prefix + items = sorted(items, key=lambda x: x[1]) # sort on prefix for v, k in items: if k: k = ":" + k @@ -920,7 +924,7 @@ def _serialize_html(write, elem, encoding, qnames, namespaces): write("<" + tag) items = elem.items() if items or namespaces: - items.sort() # lexical order + items = sorted(items) # lexical order for k, v in items: if isinstance(k, QName): k = k.text @@ -932,7 +936,7 @@ def _serialize_html(write, elem, encoding, qnames, namespaces): write(" %s=\"%s\"" % (qnames[k], v)) if namespaces: items = namespaces.items() - items.sort(key=lambda x: x[1]) # sort on prefix + items = sorted(items, key=lambda x: x[1]) # sort on prefix for v, k in items: if k: k = ":" + k @@ -1184,7 +1188,7 @@ class _IterParseIterator(object): append((event, None)) parser.EndNamespaceDeclHandler = handler - def next(self): + def __next__(self): while 1: try: item = self._events[self._index] @@ -1205,6 +1209,8 @@ class _IterParseIterator(object): self._index = self._index + 1 return item + next = __next__ # Python 2 compatibility + def __iter__(self): return self diff --git a/tests/etree13/HTMLTreeBuilder.py b/tests/etree13/HTMLTreeBuilder.py index 3e3a7d92..cf332c75 100644 --- a/tests/etree13/HTMLTreeBuilder.py +++ b/tests/etree13/HTMLTreeBuilder.py @@ -54,6 +54,8 @@ import htmlentitydefs import re, string, sys import mimetools, StringIO +from six import text_type + from . import ElementTree AUTOCLOSE = "p", "li", "tr", "th", "td", "head", "body" @@ -199,7 +201,7 @@ class HTMLTreeBuilder(HTMLParser): def handle_data(self, data): if isinstance(data, type('')) and is_not_ascii(data): # convert to unicode, but only if necessary - data = unicode(data, self.encoding, "ignore") + data = text_type(data, self.encoding, "ignore") self.__builder.data(data) ## diff --git a/tests/path.py b/tests/path.py index f40e7b04..2a4affe7 100755 --- a/tests/path.py +++ b/tests/path.py @@ -12,20 +12,22 @@ import sys import shutil from codecs import open +from six import PY2, text_type + FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding() -class path(unicode): +class path(text_type): """ Represents a path which behaves like a string. """ - if sys.version_info < (3, 0): + if PY2: def __new__(cls, s, encoding=FILESYSTEMENCODING, errors='strict'): if isinstance(s, str): s = s.decode(encoding, errors) - return unicode.__new__(cls, s) - return unicode.__new__(cls, s) + return text_type.__new__(cls, s) + return text_type.__new__(cls, s) @property def parent(self): @@ -193,4 +195,4 @@ class path(unicode): __div__ = __truediv__ = joinpath def __repr__(self): - return '%s(%s)' % (self.__class__.__name__, unicode.__repr__(self)) + return '%s(%s)' % (self.__class__.__name__, text_type.__repr__(self)) diff --git a/tests/roots/test-autosummary/contents.rst b/tests/roots/test-autosummary/contents.rst index 3f16af99..32390a32 100644 --- a/tests/roots/test-autosummary/contents.rst +++ b/tests/roots/test-autosummary/contents.rst @@ -1,6 +1,6 @@ - -.. autosummary:: - :nosignatures: - :toctree: - - dummy_module +
+.. autosummary::
+ :nosignatures:
+ :toctree:
+
+ dummy_module
diff --git a/tests/run.py b/tests/run.py index 7d51100d..b903165d 100755 --- a/tests/run.py +++ b/tests/run.py @@ -15,6 +15,7 @@ import sys from os import path, chdir, listdir, environ import shutil + testroot = path.dirname(__file__) or '.' if 'BUILD_TEST_PATH' in environ: # for tox testing @@ -25,15 +26,9 @@ else: newroot = path.join(newroot, listdir(newroot)[0], 'tests') shutil.rmtree(newroot, ignore_errors=True) - -if sys.version_info >= (3, 0): - print('Copying and converting sources to build/lib/tests...') - from distutils.util import copydir_run_2to3 - copydir_run_2to3(testroot, newroot) -else: - # just copying test directory to parallel testing - print('Copying sources to build/lib/tests...') - shutil.copytree(testroot, newroot) +# just copying test directory to parallel testing +print('Copying sources to build/lib/tests...') +shutil.copytree(testroot, newroot) # always test the sphinx package from build/lib/ sys.path.insert(0, path.abspath(path.join(newroot, path.pardir))) diff --git a/tests/test_application.py b/tests/test_application.py index 3d464eb5..49c27452 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -9,9 +9,9 @@ :license: BSD, see LICENSE for details. """ -from StringIO import StringIO - +from six import StringIO from docutils import nodes + from sphinx.application import ExtensionError from sphinx.domains import Domain diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 30de8790..b85aee78 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -10,13 +10,11 @@ :license: BSD, see LICENSE for details. """ -import sys -from StringIO import StringIO - # "raises" imported for usage by autodoc from util import TestApp, Struct, raises from nose.tools import with_setup +from six import StringIO from docutils.statemachine import ViewList from sphinx.ext.autodoc import AutoDirective, add_documenter, \ diff --git a/tests/test_autosummary.py b/tests/test_autosummary.py index fc506cd9..f83e5f33 100644 --- a/tests/test_autosummary.py +++ b/tests/test_autosummary.py @@ -10,6 +10,8 @@ """ import sys +from six import iteritems + from sphinx.ext.autosummary import mangle_signature from util import with_app, test_roots @@ -33,7 +35,7 @@ def test_mangle_signature(): (a=1, b=<SomeClass: a, b, c>, c=3) :: ([a, b, c]) """ - TEST = [map(lambda x: x.strip(), x.split("::")) for x in TEST.split("\n") + TEST = [[y.strip() for y in x.split("::")] for x in TEST.split("\n") if '::' in x] for inp, outp in TEST: res = mangle_signature(inp).strip().replace(u"\u00a0", " ") @@ -97,6 +99,6 @@ def test_get_items_summary(): 'noSentence': "this doesn't start with a", 'emptyLine': "This is the real summary", } - for key, expected in expected_values.iteritems(): + for key, expected in iteritems(expected_values): assert autosummary_items[key][2] == expected, 'Summary for %s was %r -'\ ' expected %r' % (key, autosummary_items[key], expected) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index f4b8c21c..5f0a93ce 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -11,9 +11,9 @@ import os import re -import sys -import htmlentitydefs -from StringIO import StringIO + +from six import PY3, iteritems, StringIO +from six.moves import html_entities try: import pygments @@ -54,7 +54,7 @@ None:\\d+: WARNING: citation not found: missing %(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; ' """ -if sys.version_info >= (3, 0): +if PY3: ENV_WARNINGS = remove_unicode_literals(ENV_WARNINGS) HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS) @@ -266,7 +266,7 @@ if pygments: (".//div[@class='inc-lines highlight-text']//pre", r'^class Foo:\n pass\nclass Bar:\n$'), (".//div[@class='inc-startend highlight-text']//pre", - ur'^foo = "Including Unicode characters: üöä"\n$'), + u'^foo = "Including Unicode characters: üöä"\\n$'), (".//div[@class='inc-preappend highlight-text']//pre", r'(?m)^START CODE$'), (".//div[@class='inc-pyobj-dedent highlight-python']//span", @@ -344,9 +344,9 @@ def test_html(app): '--- Expected (regex):\n' + html_warnings_exp + \ '--- Got:\n' + html_warnings - for fname, paths in HTML_XPATH.iteritems(): + for fname, paths in iteritems(HTML_XPATH): parser = NslessParser() - parser.entity.update(htmlentitydefs.entitydefs) + parser.entity.update(html_entities.entitydefs) fp = open(os.path.join(app.outdir, fname), 'rb') try: etree = ET.parse(fp, parser) diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 0b2d8964..41ae03df 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -12,10 +12,10 @@ from __future__ import print_function import os import re -import sys -from StringIO import StringIO from subprocess import Popen, PIPE +from six import PY3, StringIO + from sphinx.writers.latex import LaTeXTranslator from util import test_root, SkipTest, remove_unicode_literals, with_app @@ -35,7 +35,7 @@ WARNING: invalid pair index entry u'' WARNING: invalid pair index entry u'keyword; ' """ -if sys.version_info >= (3, 0): +if PY3: LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS) diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 2dcce7ed..fbe8a173 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -12,10 +12,10 @@ from __future__ import print_function import os import re -import sys -from StringIO import StringIO from subprocess import Popen, PIPE +from six import PY3, StringIO + from sphinx.writers.texinfo import TexinfoTranslator from util import test_root, SkipTest, remove_unicode_literals, with_app @@ -34,7 +34,7 @@ None:None: WARNING: no matching candidate for image URI u'foo.\\*' None:None: WARNING: no matching candidate for image URI u'svgimg.\\*' """ -if sys.version_info >= (3, 0): +if PY3: TEXINFO_WARNINGS = remove_unicode_literals(TEXINFO_WARNINGS) diff --git a/tests/test_config.py b/tests/test_config.py index db9a2a01..d4bf24cf 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -9,13 +9,12 @@ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ -import sys +from six import PY3 from util import TestApp, with_app, with_tempdir, raises, raises_msg from sphinx.config import Config from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError -from sphinx.util.pycompat import b @with_app(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True', @@ -101,7 +100,7 @@ def test_errors_warnings(dir): # test the warning for bytestrings with non-ascii content # bytestrings with non-ascii content are a syntax error in python3 so we # skip the test there - if sys.version_info >= (3, 0): + if PY3: return (dir / 'conf.py').write_text( u'# -*- coding: latin-1\nproject = "fooä"\n', encoding='latin-1') @@ -122,8 +121,8 @@ def test_needs_sphinx(): def test_config_eol(tmpdir): # test config file's eol patterns: LF, CRLF configfile = tmpdir / 'conf.py' - for eol in ('\n', '\r\n'): - configfile.write_bytes(b('project = "spam"' + eol)) + for eol in (b'\n', b'\r\n'): + configfile.write_bytes(b'project = "spam"' + eol) cfg = Config(tmpdir, 'conf.py', {}, None) cfg.init_values(lambda warning: 1/0) assert cfg.project == u'spam' diff --git a/tests/test_coverage.py b/tests/test_coverage.py index e6747b0c..bfa76a98 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -38,7 +38,7 @@ def test_build(app): undoc_py, undoc_c = pickle.loads((app.outdir / 'undoc.pickle').bytes()) assert len(undoc_c) == 1 # the key is the full path to the header file, which isn't testable - assert undoc_c.values()[0] == [('function', 'Py_SphinxTest')] + assert list(undoc_c.values())[0] == [('function', 'Py_SphinxTest')] assert 'test_autodoc' in undoc_py assert 'funcs' in undoc_py['test_autodoc'] diff --git a/tests/test_cpp_domain.py b/tests/test_cpp_domain.py index ba304529..5a0b8b6f 100644 --- a/tests/test_cpp_domain.py +++ b/tests/test_cpp_domain.py @@ -9,6 +9,8 @@ :license: BSD, see LICENSE for details. """ +from six import text_type + from util import raises from sphinx.domains.cpp import DefinitionParser, DefinitionError @@ -20,100 +22,100 @@ def parse(name, string): def test_type_definitions(): rv = parse('member_object', ' const std::string & name = 42') - assert unicode(rv) == 'const std::string& name = 42' + assert text_type(rv) == 'const std::string& name = 42' rv = parse('member_object', ' const std::string & name leftover') - assert unicode(rv) == 'const std::string& name' + assert text_type(rv) == 'const std::string& name' rv = parse('member_object', ' const std::string & name [n] leftover') - assert unicode(rv) == 'const std::string& name[n]' + assert text_type(rv) == 'const std::string& name[n]' rv = parse('member_object', 'const std::vector< unsigned int, long> &name') - assert unicode(rv) == 'const std::vector<unsigned int, long>& name' + assert text_type(rv) == 'const std::vector<unsigned int, long>& name' x = 'std::vector<std::pair<std::string, int>>& module::test(register ' \ 'foo, bar, std::string baz="foobar, blah, bleh") const = 0' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'module::myclass::operator std::vector<std::string>()' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'explicit module::myclass::foo::foo()' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int printf(const char* fmt, ...)' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int foo(const unsigned int j)' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int foo(const unsigned int const j)' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int foo(const int* const ptr)' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'std::vector<std::pair<std::string, long long>> module::blah' - assert unicode(parse('type_object', x)) == x + assert text_type(parse('type_object', x)) == x - assert unicode(parse('type_object', 'long long int foo')) == 'long long foo' + assert text_type(parse('type_object', 'long long int foo')) == 'long long foo' x = 'void operator()(const boost::array<VertexID, 2>& v) const' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'void operator()(const boost::array<VertexID, 2, "foo, bar">& v) const' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'MyClass::MyClass(MyClass::MyClass&&)' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'constexpr int get_value()' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'static constexpr int get_value()' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int get_value() const noexcept' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int get_value() const noexcept = delete' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'MyClass::MyClass(MyClass::MyClass&&) = default' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'MyClass::a_virtual_function() const override' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'MyClass::a_member_function() volatile' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'MyClass::a_member_function() const volatile' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'MyClass::a_member_function() &&' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'MyClass::a_member_function() &' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'MyClass::a_member_function() const &' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int main(int argc, char* argv[][])' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'std::vector<std::pair<std::string, int>>& module::test(register ' \ 'foo, bar[n], std::string baz="foobar, blah, bleh") const = 0' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'module::myclass foo[n]' - assert unicode(parse('member_object', x)) == x + assert text_type(parse('member_object', x)) == x x = 'int foo(Foo f=Foo(double(), std::make_pair(int(2), double(3.4))))' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int foo(A a=x(a))' - assert unicode(parse('function', x)) == x + assert text_type(parse('function', x)) == x x = 'int foo(B b=x(a)' raises(DefinitionError, parse, 'function', x) @@ -129,31 +131,31 @@ def test_type_definitions(): def test_bases(): x = 'A' - assert unicode(parse('class', x)) == x + assert text_type(parse('class', x)) == x x = 'A : B' - assert unicode(parse('class', x)) == x + assert text_type(parse('class', x)) == x x = 'A : private B' - assert unicode(parse('class', x)) == 'A : B' + assert text_type(parse('class', x)) == 'A : B' x = 'A : public B' - assert unicode(parse('class', x)) == x + assert text_type(parse('class', x)) == x x = 'A : B, C' - assert unicode(parse('class', x)) == x + assert text_type(parse('class', x)) == x x = 'A : B, protected C, D' - assert unicode(parse('class', x)) == x + assert text_type(parse('class', x)) == x def test_operators(): x = parse('function', 'void operator new [ ] ()') - assert unicode(x) == 'void operator new[]()' + assert text_type(x) == 'void operator new[]()' x = parse('function', 'void operator delete ()') - assert unicode(x) == 'void operator delete()' + assert text_type(x) == 'void operator delete()' for op in '*-+=/%!': x = parse('function', 'void operator %s ()' % op) - assert unicode(x) == 'void operator%s()' % op + assert text_type(x) == 'void operator%s()' % op diff --git a/tests/test_doctest.py b/tests/test_doctest.py index ba720041..9fb8a2ea 100644 --- a/tests/test_doctest.py +++ b/tests/test_doctest.py @@ -11,12 +11,13 @@ from __future__ import print_function import sys -import StringIO + +from six import StringIO from util import with_app -status = StringIO.StringIO() +status = StringIO() cleanup_called = 0 @with_app(buildername='doctest', status=status) diff --git a/tests/test_docutilsconf.py b/tests/test_docutilsconf.py index fcfef13b..3389a16e 100644 --- a/tests/test_docutilsconf.py +++ b/tests/test_docutilsconf.py @@ -11,9 +11,10 @@ import os import re -from StringIO import StringIO from functools import wraps +from six import StringIO + from util import test_roots, TestApp diff --git a/tests/test_env.py b/tests/test_env.py index eaaa212f..11e73edf 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -8,7 +8,7 @@ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ -import sys +from six import PY3 from util import TestApp, remove_unicode_literals, path @@ -57,7 +57,7 @@ def test_images(): htmlbuilder.imgpath = 'dummy' htmlbuilder.post_process_images(tree) image_uri_message = "no matching candidate for image URI u'foo.*'" - if sys.version_info >= (3, 0): + if PY3: image_uri_message = remove_unicode_literals(image_uri_message) assert image_uri_message in app._warning.content[-1] assert set(htmlbuilder.images.keys()) == \ diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py index d342590d..dd71c6fb 100644 --- a/tests/test_intersphinx.py +++ b/tests/test_intersphinx.py @@ -11,11 +11,8 @@ import zlib import posixpath -try: - from io import BytesIO -except ImportError: - from cStringIO import StringIO as BytesIO +from six import BytesIO from docutils import nodes from sphinx import addnodes diff --git a/tests/test_intl.py b/tests/test_intl.py index 56c88bd6..bb54e5df 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -13,10 +13,11 @@ from __future__ import print_function import os import re -from StringIO import StringIO from subprocess import Popen, PIPE from xml.etree import ElementTree +from six import StringIO, string_types + from util import test_roots, path, with_app, SkipTest @@ -76,7 +77,7 @@ def elem_gettexts(elem): # this function copied from Python-2.7 'ElementTree.itertext'. # for compatibility to Python-2.6 tag = self.tag - if not isinstance(tag, basestring) and tag is not None: + if not isinstance(tag, string_types) and tag is not None: return if self.text: yield self.text @@ -97,7 +98,7 @@ def assert_elem(elem, texts=None, refs=None, names=None): _texts = elem_gettexts(elem) assert _texts == texts if refs is not None: - _refs = map(elem_getref, elem.findall('reference')) + _refs = [elem_getref(x) for x in elem.findall('reference')] assert _refs == refs if names is not None: _names = elem.attrib.get('names').split() diff --git a/tests/test_markup.py b/tests/test_markup.py index 4f0b0de8..e58cfe68 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -15,7 +15,6 @@ from docutils import frontend, utils, nodes from docutils.parsers import rst from sphinx.util import texescape -from sphinx.util.pycompat import b from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator @@ -54,7 +53,7 @@ class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator): def verify_re(rst, html_expected, latex_expected): - document = utils.new_document(b('test data'), settings) + document = utils.new_document(b'test data', settings) document['file'] = 'dummy' parser.parse(rst, document) for msg in document.traverse(nodes.system_message): @@ -128,7 +127,7 @@ def test_inline(): def test_latex_escaping(): # correct escaping in normal mode yield (verify, u'Γ\\\\∞$', None, - ur'\(\Gamma\)\textbackslash{}\(\infty\)\$') + r'\(\Gamma\)\textbackslash{}\(\infty\)\$') # in verbatim code fragments yield (verify, u'::\n\n @Γ\\∞${}', None, u'\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' @@ -136,4 +135,4 @@ def test_latex_escaping(): u'\\end{Verbatim}') # in URIs yield (verify_re, u'`test <http://example.com/~me/>`_', None, - ur'\\href{http://example.com/~me/}{test}.*') + r'\\href{http://example.com/~me/}{test}.*') diff --git a/tests/test_napoleon_iterators.py b/tests/test_napoleon_iterators.py index db0be32c..320047e5 100644 --- a/tests/test_napoleon_iterators.py +++ b/tests/test_napoleon_iterators.py @@ -29,7 +29,7 @@ class BaseIteratorsTest(TestCase): self.assertTrueTwice(it.has_next) self.assertEqualTwice(expected, it.peek) self.assertTrueTwice(it.has_next) - self.assertEqual(expected, it.next()) + self.assertEqual(expected, next(it)) if is_last: self.assertFalseTwice(it.has_next) self.assertRaisesTwice(StopIteration, it.next) @@ -217,7 +217,7 @@ class PeekIterTest(BaseIteratorsTest): self.assertTrueTwice(it.has_next) self.assertEqualTwice(['1', '2', '3', it.sentinel], it.peek, 4) self.assertTrueTwice(it.has_next) - self.assertEqual('1', it.next()) + self.assertEqual('1', next(it)) self.assertTrueTwice(it.has_next) self.assertEqualTwice(['2', '3'], it.peek, 2) self.assertTrueTwice(it.has_next) @@ -237,7 +237,7 @@ class PeekIterTest(BaseIteratorsTest): it = peek_iter(a) self.assertTrueTwice(it.has_next) self.assertEqualTwice('1', it.peek) - self.assertEqual('1', it.next()) + self.assertEqual('1', next(it)) self.assertFalseTwice(it.has_next) self.assertEqualTwice(it.sentinel, it.peek) self.assertFalseTwice(it.has_next) @@ -246,10 +246,10 @@ class PeekIterTest(BaseIteratorsTest): it = peek_iter(a) self.assertTrueTwice(it.has_next) self.assertEqualTwice('1', it.peek) - self.assertEqual('1', it.next()) + self.assertEqual('1', next(it)) self.assertTrueTwice(it.has_next) self.assertEqualTwice('2', it.peek) - self.assertEqual('2', it.next()) + self.assertEqual('2', next(it)) self.assertFalseTwice(it.has_next) self.assertEqualTwice(it.sentinel, it.peek) self.assertFalseTwice(it.has_next) @@ -265,7 +265,7 @@ class PeekIterTest(BaseIteratorsTest): it = peek_iter(a) self.assertTrueTwice(it.has_next) self.assertEqualTwice(['1'], it.peek, 1) - self.assertEqual('1', it.next()) + self.assertEqual('1', next(it)) self.assertFalseTwice(it.has_next) self.assertEqualTwice([it.sentinel], it.peek, 1) self.assertFalseTwice(it.has_next) @@ -274,10 +274,10 @@ class PeekIterTest(BaseIteratorsTest): it = peek_iter(a) self.assertTrueTwice(it.has_next) self.assertEqualTwice(['1'], it.peek, 1) - self.assertEqual('1', it.next()) + self.assertEqual('1', next(it)) self.assertTrueTwice(it.has_next) self.assertEqualTwice(['2'], it.peek, 1) - self.assertEqual('2', it.next()) + self.assertEqual('2', next(it)) self.assertFalseTwice(it.has_next) self.assertEqualTwice([it.sentinel], it.peek, 1) self.assertFalseTwice(it.has_next) diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index ef29a2b0..74deb46d 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -11,10 +11,11 @@ import sys import time -from StringIO import StringIO -import tempfile -from util import raises, with_tempdir, with_app, SkipTest +from six import PY2, text_type, StringIO +from six.moves import input + +from util import raises, with_tempdir, SkipTest from sphinx import application from sphinx import quickstart as qs @@ -28,18 +29,18 @@ warnfile = StringIO() def setup_module(): nocolor() -def mock_raw_input(answers, needanswer=False): +def mock_input(answers, needanswer=False): called = set() - def raw_input(prompt): + def input_(prompt): if prompt in called: raise AssertionError('answer for %r missing and no default ' 'present' % prompt) called.add(prompt) - if sys.version_info < (3, 0): + if PY2: prompt = str(prompt) # Python2.x raw_input emulation # `raw_input` encode `prompt` by default encoding to print. else: - prompt = unicode(prompt) # Python3.x input emulation + prompt = text_type(prompt) # Python3.x input emulation # `input` decode prompt by default encoding before print. for question in answers: if prompt.startswith(qs.PROMPT_PREFIX + question): @@ -47,15 +48,12 @@ def mock_raw_input(answers, needanswer=False): if needanswer: raise AssertionError('answer for %r missing' % prompt) return '' - return raw_input + return input_ -try: - real_raw_input = raw_input -except NameError: - real_raw_input = input +real_input = input def teardown_module(): - qs.term_input = real_raw_input + qs.term_input = real_input qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None) coloron() @@ -63,12 +61,12 @@ def teardown_module(): def test_quickstart_inputstrip(): d = {} answers = { - 'Q1': 'Y\r', # input() return with '\r' on Python-3.2.0 for Windows - 'Q2': ' Yes \r', + 'Q1': 'Y', + 'Q2': ' Yes ', 'Q3': 'N', 'Q4': 'N ', } - qs.term_input = mock_raw_input(answers) + qs.term_input = mock_input(answers) qs.do_prompt(d, 'k1', 'Q1') assert d['k1'] == 'Y' qs.do_prompt(d, 'k2', 'Q2') @@ -88,7 +86,7 @@ def test_do_prompt(): 'Q5': 'no', 'Q6': 'foo', } - qs.term_input = mock_raw_input(answers) + qs.term_input = mock_input(answers) try: qs.do_prompt(d, 'k1', 'Q1') except AssertionError: @@ -113,7 +111,7 @@ def test_do_prompt_with_nonascii(): answers = { 'Q1': u'\u30c9\u30a4\u30c4', } - qs.term_input = mock_raw_input(answers) + qs.term_input = mock_input(answers) try: qs.do_prompt(d, 'k1', 'Q1', default=u'\u65e5\u672c') except UnicodeEncodeError: @@ -131,7 +129,7 @@ def test_quickstart_defaults(tempdir): 'Author name': 'Georg Brandl', 'Project version': '0.1', } - qs.term_input = mock_raw_input(answers) + qs.term_input = mock_input(answers) d = {} qs.ask_user(d) qs.generate(d) @@ -186,7 +184,7 @@ def test_quickstart_all_answers(tempdir): 'Create Windows command file': 'no', 'Do you want to use the epub builder': 'yes', } - qs.term_input = mock_raw_input(answers, needanswer=True) + qs.term_input = mock_input(answers, needanswer=True) qs.TERM_ENCODING = 'utf-8' d = {} qs.ask_user(d) @@ -232,7 +230,7 @@ def test_generated_files_eol(tempdir): 'Author name': 'Georg Brandl', 'Project version': '0.1', } - qs.term_input = mock_raw_input(answers) + qs.term_input = mock_input(answers) d = {} qs.ask_user(d) qs.generate(d) @@ -253,7 +251,7 @@ def test_quickstart_and_build(tempdir): 'Author name': 'Georg Brandl', 'Project version': '0.1', } - qs.term_input = mock_raw_input(answers) + qs.term_input = mock_input(answers) d = {} qs.ask_user(d) qs.generate(d) @@ -279,7 +277,7 @@ def test_default_filename(tempdir): 'Author name': 'Georg Brandl', 'Project version': '0.1', } - qs.term_input = mock_raw_input(answers) + qs.term_input = mock_input(answers) d = {} qs.ask_user(d) qs.generate(d) diff --git a/tests/test_search.py b/tests/test_search.py index 2efd753c..a7e99e04 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -13,7 +13,6 @@ from docutils import frontend, utils from docutils.parsers import rst from sphinx.search import IndexBuilder -from sphinx.util.pycompat import b settings = parser = None @@ -32,7 +31,7 @@ test that non-comments are indexed: fermion ''' def test_wordcollector(): - doc = utils.new_document(b('test data'), settings) + doc = utils.new_document(b'test data', settings) doc['file'] = 'dummy' parser.parse(FILE_CONTENTS, doc) diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py index 81d7c178..9a41601d 100644 --- a/tests/test_searchadapters.py +++ b/tests/test_searchadapters.py @@ -10,7 +10,8 @@ """ import os -from StringIO import StringIO + +from six import StringIO from sphinx.websupport import WebSupport diff --git a/tests/test_websupport.py b/tests/test_websupport.py index bcb75675..d355422c 100644 --- a/tests/test_websupport.py +++ b/tests/test_websupport.py @@ -10,9 +10,10 @@ """ import os -from StringIO import StringIO from functools import wraps +from six import StringIO + from sphinx.websupport import WebSupport from sphinx.websupport.errors import DocumentNotFoundError, \ CommentNotAllowedError, UserNotAuthorizedError diff --git a/tests/util.py b/tests/util.py index 8c4ff3df..72cac038 100644 --- a/tests/util.py +++ b/tests/util.py @@ -8,12 +8,13 @@ """ import sys -import StringIO import tempfile import shutil import re from functools import wraps +from six import StringIO + from sphinx import application from sphinx.theming import Theme from sphinx.ext.autodoc import AutoDirective @@ -162,7 +163,7 @@ class TestApp(application.Sphinx): if confoverrides is None: confoverrides = {} if status is None: - status = StringIO.StringIO() + status = StringIO() if warning is None: warning = ListOutput('stderr') if freshenv is None: @@ -1,5 +1,5 @@ [tox] -envlist=py26,py27,py32,py33,pypy,du11,du10 +envlist=py26,py27,py33,py34,pypy,du11,du10 [testenv] deps= @@ -22,11 +22,6 @@ deps= mock {[testenv]deps} -[testenv:py32] -deps= - mock - {[testenv]deps} - [testenv:pypy] deps= mock diff --git a/utils/check_sources.py b/utils/check_sources.py index 6b984ad6..1b78ab62 100755 --- a/utils/check_sources.py +++ b/utils/check_sources.py @@ -17,12 +17,6 @@ import cStringIO from optparse import OptionParser from os.path import join, splitext, abspath -if sys.version_info >= (3, 0): - def b(s): - return s.encode('utf-8') -else: - b = str - checkers = {} @@ -37,24 +31,24 @@ def checker(*suffixes, **kwds): name_mail_re = r'[\w ]+(<.*?>)?' -copyright_re = re.compile(b(r'^ :copyright: Copyright 200\d(-20\d\d)? ' - r'by %s(, %s)*[,.]$' % - (name_mail_re, name_mail_re))) -license_re = re.compile(b(r" :license: (.*?).\n")) -copyright_2_re = re.compile(b(r'^ %s(, %s)*[,.]$' % - (name_mail_re, name_mail_re))) -coding_re = re.compile(b(r'coding[:=]\s*([-\w.]+)')) -not_ix_re = re.compile(b(r'\bnot\s+\S+?\s+i[sn]\s\S+')) -is_const_re = re.compile(b(r'if.*?==\s+(None|False|True)\b')) - -misspellings = [b("developement"), b("adress"), # ALLOW-MISSPELLING - b("verificate"), b("informations")] # ALLOW-MISSPELLING +copyright_re = re.compile(br'^ :copyright: Copyright 200\d(-20\d\d)? ' + br'by %s(, %s)*[,.]$' % + (name_mail_re, name_mail_re)) +license_re = re.compile(br" :license: (.*?).\n") +copyright_2_re = re.compile(br'^ %s(, %s)*[,.]$' % + (name_mail_re, name_mail_re)) +coding_re = re.compile(br'coding[:=]\s*([-\w.]+)') +not_ix_re = re.compile(br'\bnot\s+\S+?\s+i[sn]\s\S+') +is_const_re = re.compile(br'if.*?==\s+(None|False|True)\b') + +misspellings = [b"developement", b"adress", # ALLOW-MISSPELLING + b"verificate", b"informations"] # ALLOW-MISSPELLING if sys.version_info < (3, 0): @checker('.py') def check_syntax(fn, lines): try: - compile(b('').join(lines), fn, "exec") + compile(b''.join(lines), fn, "exec") except SyntaxError as err: yield 0, "not compilable: %s" % err @@ -69,7 +63,7 @@ def check_style_and_encoding(fn, lines): co = coding_re.search(line) if co: encoding = co.group(1).decode('ascii') - if line.strip().startswith(b('#')): + if line.strip().startswith(b'#'): continue #m = not_ix_re.search(line) #if m: @@ -89,7 +83,7 @@ def check_style_and_encoding(fn, lines): def check_fileheader(fn, lines): # line number correction c = 1 - if lines[0:1] == [b('#!/usr/bin/env python\n')]: + if lines[0:1] == [b'#!/usr/bin/env python\n']: lines = lines[1:] c = 2 @@ -98,38 +92,38 @@ def check_fileheader(fn, lines): for lno, l in enumerate(lines): llist.append(l) if lno == 0: - if l == b('# -*- coding: rot13 -*-\n'): + if l == b'# -*- coding: rot13 -*-\n': # special-case pony package return - elif l != b('# -*- coding: utf-8 -*-\n'): + elif l != b'# -*- coding: utf-8 -*-\n': yield 1, "missing coding declaration" elif lno == 1: - if l != b('"""\n') and l != b('r"""\n'): + if l != b'"""\n' and l != b'r"""\n': yield 2, 'missing docstring begin (""")' else: docopen = True elif docopen: - if l == b('"""\n'): + if l == b'"""\n': # end of docstring if lno <= 4: yield lno+c, "missing module name in docstring" break - if l != b("\n") and l[:4] != b(' ') and docopen: + if l != b"\n" and l[:4] != b' ' and docopen: yield lno+c, "missing correct docstring indentation" if lno == 2: # if not in package, don't check the module name modname = fn[:-3].replace('/', '.').replace('.__init__', '') while modname: - if l.lower()[4:-1] == b(modname): + if l.lower()[4:-1] == bytes(modname): break modname = '.'.join(modname.split('.')[1:]) else: yield 3, "wrong module name in docstring heading" modnamelen = len(l.strip()) elif lno == 3: - if l.strip() != modnamelen * b("~"): + if l.strip() != modnamelen * b"~": yield 4, "wrong module name underline, should be ~~~...~" else: @@ -152,16 +146,16 @@ def check_fileheader(fn, lines): @checker('.py', '.html', '.rst') def check_whitespace_and_spelling(fn, lines): for lno, line in enumerate(lines): - if b("\t") in line: + if b"\t" in line: yield lno+1, "OMG TABS!!!1 " - if line[:-1].rstrip(b(' \t')) != line[:-1]: + if line[:-1].rstrip(b' \t') != line[:-1]: yield lno+1, "trailing whitespace" for word in misspellings: - if word in line and b('ALLOW-MISSPELLING') not in line: + if word in line and b'ALLOW-MISSPELLING' not in line: yield lno+1, '"%s" used' % word -bad_tags = map(b, ['<u>', '<s>', '<strike>', '<center>', '<font']) +bad_tags = [b'<u>', b'<s>', b'<strike>', b'<center>', b'<font'] @checker('.html') def check_xhtml(fn, lines): |