jenkins-bot has submitted this change and it was merged.
Change subject: Use warnings for deprecation
......................................................................
Use warnings for deprecation
Warnings are now captured by logging, fixing buggy code from compat.
https://www.mediawiki.org/wiki/Special:Code/pywikipedia/11420
Code deprecation is now ignored by default.
Config and command line args deprecation are shown by default.
Also
- fix deprecated config variable available_ssl_project
- lower case of DEPRECATED in redirect_func and DeprecationWrapper
- tool manage_wrapping for decorators
- remove use of locals() in deprecated_args
- use self._logger in ThreadedList debug calls
- intersect_generators debug call moved into ThreadedList
- Ignore import warnings when loading i18n message bundles
And deprecate methods which were too noisy to deprecate previously.
Bug: T72970
Change-Id: I95000736f3fc9ccb80fe32106fb6516abc62cdd6
---
M pywikibot/backports.py
M pywikibot/bot.py
M pywikibot/config2.py
M pywikibot/exceptions.py
M pywikibot/family.py
M pywikibot/i18n.py
M pywikibot/page.py
M pywikibot/pagegenerators.py
M pywikibot/site.py
M pywikibot/tools.py
M pywikibot/userinterfaces/terminal_interface_base.py
M scripts/casechecker.py
M scripts/delete.py
M scripts/states_redirect.py
M tests/__init__.py
M tests/aspects.py
M tests/data_ingestion_tests.py
M tests/deprecation_tests.py
M tests/dry_site_tests.py
M tests/exceptions_tests.py
M tests/family_tests.py
M tests/page_tests.py
M tests/pagegenerators_tests.py
M tests/site_tests.py
M tox.ini
25 files changed, 510 insertions(+), 193 deletions(-)
Approvals:
John Vandenberg: Looks good to me, but someone else must approve
XZise: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/backports.py b/pywikibot/backports.py
index 55712da..f24100d 100644
--- a/pywikibot/backports.py
+++ b/pywikibot/backports.py
@@ -54,10 +54,13 @@
"""
#
# (C) Python Software Foundation, 2001-2014
-# (C) with modifications from Pywikibot team, 2014
+# (C) with modifications from Pywikibot team, 2015
#
# Distributed under the terms of the PSF license.
#
+
+import logging
+import warnings
def format_range_unified(start, stop):
@@ -77,3 +80,86 @@
if not length:
beginning -= 1 # empty ranges begin at line just before the range
return '{0},{1}'.format(beginning, length)
+
+
+# Logging/Warnings integration
+
+_warnings_showwarning = None
+
+
+class NullHandler(logging.Handler):
+
+ """
+ This handler does nothing.
+
+ It's intended to be used to avoid the "No handlers could be found for
+ logger XXX" one-off warning. This is important for library code, which
+ may contain code to log events. If a user of the library does not configure
+ logging, the one-off warning might be produced; to avoid this, the library
+ developer simply needs to instantiate a NullHandler and add it to the
+ top-level logger of the library module or package.
+
+ Copied from C{logging.NullHandler} which was introduced in Python 2.7.
+
+ @see:
http://bugs.python.org/issue4384
+ """
+
+ def handle(self, record):
+ """Dummy handling."""
+ pass
+
+ def emit(self, record):
+ """Dummy handling."""
+ pass
+
+ def createLock(self):
+ """Dummy handling."""
+ self.lock = None
+
+
+def _showwarning(message, category, filename, lineno, file=None, line=None):
+ """
+ Implementation of showwarnings which redirects to logging.
+
+ It will first check to see if the file parameter is None. If a file is
+ specified, it will delegate to the original warnings implementation of
+ showwarning. Otherwise, it will call warnings.formatwarning and will log
+ the resulting string to a warnings logger named "py.warnings" with level
+ logging.WARNING.
+
+ Copied from C{logging._showwarning} which was introduced in Python 2.7.
+
+ @see:
http://bugs.python.org/issue4384
+ """
+ if file is not None:
+ if _warnings_showwarning is not None:
+ _warnings_showwarning(message, category, filename, lineno, file, line)
+ else:
+ s = warnings.formatwarning(message, category, filename, lineno, line)
+ logger = logging.getLogger("py.warnings")
+ if not logger.handlers:
+ logger.addHandler(NullHandler())
+ logger.warning("%s", s)
+
+
+def captureWarnings(capture):
+ """
+ Capture warnings into logging.
+
+ If capture is true, redirect all warnings to the logging package.
+ If capture is False, ensure that warnings are not redirected to logging
+ but to their original destinations.
+
+ Copied from C{logging.captureWarnings} which was introduced in Python 2.7.
+
+ @see:
http://bugs.python.org/issue4384
+ """
+ global _warnings_showwarning
+ if capture:
+ if _warnings_showwarning is None:
+ _warnings_showwarning = warnings.showwarning
+ warnings.showwarning = _showwarning
+ else:
+ if _warnings_showwarning is not None:
+ warnings.showwarning = _warnings_showwarning
+ _warnings_showwarning = None
diff --git a/pywikibot/bot.py b/pywikibot/bot.py
index e814aef..2e670e8 100644
--- a/pywikibot/bot.py
+++ b/pywikibot/bot.py
@@ -11,16 +11,16 @@
# class definition that can be subclassed to create new, functional bot
# scripts, instead of writing each one from scratch.
-
-import logging
-import logging.handlers
-# all output goes thru python std library "logging" module
+# Note: all output goes thru python std library "logging" module
import datetime
import json
+import logging
+import logging.handlers
import os
import re
import sys
+import warnings
import webbrowser
_logger = "bot"
@@ -32,6 +32,8 @@
INPUT = 25
import pywikibot
+
+from pywikibot import backports
from pywikibot import config
from pywikibot import version
from pywikibot.tools import deprecated
@@ -103,6 +105,29 @@
def format(self, record):
"""Strip trailing newlines before outputting text to
file."""
+ # Warnings captured from the warnings system are not processed by
+ # logoutput(), so the 'context' variables are missing.
+ # The same context details are provided by Python 2.7, but need to
+ # be extracted from the warning message for Python 2.6.
+ if record.name == 'py.warnings' and 'caller_file' not in
record.__dict__:
+ assert(len(record.args) == 1)
+ msg = record.args[0]
+
+ if sys.version_info < (2, 7):
+ record.pathname = msg.partition(':')[0]
+ record.lineno = msg.partition(':')[2].partition(':')[0]
+ record.module =
msg.rpartition('/')[2].rpartition('.')[0]
+ else:
+ assert(msg.startswith(record.pathname + ':'))
+
+ record.__dict__['caller_file'] = record.pathname
+ record.__dict__['caller_name'] = record.module
+ record.__dict__['caller_line'] = record.lineno
+
+ # Remove the path and the line number, and strip the extra space
+ msg = msg.partition(':')[2].partition(':')[2].lstrip()
+ record.args = (msg,)
+
text = logging.handlers.RotatingFileHandler.format(self, record)
return text.rstrip("\r\n")
@@ -208,8 +233,20 @@
root_logger = logging.getLogger("pywiki")
root_logger.setLevel(DEBUG + 1) # all records except DEBUG go to logger
- if hasattr(root_logger, 'captureWarnings'):
- root_logger.captureWarnings(True) # introduced in Python >= 2.7
+
+ warnings_logger = logging.getLogger("py.warnings")
+ warnings_logger.setLevel(DEBUG)
+
+ if hasattr(logging, 'captureWarnings'):
+ logging.captureWarnings(True) # introduced in Python >= 2.7
+ else:
+ backports.captureWarnings(True)
+
+ if config.debug_log or 'deprecation' in config.log:
+ warnings.filterwarnings("always")
+ elif config.verbose_output:
+ warnings.filterwarnings("module")
+
root_logger.handlers = [] # remove any old handlers
# configure handler(s) for display to user interface
@@ -243,10 +280,11 @@
debuglogger.setLevel(DEBUG)
debuglogger.addHandler(file_handler)
+ warnings_logger.addHandler(file_handler)
+
_handlers_initialized = True
pywikibot.tools.debug = debug
- pywikibot.tools.warning = warning
writelogheader()
@@ -760,6 +798,7 @@
return nonGlobalArgs
+@deprecated
def handleArgs(*args):
"""DEPRECATED. Use handle_args()."""
return handle_args(args)
diff --git a/pywikibot/config2.py b/pywikibot/config2.py
index 6fea03a..fb68ec1 100644
--- a/pywikibot/config2.py
+++ b/pywikibot/config2.py
@@ -22,9 +22,20 @@
__version__ = '$Id$'
#
+import collections
import os
import sys
-import collections
+
+from warnings import warn
+
+
+class _ConfigurationDeprecationWarning(UserWarning):
+
+ """Feature that is no longer supported."""
+
+ pass
+
+
# Please keep _imported_modules in sync with the imports above
_imported_modules = ('os', 'sys', 'collections')
@@ -40,7 +51,7 @@
_private_values = ['authenticate', 'proxy', 'db_password']
_deprecated_variables = ['use_SSL_onlogin', 'use_SSL_always',
- 'available_ssl_project_comment']
+ 'available_ssl_project']
# ############# ACCOUNT SETTINGS ##############
@@ -863,8 +874,9 @@
globals()[_key] = _uc[_key]
if _key in _deprecated_variables:
- print("WARNING: '%s' is no longer a supported configuration
variable."
- "\nPlease inform the maintainers if you depend on it." % _key)
+ warn("'%s' is no longer a supported configuration variable.\n"
+ "Please inform the maintainers if you depend on it." % _key,
+ _ConfigurationDeprecationWarning)
# Fix up default console_encoding
if console_encoding is None:
diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py
index 2a948c5..4b4161d 100644
--- a/pywikibot/exceptions.py
+++ b/pywikibot/exceptions.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
-Exception classes used throughout the framework.
+Exception and warning classes used throughout the framework.
Error: Base class, all exceptions should the subclass of this class.
- NoUsername: Username is not in user-config.py, or it is invalid.
@@ -45,6 +45,20 @@
- CoordinateGlobeUnknownException: globe is not implemented yet.
- EntityTypeUnknownException: entity type is not available on the site.
+DeprecationWarning: old functionality replaced by new functionality
+
+PendingDeprecationWarning: problematic code which has not yet been
+ fully deprecated, possibly because a replacement is not available
+
+RuntimeWarning: problems developers should have fixed, and users need to
+ be aware of its status.
+ - tools._NotImplementedWarning: do not use
+ - NotImplementedWarning: functionality not implemented
+
+UserWarning: warnings targetted at users
+ - config2._ConfigurationDeprecationWarning: user configuration file problems
+ - ArgumentDeprecationWarning: command line argument problems
+ - FamilyMaintenanceWarning: missing information in family definition
"""
#
# (C) Pywikibot team, 2008
@@ -55,12 +69,33 @@
import sys
-from pywikibot.tools import UnicodeMixin
+from pywikibot.tools import UnicodeMixin, _NotImplementedWarning
if sys.version_info[0] > 2:
unicode = str
+class NotImplementedWarning(_NotImplementedWarning):
+
+ """Feature that is no longer implemented."""
+
+ pass
+
+
+class ArgumentDeprecationWarning(UserWarning):
+
+ """Command line argument that is no longer
supported."""
+
+ pass
+
+
+class FamilyMaintenanceWarning(UserWarning):
+
+ """Family class is missing definitions."""
+
+ pass
+
+
class Error(UnicodeMixin, Exception): # noqa
"""Pywikibot error"""
diff --git a/pywikibot/family.py b/pywikibot/family.py
index 130169f..cd90d87 100644
--- a/pywikibot/family.py
+++ b/pywikibot/family.py
@@ -13,6 +13,7 @@
import re
import collections
import imp
+import warnings
if sys.version_info[0] > 2:
import urllib.parse as urlparse
@@ -20,6 +21,7 @@
import urlparse
import pywikibot
+
from pywikibot import config2 as config
from pywikibot.tools import deprecated, deprecate_arg
from pywikibot.exceptions import UnknownFamily, Error
@@ -875,7 +877,8 @@
try:
# Ignore warnings due to dots in family names.
- import warnings
+ # TODO: use more specific filter, so that family classes can use
+ # RuntimeWarning's while loading.
with warnings.catch_warnings():
warnings.simplefilter("ignore", RuntimeWarning)
myfamily = imp.load_source(fam, config.family_files[fam])
diff --git a/pywikibot/i18n.py b/pywikibot/i18n.py
index ac10b82..bd9b418 100644
--- a/pywikibot/i18n.py
+++ b/pywikibot/i18n.py
@@ -16,9 +16,13 @@
import sys
import re
import locale
+import warnings
+
from pywikibot import Error
from .plural import plural_rules
+
import pywikibot
+
from . import config2 as config
if sys.version_info[0] > 2:
@@ -236,6 +240,18 @@
pass
+def _get_messages_bundle(name):
+ """Load all translation messages for a bundle name."""
+ with warnings.catch_warnings():
+ # Ignore 'missing __init__.py' as import looks at the JSON
+ # directories before loading the python file.
+ warnings.simplefilter("ignore", ImportWarning)
+ transdict = getattr(__import__(messages_package_name,
+ fromlist=[name]),
+ name).msg
+ return transdict
+
+
def _extract_plural(code, message, parameters):
"""Check for the plural variants in message and replace them.
@@ -364,9 +380,7 @@
@type fallback: boolean
"""
package = twtitle.split("-")[0]
- transdict = getattr(__import__(messages_package_name, fromlist=[package]),
- package).msg
-
+ transdict = _get_messages_bundle(package)
code_needed = False
# If a site is given instead of a code, use its language
if hasattr(code, 'code'):
@@ -498,8 +512,7 @@
@param twtitle: The TranslateWiki string title, in <package>-<key>
format
"""
package = twtitle.split("-")[0]
- transdict = getattr(__import__(messages_package_name, fromlist=[package]),
- package).msg
+ transdict = _get_messages_bundle(package)
# If a site is given instead of a code, use its language
if hasattr(code, 'code'):
code = code.code
@@ -513,8 +526,7 @@
@param twtitle: The TranslateWiki string title, in <package>-<key>
format
"""
package = twtitle.split("-")[0]
- transdict = getattr(__import__(messages_package_name, fromlist=[package]),
- package).msg
+ transdict = _get_messages_bundle(package)
return (lang for lang in sorted(transdict.keys()) if lang != 'qqq')
diff --git a/pywikibot/page.py b/pywikibot/page.py
index ef4ac5c..6fa838d 100644
--- a/pywikibot/page.py
+++ b/pywikibot/page.py
@@ -24,6 +24,8 @@
import sys
import unicodedata
+from warnings import warn
+
try:
from collections import OrderedDict
except ImportError:
@@ -51,7 +53,7 @@
)
from pywikibot.tools import (
ComparableMixin, deprecated, deprecate_arg, deprecated_args,
- remove_last_args
+ remove_last_args, _NotImplementedWarning,
)
from pywikibot import textlib
@@ -412,7 +414,7 @@
% (self.site.hostname(),
self.site.scriptpath(),
self.title(asUrl=True),
- (oldid if oldid is not None else self.latestRevision()))
+ (oldid if oldid is not None else self.latest_revision_id))
@property
def latest_revision_id(self):
@@ -421,6 +423,7 @@
self.site.loadrevisions(self)
return self._revid
+ @deprecated('latest_revision_id')
def latestRevision(self):
"""Return the current revision id for this
page."""
return self.latest_revision_id
@@ -581,6 +584,7 @@
else:
return min(x.revid for x in history)
+ @deprecated('previous_revision_id')
def previousRevision(self):
"""
Return the revision id for the previous revision.
@@ -1619,7 +1623,8 @@
else:
undelete_revs = []
if reason is None:
- pywikibot.warning('Not passing a reason for undelete() is
deprecated.')
+ warn('Not passing a reason for undelete() is deprecated.',
+ DeprecationWarning)
pywikibot.output(u'Undeleting %s.' % (self.title(asLink=True)))
reason = pywikibot.input(u'Please enter a reason for the
undeletion:')
self.site.undelete_page(self, reason, undelete_revs)
@@ -1643,7 +1648,7 @@
Defaults to protections is None
@type prompt: bool
"""
- def deprecated(value, arg_name):
+ def process_deprecated_arg(value, arg_name):
# if protections was set and value is None, don't interpret that
# argument. But otherwise warn that the parameter was set
# (even implicit)
@@ -1654,38 +1659,39 @@
value = ""
if value is not None: # empty string is allowed
protections[arg_name] = value
- pywikibot.bot.warning(u'"protections" argument of
'
- 'protect() replaces
"{0}".'.format(arg_name))
+ warn(u'"protections" argument of protect() replaces
"{0}"'
+ .format(arg_name),
+ DeprecationWarning)
else:
if value:
- pywikibot.bot.warning(u'"protections" argument of
'
- 'protect() replaces "{0}"; cannot
'
- 'use both.'.format(arg_name))
+ warn(u'"protections" argument of protect() replaces
"{0}";'
+ u' cannot use both.'.format(arg_name),
+ RuntimeWarning)
# buffer that, because it might get changed
called_using_deprecated_arg = protections is None
if called_using_deprecated_arg:
protections = {}
- deprecated(edit, "edit")
- deprecated(move, "move")
- deprecated(create, "create")
- deprecated(upload, "upload")
+ process_deprecated_arg(edit, "edit")
+ process_deprecated_arg(move, "move")
+ process_deprecated_arg(create, "create")
+ process_deprecated_arg(upload, "upload")
if reason is None:
pywikibot.output(u'Preparing to protection change of %s.'
% (self.title(asLink=True)))
reason = pywikibot.input(u'Please enter a reason for the action:')
if unprotect:
- pywikibot.bot.warning(u'"unprotect" argument of protect() is
'
- 'deprecated')
+ warn(u'"unprotect" argument of protect() is deprecated',
+ DeprecationWarning, 2)
protections = dict(
[(p_type, "") for p_type in self.applicable_protections()])
answer = 'y'
if called_using_deprecated_arg and prompt is None:
prompt = True
if prompt:
- pywikibot.bot.warning(u'"prompt" argument of protect() is
'
- 'deprecated')
+ warn(u'"prompt" argument of protect() is deprecated',
+ DeprecationWarning, 2)
if prompt and not hasattr(self.site, '_noProtectPrompt'):
answer = pywikibot.input_choice(
u'Do you want to change the protection level of %s?'
@@ -1838,12 +1844,14 @@
def removeImage(self, image, put=False, summary=None, safe=True):
"""Old method to remove all instances of an image from
page."""
- pywikibot.warning(u"Page.removeImage() is no longer supported.")
+ warn('Page.removeImage() is no longer supported.',
+ _NotImplementedWarning, 2)
def replaceImage(self, image, replacement=None, put=False, summary=None,
safe=True):
"""Old method to replace all instances of an image with
another."""
- pywikibot.warning(u"Page.replaceImage() is no longer supported.")
+ warn('Page.replaceImage() is no longer supported.',
+ _NotImplementedWarning, 2)
class Page(BasePage):
@@ -3066,7 +3074,8 @@
return self.id
- def latestRevision(self):
+ @property
+ def latest_revision_id(self):
"""
Get the revision identifier for the most recent revision of the entity.
diff --git a/pywikibot/pagegenerators.py b/pywikibot/pagegenerators.py
index 0053ce6..1a1b15a 100644
--- a/pywikibot/pagegenerators.py
+++ b/pywikibot/pagegenerators.py
@@ -28,6 +28,8 @@
import sys
import time
+from warnings import warn
+
import pywikibot
from pywikibot import date, config, i18n
@@ -38,7 +40,8 @@
intersect_generators,
)
from pywikibot.comms import http
-import pywikibot.data.wikidataquery as wdquery
+from pywikibot.data import wikidataquery as wdquery
+from pywikibot.exceptions import ArgumentDeprecationWarning
from pywikibot.site import Namespace
if sys.version_info[0] > 2:
@@ -541,9 +544,10 @@
gen = TextfilePageGenerator(textfilename, site=self.site)
elif arg.startswith('-namespace') or arg.startswith('-ns'):
if isinstance(self._namespaces, frozenset):
- pywikibot.warning('Cannot handle arg %s as namespaces can not '
- 'be altered after a generator is created.'
- % arg)
+ warn('Cannot handle arg %s as namespaces can not '
+ 'be altered after a generator is created.'
+ % arg,
+ ArgumentDeprecationWarning, 2)
return True
value = None
if arg.startswith('-ns:'):
@@ -771,9 +775,10 @@
user = None
else:
user = None
- pywikibot.warning(u'The usage of "{0}" is discouraged. Use
'
- '-logevents "{1}" instead.'.format(
- arg, ','.join((mode, user or '',
str(total)))))
+ warn(u'The usage of "{0}" is deprecated. Use -logevents
'
+ u'"{1}" instead'.format(
+ arg, ','.join((mode, user or '', str(total)))),
+ ArgumentDeprecationWarning, 2)
gen = self._parse_log_events(mode, user, total)
if gen:
diff --git a/pywikibot/site.py b/pywikibot/site.py
index c9e75b9..f86e430 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -18,19 +18,21 @@
import os
import re
import sys
-from collections import Iterable, Container, namedtuple
import threading
import time
import json
import copy
import mimetypes
+from collections import Iterable, Container, namedtuple
+from warnings import warn
+
import pywikibot
import pywikibot.family
from pywikibot.tools import (
- itergroup, deprecated, deprecate_arg, UnicodeMixin, ComparableMixin,
- redirect_func, add_decorated_full_name, deprecated_args, remove_last_args,
- SelfCallDict, SelfCallString, signature,
+ itergroup, UnicodeMixin, ComparableMixin, SelfCallDict, SelfCallString,
+ deprecated, deprecate_arg, deprecated_args, remove_last_args,
+ redirect_func, manage_wrapping,
)
from pywikibot.tools import MediaWikiVersion
from pywikibot.throttle import Throttle
@@ -51,6 +53,7 @@
NoPage,
UnknownSite,
SiteDefinitionError,
+ FamilyMaintenanceWarning,
NoUsername,
SpamfilterError,
NoCreateError,
@@ -585,15 +588,16 @@
# should it just raise an Exception and fail?
# this will help to check the dictionary ...
except KeyError:
- pywikibot.warning(
- u"Site {0} has no language defined in doc_subpages dict
in {1}_family.py file"
- .format(self, self.family.name))
+ warn(u"Site {0} has no language defined in "
+ u"doc_subpages dict in {1}_family.py file"
+ .format(self, self.family.name),
+ FamilyMaintenanceWarning, 2)
# doc_subpages not defined in x_family.py file
except AttributeError:
doc = () # default
- pywikibot.warning(
- u"Site {0} has no doc_subpages dict in {1}_family.py file"
- .format(self, self.family.name))
+ warn(u"Site {0} has no doc_subpages dict in {1}_family.py
file"
+ .format(self, self.family.name),
+ FamilyMaintenanceWarning, 2)
self._doc_subpage = doc
return self._doc_subpage
@@ -1043,13 +1047,8 @@
if not __debug__:
return fn
- callee.__name__ = fn.__name__
- callee.__doc__ = fn.__doc__
- callee.__module__ = callee.__module__
- if not hasattr(fn, '__full_name__'):
- add_decorated_full_name(fn)
- callee.__full_name__ = fn.__full_name__
- callee.__signature__ = signature(fn)
+ manage_wrapping(callee, fn)
+
return callee
return decorator
@@ -1075,13 +1074,7 @@
if not __debug__:
return fn
- callee.__name__ = fn.__name__
- callee.__doc__ = fn.__doc__
- callee.__module__ = fn.__module__
- callee.__signature__ = signature(fn)
- if not hasattr(fn, '__full_name__'):
- add_decorated_full_name(fn)
- callee.__full_name__ = fn.__full_name__
+ manage_wrapping(callee, fn)
return callee
return decorator
diff --git a/pywikibot/tools.py b/pywikibot/tools.py
index 3d45090..6037195 100644
--- a/pywikibot/tools.py
+++ b/pywikibot/tools.py
@@ -15,6 +15,9 @@
import threading
import time
import types
+
+from warnings import warn
+
from distutils.version import Version
if sys.version_info[0] > 2:
@@ -24,11 +27,21 @@
import Queue
-# These variables are functions debug(str) and warning(str)
-# which are initially the builtin print function.
-# They exist here as the deprecators in this module rely only on them.
-# pywikibot updates these function variables in bot.init_handlers()
-debug = warning = print
+def print_debug(msg, *args, **kwargs):
+ """Simple debug routine."""
+ print(msg)
+
+
+# This variable uses the builtin print function.
+# pywikibot updates it to use logging in bot.init_handlers()
+debug = print_debug
+
+
+class _NotImplementedWarning(RuntimeWarning):
+
+ """Feature that is no longer implemented."""
+
+ pass
def empty_iterator():
@@ -83,6 +96,7 @@
def concat_options(message, line_length, options):
+ """Concatenate options."""
indent = len(message) + 2
line_length -= indent
option_msg = u''
@@ -128,6 +142,7 @@
MEDIAWIKI_VERSION =
re.compile(r'^(\d+(?:\.\d+)+)(wmf(\d+)|alpha|beta(\d+)|-?rc\.?(\d+))?$')
def parse(self, vstring):
+ """Parse version string."""
version_match = MediaWikiVersion.MEDIAWIKI_VERSION.match(vstring)
if not version_match:
raise ValueError('Invalid version number
"{0}"'.format(vstring))
@@ -334,15 +349,16 @@
time.sleep(2)
super(ThreadList, self).append(thd)
thd.start()
+ debug("thread started: %r" % thd, self._logger)
def stop_all(self):
"""Stop all threads the pool."""
if self:
- debug(u'EARLY QUIT: Threads: %d' % len(self), ThreadList._logger)
+ debug(u'EARLY QUIT: Threads: %d' % len(self), self._logger)
for thd in self:
thd.stop()
debug(u'EARLY QUIT: Queue size left in %s: %s'
- % (thd, thd.queue.qsize()), ThreadList._logger)
+ % (thd, thd.queue.qsize()), self._logger)
def intersect_generators(genlist):
@@ -361,8 +377,6 @@
@param genlist: list of page generators
@type genlist: list
"""
- _logger = ""
-
# Item is cached to check that it is found n_gen
# times before being yielded.
cache = collections.defaultdict(set)
@@ -375,7 +389,6 @@
for source in genlist:
threaded_gen = ThreadedGenerator(name=repr(source), target=source)
thrlist.append(threaded_gen)
- debug("INTERSECT: thread started: %r" % threaded_gen, _logger)
while True:
# Get items from queues in a round-robin way.
@@ -528,7 +541,7 @@
return None
-def add_decorated_full_name(obj):
+def add_decorated_full_name(obj, stacklevel=1):
"""Extract full object name, including class, and store in
__full_name__.
This must be done on all decorators that are chained together, otherwise
@@ -536,13 +549,15 @@
@param obj: A object being decorated
@type obj: object
+ @param stacklevel: level to use
+ @type stacklevel: int
"""
if hasattr(obj, '__full_name__'):
return
# The current frame is add_decorated_full_name
# The next frame is the decorator
# The next frame is the object being decorated
- frame = inspect.currentframe().f_back.f_back
+ frame = sys._getframe(stacklevel + 1)
class_name = frame.f_code.co_name
if class_name and class_name != '<module>':
obj.__full_name__ = (obj.__module__ + '.' +
@@ -551,6 +566,36 @@
else:
obj.__full_name__ = (obj.__module__ + '.' +
obj.__name__)
+
+
+def manage_wrapping(wrapper, obj):
+ """Add attributes to wrapper and wrapped functions."""
+ wrapper.__doc__ = obj.__doc__
+ wrapper.__name__ = obj.__name__
+ wrapper.__module__ = obj.__module__
+ wrapper.__signature__ = signature(obj)
+
+ if not hasattr(obj, '__full_name__'):
+ add_decorated_full_name(obj, 2)
+ wrapper.__full_name__ = obj.__full_name__
+
+ # Use the previous wrappers depth, if it exists
+ wrapper.__depth__ = getattr(obj, '__depth__', 0) + 1
+
+ # Obtain the wrapped object from the previous wrapper
+ wrapped = getattr(obj, '__wrapped__', obj)
+ wrapper.__wrapped__ = wrapped
+
+ # Increment the number of wrappers
+ if hasattr(wrapped, '__wrappers__'):
+ wrapped.__wrappers__ += 1
+ else:
+ wrapped.__wrappers__ = 1
+
+
+def get_wrapper_depth(wrapper):
+ """Return depth of wrapper function."""
+ return wrapper.__wrapped__.__wrappers__ + (1 - wrapper.__depth__)
def add_full_name(obj):
@@ -641,19 +686,20 @@
@rtype: any
"""
name = obj.__full_name__
+ depth = get_wrapper_depth(wrapper) + 1
if instead:
- warning(u"%s is deprecated, use %s instead." % (name,
instead))
+ warn(u"%s is deprecated, use %s instead." % (name, instead),
+ DeprecationWarning, depth)
else:
- warning(u"%s is deprecated." % (name))
+ warn(u"%s is deprecated." % name,
+ _NotImplementedWarning, depth)
return obj(*args, **kwargs)
if not __debug__:
return obj
- wrapper.__doc__ = obj.__doc__
- wrapper.__name__ = obj.__name__
- wrapper.__module__ = obj.__module__
- wrapper.__signature__ = signature(obj)
+ manage_wrapping(wrapper, obj)
+
return wrapper
without_parameters = len(args) == 1 and len(kwargs) == 0 and callable(args[0])
@@ -688,8 +734,6 @@
None it drops the value and prints a warning. If False it just drops
the value.
"""
- _logger = ""
-
def decorator(obj):
"""Outer wrapper.
@@ -709,33 +753,44 @@
@rtype: any
"""
name = obj.__full_name__
+ depth = get_wrapper_depth(wrapper) + 1
for old_arg, new_arg in arg_pairs.items():
+ output_args = {
+ 'name': name,
+ 'old_arg': old_arg,
+ 'new_arg': new_arg,
+ }
if old_arg in __kw:
if new_arg not in [True, False, None]:
if new_arg in __kw:
- warning(u"%(new_arg)s argument of %(name)s "
- "replaces %(old_arg)s; cannot use both."
- % locals())
+ warn(u"%(new_arg)s argument of %(name)s "
+ u"replaces %(old_arg)s; cannot use both."
+ % output_args,
+ RuntimeWarning, depth)
else:
# If the value is positionally given this will
# cause a TypeError, which is intentional
- warning(u"%(old_arg)s argument of %(name)s "
- "is deprecated; use %(new_arg)s instead."
- % locals())
+ warn(u"%(old_arg)s argument of %(name)s "
+ u"is deprecated; use %(new_arg)s instead."
+ % output_args,
+ DeprecationWarning, depth)
__kw[new_arg] = __kw[old_arg]
- elif new_arg is not False:
- debug(u"%(old_arg)s argument of %(name)s is "
- "deprecated." % locals(), _logger)
+ else:
+ if new_arg is False:
+ cls = PendingDeprecationWarning
+ else:
+ cls = DeprecationWarning
+ warn(u"%(old_arg)s argument of %(name)s is
deprecated."
+ % output_args,
+ cls, depth)
del __kw[old_arg]
return obj(*__args, **__kw)
if not __debug__:
return obj
- wrapper.__doc__ = obj.__doc__
- wrapper.__name__ = obj.__name__
- wrapper.__module__ = obj.__module__
- wrapper.__signature__ = signature(obj)
+ manage_wrapping(wrapper, obj)
+
if wrapper.__signature__:
# Build a new signature with deprecated args added.
params = collections.OrderedDict()
@@ -749,9 +804,7 @@
else NotImplemented)
wrapper.__signature__ = inspect.Signature()
wrapper.__signature__._parameters = params
- if not hasattr(obj, '__full_name__'):
- add_decorated_full_name(obj)
- wrapper.__full_name__ = obj.__full_name__
+
return wrapper
return decorator
@@ -792,6 +845,7 @@
@rtype: any
"""
name = obj.__full_name__
+ depth = get_wrapper_depth(wrapper) + 1
args, varargs, kwargs, _ = inspect.getargspec(wrapper.__wrapped__)
if varargs is not None and kwargs is not None:
raise ValueError(u'{1} may not have * or ** args.'.format(
@@ -807,19 +861,16 @@
if deprecated:
# sort them according to arg_names
deprecated = [arg for arg in arg_names if arg in deprecated]
- warning(u"The trailing arguments ('{0}') of {1} are "
- "deprecated. The value(s) provided for '{2}' have
"
- "been dropped.".format("',
'".join(arg_names), name,
- "',
'".join(deprecated)))
+ warn(u"The trailing arguments ('{0}') of {1} are deprecated.
"
+ u"The value(s) provided for '{2}' have been
dropped.".
+ format("', '".join(arg_names),
+ name,
+ "', '".join(deprecated)),
+ DeprecationWarning, depth)
return obj(*new_args, **new_kwargs)
- wrapper.__doc__ = obj.__doc__
- wrapper.__name__ = obj.__name__
- wrapper.__module__ = obj.__module__
- if not hasattr(obj, '__full_name__'):
- add_decorated_full_name(obj)
- wrapper.__full_name__ = obj.__full_name__
- wrapper.__wrapped__ = getattr(obj, '__wrapped__', obj)
+ manage_wrapping(wrapper, obj)
+
return wrapper
return decorator
@@ -852,7 +903,7 @@
@rtype: callable
"""
def call(*a, **kw):
- warning(warn_message)
+ warn(warn_message, DeprecationWarning, 2)
return target(*a, **kw)
if target_module is None:
target_module = target.__module__
@@ -867,7 +918,7 @@
if class_name:
target_module += class_name + '.'
source_module += class_name + '.'
- warn_message = ('{source}{old} is DEPRECATED, use {target}{new} '
+ warn_message = ('{source}{old} is deprecated, use {target}{new} '
'instead.').format(new=target.__name__,
old=old_name or target.__name__,
target=target_module,
@@ -944,9 +995,9 @@
if not warning_message:
if replacement_name:
- warning_message = u"{0}.{1} is DEPRECATED, use {2} instead."
+ warning_message = u"{0}.{1} is deprecated, use {2} instead."
else:
- warning_message = u"{0}.{1} is DEPRECATED."
+ warning_message = u"{0}.{1} is deprecated."
self._deprecated[name] = replacement_name, replacement, warning_message
@@ -958,9 +1009,9 @@
"""Return the attribute with a deprecation warning if
required."""
if attr in self._deprecated:
warning_message = self._deprecated[attr][2]
- warning(warning_message.format(self._module.__name__,
- attr,
- self._deprecated[attr][0]))
+ warn(warning_message.format(self._module.__name__, attr,
+ self._deprecated[attr][0]),
+ DeprecationWarning, 2)
if self._deprecated[attr][1]:
return self._deprecated[attr][1]
return getattr(self._module, attr)
diff --git a/pywikibot/userinterfaces/terminal_interface_base.py
b/pywikibot/userinterfaces/terminal_interface_base.py
index c367b65..a900870 100755
--- a/pywikibot/userinterfaces/terminal_interface_base.py
+++ b/pywikibot/userinterfaces/terminal_interface_base.py
@@ -103,6 +103,9 @@
TerminalFormatter(fmt="%(levelname)s: %(message)s%(newline)s"))
root_logger.addHandler(warning_handler)
+ warnings_logger = logging.getLogger("py.warnings")
+ warnings_logger.addHandler(warning_handler)
+
def printNonColorized(self, text, targetStream):
"""
Write the text non colorized to the target stream.
@@ -405,6 +408,24 @@
def emit(self, record):
"""Emit the record formatted to the output and return
it."""
+ if record.name == 'py.warnings':
+ # Each warning appears twice
+ # the second time it has a 'message'
+ if 'message' in record.__dict__:
+ return
+
+ # Remove the last line, if it appears to be the warn() call
+ msg = record.args[0]
+ is_useless_source_output = any(
+ s in msg for s in
+ ('warn(', 'exceptions.', 'Warning)',
'Warning,'))
+
+ if is_useless_source_output:
+ record.args = ('\n'.join(record.args[0].splitlines()[0:-1]),)
+
+ if 'newline' not in record.__dict__:
+ record.__dict__['newline'] = '\n'
+
text = self.format(record)
return self.UI.output(text, targetStream=self.stream)
diff --git a/scripts/casechecker.py b/scripts/casechecker.py
index cc9350a..e77e856 100644
--- a/scripts/casechecker.py
+++ b/scripts/casechecker.py
@@ -139,7 +139,7 @@
def __init__(self):
- for arg in pywikibot.handleArgs():
+ for arg in pywikibot.handle_args():
if arg.startswith('-from'):
if arg.startswith('-from:'):
self.apfrom = arg[6:]
diff --git a/scripts/delete.py b/scripts/delete.py
index 870fad3..5bc1904 100644
--- a/scripts/delete.py
+++ b/scripts/delete.py
@@ -33,7 +33,11 @@
__version__ = '$Id$'
#
+from warnings import warn
+
import pywikibot
+
+from pywikibot import exceptions
from pywikibot import i18n, pagegenerators, CurrentPageBot
# This is required for the text that is shown when you run this script
@@ -110,8 +114,8 @@
else:
summary = arg[len('-summary:'):]
elif arg.startswith('-images'):
- pywikibot.output('\n\03{lightred}-image option is deprecated. '
- 'Please use -imageused instead.\03{default}\n')
+ warn('-image option is deprecated. Please use -imageused instead.',
+ exceptions.ArgumentDeprecationWarning)
local_args.append('-imageused' + arg[7:])
elif arg.startswith('-undelete'):
options['undelete'] = True
diff --git a/scripts/states_redirect.py b/scripts/states_redirect.py
index 879df31..745c571 100644
--- a/scripts/states_redirect.py
+++ b/scripts/states_redirect.py
@@ -120,10 +120,16 @@
pl.save(i18n.translate(self.site, msg))
-def main():
- """Process command line arguments and invoke
UsStatesBot."""
- # Process global arguments to determine desired site
- local_args = pywikibot.handleArgs()
+def main(*args):
+ """
+ Process command line arguments and invoke bot.
+
+ If args is an empty list, sys.argv is used.
+
+ @param args: command line arguments
+ @type args: list of unicode
+ """
+ local_args = pywikibot.handle_args(args)
start = None
force = False
diff --git a/tests/__init__.py b/tests/__init__.py
index 1ffcabc..dbc93d9 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -9,6 +9,7 @@
import os
import sys
+import warnings
__all__ = ('httplib2', 'OrderedDict', '_cache_dir',
'TestRequest',
'patch_request', 'unpatch_request')
@@ -204,6 +205,8 @@
cache_misses = 0
cache_hits = 0
+warnings.filterwarnings("always")
+
class TestRequest(CachedRequest):
diff --git a/tests/aspects.py b/tests/aspects.py
index 41f5071..be21cf8 100644
--- a/tests/aspects.py
+++ b/tests/aspects.py
@@ -34,6 +34,7 @@
import os
import sys
import time
+import warnings
import pywikibot
@@ -1165,38 +1166,55 @@
"""Test cases for deprecation function in the tools
module."""
- deprecation_messages = []
+ def __init__(self, *args, **kwargs):
+ super(DeprecationTestCase, self).__init__(*args, **kwargs)
+ self.warning_log = []
- @staticmethod
- def _record_messages(msg, *args, **kwargs):
- DeprecationTestCase.deprecation_messages.append(msg)
+ self.expect_warning_filename = inspect.getfile(self.__class__)
+ if self.expect_warning_filename.endswith((".pyc", ".pyo")):
+ self.expect_warning_filename = self.expect_warning_filename[:-1]
- @staticmethod
- def _reset_messages():
- DeprecationTestCase.deprecation_messages = []
+ self._do_test_warning_filename = True
+
+ self.context_manager = warnings.catch_warnings(record=True)
+
+ def _reset_messages(self):
+ self._do_test_warning_filename = True
+ del self.warning_log[:]
+
+ @property
+ def deprecation_messages(self):
+ messages = [str(item.message) for item in self.warning_log]
+ return messages
def assertDeprecation(self, msg):
- self.assertIn(msg, DeprecationTestCase.deprecation_messages)
+ self.assertIn(msg, self.deprecation_messages)
+ if self._do_test_warning_filename:
+ self.assertDeprecationFile(self.expect_warning_filename)
def assertNoDeprecation(self, msg=None):
if msg:
- self.assertNotIn(msg, DeprecationTestCase.deprecation_messages)
+ self.assertNotIn(msg, self.deprecation_messages)
else:
- self.assertEqual([], DeprecationTestCase.deprecation_messages)
+ self.assertEqual([], self.deprecation_messages)
+
+ def assertDeprecationClass(self, cls):
+ self.assertTrue(all(isinstance(item.message, cls)
+ for item in self.warning_log))
+
+ def assertDeprecationFile(self, filename):
+ for item in self.warning_log:
+ self.assertEqual(item.filename, filename)
def setUp(self):
- self.tools_warning = pywikibot.tools.warning
- self.tools_debug = pywikibot.tools.debug
-
- pywikibot.tools.warning = DeprecationTestCase._record_messages
- pywikibot.tools.debug = DeprecationTestCase._record_messages
-
super(DeprecationTestCase, self).setUp()
- DeprecationTestCase._reset_messages()
+ self.warning_log = self.context_manager.__enter__()
+ warnings.simplefilter("always")
+
+ self._reset_messages()
def tearDown(self):
- pywikibot.tools.warning = self.tools_warning
- pywikibot.tools.debug = self.tools_warning
+ self.context_manager.__exit__()
super(DeprecationTestCase, self).tearDown()
diff --git a/tests/data_ingestion_tests.py b/tests/data_ingestion_tests.py
index 156343e..909889c 100644
--- a/tests/data_ingestion_tests.py
+++ b/tests/data_ingestion_tests.py
@@ -43,8 +43,7 @@
def test_findDuplicateImages(self):
"""Test finding duplicates on Wikimedia
Commons."""
- duplicates = self.obj.findDuplicateImages(
- site=self.get_site('commons'))
+ duplicates = self.obj.findDuplicateImages()
self.assertIn('MP sounds.png', [dup.replace("_", " ")
for dup in duplicates])
def test_getTitle(self):
diff --git a/tests/deprecation_tests.py b/tests/deprecation_tests.py
index 3f15506..699ffcb 100644
--- a/tests/deprecation_tests.py
+++ b/tests/deprecation_tests.py
@@ -160,7 +160,10 @@
@deprecate_arg('bah', 'foo')
@deprecate_arg('bah2', 'foo2')
- def deprecated_instance_method_args(self, foo, foo2):
+ @deprecate_arg('bah3', 'foo3')
+ @deprecate_arg('bah4', 'foo4')
+ def deprecated_instance_method_args(self, foo, foo2, foo3=None, foo4=None):
+ """Method with many decorators to verify wrapping depth
formula."""
self.foo = foo
self.foo2 = foo2
return (foo, foo2)
@@ -229,7 +232,7 @@
self.assertDeprecation(
__name__ + '.deprecated_func is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = deprecated_func(1)
self.assertEqual(rv, 1)
@@ -243,7 +246,7 @@
self.assertDeprecation(
__name__ + '.deprecated_func2 is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = deprecated_func2(1)
self.assertEqual(rv, 1)
@@ -263,21 +266,21 @@
self.assertDeprecation(
__name__ + '.deprecated_func_bad_args is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = deprecated_func_bad_args('a')
self.assertEqual(rv, 'a')
self.assertDeprecation(
__name__ + '.deprecated_func_bad_args is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = deprecated_func_bad_args(1)
self.assertEqual(rv, 1)
self.assertDeprecation(
__name__ + '.deprecated_func_bad_args is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
f = DeprecatedMethodClass()
rv = deprecated_func_bad_args(f)
@@ -294,7 +297,7 @@
self.assertDeprecation(
__name__ + '.DeprecatedMethodClass.instance_method is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.instance_method('a')
self.assertEqual(rv, 'a')
@@ -302,7 +305,7 @@
self.assertDeprecation(
__name__ + '.DeprecatedMethodClass.instance_method is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.instance_method(1)
self.assertEqual(rv, 1)
@@ -326,14 +329,14 @@
self.assertDeprecation(
__name__ + '.DeprecatedMethodClass.class_method is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = DeprecatedMethodClass.class_method('a')
self.assertEqual(rv, 'a')
self.assertDeprecation(
__name__ + '.DeprecatedMethodClass.class_method is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = DeprecatedMethodClass.class_method(1)
self.assertEqual(rv, 1)
@@ -352,7 +355,7 @@
self.assertEqual(rv, 'a')
self.assertDeprecation(__name__ + '.DeprecatedMethodClass.static_method is
deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = DeprecatedMethodClass.static_method(1)
self.assertEqual(rv, 1)
@@ -364,7 +367,7 @@
self.assertEqual(df.__doc__, 'Deprecated class.')
self.assertDeprecation(__name__ + '.DeprecatedClassNoInit is
deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
df = DeprecatedClass()
self.assertEqual(df.foo, None)
@@ -390,7 +393,7 @@
self.assertEqual(rv, 'b')
self.assertDeprecation('bah argument of ' + __name__ + '.' +
func.__name__ + ' is deprecated; use foo instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = func(foo=1)
self.assertEqual(rv, 1)
@@ -402,7 +405,7 @@
func,
'a', bah='b'
)
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
tests(deprecated_func_arg)
tests(deprecated_func_arg2)
@@ -414,23 +417,23 @@
rv = deprecated_func_arg3(foo=1, silent=42)
self.assertEqual(rv, 1)
- self.assertNoDeprecation()
+ self.assertDeprecationClass(PendingDeprecationWarning)
rv = deprecated_func_arg3(2)
self.assertEqual(rv, 2)
- self.assertNoDeprecation()
+ self.assertDeprecationClass(PendingDeprecationWarning)
rv = deprecated_func_arg3(3, loud='3')
self.assertEqual(rv, 3)
self.assertDeprecation('loud argument of ' + __name__ +
'.deprecated_func_arg3 is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = deprecated_func_arg3(4, old='4')
self.assertEqual(rv, 4)
self.assertDeprecation('old argument of ' + __name__ +
'.deprecated_func_arg3 is deprecated.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
def test_function_remove_last_args(self):
"""Test @remove_last_args on functions."""
@@ -616,7 +619,7 @@
'bah argument of %s.DeprecatedMethodClass.deprecated_instance_'
'method_arg is deprecated; use foo instead.' % __name__)
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_arg(foo=1)
self.assertEqual(rv, 1)
@@ -640,7 +643,7 @@
'bah2 argument of ' + __name__ + '.DeprecatedMethodClass.'
'deprecated_instance_method_args is deprecated; use foo2 instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_args(foo='b', bah2='c')
self.assertEqual(rv, ('b', 'c'))
@@ -651,7 +654,7 @@
'bah2 argument of ' + __name__ + '.DeprecatedMethodClass.'
'deprecated_instance_method_args is deprecated; use foo2 instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_args(foo2='c', bah='b')
self.assertEqual(rv, ('b', 'c'))
@@ -662,7 +665,7 @@
'bah2 argument of ' + __name__ + '.DeprecatedMethodClass.'
'deprecated_instance_method_args is deprecated; use foo2 instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_args(foo=1, foo2=2)
self.assertEqual(rv, (1, 2))
@@ -687,7 +690,7 @@
'deprecated_instance_method_args_multi is deprecated; '
'use foo2 instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_args_multi(foo='b', bah2='c')
self.assertEqual(rv, ('b', 'c'))
@@ -700,7 +703,7 @@
'deprecated_instance_method_args_multi is deprecated; '
'use foo2 instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_args_multi(foo2='c', bah='b')
self.assertEqual(rv, ('b', 'c'))
@@ -713,7 +716,7 @@
'deprecated_instance_method_args_multi is deprecated; '
'use foo2 instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_args_multi(foo=1, foo2=2)
self.assertEqual(rv, (1, 2))
@@ -732,7 +735,7 @@
'bah argument of ' + __name__ + '.DeprecatedMethodClass.'
'deprecated_instance_method_and_arg is deprecated; use foo
instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_and_arg(bah='b')
self.assertEqual(rv, 'b')
@@ -743,7 +746,7 @@
'bah argument of ' + __name__ + '.DeprecatedMethodClass.'
'deprecated_instance_method_and_arg is deprecated; use foo
instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_and_arg(foo=1)
self.assertEqual(rv, 1)
@@ -767,7 +770,7 @@
'bah argument of ' + __name__ + '.DeprecatedMethodClass.'
'deprecated_instance_method_and_arg2 is deprecated; use foo
instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_and_arg2(bah='b')
self.assertEqual(rv, 'b')
@@ -778,7 +781,7 @@
'bah argument of ' + __name__ + '.DeprecatedMethodClass.'
'deprecated_instance_method_and_arg2 is deprecated; use foo
instead.')
- DeprecatorTestCase._reset_messages()
+ self._reset_messages()
rv = f.deprecated_instance_method_and_arg2(foo=1)
self.assertEqual(rv, 1)
diff --git a/tests/dry_site_tests.py b/tests/dry_site_tests.py
index 45daa10..5b33602 100644
--- a/tests/dry_site_tests.py
+++ b/tests/dry_site_tests.py
@@ -215,6 +215,10 @@
def test_need_version_fail_with_deprecated(self):
"""Test order of combined version check and deprecation
warning."""
+ # As assertRaisesRegex calls the method, unittest is the module
+ # invoking the method instead of this test module.
+ self._do_test_warning_filename = False
+
# FIXME: The deprecation message should be:
# __name__ + '.TestNeedVersion.deprecated_unavailable_method
# The outermost decorator is the version check, so no deprecation message.
@@ -238,7 +242,7 @@
self.assertDeprecation(
__name__ + '.TestNeedVersion.deprecated_available_method is
deprecated.')
- TestNeedVersion._reset_messages()
+ self._reset_messages()
self.deprecated_available_method2()
self.assertDeprecation(
diff --git a/tests/exceptions_tests.py b/tests/exceptions_tests.py
index f4d03f0..9f3e250 100644
--- a/tests/exceptions_tests.py
+++ b/tests/exceptions_tests.py
@@ -33,7 +33,7 @@
cls = pywikibot.exceptions.UploadWarning
self.assertDeprecation(
- 'pywikibot.exceptions.UploadWarning is DEPRECATED, '
+ 'pywikibot.exceptions.UploadWarning is deprecated, '
'use pywikibot.data.api.UploadWarning instead.')
self._reset_messages()
diff --git a/tests/family_tests.py b/tests/family_tests.py
index 7258cc8..d32a3c4 100644
--- a/tests/family_tests.py
+++ b/tests/family_tests.py
@@ -102,21 +102,28 @@
f = pywikibot.site.Family('osm')
self.assertEqual(f.name, 'osm')
self.assertDeprecation(
- 'pywikibot.site.Family is DEPRECATED, use pywikibot.family.Family.load
instead.')
+ 'pywikibot.site.Family is deprecated, use pywikibot.family.Family.load
instead.')
+
+ # @deprecated warning occurs within redirect_func's call
+ # invoking the method instead of this test module.
+ self._do_test_warning_filename = False
f = pywikibot.site.Family('i18n', fatal=False)
self.assertEqual(f.name, 'i18n')
self.assertDeprecation(
- 'pywikibot.site.Family is DEPRECATED, use pywikibot.family.Family.load
instead.')
+ 'pywikibot.site.Family is deprecated, use pywikibot.family.Family.load
instead.')
self.assertDeprecation('fatal argument of pywikibot.family.Family.load is
deprecated.')
def test_old_site_family_function_invalid(self):
"""Test that an invalid family raised UnknownFamily
exception."""
+ # As assertRaises calls the method, unittest is the module
+ # invoking the method instead of this test module.
+ self._do_test_warning_filename = False
self.assertRaises(UnknownFamily, pywikibot.site.Family, 'unknown',
fatal=False)
self.assertRaises(UnknownFamily, pywikibot.site.Family, 'unknown')
self.assertDeprecation(
- 'pywikibot.site.Family is DEPRECATED, use pywikibot.family.Family.load
instead.')
+ 'pywikibot.site.Family is deprecated, use pywikibot.family.Family.load
instead.')
self.assertDeprecation('fatal argument of pywikibot.family.Family.load is
deprecated.')
diff --git a/tests/page_tests.py b/tests/page_tests.py
index 2aa20b7..f95bd6d 100644
--- a/tests/page_tests.py
+++ b/tests/page_tests.py
@@ -409,7 +409,7 @@
self.assertIsInstance(mainpage.canBeEdited(), bool)
self.assertIsInstance(mainpage.botMayEdit(), bool)
self.assertIsInstance(mainpage.editTime(), pywikibot.Timestamp)
- self.assertIsInstance(mainpage.previousRevision(), int)
+ self.assertIsInstance(mainpage.previous_revision_id, int)
self.assertIsInstance(mainpage.permalink(), basestring)
def test_talk_page(self):
diff --git a/tests/pagegenerators_tests.py b/tests/pagegenerators_tests.py
index 25e0875..8a6166f 100755
--- a/tests/pagegenerators_tests.py
+++ b/tests/pagegenerators_tests.py
@@ -23,6 +23,7 @@
from tests.aspects import (
unittest,
TestCase,
+ DeprecationTestCase,
WikidataTestCase,
DefaultSiteTestCase,
WikimediaDefaultSiteTestCase,
@@ -695,7 +696,8 @@
self.assertNotEqual(pages, pages2)
-class TestLogeventsFactoryGenerator(DefaultSiteTestCase):
+class TestLogeventsFactoryGenerator(DefaultSiteTestCase,
+ DeprecationTestCase):
"""Test GeneratorFactory with
pagegenerators.LogeventsPageGenerator."""
@@ -713,6 +715,8 @@
def test_logevents_default(self):
gf = pagegenerators.GeneratorFactory(site=self.site)
self.assertTrue(gf.handleArg('-newuserslog'))
+ self.assertDeprecation('The usage of "-newuserslog" is
deprecated.'
+ ' Use -logevents "newusers,,500"
instead')
gen = gf.getCombinedGenerator()
pages = set(gen)
self.assertLessEqual(len(pages), 500)
diff --git a/tests/site_tests.py b/tests/site_tests.py
index 1a78021..d8daebf 100644
--- a/tests/site_tests.py
+++ b/tests/site_tests.py
@@ -21,7 +21,7 @@
from pywikibot.data import api
from tests.aspects import (
- unittest, TestCase,
+ unittest, TestCase, DeprecationTestCase,
DefaultSiteTestCase,
WikimediaDefaultSiteTestCase,
WikidataTestCase,
@@ -35,7 +35,7 @@
unicode = str
-class TestSiteObjectDeprecatedFunctions(DefaultSiteTestCase):
+class TestSiteObjectDeprecatedFunctions(DefaultSiteTestCase, DeprecationTestCase):
"""Test cases for Site deprecated methods."""
@@ -63,6 +63,8 @@
"Action '[a-z]+' is not allowed for user .* on .*
wiki.")
else:
self.assertEqual(token, mysite.token(mainpage, ttype))
+ self.assertDeprecation("pywikibot.site.APISite.token is
deprecated"
+ ", use the 'tokens' property
instead.")
class TestBaseSiteProperties(TestCase):
diff --git a/tox.ini b/tox.ini
index 89f3ee8..e2e1451 100644
--- a/tox.ini
+++ b/tox.ini
@@ -55,6 +55,7 @@
pywikibot/pagegenerators.py \
pywikibot/plural.py \
pywikibot/throttle.py \
+ pywikibot/tools.py \
pywikibot/userinterfaces/__init__.py \
pywikibot/userinterfaces/terminal_interface.py \
pywikibot/weblib.py \
--
To view, visit
https://gerrit.wikimedia.org/r/187106
To unsubscribe, visit
https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I95000736f3fc9ccb80fe32106fb6516abc62cdd6
Gerrit-PatchSet: 14
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: John Vandenberg <jayvdb(a)gmail.com>
Gerrit-Reviewer: Ladsgroup <ladsgroup(a)gmail.com>
Gerrit-Reviewer: Merlijn van Deen <valhallasw(a)arctus.nl>
Gerrit-Reviewer: XZise <CommodoreFabianus(a)gmx.de>
Gerrit-Reviewer: jenkins-bot <>