Metadata-Version: 1.1
Name: ycecream
Version: 1.3.3.14
Summary: ycecream
Home-page: https://github.com/salabim/ycecream
Author: Ruud van der Ham
Author-email: info@salabim.org
License: UNKNOWN
Download-URL: https://github.com/salabim/ycecream
Description: Do you ever use ``print()`` or ``log()`` to debug your code? If so,
        ycecream, or ``y`` for short, will make printing debug information a lot
        sweeter. And on top of that, you get some basic benchmarking
        functionality.
        
        Installation
        ============
        
        Installing ycecream with pip is easy.
        
        ::
        
            $ pip install ycecream
        
        or when you want to upgrade,
        
        ::
        
            $ pip install ycecream --upgrade
        
        Alternatively, ycecream.py can be juist copied into you current work
        directory from GitHub (https://github.com/salabim/ycecream).
        
        No dependencies!
        
        Inspect variables and expressions
        =================================
        
        Have you ever printed variables or expressions to debug your program? If
        you've ever typed something like
        
        ::
        
            print(add2(1000))
        
        or the more thorough
        
        ::
        
            print("add2(1000)", add2(1000)))
        
        or (for Python >= 3.8 only):
        
        ::
        
            print(f"{add2(1000) =}")
        
        then ``y()`` is here to help. With arguments, ``y()`` inspects itself
        and prints both its own arguments and the values of those arguments.
        
        ::
        
            from ycecream import y
        
            def add2(i):
                return i + 2
        
            y(add2(1000))
        
        prints
        
        ::
        
            y| add2(1000): 1002
        
        Similarly,
        
        ::
        
            from ycecream import y
            class X:
                a = 3
            world = {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}
        
            y(world, X.a)
        
        prints
        
        ::
        
            y| world: {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}, X.a: 3
        
        Just give ``y()`` a variable or expression and you're done. Sweet, isn't
        it?
        
        Inspect execution
        =================
        
        Have you ever used ``print()`` to determine which parts of your program
        are executed, and in which order they're executed? For example, if
        you've ever added print statements to debug code like
        
        ::
        
            def add2(i):
                print("enter")
                result = i + 2
                print("exit")
                return result
        
        then ``y()`` helps here, too. Without arguments, ``y()`` inspects itself
        and prints the calling line number and -if applicable- the file name and
        parent function.
        
        ::
        
            from ycecream import y
            def add2(i):
                y()
                result = i + 2
                y()
                return result
            y(add2(1000))
        
        prints something like
        
        ::
        
            y| #3 in add2()
            y| #5 in add2()
            y| add2(1000): 1002
        
        Just call ``y()`` and you're done. Isn't that sweet?
        
        Return Value
        ============
        
        ``y()`` returns its argument(s), so ``y()`` can easily be inserted into
        pre-existing code.
        
        ::
        
            from ycecream import y
            def add2(i):
                return i + 2
            b = y(add2(1000))
            y(b)
        
        prints
        
        ::
        
            y| add2(1000): 1002
            y| b: 1002
        
        Debug entry and exit of function calls
        ======================================
        
        When you apply ``y()`` as a decorator to a function or method, both the
        entry and exit can be tracked. The (keyword) arguments passed will be
        shown and upon return, the return value.
        
        ::
        
            from ycecream import y
            @y()
            def mul(x, y):
                return x * y
                
            print(mul(5, 7))
        
        prints
        
        ::
        
            y| called mul(5, 7)
            y| returned 35 from mul(5, 7) in 0.000006 seconds
            35
        
        It is possible to suppress the print-out of either the enter or the exit
        information with the show\_enter and show\_exit parameters, like:
        
        ::
        
            inport ycecream as y
            @y(show_exit=False)
            def mul(x, y):
                return x * y
                
            print(mul(5, 7))
        
        prints
        
        ::
        
            y| called mul(5, 7)
            35
        
        Note that it is possible to use ``y`` as a decorator without the
        parentheses, like
        
        ::
        
            @y
            def diode(x):
                return 0 if x<0 else x
        
        , but this might not work correctly when the def/class definition spawns
        more than one line. So, always use ``y()`` or ``y(<parameters>)`` when
        used as a decorator.
        
        Benchmarking with ycecream
        ==========================
        
        If you decorate a function or method with y, you will be offered the
        duration between entry and exit (in seconds) as a bonus.
        
        That opens the door to simple benchmarking, like:
        
        ::
        
            from ycecream import y
            import time
        
            @y(show_enter=False,show_line_number=True)
            def do_sort(i):
                n = 10 ** i
                x = sorted(list(range(n)))
                return f"{n:9d}"  
                
            for i in range(8):
                do_sort(i)
        
        the ouput will show the effects of the population size on the sort
        speed:
        
        ::
        
            y| #5 ==> returned '        1' from do_sort(0) in 0.000027 seconds
            y| #5 ==> returned '       10' from do_sort(1) in 0.000060 seconds
            y| #5 ==> returned '      100' from do_sort(2) in 0.000748 seconds
            y| #5 ==> returned '     1000' from do_sort(3) in 0.001897 seconds
            y| #5 ==> returned '    10000' from do_sort(4) in 0.002231 seconds
            y| #5 ==> returned '   100000' from do_sort(5) in 0.024014 seconds
            y| #5 ==> returned '  1000000' from do_sort(6) in 0.257504 seconds
            y| #5 ==> returned ' 10000000' from do_sort(7) in 1.553495 seconds
        
        It is also possible to time any code by using y as a context manager,
        e.g.
        
        ::
        
            with y():
                time.sleep(1)
        
        wil print something like
        
        ::
        
            y| enter
            y| exit in 1.000900 seconds
        
        You can include parameters here as well:
        
        ::
        
            with y(show_context=True, show_time=True):
                time.sleep(1)
        
        will print somethink like:
        
        ::
        
            y| #8 @ 13:20:32.605903 ==> enter
            y| #8 @ 13:20:33.609519 ==> exit in 1.003358 seconds
        
        Finally, to help with timing code, you can request the current delta
        with
        
        ::
        
            y().delta
        
        or (re)set it with
        
        ::
        
            y().delta = 0
        
        So, e.g. to time a section of code:
        
        ::
        
            y.delta = 0
            time.sleep(1)
            duration = y.delta
            y(duration)
        
        might print:
        
        ::
        
            y| duration: 1.0001721999999997
        
        Configuration
        =============
        
        For the configuration, it is important to realize that ``y`` is an
        instance of the ``ycecream._Y`` class, which has a number of
        configuration attributes:
        
        ::
        
            --------------------------------------------------
            attribute           alternative     default
            --------------------------------------------------
            prefix              p               "y| "
            output              o               "stderr"
            serialize                           pprint.pformat
            show_line_number    sln             False
            show_time           st              False
            show_delta          sd              False
            show_enter          se              True
            show_exit           sx              True
            sort_dicts *)       sdi             False
            enabled             e               True
            line_length         ll              80
            compact *)          c               False
            indent              i               1
            depth               de              1000000
            wrap_indent         wi              "     "   
            context_delimiter   cd              " ==> "
            pair_delimiter      pd              ", "
            values_only         vo              False
            return_none         rn              False
            enforce_line_length ell             False
            decorator           d               False
            context_manager     cm              False
            --------------------------------------------------
            *) ignored under Python 2.7
        
        It is perfectly ok to set/get any of these attributes directly.
        
        But, it is also possible to apply configuration directly in the call to
        ``y``: So, it is possible to say
        
        ::
        
            from ycecream import y
            y(12, prefix="==> ")
        
        , which will print
        
        ::
        
            ==> 12
        
        It is also possible to configure y permanently with the configure
        method.
        
        ::
        
            y.configure(prefix="==> ")
            y(12)
        
        will print
        
        ::
        
            ==> 12
        
        It is arguably easier to say:
        
        ::
        
            y.prefix = "==> "
            y(12)
        
        or even
        
        ::
        
            y.p = "==> "
            y(12)
        
        to print
        
        ::
        
            ==> 12
        
        Yet another way to configure y is to get a new instance of y with
        y.new() and the required configuration:
        
        ::
        
            z = y.new(prefix="==> ")
            z(12)
        
        will print
        
        ::
        
            ==> 12
        
        Or, yet another possibility is to clone y (optionally with modified
        attributes):
        
        ::
        
            yd1 = y.clone(show_date=True)
            yd2 = y.clone()
            yd2.configure(show_date=True)
        
        After this ``yd1`` and ``yd2`` will behave similarly (but they are not
        the same!)
        
        prefix / p
        ----------
        
        ::
        
            from ycecream import y
            y('world', prefix='hello -> ')
        
        prints
        
        ::
        
            hello -> 'world'
        
        ``prefix`` can be a function, too.
        
        ::
        
            import time
            from ycecream import y
            def unix_timestamp():
                return f"{int(time.time())} "
            hello = "world"
            y = Y(prefix=unix_timestamp)
            y(hello) 
        
        prints
        
        ::
        
            1613635601 hello: 'world'
        
        output / o
        ----------
        
        This will allow the output to be handled by something else than the
        default (output being written to stderr).
        
        The ``output`` attribute can be
        
        -  a callable that accepts at least one parameter (the text to be
           printed)
        -  a string or Path object that will be used as the filename
        -  a text file that is open for writing/appending
        
        In the example below,
        
        ::
        
            from ycecream import y
            import sys
            y(1, output=print)
            y(2, output=sys.stdout
            with open("test", "a+") as f:
                y(3, output=f)
            y(4, output="")
        
        -  ``y| 1`` will be printed to stdout
        -  ``y| 2`` will be printed to stdout
        -  ``y| 3`` will be appended to the file test
        -  ``y| 4`` will *disappear*
        
        As ``output`` may be any callable, you can even use this to
        automatically log any ``y`` output:
        
        ::
        
            from ycecream import y
            import logging
            logging.basicConfig(level="INFO")
            log = logging.getLogger("demo")
            y.configure(output=log.info)
            a = {1, 2, 3, 4, 5}
            y(a)
            a.remove(4)
            y(a)
        
        will print to stderr:
        
        ::
        
            INFO:demo:y| a: {1, 2, 3, 4, 5}
            INFO:demo:y| a: {1, 2, 3, 5}
        
        Finally, you can specify the following strings:
        
        ::
        
            "stderr"           to print to stderr
            "stdput"           to print to stdout
            "null" or ""       to completely ignore (dummy) output 
            "logging.debug"    to use logging.debug
            "logging.info"     to use logging.info
            "logging.warning"  to use logging.warning
            "logging.error"    to use logging.error
            "logging.critical" to use logging.critical
        
        E.g.
        
        ::
        
            from ycecream import y
            import sys
            y.configure(output="stdout")
        
        to print to stdout.
        
        serialize
        ---------
        
        This will allow to specify how argument values are to be serialized to
        displayable strings. The default is pformat (from pprint), but this can
        be changed to, for example, to handle non-standard datatypes in a custom
        fashion. The serialize function should accept at least one parameter.
        The function can optionally accept the keyword arguments ``width`` and
        ``sort_dicts``, ``compact``, ``indent`` and ``depth``.
        
        ::
        
            from ycecream import y
            def add_len(obj):
                if hasattr(obj, "__len__"):
                    add = f" [len={len(obj)}]"
                else:
                    add = ""
                return f"{repr(obj)}{add}"
        
            l = list(range(7))
            hello = "world"
            y(7, hello, l, serialize=add_len)
        
        prints
        
        ::
        
            y| 7, hello: 'world' [len=5], l: [0, 1, 2, 3, 4, 5, 6] [len=7]
        
        show\_line\_number / sln
        ------------------------
        
        If True, adds the ``y()`` call's line number and possible the filename
        and parent function to ``y()``'s output.
        
        ::
        
            from ycecream import y
            y = Y(show_line_number=True)
            hello="world"
            y(hello)
        
        prints like
        
        ::
        
            y| #4 ==> hello: 'world'
        
        Note that if you call ``y`` without any arguments, the line number is
        always shown, regardless of the status ``show_line_number``.
        
        See below for an explanation of the information provided.
        
        show\_time / st
        ---------------
        
        If True, adds the current time to ``y()``'s output.
        
        ::
        
            from ycecream import y
            y =  Y(show_time=True)
            hello="world"
            y(hello)
        
        prints something like
        
        ::
        
            y| @ 13:01:47.588125 ==> hello: 'world'
        
        show\_delta / sd
        ----------------
        
        If True, adds the number of seconds since the start of the program to
        ``y()``'s output.
        
        ::
        
            from ycecream import y
            import time
            y.configure(show_delta=True)
            allÃ´="monde"
            hallo
            y(hello)
            time.sleep(1)
            y(Ã¢llo)
        
        prints something like
        
        ::
        
            y| delta=0.021002 ==> hello: 'world'
            y| delta=1.053234 ==> Ã¢llo: 'monde'
        
        show\_enter / se
        ----------------
        
        When used as a decorator or context manager, by default, ycecream ouputs
        a line when the decorated the function is called or the context manager
        is entered.
        
        With ``show_enter=False`` this line can be suppressed.
        
        show\_exit / sx
        ---------------
        
        When used as a decorator or context manager, by default, ycecream ouputs
        a line when the decorated the function returned or the context manager
        is exited.
        
        With ``show_exit=False`` this line can be suppressed.
        
        line\_length / ll
        -----------------
        
        This attribute is used to specify the line length (for wrapping). The
        default is 80. Ycecream always tries to keep all output on one line, but
        if it can't it will wrap:
        
        ::
        
            d = dict(a1=1,a2=dict(a=1,b=1,c=3),a3=list(range(10)))
            y(d)
            y(d, line_length=120)
        
        prints
        
        ::
        
            y|
                d:
                    {'a1': 1,
                     'a2': {'a': 1, 'b': 1, 'c': 3},
                     'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
            y| d: {'a1': 1, 'a2': {'a': 1, 'b': 1, 'c': 3}, 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
        
        compact / c
        -----------
        
        This attribute is used to specify the compact parameter for ``pformat``
        (see the pprint documentation for details). ``compact`` is False by
        default.
        
        ::
        
            a = 9 * ["0123456789"]
            y(a)
            y(a, compact=True)
        
        prints
        
        ::
        
            y|
                a:
                    ['0123456789',
                     '0123456789',
                     '0123456789',
                     '0123456789',
                     '0123456789',
                     '0123456789',
                     '0123456789',
                     '0123456789',
                     '0123456789']
            y|
                a:
                    ['0123456789', '0123456789', '0123456789', '0123456789', '0123456789',
                     '0123456789', '0123456789', '0123456789', '0123456789']
        
        Note that ``compact`` is ignored under Python 2.7.
        
        indent / i
        ----------
        
        This attribute is used to specify the indent parameter for ``pformat``
        (see the pprint documentation for details). ``indent`` is 1 by default.
        
        ::
        
            s = "01234567890012345678900123456789001234567890"
            y( [s, [s]])
            y( [s, [s]], indent=4)
        
        prints
        
        ::
        
            y|
                [s, [s]]:
                    ['01234567890012345678900123456789001234567890',
                     ['01234567890012345678900123456789001234567890']]
            y|
                [s, [s]]:
                    [   '01234567890012345678900123456789001234567890',
                        ['01234567890012345678900123456789001234567890']]
        
        depth / de
        ----------
        
        This attribute is used to specify the depth parameter for ``pformat``
        (see the pprint documentation for details). ``depth`` is ``1000000`` by
        default.
        
        ::
        
            s = "01234567890012345678900123456789001234567890"
            y([s,[s,[s,[s,s]]]])
            y([s,[s,[s,[s,s]]]], depth=3)
        
        prints
        
        ::
        
            y|
                [s,[s,[s,[s,s]]]]:
                    ['01234567890012345678900123456789001234567890',
                     ['01234567890012345678900123456789001234567890',
                      ['01234567890012345678900123456789001234567890',
                       ['01234567890012345678900123456789001234567890',
                        '01234567890012345678900123456789001234567890']]]]
            y|
                [s,[s,[s,[s,s]]]]:
                    ['01234567890012345678900123456789001234567890',
                     ['01234567890012345678900123456789001234567890',
                      ['01234567890012345678900123456789001234567890', [...]]]]
        
        wrap\_indent / wi
        -----------------
        
        This specifies the indent string if the output does not fit in the
        line\_length (has to be wrapped). Rather than a string, wrap\_indent can
        be also be an integer, in which case the wrap\_indent will be that
        amount of blanks. The default is 4 blanks.
        
        E.g.
        
        ::
        
            d = dict(a1=1,a2=dict(a=1,b=1,c=3),a3=list(range(10)))
            y(d, wrap_indent="  ")
            y(d, wrap_indent="....")
            y(d, wrap_indent=2)
        
        prints
        
        ::
        
            y|
              d:
                {'a1': 1,
                 'a2': {'a': 1, 'b': 1, 'c': 3},
                 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
            y|
            ....d:
            ........{'a1': 1,
            ........ 'a2': {'a': 1, 'b': 1, 'c': 3},
            ........ 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
            y|
              d:
                {'a1': 1,
                 'a2': {'a': 1, 'b': 1, 'c': 3},
                 'a3': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
        
        enabled / e
        -----------
        
        Can be used to disable the output:
        
        ::
        
            from ycecream import y
        
            y.configure(enabled=False)
            s = 'the world is '
            y(s + 'perfect.')
            y.configure(enabled=True)
            y(s + 'on fire.')
        
        prints
        
        ::
        
            y| s + 'on fire.': 'the world is on fire.'
        
        and nothing about a perfect world.
        
        sort\_dicts / sdi
        -----------------
        
        By default, ycecream does not sort dicts (printed by pprint). However,
        it is possible to get the default pprint behaviour (i.e. sorting dicts)
        with the sorted\_dicts attribute:
        
        ::
        
            world = {"EN": "world", "NL": "wereld", "FR": "monde", "DE": "Welt"}
            y(world))
            s1 = y(world, sort_dicts=False)
            s2 = y(world, sort_dicts=True)
        
        prints
        
        ::
        
            y| world: {'EN': 'world', 'NL': 'wereld', 'FR': 'monde', 'DE': 'Welt'}
            y| world: {'EN': 'world', 'NL': 'wereld', 'FR': 'monde', 'DE': 'Welt'}
            y| world: {'DE': 'Welt', 'EN': 'world', 'FR': 'monde', 'NL': 'wereld'}
        
        Note that ``sort_dicts`` is ignored under Python 2.7, i.e. dicts are
        always sorted.
        
        context\_delimiter / cd
        -----------------------
        
        By default the line\_number, time and/or delta are followed by ``==>``.
        It is possible to change this with the attribute ``context_delimiter``:
        
        ::
        
            a="abcd"
            y(a)
            y(a, show_time=True, context_delimiter = ' \u279c ')
        
        prints:
        
        ::
        
            y| @ 12:56:11.341650 ==> a: 'abcd'
            y| @ 12:56:11.485567 âžœ a: 'abcd'
        
        pair\_delimiter / pd
        --------------------
        
        By default, pairs (on one line) are separated by ``,``. It is possible
        to change this with the attribute ``pair_delimiter``:
        
        ::
        
            a="abcd"
            b=1
            c=1000
            d=list("ycecream")
            y(a,(b,c),d)
            y(a,(b,c),d, pair_delimiter=" | ")
        
        prints
        
        ::
        
            y| a: 'abcd', (b,c): (1, 1000), d: ['y', 'c', 'e', 'c', 'r', 'e', 'a', 'm']
            y| a: 'abcd' | (b,c): (1, 1000) | d: ['y', 'c', 'e', 'c', 'r', 'e', 'a', 'm']
        
        values\_only / vo
        -----------------
        
        If False (the default), both the left-hand side (if possible) and the
        value will be printed. If True, the left\_hand side will be suppressed:
        
        ::
        
            hello = "world"
            y(hello, 2 * hello)
            y(hello, 2 * hello, values_only=True)
        
        prints
        
        ::
        
            y| hello: 'world', 2 * hello = 'worldworld'
            y| 'world', 'worldworld'
        
        The values=True version of y can be seen as a supercharged print/pprint.
        
        return\_none / rn
        -----------------
        
        Normally, ``y()``\ returns the values passed directly, which is usually
        fine. However, when used in a notebook or REPL, that value will be
        shown, and that can be annoying. Therefore, if ``return_none``\ is True,
        ``y()``\ will return None and thus not show anything.
        
        ::
        
            a = 3
            print(y(a, a + 1))
            y.configure(return_none=True)
            print(y(a, a + 1))
        
        prints
        
        ::
        
            y| (3, 4)
            (3, 4)
            y| (3, 4)
            None
        
        enforce\_line\_length / ell
        ---------------------------
        
        If enforce\_line\_length is True, all output lines are explicitely
        truncated to the given line\_length, even those that are not truncated
        by pformat.
        
        decorator / d
        -------------
        
        Normally, an ycecream instance can be used as to show values, as a
        decorator and as a context manager.
        
        However, when used from a REPL the usage as a decorator can't be
        detected properly and in that case, specify ``decorator=True``. E.g.
        
        ::
        
            >>>@y(decorator=True)
            >>>def add2(x):
            >>>    return x + 2
            >>>print(add2(10))
            y| called add2(10)
            y| returned 12 from add2(10) in 0.000548 seconds
            12
        
        The ``decorator`` attribute is also required when using ``y()`` as a
        decorator witb *fast disabling* (see below).
        
        ::
        
                |y.enabled([])
                |@y()
                |def add2(x):
                |    return x + 2
        
        would fail with\ ``TypeError: 'NoneType' object is not callable``, but
        
        ::
        
                |y.enabled([])
                |@y(decorator=True)
                |def add2(x):
                |    return x + 2
        
        will run correctly.
        
        context\_manager / cm
        ---------------------
        
        Normally, an ycecream instance can be used as to show values, as a
        decorator and as a context manager.
        
        However, when used from a REPL the usage as a context manager can't be
        detected properly and in that case, specify ``context_manager=True``.
        E.g.
        
        ::
        
            >>>with y(context_manager=True)
            >>>    pass
            y| enter
            y| exit in 0.008644 seconds
        
        The ``context_manager`` attribute is also required when using ``y():``
        as a context manager witb *fast disabling* (see below).
        
        ::
        
                |y.enabled([])
                |with y:
                |    pass
        
        would fail with ``AttributeError: __enter__``, but
        
        ::
        
                |y.enabled([])
                |with y(context_manager=True):
                |    pass
        
        will run correctly.
        
        Return a string instead of sending to output
        ============================================
        
        ``y(*args, as_str=True)`` is like ``y(*args)`` but the output is
        returned as a string instead of written to output.
        
        ::
        
            from ycecream import y
            hello = "world"
            s = y(hello, as_str=True)
            print(s, end="")
        
        prints
        
        ::
        
            y| hello: 'world'
        
        Disabling ycecream's output
        ===========================
        
        ::
        
            from ycecream import y
            yd = y.fork(show_delta=True)
            y(1)
            yd(2)
            y.enabled = False
            y(3)
            yd(4)
            y.enabled = True
            y(5)
            yd(6)
            print(y.enabled)
        
        prints
        
        ::
        
            y| 1
            y| delta=0.011826 ==> 2
            y| 5
            y| delta=0.044893 ==> 6
            True
        
        Of course ``y()`` continues to return its arguments when disabled, of
        course.
        
        Speeding up disabled ycecream
        -----------------------------
        
        When output is disabled, either via ``y.configure(enbabled=False)`` or
        ``ycecream.enable = False``, ycecream still has to check for usage as a
        decorator or context manager, which can be rather time consuming.
        
        In order to speed up a program with disabled ycecream calls, it is
        possible to specify ``y.configure(enabled=[])``, in which case ``y``
        will always just return the given arguments. If ycecream is disabled
        this way, usage as a ``@y()`` decorator or as a ``with y():`` context
        manager will raise a runtime error, though. The ``@y`` decorator without
        parentheses will not raise any exception, though.
        
        To use ``y`` as a decorator and still want *fast disabling*:
        
        ::
        
            y.configure(enabled=[])
            @y(decorator=True):
            def add2(x):
                 return x + 2
            x34 = add2(30)
        
        And, similarly, to use ``y`` as a context manager combined with *fast
        disabling*:
        
        ::
        
            y.configure(enabled=[])
            with @y(context_manager=True):
                pass
        
        Note that calls with ``as_str=True`` will not be affected at all by the
        enabled flag.
        
        The table below shows it all.
        
        ::
        
            ---------------------------------------------------------------------
                                     enabled=True   enabled=False      enabled=[]
            ---------------------------------------------------------------------
            execution speed                normal          normal            fast     
            y()                            normal       no output       no output
            @y                             normal       no output       no output
            y(decorator=True)              normal       no output       no output
            y(context_manager=True)        normal       no output       no output
            @y()                           normal       no output       TypeError
            with y():                      normal       no output  AttributeError
            y(as_str=True)                 normal          normal          normal
            ---------------------------------------------------------------------
        
        Interpreting the line number information
        ========================================
        
        
Keywords: debugging,utility,tool,benchmarking
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: Other Audience
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
