├── tests ├── __init__.py ├── test.py └── documentation.py ├── _config.yml ├── docs ├── _config.yml ├── index.rst ├── conf.py └── documentation.md ├── setup.cfg ├── MANIFEST.in ├── Makefile ├── performance.sh ├── .gitignore ├── pyproject.toml ├── LICENSE.txt ├── .github └── workflows │ └── python-package.yml ├── src ├── decorator-stubs │ └── __init__.pyi └── decorator.py ├── README.rst └── CHANGES.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. toctree:: 2 | documentation 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [upload_docs] 2 | upload_dir = docs 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE.txt CHANGES.md performance.sh documentation.pdf 2 | include src/decorator.py 3 | include tests/*.py 4 | graft docs 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | md: tests/documentation.py 2 | python $(S)/ms/tools/py2md.py tests/documentation.py docs 3 | 4 | upload: README.rst 5 | rm -rf build/* dist/* && python -m build && twine upload --verbose dist/* 6 | -------------------------------------------------------------------------------- /performance.sh: -------------------------------------------------------------------------------- 1 | python3 -m timeit -s " 2 | from decorator import decorator 3 | @decorator 4 | def do_nothing(func, *args, **kw): 5 | return func(*args, **kw) 6 | @do_nothing 7 | def f(): 8 | pass 9 | " "f()" 10 | python3 -m timeit -s " 11 | def f(): 12 | pass 13 | " "f()" 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | 38 | # sphinx 39 | _build 40 | _static 41 | _templates 42 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "decorator" 7 | authors = [ 8 | {name = "Michele Simionato", email = "michele.simionato@gmail.com"}, 9 | ] 10 | description = "Decorators for Humans" 11 | readme = "README.rst" 12 | dynamic = ["version"] 13 | requires-python = ">=3.8" 14 | keywords = ["decorators"] 15 | license = {text = "BSD-2-Clause"} 16 | classifiers = [ 17 | 'Development Status :: 5 - Production/Stable', 18 | 'Intended Audience :: Developers', 19 | 'License :: OSI Approved :: BSD License', 20 | 'Natural Language :: English', 21 | 'Operating System :: OS Independent', 22 | 'Programming Language :: Python', 23 | 'Programming Language :: Python :: 3.8', 24 | 'Programming Language :: Python :: 3.9', 25 | 'Programming Language :: Python :: 3.10', 26 | 'Programming Language :: Python :: 3.11', 27 | 'Programming Language :: Python :: 3.12', 28 | 'Programming Language :: Python :: 3.13', 29 | 'Programming Language :: Python :: 3.14', 30 | 'Programming Language :: Python :: Implementation :: CPython', 31 | 'Topic :: Software Development :: Libraries', 32 | 'Topic :: Utilities'] 33 | 34 | [tool.setuptools.dynamic] 35 | version.attr = "decorator.__version__" 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2025, Michele Simionato 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 23 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 25 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 26 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 27 | DAMAGE. 28 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: decorator 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] 20 | 21 | steps: 22 | - uses: actions/checkout@v6 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v6 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | allow-prereleases: true 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | python -m pip install codespell flake8 32 | python -m pip install -e . 33 | - run: codespell --ignore-words-list="assertIn,ba,claus,vas" --quiet-level=2 34 | - run: flake8 . --count --ignore=E122,E226,E265,E741,E742 --max-complexity=22 --max-line-length=124 --show-source --statistics 35 | - run: python tests/test.py -v 36 | 37 | stubtest: 38 | 39 | runs-on: ubuntu-latest 40 | strategy: 41 | fail-fast: false 42 | 43 | steps: 44 | - uses: actions/checkout@v6 45 | - name: Set up Python 3.x 46 | uses: actions/setup-python@v6 47 | with: 48 | python-version: 3.x 49 | allow-prereleases: true 50 | - name: Install dependencies 51 | run: | 52 | python -m pip install --upgrade pip 53 | python -m pip install mypy 54 | python -m pip install -e . 55 | - run: | 56 | python -m mypy.stubtest decorator 57 | python -m mypy src/decorator.py 58 | 59 | -------------------------------------------------------------------------------- /src/decorator-stubs/__init__.pyi: -------------------------------------------------------------------------------- 1 | import inspect 2 | from builtins import dict as _dict # alias to avoid conflicts with attribute name 3 | from collections.abc import Callable, Generator, Iterator 4 | from contextlib import _GeneratorContextManager 5 | from inspect import Signature, getfullargspec as getfullargspec, iscoroutinefunction as iscoroutinefunction 6 | from re import Pattern 7 | from typing import Any, Final, Literal, TypeVar 8 | from typing_extensions import ParamSpec 9 | 10 | _C = TypeVar("_C", bound=Callable[..., Any]) 11 | _Func = TypeVar("_Func", bound=Callable[..., Any]) 12 | _T = TypeVar("_T") 13 | _P = ParamSpec("_P") 14 | 15 | DEF: Final[Pattern[str]] 16 | POS: Final[Literal[inspect._ParameterKind.POSITIONAL_OR_KEYWORD]] 17 | EMPTY: Final[type[inspect._empty]] 18 | 19 | class FunctionMaker: 20 | args: list[str] 21 | varargs: str | None 22 | varkw: str | None 23 | defaults: tuple[Any, ...] | None 24 | kwonlyargs: list[str] 25 | kwonlydefaults: dict[str, Any] | None 26 | shortsignature: str | None 27 | name: str 28 | doc: str | None 29 | module: str | None 30 | annotations: _dict[str, Any] 31 | signature: str 32 | dict: _dict[str, Any] 33 | def __init__( 34 | self, 35 | func: Callable[..., Any] | None = ..., 36 | name: str | None = ..., 37 | signature: str | None = ..., 38 | defaults: tuple[Any, ...] | None = ..., 39 | doc: str | None = ..., 40 | module: str | None = ..., 41 | funcdict: _dict[str, Any] | None = ..., 42 | ) -> None: ... 43 | def update(self, func: Any, **kw: Any) -> None: ... 44 | def make( 45 | self, src_templ: str, evaldict: _dict[str, Any] | None = ..., addsource: bool = ..., **attrs: Any 46 | ) -> Callable[..., Any]: ... 47 | @classmethod 48 | def create( 49 | cls, 50 | obj: Any, 51 | body: str, 52 | evaldict: _dict[str, Any], 53 | defaults: tuple[Any, ...] | None = ..., 54 | doc: str | None = ..., 55 | module: str | None = ..., 56 | addsource: bool = ..., 57 | **attrs: Any, 58 | ) -> Callable[..., Any]: ... 59 | 60 | def fix(args: tuple[Any, ...], kwargs: dict[str, Any], sig: Signature) -> tuple[tuple[Any, ...], dict[str, Any]]: ... 61 | def decorate(func: _Func, caller: Callable[..., Any], extras: tuple[Any, ...] = ..., kwsyntax: bool = False) -> _Func: ... 62 | def decoratorx(caller: Callable[..., Any]) -> Callable[..., Any]: ... 63 | def decorator( 64 | caller: Callable[..., Any], _func: Callable[..., Any] | None = None, kwsyntax: bool = False 65 | ) -> Callable[[Callable[..., Any]], Callable[..., Any]]: ... 66 | 67 | class ContextManager(_GeneratorContextManager[_T]): 68 | def __init__(self, g: Callable[..., Generator[_T]], *a: Any, **k: Any) -> None: ... 69 | def __call__(self, func: _C) -> _C: ... 70 | 71 | def contextmanager(func: Callable[_P, Iterator[_T]]) -> Callable[_P, ContextManager[_T]]: ... 72 | def append(a: type, vancestors: list[type]) -> None: ... 73 | def dispatch_on(*dispatch_args: Any) -> Callable[[Callable[..., Any]], Callable[..., Any]]: ... 74 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Decorators for Humans 2 | ===================== 3 | 4 | The goal of the decorator module is to make it easy to define 5 | signature-preserving function decorators and decorator factories. 6 | It also includes an implementation of multiple dispatch and other niceties 7 | (please check the docs). It is released under a two-clauses 8 | BSD license, i.e. basically you can do whatever you want with it but I am not 9 | responsible. 10 | 11 | Installation 12 | ------------- 13 | 14 | If you are lazy, just perform 15 | 16 | ``$ pip install decorator`` 17 | 18 | which will install just the module on your system. 19 | 20 | If you prefer to install the full distribution from source, including 21 | the documentation, clone the `GitHub repo`_ or download the tarball_, unpack it and run 22 | 23 | ``$ pip install .`` 24 | 25 | in the main directory, possibly as superuser. 26 | 27 | .. _tarball: https://pypi.org/project/decorator/#files 28 | .. _GitHub repo: https://github.com/micheles/decorator 29 | 30 | Testing 31 | -------- 32 | 33 | If you have the source code installation you can run the tests with 34 | 35 | `$ python tests/test.py -v` 36 | 37 | or (if you have setuptools installed) 38 | 39 | `$ python setup.py test` 40 | 41 | Notice that you may run into trouble if in your system there 42 | is an older version of the decorator module; in such a case remove the 43 | old version. It is safe even to copy the module `decorator.py` over 44 | an existing one, since we kept backward-compatibility for a long time. 45 | 46 | Repository 47 | --------------- 48 | 49 | The project is hosted on GitHub. You can look at the source here: 50 | 51 | https://github.com/micheles/decorator 52 | 53 | Documentation 54 | --------------- 55 | 56 | The documentation has been moved to https://github.com/micheles/decorator/blob/master/docs/documentation.md 57 | 58 | From there you can get a PDF version by simply using the print 59 | functionality of your browser. 60 | 61 | Here is the documentation for previous versions of the module: 62 | 63 | https://github.com/micheles/decorator/blob/4.3.2/docs/tests.documentation.rst 64 | https://github.com/micheles/decorator/blob/4.2.1/docs/tests.documentation.rst 65 | https://github.com/micheles/decorator/blob/4.1.2/docs/tests.documentation.rst 66 | https://github.com/micheles/decorator/blob/4.0.0/documentation.rst 67 | https://github.com/micheles/decorator/blob/3.4.2/documentation.rst 68 | 69 | For the impatient 70 | ----------------- 71 | 72 | Here is an example of how to define a family of decorators tracing slow 73 | operations: 74 | 75 | .. code-block:: python 76 | 77 | from decorator import decorator 78 | 79 | @decorator 80 | def warn_slow(func, timelimit=60, *args, **kw): 81 | t0 = time.time() 82 | result = func(*args, **kw) 83 | dt = time.time() - t0 84 | if dt > timelimit: 85 | logging.warning('%s took %d seconds', func.__name__, dt) 86 | else: 87 | logging.info('%s took %d seconds', func.__name__, dt) 88 | return result 89 | 90 | @warn_slow # warn if it takes more than 1 minute 91 | def preprocess_input_files(inputdir, tempdir): 92 | ... 93 | 94 | @warn_slow(timelimit=600) # warn if it takes more than 10 minutes 95 | def run_calculation(tempdir, outdir): 96 | ... 97 | 98 | Enjoy! 99 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # decorator documentation build configuration file, created by 5 | # sphinx-quickstart on Sun Jul 23 17:11:44 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | from decorator import __version__ 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.viewcode', 33 | 'myst_parser' 34 | ] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The suffix(es) of source filenames. 40 | # You can specify multiple suffix as a list of string: 41 | # source_suffix = ['.rst', '.md'] 42 | source_suffix = '.rst' 43 | 44 | # The encoding of source files. 45 | #source_encoding = 'utf-8-sig' 46 | 47 | # The master toctree document. 48 | master_doc = 'index' 49 | 50 | # General information about the project. 51 | project = 'decorator' 52 | copyright = '2005-2020, Michele Simionato' 53 | author = 'Michele Simionato' 54 | 55 | # The version info for the project you're documenting, acts as replacement for 56 | # |version| and |release|, also used in various other places throughout the 57 | # built documents. 58 | # 59 | # The short X.Y version. 60 | version = release = __version__ 61 | # The full version, including alpha/beta/rc tags. 62 | 63 | # The language for content autogenerated by Sphinx. Refer to documentation 64 | # for a list of supported languages. 65 | # 66 | # This is also used if you do content translation via gettext catalogs. 67 | # Usually you set "language" from the command line for these cases. 68 | language = None 69 | 70 | # There are two options for replacing |today|: either, you set today to some 71 | # non-false value, then it is used: 72 | #today = '' 73 | # Else, today_fmt is used as the format for a strftime call. 74 | #today_fmt = '%B %d, %Y' 75 | 76 | # List of patterns, relative to source directory, that match files and 77 | # directories to ignore when looking for source files. 78 | exclude_patterns = ['_build'] 79 | 80 | # The reST default role (used for this markup: `text`) to use for all 81 | # documents. 82 | #default_role = None 83 | 84 | # If true, '()' will be appended to :func: etc. cross-reference text. 85 | #add_function_parentheses = True 86 | 87 | # If true, the current module name will be prepended to all description 88 | # unit titles (such as .. function::). 89 | #add_module_names = True 90 | 91 | # If true, sectionauthor and moduleauthor directives will be shown in the 92 | # output. They are ignored by default. 93 | #show_authors = False 94 | 95 | # The name of the Pygments (syntax highlighting) style to use. 96 | pygments_style = 'sphinx' 97 | 98 | # A list of ignored prefixes for module index sorting. 99 | #modindex_common_prefix = [] 100 | 101 | # If true, keep warnings as "system message" paragraphs in the built documents. 102 | #keep_warnings = False 103 | 104 | # If true, `todo` and `todoList` produce output, else they produce nothing. 105 | todo_include_todos = False 106 | 107 | 108 | # -- Options for HTML output ---------------------------------------------- 109 | 110 | # The theme to use for HTML and HTML Help pages. See the documentation for 111 | # a list of builtin themes. 112 | html_theme = 'alabaster' 113 | 114 | # Theme options are theme-specific and customize the look and feel of a theme 115 | # further. For a list of options available for each theme, see the 116 | # documentation. 117 | #html_theme_options = {} 118 | 119 | # Add any paths that contain custom themes here, relative to this directory. 120 | #html_theme_path = [] 121 | 122 | # The name for this set of Sphinx documents. If None, it defaults to 123 | # " v documentation". 124 | #html_title = None 125 | 126 | # A shorter title for the navigation bar. Default is the same as html_title. 127 | #html_short_title = None 128 | 129 | # The name of an image file (relative to this directory) to place at the top 130 | # of the sidebar. 131 | #html_logo = None 132 | 133 | # The name of an image file (relative to this directory) to use as a favicon of 134 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 135 | # pixels large. 136 | #html_favicon = None 137 | 138 | # Add any paths that contain custom static files (such as style sheets) here, 139 | # relative to this directory. They are copied after the builtin static files, 140 | # so a file named "default.css" will overwrite the builtin "default.css". 141 | html_static_path = ['_static'] 142 | 143 | # Add any extra paths that contain custom files (such as robots.txt or 144 | # .htaccess) here, relative to this directory. These files are copied 145 | # directly to the root of the documentation. 146 | #html_extra_path = [] 147 | 148 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 149 | # using the given strftime format. 150 | #html_last_updated_fmt = '%b %d, %Y' 151 | 152 | # If true, SmartyPants will be used to convert quotes and dashes to 153 | # typographically correct entities. 154 | #html_use_smartypants = True 155 | 156 | # Custom sidebar templates, maps document names to template names. 157 | #html_sidebars = {} 158 | 159 | # Additional templates that should be rendered to pages, maps page names to 160 | # template names. 161 | #html_additional_pages = {} 162 | 163 | # If false, no module index is generated. 164 | #html_domain_indices = True 165 | 166 | # If false, no index is generated. 167 | #html_use_index = True 168 | 169 | # If true, the index is split into individual pages for each letter. 170 | #html_split_index = False 171 | 172 | # If true, links to the reST sources are added to the pages. 173 | #html_show_sourcelink = True 174 | 175 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 176 | #html_show_sphinx = True 177 | 178 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 179 | #html_show_copyright = True 180 | 181 | # If true, an OpenSearch description file will be output, and all pages will 182 | # contain a tag referring to it. The value of this option must be the 183 | # base URL from which the finished HTML is served. 184 | #html_use_opensearch = '' 185 | 186 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 187 | #html_file_suffix = None 188 | 189 | # Language to be used for generating the HTML full-text search index. 190 | # Sphinx supports the following languages: 191 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 192 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' 193 | #html_search_language = 'en' 194 | 195 | # A dictionary with options for the search language support, empty by default. 196 | # Now only 'ja' uses this config value 197 | #html_search_options = {'type': 'default'} 198 | 199 | # The name of a javascript file (relative to the configuration directory) that 200 | # implements a search results scorer. If empty, the default will be used. 201 | #html_search_scorer = 'scorer.js' 202 | 203 | # Output file base name for HTML help builder. 204 | htmlhelp_basename = 'decoratordoc' 205 | 206 | # -- Options for LaTeX output --------------------------------------------- 207 | 208 | latex_elements = { 209 | # The paper size ('letterpaper' or 'a4paper'). 210 | #'papersize': 'letterpaper', 211 | 212 | # The font size ('10pt', '11pt' or '12pt'). 213 | #'pointsize': '10pt', 214 | 215 | # Additional stuff for the LaTeX preamble. 216 | #'preamble': '', 217 | 218 | # Latex figure (float) alignment 219 | #'figure_align': 'htbp', 220 | } 221 | 222 | # Grouping the document tree into LaTeX files. List of tuples 223 | # (source start file, target name, title, 224 | # author, documentclass [howto, manual, or own class]). 225 | latex_documents = [ 226 | (master_doc, 'decorator.tex', 'decorator Documentation', 227 | 'Michele Simionato', 'manual'), 228 | ] 229 | 230 | # The name of an image file (relative to this directory) to place at the top of 231 | # the title page. 232 | #latex_logo = None 233 | 234 | # For "manual" documents, if this is true, then toplevel headings are parts, 235 | # not chapters. 236 | #latex_use_parts = False 237 | 238 | # If true, show page references after internal links. 239 | #latex_show_pagerefs = False 240 | 241 | # If true, show URL addresses after external links. 242 | #latex_show_urls = False 243 | 244 | # Documents to append as an appendix to all manuals. 245 | #latex_appendices = [] 246 | 247 | # If false, no module index is generated. 248 | #latex_domain_indices = True 249 | 250 | 251 | # -- Options for manual page output --------------------------------------- 252 | 253 | # One entry per manual page. List of tuples 254 | # (source start file, name, description, authors, manual section). 255 | man_pages = [ 256 | (master_doc, 'decorator', 'decorator Documentation', 257 | [author], 1) 258 | ] 259 | 260 | # If true, show URL addresses after external links. 261 | #man_show_urls = False 262 | 263 | 264 | # -- Options for Texinfo output ------------------------------------------- 265 | 266 | # Grouping the document tree into Texinfo files. List of tuples 267 | # (source start file, target name, title, author, 268 | # dir menu entry, description, category) 269 | texinfo_documents = [ 270 | (master_doc, 'decorator', 'decorator Documentation', 271 | author, 'decorator', 'One line description of project.', 272 | 'Miscellaneous'), 273 | ] 274 | 275 | # Documents to append as an appendix to all manuals. 276 | #texinfo_appendices = [] 277 | 278 | # If false, no module index is generated. 279 | #texinfo_domain_indices = True 280 | 281 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 282 | #texinfo_show_urls = 'footnote' 283 | 284 | # If true, do not generate a @detailmenu in the "Top" node's menu. 285 | #texinfo_no_detailmenu = False 286 | 287 | 288 | # -- Options for Epub output ---------------------------------------------- 289 | 290 | # Bibliographic Dublin Core info. 291 | epub_title = project 292 | epub_author = author 293 | epub_publisher = author 294 | epub_copyright = copyright 295 | 296 | # The basename for the epub file. It defaults to the project name. 297 | #epub_basename = project 298 | 299 | # The HTML theme for the epub output. Since the default themes are not 300 | # optimized for small screen space, using the same theme for HTML and epub 301 | # output is usually not wise. This defaults to 'epub', a theme designed to save 302 | # visual space. 303 | #epub_theme = 'epub' 304 | 305 | # The language of the text. It defaults to the language option 306 | # or 'en' if the language is not set. 307 | #epub_language = '' 308 | 309 | # The scheme of the identifier. Typical schemes are ISBN or URL. 310 | #epub_scheme = '' 311 | 312 | # The unique identifier of the text. This can be a ISBN number 313 | # or the project homepage. 314 | #epub_identifier = '' 315 | 316 | # A unique identification for the text. 317 | #epub_uid = '' 318 | 319 | # A tuple containing the cover image and cover page html template filenames. 320 | #epub_cover = () 321 | 322 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 323 | #epub_guide = () 324 | 325 | # HTML files that should be inserted before the pages created by sphinx. 326 | # The format is a list of tuples containing the path and title. 327 | #epub_pre_files = [] 328 | 329 | # HTML files that should be inserted after the pages created by sphinx. 330 | # The format is a list of tuples containing the path and title. 331 | #epub_post_files = [] 332 | 333 | # A list of files that should not be packed into the epub file. 334 | epub_exclude_files = ['search.html'] 335 | 336 | # The depth of the table of contents in toc.ncx. 337 | #epub_tocdepth = 3 338 | 339 | # Allow duplicate toc entries. 340 | #epub_tocdup = True 341 | 342 | # Choose between 'default' and 'includehidden'. 343 | #epub_tocscope = 'default' 344 | 345 | # Fix unsupported image types using the Pillow. 346 | #epub_fix_images = False 347 | 348 | # Scale large images. 349 | #epub_max_image_width = 0 350 | 351 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 352 | #epub_show_urls = 'inline' 353 | 354 | # If false, no index is generated. 355 | #epub_use_index = True 356 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | HISTORY 2 | -------- 3 | 4 | ## Unreleased 5 | 6 | Added official support for Python 3.14 (thanks to Hugo van Kemenade). 7 | 8 | ## 5.2.0 (2025-02-22) 9 | 10 | Changed the build procedure to use pyproject.toml and moved the tests 11 | outside of the generated wheel/tarball. 12 | 13 | Added official support for Python 3.11, 3.12, 3.13 (thanks to Hugo van 14 | Kemenade). 15 | 16 | Dropped official support for Python < 3.8: the module is 17 | expected to work on older Python versions, but I cannot test such 18 | versions on GitHub actions, so I cannot claim that it is officially 19 | supported. 20 | 21 | Dafu Wu provided support for decorating partial functions, i.e. 22 | functions wrapped by functools.partial. 23 | 24 | ## 5.1.1 (2022-01-07) 25 | 26 | Sangwoo Shim contributed a fix so that cythonized functions can be decorated. 27 | Brian McFee pointed out an issue in the `decorator_apply` example and 28 | Wim Glenn pointed out that the "fix" in version 5.1 broke 29 | `decorator.contextmanager` even more. Both issues are now solved. 30 | 31 | ## 5.1.0 (2021-09-11) 32 | 33 | Added a function `decoratorx` using the `FunctionMaker` and thus 34 | preserving the signature of `__code__` objects. Then fixed three small bugs: 35 | - Sphinx was printing a few warnings when building the documentation, as 36 | signaled by Tomasz Kłoczko 37 | - functions decorated with `decorator.contextmanager` were one-shot, 38 | as discovered by Alex Pizarro. 39 | - `decorator.decorator` was not passing the kwsyntax argument. 40 | 41 | ## 5.0.9 (2021-05-16) 42 | 43 | Fixed a test breaking PyPy. Restored support for Sphinx. 44 | 45 | ## 5.0.8 (2021-05-15) 46 | 47 | Made the decorator module more robust when decorating builtin functions 48 | lacking dunder attributes, like `dict.__setitem__`. 49 | 50 | ## 5.0.7 (2021-04-14) 51 | 52 | The decorator module was not passing correctly the defaults inside the 53 | `*args` tuple, thanks to Dan Shult for the fix. Also fixed some misspellings 54 | in the documentation and integrated codespell in the CI, thanks to 55 | Christian Clauss. 56 | 57 | ## 5.0.6 (2021-04-08) 58 | 59 | The decorator module was not copying the __module__ attribute anymore. 60 | Thanks to Nikolay Markov for the notice. 61 | 62 | ## 5.0.5 (2021-04-04) 63 | 64 | Dropped support for Python < 3.5 with a substantial simplification of 65 | the code base (now building a decorator does not require calling "exec"). 66 | Added a way to mimic functools.wraps-generated decorators. 67 | Ported the Continuous Integration from Travis to GitHub. 68 | 69 | ## 4.4.2 (2020-02-29) 70 | 71 | Sylvan Mosberger (https://github.com/Infinisil) contributed a patch to 72 | some doctests that were breaking on NixOS. 73 | John Vandenberg (https://github.com/jayvdb) made a case for removing the usage 74 | of `__file__`, that was breaking PyOxidizer. 75 | Miro Hrončok (https://github.com/hroncok) contributed some fixes for the 76 | future Python 3.9. 77 | Hugo van Kemenade (https://github.com/hugovk) contributed some fixes for the 78 | future Python 3.10. 79 | 80 | ## 4.4.1 (2019-10-27) 81 | 82 | Changed the description to "Decorators for Humans" are requested by 83 | several users. Fixed a .rst bug in the description as seen in PyPI. 84 | 85 | ## 4.4.0 (2019-03-16) 86 | 87 | Fixed a regression with decorator factories breaking the case with no 88 | arguments by going back to the syntax used in version 4.2. 89 | Accepted a small fix from Eric Larson (https://github.com/larsoner) affecting 90 | `isgeneratorfunction` for old Python versions. 91 | Moved the documentation from ReadTheDocs to GitHub to simplify the 92 | release process and replaced ReStructuredText with Markdown: it is 93 | an inferior solution, but it works better with GitHub and it is good enough. 94 | 95 | ## 4.3.2 (2019-01-24) 96 | 97 | Accepted a patch from Sylvain Marie (https://github.com/smarie): now the 98 | decorator module can decorate generator functions by preserving their 99 | being generator functions. Set `python_requires='>=2.6, !=3.0.*, !=3.1.*'` 100 | in setup.py, as suggested by https://github.com/hugovk. 101 | 102 | ## 4.3.1 (2018-08-04) 103 | 104 | Added a section "For the impatient" to the README, addressing an issue 105 | raised by Amir Malekpour. Added support for Python 3.7. Now 106 | the path to the decorator module appears in the tracebacks, as suggested 107 | by an user at EuroPython 2018. 108 | 109 | ## 4.3.0 (2018-04-15) 110 | 111 | Extended the decorator family facility to work with positional 112 | arguments and updated the documentation. Removed 113 | `decorator.getargspec` and provided `decorator.getfullargspec` 114 | instead. This is convenient for users of Python 2.6/2.7, the others 115 | can just use `inspect.getfullargspec`. 116 | 117 | ## 4.2.1 (2018-01-14) 118 | 119 | Fixed a regression breaking IPython reported by https://github.com/spapini . 120 | 121 | ## 4.2.0 (2018-01-14) 122 | 123 | Added a facility to define families of decorators (aka decorators with 124 | arguments) as requested by several users. Accepted a pylint patch by 125 | David Allouche. 126 | 127 | ## 4.1.2 (2017-07-23) 128 | 129 | Made it possible to define decorators converting coroutines into regular 130 | functions, as requested by Itaï Ben Yaacov. 131 | 132 | ## 4.1.1 (2017-07-16) 133 | 134 | Changed the documentation build system to sphinx and uploaded the docs 135 | on readthedocs.org. 136 | 137 | ## 4.1.0 (2017-07-15) 138 | 139 | Support for Python 3.5 coroutines defined with `async def`, thanks to 140 | Victor-Nicolae Savu who raised the issue of `iscoroutinefunction` not 141 | giving the right answer for coroutines decorated with the decorator module. 142 | 143 | ## 4.0.11 (2017-01-15) 144 | 145 | Small improvements to the documentation and tested with Python 3.6 146 | 147 | ## 4.0.10 (2016-06-07) 148 | 149 | Improved the documentation thanks to Tony Goodchild (zearin) who also 150 | provided a much better CSS than the one I was using. 151 | 152 | ## 4.0.9 (2016-02-08) 153 | 154 | Same as 4.0.7 and 4.0.8, re-uploaded due to issues on PyPI. 155 | 156 | ## 4.0.7 (2016-02-06) 157 | 158 | Switched to a new changelog format (the one in http://keepachangelog.com/) 159 | since it was contributed by Alexander Artemenko. Re-added a newline to support 160 | old version of Python, as requested by [azjps](https://github.com/azjps). 161 | 162 | ## 4.0.6 (2015-12-11) 163 | 164 | Removed a file x.py accidentally entered in the tarball. 165 | 166 | ## 4.0.5 (2015-12-09) 167 | 168 | Documented a quirk signaled by David Goldstein when writing decorators 169 | for functions with keyword arguments. Avoided copying the globals, 170 | as signaled by Benjamin Peterson. 171 | 172 | ## 4.0.4 (2015-09-25) 173 | 174 | Included a patch from Zev Benjamin: now decorated functions play well 175 | with cProfile. 176 | 177 | ## 4.0.3 (2015-09-25) 178 | 179 | Added a warning about the memoize example, as requested by Robert 180 | Buchholz. 181 | 182 | ## 4.0.2 (2015-07-28) 183 | 184 | docs/README.rst was not included in MANIFEST.in by accident, 185 | thus breaking the source installation. 186 | 187 | ## 4.0.1 (2015-07-28) 188 | 189 | Added docs directory and upload_docs command. Fixed bug with 190 | `__qualname__`, reported by Lucian Petrut. 191 | 192 | ## 4.0.0 (2015-07-24) 193 | 194 | Removed the need for 2to3 by dropping the support for Python 2.5. 195 | Added a MANIFEST.in file and produced a proper wheel. Improved 196 | the integration with setuptools so that `python setup.py test` works. 197 | Reworked the documentation and introduced `decorator.decorated`. 198 | Removed any dependence from `inspect.getargspec`, which is deprecated 199 | in Python 3.5, as signaled by Ralf Gommers. 200 | Fixed `contextmanager` to work with Python 3.5. 201 | Copied the `__qualname__` attribute, as requested by Frazer McLean. 202 | Added a `dispatch_on` facility to implement generic functions. 203 | 204 | ## 3.4.2 (2015-03-22) 205 | 206 | Same as 3.4.1, re-uploaded to PyPI. 207 | 208 | ## 3.4.1 (2015-03-16) 209 | 210 | Ported the repository from GoogleCode to GitHub and added Travis CI 211 | support. Tests are executed with the new command `python test.py -v`. 212 | setuptools is now mandatory in Python 3. The suggested 213 | installation tool is now `pip`, not `easy_install`. Supported IronPython 214 | and other Python implementations without sys._getframe, as requested by 215 | Doug Blank. 216 | 217 | ## 3.4.0 (2012-10-18) 218 | 219 | Added the ability to use classes and generic callables as callers and 220 | implemented a signature-preserving contexmanager decorator. Fixed a bug 221 | with the signature f(**kw) in Python 3 and fixed a couple of doctests 222 | broken by Python 3.3, both issues pointed out by Dominic Sacré. 223 | 224 | ## 3.3.3 (2012-04-23) 225 | 226 | Fixed a bug with kwonlyargs for Python 3, submitted by Chris 227 | Ellison. 228 | 229 | ## 3.3.2 (2011-09-01) 230 | 231 | Fixed a bug with __kwdefaults__ for Python 3, submitted by Chris 232 | Ellison. 233 | 234 | ## 3.3.1 (2011-04-22) 235 | 236 | Fixed a doctest broken for Python 3.2, as noted by 237 | Arfrever Frehtes Taifersar Arahesis; changed the name of 238 | the attribute ``undecorated`` to ``__wrapped__``, by following the 239 | Python 3.2 convention, as requested by Ram Rachum; added 240 | the Python 3 classifier to setup.py. 241 | 242 | ## 3.3 (2011-01-01) 243 | 244 | Added support for function annotations. 245 | 246 | ## 3.2.1 (2010-12-28) 247 | 248 | Now the .func_globals of the decorated function are the same of 249 | the undecorated function, as requested by Paul Ollis. 250 | 251 | ## 3.2 (2010-05-22) 252 | 253 | Added __version__ (thanks to Gregg Lind), removed functionality which 254 | has been deprecated for years, removed the confusing decorator_factory 255 | example and added official support for Python 3 (requested by Claus Klein). 256 | Moved the documentation from PyPI to googlecode. 257 | 258 | ## 3.1.2 (2009-08-25) 259 | 260 | Added attributes args, varargs, keywords and arg0, ..., argN 261 | to FunctionMaker objects generated from a function; fixed another 262 | Pylons-breaking bug signaled by Lawrence Oluyede. 263 | 264 | ## 3.1.1 (2009-08-18) 265 | 266 | Fixed a bug which was breaking Pylons, signaled by 267 | Gabriel de Perthuis, and added a test for it. 268 | 269 | ## 3.1 (2009-08-16) 270 | 271 | Added decorator.factory, an easy way to define families of decorators 272 | (requested by various users, including David Laban). Refactored the 273 | FunctionMaker class and added an easier to use .create classmethod. 274 | Internally, functools.partial is used for Python >= 2.5. 275 | 276 | ## 3.0.1 (2009-02-16) 277 | 278 | Improved the error message in case a bound/unbound method is passed 279 | instead of a function and documented this case; that should make life 280 | easier for users like Gustavo Nerea. 281 | 282 | ## 3.0 (2008-12-14) 283 | 284 | New major version introducing ``FunctionMaker`` and the two-argument 285 | syntax for ``decorator``. Moreover, added support for getting the 286 | source code. This version is Python 3.0 ready. Major overhaul of the 287 | documentation, now hosted on http://packages.python.org/decorator. 288 | 289 | ## 2.3.2 (2008-12-01) 290 | 291 | Small optimization in the code for decorator factories. First version 292 | with the code uploaded to PyPI. 293 | 294 | ## 2.3.1 (2008-07-25) 295 | 296 | Set the zipsafe flag to False, since I want my users to have the source, 297 | not a zipped egg. 298 | 299 | ## 2.3.0 (2008-07-10) 300 | 301 | Added support for writing decorator factories with minimal effort 302 | (feature requested by Matthew Wilson); implemented it by enhancing 303 | 'decorator' to a Python 2.6 class decorator. 304 | 305 | ## 2.2.0. (2007-07-31) 306 | 307 | Added a note on 'inspect.getsource' not working for decorated 308 | functions; referenced PEP 326; highlighted the snippets in the 309 | documentation with pygments; slightly simplified the code. 310 | 311 | ## 2.1.0. (2007-07-03) 312 | 313 | Replaced the utility 'update_wrapper' with 'new_wrapper' and 314 | updated the documentation accordingly; fixed and improved the 315 | doctester argument parsing, signaled by Sam Wyse. 316 | 317 | ## 2.0.1 (2007-02-17) 318 | 319 | Included the licence in the source code too; fixed a versioning 320 | issue by adding the version number to the zip file and fixing 321 | the link to it on the web page, thanks to Philip Jenvey. 322 | 323 | ## 2.0 (2007-01-13) 324 | 325 | Rewritten and simplified the implementation; broken compatibility 326 | with previous versions (in minor ways); added the utility function 327 | 'update_wrapper' instead of 'newfunc'. 328 | 329 | ## 1.1 (2006-12-02) 330 | 331 | 'decorator' instances now have attributes __name__, __doc__, 332 | __module__ and __dict__ coming from the associated caller function; 333 | included the licence into the documentation. 334 | 335 | ## 1.0 (2006-08-10) 336 | 337 | Added LICENSE.txt; added a setuptools-friendly setup.py script 338 | contributed by Luke Arno. 339 | 340 | ## 0.8.1 (2006-06-21) 341 | 342 | Minor fixes to the documentation. 343 | 344 | ## 0.8 (2006-06-16) 345 | 346 | Improved the documentation, added the 'caveats' section. 347 | 348 | ## 0.7.1 (2006-05-15) 349 | 350 | Improved the tail_recursive example. 351 | 352 | ## 0.7 (2006-05-10) 353 | 354 | Renamed 'copyfunc' into 'newfunc' and added the ability to copy 355 | the signature from a model function; improved '_decorator' to 356 | set the '__module__' attribute too, with the intent of improving 357 | error messages; updated the documentation. 358 | 359 | ## 0.6 (2005-12-20) 360 | 361 | Changed decorator.__call__ so that the module somewhat works 362 | even for Python 2.3 (but the signature-preserving feature is 363 | lost). 364 | 365 | ## 0.5.2 (2005-06-28) 366 | 367 | Minor changes to the documentation; improved `getattr_` and 368 | shortened `locked`. 369 | 370 | ## 0.5.1 (2005-05-20) 371 | 372 | Minor corrections to the documentation. 373 | 374 | ## 0.5 (2005-05-19) 375 | 376 | Fixed a bug with out-of-the-mind signatures, added a check for 377 | reserved names in the argument list and simplified the code (thanks to 378 | Duncan Booth). 379 | 380 | ## 0.4.1 (2005-05-17) 381 | 382 | Fixed a typo in the documentation (thanks to Anthon van der Neut). 383 | 384 | ## 0.4 (2005-05-12) 385 | 386 | Added getinfo, some tests and improved the documentation. 387 | 388 | ## 0.3 (2005-05-10) 389 | 390 | Simplified copyfunc, renamed deferred to delayed and added the 391 | nonblocking example. 392 | 393 | ## 0.2 (2005-05-09) 394 | 395 | Added copyfunc, improved the multithreading examples, improved the 396 | doctester program. 397 | 398 | ## 0.1.1 (2005-05-06) 399 | 400 | Added the license specification and two docstrings. 401 | 402 | ## 0.1 (2005-05-04) 403 | 404 | Initial release. 405 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import doctest 3 | import unittest 4 | import decimal 5 | import inspect 6 | import functools 7 | import asyncio 8 | from collections import defaultdict, ChainMap, abc as c 9 | from decorator import dispatch_on, contextmanager, decorator 10 | try: 11 | from . import documentation as doc # good with pytest 12 | except ImportError: 13 | import documentation as doc # good with `python src/tests/test.py` 14 | 15 | 16 | @contextmanager 17 | def assertRaises(etype): 18 | """This works in Python 2.6 too""" 19 | try: 20 | yield 21 | except etype: 22 | pass 23 | else: 24 | raise Exception('Expected %s' % etype.__name__) 25 | 26 | 27 | @decorator 28 | async def before_after(coro, *args, **kwargs): 29 | return "" + (await coro(*args, **kwargs)) + "" 30 | 31 | 32 | @decorator 33 | def coro_to_func(coro, *args, **kw): 34 | return asyncio.run(coro(*args, **kw)) 35 | 36 | 37 | class CoroutineTestCase(unittest.TestCase): 38 | def test_before_after(self): 39 | @before_after 40 | async def coro(x): 41 | return x 42 | self.assertTrue(inspect.iscoroutinefunction(coro)) 43 | out = asyncio.run(coro('x')) 44 | self.assertEqual(out, 'x') 45 | 46 | def test_coro_to_func(self): 47 | @coro_to_func 48 | async def coro(x): 49 | return x 50 | self.assertFalse(inspect.iscoroutinefunction(coro)) 51 | self.assertEqual(coro('x'), 'x') 52 | 53 | 54 | def gen123(): 55 | yield 1 56 | yield 2 57 | yield 3 58 | 59 | 60 | class GeneratorCallerTestCase(unittest.TestCase): 61 | def test_gen123(self): 62 | @decorator 63 | def square(func, *args, **kw): 64 | for x in gen123(): 65 | yield x * x 66 | new = square(gen123) 67 | self.assertTrue(inspect.isgeneratorfunction(new)) 68 | self.assertEqual(list(new()), [1, 4, 9]) 69 | 70 | 71 | class DocumentationTestCase(unittest.TestCase): 72 | def test(self): 73 | err = doctest.testmod(doc)[0] 74 | self.assertEqual(err, 0) 75 | 76 | def test_copy_dunder_attrs(self): 77 | traced = doc.trace(doc.foo) 78 | self.assertIn('documentation', traced.__module__) 79 | self.assertEqual(traced.__annotations__, {}) 80 | self.assertEqual(traced.__defaults__, (None,)) 81 | 82 | def test_singledispatch1(self): 83 | with assertRaises(RuntimeError): 84 | doc.singledispatch_example1() 85 | 86 | def test_singledispatch2(self): 87 | doc.singledispatch_example2() 88 | 89 | def test_context_manager(self): 90 | 91 | @contextmanager 92 | def before_after(before, after): 93 | print(before) 94 | yield 95 | print(after) 96 | 97 | @before_after('BEFORE', 'AFTER') 98 | def hello_user(user): 99 | print('hello %s' % user) 100 | 101 | argspec = inspect.getfullargspec(hello_user) 102 | self.assertEqual(argspec.args, ['user']) 103 | 104 | 105 | class ExtraTestCase(unittest.TestCase): 106 | def test_qualname(self): 107 | self.assertEqual(doc.operation1.__qualname__, 'operation1') 108 | 109 | def test_signature(self): 110 | sig = inspect.signature(doc.f1) 111 | self.assertEqual(str(sig), '(x)') 112 | 113 | def test_unique_filenames(self): 114 | @decorator 115 | def d1(f, *args, **kwargs): 116 | return f(*args, **kwargs) 117 | 118 | @decorator 119 | def d2(f, *args, **kwargs): 120 | return f(*args, **kwargs) 121 | 122 | @d1 123 | def f1(x, y, z): 124 | pass 125 | 126 | @d2 127 | def f2(x, y, z): 128 | pass 129 | 130 | f1_orig = f1 131 | 132 | @d1 133 | def f1(x, y, z): 134 | pass 135 | 136 | self.assertEqual(d1.__code__.co_filename, 137 | d2.__code__.co_filename) 138 | self.assertEqual(f1.__code__.co_filename, 139 | f2.__code__.co_filename) 140 | self.assertEqual(f1_orig.__code__.co_filename, 141 | f1.__code__.co_filename) 142 | 143 | def test_no_first_arg(self): 144 | @decorator 145 | def example(*args, **kw): 146 | return args[0](*args[1:], **kw) 147 | 148 | @example 149 | def func(**kw): 150 | "Docstring" 151 | return kw 152 | 153 | # there is no confusion when passing args as a keyword argument 154 | self.assertEqual(func(args='a'), {'args': 'a'}) 155 | self.assertEqual(func.__doc__, "Docstring") 156 | 157 | def test_decorator_factory(self): 158 | # similar to what IPython is doing in traitlets.config.application 159 | @decorator 160 | def catch_config_error(method, app, *args, **kwargs): 161 | return method(app) 162 | catch_config_error(lambda app, **kw: None)(1) 163 | 164 | def test_add1(self): 165 | # similar to what IPython is doing in traitlets.config.application 166 | @decorator 167 | def add(func, const=1, *args, **kwargs): 168 | return const + func(*args, **kwargs) 169 | 170 | def f(x): 171 | return x 172 | self.assertEqual(add(f, 2)(0), 2) 173 | 174 | def test_dan_schult(self): 175 | # see https://github.com/micheles/decorator/issues/120 176 | @decorator 177 | def prnt(func, index=0, *args, **kw): 178 | print(args[index]) 179 | return func(*args, **kw) 180 | 181 | @prnt(index=2) # print the value of the third argument 182 | def f(a, b, c=None): 183 | return [a, b, c] 184 | 185 | self.assertEqual(f(0, 1), [0, 1, None]) 186 | 187 | def test_slow_wrapper(self): 188 | # see https://github.com/micheles/decorator/issues/123 189 | dd = defaultdict(list) 190 | doc.trace(defaultdict.__setitem__)(dd, 'x', [1]) 191 | self.assertEqual(dd['x'], [1]) 192 | doc.trace(defaultdict.__delitem__)(dd, 'x') 193 | self.assertEqual(dd['x'], []) 194 | # NB: defaultdict.__getitem__ has no signature and cannot be 195 | # decorated in CPython, while it is regular in PyPy 196 | 197 | 198 | # ################### test dispatch_on ############################# # 199 | # adapted from test_functools in Python 3.5 200 | singledispatch = dispatch_on('obj') 201 | 202 | 203 | class TestSingleDispatch(unittest.TestCase): 204 | def test_simple_overloads(self): 205 | @singledispatch 206 | def g(obj): 207 | return "base" 208 | 209 | @g.register(int) 210 | def g_int(i): 211 | return "integer" 212 | 213 | self.assertEqual(g("str"), "base") 214 | self.assertEqual(g(1), "integer") 215 | self.assertEqual(g([1, 2, 3]), "base") 216 | 217 | def test_mro(self): 218 | @singledispatch 219 | def g(obj): 220 | return "base" 221 | 222 | class A(object): 223 | pass 224 | 225 | class C(A): 226 | pass 227 | 228 | class B(A): 229 | pass 230 | 231 | class D(C, B): 232 | pass 233 | 234 | @g.register(A) 235 | def g_A(a): 236 | return "A" 237 | 238 | @g.register(B) 239 | def g_B(b): 240 | return "B" 241 | 242 | self.assertEqual(g(A()), "A") 243 | self.assertEqual(g(B()), "B") 244 | self.assertEqual(g(C()), "A") 245 | self.assertEqual(g(D()), "B") 246 | 247 | def test_register_decorator(self): 248 | @singledispatch 249 | def g(obj): 250 | return "base" 251 | 252 | @g.register(int) 253 | def g_int(i): 254 | return "int %s" % (i,) 255 | self.assertEqual(g(""), "base") 256 | self.assertEqual(g(12), "int 12") 257 | 258 | def test_register_error(self): 259 | @singledispatch 260 | def g(obj): 261 | return "base" 262 | 263 | with assertRaises(TypeError): 264 | # wrong number of arguments 265 | @g.register(int) 266 | def g_int(): 267 | return "int" 268 | 269 | def test_wrapping_attributes(self): 270 | @singledispatch 271 | def g(obj): 272 | "Simple test" 273 | return "Test" 274 | self.assertEqual(g.__name__, "g") 275 | if sys.flags.optimize < 2: 276 | self.assertEqual(g.__doc__, "Simple test") 277 | 278 | def test_c_classes(self): 279 | @singledispatch 280 | def g(obj): 281 | return "base" 282 | 283 | @g.register(decimal.DecimalException) 284 | def _(obj): 285 | return obj.args 286 | subn = decimal.Subnormal("Exponent < Emin") 287 | rnd = decimal.Rounded("Number got rounded") 288 | self.assertEqual(g(subn), ("Exponent < Emin",)) 289 | self.assertEqual(g(rnd), ("Number got rounded",)) 290 | 291 | @g.register(decimal.Subnormal) 292 | def _g(obj): 293 | return "Too small to care." 294 | self.assertEqual(g(subn), "Too small to care.") 295 | self.assertEqual(g(rnd), ("Number got rounded",)) 296 | 297 | def test_register_abc(self): 298 | d = {"a": "b"} 299 | l = [1, 2, 3] 300 | s = set([object(), None]) 301 | f = frozenset(s) 302 | t = (1, 2, 3) 303 | 304 | @singledispatch 305 | def g(obj): 306 | return "base" 307 | 308 | self.assertEqual(g(d), "base") 309 | self.assertEqual(g(l), "base") 310 | self.assertEqual(g(s), "base") 311 | self.assertEqual(g(f), "base") 312 | self.assertEqual(g(t), "base") 313 | 314 | g.register(c.Sized)(lambda obj: "sized") 315 | self.assertEqual(g(d), "sized") 316 | self.assertEqual(g(l), "sized") 317 | self.assertEqual(g(s), "sized") 318 | self.assertEqual(g(f), "sized") 319 | self.assertEqual(g(t), "sized") 320 | 321 | g.register(c.MutableMapping)(lambda obj: "mutablemapping") 322 | self.assertEqual(g(d), "mutablemapping") 323 | self.assertEqual(g(l), "sized") 324 | self.assertEqual(g(s), "sized") 325 | self.assertEqual(g(f), "sized") 326 | self.assertEqual(g(t), "sized") 327 | 328 | g.register(ChainMap)(lambda obj: "chainmap") 329 | # irrelevant ABCs registered 330 | self.assertEqual(g(d), "mutablemapping") 331 | self.assertEqual(g(l), "sized") 332 | self.assertEqual(g(s), "sized") 333 | self.assertEqual(g(f), "sized") 334 | self.assertEqual(g(t), "sized") 335 | 336 | g.register(c.MutableSequence)(lambda obj: "mutablesequence") 337 | self.assertEqual(g(d), "mutablemapping") 338 | self.assertEqual(g(l), "mutablesequence") 339 | self.assertEqual(g(s), "sized") 340 | self.assertEqual(g(f), "sized") 341 | self.assertEqual(g(t), "sized") 342 | 343 | g.register(c.MutableSet)(lambda obj: "mutableset") 344 | self.assertEqual(g(d), "mutablemapping") 345 | self.assertEqual(g(l), "mutablesequence") 346 | self.assertEqual(g(s), "mutableset") 347 | self.assertEqual(g(f), "sized") 348 | self.assertEqual(g(t), "sized") 349 | 350 | g.register(c.Mapping)(lambda obj: "mapping") 351 | self.assertEqual(g(d), "mutablemapping") # not specific enough 352 | self.assertEqual(g(l), "mutablesequence") 353 | self.assertEqual(g(s), "mutableset") 354 | self.assertEqual(g(f), "sized") 355 | self.assertEqual(g(t), "sized") 356 | 357 | g.register(c.Sequence)(lambda obj: "sequence") 358 | self.assertEqual(g(d), "mutablemapping") 359 | self.assertEqual(g(l), "mutablesequence") 360 | self.assertEqual(g(s), "mutableset") 361 | self.assertEqual(g(f), "sized") 362 | self.assertEqual(g(t), "sequence") 363 | 364 | g.register(c.Set)(lambda obj: "set") 365 | self.assertEqual(g(d), "mutablemapping") 366 | self.assertEqual(g(l), "mutablesequence") 367 | self.assertEqual(g(s), "mutableset") 368 | self.assertEqual(g(f), "set") 369 | self.assertEqual(g(t), "sequence") 370 | 371 | g.register(dict)(lambda obj: "dict") 372 | self.assertEqual(g(d), "dict") 373 | self.assertEqual(g(l), "mutablesequence") 374 | self.assertEqual(g(s), "mutableset") 375 | self.assertEqual(g(f), "set") 376 | self.assertEqual(g(t), "sequence") 377 | 378 | g.register(list)(lambda obj: "list") 379 | self.assertEqual(g(d), "dict") 380 | self.assertEqual(g(l), "list") 381 | self.assertEqual(g(s), "mutableset") 382 | self.assertEqual(g(f), "set") 383 | self.assertEqual(g(t), "sequence") 384 | 385 | g.register(set)(lambda obj: "concrete-set") 386 | self.assertEqual(g(d), "dict") 387 | self.assertEqual(g(l), "list") 388 | self.assertEqual(g(s), "concrete-set") 389 | self.assertEqual(g(f), "set") 390 | self.assertEqual(g(t), "sequence") 391 | 392 | g.register(frozenset)(lambda obj: "frozen-set") 393 | self.assertEqual(g(d), "dict") 394 | self.assertEqual(g(l), "list") 395 | self.assertEqual(g(s), "concrete-set") 396 | self.assertEqual(g(f), "frozen-set") 397 | self.assertEqual(g(t), "sequence") 398 | 399 | g.register(tuple)(lambda obj: "tuple") 400 | self.assertEqual(g(d), "dict") 401 | self.assertEqual(g(l), "list") 402 | self.assertEqual(g(s), "concrete-set") 403 | self.assertEqual(g(f), "frozen-set") 404 | self.assertEqual(g(t), "tuple") 405 | 406 | def test_mro_conflicts(self): 407 | @singledispatch 408 | def g(obj): 409 | return "base" 410 | 411 | class O(c.Sized): 412 | def __len__(self): 413 | return 0 414 | o = O() 415 | self.assertEqual(g(o), "base") 416 | g.register(c.Iterable)(lambda arg: "iterable") 417 | g.register(c.Container)(lambda arg: "container") 418 | g.register(c.Sized)(lambda arg: "sized") 419 | g.register(c.Set)(lambda arg: "set") 420 | self.assertEqual(g(o), "sized") 421 | c.Iterable.register(O) 422 | self.assertEqual(g(o), "sized") 423 | c.Container.register(O) 424 | with assertRaises(RuntimeError): # was "sized" because in mro 425 | self.assertEqual(g(o), "sized") 426 | c.Set.register(O) 427 | self.assertEqual(g(o), "set") 428 | 429 | class P(object): 430 | pass 431 | p = P() 432 | self.assertEqual(g(p), "base") 433 | c.Iterable.register(P) 434 | self.assertEqual(g(p), "iterable") 435 | c.Container.register(P) 436 | 437 | with assertRaises(RuntimeError): 438 | self.assertEqual(g(p), "iterable") 439 | 440 | class Q(c.Sized): 441 | def __len__(self): 442 | return 0 443 | q = Q() 444 | self.assertEqual(g(q), "sized") 445 | c.Iterable.register(Q) 446 | self.assertEqual(g(q), "sized") 447 | c.Set.register(Q) 448 | self.assertEqual(g(q), "set") 449 | # because c.Set is a subclass of c.Sized and c.Iterable 450 | 451 | @singledispatch 452 | def h(obj): 453 | return "base" 454 | 455 | @h.register(c.Sized) 456 | def h_sized(arg): 457 | return "sized" 458 | 459 | @h.register(c.Container) 460 | def h_container(arg): 461 | return "container" 462 | # Even though Sized and Container are explicit bases of MutableMapping, 463 | # this ABC is implicitly registered on defaultdict which makes all of 464 | # MutableMapping's bases implicit as well from defaultdict's 465 | # perspective. 466 | with assertRaises(RuntimeError): 467 | self.assertEqual(h(defaultdict(lambda: 0)), "sized") 468 | 469 | class R(defaultdict): 470 | pass 471 | c.MutableSequence.register(R) 472 | 473 | @singledispatch 474 | def i(obj): 475 | return "base" 476 | 477 | @i.register(c.MutableMapping) 478 | def i_mapping(arg): 479 | return "mapping" 480 | 481 | @i.register(c.MutableSequence) 482 | def i_sequence(arg): 483 | return "sequence" 484 | r = R() 485 | with assertRaises(RuntimeError): # was no error 486 | self.assertEqual(i(r), "sequence") 487 | 488 | class S(object): 489 | pass 490 | 491 | class T(S, c.Sized): 492 | def __len__(self): 493 | return 0 494 | t = T() 495 | self.assertEqual(h(t), "sized") 496 | c.Container.register(T) 497 | self.assertEqual(h(t), "sized") # because it's explicitly in the MRO 498 | 499 | class U(object): 500 | def __len__(self): 501 | return 0 502 | u = U() 503 | self.assertEqual(h(u), "sized") 504 | # implicit Sized subclass inferred 505 | # from the existence of __len__() 506 | 507 | c.Container.register(U) 508 | # There is no preference for registered versus inferred ABCs. 509 | with assertRaises(RuntimeError): 510 | h(u) 511 | 512 | 513 | @decorator 514 | def partial_before_after(func, *args, **kwargs): 515 | return "" + func(*args, **kwargs) + "" 516 | 517 | 518 | class PartialTestCase(unittest.TestCase): 519 | def test_before_after(self): 520 | def origin_func(x, y): 521 | return x + y 522 | _func = functools.partial(origin_func, "x") 523 | partial_func = partial_before_after(_func) 524 | out = partial_func("y") 525 | self.assertEqual(out, 'xy') 526 | 527 | 528 | if __name__ == '__main__': 529 | unittest.main() 530 | -------------------------------------------------------------------------------- /src/decorator.py: -------------------------------------------------------------------------------- 1 | # ######################### LICENSE ############################ # 2 | 3 | # Copyright (c) 2005-2025, Michele Simionato 4 | # All rights reserved. 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | 10 | # Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # Redistributions in bytecode form must reproduce the above copyright 13 | # notice, this list of conditions and the following disclaimer in 14 | # the documentation and/or other materials provided with the 15 | # distribution. 16 | 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 24 | # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 26 | # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 27 | # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 28 | # DAMAGE. 29 | 30 | """ 31 | Decorator module, see 32 | https://github.com/micheles/decorator/blob/master/docs/documentation.md 33 | for the documentation. 34 | """ 35 | import re 36 | import sys 37 | import inspect 38 | import operator 39 | import itertools 40 | import functools 41 | from contextlib import _GeneratorContextManager 42 | from inspect import getfullargspec, iscoroutinefunction, isgeneratorfunction 43 | from typing import Any, Dict, List, Optional 44 | 45 | __version__ = '5.2.0' 46 | 47 | DEF = re.compile(r'\s*def\s*([_\w][_\w\d]*)\s*\(') 48 | POS = inspect.Parameter.POSITIONAL_OR_KEYWORD 49 | EMPTY = inspect.Parameter.empty 50 | 51 | 52 | # this is not used anymore in the core, but kept for backward compatibility 53 | class FunctionMaker(object): 54 | """ 55 | An object with the ability to create functions with a given signature. 56 | It has attributes name, doc, module, signature, defaults, dict and 57 | methods update and make. 58 | """ 59 | 60 | # Atomic get-and-increment provided by the GIL 61 | _compile_count = itertools.count() 62 | 63 | # make pylint happy 64 | args: List[str] = [] 65 | varargs = varkw = defaults = None 66 | kwonlyargs: List[str] = [] 67 | kwonlydefaults: Optional[Dict[str, Any]] = None 68 | 69 | def __init__(self, func=None, name=None, signature=None, 70 | defaults=None, doc=None, module=None, funcdict=None): 71 | self.shortsignature = signature 72 | if func: 73 | # func can be a class or a callable, but not an instance method 74 | self.name = func.__name__ 75 | if self.name == '': # small hack for lambda functions 76 | self.name = '_lambda_' 77 | self.doc = func.__doc__ 78 | self.module = func.__module__ 79 | if inspect.isroutine(func) or isinstance(func, functools.partial): 80 | argspec = getfullargspec(func) 81 | self.annotations = getattr(func, '__annotations__', {}) 82 | for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs', 83 | 'kwonlydefaults'): 84 | setattr(self, a, getattr(argspec, a)) 85 | for i, arg in enumerate(self.args): 86 | setattr(self, 'arg%d' % i, arg) 87 | allargs = list(self.args) 88 | allshortargs = list(self.args) 89 | if self.varargs: 90 | allargs.append('*' + self.varargs) 91 | allshortargs.append('*' + self.varargs) 92 | elif self.kwonlyargs: 93 | allargs.append('*') # single star syntax 94 | for a in self.kwonlyargs: 95 | allargs.append('%s=None' % a) 96 | allshortargs.append('%s=%s' % (a, a)) 97 | if self.varkw: 98 | allargs.append('**' + self.varkw) 99 | allshortargs.append('**' + self.varkw) 100 | self.signature = ', '.join(allargs) 101 | self.shortsignature = ', '.join(allshortargs) 102 | self.dict = func.__dict__.copy() 103 | # func=None happens when decorating a caller 104 | if name: 105 | self.name = name 106 | if signature is not None: 107 | self.signature = signature 108 | if defaults: 109 | self.defaults = defaults 110 | if doc: 111 | self.doc = doc 112 | if module: 113 | self.module = module 114 | if funcdict: 115 | self.dict = funcdict 116 | # check existence required attributes 117 | assert hasattr(self, 'name') 118 | if not hasattr(self, 'signature'): 119 | raise TypeError('You are decorating a non function: %s' % func) 120 | 121 | def update(self, func, **kw): 122 | """ 123 | Update the signature of func with the data in self 124 | """ 125 | func.__name__ = self.name 126 | func.__doc__ = getattr(self, 'doc', None) 127 | func.__dict__ = getattr(self, 'dict', {}) 128 | func.__defaults__ = self.defaults 129 | func.__kwdefaults__ = self.kwonlydefaults or None 130 | func.__annotations__ = getattr(self, 'annotations', None) 131 | try: 132 | frame = sys._getframe(3) 133 | except AttributeError: # for IronPython and similar implementations 134 | callermodule = '?' 135 | else: 136 | callermodule = frame.f_globals.get('__name__', '?') 137 | func.__module__ = getattr(self, 'module', callermodule) 138 | func.__dict__.update(kw) 139 | 140 | def make(self, src_templ, evaldict=None, addsource=False, **attrs): 141 | """ 142 | Make a new function from a given template and update the signature 143 | """ 144 | src = src_templ % vars(self) # expand name and signature 145 | evaldict = evaldict or {} 146 | mo = DEF.search(src) 147 | if mo is None: 148 | raise SyntaxError('not a valid function template\n%s' % src) 149 | name = mo.group(1) # extract the function name 150 | names = set([name] + [arg.strip(' *') for arg in 151 | self.shortsignature.split(',')]) 152 | for n in names: 153 | if n in ('_func_', '_call_'): 154 | raise NameError('%s is overridden in\n%s' % (n, src)) 155 | 156 | if not src.endswith('\n'): # add a newline for old Pythons 157 | src += '\n' 158 | 159 | # Ensure each generated function has a unique filename for profilers 160 | # (such as cProfile) that depend on the tuple of (, 161 | # , ) being unique. 162 | filename = '' % next(self._compile_count) 163 | try: 164 | code = compile(src, filename, 'single') 165 | exec(code, evaldict) 166 | except Exception: 167 | print('Error in generated code:', file=sys.stderr) 168 | print(src, file=sys.stderr) 169 | raise 170 | func = evaldict[name] 171 | if addsource: 172 | attrs['__source__'] = src 173 | self.update(func, **attrs) 174 | return func 175 | 176 | @classmethod 177 | def create(cls, obj, body, evaldict, defaults=None, 178 | doc=None, module=None, addsource=True, **attrs): 179 | """ 180 | Create a function from the strings name, signature and body. 181 | evaldict is the evaluation dictionary. If addsource is true an 182 | attribute __source__ is added to the result. The attributes attrs 183 | are added, if any. 184 | """ 185 | if isinstance(obj, str): # "name(signature)" 186 | name, rest = obj.strip().split('(', 1) 187 | signature = rest[:-1] # strip a right parens 188 | func = None 189 | else: # a function 190 | name = None 191 | signature = None 192 | func = obj 193 | self = cls(func, name, signature, defaults, doc, module) 194 | ibody = '\n'.join(' ' + line for line in body.splitlines()) 195 | caller = evaldict.get('_call_') # when called from `decorate` 196 | if caller and iscoroutinefunction(caller): 197 | body = ('async def %(name)s(%(signature)s):\n' + ibody).replace( 198 | 'return', 'return await') 199 | else: 200 | body = 'def %(name)s(%(signature)s):\n' + ibody 201 | return self.make(body, evaldict, addsource, **attrs) 202 | 203 | 204 | def fix(args, kwargs, sig): 205 | """ 206 | Fix args and kwargs to be consistent with the signature 207 | """ 208 | ba = sig.bind(*args, **kwargs) 209 | ba.apply_defaults() # needed for test_dan_schult 210 | return ba.args, ba.kwargs 211 | 212 | 213 | def decorate(func, caller, extras=(), kwsyntax=False): 214 | """ 215 | Decorates a function/generator/coroutine using a caller. 216 | If kwsyntax is True calling the decorated functions with keyword 217 | syntax will pass the named arguments inside the ``kw`` dictionary, 218 | even if such argument are positional, similarly to what functools.wraps 219 | does. By default kwsyntax is False and the the arguments are untouched. 220 | """ 221 | sig = inspect.signature(func) 222 | if isinstance(func, functools.partial): 223 | func = functools.update_wrapper(func, func.func) 224 | if iscoroutinefunction(caller): 225 | async def fun(*args, **kw): 226 | if not kwsyntax: 227 | args, kw = fix(args, kw, sig) 228 | return await caller(func, *(extras + args), **kw) 229 | elif isgeneratorfunction(caller): 230 | def fun(*args, **kw): 231 | if not kwsyntax: 232 | args, kw = fix(args, kw, sig) 233 | for res in caller(func, *(extras + args), **kw): 234 | yield res 235 | else: 236 | def fun(*args, **kw): 237 | if not kwsyntax: 238 | args, kw = fix(args, kw, sig) 239 | return caller(func, *(extras + args), **kw) 240 | 241 | fun.__name__ = func.__name__ 242 | fun.__doc__ = func.__doc__ 243 | fun.__wrapped__ = func 244 | fun.__signature__ = sig 245 | fun.__qualname__ = func.__qualname__ 246 | # builtin functions like defaultdict.__setitem__ lack many attributes 247 | try: 248 | fun.__defaults__ = func.__defaults__ 249 | except AttributeError: 250 | pass 251 | try: 252 | fun.__kwdefaults__ = func.__kwdefaults__ 253 | except AttributeError: 254 | pass 255 | try: 256 | fun.__annotations__ = func.__annotations__ 257 | except AttributeError: 258 | pass 259 | try: 260 | fun.__module__ = func.__module__ 261 | except AttributeError: 262 | pass 263 | try: 264 | fun.__dict__.update(func.__dict__) 265 | except AttributeError: 266 | pass 267 | return fun 268 | 269 | 270 | def decoratorx(caller): 271 | """ 272 | A version of "decorator" implemented via "exec" and not via the 273 | Signature object. Use this if you are want to preserve the `.__code__` 274 | object properties (https://github.com/micheles/decorator/issues/129). 275 | """ 276 | def dec(func): 277 | return FunctionMaker.create( 278 | func, 279 | "return _call_(_func_, %(shortsignature)s)", 280 | dict(_call_=caller, _func_=func), 281 | __wrapped__=func, __qualname__=func.__qualname__) 282 | return dec 283 | 284 | 285 | def decorator(caller, _func=None, kwsyntax=False): 286 | """ 287 | decorator(caller) converts a caller function into a decorator 288 | """ 289 | if _func is not None: # return a decorated function 290 | # this is obsolete behavior; you should use decorate instead 291 | return decorate(_func, caller, (), kwsyntax) 292 | # else return a decorator function 293 | sig = inspect.signature(caller) 294 | dec_params = [p for p in sig.parameters.values() if p.kind is POS] 295 | 296 | def dec(func=None, *args, **kw): 297 | na = len(args) + 1 298 | extras = args + tuple(kw.get(p.name, p.default) 299 | for p in dec_params[na:] 300 | if p.default is not EMPTY) 301 | if func is None: 302 | return lambda func: decorate(func, caller, extras, kwsyntax) 303 | else: 304 | return decorate(func, caller, extras, kwsyntax) 305 | dec.__signature__ = sig.replace(parameters=dec_params) 306 | dec.__name__ = caller.__name__ 307 | dec.__doc__ = caller.__doc__ 308 | dec.__wrapped__ = caller 309 | dec.__qualname__ = caller.__qualname__ 310 | dec.__kwdefaults__ = getattr(caller, '__kwdefaults__', None) 311 | dec.__dict__.update(caller.__dict__) 312 | return dec 313 | 314 | 315 | # ####################### contextmanager ####################### # 316 | 317 | 318 | class ContextManager(_GeneratorContextManager): 319 | def __init__(self, g, *a, **k): 320 | _GeneratorContextManager.__init__(self, g, a, k) 321 | 322 | def __call__(self, func): 323 | def caller(f, *a, **k): 324 | with self.__class__(self.func, *self.args, **self.kwds): 325 | return f(*a, **k) 326 | return decorate(func, caller) 327 | 328 | 329 | _contextmanager = decorator(ContextManager) 330 | 331 | 332 | def contextmanager(func): 333 | # Enable Pylint config: contextmanager-decorators=decorator.contextmanager 334 | return _contextmanager(func) 335 | 336 | 337 | # ############################ dispatch_on ############################ # 338 | 339 | def append(a, vancestors): 340 | """ 341 | Append ``a`` to the list of the virtual ancestors, unless it is already 342 | included. 343 | """ 344 | add = True 345 | for j, va in enumerate(vancestors): 346 | if issubclass(va, a): 347 | add = False 348 | break 349 | if issubclass(a, va): 350 | vancestors[j] = a 351 | add = False 352 | if add: 353 | vancestors.append(a) 354 | 355 | 356 | # inspired from simplegeneric by P.J. Eby and functools.singledispatch 357 | def dispatch_on(*dispatch_args): 358 | """ 359 | Factory of decorators turning a function into a generic function 360 | dispatching on the given arguments. 361 | """ 362 | assert dispatch_args, 'No dispatch args passed' 363 | dispatch_str = '(%s,)' % ', '.join(dispatch_args) 364 | 365 | def check(arguments, wrong=operator.ne, msg=''): 366 | """Make sure one passes the expected number of arguments""" 367 | if wrong(len(arguments), len(dispatch_args)): 368 | raise TypeError('Expected %d arguments, got %d%s' % 369 | (len(dispatch_args), len(arguments), msg)) 370 | 371 | def gen_func_dec(func): 372 | """Decorator turning a function into a generic function""" 373 | 374 | # first check the dispatch arguments 375 | argset = set(getfullargspec(func).args) 376 | if not set(dispatch_args) <= argset: 377 | raise NameError('Unknown dispatch arguments %s' % dispatch_str) 378 | 379 | typemap = {} 380 | 381 | def vancestors(*types): 382 | """ 383 | Get a list of sets of virtual ancestors for the given types 384 | """ 385 | check(types) 386 | ras = [[] for _ in range(len(dispatch_args))] 387 | for types_ in typemap: 388 | for t, type_, ra in zip(types, types_, ras): 389 | if issubclass(t, type_) and type_ not in t.mro(): 390 | append(type_, ra) 391 | return [set(ra) for ra in ras] 392 | 393 | def ancestors(*types): 394 | """ 395 | Get a list of virtual MROs, one for each type 396 | """ 397 | check(types) 398 | lists = [] 399 | for t, vas in zip(types, vancestors(*types)): 400 | n_vas = len(vas) 401 | if n_vas > 1: 402 | raise RuntimeError( 403 | 'Ambiguous dispatch for %s: %s' % (t, vas)) 404 | elif n_vas == 1: 405 | va, = vas 406 | mro = type('t', (t, va), {}).mro()[1:] 407 | else: 408 | mro = t.mro() 409 | lists.append(mro[:-1]) # discard t and object 410 | return lists 411 | 412 | def register(*types): 413 | """ 414 | Decorator to register an implementation for the given types 415 | """ 416 | check(types) 417 | 418 | def dec(f): 419 | check(getfullargspec(f).args, operator.lt, ' in ' + f.__name__) 420 | typemap[types] = f 421 | return f 422 | return dec 423 | 424 | def dispatch_info(*types): 425 | """ 426 | An utility to introspect the dispatch algorithm 427 | """ 428 | check(types) 429 | lst = [] 430 | for ancs in itertools.product(*ancestors(*types)): 431 | lst.append(tuple(a.__name__ for a in ancs)) 432 | return lst 433 | 434 | def _dispatch(dispatch_args, *args, **kw): 435 | types = tuple(type(arg) for arg in dispatch_args) 436 | try: # fast path 437 | f = typemap[types] 438 | except KeyError: 439 | pass 440 | else: 441 | return f(*args, **kw) 442 | combinations = itertools.product(*ancestors(*types)) 443 | next(combinations) # the first one has been already tried 444 | for types_ in combinations: 445 | f = typemap.get(types_) 446 | if f is not None: 447 | return f(*args, **kw) 448 | 449 | # else call the default implementation 450 | return func(*args, **kw) 451 | 452 | return FunctionMaker.create( 453 | func, 'return _f_(%s, %%(shortsignature)s)' % dispatch_str, 454 | dict(_f_=_dispatch), register=register, default=func, 455 | typemap=typemap, vancestors=vancestors, ancestors=ancestors, 456 | dispatch_info=dispatch_info, __wrapped__=func) 457 | 458 | gen_func_dec.__name__ = 'dispatch_on' + dispatch_str 459 | return gen_func_dec 460 | -------------------------------------------------------------------------------- /docs/documentation.md: -------------------------------------------------------------------------------- 1 | # Decorators for Humans 2 | 3 | |Author | Michele Simionato| 4 | |---|---| 5 | |E-mail | michele.simionato@gmail.com| 6 | |Version| 5.2.0 (2025-02-22)| 7 | |Supports| Python 3.7, 3.8, 3.9, 3.10, 3.11, 3.12| 8 | |Download page| https://pypi.org/project/decorator/5.2.0| 9 | |Installation| ``pip install decorator``| 10 | |License | BSD license| 11 | 12 | ## Introduction 13 | 14 | The ``decorator`` module is over ten years old, but still alive and 15 | kicking. It is used by several frameworks (IPython, scipy, authkit, 16 | pylons, pycuda, sugar, ...) and has been stable for a *long* time. It 17 | is your best option if you want to preserve the signature of decorated 18 | functions in a consistent way across Python releases. Versions 5.X 19 | supports Python versions greater than 3.4, versions 4.X supports Python 20 | versions back to 2.6; versions 3.X are able to support even Python 2.5 and 21 | 2.4. 22 | 23 | ## What's New in version 5 24 | 25 | Version 5 of the decorator module features a major simplification of 26 | the code base made possible by dropping support for Python releases 27 | older than 3.5. From that version the ``Signature`` object works well 28 | enough that it is possible to fix the signature of a decorated 29 | function without resorting to ``exec`` tricks. The simplification 30 | has a very neat advantage: in case of exceptions raised in decorated 31 | functions the traceback is nicer than it used to be. Moreover, it is 32 | now possible to mimic the behavior of decorators defined with 33 | ``functool.wraps``: see the section about the ``kwsyntax`` flag below. 34 | 35 | ## What's New in version 4 36 | 37 | - **New documentation** 38 | There is now a single manual for all Python versions, so I took the 39 | opportunity to overhaul the documentation. 40 | Even if you are a long-time user, you may want to revisit the docs, since 41 | several examples have been improved. 42 | 43 | - **Packaging improvements** 44 | The code is now also available in wheel format. Integration with 45 | setuptools has improved and you can run the tests with the command 46 | ``python setup.py test`` too. 47 | 48 | - **Code changes** 49 | A new utility function ``decorate(func, caller)`` has been added. 50 | It does the same job that was performed by the older 51 | ``decorator(caller, func)``. The old functionality is now deprecated 52 | and no longer documented, but still available for now. 53 | 54 | - **Multiple dispatch** 55 | The decorator module now includes an implementation of generic 56 | functions (sometimes called "multiple dispatch functions"). 57 | The API is designed to mimic ``functools.singledispatch`` (added 58 | in Python 3.4), but the implementation is much simpler. 59 | Moreover, all decorators involved preserve the signature of the 60 | decorated functions. For now, this exists mostly to demonstrate 61 | the power of the module. In the future it could be enhanced/optimized. 62 | In any case, it is very short and compact (less then 100 lines), so you 63 | can extract it for your own use. Take it as food for thought. 64 | 65 | - **Python 3.5 coroutines** 66 | From version 4.1 it is possible to decorate coroutines, i.e. functions 67 | defined with the `async def` syntax, and to maintain the 68 | `inspect.iscoroutinefunction` check working for the decorated function. 69 | 70 | - **Decorator factories** 71 | From version 4.2 there is facility to define factories of decorators in 72 | a simple way, a feature requested by the users since a long time. 73 | 74 | ## Usefulness of decorators 75 | 76 | Python decorators are an interesting example of why syntactic sugar 77 | matters. In principle, their introduction in Python 2.4 changed 78 | nothing, since they did not provide any new functionality which was not 79 | already present in the language. In practice, their introduction has 80 | significantly changed the way we structure our programs. 81 | I believe the change is for the best, and that decorators are a great 82 | idea since: 83 | 84 | * decorators help reducing boilerplate code; 85 | * decorators help separation of concerns; 86 | * decorators enhance readability and maintenability; 87 | * decorators are explicit. 88 | 89 | Still, as of now, writing custom decorators correctly requires 90 | some experience and it is not as easy as it could be. For instance, 91 | typical implementations of decorators involve nested functions, and 92 | we all know that flat is better than nested. 93 | 94 | The aim of the ``decorator`` module it to simplify the usage of 95 | decorators for the average programmer, and to popularize decorators by 96 | showing various non-trivial examples. Of course, as all techniques, 97 | decorators can be abused (I have seen that) and you should not try to 98 | solve every problem with a decorator, just because you can. 99 | 100 | You may find the source code for all the examples 101 | discussed here in the ``documentation.py`` file, which contains 102 | the documentation you are reading in the form of doctests. 103 | 104 | ## Definitions 105 | 106 | Technically speaking, any Python object which can be called with one argument 107 | can be used as a decorator. However, this definition is somewhat too large 108 | to be really useful. It is more convenient to split the generic class of 109 | decorators in two subclasses: 110 | 111 | 1. **signature-preserving decorators**, callable objects which accept 112 | a function as input and return a function as output, *with the 113 | same signature* 114 | 115 | 2. **signature-changing** decorators, i.e. decorators 116 | which change the signature of their input function, or decorators 117 | that return non-callable objects 118 | 119 | Signature-changing decorators have their use: for instance, the 120 | builtin classes ``staticmethod`` and ``classmethod`` are in this 121 | group. They take functions and return descriptor objects which 122 | are neither functions, nor callables. 123 | 124 | Still, signature-preserving decorators are more common, and easier 125 | to reason about. In particular, they can be composed together, 126 | whereas other decorators generally cannot. 127 | 128 | Writing signature-preserving decorators from scratch is not that 129 | obvious, especially if one wants to define proper decorators that 130 | can accept functions with any signature. A simple example will clarify 131 | the issue. 132 | 133 | ## Statement of the problem 134 | 135 | A very common use case for decorators is the memoization of functions. 136 | A ``memoize`` decorator works by caching 137 | the result of the function call in a dictionary, so that the next time 138 | the function is called with the same input parameters the result is retrieved 139 | from the cache and not recomputed. 140 | 141 | There are many implementations of ``memoize`` in 142 | http://www.python.org/moin/PythonDecoratorLibrary, 143 | but they do not preserve the signature. In recent versions of 144 | Python you can find a sophisticated ``lru_cache`` decorator 145 | in the standard library's ``functools``. Here I am just 146 | interested in giving an example. 147 | 148 | Consider the following simple implementation (note that it is 149 | generally impossible to *correctly* memoize something 150 | that depends on non-hashable arguments): 151 | 152 | ```python 153 | 154 | def memoize_uw(func): 155 | func.cache = {} 156 | 157 | def memoize(*args, **kw): 158 | if kw: # frozenset is used to ensure hashability 159 | key = args, frozenset(kw.items()) 160 | else: 161 | key = args 162 | if key not in func.cache: 163 | func.cache[key] = func(*args, **kw) 164 | return func.cache[key] 165 | return functools.update_wrapper(memoize, func) 166 | ``` 167 | 168 | Here I used the functools.update_wrapper_ utility, which was added 169 | in Python 2.5 to simplify the writing of decorators. 170 | (Previously, you needed to manually copy the function attributes 171 | ``__name__``, ``__doc__``, ``__module__``, and ``__dict__`` 172 | to the decorated function by hand). 173 | 174 | This works insofar as the decorator accepts functions with generic signatures. 175 | Unfortunately, it is *not* a signature-preserving decorator, since 176 | ``memoize_uw`` generally returns a function with a *different signature* 177 | from the original. 178 | 179 | Consider for instance the following case: 180 | 181 | ```python 182 | 183 | @memoize_uw 184 | def f1(x): 185 | "Simulate some long computation" 186 | time.sleep(1) 187 | return x 188 | ``` 189 | 190 | Here, the original function takes a single argument named ``x``, 191 | but the decorated function takes any number of arguments and 192 | keyword arguments: 193 | 194 | ```python 195 | >>> from inspect import getfullargspec 196 | >>> print(getfullargspec(f1)) 197 | FullArgSpec(args=[], varargs='args', varkw='kw', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) 198 | 199 | ``` 200 | 201 | This means that introspection tools like ``getfullargspec`` will give 202 | you false information about the signature of ``f1`` This is pretty bad: 203 | ``getfullargspec`` says that the function accepts the generic 204 | signature ``*args, **kw``, but calling the function with more than one 205 | argument raises an error: 206 | 207 | ```python 208 | >>> f1(0, 1) 209 | Traceback (most recent call last): 210 | ... 211 | TypeError: f1() takes exactly 1 positional argument (2 given) 212 | 213 | ``` 214 | 215 | Notice that ``pydoc`` will give the right signature, but only in Python 216 | versions greater than 3.5. 217 | 218 | ## The solution 219 | 220 | The solution is to provide a generic factory of generators, which 221 | hides the complexity of making signature-preserving decorators 222 | from the application programmer. The ``decorate`` function in 223 | the ``decorator`` module is such a factory: 224 | 225 | ```python 226 | >>> from decorator import decorate 227 | 228 | ``` 229 | 230 | ``decorate`` takes two arguments: 231 | 232 | 1. a caller function describing the functionality of the decorator, and 233 | 234 | 2. a function to be decorated. 235 | 236 | The caller function must have signature ``(f, *args, **kw)``, and it 237 | must call the original function ``f`` with arguments ``args`` and ``kw``, 238 | implementing the wanted capability (in this case, memoization): 239 | 240 | ```python 241 | 242 | def _memoize(func, *args, **kw): 243 | if kw: # frozenset is used to ensure hashability 244 | key = args, frozenset(kw.items()) 245 | else: 246 | key = args 247 | cache = func.cache # attribute added by memoize 248 | if key not in cache: 249 | cache[key] = func(*args, **kw) 250 | return cache[key] 251 | ``` 252 | 253 | Now, you can define your decorator as follows: 254 | 255 | ```python 256 | 257 | def memoize(f): 258 | """ 259 | A simple memoize implementation. It works by adding a .cache dictionary 260 | to the decorated function. The cache will grow indefinitely, so it is 261 | your responsibility to clear it, if needed. 262 | """ 263 | f.cache = {} 264 | return decorate(f, _memoize) 265 | ``` 266 | 267 | The difference from the nested function approach of ``memoize_uw`` 268 | is that the decorator module forces you to lift the inner function 269 | to the outer level. Moreover, you are forced to explicitly pass the 270 | function you want to decorate; there are no closures. 271 | 272 | Here is a test of usage: 273 | 274 | ```python 275 | >>> @memoize 276 | ... def heavy_computation(): 277 | ... time.sleep(2) 278 | ... return "done" 279 | 280 | >>> print(heavy_computation()) # the first time it will take 2 seconds 281 | done 282 | 283 | >>> print(heavy_computation()) # the second time it will be instantaneous 284 | done 285 | 286 | ``` 287 | 288 | The signature of ``heavy_computation`` is the one you would expect: 289 | 290 | ```python 291 | >>> print(getfullargspec(heavy_computation)) 292 | FullArgSpec(args=[], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) 293 | 294 | ``` 295 | 296 | ## A ``trace`` decorator 297 | 298 | Here is an example of how to define a simple ``trace`` decorator, 299 | which prints a message whenever the traced function is called: 300 | 301 | ```python 302 | 303 | def _trace(f, *args, **kw): 304 | kwstr = ', '.join('%r: %r' % (k, kw[k]) for k in sorted(kw)) 305 | print("calling %s with args %s, {%s}" % (f.__name__, args, kwstr)) 306 | return f(*args, **kw) 307 | ``` 308 | 309 | ```python 310 | 311 | def trace(f): 312 | return decorate(f, _trace) 313 | ``` 314 | 315 | Here is an example of usage: 316 | 317 | ```python 318 | >>> @trace 319 | ... def f1(x): 320 | ... pass 321 | 322 | ``` 323 | 324 | It is immediate to verify that ``f1`` works... 325 | 326 | ```python 327 | >>> f1(0) 328 | calling f1 with args (0,), {} 329 | 330 | ``` 331 | 332 | ...and it that it has the correct signature: 333 | 334 | ```python 335 | >>> print(getfullargspec(f1)) 336 | FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) 337 | 338 | ``` 339 | 340 | The decorator works with functions of any signature: 341 | 342 | ```python 343 | >>> @trace 344 | ... def f(x, y=1, *args, **kw): 345 | ... pass 346 | 347 | >>> f(0, 3) 348 | calling f with args (0, 3), {} 349 | 350 | >>> print(getfullargspec(f)) 351 | FullArgSpec(args=['x', 'y'], varargs='args', varkw='kw', defaults=(1,), kwonlyargs=[], kwonlydefaults=None, annotations={}) 352 | 353 | ``` 354 | 355 | ## Function annotations 356 | 357 | Python 3 introduced the concept of [function annotations]( 358 | http://www.python.org/dev/peps/pep-3107/): the ability 359 | to annotate the signature of a function with additional information, 360 | stored in a dictionary named ``__annotations__``. The ``decorator`` module 361 | (starting from release 3.3) will understand and preserve these annotations. 362 | 363 | Here is an example: 364 | 365 | ```python 366 | >>> @trace 367 | ... def f(x: 'the first argument', y: 'default argument'=1, z=2, 368 | ... *args: 'varargs', **kw: 'kwargs'): 369 | ... pass 370 | 371 | ``` 372 | 373 | In order to introspect functions with annotations, one needs 374 | ``inspect.getfullargspec`` (introduced in Python 3, then 375 | deprecated in Python 3.5, then undeprecated in Python 3.6): 376 | 377 | ```python 378 | >>> from inspect import getfullargspec 379 | >>> argspec = getfullargspec(f) 380 | >>> argspec.args 381 | ['x', 'y', 'z'] 382 | >>> argspec.varargs 383 | 'args' 384 | >>> argspec.varkw 385 | 'kw' 386 | >>> argspec.defaults 387 | (1, 2) 388 | >>> argspec.kwonlyargs 389 | [] 390 | >>> argspec.kwonlydefaults 391 | 392 | ``` 393 | 394 | You can check that the ``__annotations__`` dictionary is preserved: 395 | 396 | ```python 397 | >>> f.__annotations__ is f.__wrapped__.__annotations__ 398 | True 399 | 400 | ``` 401 | 402 | Here ``f.__wrapped__`` is the original undecorated function. 403 | This attribute exists for consistency with the behavior of 404 | ``functools.update_wrapper``. 405 | 406 | Another attribute copied from the original function is ``__qualname__``, 407 | the qualified name. This attribute was introduced in Python 3.3. 408 | 409 | ## ``decorator.decorator`` 410 | 411 | It can become tedious to write a caller function (like the above 412 | ``_trace`` example) and then a trivial wrapper 413 | (``def trace(f): return decorate(f, _trace)``) every time. 414 | Not to worry! The ``decorator`` module provides an easy shortcut 415 | to convert the caller function into a signature-preserving decorator. 416 | 417 | It is the ``decorator`` function: 418 | 419 | ```python 420 | >>> from decorator import decorator 421 | 422 | ``` 423 | The ``decorator`` function can be used as a signature-changing 424 | decorator, just like ``classmethod`` and ``staticmethod``. 425 | But ``classmethod`` and ``staticmethod`` return generic 426 | objects which are not callable. Instead, ``decorator`` returns 427 | signature-preserving decorators (i.e. functions with a single argument). 428 | 429 | For instance, you can write: 430 | 431 | ```python 432 | >>> @decorator 433 | ... def trace(f, *args, **kw): 434 | ... kwstr = ', '.join('%r: %r' % (k, kw[k]) for k in sorted(kw)) 435 | ... print("calling %s with args %s, {%s}" % (f.__name__, args, kwstr)) 436 | ... return f(*args, **kw) 437 | 438 | ``` 439 | 440 | And ``trace`` is now a decorator! 441 | 442 | ```python 443 | >>> trace 444 | 445 | 446 | ``` 447 | 448 | Here is an example of usage: 449 | 450 | ```python 451 | >>> @trace 452 | ... def func(): pass 453 | 454 | >>> func() 455 | calling func with args (), {} 456 | 457 | ``` 458 | 459 | ## Mimicking the behavior of functools.wrap 460 | 461 | Often people are confused by the decorator module since, contrarily 462 | to ``functools.wraps`` in the standard library, it tries very hard 463 | to keep the semantics of the arguments: in particular, positional arguments 464 | stay positional even if they are called with the keyword argument syntax. 465 | An example will make the issue clear. Here is a simple caller 466 | 467 | ```python 468 | 469 | def chatty(func, *args, **kwargs): 470 | print(args, sorted(kwargs.items())) 471 | return func(*args, **kwargs) 472 | ``` 473 | 474 | and here is a function to decorate: 475 | 476 | ```python 477 | 478 | @decorator(chatty) 479 | def printsum(x=1, y=2): 480 | print(x + y) 481 | ``` 482 | 483 | In this example ``x`` and ``y`` are positional arguments (with 484 | defaults). From the caller perspective, it does not matter if the user 485 | calls them as named arguments, they will stay inside the ``args`` 486 | tuple and not inside the ``kwargs`` dictionary: 487 | 488 | ```python 489 | >>> printsum(y=2, x=1) 490 | (1, 2) [] 491 | 3 492 | 493 | ``` 494 | 495 | This is quite different from the behavior of ``functools.wraps``; if you 496 | define the decorator as follows 497 | 498 | ```python 499 | 500 | def chattywrapper(func): 501 | @functools.wraps(func) 502 | def wrapper(*args, **kwargs): 503 | print(args, kwargs) 504 | return func(*args, **kwargs) 505 | return functools.wraps(wrapper) 506 | ``` 507 | 508 | you will see that calling ``printsum`` with named arguments will pass 509 | such arguments to ``kwargs``, while ``args`` will be the empty tuple. 510 | Since version 5 of the decorator module it is possible to mimic that 511 | behavior by using the ``kwsyntax`` flag: 512 | 513 | ```python 514 | 515 | @decorator(chatty, kwsyntax=True) 516 | def printsum2(x=1, y=2): 517 | print(x + y) 518 | ``` 519 | 520 | Here is how it works: 521 | 522 | ```python 523 | >>> printsum2(y=2, x=1) 524 | () [('x', 1), ('y', 2)] 525 | 3 526 | 527 | ``` 528 | 529 | This is exactly what the ``chattywrapper`` decorator would print: 530 | positional arguments are seen as keyword arguments, but only if the 531 | client code calls them with the keyword syntax. Otherwise they stay 532 | positional, i.e. they belongs to the ``args`` tuple and not to ``kwargs``: 533 | 534 | ```python 535 | >>> printsum2(1, 2) 536 | (1, 2) [] 537 | 3 538 | 539 | ``` 540 | 541 | ## Decorator factories 542 | 543 | The `decorator` function can also be used to define factories of decorators, 544 | i.e. functions returning decorators. In general you can just write something 545 | like this: 546 | 547 | ```python 548 | def decfactory(param1, param2, ...): 549 | def caller(f, *args, **kw): 550 | return somefunc(f, param1, param2, .., *args, **kw) 551 | return decorator(caller) 552 | ``` 553 | 554 | This is fully general but requires an additional level of nesting. For this 555 | reason since version 4.2 there is a facility to build decorator factories by 556 | using a single caller with default arguments: 557 | 558 | ```python 559 | def caller(f, param1=default1, param2=default2, ..., *args, **kw): 560 | return somefunc(f, param1, param2, *args, **kw) 561 | decfactory = decorator(caller) 562 | ``` 563 | 564 | Notice that this simplified approach *only works with default arguments*, 565 | i.e. `param1`, `param2` etc must have known defaults. Thanks to this 566 | restriction, there exists an unique default decorator, i.e. the member 567 | of the family which uses the default values for all parameters. Such 568 | decorator can be written as ``decfactory()`` with no parameters specified; 569 | moreover, as a shortcut, it is also possible to elide the parenthesis, 570 | a feature much requested by the users. For years I have been opposing 571 | the request, since having explicit parenthesis to me is more clear 572 | and less magic; however once this feature entered in decorators of 573 | the Python standard library (I am referring to the [dataclass decorator]( 574 | https://www.python.org/dev/peps/pep-0557/)) I finally gave up. 575 | 576 | The example below shows how it works in practice. The goal is to 577 | convert a function relying on a blocking resource into a function 578 | returning a "busy" message if the resource is not available. 579 | This can be accomplished with a suitable family of decorators 580 | parameterize by a string, the busy message: 581 | 582 | ```python 583 | 584 | @decorator 585 | def blocking(f, msg='blocking', *args, **kw): 586 | if not hasattr(f, "thread"): # no thread running 587 | def set_result(): 588 | f.result = f(*args, **kw) 589 | f.thread = threading.Thread(None, set_result) 590 | f.thread.start() 591 | return msg 592 | elif f.thread.is_alive(): 593 | return msg 594 | else: # the thread is ended, return the stored result 595 | del f.thread 596 | return f.result 597 | ``` 598 | 599 | Functions decorated with ``blocking`` will return a busy message if 600 | the resource is unavailable, and the intended result if the resource is 601 | available. For instance: 602 | 603 | ```python 604 | >>> @blocking(msg="Please wait ...") 605 | ... def read_data(): 606 | ... time.sleep(3) # simulate a blocking resource 607 | ... return "some data" 608 | 609 | >>> print(read_data()) # data is not available yet 610 | Please wait ... 611 | 612 | >>> time.sleep(1) 613 | >>> print(read_data()) # data is not available yet 614 | Please wait ... 615 | 616 | >>> time.sleep(1) 617 | >>> print(read_data()) # data is not available yet 618 | Please wait ... 619 | 620 | >>> time.sleep(1.1) # after 3.1 seconds, data is available 621 | >>> print(read_data()) 622 | some data 623 | 624 | ``` 625 | 626 | Decorator factories are most useful to framework builders. Here is an example 627 | that gives an idea of how you could manage permissions in a framework: 628 | 629 | ```python 630 | 631 | class Action(object): 632 | @restricted(user_class=User) 633 | def view(self): 634 | "Any user can view objects" 635 | 636 | @restricted(user_class=PowerUser) 637 | def insert(self): 638 | "Only power users can insert objects" 639 | 640 | @restricted(user_class=Admin) 641 | def delete(self): 642 | "Only the admin can delete objects" 643 | ``` 644 | 645 | where ``restricted`` is a decorator factory defined as follows 646 | 647 | ```python 648 | 649 | @decorator 650 | def restricted(func, user_class=User, *args, **kw): 651 | "Restrict access to a given class of users" 652 | self = args[0] 653 | if isinstance(self.user, user_class): 654 | return func(*args, **kw) 655 | else: 656 | raise PermissionError( 657 | '%s does not have the permission to run %s!' 658 | % (self.user, func.__name__)) 659 | ``` 660 | 661 | Notice that if you forget to use the keyword argument notation, i.e. if you 662 | write ``restricted(User)`` instead of ``restricted(user_class=User)`` you 663 | will get an error 664 | 665 | ```python 666 | TypeError: You are decorating a non function: 667 | 668 | ``` 669 | 670 | Be careful! 671 | 672 | ## ``decorator(cls)`` 673 | 674 | The ``decorator`` facility can also produce a decorator starting 675 | from a class with the signature of a caller. In such a case the 676 | produced generator is able to convert functions into factories 677 | to create instances of that class. 678 | 679 | As an example, here is a decorator which can convert a 680 | blocking function into an asynchronous function. When 681 | the function is called, it is executed in a separate thread. 682 | 683 | (This is similar to the approach used in the ``concurrent.futures`` package. 684 | But I don't recommend that you implement futures this way; this is just an 685 | example.) 686 | 687 | ```python 688 | 689 | class Future(threading.Thread): 690 | """ 691 | A class converting blocking functions into asynchronous 692 | functions by using threads. 693 | """ 694 | def __init__(self, func, *args, **kw): 695 | try: 696 | counter = func.counter 697 | except AttributeError: # instantiate the counter at the first call 698 | counter = func.counter = itertools.count(1) 699 | name = '%s-%s' % (func.__name__, next(counter)) 700 | 701 | def func_wrapper(): 702 | self._result = func(*args, **kw) 703 | super(Future, self).__init__(target=func_wrapper, name=name) 704 | self.start() 705 | 706 | def result(self): 707 | self.join() 708 | return self._result 709 | ``` 710 | 711 | The decorated function returns a ``Future`` object. It has a ``.result()`` 712 | method which blocks until the underlying thread finishes and returns 713 | the final result. 714 | 715 | Here is the minimalistic usage: 716 | 717 | ```python 718 | >>> @decorator(Future) 719 | ... def long_running(x): 720 | ... time.sleep(.5) 721 | ... return x 722 | 723 | >>> fut1 = long_running(1) 724 | >>> fut2 = long_running(2) 725 | >>> fut1.result() + fut2.result() 726 | 3 727 | 728 | ``` 729 | 730 | ## contextmanager 731 | 732 | Python's standard library has the ``contextmanager`` decorator, 733 | which converts a generator function into a ``GeneratorContextManager`` 734 | factory. For instance, if you write this... 735 | 736 | ```python 737 | >>> from contextlib import contextmanager 738 | >>> @contextmanager 739 | ... def before_after(before, after): 740 | ... print(before) 741 | ... yield 742 | ... print(after) 743 | 744 | ``` 745 | 746 | ...then ``before_after`` is a factory function that returns 747 | ``GeneratorContextManager`` objects, usable with the ``with`` statement: 748 | 749 | ```python 750 | >>> with before_after('BEFORE', 'AFTER'): 751 | ... print('hello') 752 | BEFORE 753 | hello 754 | AFTER 755 | 756 | ``` 757 | 758 | Basically, it is as if the content of the ``with`` block was executed 759 | in the place of the ``yield`` expression in the generator function. 760 | 761 | In Python 3.2, ``GeneratorContextManager`` objects were enhanced with 762 | a ``__call__`` method, so that they can be used as decorators, like so: 763 | 764 | ```python 765 | >>> ba = before_after('BEFORE', 'AFTER') 766 | >>> 767 | >>> @ba 768 | ... def hello(): 769 | ... print('hello') 770 | ... 771 | >>> hello() 772 | BEFORE 773 | hello 774 | AFTER 775 | 776 | ``` 777 | 778 | The ``ba`` decorator basically inserts a ``with ba:`` block 779 | inside the function. 780 | 781 | However ``GeneratorContextManager`` objects do not preserve the signature of 782 | the decorated functions. The decorated ``hello`` function above will 783 | have the generic signature ``hello(*args, **kwargs)``, but fails if 784 | called with more than zero arguments. 785 | 786 | For these reasons, the `decorator` module, starting from release 3.4, offers a 787 | ``decorator.contextmanager`` decorator that solves both problems, 788 | *and* works in all supported Python versions. Its usage is identical, 789 | and factories decorated with ``decorator.contextmanager`` will return 790 | instances of ``ContextManager``, a subclass of the standard library's 791 | ``contextlib.GeneratorContextManager`` class. The subclass includes 792 | an improved ``__call__`` method, which acts as a signature-preserving 793 | decorator. 794 | 795 | ## The ``FunctionMaker`` class 796 | 797 | The ``decorator`` module also provides a ``FunctionMaker`` class, which 798 | is able to generate on-the-fly functions 799 | with a given name and signature from a function template 800 | passed as a string. 801 | 802 | If you're just writing ordinary decorators, then you probably won't 803 | need to use ``FunctionMaker``. But in some circumstances, it 804 | can be handy. You will see an example shortly--in 805 | the implementation of a cool decorator utility (``decorator_apply``). 806 | 807 | ``FunctionMaker`` provides the ``.create`` classmethod, which 808 | accepts the *name*, *signature*, and *body* of the function 809 | you want to generate, as well as the execution environment 810 | where the function is generated by ``exec``. 811 | 812 | Here's an example: 813 | 814 | ```python 815 | >>> def f(*args, **kw): # a function with a generic signature 816 | ... print(args, kw) 817 | 818 | >>> f1 = FunctionMaker.create('f1(a, b)', 'f(a, b)', dict(f=f)) 819 | >>> f1(1,2) 820 | (1, 2) {} 821 | 822 | ``` 823 | 824 | It is important to notice that the function body is interpolated 825 | before being executed; **be careful** with the ``%`` sign! 826 | 827 | ``FunctionMaker.create`` also accepts keyword arguments. 828 | The keyword arguments are attached to the generated function. 829 | This is useful if you want to set some function attributes 830 | (e.g., the docstring ``__doc__``). 831 | 832 | For debugging/introspection purposes, it may be useful to see 833 | the source code of the generated function. To do this, just 834 | pass ``addsource=True``, and the generated function will get 835 | a ``__source__`` attribute: 836 | 837 | ```python 838 | >>> f1 = FunctionMaker.create( 839 | ... 'f1(a, b)', 'f(a, b)', dict(f=f), addsource=True) 840 | >>> print(f1.__source__) 841 | def f1(a, b): 842 | f(a, b) 843 | 844 | 845 | ``` 846 | 847 | The first argument to ``FunctionMaker.create`` can be a string (as above), 848 | or a function. This is the most common usage, since you typically decorate 849 | pre-existing functions. 850 | 851 | If you're writing a framework, however, you may want to use 852 | ``FunctionMaker.create`` directly, rather than ``decorator``, because it gives 853 | you direct access to the body of the generated function. 854 | 855 | For instance, suppose you want to instrument the ``__init__`` methods of a 856 | set of classes, by preserving their signature. 857 | (This use case is not made up. This is done by SQAlchemy, and other frameworks, 858 | too.) 859 | Here is what happens: 860 | 861 | - If first argument of ``FunctionMaker.create`` is a function, 862 | an instance of ``FunctionMaker`` is created with the attributes 863 | ``args``, ``varargs``, ``keywords``, and ``defaults`` 864 | (these mirror the return values of the standard library's 865 | ``inspect.getfullargspec``). 866 | 867 | - For each item in ``args`` (a list of strings of the names of all required 868 | arguments), an attribute ``arg0``, ``arg1``, ..., ``argN`` is also generated. 869 | 870 | - Finally, there is a ``signature`` attribute, which is a string with the 871 | signature of the original function. 872 | 873 | **NOTE:** You should not pass signature strings with default arguments 874 | (e.g., something like ``'f1(a, b=None)'``). Just pass ``'f1(a, b)'``, 875 | followed by a tuple of defaults: 876 | 877 | ```python 878 | >>> f1 = FunctionMaker.create( 879 | ... 'f1(a, b)', 'f(a, b)', dict(f=f), addsource=True, defaults=(None,)) 880 | >>> print(getfullargspec(f1)) 881 | FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={}) 882 | 883 | ``` 884 | 885 | ## Getting the source code 886 | 887 | Internally, ``FunctionMaker.create`` uses ``exec`` to generate the 888 | decorated function. Therefore ``inspect.getsource`` will not work for 889 | decorated functions. In IPython, this means that the usual ``??`` trick 890 | will give you the (right on the spot) message ``Dynamically generated 891 | function. No source code available``. 892 | However, there is a workaround. The decorated function has the ``__wrapped__`` 893 | attribute, pointing to the original function. The simplest way to get the 894 | source code is to call ``inspect.getsource`` on the undecorated function: 895 | 896 | ```python 897 | >>> print(inspect.getsource(factorial.__wrapped__)) 898 | @tail_recursive 899 | def factorial(n, acc=1): 900 | "The good old factorial" 901 | if n == 0: 902 | return acc 903 | return factorial(n-1, n*acc) 904 | 905 | 906 | ``` 907 | 908 | ## Dealing with third-party decorators 909 | 910 | Sometimes on the net you find some cool decorator that you would 911 | like to include in your code. However, more often than not, the cool 912 | decorator is not signature-preserving. What you need is an easy way to 913 | upgrade third party decorators to signature-preserving decorators... 914 | *without* having to rewrite them in terms of ``decorator``. 915 | 916 | You can use a ``FunctionMaker`` to implement that functionality as follows: 917 | 918 | ```python 919 | 920 | def decorator_apply(dec, func): 921 | """ 922 | Decorate a function by preserving the signature even if dec 923 | is not a signature-preserving decorator. 924 | """ 925 | return FunctionMaker.create( 926 | func, 'return decfunc(%(shortsignature)s)', 927 | dict(decfunc=dec(func)), __wrapped__=func) 928 | ``` 929 | 930 | ``decorator_apply`` sets the generated function's ``__wrapped__`` attribute 931 | to the original function, so you can get the right source code. 932 | If you are using a Python later than 3.2, you should also set the 933 | ``__qualname__`` attribute to preserve the qualified name of the original 934 | function. 935 | 936 | Notice that I am not providing this functionality in the ``decorator`` 937 | module directly, since I think it is best to rewrite the decorator instead 938 | of adding another level of indirection. However, practicality 939 | beats purity, so you can add ``decorator_apply`` to your toolbox and 940 | use it if you need to. 941 | 942 | To give a good example for ``decorator_apply``, I will show a pretty slick 943 | decorator that converts a tail-recursive function into an iterative function. 944 | I have shamelessly stolen the core concept from Kay Schluehr's recipe 945 | in the Python Cookbook, 946 | http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691. 947 | 948 | ```python 949 | 950 | class TailRecursive(object): 951 | """ 952 | tail_recursive decorator based on Kay Schluehr's recipe 953 | http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691 954 | with improvements by me and George Sakkis. 955 | """ 956 | 957 | def __init__(self, func): 958 | self.func = func 959 | self.firstcall = True 960 | self.CONTINUE = object() # sentinel 961 | 962 | def __call__(self, *args, **kwd): 963 | CONTINUE = self.CONTINUE 964 | if self.firstcall: 965 | func = self.func 966 | self.firstcall = False 967 | try: 968 | while True: 969 | result = func(*args, **kwd) 970 | if result is CONTINUE: # update arguments 971 | args, kwd = self.argskwd 972 | else: # last call 973 | return result 974 | finally: 975 | self.firstcall = True 976 | else: # return the arguments of the tail call 977 | self.argskwd = args, kwd 978 | return CONTINUE 979 | ``` 980 | 981 | Here the decorator is implemented as a class returning callable 982 | objects. 983 | 984 | ```python 985 | 986 | def tail_recursive(func): 987 | return decorator_apply(TailRecursive, func) 988 | ``` 989 | 990 | Here is how you apply the upgraded decorator to the good old factorial: 991 | 992 | ```python 993 | 994 | @tail_recursive 995 | def factorial(n, acc=1): 996 | "The good old factorial" 997 | if n == 0: 998 | return acc 999 | return factorial(n-1, n*acc) 1000 | ``` 1001 | 1002 | ```python 1003 | >>> print(factorial(4)) 1004 | 24 1005 | 1006 | ``` 1007 | 1008 | This decorator is pretty impressive, and should give you some food for 1009 | thought! ;) 1010 | 1011 | Notice that there is no recursion limit now; you can easily compute 1012 | ``factorial(1001)`` (or larger) without filling the stack frame. 1013 | 1014 | Notice also that the decorator will *not* work on functions which 1015 | are not tail recursive, such as the following: 1016 | 1017 | ```python 1018 | 1019 | def fact(n): # this is not tail-recursive 1020 | if n == 0: 1021 | return 1 1022 | return n * fact(n-1) 1023 | ``` 1024 | 1025 | **Reminder:** A function is *tail recursive* if it does either of the 1026 | following: 1027 | 1028 | - returns a value without making a recursive call; or, 1029 | - returns directly the result of a recursive call. 1030 | 1031 | ## Python 3.5 coroutines 1032 | 1033 | I am personally not using Python 3.5 coroutines yet. However, some 1034 | users requested support for coroutines and since version 4.1 the 1035 | decorator module has it. You should consider the support experimental 1036 | and kindly report issues if you find any. 1037 | 1038 | Here I will give a single example of usage. Suppose you want to log the moment 1039 | a coroutine starts and the moment it stops for debugging purposes. You could 1040 | write code like the following: 1041 | 1042 | ```python 1043 | import time 1044 | import logging 1045 | from asyncio import run, sleep, wait 1046 | from decorator import decorator 1047 | 1048 | @decorator 1049 | async def log_start_stop(coro, *args, **kwargs): 1050 | logging.info('Starting %s%s', coro.__name__, args) 1051 | t0 = time.time() 1052 | await coro(*args, **kwargs) 1053 | dt = time.time() - t0 1054 | logging.info('Ending %s%s after %d seconds', coro.__name__, args, dt) 1055 | 1056 | @log_start_stop 1057 | async def make_task(n): 1058 | for i in range(n): 1059 | await sleep(1) 1060 | 1061 | if __name__ == '__main__': 1062 | logging.basicConfig(level=logging.INFO) 1063 | tasks = [make_task(3), make_task(2), make_task(1)] 1064 | run(wait(tasks)) 1065 | ``` 1066 | 1067 | and you will get an output like this: 1068 | 1069 | ```bash 1070 | INFO:root:Starting make_task(1,) 1071 | INFO:root:Starting make_task(3,) 1072 | INFO:root:Starting make_task(2,) 1073 | INFO:root:Ending make_task(1,) after 1 seconds 1074 | INFO:root:Ending make_task(2,) after 2 seconds 1075 | INFO:root:Ending make_task(3,) after 3 seconds 1076 | ``` 1077 | 1078 | This may be handy if you have trouble understanding what it going on 1079 | with a particularly complex chain of coroutines. With a single line you 1080 | can decorate the troubling coroutine function, understand what happens, fix the 1081 | issue and then remove the decorator (or keep it if continuous monitoring 1082 | of the coroutines makes sense). Notice that 1083 | ``inspect.iscoroutinefunction(make_task)`` 1084 | will return the right answer (i.e. ``True``). 1085 | 1086 | It is also possible to define decorators converting coroutine functions 1087 | into regular functions, such as the following: 1088 | 1089 | ```python 1090 | @decorator 1091 | def coro_to_func(coro, *args, **kw): 1092 | "Convert a coroutine into a function" 1093 | return run(coro(*args, **kw)) 1094 | ``` 1095 | 1096 | Notice the difference: the caller in ``log_start_stop`` was a coroutine 1097 | function and the associate decorator was converting coroutines in coroutines; 1098 | the caller in ``coro_to_func`` is a regular function and converts 1099 | coroutines -> functions. 1100 | 1101 | ## Multiple dispatch 1102 | 1103 | There has been talk of implementing multiple dispatch functions 1104 | (i.e. "generic functions") in Python for over ten years. Last year, 1105 | something concrete was done for the first time. As of Python 3.4, 1106 | we have the decorator ``functools.singledispatch`` to implement generic 1107 | functions! 1108 | 1109 | As its name implies, it is limited to *single dispatch*; in other words, 1110 | it is able to dispatch on the first argument of the function only. 1111 | 1112 | The ``decorator`` module provides the decorator factory ``dispatch_on``, 1113 | which can be used to implement generic functions dispatching on *any* argument. 1114 | Moreover, it can manage dispatching on more than one argument. 1115 | (And, of course, it is signature-preserving.) 1116 | 1117 | Here is a concrete example (from a real-life use case) where it is desiderable 1118 | to dispatch on the second argument. 1119 | 1120 | Suppose you have an ``XMLWriter`` class, which is instantiated 1121 | with some configuration parameters, and has the ``.write`` method which 1122 | serializes objects to XML: 1123 | 1124 | ```python 1125 | 1126 | class XMLWriter(object): 1127 | def __init__(self, **config): 1128 | self.cfg = config 1129 | 1130 | @dispatch_on('obj') 1131 | def write(self, obj): 1132 | raise NotImplementedError(type(obj)) 1133 | ``` 1134 | 1135 | Here, you want to dispatch on the *second* argument; the first is already 1136 | taken by ``self``. The ``dispatch_on`` decorator factory allows you to specify 1137 | the dispatch argument simply by passing its name as a string. (Note 1138 | that if you misspell the name you will get an error.) 1139 | 1140 | The decorated function `write` is turned into a generic function ( 1141 | `write` is a function at the idea it is decorated; it will be turned 1142 | into a method later, at class instantiation time), 1143 | and it is called if there are no more specialized implementations. 1144 | 1145 | Usually, default functions should raise a ``NotImplementedError``, thus 1146 | forcing people to register some implementation. 1147 | You can perform the registration with a decorator: 1148 | 1149 | ```python 1150 | 1151 | @XMLWriter.write.register(float) 1152 | def writefloat(self, obj): 1153 | return '%s' % obj 1154 | ``` 1155 | 1156 | Now ``XMLWriter`` can serialize floats: 1157 | 1158 | ```python 1159 | >>> writer = XMLWriter() 1160 | >>> writer.write(2.3) 1161 | '2.3' 1162 | 1163 | ``` 1164 | 1165 | I could give a down-to-earth example of situations in which it is desiderable 1166 | to dispatch on more than one argument--for instance, I once implemented 1167 | a database-access library where the first dispatching argument was the 1168 | the database driver, and the second was the database record--but here 1169 | I will follow tradition, and show the time-honored Rock-Paper-Scissors example: 1170 | 1171 | ```python 1172 | 1173 | class Rock(object): 1174 | ordinal = 0 1175 | ``` 1176 | ```python 1177 | 1178 | class Paper(object): 1179 | ordinal = 1 1180 | ``` 1181 | ```python 1182 | 1183 | class Scissors(object): 1184 | ordinal = 2 1185 | ``` 1186 | 1187 | I have added an ordinal to the Rock-Paper-Scissors classes to simplify 1188 | the implementation. The idea is to define a generic function (``win(a, 1189 | b)``) of two arguments corresponding to the *moves* of the first and 1190 | second players. The *moves* are instances of the classes 1191 | Rock, Paper, and Scissors: 1192 | 1193 | - Paper wins over Rock 1194 | - Scissors wins over Paper 1195 | - Rock wins over Scissors 1196 | 1197 | The function will return +1 for a win, -1 for a loss, and 0 for parity. 1198 | There are 9 combinations, but combinations with the same ordinal 1199 | (i.e. the same class) return 0. Moreover, by exchanging the order of the 1200 | arguments, the sign of the result changes. Therefore, it is sufficient to 1201 | directly specify only 3 implementations: 1202 | 1203 | ```python 1204 | 1205 | @dispatch_on('a', 'b') 1206 | def win(a, b): 1207 | if a.ordinal == b.ordinal: 1208 | return 0 1209 | elif a.ordinal > b.ordinal: 1210 | return -win(b, a) 1211 | raise NotImplementedError((type(a), type(b))) 1212 | ``` 1213 | ```python 1214 | 1215 | @win.register(Rock, Paper) 1216 | def winRockPaper(a, b): 1217 | return -1 1218 | ``` 1219 | ```python 1220 | 1221 | @win.register(Paper, Scissors) 1222 | def winPaperScissors(a, b): 1223 | return -1 1224 | ``` 1225 | ```python 1226 | 1227 | @win.register(Rock, Scissors) 1228 | def winRockScissors(a, b): 1229 | return 1 1230 | ``` 1231 | 1232 | Here is the result: 1233 | 1234 | ```python 1235 | >>> win(Paper(), Rock()) 1236 | 1 1237 | >>> win(Scissors(), Paper()) 1238 | 1 1239 | >>> win(Rock(), Scissors()) 1240 | 1 1241 | >>> win(Paper(), Paper()) 1242 | 0 1243 | >>> win(Rock(), Rock()) 1244 | 0 1245 | >>> win(Scissors(), Scissors()) 1246 | 0 1247 | >>> win(Rock(), Paper()) 1248 | -1 1249 | >>> win(Paper(), Scissors()) 1250 | -1 1251 | >>> win(Scissors(), Rock()) 1252 | -1 1253 | 1254 | ``` 1255 | 1256 | The point of generic functions is that they play well with subclassing. 1257 | For instance, suppose we define a ``StrongRock``, which does not lose against 1258 | Paper: 1259 | 1260 | ```python 1261 | 1262 | class StrongRock(Rock): 1263 | pass 1264 | ``` 1265 | ```python 1266 | 1267 | @win.register(StrongRock, Paper) 1268 | def winStrongRockPaper(a, b): 1269 | return 0 1270 | ``` 1271 | 1272 | Then you do not need to define other implementations; they are 1273 | inherited from the parent: 1274 | 1275 | ```python 1276 | >>> win(StrongRock(), Scissors()) 1277 | 1 1278 | 1279 | ``` 1280 | 1281 | You can introspect the precedence used by the dispatch algorithm by 1282 | calling ``.dispatch_info(*types)``: 1283 | 1284 | ```python 1285 | >>> win.dispatch_info(StrongRock, Scissors) 1286 | [('StrongRock', 'Scissors'), ('Rock', 'Scissors')] 1287 | 1288 | ``` 1289 | 1290 | Since there is no direct implementation for (``StrongRock``, ``Scissors``), 1291 | the dispatcher will look at the implementation for (``Rock``, ``Scissors``) 1292 | which is available. Internally, the algorithm is doing a cross 1293 | product of the class precedence lists (or *Method Resolution Orders*, 1294 | [MRO](http://www.python.org/2.3/mro.html) for short) of ``StrongRock`` 1295 | and ``Scissors``, respectively. 1296 | 1297 | ## Generic functions and virtual ancestors 1298 | 1299 | In Python, generic functions are complicated by the existence of 1300 | "virtual ancestors": superclasses which are not in the class hierarchy. 1301 | 1302 | Consider this class: 1303 | 1304 | ```python 1305 | 1306 | class WithLength(object): 1307 | def __len__(self): 1308 | return 0 1309 | ``` 1310 | 1311 | This class defines a ``__len__`` method, and is therefore 1312 | considered to be a subclass of the abstract base class 1313 | ``collections.abc.Sized`` (``collections.Sized`` on Python 2): 1314 | 1315 | ```python 1316 | >>> issubclass(WithLength, collections.abc.Sized) 1317 | True 1318 | 1319 | ``` 1320 | 1321 | However, ``collections.abc.Sized`` is not in the MRO_ of ``WithLength``; it 1322 | is not a true ancestor. Any implementation of generic functions (even 1323 | with single dispatch) must go through some contorsion to take into 1324 | account the virtual ancestors. 1325 | 1326 | In particular, if we define a generic function... 1327 | 1328 | ```python 1329 | 1330 | @dispatch_on('obj') 1331 | def get_length(obj): 1332 | raise NotImplementedError(type(obj)) 1333 | ``` 1334 | 1335 | ...implemented on all classes with a length... 1336 | 1337 | ```python 1338 | 1339 | @get_length.register(collections.abc.Sized) 1340 | def get_length_sized(obj): 1341 | return len(obj) 1342 | ``` 1343 | 1344 | ...then ``get_length`` must be defined on ``WithLength`` instances... 1345 | 1346 | ```python 1347 | >>> get_length(WithLength()) 1348 | 0 1349 | 1350 | ``` 1351 | 1352 | ...even if ``collections.abc.Sized`` is not a true ancestor of ``WithLength``. 1353 | 1354 | Of course, this is a contrived example--you could just use the 1355 | builtin ``len``--but you should get the idea. 1356 | 1357 | Since in Python it is possible to consider any instance of ``ABCMeta`` 1358 | as a virtual ancestor of any other class (it is enough to register it 1359 | as ``ancestor.register(cls)``), any implementation of generic functions 1360 | must be aware of the registration mechanism. 1361 | 1362 | For example, suppose you are using a third-party set-like class, like 1363 | the following: 1364 | 1365 | ```python 1366 | 1367 | class SomeSet(collections.abc.Sized): 1368 | # methods that make SomeSet set-like 1369 | # not shown ... 1370 | def __len__(self): 1371 | return 0 1372 | ``` 1373 | 1374 | Here, the author of ``SomeSet`` made a mistake by inheriting from 1375 | ``collections.abc.Sized`` (instead of ``collections.abc.Set``). 1376 | 1377 | This is not a problem. You can register *a posteriori* 1378 | ``collections.abc.Set`` as a virtual ancestor of ``SomeSet``: 1379 | 1380 | ```python 1381 | >>> _ = collections.abc.Set.register(SomeSet) 1382 | >>> issubclass(SomeSet, collections.abc.Set) 1383 | True 1384 | 1385 | ``` 1386 | 1387 | Now, let's define an implementation of ``get_length`` specific to set: 1388 | 1389 | ```python 1390 | 1391 | @get_length.register(collections.abc.Set) 1392 | def get_length_set(obj): 1393 | return 1 1394 | ``` 1395 | 1396 | The current implementation (and ``functools.singledispatch`` too) 1397 | is able to discern that a ``Set`` is a ``Sized`` object, by looking at 1398 | the class registry, so it uses the more specific implementation for ``Set``: 1399 | 1400 | ```python 1401 | >>> get_length(SomeSet()) # NB: the implementation for Sized would give 0 1402 | 1 1403 | 1404 | ``` 1405 | 1406 | Sometimes it is not clear how to dispatch. For instance, consider a 1407 | class ``C`` registered both as ``collections.abc.Iterable`` and 1408 | ``collections.abc.Sized``, and defines a generic function ``g`` with 1409 | implementations for both ``collections.abc.Iterable`` *and* 1410 | ``collections.abc.Sized``: 1411 | 1412 | ```python 1413 | 1414 | def singledispatch_example1(): 1415 | singledispatch = dispatch_on('obj') 1416 | 1417 | @singledispatch 1418 | def g(obj): 1419 | raise NotImplementedError(type(g)) 1420 | 1421 | @g.register(collections.abc.Sized) 1422 | def g_sized(object): 1423 | return "sized" 1424 | 1425 | @g.register(collections.abc.Iterable) 1426 | def g_iterable(object): 1427 | return "iterable" 1428 | 1429 | g(C()) # RuntimeError: Ambiguous dispatch: Iterable or Sized? 1430 | ``` 1431 | 1432 | It is impossible to decide which implementation to use, since the ancestors 1433 | are independent. The following function will raise a ``RuntimeError`` 1434 | when called. This is consistent with the "refuse the temptation to guess" 1435 | philosophy. ``functools.singledispatch`` would raise a similar error. 1436 | 1437 | It would be easy to rely on the order of registration to decide the 1438 | precedence order. This is reasonable, but also fragile: 1439 | 1440 | - if, during some refactoring, you change the registration order by mistake, 1441 | a different implementation could be taken; 1442 | - if implementations of the generic functions are distributed across modules, 1443 | and you change the import order, a different implementation could be taken. 1444 | 1445 | So the ``decorator`` module prefers to raise an error in the face of ambiguity. 1446 | This is the same approach taken by the standard library. 1447 | 1448 | However, it should be noted that the *dispatch algorithm* used by the decorator 1449 | module is different from the one used by the standard library, so in certain 1450 | cases you will get different answers. The difference is that 1451 | ``functools.singledispatch`` tries to insert the virtual ancestors *before* the 1452 | base classes, whereas ``decorator.dispatch_on`` tries to insert them *after* 1453 | the base classes. 1454 | 1455 | Here's an example that shows the difference: 1456 | 1457 | ```python 1458 | 1459 | def singledispatch_example2(): 1460 | # adapted from functools.singledispatch test case 1461 | singledispatch = dispatch_on('arg') 1462 | 1463 | class S(object): 1464 | pass 1465 | 1466 | class V(c.Sized, S): 1467 | def __len__(self): 1468 | return 0 1469 | 1470 | @singledispatch 1471 | def g(arg): 1472 | return "base" 1473 | 1474 | @g.register(S) 1475 | def g_s(arg): 1476 | return "s" 1477 | 1478 | @g.register(c.Container) 1479 | def g_container(arg): 1480 | return "container" 1481 | 1482 | v = V() 1483 | assert g(v) == "s" 1484 | c.Container.register(V) # add c.Container to the virtual mro of V 1485 | assert g(v) == "s" # since the virtual mro is V, Sized, S, Container 1486 | return g, V 1487 | ``` 1488 | 1489 | If you play with this example and replace the ``singledispatch`` definition 1490 | with ``functools.singledispatch``, the assertion will break: ``g`` will return 1491 | ``"container"`` instead of ``"s"``, because ``functools.singledispatch`` 1492 | will insert the ``Container`` class right before ``S``. 1493 | 1494 | Notice that here I am not making any bold claim such as "the standard 1495 | library algorithm is wrong and my algorithm is right" or vice versa. It 1496 | just point out that there are some subtle differences. The only way to 1497 | understand what is really happening here is to scratch your head by 1498 | looking at the implementations. I will just notice that 1499 | ``.dispatch_info`` is quite essential to see the class precedence 1500 | list used by algorithm: 1501 | 1502 | ```python 1503 | >>> g, V = singledispatch_example2() 1504 | >>> g.dispatch_info(V) 1505 | [('V',), ('Sized',), ('S',), ('Container',)] 1506 | 1507 | ``` 1508 | 1509 | The current implementation does not implement any kind of cooperation 1510 | between implementations. In other words, nothing is akin either to 1511 | call-next-method in Lisp, or to ``super`` in Python. 1512 | 1513 | Finally, let me notice that the decorator module implementation does 1514 | not use any cache, whereas the ``singledispatch`` implementation does. 1515 | 1516 | ## Caveats and limitations 1517 | 1518 | In the present implementation, decorators generated by ``decorator`` 1519 | can only be used on user-defined Python functions, methods or coroutines. 1520 | I have no interest in decorating generic callable objects. If you want to 1521 | decorate things like classmethods/staticmethods and general callables - 1522 | which I will never support in the decorator module - I suggest you 1523 | to look at the [wrapt](https://wrapt.readthedocs.io/en/latest/) 1524 | project by Graeme Dumpleton. 1525 | 1526 | Since version 5 the ``decorator`` module uses the ``inspect.Signature`` 1527 | object in the standard library. Unfortunately, for legacy reasons, some 1528 | applications introspect decorated functions by using low-level entities like 1529 | the ``__code__`` object and not signature objects. An example will make 1530 | the issue clear: 1531 | 1532 | ```python 1533 | >>> def f(a, b): pass 1534 | >>> f_dec = decorator(_trace)(f) 1535 | >>> f_dec.__code__.co_argcount 1536 | 0 1537 | >>> f_dec.__code__.co_varnames 1538 | ('args', 'kw') 1539 | 1540 | ``` 1541 | This is not what one would expect: the `argcount` should be 2 since 1542 | the original functions has two arguments and the `varnames` should be 1543 | `a` and `b`. The only way to fix the issue is to go back to an implementation 1544 | of the decorator using ``exec``, which is provided for convenience since 1545 | version 5.1: 1546 | 1547 | ```python 1548 | >>> from decorator import decoratorx 1549 | >>> f_dec = decoratorx(_trace)(f) 1550 | >>> f_dec.__code__.co_argcount 1551 | 2 1552 | >>> f_dec.__code__.co_varnames 1553 | ('a', 'b') 1554 | 1555 | ``` 1556 | Rather than using `decoratorx`, you should fix your introspection 1557 | routines to use ``inspect.Signature`` without fiddling with the 1558 | ``__code__`` object. 1559 | 1560 | There is a strange quirk when decorating functions with keyword 1561 | arguments, if one of the arguments has the same name used in the 1562 | caller function for the first argument. The quirk was reported by 1563 | David Goldstein. 1564 | 1565 | Here is an example where it is manifest: 1566 | 1567 | ```python 1568 | >>> @memoize 1569 | ... def getkeys(**kw): 1570 | ... return kw.keys() 1571 | 1572 | >>> getkeys(func='a') 1573 | Traceback (most recent call last): 1574 | ... 1575 | TypeError: _memoize() got multiple values for ... 'func' 1576 | 1577 | ``` 1578 | 1579 | The error message looks really strange... until you realize that 1580 | the caller function `_memoize` uses `func` as first argument, 1581 | so there is a confusion between the positional argument and the 1582 | keyword arguments. 1583 | 1584 | The solution is to change the name of the first argument in `_memoize`, 1585 | or to change the implementation like so: 1586 | 1587 | ```python 1588 | 1589 | def _memoize(*all_args, **kw): 1590 | func = all_args[0] 1591 | args = all_args[1:] 1592 | if kw: # frozenset is used to ensure hashability 1593 | key = args, frozenset(kw.items()) 1594 | else: 1595 | key = args 1596 | cache = func.cache # attribute added by memoize 1597 | if key not in cache: 1598 | cache[key] = func(*args, **kw) 1599 | return cache[key] 1600 | ``` 1601 | 1602 | This avoids the need to name the first argument, so the problem 1603 | simply disappears. This is a technique that you should keep in mind 1604 | when writing decorators for functions with keyword arguments. Also, 1605 | notice that lately I have come to believe that decorating functions with 1606 | keyword arguments is not such a good idea, and you may want not to do 1607 | that. 1608 | 1609 | The implementation is such that the decorated function makes 1610 | a (shallow) copy of the original function dictionary: 1611 | 1612 | ```python 1613 | >>> def f(): pass # the original function 1614 | >>> f.attr1 = "something" # setting an attribute 1615 | >>> f.attr2 = "something else" # setting another attribute 1616 | 1617 | >>> traced_f = trace(f) # the decorated function 1618 | 1619 | >>> traced_f.attr1 1620 | 'something' 1621 | >>> traced_f.attr2 = "something different" # setting attr 1622 | >>> f.attr2 # the original attribute did not change 1623 | 'something else' 1624 | 1625 | ``` 1626 | 1627 | Finally, you should be aware of the performance penalty of decorators. 1628 | The worse case is shown by the following example: 1629 | 1630 | ```bash 1631 | $ cat performance.sh 1632 | python3 -m timeit -s " 1633 | from decorator import decorator 1634 | 1635 | @decorator 1636 | def do_nothing(func, *args, **kw): 1637 | return func(*args, **kw) 1638 | 1639 | @do_nothing 1640 | def f(): 1641 | pass 1642 | " "f()" 1643 | 1644 | python3 -m timeit -s " 1645 | def f(): 1646 | pass 1647 | " "f()" 1648 | 1649 | ``` 1650 | On my laptop, using the ``do_nothing`` decorator instead of the 1651 | plain function is five times slower: 1652 | 1653 | ```bash 1654 | $ bash performance.sh 1655 | 1000000 loops, best of 3: 1.39 usec per loop 1656 | 1000000 loops, best of 3: 0.278 usec per loop 1657 | ``` 1658 | 1659 | Of course, a real life function probably does something more useful 1660 | than the function ``f`` here, so the real life performance penalty 1661 | *could* be negligible. As always, the only way to know if there is a 1662 | penalty in your specific use case is to measure it. 1663 | 1664 | ## LICENSE (2-clause BSD) 1665 | 1666 | Copyright (c) 2005-2025, Michele Simionato 1667 | All rights reserved. 1668 | 1669 | Redistribution and use in source and binary forms, with or without 1670 | modification, are permitted provided that the following conditions are 1671 | met: 1672 | 1673 | Redistributions of source code must retain the above copyright 1674 | notice, this list of conditions and the following disclaimer. 1675 | Redistributions in bytecode form must reproduce the above copyright 1676 | notice, this list of conditions and the following disclaimer in 1677 | the documentation and/or other materials provided with the 1678 | distribution. 1679 | 1680 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1681 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1682 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1683 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1684 | HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1685 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1686 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 1687 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1688 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 1689 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 1690 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 1691 | DAMAGE. 1692 | 1693 | If you use this software and you are happy with it, consider sending me a 1694 | note, just to gratify my ego. On the other hand, if you use this software and 1695 | you are unhappy with it, send me a patch! -------------------------------------------------------------------------------- /tests/documentation.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import threading 3 | import time 4 | import functools 5 | import itertools 6 | import collections 7 | import collections.abc as c 8 | from decorator import (decorator, decorate, FunctionMaker, 9 | dispatch_on, __version__) 10 | 11 | doc = r"""# Decorators for Humans 12 | 13 | |Author | Michele Simionato| 14 | |---|---| 15 | |E-mail | michele.simionato@gmail.com| 16 | |Version| $VERSION ($DATE)| 17 | |Supports| Python 3.7, 3.8, 3.9, 3.10, 3.11, 3.12| 18 | |Download page| https://pypi.org/project/decorator/$VERSION| 19 | |Installation| ``pip install decorator``| 20 | |License | BSD license| 21 | 22 | ## Introduction 23 | 24 | The ``decorator`` module is over ten years old, but still alive and 25 | kicking. It is used by several frameworks (IPython, scipy, authkit, 26 | pylons, pycuda, sugar, ...) and has been stable for a *long* time. It 27 | is your best option if you want to preserve the signature of decorated 28 | functions in a consistent way across Python releases. Versions 5.X 29 | supports Python versions greater than 3.4, versions 4.X supports Python 30 | versions back to 2.6; versions 3.X are able to support even Python 2.5 and 31 | 2.4. 32 | 33 | ## What's New in version 5 34 | 35 | Version 5 of the decorator module features a major simplification of 36 | the code base made possible by dropping support for Python releases 37 | older than 3.5. From that version the ``Signature`` object works well 38 | enough that it is possible to fix the signature of a decorated 39 | function without resorting to ``exec`` tricks. The simplification 40 | has a very neat advantage: in case of exceptions raised in decorated 41 | functions the traceback is nicer than it used to be. Moreover, it is 42 | now possible to mimic the behavior of decorators defined with 43 | ``functool.wraps``: see the section about the ``kwsyntax`` flag below. 44 | 45 | ## What's New in version 4 46 | 47 | - **New documentation** 48 | There is now a single manual for all Python versions, so I took the 49 | opportunity to overhaul the documentation. 50 | Even if you are a long-time user, you may want to revisit the docs, since 51 | several examples have been improved. 52 | 53 | - **Packaging improvements** 54 | The code is now also available in wheel format. Integration with 55 | setuptools has improved and you can run the tests with the command 56 | ``python setup.py test`` too. 57 | 58 | - **Code changes** 59 | A new utility function ``decorate(func, caller)`` has been added. 60 | It does the same job that was performed by the older 61 | ``decorator(caller, func)``. The old functionality is now deprecated 62 | and no longer documented, but still available for now. 63 | 64 | - **Multiple dispatch** 65 | The decorator module now includes an implementation of generic 66 | functions (sometimes called "multiple dispatch functions"). 67 | The API is designed to mimic ``functools.singledispatch`` (added 68 | in Python 3.4), but the implementation is much simpler. 69 | Moreover, all decorators involved preserve the signature of the 70 | decorated functions. For now, this exists mostly to demonstrate 71 | the power of the module. In the future it could be enhanced/optimized. 72 | In any case, it is very short and compact (less then 100 lines), so you 73 | can extract it for your own use. Take it as food for thought. 74 | 75 | - **Python 3.5 coroutines** 76 | From version 4.1 it is possible to decorate coroutines, i.e. functions 77 | defined with the `async def` syntax, and to maintain the 78 | `inspect.iscoroutinefunction` check working for the decorated function. 79 | 80 | - **Decorator factories** 81 | From version 4.2 there is facility to define factories of decorators in 82 | a simple way, a feature requested by the users since a long time. 83 | 84 | ## Usefulness of decorators 85 | 86 | Python decorators are an interesting example of why syntactic sugar 87 | matters. In principle, their introduction in Python 2.4 changed 88 | nothing, since they did not provide any new functionality which was not 89 | already present in the language. In practice, their introduction has 90 | significantly changed the way we structure our programs. 91 | I believe the change is for the best, and that decorators are a great 92 | idea since: 93 | 94 | * decorators help reducing boilerplate code; 95 | * decorators help separation of concerns; 96 | * decorators enhance readability and maintenability; 97 | * decorators are explicit. 98 | 99 | Still, as of now, writing custom decorators correctly requires 100 | some experience and it is not as easy as it could be. For instance, 101 | typical implementations of decorators involve nested functions, and 102 | we all know that flat is better than nested. 103 | 104 | The aim of the ``decorator`` module it to simplify the usage of 105 | decorators for the average programmer, and to popularize decorators by 106 | showing various non-trivial examples. Of course, as all techniques, 107 | decorators can be abused (I have seen that) and you should not try to 108 | solve every problem with a decorator, just because you can. 109 | 110 | You may find the source code for all the examples 111 | discussed here in the ``documentation.py`` file, which contains 112 | the documentation you are reading in the form of doctests. 113 | 114 | ## Definitions 115 | 116 | Technically speaking, any Python object which can be called with one argument 117 | can be used as a decorator. However, this definition is somewhat too large 118 | to be really useful. It is more convenient to split the generic class of 119 | decorators in two subclasses: 120 | 121 | 1. **signature-preserving decorators**, callable objects which accept 122 | a function as input and return a function as output, *with the 123 | same signature* 124 | 125 | 2. **signature-changing** decorators, i.e. decorators 126 | which change the signature of their input function, or decorators 127 | that return non-callable objects 128 | 129 | Signature-changing decorators have their use: for instance, the 130 | builtin classes ``staticmethod`` and ``classmethod`` are in this 131 | group. They take functions and return descriptor objects which 132 | are neither functions, nor callables. 133 | 134 | Still, signature-preserving decorators are more common, and easier 135 | to reason about. In particular, they can be composed together, 136 | whereas other decorators generally cannot. 137 | 138 | Writing signature-preserving decorators from scratch is not that 139 | obvious, especially if one wants to define proper decorators that 140 | can accept functions with any signature. A simple example will clarify 141 | the issue. 142 | 143 | ## Statement of the problem 144 | 145 | A very common use case for decorators is the memoization of functions. 146 | A ``memoize`` decorator works by caching 147 | the result of the function call in a dictionary, so that the next time 148 | the function is called with the same input parameters the result is retrieved 149 | from the cache and not recomputed. 150 | 151 | There are many implementations of ``memoize`` in 152 | http://www.python.org/moin/PythonDecoratorLibrary, 153 | but they do not preserve the signature. In recent versions of 154 | Python you can find a sophisticated ``lru_cache`` decorator 155 | in the standard library's ``functools``. Here I am just 156 | interested in giving an example. 157 | 158 | Consider the following simple implementation (note that it is 159 | generally impossible to *correctly* memoize something 160 | that depends on non-hashable arguments): 161 | 162 | $$memoize_uw 163 | 164 | Here I used the functools.update_wrapper_ utility, which was added 165 | in Python 2.5 to simplify the writing of decorators. 166 | (Previously, you needed to manually copy the function attributes 167 | ``__name__``, ``__doc__``, ``__module__``, and ``__dict__`` 168 | to the decorated function by hand). 169 | 170 | This works insofar as the decorator accepts functions with generic signatures. 171 | Unfortunately, it is *not* a signature-preserving decorator, since 172 | ``memoize_uw`` generally returns a function with a *different signature* 173 | from the original. 174 | 175 | Consider for instance the following case: 176 | 177 | $$f1 178 | 179 | Here, the original function takes a single argument named ``x``, 180 | but the decorated function takes any number of arguments and 181 | keyword arguments: 182 | 183 | ```python 184 | >>> from inspect import getfullargspec 185 | >>> print(getfullargspec(f1)) 186 | FullArgSpec(args=[], varargs='args', varkw='kw', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) 187 | 188 | ``` 189 | 190 | This means that introspection tools like ``getfullargspec`` will give 191 | you false information about the signature of ``f1`` This is pretty bad: 192 | ``getfullargspec`` says that the function accepts the generic 193 | signature ``*args, **kw``, but calling the function with more than one 194 | argument raises an error: 195 | 196 | ```python 197 | >>> f1(0, 1) # doctest: +IGNORE_EXCEPTION_DETAIL 198 | Traceback (most recent call last): 199 | ... 200 | TypeError: f1() takes exactly 1 positional argument (2 given) 201 | 202 | ``` 203 | 204 | Notice that ``pydoc`` will give the right signature, but only in Python 205 | versions greater than 3.5. 206 | 207 | ## The solution 208 | 209 | The solution is to provide a generic factory of generators, which 210 | hides the complexity of making signature-preserving decorators 211 | from the application programmer. The ``decorate`` function in 212 | the ``decorator`` module is such a factory: 213 | 214 | ```python 215 | >>> from decorator import decorate 216 | 217 | ``` 218 | 219 | ``decorate`` takes two arguments: 220 | 221 | 1. a caller function describing the functionality of the decorator, and 222 | 223 | 2. a function to be decorated. 224 | 225 | The caller function must have signature ``(f, *args, **kw)``, and it 226 | must call the original function ``f`` with arguments ``args`` and ``kw``, 227 | implementing the wanted capability (in this case, memoization): 228 | 229 | $$_memoize 230 | 231 | Now, you can define your decorator as follows: 232 | 233 | $$memoize 234 | 235 | The difference from the nested function approach of ``memoize_uw`` 236 | is that the decorator module forces you to lift the inner function 237 | to the outer level. Moreover, you are forced to explicitly pass the 238 | function you want to decorate; there are no closures. 239 | 240 | Here is a test of usage: 241 | 242 | ```python 243 | >>> @memoize 244 | ... def heavy_computation(): 245 | ... time.sleep(2) 246 | ... return "done" 247 | 248 | >>> print(heavy_computation()) # the first time it will take 2 seconds 249 | done 250 | 251 | >>> print(heavy_computation()) # the second time it will be instantaneous 252 | done 253 | 254 | ``` 255 | 256 | The signature of ``heavy_computation`` is the one you would expect: 257 | 258 | ```python 259 | >>> print(getfullargspec(heavy_computation)) 260 | FullArgSpec(args=[], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) 261 | 262 | ``` 263 | 264 | ## A ``trace`` decorator 265 | 266 | Here is an example of how to define a simple ``trace`` decorator, 267 | which prints a message whenever the traced function is called: 268 | 269 | $$_trace 270 | 271 | $$trace 272 | 273 | Here is an example of usage: 274 | 275 | ```python 276 | >>> @trace 277 | ... def f1(x): 278 | ... pass 279 | 280 | ``` 281 | 282 | It is immediate to verify that ``f1`` works... 283 | 284 | ```python 285 | >>> f1(0) 286 | calling f1 with args (0,), {} 287 | 288 | ``` 289 | 290 | ...and it that it has the correct signature: 291 | 292 | ```python 293 | >>> print(getfullargspec(f1)) 294 | FullArgSpec(args=['x'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={}) 295 | 296 | ``` 297 | 298 | The decorator works with functions of any signature: 299 | 300 | ```python 301 | >>> @trace 302 | ... def f(x, y=1, *args, **kw): 303 | ... pass 304 | 305 | >>> f(0, 3) 306 | calling f with args (0, 3), {} 307 | 308 | >>> print(getfullargspec(f)) 309 | FullArgSpec(args=['x', 'y'], varargs='args', varkw='kw', defaults=(1,), kwonlyargs=[], kwonlydefaults=None, annotations={}) 310 | 311 | ``` 312 | 313 | ## Function annotations 314 | 315 | Python 3 introduced the concept of [function annotations]( 316 | http://www.python.org/dev/peps/pep-3107/): the ability 317 | to annotate the signature of a function with additional information, 318 | stored in a dictionary named ``__annotations__``. The ``decorator`` module 319 | (starting from release 3.3) will understand and preserve these annotations. 320 | 321 | Here is an example: 322 | 323 | ```python 324 | >>> @trace 325 | ... def f(x: 'the first argument', y: 'default argument'=1, z=2, 326 | ... *args: 'varargs', **kw: 'kwargs'): 327 | ... pass 328 | 329 | ``` 330 | 331 | In order to introspect functions with annotations, one needs 332 | ``inspect.getfullargspec`` (introduced in Python 3, then 333 | deprecated in Python 3.5, then undeprecated in Python 3.6): 334 | 335 | ```python 336 | >>> from inspect import getfullargspec 337 | >>> argspec = getfullargspec(f) 338 | >>> argspec.args 339 | ['x', 'y', 'z'] 340 | >>> argspec.varargs 341 | 'args' 342 | >>> argspec.varkw 343 | 'kw' 344 | >>> argspec.defaults 345 | (1, 2) 346 | >>> argspec.kwonlyargs 347 | [] 348 | >>> argspec.kwonlydefaults 349 | 350 | ``` 351 | 352 | You can check that the ``__annotations__`` dictionary is preserved: 353 | 354 | ```python 355 | >>> f.__annotations__ is f.__wrapped__.__annotations__ 356 | True 357 | 358 | ``` 359 | 360 | Here ``f.__wrapped__`` is the original undecorated function. 361 | This attribute exists for consistency with the behavior of 362 | ``functools.update_wrapper``. 363 | 364 | Another attribute copied from the original function is ``__qualname__``, 365 | the qualified name. This attribute was introduced in Python 3.3. 366 | 367 | ## ``decorator.decorator`` 368 | 369 | It can become tedious to write a caller function (like the above 370 | ``_trace`` example) and then a trivial wrapper 371 | (``def trace(f): return decorate(f, _trace)``) every time. 372 | Not to worry! The ``decorator`` module provides an easy shortcut 373 | to convert the caller function into a signature-preserving decorator. 374 | 375 | It is the ``decorator`` function: 376 | 377 | ```python 378 | >>> from decorator import decorator 379 | 380 | ``` 381 | The ``decorator`` function can be used as a signature-changing 382 | decorator, just like ``classmethod`` and ``staticmethod``. 383 | But ``classmethod`` and ``staticmethod`` return generic 384 | objects which are not callable. Instead, ``decorator`` returns 385 | signature-preserving decorators (i.e. functions with a single argument). 386 | 387 | For instance, you can write: 388 | 389 | ```python 390 | >>> @decorator 391 | ... def trace(f, *args, **kw): 392 | ... kwstr = ', '.join('%r: %r' % (k, kw[k]) for k in sorted(kw)) 393 | ... print("calling %s with args %s, {%s}" % (f.__name__, args, kwstr)) 394 | ... return f(*args, **kw) 395 | 396 | ``` 397 | 398 | And ``trace`` is now a decorator! 399 | 400 | ```python 401 | >>> trace # doctest: +ELLIPSIS 402 | 403 | 404 | ``` 405 | 406 | Here is an example of usage: 407 | 408 | ```python 409 | >>> @trace 410 | ... def func(): pass 411 | 412 | >>> func() 413 | calling func with args (), {} 414 | 415 | ``` 416 | 417 | ## Mimicking the behavior of functools.wrap 418 | 419 | Often people are confused by the decorator module since, contrarily 420 | to ``functools.wraps`` in the standard library, it tries very hard 421 | to keep the semantics of the arguments: in particular, positional arguments 422 | stay positional even if they are called with the keyword argument syntax. 423 | An example will make the issue clear. Here is a simple caller 424 | 425 | $$chatty 426 | 427 | and here is a function to decorate: 428 | 429 | $$printsum 430 | 431 | In this example ``x`` and ``y`` are positional arguments (with 432 | defaults). From the caller perspective, it does not matter if the user 433 | calls them as named arguments, they will stay inside the ``args`` 434 | tuple and not inside the ``kwargs`` dictionary: 435 | 436 | ```python 437 | >>> printsum(y=2, x=1) 438 | (1, 2) [] 439 | 3 440 | 441 | ``` 442 | 443 | This is quite different from the behavior of ``functools.wraps``; if you 444 | define the decorator as follows 445 | 446 | $$chattywrapper 447 | 448 | you will see that calling ``printsum`` with named arguments will pass 449 | such arguments to ``kwargs``, while ``args`` will be the empty tuple. 450 | Since version 5 of the decorator module it is possible to mimic that 451 | behavior by using the ``kwsyntax`` flag: 452 | 453 | $$printsum2 454 | 455 | Here is how it works: 456 | 457 | ```python 458 | >>> printsum2(y=2, x=1) 459 | () [('x', 1), ('y', 2)] 460 | 3 461 | 462 | ``` 463 | 464 | This is exactly what the ``chattywrapper`` decorator would print: 465 | positional arguments are seen as keyword arguments, but only if the 466 | client code calls them with the keyword syntax. Otherwise they stay 467 | positional, i.e. they belongs to the ``args`` tuple and not to ``kwargs``: 468 | 469 | ```python 470 | >>> printsum2(1, 2) 471 | (1, 2) [] 472 | 3 473 | 474 | ``` 475 | 476 | ## Decorator factories 477 | 478 | The `decorator` function can also be used to define factories of decorators, 479 | i.e. functions returning decorators. In general you can just write something 480 | like this: 481 | 482 | ```python 483 | def decfactory(param1, param2, ...): 484 | def caller(f, *args, **kw): 485 | return somefunc(f, param1, param2, .., *args, **kw) 486 | return decorator(caller) 487 | ``` 488 | 489 | This is fully general but requires an additional level of nesting. For this 490 | reason since version 4.2 there is a facility to build decorator factories by 491 | using a single caller with default arguments: 492 | 493 | ```python 494 | def caller(f, param1=default1, param2=default2, ..., *args, **kw): 495 | return somefunc(f, param1, param2, *args, **kw) 496 | decfactory = decorator(caller) 497 | ``` 498 | 499 | Notice that this simplified approach *only works with default arguments*, 500 | i.e. `param1`, `param2` etc must have known defaults. Thanks to this 501 | restriction, there exists an unique default decorator, i.e. the member 502 | of the family which uses the default values for all parameters. Such 503 | decorator can be written as ``decfactory()`` with no parameters specified; 504 | moreover, as a shortcut, it is also possible to elide the parenthesis, 505 | a feature much requested by the users. For years I have been opposing 506 | the request, since having explicit parenthesis to me is more clear 507 | and less magic; however once this feature entered in decorators of 508 | the Python standard library (I am referring to the [dataclass decorator]( 509 | https://www.python.org/dev/peps/pep-0557/)) I finally gave up. 510 | 511 | The example below shows how it works in practice. The goal is to 512 | convert a function relying on a blocking resource into a function 513 | returning a "busy" message if the resource is not available. 514 | This can be accomplished with a suitable family of decorators 515 | parameterize by a string, the busy message: 516 | 517 | $$blocking 518 | 519 | Functions decorated with ``blocking`` will return a busy message if 520 | the resource is unavailable, and the intended result if the resource is 521 | available. For instance: 522 | 523 | ```python 524 | >>> @blocking(msg="Please wait ...") 525 | ... def read_data(): 526 | ... time.sleep(3) # simulate a blocking resource 527 | ... return "some data" 528 | 529 | >>> print(read_data()) # data is not available yet 530 | Please wait ... 531 | 532 | >>> time.sleep(1) 533 | >>> print(read_data()) # data is not available yet 534 | Please wait ... 535 | 536 | >>> time.sleep(1) 537 | >>> print(read_data()) # data is not available yet 538 | Please wait ... 539 | 540 | >>> time.sleep(1.1) # after 3.1 seconds, data is available 541 | >>> print(read_data()) 542 | some data 543 | 544 | ``` 545 | 546 | Decorator factories are most useful to framework builders. Here is an example 547 | that gives an idea of how you could manage permissions in a framework: 548 | 549 | $$Action 550 | 551 | where ``restricted`` is a decorator factory defined as follows 552 | 553 | $$restricted 554 | 555 | Notice that if you forget to use the keyword argument notation, i.e. if you 556 | write ``restricted(User)`` instead of ``restricted(user_class=User)`` you 557 | will get an error 558 | 559 | ```python 560 | TypeError: You are decorating a non function: 561 | 562 | ``` 563 | 564 | Be careful! 565 | 566 | ## ``decorator(cls)`` 567 | 568 | The ``decorator`` facility can also produce a decorator starting 569 | from a class with the signature of a caller. In such a case the 570 | produced generator is able to convert functions into factories 571 | to create instances of that class. 572 | 573 | As an example, here is a decorator which can convert a 574 | blocking function into an asynchronous function. When 575 | the function is called, it is executed in a separate thread. 576 | 577 | (This is similar to the approach used in the ``concurrent.futures`` package. 578 | But I don't recommend that you implement futures this way; this is just an 579 | example.) 580 | 581 | $$Future 582 | 583 | The decorated function returns a ``Future`` object. It has a ``.result()`` 584 | method which blocks until the underlying thread finishes and returns 585 | the final result. 586 | 587 | Here is the minimalistic usage: 588 | 589 | ```python 590 | >>> @decorator(Future) 591 | ... def long_running(x): 592 | ... time.sleep(.5) 593 | ... return x 594 | 595 | >>> fut1 = long_running(1) 596 | >>> fut2 = long_running(2) 597 | >>> fut1.result() + fut2.result() 598 | 3 599 | 600 | ``` 601 | 602 | ## contextmanager 603 | 604 | Python's standard library has the ``contextmanager`` decorator, 605 | which converts a generator function into a ``GeneratorContextManager`` 606 | factory. For instance, if you write this... 607 | 608 | ```python 609 | >>> from contextlib import contextmanager 610 | >>> @contextmanager 611 | ... def before_after(before, after): 612 | ... print(before) 613 | ... yield 614 | ... print(after) 615 | 616 | ``` 617 | 618 | ...then ``before_after`` is a factory function that returns 619 | ``GeneratorContextManager`` objects, usable with the ``with`` statement: 620 | 621 | ```python 622 | >>> with before_after('BEFORE', 'AFTER'): 623 | ... print('hello') 624 | BEFORE 625 | hello 626 | AFTER 627 | 628 | ``` 629 | 630 | Basically, it is as if the content of the ``with`` block was executed 631 | in the place of the ``yield`` expression in the generator function. 632 | 633 | In Python 3.2, ``GeneratorContextManager`` objects were enhanced with 634 | a ``__call__`` method, so that they can be used as decorators, like so: 635 | 636 | ```python 637 | >>> ba = before_after('BEFORE', 'AFTER') 638 | >>> 639 | >>> @ba 640 | ... def hello(): 641 | ... print('hello') 642 | ... 643 | >>> hello() 644 | BEFORE 645 | hello 646 | AFTER 647 | 648 | ``` 649 | 650 | The ``ba`` decorator basically inserts a ``with ba:`` block 651 | inside the function. 652 | 653 | However ``GeneratorContextManager`` objects do not preserve the signature of 654 | the decorated functions. The decorated ``hello`` function above will 655 | have the generic signature ``hello(*args, **kwargs)``, but fails if 656 | called with more than zero arguments. 657 | 658 | For these reasons, the `decorator` module, starting from release 3.4, offers a 659 | ``decorator.contextmanager`` decorator that solves both problems, 660 | *and* works in all supported Python versions. Its usage is identical, 661 | and factories decorated with ``decorator.contextmanager`` will return 662 | instances of ``ContextManager``, a subclass of the standard library's 663 | ``contextlib.GeneratorContextManager`` class. The subclass includes 664 | an improved ``__call__`` method, which acts as a signature-preserving 665 | decorator. 666 | 667 | ## The ``FunctionMaker`` class 668 | 669 | The ``decorator`` module also provides a ``FunctionMaker`` class, which 670 | is able to generate on-the-fly functions 671 | with a given name and signature from a function template 672 | passed as a string. 673 | 674 | If you're just writing ordinary decorators, then you probably won't 675 | need to use ``FunctionMaker``. But in some circumstances, it 676 | can be handy. You will see an example shortly--in 677 | the implementation of a cool decorator utility (``decorator_apply``). 678 | 679 | ``FunctionMaker`` provides the ``.create`` classmethod, which 680 | accepts the *name*, *signature*, and *body* of the function 681 | you want to generate, as well as the execution environment 682 | where the function is generated by ``exec``. 683 | 684 | Here's an example: 685 | 686 | ```python 687 | >>> def f(*args, **kw): # a function with a generic signature 688 | ... print(args, kw) 689 | 690 | >>> f1 = FunctionMaker.create('f1(a, b)', 'f(a, b)', dict(f=f)) 691 | >>> f1(1,2) 692 | (1, 2) {} 693 | 694 | ``` 695 | 696 | It is important to notice that the function body is interpolated 697 | before being executed; **be careful** with the ``%`` sign! 698 | 699 | ``FunctionMaker.create`` also accepts keyword arguments. 700 | The keyword arguments are attached to the generated function. 701 | This is useful if you want to set some function attributes 702 | (e.g., the docstring ``__doc__``). 703 | 704 | For debugging/introspection purposes, it may be useful to see 705 | the source code of the generated function. To do this, just 706 | pass ``addsource=True``, and the generated function will get 707 | a ``__source__`` attribute: 708 | 709 | ```python 710 | >>> f1 = FunctionMaker.create( 711 | ... 'f1(a, b)', 'f(a, b)', dict(f=f), addsource=True) 712 | >>> print(f1.__source__) 713 | def f1(a, b): 714 | f(a, b) 715 | 716 | 717 | ``` 718 | 719 | The first argument to ``FunctionMaker.create`` can be a string (as above), 720 | or a function. This is the most common usage, since you typically decorate 721 | pre-existing functions. 722 | 723 | If you're writing a framework, however, you may want to use 724 | ``FunctionMaker.create`` directly, rather than ``decorator``, because it gives 725 | you direct access to the body of the generated function. 726 | 727 | For instance, suppose you want to instrument the ``__init__`` methods of a 728 | set of classes, by preserving their signature. 729 | (This use case is not made up. This is done by SQAlchemy, and other frameworks, 730 | too.) 731 | Here is what happens: 732 | 733 | - If first argument of ``FunctionMaker.create`` is a function, 734 | an instance of ``FunctionMaker`` is created with the attributes 735 | ``args``, ``varargs``, ``keywords``, and ``defaults`` 736 | (these mirror the return values of the standard library's 737 | ``inspect.getfullargspec``). 738 | 739 | - For each item in ``args`` (a list of strings of the names of all required 740 | arguments), an attribute ``arg0``, ``arg1``, ..., ``argN`` is also generated. 741 | 742 | - Finally, there is a ``signature`` attribute, which is a string with the 743 | signature of the original function. 744 | 745 | **NOTE:** You should not pass signature strings with default arguments 746 | (e.g., something like ``'f1(a, b=None)'``). Just pass ``'f1(a, b)'``, 747 | followed by a tuple of defaults: 748 | 749 | ```python 750 | >>> f1 = FunctionMaker.create( 751 | ... 'f1(a, b)', 'f(a, b)', dict(f=f), addsource=True, defaults=(None,)) 752 | >>> print(getfullargspec(f1)) 753 | FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(None,), kwonlyargs=[], kwonlydefaults=None, annotations={}) 754 | 755 | ``` 756 | 757 | ## Getting the source code 758 | 759 | Internally, ``FunctionMaker.create`` uses ``exec`` to generate the 760 | decorated function. Therefore ``inspect.getsource`` will not work for 761 | decorated functions. In IPython, this means that the usual ``??`` trick 762 | will give you the (right on the spot) message ``Dynamically generated 763 | function. No source code available``. 764 | However, there is a workaround. The decorated function has the ``__wrapped__`` 765 | attribute, pointing to the original function. The simplest way to get the 766 | source code is to call ``inspect.getsource`` on the undecorated function: 767 | 768 | ```python 769 | >>> print(inspect.getsource(factorial.__wrapped__)) 770 | @tail_recursive 771 | def factorial(n, acc=1): 772 | "The good old factorial" 773 | if n == 0: 774 | return acc 775 | return factorial(n-1, n*acc) 776 | 777 | 778 | ``` 779 | 780 | ## Dealing with third-party decorators 781 | 782 | Sometimes on the net you find some cool decorator that you would 783 | like to include in your code. However, more often than not, the cool 784 | decorator is not signature-preserving. What you need is an easy way to 785 | upgrade third party decorators to signature-preserving decorators... 786 | *without* having to rewrite them in terms of ``decorator``. 787 | 788 | You can use a ``FunctionMaker`` to implement that functionality as follows: 789 | 790 | $$decorator_apply 791 | 792 | ``decorator_apply`` sets the generated function's ``__wrapped__`` attribute 793 | to the original function, so you can get the right source code. 794 | If you are using a Python later than 3.2, you should also set the 795 | ``__qualname__`` attribute to preserve the qualified name of the original 796 | function. 797 | 798 | Notice that I am not providing this functionality in the ``decorator`` 799 | module directly, since I think it is best to rewrite the decorator instead 800 | of adding another level of indirection. However, practicality 801 | beats purity, so you can add ``decorator_apply`` to your toolbox and 802 | use it if you need to. 803 | 804 | To give a good example for ``decorator_apply``, I will show a pretty slick 805 | decorator that converts a tail-recursive function into an iterative function. 806 | I have shamelessly stolen the core concept from Kay Schluehr's recipe 807 | in the Python Cookbook, 808 | http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691. 809 | 810 | $$TailRecursive 811 | 812 | Here the decorator is implemented as a class returning callable 813 | objects. 814 | 815 | $$tail_recursive 816 | 817 | Here is how you apply the upgraded decorator to the good old factorial: 818 | 819 | $$factorial 820 | 821 | ```python 822 | >>> print(factorial(4)) 823 | 24 824 | 825 | ``` 826 | 827 | This decorator is pretty impressive, and should give you some food for 828 | thought! ;) 829 | 830 | Notice that there is no recursion limit now; you can easily compute 831 | ``factorial(1001)`` (or larger) without filling the stack frame. 832 | 833 | Notice also that the decorator will *not* work on functions which 834 | are not tail recursive, such as the following: 835 | 836 | $$fact 837 | 838 | **Reminder:** A function is *tail recursive* if it does either of the 839 | following: 840 | 841 | - returns a value without making a recursive call; or, 842 | - returns directly the result of a recursive call. 843 | 844 | ## Python 3.5 coroutines 845 | 846 | I am personally not using Python 3.5 coroutines yet. However, some 847 | users requested support for coroutines and since version 4.1 the 848 | decorator module has it. You should consider the support experimental 849 | and kindly report issues if you find any. 850 | 851 | Here I will give a single example of usage. Suppose you want to log the moment 852 | a coroutine starts and the moment it stops for debugging purposes. You could 853 | write code like the following: 854 | 855 | ```python 856 | import time 857 | import logging 858 | from asyncio import run, sleep, wait 859 | from decorator import decorator 860 | 861 | @decorator 862 | async def log_start_stop(coro, *args, **kwargs): 863 | logging.info('Starting %s%s', coro.__name__, args) 864 | t0 = time.time() 865 | await coro(*args, **kwargs) 866 | dt = time.time() - t0 867 | logging.info('Ending %s%s after %d seconds', coro.__name__, args, dt) 868 | 869 | @log_start_stop 870 | async def make_task(n): 871 | for i in range(n): 872 | await sleep(1) 873 | 874 | if __name__ == '__main__': 875 | logging.basicConfig(level=logging.INFO) 876 | tasks = [make_task(3), make_task(2), make_task(1)] 877 | run(wait(tasks)) 878 | ``` 879 | 880 | and you will get an output like this: 881 | 882 | ```bash 883 | INFO:root:Starting make_task(1,) 884 | INFO:root:Starting make_task(3,) 885 | INFO:root:Starting make_task(2,) 886 | INFO:root:Ending make_task(1,) after 1 seconds 887 | INFO:root:Ending make_task(2,) after 2 seconds 888 | INFO:root:Ending make_task(3,) after 3 seconds 889 | ``` 890 | 891 | This may be handy if you have trouble understanding what it going on 892 | with a particularly complex chain of coroutines. With a single line you 893 | can decorate the troubling coroutine function, understand what happens, fix the 894 | issue and then remove the decorator (or keep it if continuous monitoring 895 | of the coroutines makes sense). Notice that 896 | ``inspect.iscoroutinefunction(make_task)`` 897 | will return the right answer (i.e. ``True``). 898 | 899 | It is also possible to define decorators converting coroutine functions 900 | into regular functions, such as the following: 901 | 902 | ```python 903 | @decorator 904 | def coro_to_func(coro, *args, **kw): 905 | "Convert a coroutine into a function" 906 | return run(coro(*args, **kw)) 907 | ``` 908 | 909 | Notice the difference: the caller in ``log_start_stop`` was a coroutine 910 | function and the associate decorator was converting coroutines in coroutines; 911 | the caller in ``coro_to_func`` is a regular function and converts 912 | coroutines -> functions. 913 | 914 | ## Multiple dispatch 915 | 916 | There has been talk of implementing multiple dispatch functions 917 | (i.e. "generic functions") in Python for over ten years. Last year, 918 | something concrete was done for the first time. As of Python 3.4, 919 | we have the decorator ``functools.singledispatch`` to implement generic 920 | functions! 921 | 922 | As its name implies, it is limited to *single dispatch*; in other words, 923 | it is able to dispatch on the first argument of the function only. 924 | 925 | The ``decorator`` module provides the decorator factory ``dispatch_on``, 926 | which can be used to implement generic functions dispatching on *any* argument. 927 | Moreover, it can manage dispatching on more than one argument. 928 | (And, of course, it is signature-preserving.) 929 | 930 | Here is a concrete example (from a real-life use case) where it is desiderable 931 | to dispatch on the second argument. 932 | 933 | Suppose you have an ``XMLWriter`` class, which is instantiated 934 | with some configuration parameters, and has the ``.write`` method which 935 | serializes objects to XML: 936 | 937 | $$XMLWriter 938 | 939 | Here, you want to dispatch on the *second* argument; the first is already 940 | taken by ``self``. The ``dispatch_on`` decorator factory allows you to specify 941 | the dispatch argument simply by passing its name as a string. (Note 942 | that if you misspell the name you will get an error.) 943 | 944 | The decorated function `write` is turned into a generic function ( 945 | `write` is a function at the idea it is decorated; it will be turned 946 | into a method later, at class instantiation time), 947 | and it is called if there are no more specialized implementations. 948 | 949 | Usually, default functions should raise a ``NotImplementedError``, thus 950 | forcing people to register some implementation. 951 | You can perform the registration with a decorator: 952 | 953 | $$writefloat 954 | 955 | Now ``XMLWriter`` can serialize floats: 956 | 957 | ```python 958 | >>> writer = XMLWriter() 959 | >>> writer.write(2.3) 960 | '2.3' 961 | 962 | ``` 963 | 964 | I could give a down-to-earth example of situations in which it is desiderable 965 | to dispatch on more than one argument--for instance, I once implemented 966 | a database-access library where the first dispatching argument was the 967 | the database driver, and the second was the database record--but here 968 | I will follow tradition, and show the time-honored Rock-Paper-Scissors example: 969 | 970 | $$Rock 971 | $$Paper 972 | $$Scissors 973 | 974 | I have added an ordinal to the Rock-Paper-Scissors classes to simplify 975 | the implementation. The idea is to define a generic function (``win(a, 976 | b)``) of two arguments corresponding to the *moves* of the first and 977 | second players. The *moves* are instances of the classes 978 | Rock, Paper, and Scissors: 979 | 980 | - Paper wins over Rock 981 | - Scissors wins over Paper 982 | - Rock wins over Scissors 983 | 984 | The function will return +1 for a win, -1 for a loss, and 0 for parity. 985 | There are 9 combinations, but combinations with the same ordinal 986 | (i.e. the same class) return 0. Moreover, by exchanging the order of the 987 | arguments, the sign of the result changes. Therefore, it is sufficient to 988 | directly specify only 3 implementations: 989 | 990 | $$win 991 | $$winRockPaper 992 | $$winPaperScissors 993 | $$winRockScissors 994 | 995 | Here is the result: 996 | 997 | ```python 998 | >>> win(Paper(), Rock()) 999 | 1 1000 | >>> win(Scissors(), Paper()) 1001 | 1 1002 | >>> win(Rock(), Scissors()) 1003 | 1 1004 | >>> win(Paper(), Paper()) 1005 | 0 1006 | >>> win(Rock(), Rock()) 1007 | 0 1008 | >>> win(Scissors(), Scissors()) 1009 | 0 1010 | >>> win(Rock(), Paper()) 1011 | -1 1012 | >>> win(Paper(), Scissors()) 1013 | -1 1014 | >>> win(Scissors(), Rock()) 1015 | -1 1016 | 1017 | ``` 1018 | 1019 | The point of generic functions is that they play well with subclassing. 1020 | For instance, suppose we define a ``StrongRock``, which does not lose against 1021 | Paper: 1022 | 1023 | $$StrongRock 1024 | $$winStrongRockPaper 1025 | 1026 | Then you do not need to define other implementations; they are 1027 | inherited from the parent: 1028 | 1029 | ```python 1030 | >>> win(StrongRock(), Scissors()) 1031 | 1 1032 | 1033 | ``` 1034 | 1035 | You can introspect the precedence used by the dispatch algorithm by 1036 | calling ``.dispatch_info(*types)``: 1037 | 1038 | ```python 1039 | >>> win.dispatch_info(StrongRock, Scissors) 1040 | [('StrongRock', 'Scissors'), ('Rock', 'Scissors')] 1041 | 1042 | ``` 1043 | 1044 | Since there is no direct implementation for (``StrongRock``, ``Scissors``), 1045 | the dispatcher will look at the implementation for (``Rock``, ``Scissors``) 1046 | which is available. Internally, the algorithm is doing a cross 1047 | product of the class precedence lists (or *Method Resolution Orders*, 1048 | [MRO](http://www.python.org/2.3/mro.html) for short) of ``StrongRock`` 1049 | and ``Scissors``, respectively. 1050 | 1051 | ## Generic functions and virtual ancestors 1052 | 1053 | In Python, generic functions are complicated by the existence of 1054 | "virtual ancestors": superclasses which are not in the class hierarchy. 1055 | 1056 | Consider this class: 1057 | 1058 | $$WithLength 1059 | 1060 | This class defines a ``__len__`` method, and is therefore 1061 | considered to be a subclass of the abstract base class 1062 | ``collections.abc.Sized`` (``collections.Sized`` on Python 2): 1063 | 1064 | ```python 1065 | >>> issubclass(WithLength, collections.abc.Sized) 1066 | True 1067 | 1068 | ``` 1069 | 1070 | However, ``collections.abc.Sized`` is not in the MRO_ of ``WithLength``; it 1071 | is not a true ancestor. Any implementation of generic functions (even 1072 | with single dispatch) must go through some contorsion to take into 1073 | account the virtual ancestors. 1074 | 1075 | In particular, if we define a generic function... 1076 | 1077 | $$get_length 1078 | 1079 | ...implemented on all classes with a length... 1080 | 1081 | $$get_length_sized 1082 | 1083 | ...then ``get_length`` must be defined on ``WithLength`` instances... 1084 | 1085 | ```python 1086 | >>> get_length(WithLength()) 1087 | 0 1088 | 1089 | ``` 1090 | 1091 | ...even if ``collections.abc.Sized`` is not a true ancestor of ``WithLength``. 1092 | 1093 | Of course, this is a contrived example--you could just use the 1094 | builtin ``len``--but you should get the idea. 1095 | 1096 | Since in Python it is possible to consider any instance of ``ABCMeta`` 1097 | as a virtual ancestor of any other class (it is enough to register it 1098 | as ``ancestor.register(cls)``), any implementation of generic functions 1099 | must be aware of the registration mechanism. 1100 | 1101 | For example, suppose you are using a third-party set-like class, like 1102 | the following: 1103 | 1104 | $$SomeSet 1105 | 1106 | Here, the author of ``SomeSet`` made a mistake by inheriting from 1107 | ``collections.abc.Sized`` (instead of ``collections.abc.Set``). 1108 | 1109 | This is not a problem. You can register *a posteriori* 1110 | ``collections.abc.Set`` as a virtual ancestor of ``SomeSet``: 1111 | 1112 | ```python 1113 | >>> _ = collections.abc.Set.register(SomeSet) 1114 | >>> issubclass(SomeSet, collections.abc.Set) 1115 | True 1116 | 1117 | ``` 1118 | 1119 | Now, let's define an implementation of ``get_length`` specific to set: 1120 | 1121 | $$get_length_set 1122 | 1123 | The current implementation (and ``functools.singledispatch`` too) 1124 | is able to discern that a ``Set`` is a ``Sized`` object, by looking at 1125 | the class registry, so it uses the more specific implementation for ``Set``: 1126 | 1127 | ```python 1128 | >>> get_length(SomeSet()) # NB: the implementation for Sized would give 0 1129 | 1 1130 | 1131 | ``` 1132 | 1133 | Sometimes it is not clear how to dispatch. For instance, consider a 1134 | class ``C`` registered both as ``collections.abc.Iterable`` and 1135 | ``collections.abc.Sized``, and defines a generic function ``g`` with 1136 | implementations for both ``collections.abc.Iterable`` *and* 1137 | ``collections.abc.Sized``: 1138 | 1139 | $$singledispatch_example1 1140 | 1141 | It is impossible to decide which implementation to use, since the ancestors 1142 | are independent. The following function will raise a ``RuntimeError`` 1143 | when called. This is consistent with the "refuse the temptation to guess" 1144 | philosophy. ``functools.singledispatch`` would raise a similar error. 1145 | 1146 | It would be easy to rely on the order of registration to decide the 1147 | precedence order. This is reasonable, but also fragile: 1148 | 1149 | - if, during some refactoring, you change the registration order by mistake, 1150 | a different implementation could be taken; 1151 | - if implementations of the generic functions are distributed across modules, 1152 | and you change the import order, a different implementation could be taken. 1153 | 1154 | So the ``decorator`` module prefers to raise an error in the face of ambiguity. 1155 | This is the same approach taken by the standard library. 1156 | 1157 | However, it should be noted that the *dispatch algorithm* used by the decorator 1158 | module is different from the one used by the standard library, so in certain 1159 | cases you will get different answers. The difference is that 1160 | ``functools.singledispatch`` tries to insert the virtual ancestors *before* the 1161 | base classes, whereas ``decorator.dispatch_on`` tries to insert them *after* 1162 | the base classes. 1163 | 1164 | Here's an example that shows the difference: 1165 | 1166 | $$singledispatch_example2 1167 | 1168 | If you play with this example and replace the ``singledispatch`` definition 1169 | with ``functools.singledispatch``, the assertion will break: ``g`` will return 1170 | ``"container"`` instead of ``"s"``, because ``functools.singledispatch`` 1171 | will insert the ``Container`` class right before ``S``. 1172 | 1173 | Notice that here I am not making any bold claim such as "the standard 1174 | library algorithm is wrong and my algorithm is right" or vice versa. It 1175 | just point out that there are some subtle differences. The only way to 1176 | understand what is really happening here is to scratch your head by 1177 | looking at the implementations. I will just notice that 1178 | ``.dispatch_info`` is quite essential to see the class precedence 1179 | list used by algorithm: 1180 | 1181 | ```python 1182 | >>> g, V = singledispatch_example2() 1183 | >>> g.dispatch_info(V) 1184 | [('V',), ('Sized',), ('S',), ('Container',)] 1185 | 1186 | ``` 1187 | 1188 | The current implementation does not implement any kind of cooperation 1189 | between implementations. In other words, nothing is akin either to 1190 | call-next-method in Lisp, or to ``super`` in Python. 1191 | 1192 | Finally, let me notice that the decorator module implementation does 1193 | not use any cache, whereas the ``singledispatch`` implementation does. 1194 | 1195 | ## Caveats and limitations 1196 | 1197 | In the present implementation, decorators generated by ``decorator`` 1198 | can only be used on user-defined Python functions, methods or coroutines. 1199 | I have no interest in decorating generic callable objects. If you want to 1200 | decorate things like classmethods/staticmethods and general callables - 1201 | which I will never support in the decorator module - I suggest you 1202 | to look at the [wrapt](https://wrapt.readthedocs.io/en/latest/) 1203 | project by Graeme Dumpleton. 1204 | 1205 | Since version 5 the ``decorator`` module uses the ``inspect.Signature`` 1206 | object in the standard library. Unfortunately, for legacy reasons, some 1207 | applications introspect decorated functions by using low-level entities like 1208 | the ``__code__`` object and not signature objects. An example will make 1209 | the issue clear: 1210 | 1211 | ```python 1212 | >>> def f(a, b): pass 1213 | >>> f_dec = decorator(_trace)(f) 1214 | >>> f_dec.__code__.co_argcount 1215 | 0 1216 | >>> f_dec.__code__.co_varnames 1217 | ('args', 'kw') 1218 | 1219 | ``` 1220 | This is not what one would expect: the `argcount` should be 2 since 1221 | the original functions has two arguments and the `varnames` should be 1222 | `a` and `b`. The only way to fix the issue is to go back to an implementation 1223 | of the decorator using ``exec``, which is provided for convenience since 1224 | version 5.1: 1225 | 1226 | ```python 1227 | >>> from decorator import decoratorx 1228 | >>> f_dec = decoratorx(_trace)(f) 1229 | >>> f_dec.__code__.co_argcount 1230 | 2 1231 | >>> f_dec.__code__.co_varnames 1232 | ('a', 'b') 1233 | 1234 | ``` 1235 | Rather than using `decoratorx`, you should fix your introspection 1236 | routines to use ``inspect.Signature`` without fiddling with the 1237 | ``__code__`` object. 1238 | 1239 | There is a strange quirk when decorating functions with keyword 1240 | arguments, if one of the arguments has the same name used in the 1241 | caller function for the first argument. The quirk was reported by 1242 | David Goldstein. 1243 | 1244 | Here is an example where it is manifest: 1245 | 1246 | ```python 1247 | >>> @memoize 1248 | ... def getkeys(**kw): 1249 | ... return kw.keys() 1250 | 1251 | >>> getkeys(func='a') # doctest: +ELLIPSIS 1252 | Traceback (most recent call last): 1253 | ... 1254 | TypeError: _memoize() got multiple values for ... 'func' 1255 | 1256 | ``` 1257 | 1258 | The error message looks really strange... until you realize that 1259 | the caller function `_memoize` uses `func` as first argument, 1260 | so there is a confusion between the positional argument and the 1261 | keyword arguments. 1262 | 1263 | The solution is to change the name of the first argument in `_memoize`, 1264 | or to change the implementation like so: 1265 | 1266 | ```python 1267 | 1268 | def _memoize(*all_args, **kw): 1269 | func = all_args[0] 1270 | args = all_args[1:] 1271 | if kw: # frozenset is used to ensure hashability 1272 | key = args, frozenset(kw.items()) 1273 | else: 1274 | key = args 1275 | cache = func.cache # attribute added by memoize 1276 | if key not in cache: 1277 | cache[key] = func(*args, **kw) 1278 | return cache[key] 1279 | ``` 1280 | 1281 | This avoids the need to name the first argument, so the problem 1282 | simply disappears. This is a technique that you should keep in mind 1283 | when writing decorators for functions with keyword arguments. Also, 1284 | notice that lately I have come to believe that decorating functions with 1285 | keyword arguments is not such a good idea, and you may want not to do 1286 | that. 1287 | 1288 | The implementation is such that the decorated function makes 1289 | a (shallow) copy of the original function dictionary: 1290 | 1291 | ```python 1292 | >>> def f(): pass # the original function 1293 | >>> f.attr1 = "something" # setting an attribute 1294 | >>> f.attr2 = "something else" # setting another attribute 1295 | 1296 | >>> traced_f = trace(f) # the decorated function 1297 | 1298 | >>> traced_f.attr1 1299 | 'something' 1300 | >>> traced_f.attr2 = "something different" # setting attr 1301 | >>> f.attr2 # the original attribute did not change 1302 | 'something else' 1303 | 1304 | ``` 1305 | 1306 | Finally, you should be aware of the performance penalty of decorators. 1307 | The worse case is shown by the following example: 1308 | 1309 | ```bash 1310 | $ cat performance.sh 1311 | python3 -m timeit -s " 1312 | from decorator import decorator 1313 | 1314 | @decorator 1315 | def do_nothing(func, *args, **kw): 1316 | return func(*args, **kw) 1317 | 1318 | @do_nothing 1319 | def f(): 1320 | pass 1321 | " "f()" 1322 | 1323 | python3 -m timeit -s " 1324 | def f(): 1325 | pass 1326 | " "f()" 1327 | 1328 | ``` 1329 | On my laptop, using the ``do_nothing`` decorator instead of the 1330 | plain function is five times slower: 1331 | 1332 | ```bash 1333 | $ bash performance.sh 1334 | 1000000 loops, best of 3: 1.39 usec per loop 1335 | 1000000 loops, best of 3: 0.278 usec per loop 1336 | ``` 1337 | 1338 | Of course, a real life function probably does something more useful 1339 | than the function ``f`` here, so the real life performance penalty 1340 | *could* be negligible. As always, the only way to know if there is a 1341 | penalty in your specific use case is to measure it. 1342 | 1343 | ## LICENSE (2-clause BSD) 1344 | 1345 | Copyright (c) 2005-2025, Michele Simionato 1346 | All rights reserved. 1347 | 1348 | Redistribution and use in source and binary forms, with or without 1349 | modification, are permitted provided that the following conditions are 1350 | met: 1351 | 1352 | Redistributions of source code must retain the above copyright 1353 | notice, this list of conditions and the following disclaimer. 1354 | Redistributions in bytecode form must reproduce the above copyright 1355 | notice, this list of conditions and the following disclaimer in 1356 | the documentation and/or other materials provided with the 1357 | distribution. 1358 | 1359 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1360 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1361 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1362 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1363 | HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1364 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1365 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 1366 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1367 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 1368 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 1369 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 1370 | DAMAGE. 1371 | 1372 | If you use this software and you are happy with it, consider sending me a 1373 | note, just to gratify my ego. On the other hand, if you use this software and 1374 | you are unhappy with it, send me a patch! 1375 | """ 1376 | 1377 | today = time.strftime('%Y-%m-%d') 1378 | 1379 | __doc__ = doc.replace('$VERSION', __version__).replace('$DATE', today) 1380 | 1381 | 1382 | def decorator_apply(dec, func): 1383 | """ 1384 | Decorate a function by preserving the signature even if dec 1385 | is not a signature-preserving decorator. 1386 | """ 1387 | return FunctionMaker.create( 1388 | func, 'return decfunc(%(shortsignature)s)', 1389 | dict(decfunc=dec(func)), __wrapped__=func) 1390 | 1391 | 1392 | def _trace(f, *args, **kw): 1393 | kwstr = ', '.join('%r: %r' % (k, kw[k]) for k in sorted(kw)) 1394 | print("calling %s with args %s, {%s}" % (f.__name__, args, kwstr)) 1395 | return f(*args, **kw) 1396 | 1397 | 1398 | def trace(f): 1399 | return decorate(f, _trace) 1400 | 1401 | 1402 | class Future(threading.Thread): 1403 | """ 1404 | A class converting blocking functions into asynchronous 1405 | functions by using threads. 1406 | """ 1407 | def __init__(self, func, *args, **kw): 1408 | try: 1409 | counter = func.counter 1410 | except AttributeError: # instantiate the counter at the first call 1411 | counter = func.counter = itertools.count(1) 1412 | name = '%s-%s' % (func.__name__, next(counter)) 1413 | 1414 | def func_wrapper(): 1415 | self._result = func(*args, **kw) 1416 | super(Future, self).__init__(target=func_wrapper, name=name) 1417 | self.start() 1418 | 1419 | def result(self): 1420 | self.join() 1421 | return self._result 1422 | 1423 | 1424 | def identity_dec(func): 1425 | def wrapper(*args, **kw): 1426 | return func(*args, **kw) 1427 | return wrapper 1428 | 1429 | 1430 | @identity_dec 1431 | def example(): 1432 | pass 1433 | 1434 | 1435 | def memoize_uw(func): 1436 | func.cache = {} 1437 | 1438 | def memoize(*args, **kw): 1439 | if kw: # frozenset is used to ensure hashability 1440 | key = args, frozenset(kw.items()) 1441 | else: 1442 | key = args 1443 | if key not in func.cache: 1444 | func.cache[key] = func(*args, **kw) 1445 | return func.cache[key] 1446 | return functools.update_wrapper(memoize, func) 1447 | 1448 | 1449 | @memoize_uw 1450 | def f1(x): 1451 | "Simulate some long computation" 1452 | time.sleep(1) 1453 | return x 1454 | 1455 | 1456 | def _memoize(func, *args, **kw): 1457 | if kw: # frozenset is used to ensure hashability 1458 | key = args, frozenset(kw.items()) 1459 | else: 1460 | key = args 1461 | cache = func.cache # attribute added by memoize 1462 | if key not in cache: 1463 | cache[key] = func(*args, **kw) 1464 | return cache[key] 1465 | 1466 | 1467 | def memoize(f): 1468 | """ 1469 | A simple memoize implementation. It works by adding a .cache dictionary 1470 | to the decorated function. The cache will grow indefinitely, so it is 1471 | your responsibility to clear it, if needed. 1472 | """ 1473 | f.cache = {} 1474 | return decorate(f, _memoize) 1475 | 1476 | 1477 | @decorator 1478 | def blocking(f, msg='blocking', *args, **kw): 1479 | if not hasattr(f, "thread"): # no thread running 1480 | def set_result(): 1481 | f.result = f(*args, **kw) 1482 | f.thread = threading.Thread(None, set_result) 1483 | f.thread.start() 1484 | return msg 1485 | elif f.thread.is_alive(): 1486 | return msg 1487 | else: # the thread is ended, return the stored result 1488 | del f.thread 1489 | return f.result 1490 | 1491 | 1492 | class User(object): 1493 | "Will just be able to see a page" 1494 | 1495 | 1496 | class PowerUser(User): 1497 | "Will be able to add new pages too" 1498 | 1499 | 1500 | class Admin(PowerUser): 1501 | "Will be able to delete pages too" 1502 | 1503 | 1504 | class PermissionError(Exception): 1505 | """ 1506 | >>> a = Action() 1507 | >>> a.user = User() 1508 | >>> a.view() # ok 1509 | >>> a.insert() # doctest: +IGNORE_EXCEPTION_DETAIL 1510 | Traceback (most recent call last): 1511 | ... 1512 | PermissionError: User does not have the permission to run insert! 1513 | """ 1514 | 1515 | 1516 | @decorator 1517 | def restricted(func, user_class=User, *args, **kw): 1518 | "Restrict access to a given class of users" 1519 | self = args[0] 1520 | if isinstance(self.user, user_class): 1521 | return func(*args, **kw) 1522 | else: 1523 | raise PermissionError( 1524 | '%s does not have the permission to run %s!' 1525 | % (self.user, func.__name__)) 1526 | 1527 | 1528 | class Action(object): 1529 | @restricted(user_class=User) 1530 | def view(self): 1531 | "Any user can view objects" 1532 | 1533 | @restricted(user_class=PowerUser) 1534 | def insert(self): 1535 | "Only power users can insert objects" 1536 | 1537 | @restricted(user_class=Admin) 1538 | def delete(self): 1539 | "Only the admin can delete objects" 1540 | 1541 | 1542 | class TailRecursive(object): 1543 | """ 1544 | tail_recursive decorator based on Kay Schluehr's recipe 1545 | http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691 1546 | with improvements by me and George Sakkis. 1547 | """ 1548 | 1549 | def __init__(self, func): 1550 | self.func = func 1551 | self.firstcall = True 1552 | self.CONTINUE = object() # sentinel 1553 | 1554 | def __call__(self, *args, **kwd): 1555 | CONTINUE = self.CONTINUE 1556 | if self.firstcall: 1557 | func = self.func 1558 | self.firstcall = False 1559 | try: 1560 | while True: 1561 | result = func(*args, **kwd) 1562 | if result is CONTINUE: # update arguments 1563 | args, kwd = self.argskwd 1564 | else: # last call 1565 | return result 1566 | finally: 1567 | self.firstcall = True 1568 | else: # return the arguments of the tail call 1569 | self.argskwd = args, kwd 1570 | return CONTINUE 1571 | 1572 | 1573 | def tail_recursive(func): 1574 | return decorator_apply(TailRecursive, func) 1575 | 1576 | 1577 | @tail_recursive 1578 | def factorial(n, acc=1): 1579 | "The good old factorial" 1580 | if n == 0: 1581 | return acc 1582 | return factorial(n-1, n*acc) 1583 | 1584 | 1585 | def fact(n): # this is not tail-recursive 1586 | if n == 0: 1587 | return 1 1588 | return n * fact(n-1) 1589 | 1590 | 1591 | def a_test_for_pylons(): 1592 | """ 1593 | In version 3.1.0 decorator(caller) returned a nameless partial 1594 | object, thus breaking Pylons. That must not happen again. 1595 | 1596 | >>> decorator(_memoize).__name__ 1597 | '_memoize' 1598 | 1599 | Here is another bug of version 3.1.1 missing the docstring: 1600 | 1601 | >>> factorial.__doc__ 1602 | 'The good old factorial' 1603 | """ 1604 | 1605 | 1606 | def test_kwonlydefaults(): 1607 | """ 1608 | >>> @trace 1609 | ... def f(arg, defarg=1, *args, kwonly=2): pass 1610 | ... 1611 | >>> f.__kwdefaults__ 1612 | {'kwonly': 2} 1613 | """ 1614 | 1615 | 1616 | def test_kwonlyargs(): 1617 | """ 1618 | >>> @trace 1619 | ... def func(a, b, *args, y=2, z=3, **kwargs): 1620 | ... return y, z 1621 | ... 1622 | >>> func('a', 'b', 'c', 'd', 'e', y='y', z='z', cat='dog') 1623 | calling func with args ('a', 'b', 'c', 'd', 'e'), {'cat': 'dog', 'y': 'y', 'z': 'z'} 1624 | ('y', 'z') 1625 | """ 1626 | 1627 | 1628 | def test_kwonly_no_args(): 1629 | """# this was broken with decorator 3.3.3 1630 | >>> @trace 1631 | ... def f(**kw): pass 1632 | ... 1633 | >>> f() 1634 | calling f with args (), {} 1635 | """ 1636 | 1637 | 1638 | def test_kwonly_star_notation(): 1639 | """ 1640 | >>> @trace 1641 | ... def f(*, a=1, **kw): pass 1642 | ... 1643 | >>> import inspect 1644 | >>> inspect.getfullargspec(f) 1645 | FullArgSpec(args=[], varargs=None, varkw='kw', defaults=None, kwonlyargs=['a'], kwonlydefaults={'a': 1}, annotations={}) 1646 | """ 1647 | 1648 | # ####################### multiple dispatch ############################ # 1649 | 1650 | 1651 | class XMLWriter(object): 1652 | def __init__(self, **config): 1653 | self.cfg = config 1654 | 1655 | @dispatch_on('obj') 1656 | def write(self, obj): 1657 | raise NotImplementedError(type(obj)) 1658 | 1659 | 1660 | @XMLWriter.write.register(float) 1661 | def writefloat(self, obj): 1662 | return '%s' % obj 1663 | 1664 | 1665 | class Rock(object): 1666 | ordinal = 0 1667 | 1668 | 1669 | class Paper(object): 1670 | ordinal = 1 1671 | 1672 | 1673 | class Scissors(object): 1674 | ordinal = 2 1675 | 1676 | 1677 | class StrongRock(Rock): 1678 | pass 1679 | 1680 | 1681 | @dispatch_on('a', 'b') 1682 | def win(a, b): 1683 | if a.ordinal == b.ordinal: 1684 | return 0 1685 | elif a.ordinal > b.ordinal: 1686 | return -win(b, a) 1687 | raise NotImplementedError((type(a), type(b))) 1688 | 1689 | 1690 | @win.register(Rock, Paper) 1691 | def winRockPaper(a, b): 1692 | return -1 1693 | 1694 | 1695 | @win.register(Rock, Scissors) 1696 | def winRockScissors(a, b): 1697 | return 1 1698 | 1699 | 1700 | @win.register(Paper, Scissors) 1701 | def winPaperScissors(a, b): 1702 | return -1 1703 | 1704 | 1705 | @win.register(StrongRock, Paper) 1706 | def winStrongRockPaper(a, b): 1707 | return 0 1708 | 1709 | 1710 | class WithLength(object): 1711 | def __len__(self): 1712 | return 0 1713 | 1714 | 1715 | class SomeSet(collections.abc.Sized): 1716 | # methods that make SomeSet set-like 1717 | # not shown ... 1718 | def __len__(self): 1719 | return 0 1720 | 1721 | 1722 | @dispatch_on('obj') 1723 | def get_length(obj): 1724 | raise NotImplementedError(type(obj)) 1725 | 1726 | 1727 | @get_length.register(collections.abc.Sized) 1728 | def get_length_sized(obj): 1729 | return len(obj) 1730 | 1731 | 1732 | @get_length.register(collections.abc.Set) 1733 | def get_length_set(obj): 1734 | return 1 1735 | 1736 | 1737 | class C(object): 1738 | "Registered as Sized and Iterable" 1739 | 1740 | 1741 | collections.abc.Sized.register(C) 1742 | collections.abc.Iterable.register(C) 1743 | 1744 | 1745 | def singledispatch_example1(): 1746 | singledispatch = dispatch_on('obj') 1747 | 1748 | @singledispatch 1749 | def g(obj): 1750 | raise NotImplementedError(type(g)) 1751 | 1752 | @g.register(collections.abc.Sized) 1753 | def g_sized(object): 1754 | return "sized" 1755 | 1756 | @g.register(collections.abc.Iterable) 1757 | def g_iterable(object): 1758 | return "iterable" 1759 | 1760 | g(C()) # RuntimeError: Ambiguous dispatch: Iterable or Sized? 1761 | 1762 | 1763 | def singledispatch_example2(): 1764 | # adapted from functools.singledispatch test case 1765 | singledispatch = dispatch_on('arg') 1766 | 1767 | class S(object): 1768 | pass 1769 | 1770 | class V(c.Sized, S): 1771 | def __len__(self): 1772 | return 0 1773 | 1774 | @singledispatch 1775 | def g(arg): 1776 | return "base" 1777 | 1778 | @g.register(S) 1779 | def g_s(arg): 1780 | return "s" 1781 | 1782 | @g.register(c.Container) 1783 | def g_container(arg): 1784 | return "container" 1785 | 1786 | v = V() 1787 | assert g(v) == "s" 1788 | c.Container.register(V) # add c.Container to the virtual mro of V 1789 | assert g(v) == "s" # since the virtual mro is V, Sized, S, Container 1790 | return g, V 1791 | 1792 | 1793 | @decorator 1794 | def warn_slow(func, duration=0, *args, **kwargs): 1795 | t0 = time.time() 1796 | res = func(*args, **kwargs) 1797 | dt = time.time() - t0 1798 | if dt >= duration: 1799 | print('%s is slow' % func.__name__) 1800 | return res 1801 | 1802 | 1803 | @warn_slow() # with parens 1804 | def operation1(): 1805 | """ 1806 | >>> operation1() 1807 | operation1 is slow 1808 | """ 1809 | time.sleep(.1) 1810 | 1811 | 1812 | @warn_slow # without parens 1813 | def operation2(): 1814 | """ 1815 | >>> operation2() 1816 | operation2 is slow 1817 | """ 1818 | time.sleep(.1) 1819 | 1820 | 1821 | def chatty(func, *args, **kwargs): 1822 | print(args, sorted(kwargs.items())) 1823 | return func(*args, **kwargs) 1824 | 1825 | 1826 | @decorator(chatty) 1827 | def printsum(x=1, y=2): 1828 | print(x + y) 1829 | 1830 | 1831 | @decorator(chatty, kwsyntax=True) 1832 | def printsum2(x=1, y=2): 1833 | print(x + y) 1834 | 1835 | 1836 | def chattywrapper(func): 1837 | @functools.wraps(func) 1838 | def wrapper(*args, **kwargs): 1839 | print(args, kwargs) 1840 | return func(*args, **kwargs) 1841 | return functools.wraps(wrapper) 1842 | 1843 | # ####################### changing the signature ########################## # 1844 | 1845 | 1846 | # see https://github.com/micheles/decorator/pull/85 1847 | def to_method(f): 1848 | """ 1849 | Takes a function with signature (..., context) and returns a new 1850 | function with signature (self, ...) to be used a a method in a 1851 | class with a .context attribute. 1852 | """ 1853 | sig = inspect.signature(f) 1854 | params = list(sig.parameters.values()) 1855 | assert params[-1].name == 'context' 1856 | self = inspect.Parameter('self', inspect.Parameter.POSITIONAL_OR_KEYWORD) 1857 | params.insert(0, self) # insert self 1858 | del params[-1] # remove context 1859 | newsig = '%s%s' % (f.__name__, sig.replace(parameters=params)) 1860 | return FunctionMaker.create( 1861 | newsig, 'context = self.context; return _func_%s' % sig, 1862 | dict(_func_=f)) 1863 | 1864 | 1865 | def foo(x, context=None): 1866 | return x 1867 | 1868 | 1869 | def bar(x, y, context): 1870 | return x + y 1871 | 1872 | 1873 | class Client: 1874 | def __init__(self, context): 1875 | self.context = context 1876 | 1877 | 1878 | def test_change_sig(): 1879 | """ 1880 | >>> Client.foo = to_method(foo) 1881 | >>> Client.bar = to_method(bar) 1882 | >>> c = Client(None) 1883 | >>> assert c.foo(1) == 1 1884 | >>> assert c.bar(1, 2) == 3 1885 | """ 1886 | 1887 | 1888 | if __name__ == '__main__': 1889 | import doctest 1890 | doctest.testmod() 1891 | --------------------------------------------------------------------------------