├── .gitignore ├── DESCRIPTION.rst ├── LICENSE ├── MANIFEST.in ├── README.md ├── conftest.py ├── pytest.ini ├── pytest_readme └── __init__.py ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | test_readme.py 2 | pytest_readme.egg-info 3 | dist -------------------------------------------------------------------------------- /DESCRIPTION.rst: -------------------------------------------------------------------------------- 1 | pytest-readme 2 | ============= 3 | 4 | A pytest plugin to pull out tests from your README.md into pytest automatically. This will make it easy to make sure your readme doesn't contain any errors. 5 | 6 | 7 | Full documentation at https://github.com/boxed/pytest-readme -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020, Anders Hovmöller 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of iommi nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include DESCRIPTION.rst -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pytest-readme 2 | ============= 3 | 4 | A pytest plugin to pull out tests from your README.md into pytest automatically. This will make it easy to make sure your readme doesn't contain any errors. 5 | 6 | Usage 7 | ===== 8 | 9 | First `pip install pytest-readme`, then create a `conftest.py` file in the same directory as your README.md and put the following two lines in it: 10 | 11 | ```python 12 | 13 | from pytest_readme import setup 14 | setup() 15 | ``` 16 | 17 | pytest-readme will generate a file called `test_readme.py`, so you probably want to add that to your `.gitignore` file. 18 | 19 | pytest-readme supports two formats of tests: 20 | * doctest style 21 | * pytest style 22 | 23 | Note also that any blocks of python code without asserts is treated as pytest style test, so the code will be executed in a test. This is useful for at least checking that the functions exists, the parameters are in the correct order and that exceptions aren't raised. 24 | 25 | Doctest style: 26 | 27 | You will need to add the `doctest-modules` flag to your test runs. I recommend creating a pytest.ini file and having the following contents in it to make this automatic: 28 | 29 | ``` 30 | [pytest] 31 | addopts = --doctest-modules 32 | ``` 33 | 34 | Example: 35 | 36 | ```python 37 | >>> 1 + 1 38 | 2 39 | ``` 40 | 41 | Pytest style example: 42 | 43 | ```python 44 | assert 1 + 1 == 2 45 | ``` 46 | 47 | To avoid running a block of code from your readme, just make sure you have an empty line first. This won't show in in the markdown render and pytest-readme will skip the block. 48 | 49 | Badge 50 | ===== 51 | 52 | You can show that your README is tested with this badge: [![Examples tested with pytest-readme](http://img.shields.io/badge/readme-tested-brightgreen.svg)](https://github.com/boxed/pytest-readme) Copy paste the markdown syntax below: 53 | 54 | [![Examples tested with pytest-readme](http://img.shields.io/badge/readme-tested-brightgreen.svg)](https://github.com/boxed/pytest-readme) 55 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | from pytest_readme import setup 2 | setup() -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | # content of pytest.ini 2 | [pytest] 3 | addopts = --doctest-modules -------------------------------------------------------------------------------- /pytest_readme/__init__.py: -------------------------------------------------------------------------------- 1 | def setup(): 2 | with open('test_readme.py', 'w') as out, open('README.md') as readme: 3 | mode = None 4 | output = [] 5 | for i, line in enumerate(readme.readlines()): 6 | output.append('\n') 7 | if mode is None and line.strip() == '```python': 8 | mode = 'first_line' 9 | output[i] = 'def test_line_%s():\n' % i 10 | continue 11 | elif line.strip() == '```': 12 | if mode == 'doctest': 13 | output[i] = ' """\n' 14 | mode = None 15 | continue 16 | elif mode is 'first_line': 17 | if line.strip() == '': 18 | mode = None 19 | output[i - 1] = '\n' 20 | continue 21 | if line.strip().startswith('>>>'): 22 | mode = 'doctest' 23 | output[i - 2] = output[i - 1][:-1] + ' ' + output[i - 2] # move the def line one line up 24 | output[i - 1] = ' """\n' 25 | else: 26 | mode = 'test' 27 | if mode in ('doctest', 'test'): 28 | output[i] = ' ' + line 29 | else: 30 | output[i] = '# %s' % line 31 | 32 | out.writelines(output) -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | # python 2 and 3 3 | universal=1 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from setuptools import setup, find_packages # Always prefer setuptools over distutils 3 | from codecs import open # To use a consistent encoding 4 | from os import path 5 | 6 | here = path.abspath(path.dirname(__file__)) 7 | 8 | # Get the long description from the relevant file 9 | with open(path.join(here, 'DESCRIPTION.rst'), encoding='utf-8') as f: 10 | long_description = f.read() 11 | 12 | setup( 13 | name='pytest-readme', 14 | 15 | version='1.0.1', 16 | 17 | description='Test your README.md file', 18 | long_description=long_description, 19 | 20 | url='https://github.com/boxed/pytest-readme', 21 | 22 | author='Anders Hovmöller', 23 | author_email='boxed@killingar.net', 24 | 25 | license='MIT', 26 | 27 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 28 | classifiers=[ 29 | 'Development Status :: 5 - Production/Stable', 30 | 31 | 'Intended Audience :: Developers', 32 | 'Topic :: Software Development :: Testing', 33 | 34 | 'License :: OSI Approved :: MIT License', 35 | 36 | 'Programming Language :: Python :: 2', 37 | 'Programming Language :: Python :: 2.6', 38 | 'Programming Language :: Python :: 2.7', 39 | 'Programming Language :: Python :: 3', 40 | 'Programming Language :: Python :: 3.2', 41 | 'Programming Language :: Python :: 3.3', 42 | 'Programming Language :: Python :: 3.4', 43 | ], 44 | 45 | keywords='test pytest development', 46 | 47 | # You can just specify the packages manually here if your project is 48 | # simple. Or you can use find_packages(). 49 | packages=['pytest_readme'], 50 | 51 | install_requires=['pytest'], 52 | ) 53 | --------------------------------------------------------------------------------