Política de Obsolescencia#

Muchos usuarios y otros paquetes dependen de diferentes partes de Qiskit. Debemos asegurarnos de que cada vez que hagamos cambios en el código, demos a los usuarios tiempo suficiente para ajustarse sin romper el código que ya han escrito.

Lo más importante: no cambies ninguna interfaz pública a menos que sea absolutamente necesario. Agregar cosas está bien, quitar cosas es molesto para los usuarios, pero se puede manejar razonablemente con mucha anticipación, pero cambiar el comportamiento generalmente significa que los usuarios no pueden escribir código que funcione con dos versiones posteriores de Qiskit, lo cual no es aceptable.

Ten en cuenta que los usuarios a menudo utilizarán funciones, clases y métodos que nosotros, los desarrolladores de Qiskit, podemos considerar internos o no muy utilizados. No hagas suposiciones de que «esto está enterrado, por lo que nadie lo usará»; si es público, está sujeto a la política. Las únicas excepciones aquí son funciones y módulos que son explícitamente internos, es decir, aquellos cuyos nombres comienzan con un guion bajo (_).

Los principios rectores son:

  • no debemos eliminar ni cambiar el código sin advertencias activas durante al menos tres meses o dos ciclos de versión completos;

  • siempre debe haber una forma de lograr objetivos válidos que no emita advertencias;

  • nunca asumas que una función que no es explícitamente interna no está en uso;

  • todas las obsolescencias, cambios y eliminaciones se consideran cambios de API y solo pueden ocurrir en versiones menores, no en versiones de parches, según la política de rama estable.

Eliminación de una característica#

Al eliminar una característica (por ejemplo, una clase, función o parámetro de función), seguiremos este procedimiento:

  1. La ruta alternativa debe estar en su lugar para una versión secundaria antes de que se emitan advertencias. Por ejemplo, si queremos reemplazar la función foo() con bar(), debemos hacer al menos una liberación con ambas funciones antes de emitir cualquier advertencia dentro de foo(). Puedes emitir PendingDeprecationWarnings desde las rutas inmediatamente anteriores.

    Motivo: necesitamos darles a las personas tiempo para cambiar sin romper su código tan pronto como actualicen.

  2. Después de que la ruta alternativa haya estado presente en al menos una versión menor, se deben emitir las advertencias de desaprobación. Agrega una nota de la versión con una sección de deprecations que enumere todas las rutas obsoletas, sus alternativas y el motivo de la obsolescencia. Actualiza las pruebas para probar las advertencias.

    Motivo: las eliminaciones deben ser muy visibles en al menos una versión, a fin de minimizar la sorpresa de los usuarios cuando realmente se vayan.

  3. Establece una fecha de eliminación para la función anterior y elimínala (junto con las advertencias) cuando suceda. Esto debe ser al menos tres meses después de que se lanzó por primera vez la versión con las advertencias, y no puede ser la versión secundaria inmediatamente después de las advertencias. Agrega una nota de lanzamiento de upgrade que enumere todas las eliminaciones. Por ejemplo, si la ruta alternativa se proporcionó en 0.19.0 y las advertencias se agregaron en 0.20.0, la primera versión para la remoción es 0.22.0, incluso si 0.21.0 se lanzó más de tres meses después de 0.20.0.

    Nota

    Estos son requisitos mínimos. Para la eliminación de funciones importantes o principales, proporciona a los usuarios al menos una versión menor adicional, si no es que más.

    Motivo: debe haber tiempo para que los usuarios vean estos mensajes y darles tiempo para adaptarse. No todos los usuarios actualizarán su versión de Qiskit inmediatamente y algunos pueden omitir versiones menores.

Cuando una función se marca como obsoleta, está programada para su eliminación, pero los usuarios aún deberían poder confiar en que funcionará correctamente. Consideramos una característica marcada como «obsoleta» como congelada; nos comprometemos a mantenerlo con correcciones de errores críticos hasta que sea eliminada, pero no le agregaremos nueva funcionalidad.

Cambio de comportamiento#

Cambiar el comportamiento sin una eliminación es particularmente difícil de administrar, porque necesitamos tener ambas opciones disponibles para dos versiones y poder emitir advertencias. Por ejemplo, cambiar el tipo de valor de retorno de una función implicará casi invariablemente hacer un rompimiento de la API, lo que es frustrante para los usuarios y les dificulta el uso de Qiskit.

La mejor solución aquí es a menudo hacer una nueva función y luego usar los procedimientos para la eliminación anteriores.

