admin管理员组

文章数量:1289877

I have a function called get_data(**kwargs) which is the entry point of the API I am building. This function controls the API flow, instantiate various objects etc. I want to implement an efficient and robust kwarg validation. I have already done something but I am not sure that is the proper approach.

A simplified version of the code is (with explanations afterwards):

import re

def get_data(**kwargs):

    # Input validation dictionary
    parser = {"ids": lambda x: isinstance(x, str) or (isinstance(x, list) and all(isinstance(id, str) for id in x)),
              "id_type": ["type1", "type2", "type3"],
              "ctry_code": ("", lambda x: x == "LOCAL" or bool(re.fullmatch("[A-Z]{3}", x))),
              "is_alive": (True, lambda x: isinstance(x, bool))}

    for key in parser.keys():
        if isinstance(parser[key], tuple):
            default = parser[key][0]
            validator = parser[key][1]
        else:
            default = None
            validator = parser[key]
        if key in kwargs.keys():
            if callable(validator):
                if validator(kwargs[key]):
                    kv[key] = kwargs[key]
                else:
                    raise ValueError(f"Argument [{key}] must satisfy validation function")
            else:
                if kwargs[key] in validator:
                    kv[key] = kwargs[key]
                else:
                    raise ValueError(f"Argument [{key}] must satisfy validation function")
        else:
            if default is None:
                raise ValueError(f"Argument [{key}] has no default value and must be provided")
            else:
                kv[key] = default

    unmatched = {k: dict[k] for k in kwargs.keys() if k not in parser.keys()}
    # doing some stuff with kv
    object1 = Object(unmatched)
    # doing some other stuff to get data
    return data

Some of the function kwargs are used directly in the function and stored in variable kv and some are passed to object instantiation in the function, those are stored in variable unmatched. The distinction between kv and unmatched are done by an argparser. I want to be able to validate what ends up being in kv.

The dictionary parser has all possible kv as key and their value are validation parameters. If the value is a tuple, its first element is the default value and the second is a "validator". If the value is not a tuple, then it is only a "validator". A validator can be a lambda function or a list of allowed values.

This might raise the question "why not separate the kv and unmatched at the function definition level?" with something like:

def get_data(kv1=x1, kv2=x2, ..., kvn=xn, **kwargs):

The reason is because I want to validate each kv and if they are seperated from kwargs in the function definition, I would have to use e.g. inspect.getfullargspec(get_data) to get them and in anycase, I would have to define them again in the dict parser to assotiate them with "validators". So it seems to increase complexity rather than decrease it. But I might be fully wrong there and happy to be corrected.

So, this works as intented. 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.

本文标签: python 3xImplementing robust function argument validationStack Overflow