From 929683df270c18a221e38217e2ab85386f5aa590 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 5 Jan 2017 13:41:17 +0900 Subject: [PATCH] Sphinx.status_iterator() is now deprecated --- CHANGES | 2 ++ sphinx/application.py | 54 ++++++++++++---------------------- sphinx/builders/__init__.py | 18 ++++++------ sphinx/builders/epub.py | 6 ++-- sphinx/builders/gettext.py | 17 ++++++----- sphinx/builders/html.py | 15 +++++----- sphinx/environment/__init__.py | 13 ++++---- sphinx/ext/viewcode.py | 10 +++---- sphinx/util/__init__.py | 50 +++++++++++++++++++++++++++++-- sphinx/util/console.py | 2 +- tests/test_util.py | 50 ++++++++++++++++++++++++++++++- 11 files changed, 158 insertions(+), 79 deletions(-) diff --git a/CHANGES b/CHANGES index 29eb095d5..671e65b7c 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,8 @@ Deprecated * ``sphinx.util.compat.docutils_version`` is now deprecated * #2367: ``Sphinx.warn()``, ``Sphinx.info()`` and other logging methods are now deprecated. Please use ``sphinx.util.logging`` (:ref:`logging-api`) instead. +* ``Sphinx.status_iterator()` and ``Sphinx.old_status_iterator()`` is now + deprecated. Please use ``sphinx.util:status_iterator()`` intead. Release 1.5.2 (in development) =============================== diff --git a/sphinx/application.py b/sphinx/application.py index d6b781f59..52194294d 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -21,7 +21,7 @@ import traceback from os import path from collections import deque -from six import iteritems, itervalues, text_type +from six import iteritems, itervalues from six.moves import cStringIO from docutils import nodes @@ -42,11 +42,10 @@ from sphinx.roles import XRefRole from sphinx.util import pycompat # noqa: F401 from sphinx.util import import_object from sphinx.util import logging +from sphinx.util import status_iterator, old_status_iterator, display_chunk from sphinx.util.tags import Tags from sphinx.util.osutil import ENOENT -from sphinx.util.console import ( # type: ignore - bold, darkgreen, term_width_line -) +from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.util.i18n import find_catalog_source_files if False: @@ -431,48 +430,31 @@ class Sphinx(object): def _display_chunk(chunk): # type: (Any) -> unicode - if isinstance(chunk, (list, tuple)): - if len(chunk) == 1: - return text_type(chunk[0]) - return '%s .. %s' % (chunk[0], chunk[-1]) - return text_type(chunk) + warnings.warn('app._display_chunk() is now deprecated. ' + 'Use sphinx.util.display_chunk() instead.', + RemovedInSphinx17Warning) + return display_chunk(chunk) def old_status_iterator(self, iterable, summary, colorfunc=darkgreen, - stringify_func=_display_chunk): + stringify_func=display_chunk): # type: (Iterable, unicode, Callable, Callable[[Any], unicode]) -> Iterator - l = 0 - for item in iterable: - if l == 0: - logger.info(bold(summary), nonl=True) - l = 1 - logger.info(colorfunc(stringify_func(item)) + ' ', nonl=True) + warnings.warn('app.old_status_iterator() is now deprecated. ' + 'Use sphinx.util.status_iterator() instead.', + RemovedInSphinx17Warning) + for item in old_status_iterator(iterable, summary, + color="darkgreen", stringify_func=stringify_func): yield item - if l == 1: - logger.info('') # new version with progress info def status_iterator(self, iterable, summary, colorfunc=darkgreen, length=0, stringify_func=_display_chunk): # type: (Iterable, unicode, Callable, int, Callable[[Any], unicode]) -> Iterable - if length == 0: - for item in self.old_status_iterator(iterable, summary, colorfunc, - stringify_func): - yield item - return - l = 0 - summary = bold(summary) - for item in iterable: - l += 1 - s = '%s[%3d%%] %s' % (summary, 100*l/length, - colorfunc(stringify_func(item))) - if self.verbosity: - s += '\n' - else: - s = term_width_line(s) - logger.info(s, nonl=True) + warnings.warn('app.status_iterator() is now deprecated. ' + 'Use sphinx.util.status_iterator() instead.', + RemovedInSphinx17Warning) + for item in status_iterator(iterable, summary, length=length, verbosity=self.verbosity, + color="darkgreen", stringify_func=stringify_func): yield item - if l > 0: - logger.info('') # ---- general extensibility interface ------------------------------------- diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 35bf52ad6..00aeac771 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -19,10 +19,10 @@ except ImportError: from docutils import nodes -from sphinx.util import i18n, path_stabilize, logging +from sphinx.util import i18n, path_stabilize, logging, status_iterator from sphinx.util.osutil import SEP, relative_uri from sphinx.util.i18n import find_catalog -from sphinx.util.console import bold, darkgreen # type: ignore +from sphinx.util.console import bold # type: ignore from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \ parallel_available @@ -183,9 +183,9 @@ class Builder(object): return path.relpath(cat.mo_path, self.env.srcdir).replace(path.sep, SEP) logger.info(bold('building [mo]: ') + message) - for catalog in self.app.status_iterator( - catalogs, 'writing output... ', darkgreen, len(catalogs), - cat2relpath): + for catalog in status_iterator(catalogs, 'writing output... ', "darkgreen", + len(catalogs), self.app.verbosity, + stringify_func=cat2relpath): catalog.write_mo(self.config.language) def compile_all_catalogs(self): @@ -384,8 +384,8 @@ class Builder(object): def _write_serial(self, docnames): # type: (Sequence[unicode]) -> None with logging.pending_warnings(): - for docname in self.app.status_iterator( - docnames, 'writing output... ', darkgreen, len(docnames)): + for docname in status_iterator(docnames, 'writing output... ', "darkgreen", + len(docnames), self.app.verbosity): doctree = self.env.get_and_resolve_doctree(docname, self) self.write_doc_serialized(docname, doctree) self.write_doc(docname, doctree) @@ -406,8 +406,8 @@ class Builder(object): tasks = ParallelTasks(nproc) chunks = make_chunks(docnames, nproc) - for chunk in self.app.status_iterator( - chunks, 'writing output... ', darkgreen, len(chunks)): + for chunk in status_iterator(chunks, 'writing output... ', "darkgreen", + len(chunks), self.app.verbosity): arg = [] for i, docname in enumerate(chunk): doctree = self.env.get_and_resolve_doctree(docname, self) diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py index a48f94436..d351be5a2 100644 --- a/sphinx/builders/epub.py +++ b/sphinx/builders/epub.py @@ -30,9 +30,9 @@ from docutils import nodes from sphinx import addnodes from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.util import logging +from sphinx.util import status_iterator from sphinx.util.osutil import ensuredir, copyfile, make_filename, EEXIST from sphinx.util.smartypants import sphinx_smarty_pants as ssp -from sphinx.util.console import brown # type: ignore if False: # For type annotation @@ -470,8 +470,8 @@ class EpubBuilder(StandaloneHTMLBuilder): converting the format and resizing the image if necessary/possible. """ ensuredir(path.join(self.outdir, self.imagedir)) - for src in self.app.status_iterator(self.images, 'copying images... ', - brown, len(self.images)): + for src in status_iterator(self.images, 'copying images... ', "brown", + len(self.images), self.app.verbosity): dest = self.images[src] try: img = Image.open(path.join(self.srcdir, src)) diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 6993210f3..0c255abf2 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -21,12 +21,12 @@ from uuid import uuid4 from six import iteritems from sphinx.builders import Builder -from sphinx.util import split_index_msg, logging +from sphinx.util import split_index_msg, logging, status_iterator from sphinx.util.tags import Tags from sphinx.util.nodes import extract_messages, traverse_translatable_index from sphinx.util.osutil import safe_relpath, ensuredir, canon_path from sphinx.util.i18n import find_catalog -from sphinx.util.console import darkgreen, purple, bold # type: ignore +from sphinx.util.console import bold # type: ignore from sphinx.locale import pairindextypes if False: @@ -224,8 +224,8 @@ class MessageCatalogBuilder(I18nBuilder): extract_translations = self.templates.environment.extract_translations - for template in self.app.status_iterator( - files, 'reading templates... ', purple, len(files)): + for template in status_iterator(files, 'reading templates... ', "purple", + len(files), self.app.verbosity): with open(template, 'r', encoding='utf-8') as f: # type: ignore context = f.read() for line, meth, msg in extract_translations(context): @@ -247,10 +247,11 @@ class MessageCatalogBuilder(I18nBuilder): ctime = datetime.fromtimestamp( # type: ignore timestamp, ltz).strftime('%Y-%m-%d %H:%M%z'), ) - for textdomain, catalog in self.app.status_iterator( - iteritems(self.catalogs), "writing message catalogs... ", - darkgreen, len(self.catalogs), - lambda textdomain__: textdomain__[0]): + for textdomain, catalog in status_iterator(iteritems(self.catalogs), + "writing message catalogs... ", + "darkgreen", len(self.catalogs), + self.app.verbosity, + lambda textdomain__: textdomain__[0]): # noop if config.gettext_compact is set ensuredir(path.join(self.outdir, path.dirname(textdomain))) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 2fd7a72a8..a07248d82 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -28,7 +28,7 @@ from docutils.frontend import OptionParser from docutils.readers.doctree import Reader as DoctreeReader from sphinx import package_dir, __display_version__ -from sphinx.util import jsonimpl, logging +from sphinx.util import jsonimpl, logging, status_iterator from sphinx.util.i18n import format_date from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \ movefile, copyfile @@ -42,7 +42,7 @@ from sphinx.theming import Theme from sphinx.builders import Builder from sphinx.application import ENV_PICKLE_FILENAME from sphinx.highlighting import PygmentsBridge -from sphinx.util.console import bold, darkgreen, brown # type: ignore +from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.writers.html import HTMLWriter, HTMLTranslator, \ SmartyPantsHTMLTranslator @@ -584,8 +584,8 @@ class StandaloneHTMLBuilder(Builder): # copy image files if self.images: ensuredir(path.join(self.outdir, self.imagedir)) - for src in self.app.status_iterator(self.images, 'copying images... ', - brown, len(self.images)): + for src in status_iterator(self.images, 'copying images... ', "brown", + len(self.images), self.app.verbosity): dest = self.images[src] try: copyfile(path.join(self.srcdir, src), @@ -601,10 +601,9 @@ class StandaloneHTMLBuilder(Builder): # copy downloadable files if self.env.dlfiles: ensuredir(path.join(self.outdir, '_downloads')) - for src in self.app.status_iterator(self.env.dlfiles, - 'copying downloadable files... ', - brown, len(self.env.dlfiles), - stringify_func=to_relpath): + for src in status_iterator(self.env.dlfiles, 'copying downloadable files... ', + "brown", len(self.env.dlfiles), self.app.verbosity, + stringify_func=to_relpath): dest = self.env.dlfiles[src][1] try: copyfile(path.join(self.srcdir, src), diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 1fb9ec19e..dee67c96d 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -33,14 +33,15 @@ from docutils.frontend import OptionParser from sphinx import addnodes from sphinx.io import SphinxStandaloneReader, SphinxDummyWriter, SphinxFileInput -from sphinx.util import get_matching_docs, docname_join, FilenameUniqDict, logging +from sphinx.util import logging +from sphinx.util import get_matching_docs, docname_join, FilenameUniqDict, status_iterator from sphinx.util.nodes import clean_astext, WarningStream, is_translatable, \ process_only_nodes from sphinx.util.osutil import SEP, getcwd, fs_encoding, ensuredir from sphinx.util.images import guess_mimetype from sphinx.util.i18n import find_catalog_files, get_image_filename_for_language, \ search_image_for_language -from sphinx.util.console import bold, purple # type: ignore +from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import sphinx_domains from sphinx.util.matching import compile_matchers from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks @@ -623,8 +624,8 @@ class BuildEnvironment(object): def _read_serial(self, docnames, app): # type: (List[unicode], Sphinx) -> None - for docname in app.status_iterator(docnames, 'reading sources... ', - purple, len(docnames)): + for docname in status_iterator(docnames, 'reading sources... ', "purple", + len(docnames), self.app.verbosity): # remove all inventory entries for that file app.emit('env-purge-doc', self, docname) self.clear_doc(docname) @@ -661,8 +662,8 @@ class BuildEnvironment(object): chunks = make_chunks(docnames, nproc) warnings = [] # type: List[Tuple] - for chunk in app.status_iterator( - chunks, 'reading sources... ', purple, len(chunks)): + for chunk in status_iterator(chunks, 'reading sources... ', "purple", + len(chunks), self.app.verbosity): tasks.add_task(read_process, chunk, merge) # make sure all threads have finished diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index c639c6c55..c6b666a0a 100644 --- a/sphinx/ext/viewcode.py +++ b/sphinx/ext/viewcode.py @@ -19,9 +19,8 @@ import sphinx from sphinx import addnodes from sphinx.locale import _ from sphinx.pycode import ModuleAnalyzer -from sphinx.util import get_full_modname, logging +from sphinx.util import get_full_modname, logging, status_iterator from sphinx.util.nodes import make_refnode -from sphinx.util.console import blue # type: ignore if False: # For type annotation @@ -147,9 +146,10 @@ def collect_pages(app): # app.builder.info(' (%d module code pages)' % # len(env._viewcode_modules), nonl=1) - for modname, entry in app.status_iterator( - iteritems(env._viewcode_modules), 'highlighting module code... ', # type:ignore - blue, len(env._viewcode_modules), lambda x: x[0]): # type:ignore + for modname, entry in status_iterator(iteritems(env._viewcode_modules), # type: ignore + 'highlighting module code... ', "blue", + len(env._viewcode_modules), # type: ignore + app.verbosity, lambda x: x[0]): if not entry: continue code, tags, used, refname = entry diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 11e48b4a1..ff5e35fa4 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -29,7 +29,7 @@ from docutils.utils import relative_path from sphinx.errors import PycodeError, SphinxParallelError, ExtensionError from sphinx.util import logging -from sphinx.util.console import strip_colors +from sphinx.util.console import strip_colors, colorize, bold, term_width_line # type: ignore from sphinx.util.fileutil import copy_asset_file from sphinx.util.osutil import fs_encoding @@ -45,7 +45,7 @@ from sphinx.util.matching import patfilter # noqa if False: # For type annotation - from typing import Any, Callable, Iterable, Pattern, Sequence, Tuple # NOQA + from typing import Any, Callable, Iterable, Iterator, Pattern, Sequence, Tuple # NOQA logger = logging.getLogger(__name__) @@ -537,3 +537,49 @@ def split_docinfo(text): return '', result[0] else: return result[1:] + + +def display_chunk(chunk): + # type: (Any) -> unicode + if isinstance(chunk, (list, tuple)): + if len(chunk) == 1: + return text_type(chunk[0]) + return '%s .. %s' % (chunk[0], chunk[-1]) + return text_type(chunk) + + +def old_status_iterator(iterable, summary, color="darkgreen", stringify_func=display_chunk): + # type: (Iterable, unicode, str, Callable[[Any], unicode]) -> Iterator + l = 0 + for item in iterable: + if l == 0: + logger.info(bold(summary), nonl=True) + l = 1 + logger.info(stringify_func(item), color=color, nonl=True) + logger.info(" ", nonl=True) + yield item + if l == 1: + logger.info('') + + +# new version with progress info +def status_iterator(iterable, summary, color="darkgreen", length=0, verbosity=0, + stringify_func=display_chunk): + # type: (Iterable, unicode, str, int, int, Callable[[Any], unicode]) -> Iterable + if length == 0: + for item in old_status_iterator(iterable, summary, color, stringify_func): + yield item + return + l = 0 + summary = bold(summary) + for item in iterable: + l += 1 + s = '%s[%3d%%] %s' % (summary, 100 * l / length, colorize(color, stringify_func(item))) + if verbosity: + s += '\n' + else: + s = term_width_line(s) + logger.info(s, nonl=True) + yield item + if l > 0: + logger.info('') diff --git a/sphinx/util/console.py b/sphinx/util/console.py index 6dc4b88ca..f4e03775f 100644 --- a/sphinx/util/console.py +++ b/sphinx/util/console.py @@ -83,7 +83,7 @@ def coloron(): def colorize(name, text): - # type: (str, str) -> str + # type: (str, unicode) -> unicode return codes.get(name, '') + text + codes.get('reset', '') diff --git a/tests/test_util.py b/tests/test_util.py index d97329668..08a4f03c7 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -8,7 +8,14 @@ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ -from sphinx.util import encode_uri, split_docinfo + +import pytest +from mock import patch + +from sphinx.util import display_chunk, encode_uri, split_docinfo, status_iterator +from sphinx.util import logging + +from util import with_app, strip_escseq def test_encode_uri(): @@ -46,3 +53,44 @@ def test_splitdocinfo(): docinfo, content = split_docinfo(source) assert docinfo == ":multiline: one\n\ttwo\n\tthree\n" assert content == '\nHello world.\n' + + +def test_display_chunk(): + assert display_chunk('hello') == 'hello' + assert display_chunk(['hello']) == 'hello' + assert display_chunk(['hello', 'sphinx', 'world']) == 'hello .. world' + assert display_chunk(('hello',)) == 'hello' + assert display_chunk(('hello', 'sphinx', 'world')) == 'hello .. world' + + +@pytest.mark.sphinx('dummy') +@patch('sphinx.util.console._tw', 40) # terminal width = 40 +def test_status_iterator(app, status, warning): + logging.setup(app, status, warning) + + # test for old_status_iterator + status.truncate(0) + yields = list(status_iterator(['hello', 'sphinx', 'world'], 'testing ... ')) + output = strip_escseq(status.getvalue()) + assert 'testing ... hello sphinx world \n' in output + assert yields == ['hello', 'sphinx', 'world'] + + # test for status_iterator (verbosity=0) + status.truncate(0) + yields = list(status_iterator(['hello', 'sphinx', 'world'], 'testing ... ', + length=3, verbosity=0)) + output = strip_escseq(status.getvalue()) + assert 'testing ... [ 33%] hello \r' in output + assert 'testing ... [ 66%] sphinx \r' in output + assert 'testing ... [100%] world \r\n' in output + assert yields == ['hello', 'sphinx', 'world'] + + # test for status_iterator (verbosity=1) + status.truncate(0) + yields = list(status_iterator(['hello', 'sphinx', 'world'], 'testing ... ', + length=3, verbosity=1)) + output = strip_escseq(status.getvalue()) + assert 'testing ... [ 33%] hello\n' in output + assert 'testing ... [ 66%] sphinx\n' in output + assert 'testing ... [100%] world\n\n' in output + assert yields == ['hello', 'sphinx', 'world']