Manejar el AssertionError en Python

En este artículo, exploraremos el AssertionError, comenzando por el lugar donde reside en la jerarquía general de clases de excepción de Python.

Un AssertionError, solo puede ocurrir cuando falla una declaración de aserción (afirmación), por lo tanto, un AssertionError nunca debe ser una sorpresa o aparecer en una sección del código de su aplicación que sea inesperado.

Cada vez que escribas una declaración de aserción, también debes proporcionar un código de manejo de excepciones apropiado, para hacer frente a un error de aserción inevitable.

También hara una prueba con código funcional, que ilustra cómo se pueden usar las declaraciones assert y cómo la falla de dicha declaración generará un AssertionError que debe detectarse y manejarse, como cualquier otro error

Herencia

Todas las excepciones de Python heredan de la clase BaseException, o bien  extienden desde una clase heredada en la misma. La jerarquía de excepciones completa de este error es:

• BaseException

o Excepción

 AssertionError

Código

A continuación, se muestra el ejemplo de código completo que usaremos en este artículo. Se puede copiar y pegar si desea jugar con el código usted mismo y ver cómo funciona todo.

import datetime

from gw_utility.book import Book
from gw_utility.logging import Logging


def main():
    Logging.line_separator("BOTH INCLUDE PUBLICATION DATES", 50, '+')
    # Creamos dos Books con argumentos identicos.
    the_stand = Book("The Stand", "Stephen King", 1153, datetime.date(1978, 1, 1))
    the_stand_2 = Book("The Stand", "Stephen King", 1153, datetime.date(1978, 1, 1))

    # Afirma la equivalencia de los Books.
    check_equality(the_stand, the_stand_2)

    Logging.line_separator("ONE MISSING PUBLICATION DATE", 50, '+')
    # Create two Books, one without publication_date argument specified.
    the_hobbit = Book("The Hobbit", "J.R.R. Tolkien", 366, datetime.date(1937, 9, 15))
    the_hobbit_2 = Book("The Hobbit", "J.R.R. Tolkien", 366)

    # Afirma la equivalencia de los Books.
    check_equality(the_hobbit, the_hobbit_2)


def check_equality(a, b):
    """Afirmacion de equivalencia de dos objetos pasados..

    :param a: First object.
    :param b: Second object.
    :return: Indicates if assertion was successful.
    """
    try:
        Logging.line_separator("ASSERTING EQUIVALENCE OF...")
        # Output objects using __str__ method.
        Logging.log(a)
        Logging.log(b)
        # Afirmacion de la  equivalencia de los objetos indicando que la inigualdad ha fallado..
        assert a == b, "The objects ARE NOT equal."
        # Indica que la   assertion ha sucedido.
        Logging.log("The objects are equal.")
        return True
    except AssertionError as error:
        # Salida  esperada de un AssertionErrors.
        Logging.log_exception(error)
    except Exception as exception:
        # Salida de una excepcion inesperada.
        Logging.log_exception(exception, False)


if __name__ == "__main__":
    main()

import datetime


class Book:
    author: str
    page_count: int
    publication_date: datetime.date
    title: str

    def __eq__(self, other):
        """Determina si los objetos pasados son equivalentes a un objeto actual. """
        return self.__dict__ == other.__dict__

    def __init__(self, title: str = None, author: str = None, page_count: int = None,
                 publication_date: datetime.date = None):
        """Inicializa una instancia de Book.

        :param title: Title of Book.
        :param author: Author of Book.
        :param page_count: Page Count of Book.
        :param publication_date: Publication Date of Book.
        """
        self.author = author
        self.page_count = page_count
        self.publication_date = publication_date
        self.title = title

    def __len__(self):
        """Devuelve el length del titulo."""
        return len(self.title)

    def __str__(self):
        """Devuelve un string formateado como represntacion de   Book."""
        date = '' if self.publication_date is None else f', published on {self.publication_date.__format__("%B %d, %Y")}'
        return f'\'{self.title}\' by {self.author} at {self.page_count} pages{date}.'

Como funciona un assert (afirmación)

Para ilustrar cómo funcionan las afirmaciones, realizaremos algunas pruebas de equivalencia básicas para determinar si un objeto es igual a un segundo objeto.

Para hacer las cosas un poco más interesantes, hemos creado una clase Book personalizada simple, que almacena información básica sobre cada instancia de Book.

class Book:
    author: str
    page_count: int
    publication_date: datetime.date
    title: str

    def __eq__(self, other):
        """Determines if passed object is equivalent to current object."""
        return self.__dict__ == other.__dict__

    def __init__(self, title: str = None, author: str = None, page_count: int = None,
                 publication_date: datetime.date = None):
        """Inicializando instancia de Book.

        :param title: Title of Book.
        :param author: Author of Book.
        :param page_count: Page Count of Book.
        :param publication_date: Publication Date of Book.
        """
        self.author = author
        self.page_count = page_count
        self.publication_date = publication_date
        self.title = title

    def __len__(self):
        """Returns the length of title."""
        return len(self.title)

    def __str__(self):
        """Returns a formatted string representation of Book."""
        date = '' if self.publication_date is None else f', published on {self.publication_date.__format__("%B %d, %Y")}'
        return f'\'{self.title}\' by {self.author} at {self.page_count} pages{date}.'

