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')")
pywikibot-commits@lists.wikimedia.org