Si es absolutamente necesario cambiar el comportamiento del código existente (aparte de corregir errores), deberás usar tu mejor criterio para aplicar los principios rectores que se encuentran en la parte superior de este documento. La advertencia más adecuada para los cambios de comportamiento suele ser FutureWarning. Algunas posibilidades de cómo efectuar un cambio:

  • Si estás cambiando el comportamiento predeterminado de una función, considera agregar un argumento de palabra clave para seleccionar entre comportamientos antiguos y nuevos. Cuando llegue el momento, puedes emitir una FutureWarning si no se proporciona el argumento de palabra clave (por ejemplo si es None), diciendo que el nuevo valor pronto se convertirá en el valor predeterminado. Deberás pasar por el período normal de desuso para eliminar este argumento de palabra clave después de haber realizado el cambio de comportamiento. Esto llevará al menos seis meses para pasar por ambos ciclos.

  • Si necesitas cambiar el tipo de devolución de una función, considera agregar una nueva función que devuelva el nuevo tipo y luego sigue los procedimientos para obsoletar la función anterior.

  • Si necesitas aceptar una nueva entrada que no puedes distinguir de una posibilidad existente debido a su tipo, considera dejar que se pase por un argumento de palabra clave diferente o agrega una segunda función que solo acepte la nueva forma.

Emisión de advertencias de obsolescencia#

The proper way to raise a deprecation warning is to use the decorators @deprecate_arg and @deprecate_func from qiskit.utils.deprecation. These will generate a standardized message and and add the deprecation to that function’s docstring so that it shows up in the docs.

from qiskit.utils.deprecation import deprecate_arg, deprecate_func

@deprecate_func(since="0.24.0", additional_msg="No replacement is provided.")
def deprecated_func():
    pass

@deprecate_arg("bad_arg", new_alias="new_name", since="0.24.0")
def another_func(bad_arg: str, new_name: str):
    pass

Usually, you should set additional_msg: str `` with the format ``"Instead, use ..." so that people know how to migrate. Read those functions” docstrings for additional arguments like pending: bool and predicate.

If you are deprecating outside the main Qiskit repo, set package_name to match your package. Alternatively, if you prefer to use your own decorator helpers, then have them call add_deprecation_to_docstring from qiskit.utils.deprecation.

If @deprecate_func and @deprecate_arg cannot handle your use case, consider improving them. Otherwise, you can directly call the warn function from the warnings module in the Python standard library, using the category DeprecationWarning. For example:

import warnings

def deprecated_function():
    warnings.warn(
        "The function qiskit.deprecated_function() is deprecated since "
        "Qiskit Terra 0.20.0, and will be removed 3 months or more later. "
        "Instead, you should use qiskit.other_function().",
        category=DeprecationWarning,
        stacklevel=2,
    )
    # ... the rest of the function ...

Asegúrate de incluir la versión del paquete que introdujo la advertencia de obsolescencia (para que los mantenedores puedan ver fácilmente cuándo es válido eliminarlo) y cuál es la ruta alternativa.

Toma nota del argumento stacklevel. Esto controla qué función se acusa de estar en desuso. Establecer stacklevel=1 (predeterminado) significa que la advertencia responsabilizará a la función warn en sí misma, mientras que stacklevel=2 responsabilizará correctamente a la función contenedora. Es inusual establecer esto en algo que no sea 2, pero puede ser útil si usas una función auxiliar para emitir la misma advertencia en varios lugares.

Prueba de funcionalidad obsoleta#

Cada vez que agregues advertencias de obsolescencia, deberás actualizar las pruebas relacionadas con la funcionalidad. De lo contrario, el conjunto de pruebas debería fallar debido a las nuevas advertencias. Debemos continuar probando la funcionalidad en desuso durante el período de desuso, para asegurarnos de que aún funcione.

Para actualizar las pruebas, debes envolver cada llamada de comportamiento obsoleto en su propio bloque de aserción. Para las subclases de unittest.TestCase (que son todos los casos de prueba de Qiskit), esto se hace de la siguiente manera:

class MyTestSuite(QiskitTestCase):
   def test_deprecated_function(self):
      with self.assertWarns(DeprecationWarning):
         output = deprecated_function()
      # ... do some things with output ...
      self.assertEqual(output, expected)

Documentación de obsolescencias y cambios importantes#

It is important to warn the user when your breaking changes are coming.

@deprecate_arg and @deprecate_func will automatically add the deprecation to the docstring for the function so that it shows up in docs.

If you are not using those decorators, you should directly add a Sphinx deprecated directive

.. code-block:: python
def deprecated_function():

«»» Short description of the deprecated function.

Obsoleto desde la versión 0.20.0: The function qiskit.deprecated_function() is deprecated since Qiskit Terra 0.20.0, and will be removed 3 months or more later. Instead, you should use qiskit.other_function().

<rest of the docstring> «»» # … the rest of the function …

You should also document the deprecation in the changelog by using Reno. Explain the deprecation and how to migrate.

In particular situations where a deprecation or change might be a major disruptor for users, a migration guide might be needed. Once the migration guide is written and published, deprecation messages and documentation should link to it (use the additional_msg: str argument for @deprecate_arg and @deprecate_func).