commit 8265c13fcc3880862aa541732c13ee7e57eabd8ebd538910ab77cbdd42eb9dac Author: Steve Kowalik Date: Tue Jun 17 04:19:49 2025 +0000 - Switch to pyproject macros. - No more greedy globs in %files. OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-flake8-debugger?expand=0&rev=12 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bcbb744 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Joseph Kahn + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/flake8-debugger-4.1.2.tar.gz b/flake8-debugger-4.1.2.tar.gz new file mode 100644 index 0000000..4444888 --- /dev/null +++ b/flake8-debugger-4.1.2.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52b002560941e36d9bf806fca2523dc7fb8560a295d5f1a6e15ac2ded7a73840 +size 7801 diff --git a/pycodestyle-indent-size.patch b/pycodestyle-indent-size.patch new file mode 100644 index 0000000..454b275 --- /dev/null +++ b/pycodestyle-indent-size.patch @@ -0,0 +1,10 @@ +--- a/test_linter.py 2021-04-01 09:11:21.755700607 +0200 ++++ b/test_linter.py 2021-04-01 09:11:52.280287278 +0200 +@@ -31,6 +31,7 @@ + max_doc_length = None + hang_closing = False + verbose = False ++ indent_size = 4 + benchmark_keys = {"files": 0, "physical lines": 0, "logical lines": 0} + + diff --git a/python-flake8-debugger.changes b/python-flake8-debugger.changes new file mode 100644 index 0000000..a5c8c75 --- /dev/null +++ b/python-flake8-debugger.changes @@ -0,0 +1,48 @@ +------------------------------------------------------------------- +Tue Jun 17 04:14:45 UTC 2025 - Steve Kowalik + +- Switch to pyproject macros. +- No more greedy globs in %files. + +------------------------------------------------------------------- +Tue May 21 10:32:02 UTC 2024 - Dominique Leuenberger + +- Replace %patchN with %patch -P N: %patchN is deprecated. + +------------------------------------------------------------------- +Wed Oct 12 03:20:30 UTC 2022 - Yogalakshmi Arunachalam + +- Update to version 4.1.2 + * Add tests to bundle. + +- Update to version 4.1.1 + * Add tests to bundle. + +- Update to version 4.1.0 + * Drop support for python 3.6 and remove special handling code. + * bundle licence file. + +------------------------------------------------------------------- +Thu Apr 1 07:18:57 UTC 2021 - Dirk Müller + +- update to 4.0.0: + * Opted back into using Poetry now that the existing issues have been fixed. + * Python 2.7 support was no officially dropped. +- add pycodestyle-indent-size.patch and use pypi tarball to avoid poetry + +------------------------------------------------------------------- +Thu Apr 16 10:43:35 UTC 2020 - Tomáš Chvátal + +- Update to 3.2.1: + * no changelog + +------------------------------------------------------------------- +Tue Dec 4 12:48:02 UTC 2018 - Matej Cepl + +- Remove superfluous devel dependency for noarch package + +------------------------------------------------------------------- +Fri Nov 16 13:05:41 UTC 2018 - John Paul Adrian Glaubitz + +- Initial build + + Version 3.1.0 diff --git a/python-flake8-debugger.spec b/python-flake8-debugger.spec new file mode 100644 index 0000000..e9c65ff --- /dev/null +++ b/python-flake8-debugger.spec @@ -0,0 +1,70 @@ +# +# spec file for package python-flake8-debugger +# +# Copyright (c) 2025 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + + +Name: python-flake8-debugger +Version: 4.1.2 +Release: 0 +Summary: ipdb/pdb statement checker plugin for flake8 +License: MIT +URL: https://github.com/jbkahn/flake8-debugger +Source0: https://files.pythonhosted.org/packages/source/f/flake8-debugger/flake8-debugger-%{version}.tar.gz +Source1: LICENSE +Source2: https://raw.githubusercontent.com/JBKahn/flake8-debugger/4.0.0/test_linter.py +# https://github.com/JBKahn/flake8-debugger/issues/28 +Patch1: pycodestyle-indent-size.patch +BuildRequires: %{python_module pip} +BuildRequires: %{python_module poetry-core} +BuildRequires: fdupes +BuildRequires: python-rpm-macros +BuildArch: noarch +# SECTION test requirements +BuildRequires: %{python_module flake8 >= 1.5} +BuildRequires: %{python_module pycodestyle} +BuildRequires: %{python_module pytest} +# /SECTION +Requires: python-flake8 >= 1.5 +Requires: python-pycodestyle +%python_subpackages + +%description +ipdb/pdb statement checker plugin for flake8 + +%prep +%setup -q -n flake8-debugger-%{version} +cp %{SOURCE1} . +cp %{SOURCE2} . +%patch -P 1 -p1 + +%build +%pyproject_wheel + +%install +%pyproject_install +%python_expand %fdupes %{buildroot}%{$python_sitelib} + +%check +%pytest + +%files %{python_files} +%doc README.md +%license LICENSE +%{python_sitelib}/flake8_debugger.py +%pycache_only %{python_sitelib}/__pycache__/flake8_debugger.*.pyc +%{python_sitelib}/flake8_debugger-%{version}.dist-info + +%changelog diff --git a/test_linter.py b/test_linter.py new file mode 100644 index 0000000..bf2c257 --- /dev/null +++ b/test_linter.py @@ -0,0 +1,400 @@ +import pycodestyle + +from flake8_debugger import DebuggerChecker + +import pytest +import sys + + +class CaptureReport(pycodestyle.BaseReport): + """Collect the results of the checks.""" + + def __init__(self, options): + self._results = [] + super(CaptureReport, self).__init__(options) + + def error(self, line_number, offset, text, check): + """Store each error.""" + code = super(CaptureReport, self).error(line_number, offset, text, check) + if code: + record = {"line": line_number, "col": offset, "message": "{0} {1}".format(code, text[5:])} + self._results.append(record) + return code + + +class DebuggerTestStyleGuide(pycodestyle.StyleGuide): + + logical_checks = [] + physical_checks = [] + ast_checks = [("debugger_usage", DebuggerChecker, ["tree", "filename", "lines"])] + max_line_length = None + max_doc_length = None + hang_closing = False + verbose = False + benchmark_keys = {"files": 0, "physical lines": 0, "logical lines": 0} + + +_debugger_test_style = DebuggerTestStyleGuide() + + +def check_code_for_debugger_statements(code): + """Process code using pycodestyle Checker and return all errors.""" + from tempfile import NamedTemporaryFile + + test_file = NamedTemporaryFile(delete=False) + test_file.write(code.encode()) + test_file.flush() + report = CaptureReport(options=_debugger_test_style) + lines = [line + "\n" for line in code.split("\n")] + checker = pycodestyle.Checker(filename=test_file.name, lines=lines, options=_debugger_test_style, report=report) + + checker.check_all() + return report._results + + +class TestQA(object): + def test_catches_simple_debugger(self): + result = check_code_for_debugger_statements("from ipdb import set_trace as r\nr()") + + expected_result = [ + {"line": 2, "message": "T100 trace found: set_trace used as r", "col": 0}, + {"line": 1, "message": "T100 import for set_trace found as r", "col": 0}, + ] + + assert result == expected_result + + def test_catches_simple_debugger_when_called_off_lib(self): + result = check_code_for_debugger_statements("import ipdb\nipdb.set_trace()") + + expected_result = [ + {"line": 2, "message": "T100 trace found: ipdb.set_trace used", "col": 0}, + {"line": 1, "message": "T100 import for ipdb found", "col": 0}, + ] + + assert result == expected_result + + def test_catches_simple_debugger_when_called_off_global(self): + result = check_code_for_debugger_statements("__import__('ipdb').set_trace()") + + expected_result = [{"line": 1, "message": "T100 trace found: set_trace used", "col": 0}] + + assert result == expected_result + + @pytest.mark.skipif(True, reason="Not supported just yet") + def test_catches_simple_debugger_when_called_off_var(self): + result = check_code_for_debugger_statements("import ipdb\ntest = ipdb.set_trace\ntest()") + + expected_result = [ + {"line": 1, "message": "T100 import for ipdb found", "col": 0}, + {"line": 3, "message": "T100 trace found: ipdb.set_trace used", "col": 0}, + ] + assert result == expected_result + + +class TestBreakpoint(object): + @pytest.mark.skipif(sys.version_info < (3, 7), reason="breakpoint builtin introduced in 3.7") + def test_catches_breakpoint_call_for_python_3_7_and_above(self): + result = check_code_for_debugger_statements("breakpoint()") + + expected_result = [{"line": 1, "message": "T100 trace found: breakpoint used", "col": 0}] + + assert result == expected_result + + @pytest.mark.skipif(sys.version_info < (3, 7), reason="breakpoint builtin introduced in 3.7") + def test_catches_breakpoint_import(self): + result = check_code_for_debugger_statements("from builtins import breakpoint") + + expected_result = [{"line": 1, "message": "T100 import for breakpoint found", "col": 0}] + + assert result == expected_result + + @pytest.mark.skipif(sys.version_info < (3, 7), reason="breakpoint builtin introduced in 3.7") + def test_allows_builtins_import(self): + result = check_code_for_debugger_statements("import builtins") + + expected_result = [] + + assert result == expected_result + + @pytest.mark.skipif(sys.version_info < (3, 7), reason="breakpoint builtin introduced in 3.7") + def test_catches_breakpoint_usage_from_builtins(self): + result = check_code_for_debugger_statements("import builtins\nbuiltins.breakpoint()") + + expected_result = [{"col": 0, "line": 2, "message": "T100 trace found: breakpoint used"}] + + assert result == expected_result + + @pytest.mark.skipif(sys.version_info < (3, 7), reason="breakpoint builtin introduced in 3.7") + def test_catches_breakpoint_imported_as_other_name(self): + result = check_code_for_debugger_statements("from builtins import breakpoint as b\nb()") + + expected_result = [ + {"line": 2, "message": "T100 trace found: breakpoint used as b", "col": 0}, + {"line": 1, "message": "T100 import for breakpoint found as b", "col": 0}, + ] + + assert result == expected_result + + @pytest.mark.skipif(sys.version_info >= (3, 7), reason="breakpoint builtin introduced in 3.7") + def test_allows_breakpoint_call_for_python_below_3_7(self): + result = check_code_for_debugger_statements("breakpoint()") + + expected_result = [] + + assert result == expected_result + + +class TestNoQA(object): + @pytest.mark.skipif(sys.version_info < (2, 7), reason="Python 2.6 does not support noqa") + def test_skip_import(self): + result = check_code_for_debugger_statements("from ipdb import set_trace as r # noqa\nr()") + + expected_result = [{"line": 2, "message": "T100 trace found: set_trace used as r", "col": 0}] + + assert result == expected_result + + @pytest.mark.skipif(sys.version_info < (2, 7), reason="Python 2.6 does not support noqa") + def test_skip_usage(self): + result = check_code_for_debugger_statements("from ipdb import set_trace as r\nr() # noqa") + + expected_result = [{"line": 1, "message": "T100 import for set_trace found as r", "col": 0}] + + assert result == expected_result + + @pytest.mark.skipif(sys.version_info < (2, 7), reason="Python 2.6 does not support noqa") + def test_skip_import_and_usage(self): + result = check_code_for_debugger_statements("from ipdb import set_trace as r # noqa\nr() # noqa") + + expected_result = [] + + assert result == expected_result + + +class TestImportCases(object): + def test_import_multiple(self): + result = check_code_for_debugger_statements("import math, ipdb, collections") + assert result == [{"col": 0, "line": 1, "message": "T100 import for ipdb found"}] + + def test_import(self): + result = check_code_for_debugger_statements("import pdb") + assert result == [{"col": 0, "line": 1, "message": "T100 import for pdb found"}] + + def test_import_interactive_shell_embed(self): + result = check_code_for_debugger_statements("from IPython.terminal.embed import InteractiveShellEmbed") + assert result == [{"col": 0, "line": 1, "message": "T100 import for InteractiveShellEmbed found"}] + + def test_import_both_same_line(self): + result = check_code_for_debugger_statements("import pdb, ipdb") + result = sorted(result, key=lambda debugger: debugger["message"]) + expected_result = [ + {"col": 0, "line": 1, "message": "T100 import for ipdb found"}, + {"col": 0, "line": 1, "message": "T100 import for pdb found"}, + ] + assert result == expected_result + + def test_import_math(self): + result = check_code_for_debugger_statements("import math") + assert result == [] + + def test_import_noqa(self): + result = check_code_for_debugger_statements("import ipdb # noqa") + assert result == [] + + +class TestModuleSetTraceCases(object): + def test_import_ipython_terminal_embed_use_InteractiveShellEmbed(self): + result = check_code_for_debugger_statements( + "from IPython.terminal.embed import InteractiveShellEmbed; InteractiveShellEmbed()()" + ) + + expected_result = [ + {"col": 58, "line": 1, "message": "T100 trace found: InteractiveShellEmbed used"}, + {"col": 0, "line": 1, "message": "T100 import for InteractiveShellEmbed found"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_ipdb_use_set_trace(self): + result = check_code_for_debugger_statements("import ipdb;ipdb.set_trace();") + + expected_result = [ + {"col": 12, "line": 1, "message": "T100 trace found: ipdb.set_trace used"}, + {"col": 0, "line": 1, "message": "T100 import for ipdb found"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_pdb_use_set_trace(self): + result = check_code_for_debugger_statements("import pdb;pdb.set_trace();") + + expected_result = [ + {"col": 11, "line": 1, "message": "T100 trace found: pdb.set_trace used"}, + {"col": 0, "line": 1, "message": "T100 import for pdb found"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_pdb_use_set_trace_twice(self): + result = check_code_for_debugger_statements("import pdb;pdb.set_trace() and pdb.set_trace();") + + expected_result = [ + {"col": 11, "line": 1, "message": "T100 trace found: pdb.set_trace used"}, + {"col": 31, "line": 1, "message": "T100 trace found: pdb.set_trace used"}, + {"col": 0, "line": 1, "message": "T100 import for pdb found"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_other_module_as_set_trace_and_use_it(self): + result = check_code_for_debugger_statements("from math import Max as set_trace\nset_trace()") + assert result == [] + + +class TestImportAsCases(object): + def test_import_ipdb_as(self): + result = check_code_for_debugger_statements("import math, ipdb as sif, collections") + assert result == [{"col": 0, "line": 1, "message": "T100 import for ipdb found as sif"}] + + +class TestModuleASSetTraceCases(object): + def test_import_ipdb_as_use_set_trace(self): + result = check_code_for_debugger_statements("import ipdb as sif;sif.set_trace();") + + expected_result = [ + {"col": 19, "line": 1, "message": "T100 trace found: sif.set_trace used"}, + {"col": 0, "line": 1, "message": "T100 import for ipdb found as sif"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + +class TestImportSetTraceCases(object): + def test_import_set_trace_ipdb(self): + result = check_code_for_debugger_statements("from ipdb import run, set_trace;set_trace();") + + expected_result = [ + {"col": 32, "line": 1, "message": "T100 trace found: set_trace used"}, + {"col": 0, "line": 1, "message": "T100 import for set_trace found"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_set_trace_pdb(self): + result = check_code_for_debugger_statements("from pdb import set_trace; set_trace();") + + expected_result = [ + {"col": 27, "line": 1, "message": "T100 trace found: set_trace used"}, + {"col": 0, "line": 1, "message": "T100 import for set_trace found"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_set_trace_ipdb_as_and_use(self): + result = check_code_for_debugger_statements("from ipdb import run, set_trace as sif; sif();") + + expected_result = [ + {"col": 40, "line": 1, "message": "T100 trace found: set_trace used as sif"}, + {"col": 0, "line": 1, "message": "T100 import for set_trace found as sif"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_set_trace_ipdb_as_and_use_with_conjunction_and(self): + result = check_code_for_debugger_statements("from ipdb import run, set_trace as sif; True and sif();") + + expected_result = [ + {"col": 49, "line": 1, "message": "T100 trace found: set_trace used as sif"}, + {"col": 0, "line": 1, "message": "T100 import for set_trace found as sif"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_set_trace_ipdb_as_and_use_with_conjunction_or(self): + result = check_code_for_debugger_statements("from ipdb import run, set_trace as sif; True or sif();") + + expected_result = [ + {"col": 48, "line": 1, "message": "T100 trace found: set_trace used as sif"}, + {"col": 0, "line": 1, "message": "T100 import for set_trace found as sif"}, + ] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result + + def test_import_set_trace_ipdb_as_and_use_with_conjunction_or_noqa(self): + result = check_code_for_debugger_statements("from ipdb import run, set_trace as sif; True or sif(); # noqa") + try: + assert result == [] + except AssertionError: + pass + + def test_import_set_trace_ipdb_as_and_use_with_conjunction_or_noqa_import_only(self): + result = check_code_for_debugger_statements("from ipdb import run, set_trace as sif # noqa\nTrue or sif()") + + expected_result = [{"col": 8, "line": 2, "message": "T100 trace found: set_trace used as sif"}] + + try: + assert result == expected_result + except AssertionError: + for item in expected_result: + item["col"] = 0 + + assert result == expected_result