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

1# vim: set filetype=python fileencoding=utf-8: 

2# -*- coding: utf-8 -*- 

3 

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#============================================================================# 

19 

20 

21''' Validators for internal use. ''' 

22 

23 

24 

25from . import imports as __ 

26 

27 

28def validate_arguments( 

29 globalvars: dict[ str, __.typx.Any ], 

30 errorclass: type[ Exception ], 

31): 

32 ''' Decorator factory which produces argument validators. ''' 

33 

34 def decorate( function: __.cabc.Callable[ ..., __.typx.Any ] ): 

35 ''' Decorates function to be validated. ''' 

36 

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 ) 

53 

54 return validate 

55 

56 return decorate 

57 

58 

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, )