2016-11-11 2 views
3

Situation: Ich habe einige benutzerdefinierte Exception-Klassen in meiner Bibliothek, die von Benutzern in ihrem Code verwendet werden. Aus irgendeinem Grund möchte ich eine der Ausnahmen umbenennen und die alte ablehnen.Kann eine Exception-Klasse veraltet werden?

die alte Ausnahme als Alias ​​zu halten, ist nicht schwer:

class MyNewError(ValueError): 
    pass 

MyOldError = MyNewError 

Aber schließlich würde Ich mag die alten Fehler Namen für meine Bibliothek entfernen, und deshalb Nutzer Ich möchte, die in Downstream-Code diese benutzerdefinierte Ausnahme verwenden mit einem DeprecationWarning benachrichtigt werden, dass dieser Fehler entfernt wird.

Aber ich will den DeprecationWarning in der folgenden usecase erhöhen (sagen, dass meine Bibliothek der benutzerdefinierten Ausnahmen enthält mypackage genannt wird):

# downstream user code 
import mypackage 

... 

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

Deshalb mag ich die Warnung erhöhen, wenn der Benutzer zu versucht catch der Fehler, nicht nur, wenn der Benutzer den Fehler auslösen würde.

Ist es möglich, dies in irgendeiner Weise zu tun? (da der Benutzer hier keine Funktion aufruft, in der ich eine Verwarnungswarnung auslösen kann)

+0

Ich glaube nicht, Sie können - Sie vermutlich nicht wollen, eine Warnung jedes Mal, wenn jemand importiert 'mypackage', und ich nicht erhöhen denke, es gibt eine sinnvolle Möglichkeit, '__getitem__' auf Modulebene zu überschreiben (siehe zB http://Stackoverflow.com/q/10438894/3001761 - es ist peinlich). – jonrsharpe

Antwort

2

Es ist sicherlich möglich, innerhalb einer Ausnahme eine Ausnahme auszulösen, aber wenn der Benutzer dies nicht erwartet, kann dies zu Problemen führen. Unter der Annahme, das war nur ein Fehltritt in Ihrem Beitrag, und Sie haben keine DeprecationWarning Ausnahme, die Sie erhöhen möchten, können Sie wie folgt vorgehen:

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 
>>> 
+0

Danke! Dies funktioniert, um einen Fehler zu erzeugen, aber ich möchte auch eine Verwarnungswarnung anzeigen, falls jemand versucht, den Fehler zu fangen (siehe Beispiel Anwendungsfall in meiner Frage) – joris

+0

Aah Ich verstehe was du meinst. Es wäre möglich, dies mit [Metaklassen und magischen Methoden] (https://docs.python.org/3/reference/datamodel.html#customizing-instance-and-subclass-checks) zu tun, aber ich bin mir nicht sicher wie nützlich das wäre. Ich denke in der Zwischenzeit an eine elegante Lösung, aber keine Versprechungen. Jemand könnte mich auch dazu schlagen. –

4

Sie können den Hack/Trick von this answer anwenden, das Umwandeln Ihrer Modul in eine Klasse und Abfangen der __getattr__() Betrieb:

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') 

Ausgang:

$ 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