├── .editorconfig ├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── NEWS.rst ├── README.rst ├── changelog.d ├── .gitignore └── 36.doc.rst ├── docs ├── .gitignore ├── Makefile ├── api │ ├── index.rst │ ├── inspect.rst │ └── variants.rst ├── authors.rst ├── changelog.rst ├── conf.py ├── contributing.rst ├── examples │ ├── __init__.py │ └── example_variants.py ├── index.rst ├── installation.rst ├── make.bat ├── readme.rst ├── requirements-docs.txt ├── sphinx.rst └── usage.rst ├── pyproject.toml ├── requirements_dev.txt ├── setup.cfg ├── setup.py ├── src └── variants │ ├── __init__.py │ ├── _variants.py │ └── inspect.py ├── tests ├── __init__.py ├── _division_data.py ├── test_functions.py ├── test_inspect.py ├── test_instance_methods.py └── test_single_dispatch.py └── tox.ini /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # setuptools_scm 10 | src/variants/_version.py 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | .mypy_cache/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | 60 | # Sphinx documentation 61 | docs/_build/ 62 | 63 | # PyBuilder 64 | target/ 65 | 66 | # pyenv python configuration file 67 | .python-version 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | 3 | language: python 4 | python: 5 | - 3.6 6 | - 3.5 7 | - 3.4 8 | - 3.3 9 | - 2.7 10 | 11 | matrix: 12 | fast_finish: true 13 | include: 14 | - python: 3.6 15 | env: TOXENV=docs 16 | 17 | # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors 18 | install: 19 | - pip install -U tox-travis 20 | - if [[ $TRAVIS_PYTHON_VERSION == "3.3" ]]; then pip install 'virtualenv<16.0'; fi 21 | - if [[ $TRAVIS_PYTHON_VERSION == "3.3" ]]; then pip install 'setuptools<40.0'; fi 22 | 23 | # command to run tests, e.g. python setup.py test 24 | script: tox 25 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Development Lead 6 | ---------------- 7 | 8 | * Paul Ganssle 9 | 10 | Contributors 11 | ------------ 12 | 13 | None yet. Why not be the first? 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============ 4 | Contributing 5 | ============ 6 | 7 | Contributions are welcome, and they are greatly appreciated! Every 8 | little bit helps, and credit will always be given. 9 | 10 | You can contribute in many ways: 11 | 12 | Types of Contributions 13 | ---------------------- 14 | 15 | Report Bugs 16 | ~~~~~~~~~~~ 17 | 18 | Report bugs at https://github.com/python-variants/variants/issues. 19 | 20 | If you are reporting a bug, please include: 21 | 22 | * Your operating system name and version. 23 | * Any details about your local setup that might be helpful in troubleshooting. 24 | * Detailed steps to reproduce the bug. 25 | 26 | Fix Bugs 27 | ~~~~~~~~ 28 | 29 | Look through the GitHub issues for bugs. Anything tagged with "bug" 30 | and "help wanted" is open to whoever wants to implement it. 31 | 32 | Implement Features 33 | ~~~~~~~~~~~~~~~~~~ 34 | 35 | Look through the GitHub issues for features. Anything tagged with "enhancement" 36 | and "help wanted" is open to whoever wants to implement it. 37 | 38 | Write Documentation 39 | ~~~~~~~~~~~~~~~~~~~ 40 | 41 | variants could always use more documentation, whether as part of the 42 | official variants docs, in docstrings, or even on the web in blog posts, 43 | articles, and such. 44 | 45 | Submit Feedback 46 | ~~~~~~~~~~~~~~~ 47 | 48 | The best way to send feedback is to file an issue at https://github.com/python-variants/variants/issues. 49 | 50 | If you are proposing a feature: 51 | 52 | * Explain in detail how it would work. 53 | * Keep the scope as narrow as possible, to make it easier to implement. 54 | * Remember that this is a volunteer-driven project, and that contributions 55 | are welcome :) 56 | 57 | Get Started! 58 | ------------ 59 | 60 | Ready to contribute? Here's how to set up `variants` for local development. 61 | 62 | 1. Fork the `variants` repo on GitHub. 63 | 2. Clone your fork locally:: 64 | 65 | $ git clone git@github.com:your_name_here/variants.git 66 | 67 | 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: 68 | 69 | $ mkvirtualenv variants 70 | $ cd variants/ 71 | $ python setup.py develop 72 | 73 | 4. Create a branch for local development:: 74 | 75 | $ git checkout -b name-of-your-bugfix-or-feature 76 | 77 | Now you can make your changes locally. 78 | 79 | 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: 80 | 81 | $ flake8 variants tests 82 | $ python setup.py test or py.test 83 | $ tox 84 | 85 | To get flake8 and tox, just pip install them into your virtualenv. 86 | 87 | 6. Commit your changes and push your branch to GitHub:: 88 | 89 | $ git add . 90 | $ git commit -m "Your detailed description of your changes." 91 | $ git push origin name-of-your-bugfix-or-feature 92 | 93 | 7. Submit a pull request through the GitHub website. 94 | 95 | Pull Request Guidelines 96 | ----------------------- 97 | 98 | Before you submit a pull request, check that it meets these guidelines: 99 | 100 | 1. The pull request should include tests. 101 | 2. If the pull request adds functionality, the docs should be updated. Put 102 | your new functionality into a function with a docstring, and add the 103 | feature to the list in README.rst. 104 | 3. The pull request should work for Python 2.7, 3.4, 3.5, 3.6 and for PyPy. Check 105 | https://travis-ci.org/python-variants/variants/pull_requests 106 | and make sure that the tests pass for all supported Python versions. 107 | 108 | 109 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache Software License 2.0 3 | 4 | Copyright (c) 2017, Paul Ganssle 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include CONTRIBUTING.rst 3 | include NEWS.rst 4 | include LICENSE 5 | include README.rst 6 | 7 | recursive-include tests * 8 | recursive-exclude * __pycache__ 9 | recursive-exclude * *.py[co] 10 | 11 | recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif 12 | -------------------------------------------------------------------------------- /NEWS.rst: -------------------------------------------------------------------------------- 1 | Version 0.2.0 (2018-05-03) 2 | ========================== 3 | 4 | Features 5 | -------- 6 | 7 | - Added the ``variants.inspect`` module, which provides a public API for 8 | inspecting variant function groups. It currently provides two functions: 9 | ``is_primary`` and ``is_primary_method``. (GH #29) 10 | - Added python_requires to setup, indicating Python version support. (GH #34) 11 | 12 | Bugfixes 13 | -------- 14 | 15 | - Updated MANIFEST.in to include NEWS.rst (GH #34) 16 | 17 | Improved Documentation 18 | ---------------------- 19 | 20 | - Updated permanent redirect links in documentation. (GH #28) 21 | - Flattened TOC entry for changelog in documentation. (GH #32) 22 | - Added section for API documentation, including documentation for the inspect 23 | submodule. (GH #33) 24 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | variants 3 | ======== 4 | 5 | 6 | .. image:: https://img.shields.io/pypi/v/variants.svg 7 | :target: https://pypi.org/project/variants/ 8 | 9 | .. image:: https://img.shields.io/travis/python-variants/variants.svg 10 | :target: https://travis-ci.org/python-variants/variants 11 | 12 | .. image:: https://readthedocs.org/projects/variants/badge/?version=latest 13 | :target: https://variants.readthedocs.io/en/latest/?badge=latest 14 | :alt: Documentation Status 15 | 16 | ``variants`` is a library that provides syntactic sugar for creating alternate forms of functions and other callables, in the same way that alternate constructors are class methods that provide alternate forms of the constructor function. 17 | 18 | To create a function with variants, simply decorate the primary form with ``@variants.primary``, which then adds the ``.variant`` decorator to the original function, which can be used to register new variants. Here is a simple example of a function that prints text, with variants that specify the source of the text to print: 19 | 20 | .. code-block:: python 21 | 22 | import variants 23 | 24 | @variants.primary 25 | def print_text(txt): 26 | print(txt) 27 | 28 | @print_text.variant('from_file') 29 | def print_text(fobj): 30 | print_text(fobj.read()) 31 | 32 | @print_text.variant('from_filepath') 33 | def print_text(fpath): 34 | with open(fpath, 'r') as f: 35 | print_text.from_file(f) 36 | 37 | @print_text.variant('from_url') 38 | def print_text(url): 39 | import requests 40 | r = requests.get(url) 41 | print_text(r.text) 42 | 43 | 44 | ``print_text`` and its variants can be used as such: 45 | 46 | .. code-block:: python 47 | 48 | print_text('Hello, world!') # Hello, world! 49 | 50 | # Create a text file 51 | with open('hello_world.txt', 'w') as f: 52 | f.write('Hello, world (from file)') 53 | 54 | # Print from an open file object 55 | with open('hello_world.txt', 'r') as f: 56 | print_text.from_file(f) # Hello, world (from file) 57 | 58 | # Print from the path to a file object 59 | print_text.from_filepath('hello_world.txt') # Hello, world (from file) 60 | 61 | # Print from a URL 62 | hw_url = 'https://ganssle.io/files/hello_world.txt' 63 | print_text.from_url(hw_url) # Hello, world! (from url) 64 | 65 | 66 | Differences from singledispatch 67 | ------------------------------- 68 | 69 | While ``variants`` and |singledispatch|_ are both intended to provide alternative implementations 70 | to a primary function, the overall aims are slightly different. ``singledispatch`` transparently 71 | dispatches to variant functions based on the *type* of the argument, whereas ``variants`` provides 72 | *explicit* alternative forms of the function. Note that in the above example, both 73 | ``print_text.from_filepath`` and ``print_text.from_url`` take a string, one representing a file 74 | path and one representing a URL. 75 | 76 | Additionally, the ``variants`` is compatible with ``singledispatch``, so you can have the best of 77 | both worlds; an example that uses both: 78 | 79 | 80 | .. code-block:: python 81 | 82 | @variants.primary 83 | @singledispatch 84 | def add(x, y): 85 | return x + y 86 | 87 | @add.variant('from_list') 88 | @add.register(list) 89 | def add(x, y): 90 | return x + [y] 91 | 92 | Which then automatically dispatches between named variants based on type: 93 | 94 | .. code-block:: python 95 | 96 | >>> add(1, 2) 97 | 3 98 | >>> add([1], 2) 99 | [1, 2] 100 | 101 | But also exposes the explicit variant functions: 102 | 103 | .. code-block:: python 104 | 105 | >>> add.from_list([1], 2) 106 | [1, 2] 107 | >>> add.from_list() 108 | 7 @add.register(list) 109 | 8 def add(x, y): 110 | ----> 9 return x + [y] 111 | 112 | TypeError: unsupported operand type(s) for +: 'int' and 'list' 113 | 114 | It is important to note that the ``variants`` decorators **must be the outer decorators**. 115 | 116 | 117 | Installation 118 | ------------ 119 | 120 | To install variants, run this command in your terminal: 121 | 122 | .. code-block:: console 123 | 124 | $ pip install variants 125 | 126 | 127 | Requirements 128 | ------------ 129 | 130 | This is a library for Python, with support for versions 2.7 and 3.4+. 131 | 132 | .. |singledispatch| replace:: ``singledispatch`` 133 | .. _singledispatch: https://docs.python.org/3/library/functools.html#functools.singledispatch 134 | 135 | -------------------------------------------------------------------------------- /changelog.d/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore -------------------------------------------------------------------------------- /changelog.d/36.doc.rst: -------------------------------------------------------------------------------- 1 | Added a documentation page demonstrating the use of the sphinx-autodocs-variants extension for displaying function groups. 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | /variants.rst 2 | /variants.*.rst 3 | /modules.rst 4 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/variants.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/variants.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/variants" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/variants" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/api/index.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Variants API Documentation 3 | ========================== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | variants 9 | variants.inspect 10 | 11 | -------------------------------------------------------------------------------- /docs/api/inspect.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | inspect 3 | ======= 4 | 5 | .. automodule:: variants.inspect 6 | :members: 7 | -------------------------------------------------------------------------------- /docs/api/variants.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | variants 3 | ======== 4 | 5 | .. automodule:: variants 6 | :members: 7 | -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Changelog 3 | ========= 4 | 5 | .. include:: ../NEWS.rst 6 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # variants documentation build configuration file, created by 5 | # sphinx-quickstart on Tue Jul 9 22:26:36 2013. 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 | import sys 17 | import os 18 | 19 | # If extensions (or modules to document with autodoc) are in another 20 | # directory, add these directories to sys.path here. If the directory is 21 | # relative to the documentation root, use os.path.abspath to make it 22 | # absolute, like shown here. 23 | #sys.path.insert(0, os.path.abspath('.')) 24 | 25 | # Get the project root dir, which is the parent dir of this 26 | cwd = os.getcwd() 27 | project_root = os.path.dirname(cwd) 28 | 29 | # Insert the project root dir as the first element in the PYTHONPATH. 30 | # This lets us ensure that the source package is imported, and that its 31 | # version is used. 32 | sys.path.insert(0, os.path.abspath('.')) 33 | sys.path.insert(0, os.path.join(project_root, 'src')) 34 | sys.path.append(os.path.join(project_root, 'docs/examples')) 35 | 36 | import variants 37 | import examples 38 | 39 | # -- General configuration --------------------------------------------- 40 | 41 | # If your documentation needs a minimal Sphinx version, state it here. 42 | #needs_sphinx = '1.0' 43 | 44 | # Add any Sphinx extension module names here, as strings. They can be 45 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 46 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx_autodoc_variants'] 47 | 48 | # Add any paths that contain templates here, relative to this directory. 49 | templates_path = ['_templates'] 50 | 51 | # The suffix of source filenames. 52 | source_suffix = '.rst' 53 | 54 | # The encoding of source files. 55 | #source_encoding = 'utf-8-sig' 56 | 57 | # The master toctree document. 58 | master_doc = 'index' 59 | 60 | # General information about the project. 61 | project = u'variants' 62 | copyright = u"2017, Paul Ganssle" 63 | 64 | # The version info for the project you're documenting, acts as replacement 65 | # for |version| and |release|, also used in various other places throughout 66 | # the built documents. 67 | # 68 | # The short X.Y version. 69 | version = variants.__version__ 70 | # The full version, including alpha/beta/rc tags. 71 | release = variants.__version__ 72 | 73 | # The language for content autogenerated by Sphinx. Refer to documentation 74 | # for a list of supported languages. 75 | #language = None 76 | 77 | # There are two options for replacing |today|: either, you set today to 78 | # some non-false value, then it is used: 79 | #today = '' 80 | # Else, today_fmt is used as the format for a strftime call. 81 | #today_fmt = '%B %d, %Y' 82 | 83 | # List of patterns, relative to source directory, that match files and 84 | # directories to ignore when looking for source files. 85 | exclude_patterns = ['_build'] 86 | 87 | # The reST default role (used for this markup: `text`) to use for all 88 | # documents. 89 | #default_role = None 90 | 91 | # If true, '()' will be appended to :func: etc. cross-reference text. 92 | #add_function_parentheses = True 93 | 94 | # If true, the current module name will be prepended to all description 95 | # unit titles (such as .. function::). 96 | #add_module_names = True 97 | 98 | # If true, sectionauthor and moduleauthor directives will be shown in the 99 | # output. They are ignored by default. 100 | #show_authors = False 101 | 102 | # The name of the Pygments (syntax highlighting) style to use. 103 | pygments_style = 'sphinx' 104 | 105 | # A list of ignored prefixes for module index sorting. 106 | #modindex_common_prefix = [] 107 | 108 | # If true, keep warnings as "system message" paragraphs in the built 109 | # documents. 110 | #keep_warnings = False 111 | 112 | 113 | # -- Options for HTML output ------------------------------------------- 114 | 115 | # The theme to use for HTML and HTML Help pages. See the documentation for 116 | # a list of builtin themes. 117 | html_theme = 'sphinx_rtd_theme' 118 | 119 | # Theme options are theme-specific and customize the look and feel of a 120 | # theme further. For a list of options available for each theme, see the 121 | # documentation. 122 | #html_theme_options = {} 123 | 124 | # Add any paths that contain custom themes here, relative to this directory. 125 | #html_theme_path = [] 126 | 127 | # The name for this set of Sphinx documents. If None, it defaults to 128 | # " v documentation". 129 | #html_title = None 130 | 131 | # A shorter title for the navigation bar. Default is the same as 132 | # html_title. 133 | #html_short_title = None 134 | 135 | # The name of an image file (relative to this directory) to place at the 136 | # top of the sidebar. 137 | #html_logo = None 138 | 139 | # The name of an image file (within the static path) to use as favicon 140 | # of the docs. This file should be a Windows icon file (.ico) being 141 | # 16x16 or 32x32 pixels large. 142 | #html_favicon = None 143 | 144 | # Add any paths that contain custom static files (such as style sheets) 145 | # here, relative to this directory. They are copied after the builtin 146 | # static files, so a file named "default.css" will overwrite the builtin 147 | # "default.css". 148 | #html_static_path = ['_static'] 149 | 150 | # If not '', a 'Last updated on:' timestamp is inserted at every page 151 | # bottom, using the given strftime format. 152 | #html_last_updated_fmt = '%b %d, %Y' 153 | 154 | # If true, SmartyPants will be used to convert quotes and dashes to 155 | # typographically correct entities. 156 | #html_use_smartypants = True 157 | 158 | # Custom sidebar templates, maps document names to template names. 159 | #html_sidebars = {} 160 | 161 | # Additional templates that should be rendered to pages, maps page names 162 | # to template names. 163 | #html_additional_pages = {} 164 | 165 | # If false, no module index is generated. 166 | #html_domain_indices = True 167 | 168 | # If false, no index is generated. 169 | #html_use_index = True 170 | 171 | # If true, the index is split into individual pages for each letter. 172 | #html_split_index = False 173 | 174 | # If true, links to the reST sources are added to the pages. 175 | #html_show_sourcelink = True 176 | 177 | # If true, "Created using Sphinx" is shown in the HTML footer. 178 | # Default is True. 179 | #html_show_sphinx = True 180 | 181 | # If true, "(C) Copyright ..." is shown in the HTML footer. 182 | # Default is True. 183 | #html_show_copyright = True 184 | 185 | # If true, an OpenSearch description file will be output, and all pages 186 | # will contain a tag referring to it. The value of this option 187 | # must be the base URL from which the finished HTML is served. 188 | #html_use_opensearch = '' 189 | 190 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 191 | #html_file_suffix = None 192 | 193 | # Output file base name for HTML help builder. 194 | htmlhelp_basename = 'variantsdoc' 195 | 196 | 197 | # -- Options for LaTeX output ------------------------------------------ 198 | 199 | latex_elements = { 200 | # The paper size ('letterpaper' or 'a4paper'). 201 | #'papersize': 'letterpaper', 202 | 203 | # The font size ('10pt', '11pt' or '12pt'). 204 | #'pointsize': '10pt', 205 | 206 | # Additional stuff for the LaTeX preamble. 207 | #'preamble': '', 208 | } 209 | 210 | # Grouping the document tree into LaTeX files. List of tuples 211 | # (source start file, target name, title, author, documentclass 212 | # [howto/manual]). 213 | latex_documents = [ 214 | ('index', 'variants.tex', 215 | u'variants Documentation', 216 | u'Paul Ganssle', 'manual'), 217 | ] 218 | 219 | # The name of an image file (relative to this directory) to place at 220 | # the top of the title page. 221 | #latex_logo = None 222 | 223 | # For "manual" documents, if this is true, then toplevel headings 224 | # are parts, not chapters. 225 | #latex_use_parts = False 226 | 227 | # If true, show page references after internal links. 228 | #latex_show_pagerefs = False 229 | 230 | # If true, show URL addresses after external links. 231 | #latex_show_urls = False 232 | 233 | # Documents to append as an appendix to all manuals. 234 | #latex_appendices = [] 235 | 236 | # If false, no module index is generated. 237 | #latex_domain_indices = True 238 | 239 | 240 | # -- Options for manual page output ------------------------------------ 241 | 242 | # One entry per manual page. List of tuples 243 | # (source start file, name, description, authors, manual section). 244 | man_pages = [ 245 | ('index', 'variants', 246 | u'variants Documentation', 247 | [u'Paul Ganssle'], 1) 248 | ] 249 | 250 | # If true, show URL addresses after external links. 251 | #man_show_urls = False 252 | 253 | 254 | # -- Options for Texinfo output ---------------------------------------- 255 | 256 | # Grouping the document tree into Texinfo files. List of tuples 257 | # (source start file, target name, title, author, 258 | # dir menu entry, description, category) 259 | texinfo_documents = [ 260 | ('index', 'variants', 261 | u'variants Documentation', 262 | u'Paul Ganssle', 263 | 'variants', 264 | 'One line description of project.', 265 | 'Miscellaneous'), 266 | ] 267 | 268 | # Documents to append as an appendix to all manuals. 269 | #texinfo_appendices = [] 270 | 271 | # If false, no module index is generated. 272 | #texinfo_domain_indices = True 273 | 274 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 275 | #texinfo_show_urls = 'footnote' 276 | 277 | # If true, do not generate a @detailmenu in the "Top" node's menu. 278 | #texinfo_no_detailmenu = False 279 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/examples/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-variants/variants/1c2bf4124b4780e354d41664eb4114e6c4deb127/docs/examples/__init__.py -------------------------------------------------------------------------------- /docs/examples/example_variants.py: -------------------------------------------------------------------------------- 1 | import variants 2 | 3 | 4 | @variants.primary 5 | def primary_func(x, y): 6 | """ 7 | This is the primary function. Its docstring should be shown under the 8 | primary function documentation. 9 | """ 10 | 11 | 12 | @primary_func.variant('onearg') 13 | def primary_func(x): 14 | """This is the ``onearg`` variant, it only takes one argument.""" 15 | 16 | 17 | @primary_func.variant('threearg') 18 | def primary_func(x, y, z): 19 | """This is the ``threearg`` variant, it takes three arguments.""" 20 | 21 | 22 | class VariantMethodsClass(object): 23 | @variants.primary 24 | def primary_method(self, x, y): 25 | """ 26 | This is the primary method, its docstring should be shown under the 27 | primary method documentation. 28 | """ 29 | 30 | @primary_method.variant('onearg') 31 | def primary_method(self, x): 32 | """This is the ``onearg`` variant, it takes one argument.""" 33 | 34 | def normal_method(self, a, b): 35 | """This is a normal method, it has no variants""" 36 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to variants's documentation! 2 | ====================================== 3 | 4 | .. include:: ../README.rst 5 | 6 | Documentation 7 | ============= 8 | Contents: 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | readme 14 | installation 15 | usage 16 | Variant Autodoc (Sphinx) 17 | 18 | .. toctree:: 19 | :maxdepth: 1 20 | 21 | contributing 22 | authors 23 | changelog 24 | 25 | .. toctree:: 26 | :maxdepth: 2 27 | 28 | API Documentation 29 | 30 | Indices and tables 31 | ================== 32 | 33 | * :ref:`genindex` 34 | * :ref:`modindex` 35 | * :ref:`search` 36 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | 8 | Stable release 9 | -------------- 10 | 11 | To install variants, run this command in your terminal: 12 | 13 | .. code-block:: console 14 | 15 | $ pip install variants 16 | 17 | This is the preferred method to install variants, as it will always install the most recent stable release. 18 | 19 | If you don't have `pip`_ installed, this `Python installation guide`_ can guide 20 | you through the process. 21 | 22 | .. _pip: https://pip.pypa.io/en/stable/ 23 | .. _Python installation guide: http://docs.python-guide.org/en/latest/starting/installation/ 24 | 25 | 26 | From sources 27 | ------------ 28 | 29 | The sources for variants can be downloaded from the `Github repo`_. 30 | 31 | You can either clone the public repository: 32 | 33 | .. code-block:: console 34 | 35 | $ git clone git://github.com/python-variants/variants 36 | 37 | Or download the `tarball`_: 38 | 39 | .. code-block:: console 40 | 41 | $ curl -OL https://github.com/python-variants/variants/tarball/master 42 | 43 | Once you have a copy of the source, you can install it with: 44 | 45 | .. code-block:: console 46 | 47 | $ python setup.py install 48 | 49 | 50 | .. _Github repo: https://github.com/python-variants/variants 51 | .. _tarball: https://github.com/python-variants/variants/tarball/master 52 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\variants.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\variants.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | -------------------------------------------------------------------------------- /docs/requirements-docs.txt: -------------------------------------------------------------------------------- 1 | Sphinx 2 | sphinx_rtd_theme>=0.3.1 3 | sphinx-autodoc-variants 4 | -------------------------------------------------------------------------------- /docs/sphinx.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Documentation example 3 | ===================== 4 | 5 | Using the `sphinx_autodoc_variants `_ extension, primary and variant functions can be grouped together as a single function group. 6 | 7 | Functions 8 | --------- 9 | 10 | With the ``sphinx_autodoc_variants`` extension enabled in ``conf.py``, function groups at the module level will automatically be grouped together by the ``automodule`` directive. To explicitly document a function group, use the ``autoprimary_function`` directive: 11 | 12 | .. code-block:: rst 13 | 14 | .. autoprimary_function:: examples.example_variants.primary_func 15 | :members: 16 | 17 | Which generates: 18 | 19 | .. autoprimary_function:: examples.example_variants.primary_func 20 | :members: 21 | :noindex: 22 | 23 | 24 | Methods 25 | ------- 26 | For a class containing function group methods, the ``autoclass`` directive works, so: 27 | 28 | .. code-block:: rst 29 | 30 | .. autoclass:: examples.example_variants.VariantMethodsClass 31 | :members: 32 | 33 | Resulting in: 34 | 35 | .. autoclass:: examples.example_variants.VariantMethodsClass 36 | :members: 37 | :noindex: 38 | 39 | 40 | As with functions, individual method groups can be documented using the ``autoprimary_method`` directive: 41 | 42 | .. code-block:: rst 43 | 44 | .. autoprimary_method:: examples.example_variants.VariantMethodsClass.primary_method 45 | :members: 46 | 47 | Which generates: 48 | 49 | .. autoprimary_method:: examples.example_variants.VariantMethodsClass.primary_method 50 | :members: 51 | 52 | 53 | .. .. automodule:: examples.example_variants 54 | .. :members: 55 | .. :undoc-members: 56 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Usage 3 | ===== 4 | 5 | To use ``variants`` in a project, decorate the primary function with the ``@variants.primary`` 6 | decorator, which will add the ``variant`` method to your original function. 7 | 8 | .. module:: variants 9 | .. autofunction:: variants.primary 10 | :noindex: 11 | 12 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.towncrier] 2 | package = "variants" 3 | package_dir = "src/variants" 4 | filename = "NEWS.rst" 5 | title_format = "Version {version} ({project_date})" 6 | issue_format = "GH #{issue}" 7 | directory = "changelog.d" 8 | -------------------------------------------------------------------------------- /requirements_dev.txt: -------------------------------------------------------------------------------- 1 | pip >= 8.0 2 | wheel 3 | tox 4 | coverage 5 | singledispatch; python_version <= '3.3' 6 | pytest >= 3.0; python_version != '3.3' 7 | pytest < 3.3; python_version == '3.3' 8 | pytest-runner 9 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.1.0 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | search = version='{current_version}' 8 | replace = version='{new_version}' 9 | 10 | [bumpversion:file:variants/__init__.py] 11 | search = __version__ = '{current_version}' 12 | replace = __version__ = '{new_version}' 13 | 14 | [bdist_wheel] 15 | universal = 1 16 | 17 | [flake8] 18 | exclude = docs 19 | 20 | [aliases] 21 | test = pytest 22 | # Define setup.py command aliases here 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """The setup script.""" 5 | 6 | from setuptools import setup, find_packages 7 | 8 | with open('README.rst') as readme_file: 9 | readme = readme_file.read() 10 | 11 | with open('NEWS.rst') as history_file: 12 | history = history_file.read() 13 | 14 | requirements = [] 15 | 16 | setup_requirements = [ 17 | 'setuptools_scm' 18 | ] 19 | 20 | test_requirements = [ 21 | 'pytest', 22 | ] 23 | 24 | DESCRIPTION = ('Library that enables the creation of alternate constructor-like' 25 | 'variant forms for arbitrary functions.') 26 | 27 | setup( 28 | name='variants', 29 | use_scm_version={'write_to': 'src/variants/_version.py'}, 30 | description=DESCRIPTION, 31 | long_description=readme + '\n\n' + history, 32 | author="Paul Ganssle", 33 | author_email='paul@ganssle.io', 34 | url='https://github.com/python-variants/variants', 35 | packages=find_packages(where='src'), 36 | package_dir={'': 'src'}, 37 | include_package_data=True, 38 | install_requires=requirements, 39 | license="Apache Software License 2.0", 40 | zip_safe=True, 41 | keywords='variants', 42 | classifiers=[ 43 | 'Development Status :: 3 - Alpha', 44 | 'Intended Audience :: Developers', 45 | 'License :: OSI Approved :: Apache Software License', 46 | 'Natural Language :: English', 47 | "Programming Language :: Python :: 2", 48 | 'Programming Language :: Python :: 2.7', 49 | 'Programming Language :: Python :: 3', 50 | 'Programming Language :: Python :: 3.4', 51 | 'Programming Language :: Python :: 3.5', 52 | 'Programming Language :: Python :: 3.6', 53 | ], 54 | test_suite='tests', 55 | tests_require=test_requirements, 56 | setup_requires=setup_requirements, 57 | python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*", 58 | ) 59 | -------------------------------------------------------------------------------- /src/variants/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from ._variants import primary 3 | 4 | try: 5 | from ._version import version as __version__ 6 | except ImportError: 7 | __version__ = 'unknown' 8 | 9 | __all__ = ['primary'] 10 | -------------------------------------------------------------------------------- /src/variants/_variants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Provides the variant form decorator.""" 3 | 4 | import functools 5 | 6 | __all__ = ['primary'] 7 | 8 | 9 | VARIANT_WRAPPED_ATTRIBUTES = ( 10 | '__module__', '__name__', '__doc__', '__qualname__', '__annotations__' 11 | ) 12 | 13 | 14 | def variant_wraps(vfunc, wrapped_attributes=VARIANT_WRAPPED_ATTRIBUTES): 15 | """Update the variant function wrapper a la ``functools.wraps``.""" 16 | f = vfunc.__main_form__ 17 | 18 | class SentinelObject: 19 | """A unique sentinel that is not None.""" 20 | 21 | sentinel = SentinelObject() 22 | 23 | for attr in wrapped_attributes: 24 | attr_val = getattr(f, attr, sentinel) 25 | if attr is not sentinel: 26 | setattr(vfunc, attr, attr_val) 27 | 28 | return vfunc 29 | 30 | 31 | class VariantFunction(object): 32 | """Wrapper class for functions with variant forms.""" 33 | 34 | def __init__(self, primary_func): 35 | self._variants = set() 36 | self.__main_form__ = primary_func 37 | 38 | def __call__(self, *args, **kwargs): 39 | return self.__main_form__(*args, **kwargs) 40 | 41 | def __getattr__(self, key): 42 | return getattr(self.__main_form__, key) 43 | 44 | def _add_variant(self, var_name, vfunc): 45 | self._variants.add(var_name) 46 | setattr(self, var_name, vfunc) 47 | 48 | def variant(self, func_name): 49 | """Decorator to add a new variant form to the function.""" 50 | def decorator(vfunc): 51 | self._add_variant(func_name, vfunc) 52 | 53 | return self 54 | 55 | return decorator 56 | 57 | def __get__(self, obj, objtype=None): 58 | # This is necessary to bind instance methods 59 | if obj is not None: 60 | rv = VariantMethod(self, obj) 61 | return variant_wraps(rv) 62 | 63 | return self 64 | 65 | def __repr__(self): 66 | return '<{} {}>'.format(self.__class__.__name__, self.__name__) 67 | 68 | 69 | class VariantMethod(VariantFunction): 70 | """Wrapper class for methods with variant forms.""" 71 | 72 | def __init__(self, variant_func, instance): 73 | self.__instance = instance 74 | 75 | # Convert existing variants to methods 76 | for vname in variant_func._variants: 77 | vfunc = getattr(variant_func, vname) 78 | vmethod = self._as_bound_method(vfunc) 79 | 80 | setattr(self, vname, vmethod) 81 | 82 | self.__main_form__ = self._as_bound_method(variant_func.__main_form__) 83 | 84 | def _as_bound_method(self, vfunc): 85 | @functools.wraps(vfunc) 86 | def bound_method(*args, **kwargs): 87 | return vfunc(self.__instance, *args, **kwargs) 88 | 89 | return bound_method 90 | 91 | def _add_variant(self, var_name, vfunc): 92 | self._variants.add(var_name) 93 | setattr(self, var_name, self._as_bound_method(vfunc)) 94 | 95 | 96 | def primary(f): 97 | """ 98 | Decorator to register a function that has variant forms. 99 | 100 | Decorate the main form of the function with this decorator, and then 101 | subsequent variants should be declared with the same name as the original 102 | function [#]_: 103 | 104 | .. code-block:: python 105 | 106 | import variants 107 | 108 | @variants.primary 109 | def myfunc(fpath): 110 | with open(fpath, 'r') as f: 111 | do_something(f.read()) 112 | 113 | @myfunc.variant('from_url') as f: 114 | def myfunc(url): 115 | r = requests.get(url) 116 | do_something(r.text) 117 | 118 | The ``primary`` decorator returns an object that attempts to transparently 119 | proxy the original methods of the original callable, but variants added to 120 | the primary function will shadow the original methods and attributes. Other 121 | than this, any valid python identifier is a valid name for a variant. 122 | 123 | .. [#] Declaring subsequent variants with the same name as the original 124 | function is a stylistic convention, not a requirement. Decorating 125 | any function with the ``.variant`` decorator will mutate the primary 126 | function object, no matter the name of the variant function. However, 127 | whatever function you use for the variant function declaration will 128 | become an alias for the primary function. 129 | """ 130 | f_out = variant_wraps(VariantFunction(f)) 131 | 132 | return f_out 133 | -------------------------------------------------------------------------------- /src/variants/inspect.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Provides inspection tools for extracting metadata from function groups. 4 | """ 5 | from ._variants import VariantFunction, VariantMethod 6 | 7 | if False: # pragma: nocover 8 | from typing import Any # NOQA 9 | 10 | 11 | def is_primary(f): 12 | # type: (Any) -> bool 13 | """ 14 | Detect if a function is a primary function in a variant group 15 | """ 16 | return isinstance(f, (VariantFunction, VariantMethod)) 17 | 18 | 19 | def is_primary_method(f): 20 | # type: (Any) -> bool 21 | """ 22 | Detect if a function is a primary method in a variant group 23 | """ 24 | return isinstance(f, VariantMethod) 25 | 26 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """Unit test package for variants.""" 4 | -------------------------------------------------------------------------------- /tests/_division_data.py: -------------------------------------------------------------------------------- 1 | 2 | class DivisionDataClass(object): 3 | def __init__(self): 4 | self.MODE_VALS = self.__get_mode_vals() 5 | 6 | ALL_VALS = [(0, 1, 0)] 7 | 8 | DIV_VALS = ALL_VALS + [ 9 | (12, 8, 1.50), 10 | (11, 4, 2.75), 11 | (21, 4, 5.25) 12 | ] 13 | 14 | ROUND_VALS = ALL_VALS + [ 15 | (12, 8, 2.00), 16 | (11, 4, 3.00), 17 | (21, 4, 5.00) 18 | ] 19 | 20 | FLOOR_VALS = ALL_VALS + [ 21 | (12, 8, 1.0), 22 | (11, 4, 2.0), 23 | (21, 4, 5.0) 24 | ] 25 | 26 | CEIL_VALS = ALL_VALS + [ 27 | (12, 8, 2.0), 28 | (11, 4, 3.0), 29 | (21, 4, 6.0) 30 | ] 31 | 32 | def __get_mode_vals(self): 33 | modes_vals = [ 34 | (None, self.DIV_VALS), 35 | ('round', self.ROUND_VALS), 36 | ('floor', self.FLOOR_VALS), 37 | ('ceil', self.CEIL_VALS) 38 | ] 39 | 40 | out = [(vals + (mode,)) 41 | for mode, all_vals in modes_vals 42 | for vals in all_vals] 43 | 44 | return out 45 | 46 | DivisionData = DivisionDataClass() -------------------------------------------------------------------------------- /tests/test_functions.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import math 4 | 5 | import variants 6 | 7 | from ._division_data import DivisionData 8 | 9 | import pytest 10 | import six 11 | 12 | 13 | ### 14 | # Example implementation - division function 15 | @variants.primary 16 | def divide(x, y): 17 | """A function that divides x by y.""" 18 | return x / y 19 | 20 | 21 | @divide.variant('round') 22 | def divide(x, y): 23 | """A version of divide that also rounds.""" 24 | return round(x / y) 25 | 26 | 27 | @divide.variant('round_callmain') 28 | def divide(x, y): 29 | return round(divide(x, y)) 30 | 31 | 32 | @divide.variant('floor') 33 | def divide(x, y): 34 | return math.floor(divide(x, y)) 35 | 36 | 37 | @divide.variant('ceil') 38 | def divide(x, y): 39 | return math.ceil(divide(x, y)) 40 | 41 | 42 | @divide.variant('mode') 43 | def divide(x, y, mode=None): 44 | funcs = { 45 | None: divide, 46 | 'round': divide.round, 47 | 'floor': divide.floor, 48 | 'ceil': divide.ceil 49 | } 50 | 51 | return funcs[mode](x, y) 52 | 53 | ### 54 | # Division Function Tests 55 | @pytest.mark.parametrize('x, y, expected', DivisionData.DIV_VALS) 56 | def test_main(x, y, expected): 57 | assert divide(x, y) == expected 58 | 59 | 60 | @pytest.mark.parametrize('x,y,expected', DivisionData.ROUND_VALS) 61 | def test_round(x, y, expected): 62 | assert divide.round(x, y) == expected 63 | 64 | 65 | @pytest.mark.parametrize('x,y,expected', DivisionData.ROUND_VALS) 66 | def test_round_callmain(x, y, expected): 67 | assert divide.round_callmain(x, y) == expected 68 | 69 | 70 | @pytest.mark.parametrize('x,y,expected', DivisionData.FLOOR_VALS) 71 | def test_floor(x, y, expected): 72 | assert divide.floor(x, y) == expected 73 | 74 | 75 | @pytest.mark.parametrize('x,y,expected', DivisionData.CEIL_VALS) 76 | def test_ceil(x, y, expected): 77 | assert divide.ceil(x, y) == expected 78 | 79 | 80 | @pytest.mark.parametrize('x,y,expected,mode', DivisionData.MODE_VALS) 81 | def test_mode(x, y, expected, mode): 82 | assert divide.mode(x, y, mode) == expected 83 | 84 | 85 | ### 86 | # Division function metadata tests 87 | def test_name(): 88 | assert divide.__name__ == 'divide' 89 | 90 | 91 | @pytest.mark.skipif(six.PY2, reason='__qualname__ introduced in Python 3.3') 92 | def test_qualname(): 93 | assert divide.__qualname__ == 'divide' 94 | 95 | 96 | def test_docstring(): 97 | assert divide.__doc__ == """A function that divides x by y.""" 98 | 99 | 100 | def test_repr(): 101 | assert repr(divide) == '' 102 | -------------------------------------------------------------------------------- /tests/test_inspect.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from variants import primary 4 | from variants import inspect as vinsp 5 | 6 | import pytest 7 | 8 | ### 9 | # Setup functions 10 | @primary 11 | def prim_func(): 12 | """Example primary function""" 13 | 14 | 15 | @prim_func.variant('alt') 16 | def prim_func(): 17 | """Example alternate function""" 18 | 19 | 20 | @prim_func.variant('prim_group') 21 | @primary 22 | def prim_func(): 23 | """Nested variant functions""" 24 | 25 | 26 | @prim_func.prim_group.variant('alt2') 27 | def _(): 28 | """Nested alternate function""" 29 | 30 | 31 | def rfunc(): 32 | """Arbitrary function""" 33 | 34 | 35 | class SomeClass(object): 36 | @primary 37 | def prim_method(self): 38 | """Example of a primary method""" 39 | 40 | @prim_method.variant('alt') 41 | def prim_method(self): 42 | """Example of a method alternate""" 43 | 44 | 45 | some_instance = SomeClass() 46 | 47 | 48 | ### 49 | # Tests 50 | @pytest.mark.parametrize('f,exp', [ 51 | (prim_func, True), 52 | (rfunc, False), 53 | (prim_func.alt, False), 54 | (prim_func.prim_group, True), 55 | (prim_func.prim_group.alt2, False), 56 | (SomeClass.prim_method, True), 57 | (some_instance.prim_method, True), 58 | (SomeClass.prim_method.alt, False), 59 | (some_instance.prim_method.alt, False), 60 | ]) 61 | def test_is_primary(f, exp): 62 | assert vinsp.is_primary(f) == exp 63 | 64 | 65 | @pytest.mark.parametrize('f,exp', [ 66 | (SomeClass.prim_method, False), 67 | (some_instance.prim_method, True), 68 | (SomeClass.prim_method.alt, False), 69 | (some_instance.prim_method.alt, False), 70 | (prim_func, False), 71 | (rfunc, False), 72 | (prim_func.alt, False), 73 | (prim_func.prim_group, False), 74 | (prim_func.prim_group.alt2, False), 75 | ]) 76 | def test_is_primary_method(f, exp): 77 | assert vinsp.is_primary_method(f) == exp 78 | -------------------------------------------------------------------------------- /tests/test_instance_methods.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import math 4 | 5 | import pytest 6 | import six 7 | 8 | import variants 9 | from ._division_data import DivisionData 10 | 11 | 12 | class DivisionVariants(object): 13 | """Example class with variant forms on an instance method.""" 14 | 15 | def __init__(self, x): 16 | self.x = x 17 | 18 | @variants.primary 19 | def divide(self, y): 20 | """Function that divides the bound x by y.""" 21 | return self.x / y 22 | 23 | @divide.variant('round') 24 | def divide(self, y): 25 | return round(self.divide(y)) 26 | 27 | @divide.variant('floor') 28 | def divide(self, y): 29 | return math.floor(self.divide(y)) 30 | 31 | @divide.variant('ceil') 32 | def divide(self, y): 33 | return math.ceil(self.divide(y)) 34 | 35 | @divide.variant('mode') 36 | def divide(self, y, mode=None): 37 | funcs = { 38 | None: self.divide, 39 | 'floor': self.divide.floor, 40 | 'ceil': self.divide.ceil, 41 | 'round': self.divide.round 42 | } 43 | 44 | return funcs[mode](y) 45 | 46 | 47 | ### 48 | # Test divide instance methods 49 | @pytest.mark.parametrize('x,y,expected', DivisionData.DIV_VALS) 50 | def test_divide(x, y, expected): 51 | dv = DivisionVariants(x) 52 | assert dv.divide(y) == expected 53 | 54 | 55 | @pytest.mark.parametrize('x,y,expected', DivisionData.ROUND_VALS) 56 | def test_round(x, y, expected): 57 | dv = DivisionVariants(x) 58 | assert dv.divide.round(y) == expected 59 | 60 | 61 | @pytest.mark.parametrize('x,y,expected', DivisionData.FLOOR_VALS) 62 | def test_floor(x, y, expected): 63 | dv = DivisionVariants(x) 64 | assert dv.divide.floor(y) == expected 65 | 66 | 67 | @pytest.mark.parametrize('x,y,expected', DivisionData.CEIL_VALS) 68 | def test_ceil(x, y, expected): 69 | dv = DivisionVariants(x) 70 | assert dv.divide.ceil(y) == expected 71 | 72 | 73 | @pytest.mark.parametrize('x,y,expected,mode', DivisionData.MODE_VALS) 74 | def test_mode(x, y, expected, mode): 75 | dv = DivisionVariants(x) 76 | assert dv.divide.mode(y, mode) == expected 77 | 78 | 79 | @pytest.mark.parametrize('x,y,expected,mode', DivisionData.MODE_VALS) 80 | def test_mode_change_x(x, y, expected, mode): 81 | # Test that with mutable values it still works after x is changed 82 | dv = DivisionVariants(x) 83 | assert dv.divide.mode(y, mode) == expected 84 | 85 | dv.x = 0 86 | assert dv.divide.mode(y, mode) == 0 87 | 88 | 89 | ### 90 | # Division instance method metadata tests 91 | def test_name(): 92 | dv = DivisionVariants(0) 93 | assert dv.divide.__name__ == 'divide' 94 | 95 | 96 | @pytest.mark.skipif(six.PY2, reason='__qualname__ introduced in Python 3.3') 97 | def test_qualname(): 98 | dv = DivisionVariants(0) 99 | assert dv.divide.__qualname__ == 'DivisionVariants.divide' 100 | 101 | 102 | def test_docstring(): 103 | dv = DivisionVariants(0) 104 | assert dv.divide.__doc__ == """Function that divides the bound x by y.""" 105 | -------------------------------------------------------------------------------- /tests/test_single_dispatch.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import pytest 4 | 5 | try: 6 | from functools import singledispatch 7 | except ImportError: 8 | from singledispatch import singledispatch 9 | 10 | import variants 11 | 12 | 13 | ### 14 | # Example implementation - single dispatched function 15 | @variants.primary 16 | @singledispatch 17 | def add_one(arg): 18 | return arg + 1 19 | 20 | 21 | @add_one.variant('from_list') 22 | @add_one.register(list) 23 | def add_one(arg): 24 | return arg + [1] 25 | 26 | 27 | @add_one.variant('from_tuple') 28 | @add_one.register(tuple) 29 | def add_one(arg): 30 | return arg + (1,) 31 | 32 | 33 | ### Tests 34 | def test_single_dispatch_int(): 35 | assert add_one(1) == 2 36 | 37 | 38 | def test_single_dispatch_list(): 39 | assert add_one([2]) == [2, 1] 40 | 41 | 42 | def test_single_dispatch_tuple(): 43 | assert add_one((2,)) == (2, 1) 44 | 45 | 46 | def test_dispatch_list_variant_succeeds(): 47 | assert add_one.from_list([4]) == [4, 1] 48 | 49 | 50 | @pytest.mark.parametrize('arg', [3, (2,)]) 51 | def test_dispatch_list_variant_fails(arg): 52 | with pytest.raises(TypeError): 53 | add_one.from_list(arg) 54 | 55 | 56 | def test_dispatch_tuple_variant_succeeds(): 57 | assert add_one.from_tuple((2,)) == (2, 1) 58 | 59 | 60 | @pytest.mark.parametrize('arg', [3, [2]]) 61 | def test_dispatch_tuple_variant_fails(arg): 62 | with pytest.raises(TypeError): 63 | add_one.from_tuple(arg) 64 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27, py33, py34, py35, py36, flake8, docs 3 | skip_missing_interpreters = true 4 | 5 | [travis] 6 | python = 7 | 3.6: py36 8 | 3.5: py35 9 | 3.4: py34 10 | 3.3: py33 11 | 2.7: py27 12 | 13 | [testenv:flake8] 14 | basepython=python 15 | deps=flake8 16 | commands=flake8 variants 17 | 18 | [testenv] 19 | setenv = 20 | PYTHONPATH = {toxinidir} 21 | deps = 22 | -r{toxinidir}/requirements_dev.txt 23 | commands = 24 | pip install -U pip 25 | pip install -U . 26 | pytest --basetemp={envtmpdir} 27 | 28 | [testenv:docs] 29 | description = invoke sphinx-build to build the HTML docs, check that URIs are valid 30 | basepython = python3.6 31 | deps = {[testenv]deps} 32 | -r{toxinidir}/docs/requirements-docs.txt 33 | commands = sphinx-build -d "{toxworkdir}/docs_doctree" docs "{toxworkdir}/docs_out" {posargs:-W --color -bhtml} 34 | sphinx-build -d "{toxworkdir}/docs_doctree" docs "{toxworkdir}/docs_out" {posargs:-W --color -blinkcheck} 35 | --------------------------------------------------------------------------------