Coverage for sources/ictruck/__/validators.py: 96%
29 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-05 05:21 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-05 05:21 +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. '''
25from . import imports as __
28def validate_arguments(
29 globalvars: dict[ str, __.typx.Any ],
30 errorclass: type[ Exception ],
31):
32 ''' Decorator factory which produces argument validators. '''
34 def decorate( function: __.cabc.Callable[ ..., __.typx.Any ] ):
35 ''' Decorates function to be validated. '''
37 @__.funct.wraps( function )
38 def validate( *posargs: __.typx.Any, **nomargs: __.typx.Any ):
39 ''' Validates arguments before invocation. '''
40 signature = __.inspect.signature( function )
41 inspectee = signature.bind( *posargs, **nomargs )
42 inspectee.apply_defaults( )
43 for name, value in inspectee.arguments.items( ):
44 param = signature.parameters[ name ]
45 annotation = param.annotation
46 if __.is_absent( value ): continue
47 if annotation is param.empty: continue
48 classes = _reduce_annotation(
49 annotation, globalvars = globalvars )
50 if not isinstance( value, classes ):
51 raise errorclass( name, classes )
52 return function( *posargs, **nomargs )
54 return validate
56 return decorate
59def _reduce_annotation(
60 annotation: __.typx.Any, globalvars: dict[ str, __.typx.Any ]
61) -> tuple[ type, ... ]:
62 if isinstance( annotation, str ): 62 ↛ 63line 62 didn't jump to line 63 because the condition on line 62 was never true
63 return _reduce_annotation(
64 eval( annotation, globalvars ), # noqa: S307
65 globalvars = globalvars )
66 origin = __.typx.get_origin( annotation )
67 if isinstance( annotation, __.types.UnionType ) or origin is __.typx.Union:
68 return tuple( __.itert.chain.from_iterable(
69 map(
70 lambda a: _reduce_annotation( a, globalvars = globalvars ),
71 __.typx.get_args( annotation ) ) ) )
72 if origin is None: return ( annotation, )
73 if origin is __.typx.Annotated:
74 return _reduce_annotation(
75 annotation.__origin__, globalvars = globalvars )
76 # TODO? Other special forms.
77 return ( origin, )