├── LICENSE.rst ├── MANIFEST.in ├── README.rst ├── ipython_pytest.py ├── setup.py └── tests └── example.ipynb /LICENSE.rst: -------------------------------------------------------------------------------- 1 | ==================================== 2 | The ipython_pytest licensing terms 3 | ==================================== 4 | 5 | ipython_pytest is licensed under the terms of the Modified BSD License (also 6 | known as New or Revised or 3-Clause BSD), as follows: 7 | 8 | - Copyright (c) 2016-2018, Antti Kaihola 9 | 10 | All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | Redistributions of source code must retain the above copyright notice, this 16 | list of conditions and the following disclaimer. 17 | 18 | Redistributions in binary form must reproduce the above copyright notice, this 19 | list of conditions and the following disclaimer in the documentation and/or 20 | other materials provided with the distribution. 21 | 22 | Neither the name of the ipython_pytest development team nor the names of its 23 | contributors may be used to endorse or promote products derived from this 24 | software without specific prior written permission. 25 | 26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 27 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 28 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 29 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 30 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 33 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 34 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ipython_pytest 2 | -------------- 3 | 4 | This little IPython extension gives you the ability to 5 | run tests using Pytest in an IPython Notebook. 6 | 7 | **Note:** There are now better and more actively maintained tools to achieve this. 8 | There was also a rejected Pytest feature request for supporting notebooks - 9 | see the discussion for interesting details about this challenge: 10 | 11 | - jupyter-pytest-2_ 12 | - ipytest_ 13 | - pytest_exploratory_ 14 | - `Pytest issue #2268`_ 15 | 16 | See also testbook_ for adding separate unit tests for a notebook. 17 | 18 | The motivation for the extension was to make it possible to 19 | demonstrate test-driven development using pytest in an IPython 20 | notebook driven presentation. 21 | 22 | This extension does not attempt to enable using notebooks as test 23 | suites. The intention is also not to collect test results from 24 | multiple cells, but to do separate test runs for cells in an 25 | interactive presentation. 26 | 27 | This extension attempts to achieve for pytest_ what the 28 | ipython_nose_ extension does for nose_. Unfortunately it isn't able 29 | to inject globals from the notebook environment into the test like the 30 | ``%%nose`` `cell magic in ipython_nose`_. Instead, in 31 | ``ipython_pytest``, all imports, constants and helper functions need 32 | to be contained in the test cell itself. 33 | 34 | .. _jupyter-pytest-2: https://github.com/sashgorokhov/jupyter-pytest-2 35 | .. _ipytest: https://github.com/chmp/ipytest 36 | .. _pytest_exploratory: https://pytest-exploratory.readthedocs.io/ 37 | .. _Pytest issue #2268: https://github.com/pytest-dev/pytest/issues/2268 38 | .. _testbook: https://testbook.readthedocs.io/en/latest/ 39 | .. _pytest: https://pytest.org/ 40 | .. _ipython_nose: https://github.com/taavi/ipython_nose 41 | .. _cell magic in ipython_nose: https://github.com/taavi/ipython_nose/pull/11/files 42 | .. _nose: https://nose.readthedocs.io/ 43 | 44 | 45 | Installation 46 | ------------ 47 | 48 | * Make sure your IPython Notebook server can import ``ipython_pytest.py`` (e.g. 49 | copy it to a directory in your ``PYTHONPATH``, or modify ``PYTHONPATH`` 50 | before starting IPython Notebook). It's also probably sufficient to have 51 | ``ipython_pytest.py`` in the directory from which you run the notebook, e.g.:: 52 | 53 | $ ls 54 | ipython_pytest.py 55 | $ ipython notebook 56 | 57 | * You can also install it in a virtualenv in development mode:: 58 | 59 | $ cd ipython-pytest 60 | $ pip install -e . 61 | 62 | 63 | Usage 64 | ----- 65 | 66 | * Add a cell containing:: 67 | 68 | %load_ext ipython_pytest 69 | 70 | somewhere in your notebook. 71 | 72 | * Write tests that conform to Pytest conventions, e.g.:: 73 | 74 | def test_arithmetic(): 75 | assert 1+1 == 2 76 | 77 | * Add a cell consisting of:: 78 | 79 | %%pytest 80 | 81 | def test_my_stuff(): 82 | assert 42 == 42 83 | 84 | to your notebook and run that cell. That will discover your 85 | ``test_*`` functions, run them, and show console output from 86 | Pytest. 87 | 88 | * Pass standard Pytest arguments to the magic:: 89 | 90 | %%pytest --tb=short 91 | 92 | 93 | Caveats 94 | ------- 95 | 96 | * None of the objects defined in other cells of the notebook are available 97 | in the test cell. 98 | 99 | 100 | Authors 101 | ------- 102 | 103 | * Antti Kaihola 104 | 105 | 106 | Acknowledgements 107 | ---------------- 108 | 109 | Thanks to Taavi Burns for the idea in his ipython_nose_ package. 110 | 111 | 112 | Get the code 113 | ------------ 114 | 115 | :: 116 | 117 | git clone https://github.com/akaihola/ipython_pytest.git 118 | 119 | 120 | Copyright 121 | --------- 122 | 123 | Copyright (c) 2016, 2018, Antti Kaihola. 124 | 125 | All rights reserved. 126 | 127 | Redistribution and use in source and binary forms, with or without 128 | modification, are permitted provided that the following conditions are met: 129 | 130 | Redistributions of source code must retain the above copyright notice, this 131 | list of conditions and the following disclaimer. 132 | 133 | Redistributions in binary form must reproduce the above copyright notice, this 134 | list of conditions and the following disclaimer in the documentation and/or 135 | other materials provided with the distribution. 136 | 137 | Neither the name of the developers nor the names of contributors may 138 | be used to endorse or promote products derived from this software 139 | without specific prior written permission. 140 | 141 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 142 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 143 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 144 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 145 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 146 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 147 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 148 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 149 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 150 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 151 | -------------------------------------------------------------------------------- /ipython_pytest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shlex 3 | import sys 4 | from pathlib import Path 5 | 6 | import tempfile 7 | from IPython.core import magic 8 | from pytest import main as pytest_main 9 | 10 | 11 | TEST_MODULE_NAME = '_ipytesttmp' 12 | 13 | def pytest(line, cell): 14 | with tempfile.TemporaryDirectory() as root: 15 | oldcwd = os.getcwd() 16 | os.chdir(root) 17 | tests_module_path = '{}.py'.format(TEST_MODULE_NAME) 18 | try: 19 | Path(tests_module_path).write_text(cell) 20 | args = shlex.split(line) 21 | os.environ['COLUMNS'] = '80' 22 | pytest_main(args + [tests_module_path]) 23 | if TEST_MODULE_NAME in sys.modules: 24 | del sys.modules[TEST_MODULE_NAME] 25 | finally: 26 | os.chdir(oldcwd) 27 | 28 | def load_ipython_extension(ipython): 29 | magic.register_cell_magic(pytest) 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | 4 | with open('README.rst') as f: 5 | long_description = f.read() 6 | 7 | 8 | setup( 9 | name='ipython_pytest', 10 | version='0.0.1', 11 | author='Antti Kaihola', 12 | author_email='antti.kaihola@eniram.fi', 13 | py_modules=['ipython_pytest'], 14 | url='https://github.com/akaihola/ipython_pytest', 15 | classifiers=['Development Status :: 4 - Beta', 16 | 'Intended Audience :: Developers', 17 | 'License :: OSI Approved :: BSD License', 18 | 'Programming Language :: Python :: 3.5', 19 | 'Operating System :: OS Independent'], 20 | license='README.rst', 21 | description='IPython extension to run pytest for the current cell.', 22 | long_description=long_description, 23 | ) 24 | -------------------------------------------------------------------------------- /tests/example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "%load_ext ipython_pytest" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "%%pytest --tb=short\n", 19 | "\n", 20 | "def test_a():\n", 21 | " assert 42 == 43" 22 | ] 23 | } 24 | ], 25 | "metadata": { 26 | "kernelspec": { 27 | "display_name": "Python 3", 28 | "language": "python", 29 | "name": "python3" 30 | }, 31 | "language_info": { 32 | "codemirror_mode": { 33 | "name": "ipython", 34 | "version": 3 35 | }, 36 | "file_extension": ".py", 37 | "mimetype": "text/x-python", 38 | "name": "python", 39 | "nbconvert_exporter": "python", 40 | "pygments_lexer": "ipython3", 41 | "version": "3.6.5" 42 | } 43 | }, 44 | "nbformat": 4, 45 | "nbformat_minor": 2 46 | } 47 | --------------------------------------------------------------------------------