From 36eb0c37e3a5e0c18b832681dd81b55130d21308 Mon Sep 17 00:00:00 2001 From: Nicholas Ohs Date: Sun, 17 Feb 2019 21:27:42 +0100 Subject: [PATCH] Changed dkey to show proper filenames and lines for warnings. --- dkey/_dkey.py | 28 ++++++++++++++++++---------- tests/test_dkey.py | 13 ++++++++++++- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/dkey/_dkey.py b/dkey/_dkey.py index d08f04f..7e2178d 100644 --- a/dkey/_dkey.py +++ b/dkey/_dkey.py @@ -115,7 +115,7 @@ def __ne__(self, other): return super().__ne__(other) - def __getitem__(self, key): + def __getitem__(self, key, **kwargs): """ Get the value of the item of the given key `key`. @@ -143,7 +143,7 @@ def __getitem__(self, key): Warns with the warning stored for the given key if the key is deprecated. """ - self._check_deprecated(key) + self._check_deprecated(key, additional_stacklevel=kwargs.get('stacklevel', 0)) return super().__getitem__(key) @@ -195,7 +195,7 @@ def __delitem__(self, key): Further access to the given key will not spawn additional warnings. """ - self.pop(key) + self.pop(key, stacklevel=1) def __contains__(self, key): """ @@ -297,11 +297,11 @@ def get(self, key, default=None): """ try: - return self.__getitem__(key) + return self.__getitem__(key,stacklevel=1) except KeyError: return default - def pop(self, key, default=_DEFAULT): + def pop(self, key, default=_DEFAULT, **kwargs): """ Remove and return the item with key `key`. @@ -336,7 +336,7 @@ def pop(self, key, default=_DEFAULT): information for this key is removed. """ - if self._check_deprecated(key): + if self._check_deprecated(key, additional_stacklevel=kwargs.get('stacklevel', 0)): del self._key_mappings[key] if default is _DEFAULT: @@ -475,7 +475,7 @@ def __len__(self): return super().__len__() - def _check_deprecated(self, key): + def _check_deprecated(self, key, additional_stacklevel=0): """ Check if the given key is deprecated and warn if it is. @@ -487,6 +487,10 @@ def _check_deprecated(self, key): key The key to look up in the dict of deprecated keys + additional_stacklevel : int, optional + Additional stacklevels to remove to make sure that the shown warning + shows the place of usage and not some internal dkey line. + Returns ------- deprecated : bool @@ -495,14 +499,14 @@ def _check_deprecated(self, key): """ try: mapping = self._key_mappings[key] - self._warn_deprecation(mapping) + self._warn_deprecation(mapping, additional_stacklevel=additional_stacklevel) return True except KeyError: return False @staticmethod - def _warn_deprecation(mapping): + def _warn_deprecation(mapping, additional_stacklevel=0): """ Warn with the given deprecated key mapping. @@ -517,13 +521,17 @@ def _warn_deprecation(mapping): which should be a :any:`str`, and `'warning type'` which needs to be a valid subclass of :any:`Exception`. + additional_stacklevel : int, optional + Additional stacklevels to remove to make sure that the shown warning + shows the place of usage and not some internal dkey line. + Warns ----- CustomWarning Warns with the given message and warning type. """ - _warn(mapping['warning message'], mapping['warning type']) + _warn(mapping['warning message'], mapping['warning type'], stacklevel=4+additional_stacklevel) def dkey(*args, deprecated_in=None, removed_in=None, details=None, warning_type='developer'): diff --git a/tests/test_dkey.py b/tests/test_dkey.py index fa6ae8d..9f4f251 100644 --- a/tests/test_dkey.py +++ b/tests/test_dkey.py @@ -3,6 +3,10 @@ import warnings import unittest from contextlib import contextmanager +import re +from subprocess import check_output, STDOUT, CalledProcessError +import sys +from tempfile import NamedTemporaryFile from dkey import deprecate_keys, dkey @@ -25,8 +29,15 @@ def assertNotWarns(self, warning): else: self.assertTrue(True) + @contextmanager + def assertWarnsHere(self, warning): + with self.assertWarns(warning) as w: + yield + + self.assertIn('test_dkey.py', w.filename) + def get_key_assertions(self): - return ((key, self.assertWarns if self.is_deprecated(key) else self.assertNotWarns) for key in (x[0] for x in self.example_case['items'])) + return ((key, self.assertWarnsHere if self.is_deprecated(key) else self.assertNotWarns) for key in (x[0] for x in self.example_case['items'])) def is_deprecated(self, key): return (key == self.example_case['removed key']) or (key == self.example_case['replaced key'][0])