Package: exec_hints
exec_hintsPretty comprehensive typehint evaluator to give you code that extra bit of zazz and illegibility it always neededFor all examples, here's the obvious prerequisitefromexec_hintsimportexec_hints,check_hints,_UnionType,LiteralAlternatively, you can justimport exec_hintsand use the module as a callable!Let's start off small: you can specify a hint on an argument and it'll convert it@exec_hintsdeffoo(arg:int):print(arg*arg)foo('12')>>>144@exec_hintsdeffoo(arg:set):print(arg,arg.issubset('123'))foo('12')>>>{'2','1'}True@exec_hintsdeffoo(arg:set[int]):print(arg,arg.issubset(range(10)))foo('12')>>>{1,2}TrueA single argument to dict will modify all values, 2 arguments means the first applies to keys, 2nd to values@exec_hintsdeffoo(arg:dict[int]):print(arg,arg.keys())foo({'a':'123',12:13.2})>>>{'a':123,12:13}dict_keys(['a',12])Works with Unions (more on that later)@exec_hintsdeffoo(arg:set[int|str]):print(arg,arg.issubset(range(10)))foo('1a2')>>>{1,'a',2}FalseIf an iterable hint is provided with a number of arguments matching what was passed, each subhint will be applied to corresponding arguments@exec_hintsdeffoo(arg:list[int,str,int]):print(arg,arg[::-1])foo('1a2')>>>[1,'a',2][2,'a',1]...and of course, we can nest these 😎@exec_hintsdeffoo(arg:list[int,int|str,tuple[int|float|str]]):print(arg)foo(('1','a',['1','2.67','b']))>>>[1,'a',(1,2.67,'b')]If several options are given, it tries each in turn@exec_hintsdeffoo(arg:list[int,int|str]|tuple[int|float|str]):print(arg)#first fails because there are 2 subhints and 3 args providedfoo(['1','2.67','b'])>>>(1,2.67,'b')#first works!foo(['1','2.67'])>>>[1,'2.67']You can pass your own functions in here, but they cant directly be used with |
Blame Python, not me.@exec_hintsdeffoo(arg:lambdax:x[::2]):print(arg)foo('banana')>>>bnnThere's a workaround for that tho!list|1isnt supported by Python, so we have a class for that:LiteralAccepts a callable or a literal (ok the class name isnt the best, accepting any suggestions)
If it's callable, will try call on the arg, otherwise will just return itself@exec_hintsdeffoo(arg:int|Literal(0)):print(arg)foo('banana')>>>0We can use it to chain builtins that wouldnt normally be allowed.
Also note you can use it with[]syntax too@exec_hintsdeffoo(arg:int|Literal[str.upper]):print(arg)foo('banana')>>>BANANAThis documentation would have to be a few miles long to cover all of this
So here's a few examples:@exec_hintsdeffoo(message:int|tuple[int]|list[0],dct:dict[int,tuple[str.lower]]):print(message)print(dct)foo('a1',{'213':'BaNaNa','444':'BoBa'})>>>[0,0]>>>{213:('b','a','n','a','n','a'),444:('b','o','b','a')}Uhh, it works on a lot of stuff :)
Notably you can manipulate*argsand**kwargsas normal.
Works with defaults too, but it will try to apply the hint to the default provided@exec_hintsdefa(a,b:str.upper,c:tuple[int],d=12,*e:lambdax:map(str,x),f:int|Literal(-3)=3,**g:dict[str]):print(a,b,c,d,e,f,g)a(1,'bb',['2','3'],4,5,banana=1,apple=2)>>>1BB(2,3)4('5',)3{'banana':'1','apple':'2'}You can typehint return values too 🎉@exec_hintsdeffoo(arg:int|Literal(str.upper))->lambdax:tuple(reversed(x)):print(arg)returnargprint(foo('banana'))>>>BANANA>>>('A','N','A','N','A','B')Works with classes, including dataclasses. As of 0.1.4 it's a bit scuffed as the result is a function that calls the constructor of the class, but it works.importexec_hintsfromdataclassesimportdataclass@exec_hints@dataclassclassBot:alignment:boolserial:intname:strprint(Bot(123123,'12',2))>>>Bot(alignment=True,serial=12,name='2')Thecheck_hintsfunction is useful for debugging or making sure your hints are correct. Works as you would expect it to similar toexec_hints, but will raise an error if the hint fails. Literal checks are also done via equality, or if the literal is callable, it will be called on the arg and checked for truthiness.fromexec_hintsimportcheck_hints@check_hintsdeffoo(arg:int|Literal(str.upper)):print(arg)foo('banana')# FAILSfoo('BANANA')# PASSES