Xqt has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/630209 )

Change subject: [IMPR] make logging interface consistent
......................................................................

[IMPR] make logging interface consistent

- all interface is like msg. *args, **kwargs and all parameters
  are the same; all parameters of Python logging utility functions
  are supported
- introduce layer argument for all logging methods as proposed
- rename text parameter to msg like in python.logging
- all arguments except msg are keyword arguments, all positional
  arguments are dropped
- cleanup deprecated postitional arguments
- only use decoder if bytes is given with msg
- update frame due to deprecated_args decorators
- only update extra parameter if required but do not override the
  given content
- rename tb in exception with exc_info to be sync with Python
  logging.exception
- all Pywikibot logger output functions are similar to Python
  logging function with few differences:
  - no parameter has to be given with output and stdout; this enables
    to print an empty line previously done with pywikibot.output('')
  - if no parameter is given with exception(), the error message is
    computed from sys.exc_info. If a parameter is given with exception,
    it works like error() function. This is different with Python
    logging exception where 'exc_info' is set to True which prints
    the traceback by default.
- rename output function to info to be in sync with Python logging.info
  but keep the output function for backward compatibility
- update usage of these functions
- update ui tests

Bug: T85620
Change-Id: I17759dd9f2f31ee4efa84f6b390b20998a63f188
---
M pwb.py
M pywikibot/__init__.py
M pywikibot/cosmetic_changes.py
M pywikibot/logging.py
M pywikibot/pagegenerators.py
M scripts/archivebot.py
M scripts/category.py
M scripts/checkimages.py
M scripts/dataextend.py
M scripts/download_dump.py
M scripts/interwiki.py
M scripts/maintenance/cache.py
M scripts/maintenance/colors.py
M scripts/replicate_wiki.py
M scripts/solve_disambiguation.py
M tests/aspects.py
M tests/ui_tests.py
17 files changed, 277 insertions(+), 216 deletions(-)

Approvals:
  jenkins-bot: Verified
  Xqt: Looks good to me, approved



diff --git a/pwb.py b/pwb.py
index 6fddcec..3c46597 100755
--- a/pwb.py
+++ b/pwb.py
@@ -291,7 +291,7 @@

 def find_alternates(filename, script_paths):
     """Search for similar filenames in the given script paths."""
-    from pywikibot import config, input_choice, output
+    from pywikibot import config, input_choice, info
     from pywikibot.bot import QuitKeyboardInterrupt, ShowingListOption

     assert config.pwb_close_matches > 0, \
@@ -323,10 +323,10 @@
     if len(similar_scripts) == 1:
         script = similar_scripts[0]
         wait_time = config.pwb_autostart_waittime
-        output('NOTE: Starting the most similar script '
-               '<<lightyellow>>{}.py<<default>>\n'
-               '      in {} seconds; type CTRL-C to stop.'
-               .format(script, wait_time))
+        info('NOTE: Starting the most similar script '
+             '<<lightyellow>>{}.py<<default>>\n'
+             '      in {} seconds; type CTRL-C to stop.'
+             .format(script, wait_time))
         try:
             sleep(wait_time)  # Wait a bit to let it be cancelled
         except KeyboardInterrupt:
diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index 54780d2..b94b610 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -60,6 +60,7 @@
     debug,
     error,
     exception,
+    info,
     log,
     output,
     stdout,
@@ -78,12 +79,12 @@
     '__version__',
     'Bot', 'calledModuleName', 'Category', 'Claim', 'Coordinate', 'critical',
     'CurrentPageBot', 'debug', 'error', 'exception', 'FilePage', 'handle_args',
-    'html2unicode', 'input', 'input_choice', 'input_yn', 'ItemPage',
-    'LexemeForm', 'LexemePage', 'LexemeSense', 'Link',
-    'log', 'MediaInfo', 'output', 'Page', 'PropertyPage', 'showDiff',
-    'show_help', 'Site', 'SiteLink', 'stdout', 'Timestamp', 'translate', 'ui',
-    'url2unicode', 'User', 'warning', 'WbGeoShape', 'WbMonolingualText',
-    'WbQuantity', 'WbTabularData', 'WbTime', 'WbUnknown', 'WikidataBot',
+    'html2unicode', 'info', 'input', 'input_choice', 'input_yn', 'ItemPage',
+    'LexemeForm', 'LexemePage', 'LexemeSense', 'Link', 'log', 'MediaInfo',
+    'output', 'Page', 'PropertyPage', 'showDiff', 'show_help', 'Site',
+    'SiteLink', 'stdout', 'Timestamp', 'translate', 'ui', 'url2unicode',
+    'User', 'warning', 'WbGeoShape', 'WbMonolingualText', 'WbQuantity',
+    'WbTabularData', 'WbTime', 'WbUnknown', 'WikidataBot',
 )

 # argvu is set by pywikibot.bot when it's imported
