当我创建了一个库,其中的函数或方法将被弃用时,我可以警告用户 使用
warnings.warn(message, PendingDeprecationsWarning)
的事实库。通过设置 stacklevel
参数为 warnings.warn
适当地,我可以让警告引用要弃用的函数的调用位置,这对库的用户来说提供了更多信息。
但是我有一个基于插件的系统,它调用用户提供的插件类的
.status
方法。
status 方法用于返回 dict
,但该方法将被弃用
赞成返回一个list
(这是对实际情况的简化)。
在这种情况下,警告对于提供插件的用户来说没有用:
import os, sys
from pathlib import Path
from importlib import import_module
import warnings
warnings.simplefilter('once', PendingDeprecationWarning)
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('status() should return list, not dict', PendingDeprecationWarning)
else:
pass # assume list
# create some plug-ins
Path('plug_ins/p0.py').write_text("""\
class PlugIn:
def status(self):
return {'result': 'ok'}
""")
Path('plug_ins/p1.py').write_text("""\
class PlugIn:
def status(self):
return ['ok'] # this plug-in has been updated
""")
Path('plug_ins/p2.py').write_text("""\
class PlugIn:
def status(self):
return {'result': 'ok'}
""")
driver = Driver()
driver.load_plug_ins()
for idx in range(2):
driver.check_status()
给出:
/tmp/ryd-115/tmp_00.py:23: PendingDeprecationWarning: status() should return list, not dict
warnings.warn('status() should return list, not dict', PendingDeprecationWarning)
输出中的路径和行号来自定义了驱动程序类的文件。 我想要显示的是定义
status
的文件的实际路径,以及该方法的行号,以及每个需要更新的文件(p0.py
和 p2.py
)一次。
我怎样才能使用普通的Python警告系统(这样我就可以告诉用户使用普通的Python警告系统) 警告过滤以抑制消息)并显示
status
方法的文件名和行
需要更新,而不是显示文件名和代码中警告所在的行?
您必须修改
warnings.formatwarning
以获取警告
您调用,因此您需要定义一个警告以传递给 warnings.warn
。
如果您创建更多具有警告子类的警告,那么所有其他警告都派生自该警告子类,这是有意义的。
消息打印一次,要求它是可散列的,并且 而不是字符串作为
warnings.warn
的第一个参数,您可以
提供一个元组(然后在 formatwarning
中解压):
import os, sys
from pathlib import Path
from importlib import import_module
import warnings
class PlugInPendingDeprecationWarning(PendingDeprecationWarning):
pass
# warn once on all PlugIn deprecations
warnings.simplefilter('once', PlugInPendingDeprecationWarning)
warnings.org_formatwarning = warnings.formatwarning
def my_formatwarning(message, category, filename, lineno, line):
if isinstance(message, PlugInPendingDeprecationWarning):
try:
message, filename, lineno = message.args[0]
except Exception as e: # in case there was no tuple provided
pass
return warnings.org_formatwarning(message, category, filename, lineno, line)
warnings.formatwarning = my_formatwarning
class DictReturnPendingDeprecationWarning(PlugInPendingDeprecationWarning):
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
code = p.status.__func__.__code__
warnings.warn(
('callable should return list, not dict', code.co_filename, code.co_firstlineno),
DictReturnPendingDeprecationWarning,
)
else:
pass # assume list
driver = Driver()
driver.load_plug_ins()
for idx in range(2):
driver.check_status()
给出:
/Users/anthon/src/se/so_q_000/plug_ins/p0.py:2: DictReturnPendingDeprecationWarning: callable should return list, not dict
def status(self):
/Users/anthon/src/se/so_q_000/plug_ins/p2.py:2: DictReturnPendingDeprecationWarning: callable should return list, not dict
def status(self):
您应该能够导入
PlugInPendingDeprecationWarning
(可能将其定义在单独的文件中
并将其导入您的驱动程序代码中),
然后让您的用户这样做:
import warnings
from somefile import PlugInPendingDeprecationWarning
warnings.simplefilter('ignore', PlugInPendingDeprecationWarning)
在他们的插件中,但它当然也会影响所有其他插件。