Coverage for sources/ictruck/__/validators.py: 100%
30 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 16:01 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 16:01 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
4#============================================================================#
5# #
6# Licensed under the Apache License, Version 2.0 (the "License"); #
7# you may not use this file except in compliance with the License. #
8# You may obtain a copy of the License at #
9# #
10# http://www.apache.org/licenses/LICENSE-2.0 #
11# #
12# Unless required by applicable law or agreed to in writing, software #
13# distributed under the License is distributed on an "AS IS" BASIS, #
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15# See the License for the specific language governing permissions and #
16# limitations under the License. #
17# #
18#============================================================================#
21''' Validators for internal use. '''
24from __future__ import annotations
26from . import imports as __
29def validate_arguments(
30 globalvars: dict[ str, __.typx.Any ],
31 errorclass: type[ Exception ],
32):
33 ''' Decorator factory which produces argument validators. '''
35 def decorate( function: __.cabc.Callable[ ..., __.typx.Any ] ):
36 ''' Decorates function to be validated. '''
38 @__.funct.wraps( function )
39 def validate( *posargs: __.typx.Any, **nomargs: __.typx.Any ):
40 ''' Validates arguments before invocation. '''
41 signature = __.inspect.signature( function )
42 inspectee = signature.bind( *posargs, **nomargs )
43 inspectee.apply_defaults( )
44 for name, value in inspectee.arguments.items( ):
45 param = signature.parameters[ name ]
46 annotation = param.annotation
47 if __.is_absent( value ): continue
48 if annotation is param.empty: continue
49 classes = _reduce_annotation(
50 annotation, globalvars = globalvars )
51 if not isinstance( value, classes ):
52 raise errorclass( name, classes )
53 return function( *posargs, **nomargs )
55 return validate
57 return decorate
60def _reduce_annotation(
61 annotation: __.typx.Any, globalvars: dict[ str, __.typx.Any ]
62) -> tuple[ type, ... ]:
63 if isinstance( annotation, str ):
64 return _reduce_annotation(
65 eval( annotation, globalvars ), # noqa: S307
66 globalvars = globalvars )
67 origin = __.typx.get_origin( annotation )
68 if isinstance( annotation, __.types.UnionType ) or origin is __.typx.Union:
69 return tuple( __.itert.chain.from_iterable(
70 map(
71 lambda a: _reduce_annotation( a, globalvars = globalvars ),
72 __.typx.get_args( annotation ) ) ) )
73 if origin is None: return ( annotation, )
74 if origin is __.typx.Annotated:
75 return _reduce_annotation(
76 annotation.__origin__, globalvars = globalvars )
77 # TODO? Other special forms.
78 return ( origin, )