Una vez realizada la  asignación de propiedad de instancia en el método __init __ (self, title: str = None, author: str = None, page_count: int = None, publishing_date: datetime.date = None).

Podemos iniciar el proceso de comparación.

Vemos que tenemos un método __eq __ (self, other), al que se llamará cuando se intente verificar la equivalencia entre una instancia de Book y otro objeto.

Para manejar esto, usamos la propiedad incorporada __dict__ como una forma de comparación (aunque podríamos optar también por __str __ (self)).

El código que usaremos para probar algunas instancias de objetos comienza con el método check_equality (a, b)

def check_equality(a, b):
    """Afirmando la equivalencia de dos objetos 

    :param a: First object.
    :param b: Second object.
    :return: Indica que el assertion fue exitoso.
    """
    try:
        Logging.line_separator("ASSERTING EQUIVALENCE OF...")
        # Salida de objetos usando __str__ method.
        Logging.log(a)
        Logging.log(b)
        # Assert de equivalencia de objetos, indicando que la desigualdad ha fallado 
        assert a == b, "The objects ARE NOT equal."
        # Indica que la afirmación se realizó correctamente.

        Logging.log("The objects are equal.")
        return True
    except AssertionError as error:
        # Esperando salida de un AssertionError.
        Logging.log_exception(error)
    except Exception as exception:
        # Salida de una excepción inesperada
        Logging.log_exception(exception, False)

La mayor parte del código  que tenemos, maneja la salida de información al registro (log) sobre la prueba de igualdad.

La línea crítica es assert a == b, "Los objetos NO SON iguales", que realiza una afirmación de que ambos objetos pasados ​​son equivalentes entre sí.

El segundo argumento de una declaración de aserción es el mensaje de fallo que se usa como argumento si ocurre una falla.

En términos prácticos, este argumento de mensaje de falla se agrega a la propiedad .args de la instancia AssertionError, dándonos un mensaje de error real cuando detectamos la excepción en otra parte de nuestro código.

Dado que una declaración de aserción fallida siempre genera un AssertionError, si la ejecución continúa más allá de esa declaración, podemos asumir que los objetos son iguales y la salida al registro.

Con todo configurado, podemos probar nuestro método de aserción creando un par de instancias de Book, the_stand y the_stand_2:

def main():
    Logging.line_separator("BOTH INCLUDE PUBLICATION DATES", 50, '+')
    # Creando dos  Books con argumentos identicos.
    the_stand = Book("The Stand", "Stephen King", 1153, datetime.date(1978, 1, 1))
    the_stand_2 = Book("The Stand", "Stephen King", 1153, datetime.date(1978, 1, 1))

    # Comparando equivalencia de los libros
    check_equality(the_stand, the_stand_2)

    # ...

Pasar ambas instancias de Book a check_equality (a, b) produce el siguiente resultado:

+++++++++ BOTH INCLUDE PUBLICATION DATES +++++++++
----- ASSERTING EQUIVALENCE OF... ------
'The Stand' by Stephen King at 1153 pages, published on January 01, 1978.
'The Stand' by Stephen King at 1153 pages, published on January 01, 1978.
The objects are equal

Como podemos suponer lógicamente, dado que todos los argumentos pasados ​​a ambos inicializadores de Libro eran idénticos, nuestra declaración assert  tuvo éxito y vemos la salida de confirmación en el registro. 

Sin embargo, veamos qué sucede si intentamos una segunda prueba con dos objetos Book ligeramente diferentes, donde una instancia no pasó un argumento publication_date durante la inicialización:

Logging.line_separator("ONE MISSING PUBLICATION DATE", 50, '+')
# Creando dos Books, uno de ello sin el argumento publication_date 
the_hobbit = Book("The Hobbit", "J.R.R. Tolkien", 366, datetime.date(1937, 9, 15))
the_hobbit_2 = Book("The Hobbit", "J.R.R. Tolkien", 366)

# Comparando equivalencia de los libros
check_equality(the_hobbit, the_hobbit_2)

 

Como probablemente pueda adivinar, estos dos objetos Book no se consideran iguales, ya que sus propiedades __dict__ subyacentes son diferentes entre sí.

En consecuencia, nuestra declaración de aserción falla y genera un AssertionError en la salida:

++++++++++ ONE WITHOUT PUBLICATION DATE ++++++++++
----- ASSERTING EQUIVALENCE OF... ------
'The Hobbit' by J.R.R. Tolkien at 366 pages, published on September 15, 1937.
'The Hobbit' by J.R.R. Tolkien at 366 pages.
[EXPECTED] AssertionError: The objects ARE NOT equal.

Espero que lo hayas entendido. Gracias

…. hay que levantarse cada mañana con una esperanza y dormirse cada noche con una meta….

Y