A small package that provides a number of powerful introspection tools
Project description
🤔 Contemplation
Novel 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 contemplation
Table of Contents
Documentation
Introspections
There are three categories of introspection provided in this package:
- Execution Introspections
- Instance Introspections
- Type Introspections
Execution 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 Introspections
The 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 it
These 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 an ExecutionTimer
instance.
import time
from contemplation import ExecutionTimer
execution_timer = ExecutionTimer()
@execution_timer.time_execution
def my_func(a: int, b: int):
time.sleep(0.01)
return a + b
for i in range(100):
_ = my_func(i, i + 1)
# get the total execution time by function
print(execution_timer.get_execution_time(my_func))
# or by name
print(execution_timer.get_execution_time("my_func"))
# log another function
@execution_timer.time_execution
def my_other_func(a: int, b: int):
time.sleep(0.005)
return a - b
for i in range(100):
_ = my_other_func(i, i + 1)
# print the times in a table
execution_timer.pretty_print_times()
The same API applies to the CallCounter
:
import time
from contemplation import CallCounter
call_counter = CallCounter()
@call_counter.count_calls
def my_func(a: int, b: int):
return a + b
for i in range(100):
_ = my_func(i, i + 1)
# get the total execution time by function
print(call_counter.get_counts(my_func))
# or by name
print(call_counter.get_counts("my_func"))
# log another function
@call_counter.count_calls
def my_other_func(a: int, b: int):
return a - b
for i in range(50):
_ = my_other_func(i, i + 1)
# print the times in a table
call_counter.pretty_print_counts()
The FunctionLogger
is 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.
from contemplation import FunctionLogger
function_logger = FunctionLogger()
@function_logger.log_function(log_args=True, log_returns=True)
def my_func(a: int, b: int):
return a + b
_ = my_func(1, 2)
function_logger.pretty_print_logs()
# log another function
@function_logger.log_function(log_args=True, log_returns=True)
def my_other_func(a: int, b: int):
return a - b
_ = my_other_func(3, 4)
_ = my_func(5, 6)
for event in function_logger:
print(event)
Instance Introspections
Instance 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 scopes
These introspections are useful for debugging and analysing code. For example, you can use how_many_of_my_type_exist
and how_many_of_type_exist
to find out how many instances of a type exist given an instance.
from contemplation import how_many_of_my_type_exist, how_many_of_type_exist
class MyClass:
pass
my_instance = MyClass()
my_instance_2 = MyClass()
print(how_many_of_my_type_exist(my_instance))
print(how_many_of_type_exist(MyClass))
You can use get_name_in_caller_scope
an instance was assigned to in the scope of the caller.
from collections import defaultdict
from contemplation import get_name_in_caller_scope
class MyClass:
pass
my_class = MyClass()
def my_func1(obj: object):
print(get_name_in_caller_scope(obj))
assert get_name_in_caller_scope(obj) == "my_class"
my_func1(my_class)
dd = defaultdict(int)
def my_func2(obj: object):
print(get_name_in_caller_scope(obj))
assert get_name_in_caller_scope(obj) == "dd"
my_func2(dd)
You can use get_name_in_all_scope
an 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.
from contemplation import get_name_in_all_scope
def f1(a):
print(get_name_in_all_scope(a))
return get_name_in_all_scope(a)
def f2(b):
return f1(b)
def f3(c):
return f2(c)
def f4(d):
return f3(d)
names = f4(1)
assert names[3] == "d"
assert names[2] == "c"
assert names[1] == "b"
assert names[0] == "a"
Experimental Type Introspections
These 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 the introspect_type
function.
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 the typing
module 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.
from typing import Dict, List
from contemplation.experimental import type_enforced
@type_enforced(deep=True)
def test_func(d: Dict[str, List[int]]) -> int:
s = 0
for k in d:
s += sum(d[k])
return s
# this works no problems
d = {"a": [1, 2, 3], "b": [4, 5, 6]}
print(test_func(d))
# this fails
d = {"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 passed
You can also get this type information directly for any object with the introspect_type
function:
from contemplation.experimental import introspect_type
my_dict = {"a": [1, 2, 3], "b": [4, 5, 6]}
print(introspect_type(my_dict))
This will print typing.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 Documentation
Documentation is located at docs/index.html
.
You can generate documentation with pdoc3
.
pip3 install pdoc3
pdoc3 --html --output-dir docs contemplation --force
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file contemplation-0.0.2.tar.gz
.
File metadata
- Download URL: contemplation-0.0.2.tar.gz
- Upload date:
- Size: 14.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 966686b1e417b71b8499faed3e117644b8fbe5142164d63fa279210ada7b62fc |
|
MD5 | 49a6e5335e6821953c030082f9e0807b |
|
BLAKE2b-256 | 7613551d580e1b0f5719c52d96ab30baf64ce2627dd842b41693f91c1d09ab5a |
File details
Details for the file contemplation-0.0.2-py3-none-any.whl
.
File metadata
- Download URL: contemplation-0.0.2-py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f9f202b7e1d0d08caea9a2f4bdfe19725e9ecac73c14ede36c32a6c413f333d7 |
|
MD5 | a8247c9a3f90c935dee6721912067f5f |
|
BLAKE2b-256 | 24bc103ab9e7fd896905dbd0ded720ea2ba5b63fffe518ae4b294806e98a3637 |