strictyaml icon indicating copy to clipboard operation
strictyaml copied to clipboard

Revalidating from type hints

Open orangelynx opened this issue 5 years ago • 1 comments

In my code, I wish to parse function parameters from a YAML configuration file, something like this:

modules:
- type: MyFirstFunc
  args:
  - people:
    - Claire
    - Frank
  - hi
  - 2
- type: MySecondFunc
  args:
  - True
  - 25.5

Because the argument list is dynamic, I have to use strictyaml.MapPattern with strictyaml.Any() as key-type to parse the args in this initial config correctly. After inspecting type I can inspect the type-hints on the functions and theoretically revalidate the args using information from the type-hints.

I wrote this prototype function for it:


def _build_strictyaml_schema_from_signature(signature):
    type_hint_map = {
        bool: strictyaml.Bool(),
        int: strictyaml.Int(),
        float: strictyaml.Float(),
        str: strictyaml.Str(),
        datetime: strictyaml.Datetime()
    }

    schema = {}

    for param_name, param in signature.parameters.items():
        optional = False

        origin = typing.get_origin(param.annotation)
        if origin is not None:
            if origin is typing.Union:
                arg_subtypes = typing.get_args(param.annotation)
                optional = (arg_subtypes[-1] is type(None))
            if issubclass(origin, collections.abc.Mapping):
                key_type, value_type = typing.get_args(param.annotation)
                new_validator = strictyaml.MapPattern(type_hint_map[key_type], type_hint_map[value_type])
            elif issubclass(origin, collections.abc.Sequence):
                value_type, = typing.get_args(param.annotation)
                new_validator = strictyaml.Seq(type_hint_map[value_type])
            elif issubclass(origin, collections.abc.Set):
                value_type, = typing.get_args(param.annotation)
                new_validator = strictyaml.UniqueSeq(type_hint_map[value_type])
            else:
                raise ValueError(f"Unknown typing format '{param}'. "
                                 "If you think this is a valid type, please consider filing a bug report.")
        else:
            new_validator = type_hint_map[param.annotation]

        if param.default != inspect.Parameter.empty:
            schema[strictyaml.Optional(param_name, default=param.default)] = new_validator
        elif optional:
            schema[strictyaml.Optional(param_name, default=None)] = new_validator
        else:
            schema[param_name] = new_validator

    return strictyaml.Map(schema)

If anybody would like to suggest what I can improve or state that I lost my sanity, please go ahead.

orangelynx avatar Feb 20 '20 12:02 orangelynx

You don't seem to be using .revalidate( ) to revalidate but other than that it all seems pretty much as I'd do it.

I did something similar in the hitchstory package.

On Thu, 20 Feb 2020, 12:07 orangelynx, [email protected] wrote:

In my code, I wish to parse function parameters from a YAML configuration file, something like this:

modules:

  • type: MyFirstFunc
  • args:
    • people:
      • Claire
      • Frank
    • hi
    • 2
  • type: MySecondFunc
  • args:
    • True
    • 25.5

Because the argument list is dynamic, I have to use strictyaml.MapPattern with strictyaml.Any() as key-type to parse the args in this initial config correctly. After inspecting type I can inspect the type-hints on the functions and theoretically revalidate the args using information from the type-hints.

I wrote this prototype function for it:

def _build_strictyaml_schema_from_type_hints(type_hints): type_hint_map = { bool: strictyaml.Bool(), int: strictyaml.Int(), float: strictyaml.Float(), str: strictyaml.Str(), datetime: strictyaml.Datetime() }

schema = {}

for arg_name, arg_type in type_hints.items():
    origin = typing.get_origin(arg_type)
    if origin is not None:
        if issubclass(origin, collections.abc.Mapping):
            key_type, value_type = typing.get_args(arg_type)
            schema[arg_name] = strictyaml.MapPattern(type_hint_map[key_type], type_hint_map[value_type])
        elif issubclass(origin, collections.abc.Sequence):
            value_type, = typing.get_args(arg_type)
            schema[arg_name] = strictyaml.Seq(type_hint_map[value_type])
        elif issubclass(origin, collections.abc.Set):
            value_type, = typing.get_args(arg_type)
            schema[arg_name] = strictyaml.UniqueSeq(type_hint_map[value_type])
        else:
            raise ValueError(f"Unknown typing format '{arg_type}'. "
                             "If you think this is a valid type, please consider filing a bug report.")
    else:
        schema[arg_name] = type_hint_map[arg_type]

return strictyaml.Map(schema)

If anybody would like to suggest what I can improve or state that I lost my sanity, please go ahead.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/crdoconnor/strictyaml/issues/89?email_source=notifications&email_token=ABOJKNN2JVVH2HWEXYNNYITRDZW7JA5CNFSM4KYNOYBKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IO64DPQ, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABOJKNL56BVBEIIBC6LDLA3RDZW7JANCNFSM4KYNOYBA .

crdoconnor avatar Feb 20 '20 17:02 crdoconnor