File | Date | Author | Commit |
---|---|---|---|
_doc | 2023-07-27 | Anthon van der Neut | [dbcec8] allow filtering of subclasses |
.hgtags | 2023-07-27 | Anthon van der Neut | [3a2555] Added tag 0.3.0 for changeset dbcec88e5c4c |
README.rst | 2023-07-26 | Anthon van der Neut | [06fc6a] added icons, update package name |
_README.ryd | 2023-07-26 | Anthon van der Neut | [06fc6a] added icons, update package name |
__init__.py | 2023-07-27 | Anthon van der Neut | [dbcec8] allow filtering of subclasses |
setup.py | 2023-07-27 | Anthon van der Neut | [dbcec8] allow filtering of subclasses |
tox.ini | 2023-07-27 | Anthon van der Neut | [dbcec8] allow filtering of subclasses |
extend warnings.warn with callee parameter
When you have some library, with some deprecated function X, you can use the stacklevel=2 parameter on warnings.warn to show the file-name and line-number of the routine calling X
But if you have some framework that calls the user provided code, either through plug-ins or by explicit registering, the stacklevel doesn't help you to complain about return values that are deprecated.
This library extends warnings.warn with a callee parameter. If this is provided stacklevel should not be provided and the value for callee should be a method or function for which the warning is raised.
The warning will usually be a PendingDeprecationWarning, a DeprecationWarning or a subclass of either.
As an example, if you have two files p0.py and p2.py both with content:
class PlugIn: def status(self): return {'result': 'ok'}
And a file p1.py:
class PlugIn: def status(self): return ['ok'] # this plug-in has been updated
And these files are in a subfolder plug_ins where your framework can find them. Then running:
import sys from pathlib import Path from importlib import import_module import ruamel.std.warnings import warnings class DictReturnPendingDeprecationWarning(PendingDeprecationWarning): pass class Driver: def __init__(self): self.plug_ins = [] def load_plug_ins(self): sys.path.append('plug_ins') for file_name in Path('plug_ins').glob('p*.py'): mod = import_module(file_name.stem) self.plug_ins.append(mod.PlugIn()) def check_status(self): for p in self.plug_ins: retval = p.status() if isinstance(retval, dict): # assume dict warnings.warn( 'callable should return list, not dict', DictReturnPendingDeprecationWarning, callee=p.status, ) else: pass # assume list warnings.simplefilter('once', PendingDeprecationWarning) def doit(): driver = Driver() driver.load_plug_ins() for idx in range(2): driver.check_status() warnings.warn('almost done', PendingDeprecationWarning) doit()
will result in:
/tmp/plug_ins/p0.py:2: DictReturnPendingDeprecationWarning: callable should return list, not dict def status(self): /tmp/plug_ins/p2.py:2: DictReturnPendingDeprecationWarning: callable should return list, not dict def status(self): /tmp/tmp_00.py:40: PendingDeprecationWarning: almost done warnings.warn('almost done', PendingDeprecationWarning)