toggling decorators

后端 未结 7 1849
伪装坚强ぢ
伪装坚强ぢ 2021-01-04 18:37

What\'s the best way to toggle decorators on and off, without actually going to each decoration and commenting it out? Say you have a benchmarking decorator:



        
7条回答
  •  隐瞒了意图╮
    2021-01-04 18:53

    Here is what I finally came up with for per-module toggling. It uses @nneonneo's suggestion as a starting point.

    Random modules use decorators as normal, no knowledge of toggling.

    foopkg.py:

    from toggledeco import benchmark
    
    @benchmark
    def foo():
        print("function in foopkg")
    

    barpkg.py:

    from toggledeco import benchmark
    
    @benchmark
    def bar():
        print("function in barpkg")
    

    The decorator module itself maintains a set of function references for all decorators that have been disabled, and each decorator checks for its existence in this set. If so, it just returns the raw function (no decorator). By default the set is empty (everything enabled).

    toggledeco.py:

    import functools
    
    _disabled = set()
    def disable(func):
        _disabled.add(func)
    def enable(func):
        _disabled.discard(func)
    
    def benchmark(func):
        if benchmark in _disabled:
            return func
        @functools.wraps(func)
        def deco(*args,**kwargs):
            print("--> benchmarking %s(%s,%s)" % (func.__name__,args,kwargs))
            ret = func(*args,**kwargs)
            print("<-- done")
        return deco
    

    The main program can toggle individual decorators on and off during imports:

    from toggledeco import benchmark, disable, enable
    
    disable(benchmark) # no benchmarks...
    import foopkg
    
    enable(benchmark) # until they are enabled again
    import barpkg
    
    foopkg.foo() # no benchmarking 
    barpkg.bar() # yes benchmarking
    
    reload(foopkg)
    foopkg.foo() # now with benchmarking
    

    Output:

    function in foopkg
    --> benchmarking bar((),{})
    function in barpkg
    <-- done
    --> benchmarking foo((),{})
    function in foopkg
    <-- done
    

    This has the added bug/feature that enabling/disabling will trickle down to any submodules imported from modules imported in the main function.

    EDIT:

    Here's class suggested by @nneonneo. In order to use it, the decorator must be called as a function ( @benchmark(), not @benchmark ).

    class benchmark:
        disabled = False
    
        @classmethod
        def enable(cls):
            cls.disabled = False
    
        @classmethod
        def disable(cls):
            cls.disabled = True
    
        def __call__(cls,func):
            if cls.disabled:
                return func
            @functools.wraps(func)
            def deco(*args,**kwargs):
                print("--> benchmarking %s(%s,%s)" % (func.__name__,args,kwargs))
                ret = func(*args,**kwargs)
                print("<-- done")
            return deco
    

提交回复
热议问题