Stefano Borini
Necesito marcar las rutinas como obsoletas, pero aparentemente no hay un decorador de biblioteca estándar para la obsolescencia. Conozco las recetas y el módulo de advertencias, pero mi pregunta es: ¿por qué no hay un decorador de biblioteca estándar para esta tarea (común)?
Pregunta adicional: ¿hay decoradores estándar en la biblioteca estándar?
patricio bertoni
Aquí hay un fragmento, modificado de los citados por Leandro:
import warnings
import functools
def deprecated(func):
"""This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used."""
@functools.wraps(func)
def new_func(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning) # turn off filter
warnings.warn("Call to deprecated function {}.".format(func.__name__),
category=DeprecationWarning,
stacklevel=2)
warnings.simplefilter('default', DeprecationWarning) # reset filter
return func(*args, **kwargs)
return new_func
# Examples
@deprecated
def some_old_function(x, y):
return x + y
class SomeClass:
@deprecated
def some_old_method(self, x, y):
return x + y
Porque en algunos intérpretes la primera solución expuesta (sin manejo de filtro) puede resultar en una supresión de advertencia.
-
¿Por qué no usar?
functools.wraps
en lugar de establecer el nombre y el documento de esa manera?– Maximiliano
06/08/2015 a las 14:40
-
@Maximilian: editado para agregar eso, para evitar que futuros copiadores de este código también lo hagan mal
–Eric
28 de junio de 2016 a las 3:59
-
No me gustan los efectos secundarios (encender/apagar el filtro). No es el trabajo del decorador decidir esto.
– Kentzo
18 de abril de 2017 a las 17:48
-
no responde la pregunta real.
– Catskul
2 mayo 2019 a las 16:41
-
Totalmente de acuerdo con @Kentzo: deshabilitar los filtros y luego restablecerlos a los valores predeterminados le dará a algunos desarrolladores un dolor de cabeza increíble
– Aarón V
7 dic 2020 a las 21:34
laurent laporte
Aquí hay otra solución:
Este decorador (de hecho, una fábrica de decoradores) le permite dar una razón mensaje. También es más útil ayudar al desarrollador a diagnosticar el problema proporcionando la fuente Nombre del archivo y número de línea.
EDITAR: Este código usa la recomendación de Zero: reemplaza warnings.warn_explicit
línea por warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
, que imprime el sitio de llamada de función en lugar del sitio de definición de función. Facilita la depuración.
EDIT2: Esta versión permite al desarrollador especificar un mensaje de “razón” opcional.
import functools
import inspect
import warnings
string_types = (type(b''), type(u''))
def deprecated(reason):
"""
This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used.
"""
if isinstance(reason, string_types):
# The @deprecated is used with a 'reason'.
#
# .. code-block:: python
#
# @deprecated("please, use another function")
# def old_function(x, y):
# pass
def decorator(func1):
if inspect.isclass(func1):
fmt1 = "Call to deprecated class {name} ({reason})."
else:
fmt1 = "Call to deprecated function {name} ({reason})."
@functools.wraps(func1)
def new_func1(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning)
warnings.warn(
fmt1.format(name=func1.__name__, reason=reason),
category=DeprecationWarning,
stacklevel=2
)
warnings.simplefilter('default', DeprecationWarning)
return func1(*args, **kwargs)
return new_func1
return decorator
elif inspect.isclass(reason) or inspect.isfunction(reason):
# The @deprecated is used without any 'reason'.
#
# .. code-block:: python
#
# @deprecated
# def old_function(x, y):
# pass
func2 = reason
if inspect.isclass(func2):
fmt2 = "Call to deprecated class {name}."
else:
fmt2 = "Call to deprecated function {name}."
@functools.wraps(func2)
def new_func2(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning)
warnings.warn(
fmt2.format(name=func2.__name__),
category=DeprecationWarning,
stacklevel=2
)
warnings.simplefilter('default', DeprecationWarning)
return func2(*args, **kwargs)
return new_func2
else:
raise TypeError(repr(type(reason)))
Puedes usar este decorador para funciones, métodos y clases.
Aquí hay un ejemplo simple:
@deprecated("use another function")
def some_old_function(x, y):
return x + y
class SomeClass(object):
@deprecated("use another method")
def some_old_method(self, x, y):
return x + y
@deprecated("use another class")
class SomeOldClass(object):
pass
some_old_function(5, 3)
SomeClass().some_old_method(8, 9)
SomeOldClass()
Obtendrás:
deprecated_example.py:59: DeprecationWarning: Call to deprecated function or method some_old_function (use another function).
some_old_function(5, 3)
deprecated_example.py:60: DeprecationWarning: Call to deprecated function or method some_old_method (use another method).
SomeClass().some_old_method(8, 9)
deprecated_example.py:61: DeprecationWarning: Call to deprecated class SomeOldClass (use another class).
SomeOldClass()
EDIT3: Este decorador ahora es parte de la biblioteca en desuso:
Nueva versión estable v1.2.13 🎉
-
Funciona, bien, prefiero reemplazar el
warn_explicit
linea conwarnings.warn(msg, category=DeprecationWarning, stacklevel=2)
que imprime el sitio de llamada de función en lugar del sitio de definición de función. Facilita la depuración.– Cero
28 de noviembre de 2016 a las 23:24
-
Hola, me gustaría usar su fragmento de código en una biblioteca con licencia GPLv3. ¿Estaría dispuesto a volver a licenciar su código bajo GPLv3? o cualquier licencia más permisivapara que pueda hacerlo legalmente?
– Gerrit
7 julio 2017 a las 17:19
-
@LaurentLAPORTE Lo sé. CC-BY-SO no permite el uso dentro de GPLv3 (debido al bit compartido), por lo que le pregunto si estaría dispuesto a publicar este código específicamente bajo una licencia compatible con GPL. Si no, está bien y no usaré tu código.
– Gerrit
9 de julio de 2017 a las 14:27
-
no responde la pregunta real.
– Catskul
2 mayo 2019 a las 16:42
-
@DannyVarod Lo sé, pero para el código, CC-BY-SA es incluso más restrictivo que GPL. Cuando hice la pregunta, estaba trabajando en una biblioteca GPL. Las bibliotecas GPL pueden usar código GPL o código más permisivo, pero las bibliotecas GPL pueden no use el código CC-BY-SA, por lo que no pude usar este fragmento de código. (Y CC-BY-SA nunca se usó para el código de todos modos; SO haría bien en otorgar licencias de fragmentos de código en las contribuciones de los usuarios bajo algo más permisivo, porque tal como está ahora, la mayoría de los usuarios no pueden usar fragmentos de código que encuentran en SO)
– Gerrit
9 mayo 2021 a las 20:23
Stevoisiak
Como sugirió muon, puede instalar el deprecation
paquete para esto.
los
deprecation
biblioteca proporciona undeprecated
decorador y unfail_if_not_removed
decorador para tus pruebas.
Instalación
pip install deprecation
Ejemplo de uso
import deprecation
@deprecation.deprecated(deprecated_in="1.0", removed_in="2.0",
current_version=__version__,
details="Use the bar function instead")
def foo():
"""Do some stuff"""
return 1
Ver http://deprecation.readthedocs.io/ para la documentación completa.
-
no responde la pregunta real.
– Catskul
2 mayo 2019 a las 16:43
-
Nota PyCharm no reconoce esto
– cz
23 de agosto de 2019 a las 10:55
único
Supongo que la razón es que el código de Python no se puede procesar de forma estática (como se hace con los compiladores de C++), no puede recibir una advertencia sobre el uso de algunas cosas antes de usarlo. No creo que sea una buena idea enviar spam al usuario de su secuencia de comandos con un montón de mensajes “Advertencia: este desarrollador de esta secuencia de comandos está utilizando una API obsoleta”.
Actualizar: pero puede crear un decorador que transformará la función original en otra. La nueva función marcará/verificará el interruptor indicando que esta función ya fue llamada y mostrará un mensaje solo al encender el interruptor. Y/o al salir, puede imprimir una lista de todas las funciones obsoletas utilizadas en el programa.
Puedes crear un archivo utils
import warnings
def deprecated(message):
def deprecated_decorator(func):
def deprecated_func(*args, **kwargs):
warnings.warn("{} is a deprecated function. {}".format(func.__name__, message),
category=DeprecationWarning,
stacklevel=2)
warnings.simplefilter('default', DeprecationWarning)
return func(*args, **kwargs)
return deprecated_func
return deprecated_decorator
Y luego importe el decorador de desaprobación de la siguiente manera:
from .utils import deprecated
@deprecated("Use method yyy instead")
def some_method()"
pass
-
¡Gracias, estoy usando esto para enviar al usuario al lugar correcto en lugar de solo mostrar el mensaje de obsolescencia!
– Germán Attanasio
5 de febrero de 2018 a las 22:03
-
no responde la pregunta real.
– Catskul
2 mayo 2019 a las 16:42
ACTUALIZACIÓN: creo que es mejor, cuando mostramos DeprecationWarning solo la primera vez para cada línea de código y cuando podemos enviar algún mensaje:
import inspect
import traceback
import warnings
import functools
import time
def deprecated(message: str=""):
"""
This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used first time and filter is set for show DeprecationWarning.
"""
def decorator_wrapper(func):
@functools.wraps(func)
def function_wrapper(*args, **kwargs):
current_call_source="|".join(traceback.format_stack(inspect.currentframe()))
if current_call_source not in function_wrapper.last_call_source:
warnings.warn("Function {} is now deprecated! {}".format(func.__name__, message),
category=DeprecationWarning, stacklevel=2)
function_wrapper.last_call_source.add(current_call_source)
return func(*args, **kwargs)
function_wrapper.last_call_source = set()
return function_wrapper
return decorator_wrapper
@deprecated('You must use my_func2!')
def my_func():
time.sleep(.1)
print('aaa')
time.sleep(.1)
def my_func2():
print('bbb')
warnings.simplefilter('always', DeprecationWarning) # turn off filter
print('before cycle')
for i in range(5):
my_func()
print('after cycle')
my_func()
my_func()
my_func()
Resultado:
before cycle
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:45: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
aaa
aaa
aaa
aaa
after cycle
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:47: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:48: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
C:/Users/adr-0/OneDrive/Projects/Python/test/unit1.py:49: DeprecationWarning: Function my_func is now deprecated! You must use my_func2!
aaa
Process finished with exit code 0
Simplemente podemos hacer clic en la ruta de advertencia e ir a la línea en PyCharm.
-
¡Gracias, estoy usando esto para enviar al usuario al lugar correcto en lugar de solo mostrar el mensaje de obsolescencia!
– Germán Attanasio
5 de febrero de 2018 a las 22:03
-
no responde la pregunta real.
– Catskul
2 mayo 2019 a las 16:42
Python
es un dinamicamente lenguaje mecanografiado. No es necesario declarar el tipo a variable o tipo de argumento para la función de forma estática.
Dado que todo es dinámico si se procesa en tiempo de ejecución. Incluso si un método está en desuso, se conocerá en tiempo de ejecución o solo durante la interpretación.
usar deprecación módulo para desaprobar los métodos.
deprecation es una biblioteca que habilita las deprecations automatizadas. ofrece la obsoleto() decorador para envolver funciones, proporcionando advertencias adecuadas tanto en la documentación como a través de Python advertencias sistema, así como el deprecación.fail_if_not_removed() decorador de métodos de prueba para garantizar que el código en desuso finalmente se elimine.
Instalación:
python3.10 -m pip install deprecation
Pequeña demostración:
import deprecation
@deprecation.deprecated(details="Use bar instead")
def foo():
print("Foo")
def bar():
print("Bar")
foo()
bar()
Producción:
test.py: DeprecatedWarning: foo is deprecated. Use bar instead
foo()
Foo
Bar
ahora hay un deprecación paquete
– muón
1 de noviembre de 2017 a las 21:13
Entiendo las formas de hacerlo, pero vine aquí para obtener una idea de por qué no está en la biblioteca estándar (como supongo que es el caso del OP) y no veo una buena respuesta a la pregunta real
– SwimBikeRun
1 de abril de 2019 a las 14:57
¿Por qué sucede tan a menudo que las preguntas obtienen docenas de respuestas que ni siquiera intentan responder la pregunta e ignoran activamente cosas como “Conozco recetas”? ¡Es enloquecedor!
– Catskul
2 mayo 2019 a las 16:46
@Catskul por puntos de internet falsos.
– Stefano Borini
2 mayo 2019 a las 19:07
Puedes usar el Obsoleto Biblioteca.
– Laurent LAPORTE
20 mayo 2019 a las 20:21