jenkins-bot has submitted this change and it was merged.
Change subject: Deprecation warning context handler
......................................................................
Deprecation warning context handler
Correctly handle filenames of deprecation warnings occurring
during assertRaises and assertRaisesRegex.
Improve error when deprecation filename does not match expected filename.
Bug: T102365
Change-Id: I23b55f48b0c47c92ee8f5002e81a37f5e8698b57
---
M tests/aspects.py
M tests/dry_site_tests.py
M tests/utils.py
3 files changed, 120 insertions(+), 18 deletions(-)
Approvals:
XZise: Looks good to me, approved
jenkins-bot: Verified
diff --git a/tests/aspects.py b/tests/aspects.py
index 8af9ade..a3f33bf 100644
--- a/tests/aspects.py
+++ b/tests/aspects.py
@@ -7,7 +7,7 @@
mixin to show cache usage is included.
"""
#
-# (C) Pywikibot team, 2014
+# (C) Pywikibot team, 2015
#
# Distributed under the terms of the MIT license.
#
@@ -53,7 +53,10 @@
import tests
from tests import unittest, patch_request, unpatch_request
-from tests.utils import execute_pwb, DrySite, DryRequest, add_metaclass
+from tests.utils import (
+ add_metaclass, execute_pwb, DrySite, DryRequest,
+ WarningSourceSkipContextManager,
+)
class TestCaseBase(unittest.TestCase):
@@ -1303,6 +1306,17 @@
_generic_match = re.compile(r'.* is deprecated(, use .* instead)?\.')
+ skip_list = [
+ unittest.case._AssertRaisesContext,
+ TestCase.assertRaises,
+ TestCase.assertRaisesRegex,
+ TestCase.assertRaisesRegexp,
+ ]
+
+ # Python 3 component in the call stack of _AssertRaisesContext
+ if hasattr(unittest.case, '_AssertRaisesBaseContext'):
+ skip_list.append(unittest.case._AssertRaisesBaseContext)
+
def __init__(self, *args, **kwargs):
super(DeprecationTestCase, self).__init__(*args, **kwargs)
self.warning_log = []
@@ -1312,8 +1326,9 @@
self.expect_warning_filename = self.expect_warning_filename[:-1]
self._do_test_warning_filename = True
+ self._ignore_unknown_warning_packages = False
- self.context_manager = warnings.catch_warnings(record=True)
+ self.context_manager = WarningSourceSkipContextManager(self.skip_list)
def _reset_messages(self):
self._do_test_warning_filename = True
@@ -1352,7 +1367,14 @@
def assertDeprecationFile(self, filename):
for item in self.warning_log:
- self.assertEqual(item.filename, filename)
+ if (self._ignore_unknown_warning_packages and
+ 'pywikibot' not in item.filename):
+ continue
+
+ if item.filename != filename:
+ self.fail(
+ 'expected warning filename %s; warning item: %s'
+ % (filename, item))
def setUp(self):
super(DeprecationTestCase, self).setUp()
@@ -1375,20 +1397,16 @@
For example C{assertEqual} will do first C{assertEqual} and then
C{assertOneDeprecation}.
-
- With C{check_file} it's possible to enable or disable the check whether the
- filename matches the caller's filename. By default it does not match the
- filename if the name starts with 'assertRaises' (as it'll call it inside
the
- assert in that case).
"""
def process_assert(self, assertion, *args, **kwargs):
"""Handle assertion and call C{assertOneDeprecation} after
it."""
super(AutoDeprecationTestCase, self).process_assert(
assertion, *args, **kwargs)
- self._do_test_warning_filename = self.check_file(assertion.__name__)
self.assertOneDeprecation()
- def check_file(self, name):
- """Disable filename check on asserted
exceptions."""
- return not name.startswith('assertRaises')
+ skip_list = DeprecationTestCase.skip_list + [
+ CapturingTestCase.process_assert,
+ CapturingTestCase.patch_assert,
+ process_assert,
+ ]
diff --git a/tests/dry_site_tests.py b/tests/dry_site_tests.py
index 4e28476..b97356d 100644
--- a/tests/dry_site_tests.py
+++ b/tests/dry_site_tests.py
@@ -217,12 +217,9 @@
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.
self.assertRaisesRegex(
NotImplementedError,
diff --git a/tests/utils.py b/tests/utils.py
index 27152da..ecf02c2 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
"""Test utilities."""
#
-# (C) Pywikibot team, 2013-2014
+# (C) Pywikibot team, 2013-2015
#
# Distributed under the terms of the MIT license.
#
from __future__ import print_function, unicode_literals
__version__ = '$Id$'
#
+import inspect
import json
import os
import re
@@ -15,6 +16,7 @@
import sys
import time
import traceback
+import warnings
from collections import Mapping
from warnings import warn
@@ -116,6 +118,91 @@
return gen
+class WarningSourceSkipContextManager(warnings.catch_warnings):
+
+ """
+ Warning context manager that adjusts source of warning.
+
+ The source of the warning will be moved further down the
+ stack to skip a list of objects that have been monkey
+ patched into the call stack.
+ """
+
+ def __init__(self, skip_list):
+ """
+ Constructor.
+
+ @param skip_list: List of objects to be skipped
+ @type skip_list: list of object or (obj, str, int, int)
+ """
+ super(WarningSourceSkipContextManager, self).__init__(record=True)
+ self.skip_list = skip_list
+
+ @property
+ def skip_list(self):
+ """
+ Return list of filename and line ranges to skip.
+
+ @rtype: list of (obj, str, int, int)
+ """
+ return self._skip_list
+
+ @skip_list.setter
+ def skip_list(self, value):
+ """
+ Set list of objects to be skipped.
+
+ @param value: List of objects to be skipped
+ @type value: list of object or (obj, str, int, int)
+ """
+ self._skip_list = []
+ for item in value:
+ if isinstance(item, tuple):
+ self._skip_list.append(item)
+ else:
+ filename = inspect.getsourcefile(item)
+ code, first_line = inspect.getsourcelines(item)
+ last_line = first_line + len(code)
+ self._skip_list.append(
+ (item, filename, first_line, last_line))
+
+ def __enter__(self):
+ """Enter the context manager."""
+ def detailed_show_warning(*args, **kwargs):
+ """warnings.showwarning replacement
handler."""
+ entry = warnings.WarningMessage(*args, **kwargs)
+
+ skip_lines = 0
+ entry_line_found = False
+
+ for (_, filename, fileno, _, line, _) in inspect.stack():
+ if any(start <= fileno <= end
+ for (_, skip_filename, start, end) in self.skip_list
+ if skip_filename == filename):
+ if entry_line_found:
+ continue
+ else:
+ skip_lines += 1
+
+ if (filename, fileno) == (entry.filename, entry.lineno):
+ if not skip_lines:
+ break
+ entry_line_found = True
+
+ if entry_line_found:
+ if not skip_lines:
+ (entry.filename, entry.lineno) = (filename, fileno)
+ break
+ else:
+ skip_lines -= 1
+
+ log.append(entry)
+
+ log = super(WarningSourceSkipContextManager, self).__enter__()
+ self._module.showwarning = detailed_show_warning
+ return log
+
+
class DryParamInfo(dict):
"""Dummy class to use instead of
L{pywikibot.data.api.ParamInfo}."""
--
To view, visit
https://gerrit.wikimedia.org/r/225677
To unsubscribe, visit
https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I23b55f48b0c47c92ee8f5002e81a37f5e8698b57
Gerrit-PatchSet: 5
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 <>