{"id":13814204,"url":"https://github.com/Twangist/log_calls","last_synced_at":"2025-05-15T03:33:14.188Z","repository":{"id":22915381,"uuid":"26264205","full_name":"Twangist/log_calls","owner":"Twangist","description":"Debugging and profiling decorator that prints or logs \ncaller name(s), args+values, execution time, and more.\nEliminates reams of boilerplate code.","archived":false,"fork":false,"pushed_at":"2017-01-23T13:07:45.000Z","size":1179,"stargazers_count":61,"open_issues_count":3,"forks_count":4,"subscribers_count":6,"default_branch":"develop","last_synced_at":"2024-04-11T23:54:28.759Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Twangist.png","metadata":{"files":{"readme":"readme.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-06T10:02:57.000Z","updated_at":"2023-01-28T16:05:17.000Z","dependencies_parsed_at":"2022-08-05T20:16:21.694Z","dependency_job_id":null,"html_url":"https://github.com/Twangist/log_calls","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Twangist%2Flog_calls","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Twangist%2Flog_calls/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Twangist%2Flog_calls/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Twangist%2Flog_calls/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Twangist","download_url":"https://codeload.github.com/Twangist/log_calls/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225326417,"owners_count":17456937,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T04:01:46.930Z","updated_at":"2024-11-19T09:30:29.754Z","avatar_url":"https://github.com/Twangist.png","language":"Python","funding_links":["http://www.paypal.me/logcalls"],"categories":["Python"],"sub_categories":[],"readme":".. log_calls Readme documentation master file, created by\n   sphinx-quickstart on Tue Feb  9 05:14:29 2016.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\n.. toctree::\n    :hidden:\n    :maxdepth: 1\n    :titlesonly:\n\n\n.. role:: raw-html(raw)\n   :format: html\n\n.. |br| raw:: html\n\n   \u003cbr /\u003e\n\n.. |release| replace:: 0.3.2\n\n\n\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n`log_calls` — A Decorator for Debugging and Profiling\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nReadme (release |release|)\n############################################################\n\n`log_calls` is a Python 3.3+ decorator that can print a lot of useful information\nabout calls to decorated functions, methods and properties. The decorator can\nwrite to ``stdout``, to another stream or file, or to a logger. `log_calls`\nprovides methods for writing your own debug messages, and for easily \"dumping\"\nvariables and expressions paired with their values. It can decorate individual\nfunctions, methods and properties; but it can also programmatically decorate\ncallable members of entire classes and class hierarchies, even of entire modules,\nwith just a single line — which can greatly expedite learning a new codebase.\n\nIn short, `log_calls` can save you from writing, rewriting, copying, pasting and\ntweaking a lot of *ad hoc*, debug-only, boilerplate code — and it can keep *your*\ncodebase free of that clutter.\n\nFor each call to a decorated function or method, `log_calls` can show you:\n\n* the caller (in fact, the complete call chain back to another `log_calls`-decorated caller,\n  so there are no gaps in chains displayed)\n* the arguments passed to the function or method, and any default values used\n* nesting of calls, using indentation\n* the number of the call (whether it's the 1\\ :superscript:`st` call, the 2\\ :superscript:`nd`,\n  the 103\\ :superscript:`rd`, ...)\n* the return value\n* the time it took to execute\n* and more!\n\nThese and other features are optional and configurable settings, which can be specified\nfor each decorated callable via keyword parameters, as well as *en masse* for a group of\ncallables all sharing the same settings. You can examine and change these settings\non the fly using attributes with the same names as the keywords, or using a dict-like\ninterface whose keys are the keywords.\n\n`log_calls` can also collect profiling data and statistics, accessible at runtime, such as:\n\n* the number of calls to a function\n* total time taken by the function\n* the function's entire call history (arguments, time elapsed, return values, callers,\n  and more), available as text in CSV format and, if `Pandas \u003chttp://pandas.pydata.org\u003e`_\n  is installed, as a `DataFrame \u003chttp://pandas.pydata.org/pandas-docs/stable/dsintro.html#dataframe\u003e`_.\n\nThe package contains two other decorators:\n\n* `record_history`, a stripped-down version of `log_calls`,\n  only collects call history and statistics, and outputs no messages;\n* `used_unused_keywords` lets a function or method easily determine, per-call,\n  which of its keyword parameters were actually supplied by the caller,\n  and which received their default values.\n\nThis document introduces `log_calls` and shows you a few ways to use it. To learn more,\nsee `the complete documentation  \u003chttp://www.pythonhosted.org/log_calls/index.html\u003e`_\nfor the definitive, detailed account.\n\nThe test suites in ``log_calls/tests/``, which provide 96+% coverage, contain\nmany additional examples, with commentary.\n\n--------------------------------------------------------------------\n\n.. _requirements:\n\nRequirements\n##################\n\nThe *log_calls* package has no dependencies — it requires no other packages.\nAll it requires is a standard distribution of Python 3.3 or higher (Python 3.4+ recommended).\nThe package won't install on earlier versions.\n\n.. _installation:\n\nInstallation\n##################\n\nInstalling `log_calls` is as simple as running the command\n\n       ``$ pip install log_calls``\n\nto install `log_calls` from PyPI (the Python Package Index). Here,\n``$`` indicates your command prompt, whatever it may be.\n\nThe complete distribution of `log_calls` (available as a  ``tar.gz`` or a ``zip``\nfrom PyPI or github) contains three subdirectories: ``log_calls``, the package\nproper; ``docs``, the documentation source files; and ``tests``, mentioned above.\nThese last two subdirectories are *not* installed by pip, so to obtain those\nfiles you'll have to download an archive and unpack the archive to a fresh directory.\nYou can then install `log_calls` by changing to that directory and running\nthe following command:\n\n        ``$ python3 setup.py install``\n\n\nWhichever method you choose, ideally you'll install `log_calls` in a virtual\nenvironment (a *virtualenv*).\nIt's easy to set up a virtual environment using tools included in the standard\ndistribution: in Python 3.3 - 3.5, use\n`pyvenv \u003chttps://docs.python.org/3/using/scripts.html?highlight=pyvenv#pyvenv-creating-virtual-environments\u003e`_\n; in Python 3.6, which deprecates ``pyvenv``, use\n\n       ``$ python3.6 -m venv /path/to/virtualenv``\n\nas per `the relevant docs \u003chttps://docs.python.org/3/library/venv.html?highlight=venv#creating-virtual-environments\u003e`_.\n\n--------------------------------------------------------------------\n\n.. _quickstart:\n\nQuick Start\n############\n\n.. _Basic-usage:\n\nBasic usage\n==================================================\n\nFirst, let's import the `log_calls` decorator from the package of the same name:\n\n    \u003e\u003e\u003e from log_calls import log_calls\n\nIn code, ``log_calls`` now refers to the decorator, a class (an object of type ``type``),\nand not to the module:\n\n    \u003e\u003e\u003e type(log_calls)\n    type\n\nThe decorator has many options, and thus can take many parameters,\nbut let's first see the simplest examples possible, using no parameters at all.\n\n.. _quickstart-functions:\n\nDecorating functions\n---------------------\n\nIf you decorate a function with `log_calls`, each call to the function is generally\npreceded and followed by some reportage. The decorator first writes messages announcing\nentry to the function and what arguments it has received; the decorator calls the function\nwith those arguments, and the function executes; upon its return, the decorator finishes up\nand announces the return of the function:\n\n    \u003e\u003e\u003e @log_calls()\n    ... def f(a, b, c):\n    ...     print(\"--- Hi from f\")\n    \u003e\u003e\u003e f(1, 2, 3)\n    f \u003c== called by \u003cmodule\u003e\n        arguments: a=1, b=2, c=3\n    --- Hi from f\n    f ==\u003e returning to \u003cmodule\u003e\n\nAdding another decorated function to the call chain presents useful information too. Here,\n``g`` calls the decorated ``f`` above. Observe that (by default) the `log_calls` output for the\nnested call to ``f`` is indented to align with the inner lines of the `log_calls` output for ``g``:\n\n    \u003e\u003e\u003e @log_calls()\n    ... def g(n):\n    ...     print(\"*** Hi from g\")\n    ...     f(n, 2*n, 3*n)\n    ...     print(\"*** Bye from g\")\n    \u003e\u003e\u003e g(3)\n    g \u003c== called by \u003cmodule\u003e\n        arguments: n=3\n    *** Hi from g\n        f \u003c== called by g\n            arguments: a=3, b=6, c=9\n    --- Hi from f\n        f ==\u003e returning to g\n    *** Bye from g\n    g ==\u003e returning to \u003cmodule\u003e\n\n`log_calls` gives informative output even when call chains include undecorated functions.\nIn the next example, a decorated function ``h`` calls an undecorated ``g2``, which calls\nan undecorated ``g1``, which, finally, calls our original decorated ``f``:\n\n    \u003e\u003e\u003e def g1(n): f(n, 2*n, 3*n)\n    \u003e\u003e\u003e def g2(n): g1(n)\n    \u003e\u003e\u003e @log_calls()\n    ... def h(x, y): g2(x+y)\n\nNow let's call ``h``:\n\n    \u003e\u003e\u003e h(2, 3)\n    h \u003c== called by \u003cmodule\u003e\n        arguments: x=2, y=3\n        f \u003c== called by g1 \u003c== g2 \u003c== h\n            arguments: a=5, b=10, c=15\n    --- Hi from f\n        f ==\u003e returning to g1 ==\u003e g2 ==\u003e h\n    h ==\u003e returning to \u003cmodule\u003e\n\nNotice that when writing entry and exit messages for ``f``, `log_calls` displays\nthe entire active call chain *back to the nearest decorated function*, so that there\naren't \"gaps\" in the chain of functions it reports on. If it didn't do this, we'd\nsee only ``f \u003c== called by g1``, and then ``f ==\u003e returning to g1`` followed by\n``h ==\u003e returning to \u003cmodule\u003e``, which wouldn't tell us the whole story about how\ncontrol reached ``g1`` from ``h``.\n\nSee the `Call Chains \u003chttp://www.pythonhosted.org/log_calls/call_chains.html\u003e`_\nchapter of the documentation contains for examples and finer points.\n\n\n.. _quickstart-methods:\n\nDecorating methods\n-------------------\n\nSimilarly, you can decorate methods (and properties) within a class:\n\n    \u003e\u003e\u003e class A():\n    ...     def __init__(self, n):\n    ...         self.n = n\n    ...\n    ...     @log_calls()\n    ...     def ntimes(self, m):\n    ...         return self.n * m\n\nOnly the ``ntimes`` method is decorated:\n\n    \u003e\u003e\u003e a = A(3)            # __init__ called\n    \u003e\u003e\u003e a.ntimes(4)                 # doctest: +ELLIPSIS\n    A.ntimes \u003c== called by \u003cmodule\u003e\n        arguments: self=\u003c__main__.A object at 0x...\u003e, m=4\n    A.ntimes ==\u003e returning to \u003cmodule\u003e\n    12\n\n---------------------------------------------------------------------------------------------\n\n.. _quickstart-classes:\n\nDecorating classes\n==================================================\n\nTo decorate all methods and properties of a class, simply decorate the class itself:\n\n    \u003e\u003e\u003e @log_calls()\n    ... class C():\n    ...     def __init__(self, n):\n    ...         self.n = n if n \u003e= 0 else -n\n    ...\n    ...     @staticmethod\n    ...     def revint(x): return int(str(x)[::-1])\n    ...\n    ...     @property\n    ...     def revn(self): return self.revint(self.n)\n\nAll methods of ``C`` are now decorated. Creating an instance logs the call to ``__init__``:\n\n    \u003e\u003e\u003e c = C(123)                    # doctest: +ELLIPSIS\n    C.__init__ \u003c== called by \u003cmodule\u003e\n        arguments: self=\u003c__main__.C object at 0x...\u003e, n=123\n    C.__init__ ==\u003e returning to \u003cmodule\u003e\n\nAccessing its ``revn`` property calls the staticmethod ``revint``, and both calls are logged:\n\n    \u003e\u003e\u003e c.revn                        # doctest: +ELLIPSIS\n    C.revn \u003c== called by \u003cmodule\u003e\n        arguments: self=\u003c__main__.C object at 0x...\u003e\n        C.revint \u003c== called by C.revn\n            arguments: x=123\n        C.revint ==\u003e returning to C.revn\n    C.revn ==\u003e returning to \u003cmodule\u003e\n    321\n\nIf you want to decorate only some of the methods of a class, you *don't* have to\nindividually decorate all and only all the ones you want: the ``only`` and ``omit``\nkeyword parameters to the class decorator let you concisely specify which methods\nwill and won't be decorated. The following section introduces ``omit``.\n\nDecorating *most* methods, overriding the settings of one method\n----------------------------------------------------------------------\n\nSuppose you have a class ``D`` that's just like ``C`` above, but adds a ``double()`` method.\n(For the sake of example, never mind that in practice you might subclass ``C``.)\nSuppose you want to decorate all callables in ``D`` *except* ``revint``,\nand furthermore, you want `log_calls` to report values returned by the property getter\n``revn``. Here's how to do it:\n\n    \u003e\u003e\u003e @log_calls(omit='revint')\n    ... class D():\n    ...     def __init__(self, n):\n    ...         self.n = n if n \u003e= 0 else -n\n    ...\n    ...     @staticmethod\n    ...     def revint(x): return int(str(x)[::-1])\n    ...\n    ...     def double(self): return self.n + self.n\n    ...\n    ...     @property\n    \u003e\u003e\u003e     @log_calls(log_retval=True)\n    ...     def revn(self): return self.revint(self.n)\n\nBy default, `log_calls` does *not* display return values, and the outer, class-level\ndecorator uses that default. The explicit decorator of ``revn`` overrides that,\nspecifying the desired setting. Note that ``@log_calls`` follows ``@property``:\nin general, when decorating a callable in a class, ``@log_calls``\nshould come *after* any ``@property``, ``@classmethod`` or ``@staticmethod`` decorator.\n\nLet's see this class in action:\n\n    \u003e\u003e\u003e d = D(71)                                           # doctest: +ELLIPSIS\n    D.__init__ \u003c== called by \u003cmodule\u003e\n        arguments: self=\u003c__main__.D object at 0x...\u003e, n=71\n    D.__init__ ==\u003e returning to \u003cmodule\u003e\n\nThe return value of ``d.double()`` is *not* logged:\n\n    \u003e\u003e\u003e d.double()                                          # doctest: +ELLIPSIS\n    D.double \u003c== called by \u003cmodule\u003e\n        arguments: self=\u003c__main__.D object at 0x...\u003e\n    D.double ==\u003e returning to \u003cmodule\u003e\n\nHowever, the return value of ``revn`` *is* logged, and ``revint`` has *not* been decorated:\n\n    \u003e\u003e\u003e print('~~~\\\\nMy favorite number plus 3 is', d.revn + 3)   # doctest: +ELLIPSIS\n    D.revn \u003c== called by \u003cmodule\u003e\n        arguments: self=\u003c__main__.D object at 0x...\u003e\n        D.revn return value: 17\n    D.revn ==\u003e returning to \u003cmodule\u003e\n    ~~~\n    My favorite number plus 3 is 20\n\nFor more information\n----------------------\n\nThe chapter `Decorating Classes \u003chttp://www.pythonhosted.org/log_calls/decorating_classes.html\u003e`_\ncovers that subject thoroughly — basics, details,\nsubtleties and techniques.\nIn particular, ``only`` and ``omit`` are documented there, in the\nsection `the omit and only keyword parameters  \u003chttp://www.pythonhosted.org/log_calls/decorating_classes.html#the-omit-and-only-keyword-parameters-default-tuple\u003e`_\n.\n\n---------------------------------------------------------------------------------------------\n\nWriting `log_calls`-aware debugging messages\n=====================================================\n\nPrinting statements to an output device or file is one of the oldest forms of debugging.\nThese statements (let's call them `debugging messages`, `aka` \"print statements\") track\na program's progress, display the values of variables, announce milestones, report on\nthe consistency of internal state, and so on.\n\nThe ``@log_calls`` decorator automates the boilerplate aspects of this reportage:\nwho calls whom, when, how, and with what result. `log_calls` also provides the methods\n\n    * ``log_calls.print()`` and\n    * ``log_calls.print_exprs()``\n\nas attractive alternatives to the ``print`` function for writing other debugging messages.\n\nOne common kind of debugging message reports the values of variables as a program runs,\ntaking snapshots at strategic places at the top level of the code, or within a loop as an\nalgorithm executes. Writing such statements becomes tedious quickly — they're all alike\nthough in details all different too. The ``log_calls.print_exprs`` method lets you easily\ndisplay the values of variables and expressions within a decorated function.\n\nAll other debugging messages require a method as general as ``print``: the ``log_calls.print``\nmethod is that counterpart.\n\nBoth methods write to the same output destination as the decorator,\nwhether that's the console, a file or a logger, and their output is properly synced and aligned with\nthe decorator's output:\n\n    \u003e\u003e\u003e @log_calls()\n    ... def gcd(a, b):\n    ...     log_calls.print(\"At bottom of loop:\")\n    ...     while b:\n    ...         a, b = b, (a % b)\n    ...         log_calls.print_exprs('a', 'b', prefix=\"\\\\t\", suffix= '\\\\t\u003c--')\n    ...     return a\n    \u003e\u003e\u003e gcd(48, 246)            # doctest: +NORMALIZE_WHITESPACE\n    gcd \u003c== called by \u003cmodule\u003e\n        arguments: a=48, b=246\n        At bottom of loop:\n        \ta = 246, b = 48\t\u003c--\n        \ta = 48, b = 6\t\u003c--\n        \ta = 6, b = 0\t\u003c--\n    gcd ==\u003e returning to \u003cmodule\u003e\n    6\n\nIf you delete, comment out or otherwise disable the decorator, the ``print*`` methods will do nothing\n(except waste a little time). To illustrate this, we could just repeat the above function with the\ndecorator omitted or commented out; but we can also disable the decorator dynamically, and the\n``print*`` methods will be silent too:\n\n    \u003e\u003e\u003e gcd.log_calls_settings.enabled = False\n    \u003e\u003e\u003e gcd(48, 246)\n    6\n\nYou can pass expressions to ``print_exprs``:\n\n    \u003e\u003e\u003e @log_calls()\n    ... def f():\n    ...     x = 42\n    ...     log_calls.print_exprs('x', 'x//6', 'x/6')\n    \u003e\u003e\u003e f()\n    f \u003c== called by \u003cmodule\u003e\n        x = 42, x//6 = 7, x/6 = 7.0\n    f ==\u003e returning to \u003cmodule\u003e\n\n``print`` and ``print_exprs`` properly indent even multiline messages:\n\n    \u003e\u003e\u003e @log_calls()\n    ... def f(a):\n    ...     log_calls.print(\"Even multiline messages\\\\n\"\n    ...                     \"are properly indented.\")\n    ...     return g(a, 2*a)\n    \u003e\u003e\u003e @log_calls()\n    ... def g(x, y):\n    ...     retval = x + y + 1\n    ...     log_calls.print_exprs('retval',\n    ...                           prefix=\"So are multiline\\\\n\"\n    ...                                  \"prefixes --\\\\n\",\n    ...                           suffix=\"\\\\n-- and suffixes.\")\n    ...     return retval\n    \u003e\u003e\u003e f(2)\n    f \u003c== called by \u003cmodule\u003e\n        arguments: a=2\n        Even multiline messages\n        are properly indented.\n        g \u003c== called by f\n            arguments: x=2, y=4\n            So are multiline\n            prefixes --\n            retval = 7\n            -- and suffixes.\n        g ==\u003e returning to f\n    f ==\u003e returning to \u003cmodule\u003e\n    7\n\nYou can specify multiple lines for ``print`` either with one string that has explicit newlines,\nas above, or by using the ``sep`` keyword parameter together with multiple positional string arguments:\n\n    \u003e\u003e\u003e @log_calls()\n    ... def h():\n    ...     log_calls.print(\"Line 1 of 3\", \"line 2 of 3\", \"line 3 of 3\",\n    ...                     sep='\\\\n')\n    \u003e\u003e\u003e h()\n    h \u003c== called by \u003cmodule\u003e\n        Line 1 of 3\n        line 2 of 3\n        line 3 of 3\n    h ==\u003e returning to \u003cmodule\u003e\n\n\nThe behavior of the ``print*`` methods is configurable in a few ways:\n\n    * their output can be \"allowed through\" while muting the output of the decorators;\n    * their output doesn't *have* to be indented, it can be flush left (``extra_indent_level=-1000``);\n    * optionally the methods can raise an exception if called from within a function or method that\n      isn't decorated, so that development-only code doesn't sneak into production.\n\nIn the main documentation, the chapter\n`Appendix I: Keyword Parameters Reference \u003chttp://www.pythonhosted.org/log_calls/writing_log_calls_aware_debug_messages.html\u003e`_\ndetails\nthe ``print()`` and ``print_exprs()`` methods; the chapter\n`Appendix I: Keyword Parameters Reference \u003chttp://www.pythonhosted.org/log_calls/dynamic_control_of_settings.html\u003e`_\ndocuments the ``log_calls_settings`` attribute of a decorated callable.\n\n---------------------------------------------------------------------------------------------\n\nDecorating \"external\" code\n==================================================\n\nSometimes it's enlightening and instructive to decorate objects in a package or module\nthat you import. It might be in a new codebase you're getting to know, your own nontrivial\ncode from a while ago which you now wish you had documented more, or even a function, class\nor module in Python's standard library.\n\nWe'll illustrate techniques with a simple example: decorating the fractions class\n``fractions.Fraction`` in the standard library, to examine how it works. Along the way\nwe'll illustrate using `log_calls` settings to filter the output, forming hunches about\nhow ``Fraction`` works based on the information the decorator presents, and consulting\nthe source code to confirm or refute those hunches.\n\nFirst, let's import the class, decorate it and create an instance:\n\n    \u003e\u003e\u003e from log_calls import log_calls\n    \u003e\u003e\u003e from fractions import Fraction as Frac\n    \u003e\u003e\u003e log_calls.decorate_class(Frac)\n    \u003e\u003e\u003e print(Frac(3,4))\n    Fraction.__new__ \u003c== called by \u003cmodule\u003e\n        arguments: cls=\u003cclass 'fractions.Fraction'\u003e, numerator=3, denominator=4\n        defaults:  _normalize=True\n    Fraction.__new__ ==\u003e returning to \u003cmodule\u003e\n    Fraction.__str__ \u003c== called by \u003cmodule\u003e\n        arguments: self=Fraction(3, 4)\n    Fraction.__str__ ==\u003e returning to \u003cmodule\u003e\n    3/4\n\n(**Note**: *In this section, the expected output shown is from Python 3.6 and 3.5.\nThe output of Python ≤ 3.4 differs slightly: in places it's less efficient, and* __new__,\n*indirectly called below, had no* _normalize *parameter.*)\n\nNow create a couple of fractions, using the `log_calls` global mute to do it in silence:\n\n    \u003e\u003e\u003e log_calls.mute = True\n    \u003e\u003e\u003e fr56 = Frac(5,6)\n    \u003e\u003e\u003e fr78 = Frac(7,8)\n    \u003e\u003e\u003e log_calls.mute = False\n\nBefore using these, let's redecorate to improve `log_calls` output.\nAfter trying other examples at the command line it becomes apparent that\n``__str__`` gets called a lot, and the calls become just noise, so let's\n``omit`` that. To eliminate more clutter, let's suppress the exit lines\n(\"... returning to...\"). We'll also display return values. Here's how to\naccomplish all of that, with another call to ``decorate_class``, which won't\nwrap the `log_calls` wrappers already created but will instead just update their settings:\n\n    \u003e\u003e\u003e log_calls.decorate_class(Frac,\n    ...                          omit='__str__', log_exit=False, log_retval=True)\n\nFinally, let's do some arithmetic on fractions:\n\n    \u003e\u003e\u003e print(fr78 - fr56)      # doctest: +SKIP\n    Fraction._operator_fallbacks.\u003clocals\u003e.forward (__sub__) \u003c== called by \u003cmodule\u003e\n        arguments: a=Fraction(7, 8), b=Fraction(5, 6)\n        Fraction.denominator \u003c== called by _sub \u003c== Fraction._operator_fallbacks.\u003clocals\u003e.forward (__sub__)\n            arguments: a=Fraction(7, 8)\n            Fraction.denominator return value: 8\n        Fraction.denominator \u003c== called by _sub \u003c== Fraction._operator_fallbacks.\u003clocals\u003e.forward (__sub__)\n            arguments: a=Fraction(5, 6)\n            Fraction.denominator return value: 6\n        Fraction.numerator \u003c== called by _sub \u003c== Fraction._operator_fallbacks.\u003clocals\u003e.forward (__sub__)\n            arguments: a=Fraction(7, 8)\n            Fraction.numerator return value: 7\n        Fraction.numerator \u003c== called by _sub \u003c== Fraction._operator_fallbacks.\u003clocals\u003e.forward (__sub__)\n            arguments: a=Fraction(5, 6)\n            Fraction.numerator return value: 5\n        Fraction.__new__ \u003c== called by _sub \u003c== Fraction._operator_fallbacks.\u003clocals\u003e.forward (__sub__)\n            arguments: cls=\u003cclass 'fractions.Fraction'\u003e, numerator=2, denominator=48\n            defaults:  _normalize=True\n            Fraction.__new__ return value: 1/24\n        Fraction._operator_fallbacks.\u003clocals\u003e.forward (__sub__) return value: 1/24\n    1/24\n\nThe topmost call is to an inner function ``forward`` of the method ``Fraction._operator_fallbacks``,\npresumably a closure. The ``__name__`` of the callable is actually ``__sub__`` (its ``__qualname__``\nis ``Fraction._operator_fallbacks.\u003clocals\u003e.forward``). We know that classes implement\nthe infix subtraction operator ``-`` with \"dunder\" methods ``__sub__`` and ``__rsub__``,\nso it appears that in ``Fraction``, the closure `is` the value of the attribute ``__sub__``:\n\n    \u003e\u003e\u003e Frac.__sub__      # doctest: +ELLIPSIS\n    \u003cfunction Fraction._operator_fallbacks.\u003clocals\u003e.forward...\u003e\n    \u003e\u003e\u003e Frac.__sub__.__qualname__\n    'Fraction._operator_fallbacks.\u003clocals\u003e.forward'\n    \u003e\u003e\u003e Frac.__sub__.__name__\n    '__sub__'\n\nThe closure calls an undecorated function or method ``_sub``. Because\n``_sub`` isn't decorated we don't know what its arguments are, and the call chains for\nthe decorated ``numerator``, ``denominator`` and ``__new__`` chase back to ``__sub__``.\nIt appears to know about both operands, so we might guess that it takes two arguments.\nA look at the source code for ``fractions``,\n`fractions.py \u003chttps://hg.python.org/cpython/file/3.6/Lib/fractions.py\u003e`_\nconfirms that guess (``_sub`` is on line 433).\n\nWhy isn't ``_sub`` decorated?\n------------------------------\n\nLet's check that:\n\n    \u003e\u003e\u003e print(Frac._sub(fr78, fr56))\n    Fraction._sub \u003c== called by \u003cmodule\u003e\n        arguments: a=Fraction(7, 8), b=Fraction(5, 6)\n        Fraction.denominator \u003c== called by Fraction._sub\n            arguments: a=Fraction(7, 8)\n            Fraction.denominator return value: 8\n        Fraction.denominator \u003c== called by Fraction._sub\n            arguments: a=Fraction(5, 6)\n            Fraction.denominator return value: 6\n        Fraction.numerator \u003c== called by Fraction._sub\n            arguments: a=Fraction(7, 8)\n            Fraction.numerator return value: 7\n        Fraction.numerator \u003c== called by Fraction._sub\n            arguments: a=Fraction(5, 6)\n            Fraction.numerator return value: 5\n        Fraction.__new__ \u003c== called by Fraction._sub\n            arguments: cls=\u003cclass 'fractions.Fraction'\u003e, numerator=2, denominator=48\n            defaults:  _normalize=True\n            Fraction.__new__ return value: 1/24\n        Fraction._sub return value: 1/24\n    1/24\n\nAha: it *is* decorated after all, and the `log_calls` output certainly looks familiar.\n\nConsulting the source code makes clear what's going on.\nWhen ``Fraction`` is created, on line 439 ``__sub__`` is set equal to a closure\nreturned by ``_operator_fallbacks(_sub, operator.sub)``, defined on line 318.\nThe closure is an instance of its inner function ``forward`` on line 398, which\nimplements generic dispatch based on argument types to one of the two functions\npassed to ``_operator_fallbacks``. When called with two ``Fraction``\\ s, ``__sub__``\ncalls ``_sub`` and not ``operator.sub``. On line 407, ``_operator_fallbacks`` sets\nthe name of the closure to ``__sub__``.\n\nSo, the closure ``forward`` that implements ``__sub__`` has a nonlocal variable bound\nto the real ``_sub`` at class initialization, before the methods of the class were decorated.\nThe closure calls the inner, decorated ``_sub``, not the `log_calls` wrapper around it.\n\nHow the code works\n-------------------\n\nUltimately, then, subtraction of fractions is performed by a function ``_sub``,\nto which ``__sub__`` i.e. ``Fraction._operator_fallbacks.\u003clocals\u003e.forward`` dispatches.\n``_sub`` uses the public properties ``denominator`` and ``numerator``\nto retrieve the fields of the ``Fraction``\\ s, and returns a new ``Fraction``, with a\nnumerator of 2 (= 7 * 6 - 8 * 5) and denominator of 48 (= 6 * 8). ``__new__`` (line 124\nof the source code) reduces the returned ``Fraction`` to lowest terms just before\nreturning it (because its parameter ``_normalize`` is true, its default value, which\ngives Python 3.4 behavior).\n\nScrolling through ``fractions.py`` reveals that other operators are implemented\nin exactly the same way.\n\nFor more information\n----------------------------\n\nThe ``decorate_*`` methods are presented in the\nmain documentation chapter `Bulk (Re)Decoration, (Re)Decorating Imports \u003chttp://www.pythonhosted.org/log_calls/decorating_functions_class_hierarchies.html\u003e`_.\n\n---------------------------------------------------------------------------------------------\n\nChanging \"settings\" dynamically\n================================\n\nWhen `log_calls` decorates a callable (a function, method, property, ...), it \"wraps\" that\ncallable in a function — the *wrapper* of the callable. Subsequently, calls to the decorated\ncallable actually call the wrapper, which delegates to the original, in between its own\npre- and post-processing. This is simply what decorators do.\n\n`log_calls` gives the wrapper a few attributes pertaining to the wrapped callable, notably\n``log_calls_settings``, a dict-like object that contains the `log_calls` state of the callable.\nThe keys of ``log_calls_settings`` are `log_calls` keyword parameters, such as ``enabled`` and\n``log_retval`` — in fact, most of the keyword parameters, though not all of them.\n\nWhat is a \"setting\"?\n---------------------------\n\n**The** *settings of a decorated callable* **are the key/value pairs of its**\n``log_calls_settings`` **object, which is an attribute of the callable's wrapper.**\nThe settings comprise the `log_calls` state of the callable.\n\n.. _the_settings:\n\nThe following keyword parameters are (keys of) settings:\n\n    ``enabled``\n    ``args_sep``\n    ``log_args``\n    ``log_retval``\n    ``log_exit``\n    ``log_call_numbers``\n    ``log_elapsed``\n    ``indent``\n    ``prefix``\n    ``file``\n    ``mute``\n    ``logger``\n    ``loglevel``\n    ``record_history``\n    ``max_history``\n\nThe other keyword parameters are *not* \"settings\":\n\n    ``NO_DECO``\n    ``settings``\n    ``name``\n    ``override``\n    ``omit``\n    ``only``\n\nThese are directives to the decorator telling it how to initialize itself. Their initial values\nare not subsequently available via attributes of the wrapper, and cannot subsequently be changed.\n\nLifecycle of a \"setting\"\n------------------------------\nInitially the value of a setting is the value passed to the `log_calls` decorator for\nthe corresponding keyword parameter, or the default value for that parameter if no\nargument was supplied for it:\n\n    \u003e\u003e\u003e @log_calls(args_sep = ' / ')\n    ... def f(*args, **kwargs): return 91\n\nYou can access and change the settings of ``f`` via its ``log_calls_settings`` attribute,\nwhich behaves like a dictionary whose keys are the `log_calls` settings keywords:\n\n    \u003e\u003e\u003e f.log_calls_settings['args_sep']\n    ' / '\n    \u003e\u003e\u003e f.log_calls_settings['enabled']\n    True\n\nAll of a decorated callable's settings can be accessed through ``log_calls_settings``,\nand almost all can be changed on the fly:\n\n    \u003e\u003e\u003e f.log_calls_settings['enabled']\n    True\n    \u003e\u003e\u003e f.log_calls_settings['enabled'] = False\n\nYou can also use the same keywords as attributes of ``log_calls_settings``\ninstead of as keys to the mapping interface — they're completely equivalent:\n\n    \u003e\u003e\u003e f.log_calls_settings.enabled\n    False\n\n`log_calls` is disabled for ``f``, hence no output here:\n\n    \u003e\u003e\u003e _ = f()                   # no output (not even 91, because of \"_ = \")\n\nLet's reenable `log_calls` for ``f``, turn on call numbering and display of return values.\nWe could do this with three separate assignments to settings, but it's easier to use\nthe ``log_calls_settings.update()`` method:\n\n    \u003e\u003e\u003e f.log_calls_settings.update(\n    ...     enabled=True, log_call_numbers=True,log_retval=True)\n\nNow call ``f`` again:\n\n    \u003e\u003e\u003e _ = f()                   # output\n    f [1] \u003c== called by \u003cmodule\u003e\n        arguments: \u003cnone\u003e\n        f [1] return value: 91\n    f [1] ==\u003e returning to \u003cmodule\u003e\n\nand again:\n\n    \u003e\u003e\u003e _ = f(17, 19, foo='bar')                 # output\n    f [2] \u003c== called by \u003cmodule\u003e\n        arguments: *args=(17, 19) / **kwargs={'foo': 'bar'}\n        f [2] return value: 91\n    f [2] ==\u003e returning to \u003cmodule\u003e\n\n\nFor more information\n----------------------------\n\nThe chapter `Dynamic Control of Settings \u003chttp://www.pythonhosted.org/log_calls/dynamic_control_of_settings.html\u003e`_\nof the documentation presents the ``log_calls_settings`` attribute and its API in detail, with many examples.\n\n---------------------------------------------------------------------------------------------\n\nThe ``settings`` keyword parameter\n==================================================\n\nThe ``settings`` parameter lets you collect common values for settings keyword parameters\nin one place, and pass them to `log_calls` with a single parameter.\n``settings`` is a useful shorthand if you have, for example, a module with several\n`log_calls`-decorated functions, all with multiple, mostly identical settings\nwhich differ from `log_calls`'s defaults. Instead of repeating multiple identical\nsettings across several uses of the decorator, a tedious and error-prone practice,\nyou can gather them all into one ``dict`` or text file, and use the ``settings``\nparameter to concisely specify them *en masse*. You can use different groups\nof settings for different sets of functions, or classes, or modules — you're\nfree to organize them as you please.\n\nWhen not ``None``, the ``settings`` parameter can be either a ``dict``, or a ``str``\nspecifying the location of a *settings file* — a text file containing *key=value* pairs and optional comments.\nIn either case, the valid keys are the keyword parameters that are \"settings\", listed :ref:`above \u003cthe_settings\u003e`,\nplus, as a convenience, ``NO_DECO``. *Invalid keys are ignored.*\n\nThe values of settings specified in a settings dict or settings file override `log_calls`'s\ndefault values for those settings, and any of the resulting settings are in turn overridden\nby corresponding keywords passed directly to the decorator. Of course, you *don't* have to provide\na value for every valid key.\n\n``settings`` as a ``dict``\n--------------------------------------\n\nThe value of ``settings`` can be a ``dict``, or more generally any object\n``d`` for which it's true that ``isinstance(d, dict)``. Here's a settings\n``dict`` and two `log_calls`-decorated functions that use it:\n\n    \u003e\u003e\u003e d = dict(\n    ...     args_sep=' | ',\n    ...     log_args=False,\n    ...     log_call_numbers=True,\n    ...     NO_DECO=False           # True: \"kill switch\"\n    ... )\n    \u003e\u003e\u003e @log_calls(settings=d)\n    ... def f(n):\n    ...     if n \u003c= 0: return\n    ...     f(n-1)\n    \u003e\u003e\u003e @log_calls(settings=d, log_args=True)\n    ... def g(s, t): print(s + t)\n\nNote that ``g`` overrides the ``log_args`` setting given in ``d``:\n\n    \u003e\u003e\u003e f.log_calls_settings.log_args, g.log_calls_settings.log_args\n    (False, True)\n\nLet's call these functions and examine their `log_calls` output:\n\n    \u003e\u003e\u003e f(2)\n    f [1] \u003c== called by \u003cmodule\u003e\n        f [2] \u003c== called by f [1]\n            f [3] \u003c== called by f [2]\n            f [3] ==\u003e returning to f [2]\n        f [2] ==\u003e returning to f [1]\n    f [1] ==\u003e returning to \u003cmodule\u003e\n\n    \u003e\u003e\u003e g('aaa', 'bbb')\n    g [1] \u003c== called by \u003cmodule\u003e\n        arguments: s='aaa' | t='bbb'\n    aaabbb\n    g [1] ==\u003e returning to \u003cmodule\u003e\n\n``settings`` as a pathname (``str``)\n------------------------------------------\n\nWhen the value of the ``settings`` parameter is a ``str``, it must be a path to a\n*settings file* — a text file containing *key=value* pairs and optional comments.\nIf the pathname is just a directory, `log_calls` looks there for a file\nnamed ``.log_calls`` and uses that as a settings file; if the pathname is a file,\n`log_calls` uses that file. In either case, if the file doesn't exist then no error\nresults *nor is any warning issued*, and the ``settings`` parameter is ignored.\n\n.. topic:: Format of a settings file\n\n    A *settings file* is a text file containing zero or more lines of the form\n\n        *setting_name*\\ ``=``\\ *value*\n\n    Whitespace is permitted around *setting_name* and *value*, and is stripped.\n    Blank lines are ignored, as are lines whose first non-whitespace character is ``#``,\n    which therefore you can use as comments.\n\nUsing ``NO_DECO`` as a global \"kill switch\"\n-------------------------------------------------\n\nThe ``NO_DECO`` parameter prevents `log_calls` from decorating a callable or class:\nwhen true, the decorator returns the decorated thing itself, unwrapped and unaltered.\nIntended for use at program startup, it provides a single \"true bypass\" switch.\n\nUsing this parameter in a settings dict or settings file lets you\ncompletely bypass `log_calls` decoration for all decorators using that ``settings`` value,\nwith a single switch, e.g. for production, without having to comment out every decoration.\n\nUse ``NO_DECO=True`` for production\n-------------------------------------------\n\nEven even when it's disabled or bypassed, `log_calls` imposes some overhead.\nFor production, therefore, it's best to not use it at all. One tedious way to guarantee\nthat would be to comment out every ``@log_calls()`` decoration in every source file.\n``NO_DECO`` allows a more humane approach: Use a settings file or settings dict\ncontaining project-wide settings, including an entry for ``NO_DECO``.\nFor development, use::\n\n    NO_DECO=False\n\nand for production, change that to::\n\n    NO_DECO=True\n\nEven though it isn't actually a \"setting\", ``NO_DECO`` is permitted in settings files and dicts\nin order to allow this.\n\nFor example, if we change the ``NO_DECO`` setting in the settings dict ``d`` above and rerun\nthe script, ``f`` and ``g`` will **not** be decorated:\n\n    \u003e\u003e\u003e d = dict(\n    ...     args_sep=' | ',\n    ...     log_args=False,\n    ...     log_call_numbers=True,\n    ...     NO_DECO=True            # True: \"kill switch\"\n    ... )\n    \u003e\u003e\u003e @log_calls(settings=d)\n    ... def f(n):\n    ...     if n \u003c= 0: return\n    ...     f(n-1)\n    \u003e\u003e\u003e @log_calls(settings=d, log_args=True)\n    ... def g(s, t): print(s + t)\n\n    \u003e\u003e\u003e f(2)                # no log_calls output\n    \u003e\u003e\u003e g('aaa', 'bbb')     # no log_calls output\n    aaabbb\n\nThe functions ``f`` and ``g`` aren't just disabled or muted; they're not decorated at all:\n\n    \u003e\u003e\u003e hasattr(f, 'log_calls_settings'), hasattr(g, 'log_calls_settings')\n    (False, False)\n\nFor more information\n----------------------------\nSee the section\non `the settings parameter \u003chttp://www.pythonhosted.org/log_calls/parameters.html#settings-default-none\u003e`_ in\nthe Keyword Parameters chapter of the documentation.\n\n---------------------------------------------------------------------------------------------\n\nWhere To Go From Here\n#######################################\n\nThese examples have shown just a few of the features that make `log_calls` powerful,\nversatile, yet easy to use. They have introduced a few of `log_calls`'s keyword\nparameters, the source of much of its versatility, as well as some of its more\nadvanced capabilities.\n\nIn the main documentation, the\nchapter `What log_calls Can Decorate \u003chttp://www.pythonhosted.org/log_calls/what_log_calls_can_decorate.html\u003e`_\ngives general culture and also introduces terminology and concepts subsequently used throughout.\nThe chapter following it,\n`Keyword Parameters \u003chttp://www.pythonhosted.org/log_calls/parameters.html\u003e`_,\ndocuments the parameters in detail.\nThat chapter is a reference, to which you can refer back\nas needed; it's not necessary to assimilate its details before proceeding on to further topics.\nFor an even more concise reference, almost a cheatsheet,\nsee `Appendix I: Keyword Parameters Reference \u003chttp://www.pythonhosted.org/log_calls/appendix_I_parameters_table.html\u003e`_.\n\n`log_calls` provides yet more functionality. The\n`full documentation \u003chttp://www.pythonhosted.org/log_calls/index.html\u003e`_\ncovers all of it.\n\n-------------------------------\n\n.. raw:: html\n\n    \u003cp style=\"font-size: 95%\"\u003e\n    You can show your appreciation and support of \u003cem\u003elog_calls\u003c/em\u003e by\n    \u003ca href=\"http://www.paypal.me/logcalls\" TARGET=\"_blank\"\u003e\n        \u003cimg src=\"docs/_static/Donate-BLUE.png\" style=\"vertical-align: middle;\" /\u003e\n    \u003c/a\u003e\n    .\u003c/p\u003e\n    \u003cp style=\"text-align: right\"\u003e\n    Built with \u003ca href=\"http://sphinx-doc.org/\"\u003eSphinx\u003c/a\u003e using\n    the Alabaster theme.\n    \u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTwangist%2Flog_calls","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTwangist%2Flog_calls","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTwangist%2Flog_calls/lists"}