是否可能弃用一个异常类?

3
情境: 我的库中有一些自定义异常类,用户在他们的代码中使用。出于某种原因,我想重命名其中一个异常并弃用旧的异常。
保留旧异常作为别名并不难:
class MyNewError(ValueError):
    pass

MyOldError = MyNewError

但我最终想要移除我的库中旧的错误名称,因此我希望在下游代码中使用此自定义异常的用户能收到一个DeprecationWarning提醒,告知该错误将被删除。

我想在以下用例中引发DeprecationWarning(假设包含自定义异常的库名为mypackage):

# downstream user code
import mypackage

...

try:
    ....
except mypackage.MyOldError:
    ....

我希望在用户尝试捕获错误时发出警告,而不仅仅是在用户抛出错误时。请问有什么方法可以做到这一点吗?(因为用户此处没有调用函数,所以我无法引发弃用警告)

我认为你不能这样做 - 你可能不想在每次有人导入“mypackage”时都发出警告,而且我认为没有明智的方法可以在模块级别上覆盖“__getitem__”(例如,请参见https://dev59.com/LGPVa4cB1Zd3GeqP5m9t - 这很麻烦)。 - jonrsharpe
2个回答

4
你可以使用 这个答案 中的技巧,将你的模块转换为类并拦截 __getattr__() 操作来实现。

mypackage/__init__.py:

import sys
import warnings

class MyNewError(ValueError):
    pass

MyOldError = MyNewError

def foo():
    print('mypackage.foo(): raising MyNewError')
    raise MyNewError()

class Wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        if name == 'MyOldError':
            warnings.warn('MyOldError is deprecated, use MyNewError')

        return getattr(self.wrapped, name)

sys.modules[__name__] = Wrapper(sys.modules[__name__])

test.py:

import mypackage

try:
    mypackage.foo()
except mypackage.MyOldError:
    print('Caught mypackage.MyOldError')

输出:

$ python test.py 
mypackage.foo(): raising MyNewError
mypackage/__init__.py:18: UserWarning: MyOldError is deprecated, use MyNewError
  warnings.warn('MyOldError is deprecated, please use MyNewError')
Caught mypackage.MyOldError

2

在异常中引发另一个异常是完全可能的,但如果用户没有预料到它,可能会引起问题。假设你的帖子只是一次失误,而你不想引发一个DeprecationWarning异常,那么你可以执行以下操作:

class MyOldError(MyNewError):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        deprecation_warning('Deprecated, please use MyNewError')

>>> raise MyOldError
Deprecated, please use MyNewError
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.MyOldError
>>>

谢谢!这个可以用来引发错误,但是如果有人试图捕获这个错误(请参见我问题中的示例用例),我也想显示一个弃用警告。 - joris
啊,我明白你的意思了。使用元类和魔术方法是可以实现这个功能的(参见https://docs.python.org/3/reference/datamodel.html#customizing-instance-and-subclass-checks),但我不确定这是否有用。我会继续思考优雅的解决方案,但不能保证一定能找到。也许其他人会比我更快地找到解决方法。 - Adam Barnes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接