samuelcolvin / python-devtools

Dev tools for python
https://python-devtools.helpmanual.io/
MIT License
985 stars 47 forks source link

Suggestion: adding parameter to configure the frame depth #97

Open aroberge opened 2 years ago

aroberge commented 2 years ago

First of all: I just discover devtools and I think it is a fantastic tool. I definitely plan to use it instead of my simple minded debugging helper.

Currently, debug() is assumed to be called explicitly in a given module, with a hard-coded value for the frame in which it looks for information:

    def _process(self, args, kwargs) -> DebugOutput:
        """
        BEWARE: this must be called from a function exactly 2 levels below the top of the stack.
        """
        # HELP: any errors other than ValueError from _getframe? If so please submit an issue
        try:
            call_frame: 'FrameType' = sys._getframe(2)
        except ValueError:
            ...

I would find it useful if the constant 2 could be a configurable parameter. For example, I could then define custom functions like the following:

def handle_problem(*args, **kwargs):
    if dev_version:
        debug = Debug(additional_frame_depth=1)
        debug(*args, **kwargs)
    else:
        print("Internal problem: please report this case.")

Obviously, I could currently do this by subclassing Debug, but I think that this might be a useful feature to have. I understand that the idea is to use devtools.debug during development and one would normally remove all traces of it for releases. However, I find it useful in one of my projects (friendly-traceback) to leave in place such debugging functions such that end-users are never exposed to tracebacks generated by my own project while still being able to cope with the unexpected.

alexmojaki commented 2 years ago

Rather than adding a numerical argument, I think there should be a decorator that indicates that a function is a wrapper around debug:

@no_debug
def handle_problem

Then when _process is looking for the correct frame it should skip any frame whose f_code was recorded by @no_debug. This means there can also easily be multiple layers of wrappers that don't know about each other, e.g. another helper function calling handle_problem. I suggested the same pattern here: https://github.com/samuelcolvin/pydantic/issues/2678#issuecomment-822685329