Skip to main content

CleanDev Validator

Librería que tiene por objetivo cambiar ligeramente el comportamiento de las clases tipo @dataclass añadiendo una forma de evitar y realizar una validación previa de sus atributos según necesidad

diagrama

@dataclass

Vamos a mostrar algunos ejemplos del uso de @dataclass en python de forma nativa y posteriormente lo compararemos con los añadidos que se implementan con esta librería para que se note la intención de la misma.

Ejemplo basico

La principal causa por la que uno quisiera usar @dataclass es crear las famosas clases de estructura de datos que solo tienen la finalidad de agrupar ciertos atributos y trabajas con ellos de una forma muy concreta.

Si quisieramos una clase con atributos con el unico fin de hacer getter y setters sin usar @dataclass tendriamos algo como esto.

class Persona:
    edad: int
    altura: int
    apellidos: str
    peso: int
    nombre: str

    @property
    def edad(self):
        return self.edad

    @edad.setter
    def edad(self, edad):
        self.edad = edad

    @property
    def altura(self):
        return self.altura

    @altura.setter
    def altura(self, altura):
        self.altura = altura

    @property
    def apellidos(self):
        return self.apellidos

    @apellidos.setter
    def apellidos(self, apellidos):
        self.apellidos = apellidos

    @property
    def peso(self):
        return self.peso

    @peso.setter
    def peso(self, peso):
        self.peso = peso

    @property
    def nombre(self):
        return self.nombre

    @nombre.setter
    def nombre(self, nombre):
        self.nombre = nombre

A continuación ejecutaremos el mismo ejemplo usando @dataclass

from dataclasses import dataclass


@dataclass()
class Persona:
    edad: int
    altura: int
    apellidos: str
    peso: int
    nombre: str

Como se puede apreciar es mucho más compacto no obstante cuando queremos usar dicha clase tenemos que definir todos sus atributos si no queremos un error, o como alternativa definir un metodo __init__() que sobreescriba el comportamiento por defecto de @dataclass para permitirnos decidir que campos y cuales no queremos dar de alta.

Veamoslo en el siguiente ejemplo

from dataclasses import dataclass


@dataclass()
class Persona:
    edad: int
    altura: int
    apellidos: str
    peso: int
    nombre: str


@dataclass()
class Animal:
    peso: int
    edad: int
    nombre: str

    def __init__(self, peso: int, edad: int):
        self.peso = peso
        self.edad = edad


if __name__ == '__main__':
    # Definiendo todos sus atributos para no tener un error
    persona: Persona = Persona(edad=18, altura=176, apellidos='Hernandez', peso=80, nombre='Daniel')

    # Para el caso de animal no estamos obligados a definirle un nombre por lo que estamos obligados a definir un método
    # __init__()
    animal: Animal = Animal(peso=20, edad=10)

Existe una forma compacta de hacer el ejemplo para la clase Animal

from dataclasses import field
from dataclasses import dataclass


@dataclass()
class Animal:
    peso: int
    edad: int
    nombre: str = field(init=False)


if __name__ == '__main__':
    animal: Animal = Animal(peso=20, edad=10)

Ahora bien realmente pese a definir el tipo de dato realmente no se comprueba si el tipo de datos que se le asigna es el correcto, veamos un ejemplo

from dataclasses import field
from dataclasses import dataclass


@dataclass()
class Animal:
    peso: int
    edad: int  # Definimos como entero
    nombre: str = field(init=False)


if __name__ == '__main__':
    animal: Animal = Animal(peso=20, edad='tengo 10 años')  # ¿¿?? Le pasamos una string para edad

Ademas de esto no existe una forma sencilla de convertir la clase en dict y filtrarle algunos atributos cosa de evitarme que crear el tipico DTO (DataTransferObject) para ese fin o cosas equivalente. Buscando sobre ese caso uno de los mejores intentos de hacer esto lo encontre aquí

Ni tampoco existe una forma facil de retornar unicamente el nombre de los atributos de mi clase.

DataClass

Clase que añade dos metodos a toda clase que herede de ella que sirven para filtrar atributos de la clase y retornar los nombres de los atributos de la misa.

fields

La propiedad __fields__ de esta clase retorna los atributos de la misma.

from dataclasses import field
from dataclasses import dataclass
from cleandev_validator import DataClass


@dataclass
class Animal(DataClass):
    peso: int
    edad: int
    nombre: str = field(init=False)


if __name__ == '__main__':
    animal: Animal = Animal(peso=20, edad='tengo 10 años')
    animal.__fields__  # ['peso', 'edad', 'nombre']

filter()

Recibe como parametro un list y un bool (default=True) bool: Bandera que decide si queremos incluir o excluir los campos pasados por la lista list: Dado una lista en funcion de bool, si es True solo mostrara (k,v) de los valores pasados por la lista en caso contrario solo excluira dichos valores.

from dataclasses import field
from dataclasses import dataclass
from cleandev_validator import DataClass


@dataclass
class Animal(DataClass):
    peso: int
    edad: int
    nombre: str = field(init=False)


if __name__ == '__main__':
    animal: Animal = Animal(peso=20, edad='tengo 10 años')
    animal.__filter__(['peso'])  # {'peso': 20}
    animal.__filter__(['peso'], False)  # {'edad': 'tengo 10 años'}

validaciones de atributos

Para ejecutar la validación propuesta de atributos hay que definir un método __constrains__ de esta manera ademas de

from dataclasses import field
from dataclasses import dataclass
from cleandev_validator import DataClass, _DataClassConstrains


@dataclass()
class Animal(DataClass):
    peso: int
    edad: int
    active: bool
    nombre: str = field(init=False)

    @property
    def __constrains__(self):
        return {
            'edad': str(_DataClassConstrains.INT)
        }

Ademas de un método para que se mantegan las validaciones propias del @dataclass

def __post_init__(self):
    super(Animal, self)._validate(**self.__dict__)

El resultado final

from dataclasses import field
from dataclasses import dataclass

from cleandev_validator import DataClass, _DataClassConstrains

@dataclass()
class Animal(DataClass):
    peso: int
    edad: int
    active: bool
    nombre: str = field(init=False)

    def __post_init__(self):
        super(Animal, self)._validate(**self.__dict__)

    @property
    def __constrains__(self):
        return {
            'peso': str(_DataClassConstrains.INT),
            'edad': str(_DataClassConstrains.INT),
            'active': str(_DataClassConstrains.BOOL),
        }


if __name__ == '__main__':
    animal: Animal = Animal(peso=20, edad=20, active=True)
    animal.__filter__(['peso'])  # {'peso': 20}
    animal.__filter__(['peso'], False)  # {'edad': 'tengo 10 años'}

De esta forma estamos obligando a que realmente todos los campos son del tipo que se define y ademas de si hay que definirlo o no a la hora de crear la clase usando field(init=False)

Metadata

Release files for cleandev-validator 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cleandev-validator 0.3.1
File Size Uploaded Provenance Actions
cleandev-validator-0.3.1.tar.gz 4.3 kB Without provenance Details for cleandev-validator-0.3.1.tar.gz

Release files / cleandev-validator-0.3.1.tar.gz

Download URL cleandev-validator-0.3.1.tar.gz
Size 4.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a3886bbe72b63dc8ef97c2d64545bd1bce813ab26cef3fa1302f6195acd7c555
BLAKE2b-256 checksum
How to use checksums
cbddc49dd95b9dfeacd80d22520e3e2d71d6d931f0a51aa31308340f30414413
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.0 CPython/3.9.10

Security for cleandev-validator 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.3.1 This release

1 release file No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page