diff --git a/pywikibot/cosmetic_changes.py b/pywikibot/cosmetic_changes.py
index 09f12dc..522f153 100644
--- a/pywikibot/cosmetic_changes.py
+++ b/pywikibot/cosmetic_changes.py
@@ -283,7 +283,7 @@
             if self.ignore == CANCEL.METHOD:
                 pywikibot.warning('Unable to perform "{}" on "{}"!'
                                   .format(method.__name__, self.title))
-                pywikibot.exception(e)
+                pywikibot.error(e)
             else:
                 raise
         return text if result is None else result
@@ -302,7 +302,7 @@
             if self.ignore == CANCEL.PAGE:
                 pywikibot.warning('Skipped "{}", because an error occurred.'
                                   .format(self.title))
-                pywikibot.exception(e)
+                pywikibot.error(e)
                 return False
             raise
         else:
diff --git a/pywikibot/logging.py b/pywikibot/logging.py
index 1eaa02a..5399d03 100644
--- a/pywikibot/logging.py
+++ b/pywikibot/logging.py
@@ -1,21 +1,21 @@
 """User output/logging functions.

-Six output functions are defined. Each requires a string argument
+Six output functions are defined. Each requires a ``msg`` argument
 All of these functions generate a message to the log file if
 logging is enabled (`-log` or `-debug` command line arguments).

-The functions :func:`output()`, :func:`stdout()`, :func:`warning()` and
-:func:`error()` all display a message to the user through the logger
-object; the only difference is the priority level, which can be used by
-the application layer to alter the display. The :func:`stdout()`
-function should be used only for data that is the "result" of a script,
-as opposed to information messages to the user.
+The functions :func:`info` (alias :func:`output`), :func:`stdout`,
+:func:`warning` and :func:`error` all display a message to the user
+through the logger object; the only difference is the priority level,
+which can be used by the application layer to alter the display. The
+:func:`stdout` function should be used only for data that is the
+"result" of a script, as opposed to information messages to the user.

-The function :func:`log()` by default does not display a message to the
+The function :func:`log` by default does not display a message to the
 user, but this can be altered by using the `-verbose` command line
 option.

-The function :func:`debug()` only logs its messages, they are never
+The function :func:`debug` only logs its messages, they are never
 displayed on the user console. :func:`debug()` takes a required second
 argument, which is a string indicating the debugging layer.
 """
@@ -30,16 +30,17 @@

 # logging levels
 from logging import CRITICAL, DEBUG, ERROR, INFO, WARNING
-from typing import Any, Optional, Union
+from typing import Any

 from pywikibot.backports import Callable, List
-
+from pywikibot.tools import deprecated_args, issue_deprecation_warning

 STDOUT = 16  #:
 VERBOSE = 18  #:
 INPUT = 25  #:
 """Three additional logging levels which are implemented beside
-`CRITICAL`, `DEBUG`, `ERROR`, `INFO` and `WARNING`.
+:const:`CRITICAL`, :const:`DEBUG`, :const:`ERROR`, :const:`INFO` and
+:const:`WARNING`.

 .. seealso:: :python:`Python Logging Levels<logging.html#logging-levels>`
 """
@@ -65,212 +66,266 @@
     _init_routines[:] = []  # the global variable is used with slice operator


