Package: contemplation
🤔 ContemplationNovel and powerful introspection utilities for Python.Contemplation is a library for introspection of Python code. It provides a number of introspection utilities that are not available in the standard library. This library is designed to be used for debugging and analysing code.pip install contemplationTable of ContentsDocumentationIntrospectionsExecution IntrospectionsInstance IntrospectionsExperimental Type IntrospectionsGenerating DocumentationDocumentationFind the documentation here.IntrospectionsThere are three categories of introspection provided in this package:Execution IntrospectionsInstance IntrospectionsType IntrospectionsExecution introspections are introspections that are performed on the execution of code.Instance introspections are introspections that are performed on instances of objects.Type introspections are introspections that are performed on types.These are detailed below.Execution IntrospectionsThe following execution introspections are provided:CallCounter- counts the number of times functions registered to it are calledExecutionTimer- times the execution of functions registered to itFunctionLogger- logs the times, arguments, and return values of functions registered to itThese classes all act in very much the same way, they use function metadata to track the execution of functions. For example, you can time the execution of a function by wrapping it in anExecutionTimerinstance.importtimefromcontemplationimportExecutionTimerexecution_timer=ExecutionTimer()@execution_timer.time_executiondefmy_func(a:int,b:int):time.sleep(0.01)returna+bforiinrange(100):_=my_func(i,i+1)# get the total execution time by functionprint(execution_timer.get_execution_time(my_func))# or by nameprint(execution_timer.get_execution_time("my_func"))# log another function@execution_timer.time_executiondefmy_other_func(a:int,b:int):time.sleep(0.005)returna-bforiinrange(100):_=my_other_func(i,i+1)# print the times in a tableexecution_timer.pretty_print_times()The same API applies to theCallCounter:importtimefromcontemplationimportCallCountercall_counter=CallCounter()@call_counter.count_callsdefmy_func(a:int,b:int):returna+bforiinrange(100):_=my_func(i,i+1)# get the total execution time by functionprint(call_counter.get_counts(my_func))# or by nameprint(call_counter.get_counts("my_func"))# log another function@call_counter.count_callsdefmy_other_func(a:int,b:int):returna-bforiinrange(50):_=my_other_func(i,i+1)# print the times in a tablecall_counter.pretty_print_counts()TheFunctionLoggeris a little different, it logs the arguments and return values of functions. It can be used to log the execution of functions to a file. The decorator for function logger has arguments as well.fromcontemplationimportFunctionLoggerfunction_logger=FunctionLogger()@function_logger.log_function(log_args=True,log_returns=True)defmy_func(a:int,b:int):returna+b_=my_func(1,2)function_logger.pretty_print_logs()# log another function@function_logger.log_function(log_args=True,log_returns=True)defmy_other_func(a:int,b:int):returna-b_=my_other_func(3,4)_=my_func(5,6)foreventinfunction_logger:print(event)Instance IntrospectionsInstance introspections are introspections that are performed on instances of objects. The following instance introspections are provided, they are poised as questions:how_many_of_my_type_exist- how many instances of a type exist given an instancehow_many_of_type_exist- how many instances of a type exist given a typeget_name_in_caller_scope- get the name an instance was assigned to in the scope of the callerget_name_in_all_scope- get the name an instance was assigned to in the scope of the caller and all parent scopesThese introspections are useful for debugging and analysing code. For example, you can usehow_many_of_my_type_existandhow_many_of_type_existto find out how many instances of a type exist given an instance.fromcontemplationimporthow_many_of_my_type_exist,how_many_of_type_existclassMyClass:passmy_instance=MyClass()my_instance_2=MyClass()print(how_many_of_my_type_exist(my_instance))print(how_many_of_type_exist(MyClass))You can useget_name_in_caller_scopean instance was assigned to in the scope of the caller.fromcollectionsimportdefaultdictfromcontemplationimportget_name_in_caller_scopeclassMyClass:passmy_class=MyClass()defmy_func1(obj:object):print(get_name_in_caller_scope(obj))assertget_name_in_caller_scope(obj)=="my_class"my_func1(my_class)dd=defaultdict(int)defmy_func2(obj:object):print(get_name_in_caller_scope(obj))assertget_name_in_caller_scope(obj)=="dd"my_func2(dd)You can useget_name_in_all_scopean instance was assigned to in the scope of the caller and all parent scopes. This is very powerful for tracing how a variable made its way somewhere but can also be a bit confusing.fromcontemplationimportget_name_in_all_scopedeff1(a):print(get_name_in_all_scope(a))returnget_name_in_all_scope(a)deff2(b):returnf1(b)deff3(c):returnf2(c)deff4(d):returnf3(d)names=f4(1)assertnames[3]=="d"assertnames[2]=="c"assertnames[1]=="b"assertnames[0]=="a"Experimental Type IntrospectionsThese introspections are experimental and may change in the future, additional there are caveats to their use and a number of edge cases that may not be covered at the moment.Type introspections are introspections that are performed on types. Currently there the@type_enforced()decorator and theintrospect_typefunction.The@type_enforced()decorator can be used to enforce the type of a function's arguments and return value.This uses the annotations on the functions to check the types of the arguments against what was received.You can toggle between shallow and deep type checking. Shallow will not work with annotations from thetypingmodule however deep will. Deep is the default.Don't use this outside of debugging, deep type checking will check every element of every iterable at every depth every time the function is called. This is slow.fromtypingimportDict,Listfromcontemplation.experimentalimporttype_enforced@type_enforced(deep=True)deftest_func(d:Dict[str,List[int]])->int:s=0forkind:s+=sum(d[k])returns# this works no problemsd={"a":[1,2,3],"b":[4,5,6]}print(test_func(d))# this failsd={"a":[1,2,3],"b":[4,5,"6"]}print(test_func(d))A failed type check will print a detailed specification of the type that was received.TypeError: Argument 'd' for function 'test_func' must be of type typing.Dict[str, typing.List[int]], instead type typing.Dict[str, typing.Union[typing.List[typing.Union[int, str]], typing.List[int]]] was passedYou can also get this type information directly for any object with theintrospect_typefunction:fromcontemplation.experimentalimportintrospect_typemy_dict={"a":[1,2,3],"b":[4,5,6]}print(introspect_type(my_dict))This will printtyping.Dict[str, typing.List[int]].The type introspection occurs recursively for arbitrarily complex objects.e.g.d={"a":[1,2,3],"b":[4,{5},"6"]}print(introspect_type(d))Generating DocumentationDocumentation is located atdocs/index.html.You can generate documentation withpdoc3.pip3 install pdoc3pdoc3 --html --output-dir docs contemplation --force