├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── pytest_checkipdb.py ├── requirements.txt ├── setup.py ├── tests ├── conftest.py ├── test_checkbreakpoint.py ├── test_checkipdb.py └── test_checkpdb.py ├── tox-local.ini └── tox.ini /.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: Python package 5 | 6 | on: 7 | push: 8 | branches: [ master, develop ] 9 | pull_request: 10 | branches: [ master, develop ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: [3.5, 3.6, 3.7, 3.8] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v2 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install flake8 pytest 30 | pip install -e . 31 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 37 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 38 | - name: Test with pytest 39 | run: | 40 | pytest -v --disable-warnings 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask instance folder 57 | instance/ 58 | 59 | # Sphinx documentation 60 | docs/_build/ 61 | 62 | # PyBuilder 63 | target/ 64 | 65 | # IPython Notebook 66 | .ipynb_checkpoints 67 | 68 | # pyenv 69 | .python-version 70 | 71 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | 3 | sudo: false 4 | language: python 5 | python: 6 | - "3.5" 7 | - "3.6" 8 | - "3.7" 9 | - "3.8" 10 | 11 | # 3.7 needs Xenial image because of https://github.com/travis-ci/travis-ci/issues/9069 12 | matrix: 13 | include: 14 | - python: "3.7" 15 | dist: xenial 16 | 17 | install: 18 | - pip install tox 19 | - pip install -e . 20 | - "TOX_ENV=${TRAVIS_PYTHON_VERSION/[0-9].[0-9]/py${TRAVIS_PYTHON_VERSION/.}}" 21 | script: tox -e $TOX_ENV 22 | 23 | before_cache: 24 | - rm -rf $HOME/.cache/pip/log 25 | cache: 26 | directories: 27 | - $HOME/.cache/pip 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 1.0.5 (2020-05-31) 4 | 5 | * Added link to github status badge 6 | 7 | 8 | ## 1.0.4 (2020-05-31) 9 | 10 | * Addressed problem with project description page on pypi.org 11 | 12 | 13 | ## 1.0.3 (2020-05-31) 14 | 15 | * Restructured README.md 16 | * Added github status badge 17 | 18 | 19 | ## 1.0.2 (2020-05-30) 20 | 21 | * Fixed `long_description_content_type` for proper markdown render 22 | 23 | 24 | ## 1.0.1 (2020-05-30) 25 | 26 | * Added github action in order to run tests 27 | * Changed the README.rst to README.md 28 | * Updated the package classifiers 29 | 30 | 31 | ## 1.0.0 (2020-05-30) 32 | 33 | * Dropped support for Python 2.7, Python 3.4 and Pypy 34 | * Added support for Python 3.7 and Python 3.8 35 | * Added checker for **breakpoint()** keyword, avalaible since python 3.7 36 | 37 | 38 | ## 0.3.5 (2019-04-30) 39 | 40 | * Dropped support for Python 3.3 41 | 42 | 43 | ## 0.3.4 (2017-02-26) 44 | 45 | * Initial version 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2017 Andreu Vallbona 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include CHANGELOG.md 4 | 5 | recursive-exclude * __pycache__ 6 | recursive-exclude * *.py[co] 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pytest-checkipdb 2 | 3 | ## This repo is obsolete. 4 | 5 | Visit [https://github.com/avallbona/pytest-checkipdb](https://github.com/avallbona/pytest-checkipdb). 6 | -------------------------------------------------------------------------------- /pytest_checkipdb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | import ast 5 | 6 | 7 | def pytest_addoption(parser): 8 | group = parser.getgroup('general') 9 | group.addoption( 10 | '--cipdb', 11 | action='store_true', 12 | dest='check_ipdb', 13 | help='perform ipdb sanity check on .py files' 14 | ) 15 | 16 | 17 | def pytest_collect_file(parent, path): 18 | config = parent.config 19 | if path.ext != '.py' or not config.option.check_ipdb: 20 | return 21 | return CheckIpdbItem(path, parent=parent) 22 | 23 | 24 | class CheckIpdbItem(pytest.Item, pytest.File): 25 | 26 | def __init__(self, path, parent): 27 | super(CheckIpdbItem, self).__init__(path, parent=parent) 28 | self.raw_content = self.fspath.open().read() 29 | 30 | def runtest(self): 31 | t = ast.parse(self.raw_content) 32 | Visitor().visit(t) 33 | 34 | def repr_failure(self, excinfo): 35 | if excinfo.errisinstance(CheckIpdbError): 36 | return '{} in file {}'.format(excinfo.value.args[0], self.fspath) 37 | return super(CheckIpdbItem, self).repr_failure(excinfo) 38 | 39 | def reportinfo(self): 40 | return self.fspath, -1, "cipdb-check" 41 | 42 | 43 | class CheckIpdbError(Exception): 44 | """ indicates an error during cipdb checks. """ 45 | 46 | 47 | class Visitor(ast.NodeVisitor): 48 | 49 | check_functions = ['ipdb', 'pdb', 'set_trace', 'breakpoint'] 50 | 51 | def visit_Call(self, node): 52 | try: 53 | object_to_evaluate = node.func 54 | try: 55 | attribute_to_evaluate = node.func.id 56 | except AttributeError: 57 | attribute_to_evaluate = node.func.attr 58 | for item in self.check_functions: 59 | if attribute_to_evaluate == item: 60 | line_number = object_to_evaluate.lineno 61 | col_number = object_to_evaluate.col_offset 62 | raise CheckIpdbError('Detected {} call at line {} col {}'.format(item, line_number, col_number)) 63 | except AttributeError: 64 | pass 65 | ast.NodeVisitor.generic_visit(self, node) 66 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pygments==2.2.0 2 | decorator==4.0.11 3 | ipdb==0.11 4 | ipython==5.3.0 5 | ipython-genutils==0.1.0 6 | pexpect==4.8.0 7 | pickleshare==0.7.4 8 | prompt-toolkit==1.0.13 9 | ptyprocess==0.5.1 10 | py==1.8.1 11 | pytest==5.4.2 12 | simplegeneric==0.8.1 13 | six==1.10.0 14 | tox==3.5.2 15 | traitlets==4.3.2 16 | wcwidth==0.1.7 17 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import codecs 6 | from setuptools import setup 7 | 8 | 9 | def read(fname): 10 | file_path = os.path.join(os.path.dirname(__file__), fname) 11 | return codecs.open(file_path, encoding='utf-8').read() 12 | 13 | 14 | setup( 15 | name='pytest-checkipdb', 16 | version='1.0.5', 17 | author='Andreu Vallbona', 18 | author_email='avallbona@gmail.com', 19 | maintainer='Andreu Vallbona', 20 | maintainer_email='avallbona@gmail.com', 21 | license='MIT', 22 | url='https://github.com/avallbona/pytest-checkipdb', 23 | description='plugin to check if there are ipdb debugs left', 24 | long_description=read('README.md'), 25 | long_description_content_type='text/markdown', 26 | py_modules=[ 27 | 'pytest_checkipdb' 28 | ], 29 | install_requires=[ 30 | 'pytest>=2.9.2', 31 | ], 32 | classifiers=[ 33 | 'Development Status :: 4 - Beta', 34 | 'Framework :: Pytest', 35 | 'Intended Audience :: Developers', 36 | 'Topic :: Software Development :: Testing', 37 | 'Programming Language :: Python', 38 | 'Programming Language :: Python :: 3', 39 | 'Programming Language :: Python :: 3.5', 40 | 'Programming Language :: Python :: 3.6', 41 | 'Programming Language :: Python :: 3.7', 42 | 'Programming Language :: Python :: 3.8', 43 | 'Programming Language :: Python :: Implementation :: CPython', 44 | 'Operating System :: OS Independent', 45 | 'License :: OSI Approved :: MIT License', 46 | ], 47 | entry_points={ 48 | 'pytest11': [ 49 | 'checkipdb = pytest_checkipdb', 50 | ], 51 | }, 52 | ) 53 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | pytest_plugins = "pytester" 4 | -------------------------------------------------------------------------------- /tests/test_checkbreakpoint.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from _pytest.config import ExitCode 3 | 4 | 5 | class TestWithBreakpoint(object): 6 | 7 | def test_with_no_breakpoint(self, testdir): 8 | testdir.makepyfile(""" 9 | def test_ok_breakpoint_gen(): 10 | pass 11 | """) 12 | result = testdir.runpytest("--cipdb", "-v") 13 | assert result.ret == ExitCode.OK 14 | result.stdout.fnmatch_lines('* PASSED*') 15 | 16 | def test_with_breakpoint(self, testdir): 17 | testdir.makepyfile(""" 18 | # monkeypatching breakpoint 19 | def foo(): 20 | return True 21 | breakpoint = foo 22 | 23 | def test_with_breakpoint_gen(): 24 | breakpoint() 25 | pass 26 | """) 27 | result = testdir.runpytest("--cipdb", "-v") 28 | assert result.ret == ExitCode.TESTS_FAILED 29 | result.stdout.fnmatch_lines('* FAILED*') 30 | 31 | def test_with_breakpoint_commented_single_line(self, testdir): 32 | testdir.makepyfile(""" 33 | def test_with_breakpoint_commented_single_line_gen(): 34 | # breakpoint() 35 | pass 36 | """) 37 | result = testdir.runpytest("--cipdb", "-v") 38 | assert result.ret == ExitCode.OK 39 | result.stdout.fnmatch_lines('* PASSED*') 40 | 41 | def test_with_breakpoint_commented_docstring(self, testdir): 42 | testdir.makepyfile(""" 43 | def test_with_breakpoint_commented_docstring_gen(): 44 | \"\"\" 45 | breakpoint() 46 | \"\"\" 47 | pass 48 | """) 49 | result = testdir.runpytest("--cipdb", "-v") 50 | assert result.ret == ExitCode.OK 51 | result.stdout.fnmatch_lines('* PASSED*') 52 | -------------------------------------------------------------------------------- /tests/test_checkipdb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from _pytest.config import ExitCode 3 | 4 | 5 | class TestWithIpdb(object): 6 | 7 | def test_with_no_ipdb(self, testdir): 8 | testdir.makepyfile(""" 9 | def test_ok_ipdb_gen(): 10 | pass 11 | """) 12 | result = testdir.runpytest("--cipdb", "-v") 13 | assert result.ret == ExitCode.OK 14 | result.stdout.fnmatch_lines('* PASSED*') 15 | 16 | def test_with_ipdb(self, testdir): 17 | testdir.makepyfile(""" 18 | # monkeypatching ipdb 19 | def foo(): 20 | return True 21 | import ipdb; ipdb.set_trace = foo 22 | 23 | def test_ko_ipdb_gen(): 24 | ipdb.set_trace() 25 | pass 26 | """) 27 | result = testdir.runpytest("--cipdb", "-v") 28 | assert result.ret == ExitCode.TESTS_FAILED 29 | result.stdout.fnmatch_lines('* FAILED*') 30 | 31 | def test_with_ipdb_commented_single_line(self, testdir): 32 | testdir.makepyfile(""" 33 | def test_with_ipdb_commented_gen(): 34 | # import ipdb; ipdb.set_trace() 35 | pass 36 | """) 37 | result = testdir.runpytest("--cipdb", "-v") 38 | assert result.ret == ExitCode.OK 39 | result.stdout.fnmatch_lines('* PASSED*') 40 | 41 | def test_with_ipdb_commented_docstring(self, testdir): 42 | testdir.makepyfile(""" 43 | def test_with_ipdb_commented_docstring_gen(): 44 | \"\"\" 45 | import ipdb; ipdb.set_trace() 46 | \"\"\" 47 | pass 48 | """) 49 | result = testdir.runpytest("--cipdb", "-v") 50 | assert result.ret == ExitCode.OK 51 | result.stdout.fnmatch_lines('* PASSED*') 52 | -------------------------------------------------------------------------------- /tests/test_checkpdb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from _pytest.config import ExitCode 3 | 4 | 5 | class TestWithPdb(object): 6 | 7 | def test_with_no_pdb(self, testdir): 8 | testdir.makepyfile(""" 9 | def test_ok_ipdb_gen(): 10 | pass 11 | """) 12 | result = testdir.runpytest("--cipdb", "-v") 13 | assert result.ret == 0 14 | result.stdout.fnmatch_lines('* PASSED*') 15 | 16 | def test_with_pdb(self, testdir): 17 | testdir.makepyfile(""" 18 | # monkeypatching pdb 19 | def foo(): 20 | return True 21 | import pdb; pdb.set_trace = foo 22 | 23 | def test_with_pdb_gen(): 24 | import pdb; pdb.set_trace() 25 | pass 26 | """) 27 | result = testdir.runpytest("--cipdb", "-v") 28 | assert result.ret == ExitCode.TESTS_FAILED 29 | result.stdout.fnmatch_lines('* FAILED*') 30 | 31 | def test_with_pdb_commented_single_line(self, testdir): 32 | testdir.makepyfile(""" 33 | def test_with_pdb_commented_single_line_gen(): 34 | # import pdb; pdb.set_trace() 35 | pass 36 | """) 37 | result = testdir.runpytest("--cipdb", "-v") 38 | assert result.ret == 0 39 | result.stdout.fnmatch_lines('* PASSED*') 40 | 41 | def test_with_pdb_commented_docstring(self, testdir): 42 | testdir.makepyfile(""" 43 | def test_with_pdb_commented_docstring_gen(): 44 | \"\"\" 45 | import pdb; pdb.set_trace() 46 | \"\"\" 47 | pass 48 | """) 49 | result = testdir.runpytest("--cipdb", "-v") 50 | assert result.ret == 0 51 | result.stdout.fnmatch_lines('* PASSED*') 52 | -------------------------------------------------------------------------------- /tox-local.ini: -------------------------------------------------------------------------------- 1 | # For more information about tox, see https://tox.readthedocs.io/en/latest/ 2 | [tox] 3 | envlist = py35,py36, py37, py38, flake8 4 | skip_missing_interpreters=True 5 | 6 | [flake8] 7 | max-line-length = 120 8 | 9 | [testenv] 10 | deps = 11 | pytest>=3.0.4 12 | ipdb>=0.11 13 | commands = 14 | pytest -v --disable-warnings 15 | 16 | [testenv:py35] 17 | basepython = {homedir}/.pyenv/versions/3.5.3/bin/python3 18 | commands = 19 | pytest -v -s --disable-warnings -k 'not test_checkbreakpoint' 20 | 21 | [testenv:py36] 22 | commands = 23 | pytest -v -s --disable-warnings -k 'not test_checkbreakpoint' 24 | 25 | [testenv:py37] 26 | basepython = {homedir}/.pyenv/versions/3.7.7/bin/python3 27 | commands = 28 | pytest -v -s --disable-warnings 29 | 30 | [testenv:py38] 31 | basepython = {homedir}/.pyenv/versions/3.8.3/bin/python3 32 | commands = 33 | pytest -v -s --disable-warnings 34 | 35 | [testenv:flake8] 36 | basepython = python3.6 37 | deps = 38 | flake8==3.8.2 39 | commands = 40 | flake8 . 41 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # For more information about tox, see https://tox.readthedocs.io/en/latest/ 2 | [tox] 3 | envlist = py35, py36, py37, py38, flake8 4 | skip_missing_interpreters=True 5 | install_command = pip install {opts} {packages} 6 | 7 | [flake8] 8 | max-line-length = 120 9 | 10 | [testenv] 11 | setenv = 12 | PYTHONPATH = {toxinidir}:{toxinidir} 13 | deps = 14 | pytest 15 | ipdb 16 | commands = 17 | pytest -v --disable-warnings 18 | 19 | [testenv:py35] 20 | commands = 21 | pytest -v -s --disable-warnings -k 'not test_checkbreakpoint' 22 | 23 | [testenv:py36] 24 | commands = 25 | pytest -v -s --disable-warnings -k 'not test_checkbreakpoint' 26 | 27 | [testenv:py37] 28 | commands = 29 | pytest -v -s --disable-warnings 30 | 31 | [testenv:py38] 32 | commands = 33 | pytest -v -s --disable-warnings 34 | 35 | [testenv:flake8] 36 | basepython = python3.6 37 | deps = 38 | flake8==3.8.2 39 | commands = 40 | flake8 . 41 | --------------------------------------------------------------------------------