-def logoutput(text: object, decoder: Optional[str] = None,
-              newline: bool = True, _level: int = INFO, _logger: str = '',
+# Note: The frame must be updated if this decorator is removed
+@deprecated_args(text='msg')  # since 7.2
+def logoutput(msg: Any,
+              *args: Any,
+              level: int = INFO,
               **kwargs: Any) -> None:
     """Format output and send to the logging module.

     Helper function used by all the user-output convenience functions.
     It can be used to implement your own high-level output function with
-    a different lgging level.
-    """
-    if _logger:
-        logger = logging.getLogger('pywiki.' + _logger)
-    else:
-        logger = logging.getLogger('pywiki')
+    a different logging level.

+    `msg` can contain special sequences to create colored output. These
+    consist of the color name in angle bracket, e. g. <<lightpurple>>.
+    <<default>> resets the color.
+
+    Other keyword arguments are passed unchanged to the logger; so far,
+    the only argument that is useful is ``exc_info=True``, which causes
+    the log message to include an exception traceback.
+
+    :param msg: The message to be printed.
+    :param args: Not used yet; prevents positinal arguments except `msg`.
+    :param level: The logging level; supported by :func:`logoutput` only.
+    :keyword newline: If newline is True (default), a line feed will be
+        added after printing the msg.
+    :type newline: bool
+    :keyword layer: Suffix of the logger name separated by dot. By
+        default no suffix is used.
+    :type layer: str
+    :keyword decoder: If msg is bytes, this decoder is used to deccode.
+        Default is 'utf-8', fallback is 'iso8859-1'
+    :type decoder: str
+    :param kwargs: For the other keyword arguments refer
+        :python:`Logger.debug()<library/logging.html#logging.Logger.debug>`
+        and :pyhow:`logging-cookbook`
+    """
     # invoke any init routines
     if _init_routines:
         _init()

-    # frame 0 is logoutput() in this module,
-    # frame 1 is the convenience function (output(), etc.)
-    # frame 2 is whatever called the convenience function
-    frame = sys._getframe(2)
+    # cleanup positional args
+    if level == ERROR:
+        keys = ('decoder', 'newline', 'exc_info')
+    elif level == DEBUG:
+        keys = ('layer', 'decoder', 'newline')
+    else:
+        keys = ('decoder', 'newline')
+    for i, arg in enumerate(args):
+        key = keys[i]
+        issue_deprecation_warning(
+            'Positional argument {} ({})'.format(i + 1, arg),
+            'keyword argument "{}={}"'.format(key, arg),
+            since='7.2.0')
+        if key in kwargs:
+            warning('{!r} is given as keyword argument {!r} already; ignoring '
+                    '{!r}'.format(key, arg, kwargs[key]))
+        else:
+            kwargs[key] = arg
 
+    # frame 0 is logoutput() in this module,
+    # frame 1 is the deprecation wrapper of this function
+    # frame 2 is the convenience function (output(), etc.)
+    # frame 3 is the deprecation wrapper the convenience function
+    # frame 4 is whatever called the convenience function
+    newline = kwargs.pop('newline', True)
+    frame = sys._getframe(4)
     module = os.path.basename(frame.f_code.co_filename)
     context = {'caller_name': frame.f_code.co_name,
                'caller_file': module,
                'caller_line': frame.f_lineno,
                'newline': ('\n' if newline else '')}
+    context.update(kwargs.pop('extra', {}))

-    if isinstance(text, str):
-        decoded_text = text
-    elif isinstance(text, bytes):
-        if decoder:
-            decoded_text = text.decode(decoder)
-        else:
-            try:
-                decoded_text = text.decode('utf-8')
-            except UnicodeDecodeError:
-                decoded_text = text.decode('iso8859-1')
-    else:
-        # looks like text is a non-text object.
-        # Maybe it has a __str__ builtin ?
-        # (allows to print Page, Site...)
-        decoded_text = str(text)
+    decoder = kwargs.pop('decoder', 'utf-8')
+    if isinstance(msg, bytes):
+        try:
+            msg = msg.decode(decoder)
+        except UnicodeDecodeError:
+            msg = msg.decode('iso8859-1')

-    logger.log(_level, decoded_text, extra=context, **kwargs)
+    layer = kwargs.pop('layer', '')
+    logger = logging.getLogger(('pywiki.' + layer).strip('.'))
+    logger.log(level, msg, extra=context, **kwargs)


-def output(text: object, decoder: Optional[str] = None, newline: bool = True,
-           **kwargs: Any) -> None:
-    """Output a message to the user via the userinterface.
+# Note: The logoutput frame must be updated if this decorator is removed
+@deprecated_args(text='msg')  # since 7.2
+def info(msg: Any = '', *args: Any, **kwargs: Any) -> None:
+    """Output a message to the user with level :const:`INFO`.

-    Works like print, but uses the encoding used by the user's console
-    (console_encoding in the configuration file) instead of ASCII.
+    ``msg`` will be sent to stderr via :mod:`pywikibot.userinterfaces`.
+    It may be omitted and a newline is printed in that case.
+    The arguments are interpreted as for :func:`logoutput`.

-    If decoder is None, text should be a unicode string. Otherwise it
-    should be encoded in the given encoding.
+    .. versionadded:: 7.2
+       was renamed from :func:`output`.

-    If newline is True, a line feed will be added after printing the text.
-
-    text can contain special sequences to create colored output. These
-    consist of the color name in angle bracket, e. g. <<lightpurple>>.
-    <<default>> resets the color.
-
-    Other keyword arguments are passed unchanged to the logger; so far, the
-    only argument that is useful is "exc_info=True", which causes the
-    log message to include an exception traceback.
+    .. seealso::
+       :python:`Logger.info()<library/logging.html#logging.Logger.info>`
     """
-    logoutput(text, decoder, newline, INFO, **kwargs)
+    logoutput(msg, *args, **kwargs)


-def stdout(text: object, decoder: Optional[str] = None, newline: bool = True,
-           **kwargs: Any) -> None:
-    """Output script results to the user via the userinterface.
+output = info
+"""Synomym for :func:`info` for backward compatibility. The arguments
+are interpreted as for :func:`logoutput`.

-    The text will be sent to standard output, so that it can be piped to
-    another process. All other text will be sent to stderr.
-    See: https://en.wikipedia.org/wiki/Pipeline_%28Unix%29
-
-    :param text: the message printed via stdout logger to the user.
-    :param decoder: If None, text should be a unicode string else it should
-        be encoded in the given encoding.
-    :param newline: If True, a line feed will be added after printing the text.
-    :param kwargs: The keyword arguments can be found in the python doc:
-        :pyhow:`logging-cookbook`
-    """
-    logoutput(text, decoder, newline, STDOUT, **kwargs)
+.. versionchanged:: 7.2
+   was renamed to :func:`info`; `text`was renamed to `msg`; `msg`
+   paramerer may be omitted; only keyword arguments are allowed except
+   for `msg`.
+.. seealso::
+   :python:`Logger.info()<library/logging.html#logging.Logger.info>`
+"""


-def warning(text: object, decoder: Optional[str] = None,
-            newline: bool = True, **kwargs: Any) -> None:
-    """Output a warning message to the user via the userinterface.
+# Note: The logoutput frame must be updated if this decorator is removed
+@deprecated_args(text='msg')  # since 7.2
+def stdout(msg: Any = '', *args: Any, **kwargs: Any) -> None:
+    """Output script results to the user with level :const:`STDOUT`.

-    :param text: the message the user wants to display.
-    :param decoder: If None, text should be a unicode string else it
-        should be encoded in the given encoding.
-    :param newline: If True, a line feed will be added after printing the text.
-    :param kwargs: The keyword arguments can be found in the python doc:
-        :pyhow:`logging-cookbook`
-    """
-    logoutput(text, decoder, newline, WARNING, **kwargs)
+    ``msg`` will be sent to standard output (stdout) via
+    :mod:`pywikibot.userinterfaces`, so that it can be piped to another
+    process. All other functions will sent to stderr.
+    `msg` may be omitted and a newline is printed in that case.

-
-def error(text: object, decoder: Optional[str] = None, newline: bool = True,
-          **kwargs: Any) -> None:
-    """Output an error message to the user via the userinterface.
-
-    :param text: the message containing the error which occurred.
-    :param decoder: If None, text should be a unicode string else it should
-        be encoded in the given encoding.
-    :param newline: If True, a line feed will be added after printing the text.
-    :param kwargs: The keyword arguments can be found in the python doc:
-        :pyhow:`logging-cookbook`
-    """
-    logoutput(text, decoder, newline, ERROR, **kwargs)
-
-
-def log(text: object, decoder: Optional[str] = None, newline: bool = True,
-        **kwargs: Any) -> None:
-    """Output a record to the log file.
-
-    :param text: the message which is to be logged to the log file.
-    :param decoder: If None, text should be a unicode string else it should
-        be encoded in the given encoding.
-    :param newline: If True, a line feed will be added after printing the text.
-    :param kwargs: The keyword arguments can be found in the python doc:
-        :pyhow:`logging-cookbook`
-    """
-    logoutput(text, decoder, newline, VERBOSE, **kwargs)
-
-
-def critical(text: object, decoder: Optional[str] = None, newline: bool = True,
-             **kwargs: Any) -> None:
-    """Output a critical record to the user via the userinterface.
-
-    :param text: the critical message which is to be displayed to the user.
-    :param decoder: If None, text should be a unicode string else it should
-        be encoded in the given encoding.
-    :param newline: If True, a line feed will be added after printing the text.
-    :param kwargs: The keyword arguments can be found in the python doc:
-        :pyhow:`logging-cookbook`
-    """
-    logoutput(text, decoder, newline, CRITICAL, **kwargs)
-
-
-def debug(text: object, layer: str = '', decoder: Optional[str] = None,
-          newline: bool = True, **kwargs: Any) -> None:
-    """Output a debug record to the log file.
+    The arguments are interpreted as for :func:`logoutput`.

     .. versionchanged:: 7.2
-       `layer` parameter is optional.
-
-    :param text: the message of the debug record to be logged to the log file.
-    :param layer: dot-separated logger suffix to record this message
-        upon. If not given only 'pywiki' is used as logger name.
-    :param decoder: If None, text should be a unicode string else it should
-        be encoded in the given encoding.
-    :param newline: If True, a line feed will be added after printing the text.
-    :param kwargs: The keyword arguments can be found in the python doc:
-        :pyhow:`logging-cookbook`
+       `text`was renamed to `msg`; `msg` paramerer may be omitted;
+       only keyword arguments are allowed except for `msg`.
+    .. seealso::
+       - :python:`Logger.log()<library/logging.html#logging.Logger.log>`
+       - https://en.wikipedia.org/wiki/Pipeline_%28Unix%29
     """
-    logoutput(text, decoder, newline, DEBUG, layer, **kwargs)
+    logoutput(msg, *args, level=STDOUT, **kwargs)


-def exception(
-    msg: Union[Exception, str, None] = None,
-    decoder: Optional[str] = None,
-    newline: bool = True,
-    tb: bool = False,
-    **kwargs: Any
-) -> None:
-    """Output an error traceback to the user via the userinterface.
+# Note: The logoutput frame must be updated if this decorator is removed
+@deprecated_args(text='msg')  # since 7.2
+def warning(msg: Any, *args: Any, **kwargs: Any) -> None:
+    """Output a warning message to the user with level :const:`WARNING`.
+
+    ``msg`` will be sent to stderr via :mod:`pywikibot.userinterfaces`.
+    The arguments are interpreted as for :func:`logoutput`.
+
+    .. versionchanged:: 7.2
+       `text`was renamed to `msg`; only keyword arguments are allowed
+       except for `msg`.
+    .. seealso::
+       :python:`Logger.warning()<library/logging.html#logging.Logger.warning>`
+    """
+    logoutput(msg, *args, level=WARNING, **kwargs)
+
+
+# Note: The logoutput frame must be updated if this decorator is removed
+@deprecated_args(text='msg')  # since 7.2
+def error(msg: Any, *args: Any, **kwargs: Any) -> None:
+    """Output an error message to the user with level :const:`ERROR`.
+
+    ``msg`` will be sent to stderr via :mod:`pywikibot.userinterfaces`.
+    The arguments are interpreted as for :func:`logoutput`.
+
+    .. versionchanged:: 7.2
+       `text`was renamed to `msg`; only keyword arguments are allowed
+       except for `msg`.
+    .. seealso::
+       :python:`Logger.error()<library/logging.html#logging.Logger.error>`
+    """
+    logoutput(msg, *args, level=ERROR, **kwargs)
+
+
+# Note: The logoutput frame must be updated if this decorator is removed
+@deprecated_args(text='msg')  # since 7.2
+def log(msg: Any, *args: Any, **kwargs: Any) -> None:
+    """Output a record to the log file with level :const:`VERBOSE`.
+
+    The arguments are interpreted as for :func:`logoutput`.
+
+    .. versionchanged:: 7.2
+       `text`was renamed to `msg`; only keyword arguments are allowed
+       except for `msg`.
+    .. seealso::
+       :python:`Logger.log()<library/logging.html#logging.Logger.log>`
+    """
+    logoutput(msg, *args, level=VERBOSE, **kwargs)
+
+
+# Note: The logoutput frame must be updated if this decorator is removed
+@deprecated_args(text='msg')  # since 7.2
+def critical(msg: Any, *args: Any, **kwargs: Any) -> None:
+    """Output a critical record to the user with level :const:`CRITICAL`.
+
+    ``msg`` will be sent to stderr via :mod:`pywikibot.userinterfaces`.
+    The arguments are interpreted as for :func:`logoutput`.
+
+    .. versionchanged:: 7.2
+       `text`was renamed to `msg`; only keyword arguments are allowed
+       except for `msg`.
+    .. seealso::
+       :python:`Logger.critical()
+       <library/logging.html#logging.Logger.critical>`
+    """
+    logoutput(msg, *args, level=CRITICAL, **kwargs)
+
+
+# Note: The logoutput frame must be updated if this decorator is removed
+@deprecated_args(text='msg')  # since 7.2
+def debug(msg: Any, *args: Any, **kwargs: Any) -> None:
+    """Output a debug record to the log file with level :const:`DEBUG`.
+
+    The arguments are interpreted as for :func:`logoutput`.
+
+    .. versionchanged:: 7.2
+       `layer` parameter is optional; `text`was renamed to `msg`;
+       only keyword arguments are allowed except for `msg`.
+    .. seealso::
+       :python:`Logger.debug()<library/logging.html#logging.Logger.debug>`
+    """
+    logoutput(msg, *args, level=DEBUG, **kwargs)
+
+
+# Note: The logoutput frame must be updated if this decorator is removed
+@deprecated_args(tb='exc_info')  # since 7.2
+def exception(msg: Any = None, *args: Any, **kwargs: Any) -> None:
+    """Output an error traceback to the user with level :const:`ERROR`.

     Use directly after an 'except' statement::

         ...
         except Exception:
-            pywikibot.exception()
+            pywikibot.exception('exc_info'=True)
         ...

     or alternatively::

         ...
         except Exception as e:
-            pywikibot.exception(e)
+            pywikibot.exception(e, 'exc_info'=True)
         ...

+    Without `exc_info` parameter this function works like :func:`error`
+    except that the `msg` parameter may be omitted.
     This function should only be called from an Exception handler.
+    ``msg`` will be sent to stderr via :mod:`pywikibot.userinterfaces`.
+    The arguments are interpreted as for :func:`logoutput`.

-    :param msg: If not None, contains the description of the exception
-        that occurred.
-    :param decoder: If None, text should be a unicode string else it should
-        be encoded in the given encoding.
-    :param newline: If True, a line feed will be added after printing the text.
-    :param kwargs: The keyword arguments can be found in the python doc:
-        :pyhow:`logging-cookbook`
-    :param tb: Set to True in order to output traceback also.
+    .. versionchanged:: 7.2
+       only keyword arguments are allowed except for `msg`;
+       `exc_info` keyword is to be used instead of `tb`.
+    .. seealso::
+       :python:`Logger.exception()
+       <library/logging.html#logging.Logger.exception>`
+
+    The arguments are interpreted as for :meth:`output`.
     """
-    if isinstance(msg, BaseException):
-        if tb:
-            kwargs['exc_info'] = 1
-    else:
-        exc_info = sys.exc_info()
-        msg = '{}: {}'.format(repr(exc_info[1]).split('(')[0],
-                              str(exc_info[1]).strip())
-        if tb:
-            kwargs['exc_info'] = exc_info
+    if msg is None:
+        exc_type, value, _tb = sys.exc_info()
+        msg = str(value)
+        if not kwargs.get('exc_info', False):
+            msg += ' ({})'.format(exc_type.__name__)
     assert msg is not None
-    logoutput(msg, decoder, newline, ERROR, **kwargs)
+    error(msg, *args, **kwargs)
diff --git a/pywikibot/pagegenerators.py b/pywikibot/pagegenerators.py
index 50160b1..796cecf 100644
--- a/pywikibot/pagegenerators.py
+++ b/pywikibot/pagegenerators.py
@@ -1422,7 +1422,7 @@
             pywikibot.warning('LogeventsPageGenerator: '
                               'failed to load page for {!r}; skipping'
                               .format(entry.data))
-            pywikibot.exception(e)
+            pywikibot.error(e)


 def NewpagesPageGenerator(site: OPT_SITE_TYPE = None,
@@ -1752,7 +1752,7 @@
         namespaces = site.namespaces.resolve(namespaces)
     except KeyError as e:
         pywikibot.log('Failed resolving namespaces:')
-        pywikibot.exception(e)
+        pywikibot.error(e)
         raise

     return (page for page in generator if page.namespace() in namespaces)
@@ -2707,7 +2707,7 @@
         try:
             import google
         except ImportError:
-            pywikibot.error('ERROR: generator GoogleSearchPageGenerator '
+            pywikibot.error('generator GoogleSearchPageGenerator '
                             "depends on package 'google'.\n"
                             'To install, please run: pip install google.')
             sys.exit(1)
diff --git a/scripts/archivebot.py b/scripts/archivebot.py
index 13d362d..1ba136b 100755
--- a/scripts/archivebot.py
+++ b/scripts/archivebot.py
@@ -910,9 +910,8 @@
                 pywikibot.error('Missing or malformed template in page {}: {}'
                                 .format(pg, e))
             except Exception:
-                pywikibot.error('Error occurred while processing page {}'
-                                .format(pg))
-                pywikibot.exception(tb=True)
+                pywikibot.exception('Error occurred while processing page {}'
+                                    .format(pg), exc_info=True)


 if __name__ == '__main__':
diff --git a/scripts/category.py b/scripts/category.py
index 0119536..5a7e12b 100755
--- a/scripts/category.py
+++ b/scripts/category.py
@@ -1114,8 +1114,7 @@
                     pywikibot.output(line)

         # show the title of the page where the link was found.
-        pywikibot.output('')
-        pywikibot.output('>>> <<lightpurple>>{}<<default>> <<<'
+        pywikibot.output('\n>>> <<lightpurple>>{}<<default>> <<<'
                          .format(member.title()))

         # determine a reasonable amount of context.
@@ -1151,8 +1150,7 @@
                             key=methodcaller('title'))

         # show categories as possible choices with numbers
-        pywikibot.output('')
-
+        pywikibot.output()
         supercat_option = CatIntegerOption(0, len(supercatlist), 'u')
         if not supercatlist:
             pywikibot.output('This category has no supercategories.')
@@ -1171,7 +1169,7 @@
             subcat_option.list_categories(cat_list)

         # show possible options for the user
-        pywikibot.output('')
+        pywikibot.output()
         options = (supercat_option,
                    subcat_option,
                    StandardOption(
@@ -1248,7 +1246,7 @@

     def treat(self, page) -> None:
         """Process page."""
-        pywikibot.output('')
+        pywikibot.output()
         self.move_to_category(page, self.cat, self.cat)


@@ -1343,7 +1341,7 @@
         cat = pywikibot.Category(self.site, self.cat_title)
         pywikibot.output('Generating tree...', newline=False)
         tree = self.treeview(cat)
-        pywikibot.output('')
+        pywikibot.output()
         if self.filename:
             pywikibot.output('Saving results in ' + self.filename)
             with codecs.open(self.filename, 'a', 'utf-8') as f:
diff --git a/scripts/checkimages.py b/scripts/checkimages.py
index a929f54..0c9430e 100755
--- a/scripts/checkimages.py
+++ b/scripts/checkimages.py
@@ -745,7 +745,7 @@
                 self.num_notify[self.talk_page.title()] -= 1
             err = None
         if err:
-            pywikibot.exception(err)
+            pywikibot.error(err)
             pywikibot.output('Skipping saving talk page {}'
                              .format(self.talk_page))

@@ -1351,9 +1351,9 @@
             pywikibot.output('Skipping {}...'.format(self.image_name))
             self.skip_list.append(self.image_name)
             if skip_number == 1:
-                pywikibot.output('')
+                pywikibot.output()
             return True
-        pywikibot.output('')
+        pywikibot.output()
         return False

     @staticmethod
diff --git a/scripts/dataextend.py b/scripts/dataextend.py
index f8ee391..5f916df 100644
--- a/scripts/dataextend.py
+++ b/scripts/dataextend.py
@@ -1290,7 +1290,7 @@
         if not self.url and not self.sparqlquery:
             return
         newclaims = []
-        pywikibot.output('')
+        pywikibot.output()
         pagerequest = None
         if not self.skipfirst:
             try:
@@ -1638,7 +1638,7 @@
                 if result:
                     newclaims.append((prop, result, self))

-        pywikibot.output('')
+        pywikibot.output()
         for (function, prop) in [
             (self.findcoords, 'coordinates'),
         ]:
diff --git a/scripts/download_dump.py b/scripts/download_dump.py
index 66626d8..4a7b6cf 100755
--- a/scripts/download_dump.py
+++ b/scripts/download_dump.py
@@ -133,7 +133,7 @@
                         parts = 50
                         display_string = ''

-                        pywikibot.output('')
+                        pywikibot.output()
                         for data in response.iter_content(100 * 1024):
                             result_file.write(data)

@@ -156,7 +156,7 @@
                                 - len(display_string.rstrip()))

                             pywikibot.output(display_string, newline=False)
-                        pywikibot.output('')
+                        pywikibot.output()

                 # Rename the temporary file to the target file
                 # if the download completes successfully
diff --git a/scripts/interwiki.py b/scripts/interwiki.py
index 9eb1dfd..d6473b0 100755
--- a/scripts/interwiki.py
+++ b/scripts/interwiki.py
@@ -1823,7 +1823,7 @@
                                                   page, linkedPage))

         except OSError:
-            pywikibot.output('ERROR: could not report backlinks')
+            pywikibot.error('could not report backlinks')


 class InterwikiBot:
diff --git a/scripts/maintenance/cache.py b/scripts/maintenance/cache.py
index 512a818..cb24d54 100755
--- a/scripts/maintenance/cache.py
+++ b/scripts/maintenance/cache.py
@@ -268,7 +268,7 @@
         except ValueError as e:
             pywikibot.error('Failed loading {}'.format(
                 entry._cachefile_path()))
-            pywikibot.exception(e, tb=True)
+            pywikibot.exception(e, exc_info=True)
             continue

         if use_accesstime is None:
@@ -294,7 +294,7 @@
             pywikibot.error('Problems loading {} with key {}, {!r}'
                             .format(entry.filename, entry.key,
                                     entry._parsed_key))
-            pywikibot.exception(e, tb=True)
+            pywikibot.exception(e, exc_info=True)
             continue

         if func is None or func(entry):
diff --git a/scripts/maintenance/colors.py b/scripts/maintenance/colors.py
index 9a0434d..2f4543b 100755
--- a/scripts/maintenance/colors.py
+++ b/scripts/maintenance/colors.py
@@ -37,7 +37,7 @@
             line = '{} {}'.format(bg_col.ljust(max_len_bc_color), line)
             pywikibot.output(line)

-        pywikibot.output('')
+        pywikibot.output()


 if __name__ == '__main__':
diff --git a/scripts/replicate_wiki.py b/scripts/replicate_wiki.py
index f4e2478..18735fa 100755
--- a/scripts/replicate_wiki.py
+++ b/scripts/replicate_wiki.py
@@ -94,7 +94,7 @@
         for s in self.sites:
             s.login()
             pywikibot.output(str(s), newline=False)
-        pywikibot.output('')
+        pywikibot.output()

     def check_sysops(self) -> None:
         """Check if sysops are the same on all wikis."""
@@ -145,9 +145,8 @@
                     pywikibot.output('Bizarre NoPageError that we are '
                                      'just going to ignore')
                 except IsRedirectPageError:
-                    pywikibot.output(
-                        'error: Redirectpage - todo: handle gracefully')
-        pywikibot.output('')
+                    pywikibot.error('Redirectpage - todo: handle gracefully')
+        pywikibot.output()

     def generate_overviews(self) -> None:
         """Create page on wikis with overview of bot results."""
diff --git a/scripts/solve_disambiguation.py b/scripts/solve_disambiguation.py
index 63e3e2e..5e3d8a4 100755
--- a/scripts/solve_disambiguation.py
+++ b/scripts/solve_disambiguation.py
@@ -1084,7 +1084,7 @@
             else:
                 pywikibot.output('\nThe following changes have been made:\n')
                 pywikibot.showDiff(original_text, text)
-                pywikibot.output('')
+                pywikibot.output()
                 # save the page
                 self.setSummaryMessage(disamb_page, new_targets,
                                        unlink_counter, dn)
diff --git a/tests/aspects.py b/tests/aspects.py
index c93da45..b2b8af8 100644
--- a/tests/aspects.py
+++ b/tests/aspects.py
@@ -467,9 +467,9 @@
                         'HTTP status: {} - {}'.format(
                             r.status_code, HTTPStatus(r.status_code).phrase))
             except Exception as e:
-                pywikibot.error('{}: accessing {} caused exception:'
-                                .format(cls.__name__, hostname))
-                pywikibot.exception(e, tb=True)
+                pywikibot.exception('{}: accessing {} caused exception:'
+                                    .format(cls.__name__, hostname),
+                                    exc_info=True)

                 cls._checked_hostnames[hostname] = e
                 raise unittest.SkipTest(
diff --git a/tests/ui_tests.py b/tests/ui_tests.py
index cab4146..6df6054 100755
--- a/tests/ui_tests.py
+++ b/tests/ui_tests.py
@@ -160,17 +160,26 @@
             pywikibot.exception('exception')
         self.assertEqual(self.strout.getvalue(), '')
         self.assertEqual(self.strerr.getvalue(),
-                         'ERROR: TestExceptionError: Testing Exception\n')
+                         'ERROR: exception\n')
+
+    def test_exception_empty(self):
+        try:
+            raise TestExceptionError('Testing Exception')
+        except TestExceptionError:
+            pywikibot.exception()
+        self.assertEqual(self.strout.getvalue(), '')
+        self.assertEqual(self.strerr.getvalue(),
+                         'ERROR: Testing Exception (TestExceptionError)\n')

     def test_exception_tb(self):
         try:
             raise TestExceptionError('Testing Exception')
         except TestExceptionError:
-            pywikibot.exception('exception', tb=True)
+            pywikibot.exception(exc_info=True)
         self.assertEqual(self.strout.getvalue(), '')
         stderrlines = self.strerr.getvalue().split('\n')
         self.assertEqual(stderrlines[0],
-                         'ERROR: TestExceptionError: Testing Exception')
+                         'ERROR: Testing Exception')
         self.assertEqual(stderrlines[1], 'Traceback (most recent call last):')
         self.assertEqual(stderrlines[3],
                          "    raise TestExceptionError('Testing Exception')")

--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/630209
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.wikimedia.org/r/settings

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: I17759dd9f2f31ee4efa84f6b390b20998a63f188
Gerrit-Change-Number: 630209
Gerrit-PatchSet: 13
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: JJMC89 <[email protected]>
Gerrit-Reviewer: Merlijn van Deen <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to