79500722

Date: 2025-03-11 12:25:39
Score: 1.5
Natty:
Report link

In my initial post, I wrote:

However, I am wondering if there might not be a better way to do this argument validation. Potentially using a third party library. I guess my point is that I am feeling that I might be reinventing the wheel and that people smarte than me likely faced this issue before and probably implemented a better solution.

I found that I could use either attrs, pydantic, or beartype to do what I wanted so I implemented solutions using each to test them and decide for myself what seems to make more sense for my project. typeguard was another alternative which I haven't tested.

Below are the three implementations.

attrs

import attrs

@attrs.define
class GetAttrs:
    id: str | list[str] = attrs.field(
        validator=attrs.validators.or_(
            attrs.validators.instance_of(str),
            attrs.validators.deep_iterable(
                member_validator=attrs.validators.instance_of(str),
                iterable_validator=attrs.validators.instance_of(list),
            ),
        )
    )
    id_type: str = attrs.field(validator=attrs.validators.instance_of(str))
    ctry_code: str = attrs.field(default = "", validator=attrs.validators.matches_re(r"[A-Z]{3}$|^LOCAL$|^$"))
    is_alive: bool = attrs.field(default = False, validator=attrs.validators.instance_of(bool))

    def get(self, **kwargs):
        # doing some stuff
        object1 = Object(kwargs)
        # doing some other stuff to get data
        return data


GetObj = GetAttrs(id=["id1", "id2"], id_type="myidtype")
GetObj.get(kv1={"k1": 1, "k2": 2}, kv2="test")

My main issue with this solution is that I have to pass kwargs to the method get() and not at the instanciation of GetAttrs. From what I found, I could pass kwarg at the instanciation of GetAttrs but it does not seems super clean. However, I good point is that as I am now planning to use attrs in my modules, using it here would not lead to multiplication of third party libraries.

pydantic

from typing import Literal
from pydantic import Field, validate_call

type Liststr = list[str]

@validate_call
def get_pydantic(id: str | Liststr,
                 id_type: str,
                 ctry_code: str = Field(default="", pattern=r"^[A-Z]{3}$|^LOCAL$|^$"),
                 is_alive: bool = False,
                 **kwargs ):
    # doing some stuff
    object1 = Object(kwargs)
    # doing some other stuff to get data
    return data

    get_pydantic("id"=["id1", "id2"], id_type="myidtype", kv1={"k1": 1, "k2": 2}, kv2="test")

Works quite well and I have no real concern with this solution. However, the decorator @validate_call is a very tiny part of pydantic, which does many other things, so it might make sense to use something dedicated to argument validation with a bit less scope.

beartype

from typing import Annotated, Literal, Union
import re
from beartype import beartype
from beartype.cave import  IterableType
from beartype.vale import Is

IsCtry = Annotated[str, Is[lambda string: re.fullmatch(r"[A-Z]{3}$|^LOCAL$|^$", string)]]

@beartype
def get_beartype(id: Union[str, IterableType[str]],
                 id_type: str, 
                 item_type: str,
                 ctry_code: IsCtry = "",
                 is_alive: bool = False,
                 **kwargs
                 ):
    # doing some stuff
    object1 = Object(kwargs)
    # doing some other stuff to get data
    return data

get_beartype(id=["id1", "id2"], id_type="myidtype", kv1={"k1": 1, "k2": 2}, kv2="test")

Works quite well and I found it quite elegant. I do not see any strong issue and as I want to use attrs in my module, I have the impression that I don't have the strong overlap with it that pydantic might have. So I have decided to use it as my solution.

Hope that helps anyone who might have a similar problem. Further any feedback on how thos implementations could be improved is very welcome.

Reasons:
  • Whitelisted phrase (-1): Hope that helps
  • Whitelisted phrase (-1): solution is
  • RegEx Blacklisted phrase (1): I want
  • Long answer (-1):
  • Has code block (-0.5):
  • Me too answer (2.5): have a similar problem
  • Self-answer (0.5):
  • Low reputation (1):
Posted by: Aristide