From 2095bdd2293b5567ea1dbeb485cab7da13a2e2fe Mon Sep 17 00:00:00 2001 From: Perry Vargas Date: Sat, 1 Jan 2022 15:31:47 -0800 Subject: [PATCH] Allow the same special cases for B950 as E501 (#176) (#213) * Allow the same special cases for B950 as E501 (176) * Undo version bump --- .gitignore | 1 + README.rst | 15 ++++++++++++++- bugbear.py | 23 +++++++++++++++++++++++ tests/b950.py | 14 ++++++++++++++ tests/test_bugbear.py | 10 +++++++++- 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index fc8539e..1dc5815 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ var/ *.egg-info/ .installed.cfg *.egg +venv/ # PyInstaller # Usually these files are written by a python script from a template diff --git a/README.rst b/README.rst index 6d94ba7..7ad3874 100644 --- a/README.rst +++ b/README.rst @@ -173,7 +173,15 @@ significantly violate the line length, you will receive a message that states what the actual limit is. This is inspired by Raymond Hettinger's `"Beyond PEP 8" talk `_ and highway patrol not stopping you if you drive < 5mph too fast. Disable -E501 to avoid duplicate warnings. +E501 to avoid duplicate warnings. Like E501, this error ignores long shebangs +on the first line and urls or paths that are on their own line:: + + #! long shebang ignored + + # https://some-super-long-domain-name.com/with/some/very/long/paths + url = ( + "https://some-super-long-domain-name.com/with/some/very/long/paths" + ) How to enable opinionated warnings @@ -237,6 +245,11 @@ MIT Change Log ---------- +21.12.0 +~~~~~~~~~~ + +* B950: Add same special cases as E501 (#213) + 21.11.29 ~~~~~~~~~~ diff --git a/bugbear.py b/bugbear.py index eab2b53..78c2ca3 100644 --- a/bugbear.py +++ b/bugbear.py @@ -67,8 +67,31 @@ def gen_line_based_checks(self): The following simple checks are based on the raw lines, not the AST. """ for lineno, line in enumerate(self.lines, start=1): + # Special case: ignore long shebang (following pycodestyle). + if lineno == 1 and line.startswith("#!"): + continue + length = len(line) - 1 if length > 1.1 * self.max_line_length: + # Special case long URLS and paths to follow pycodestyle. + # Would use the `pycodestyle.maximum_line_length` directly but + # need to supply it arguments that are not available so chose + # to replicate instead. + chunks = line.split() + + is_line_comment_url_path = len(chunks) == 2 and chunks[0] == "#" + + just_long_url_path = len(chunks) == 1 + + num_leading_whitespaces = len(line) - len(chunks[-1]) + too_many_leading_white_spaces = ( + num_leading_whitespaces >= self.max_line_length - 7 + ) + + skip = is_line_comment_url_path or just_long_url_path + if skip and not too_many_leading_white_spaces: + continue + yield B950(lineno, length, vars=(length, self.max_line_length)) @classmethod diff --git a/tests/b950.py b/tests/b950.py index 1939105..942e50c 100644 --- a/tests/b950.py +++ b/tests/b950.py @@ -1,3 +1,4 @@ +#! Ignore long shebang fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo # Assumes the default allowed line length of 79 "line is fine" @@ -5,3 +6,16 @@ " line is still fine " " line is no longer fine by any measures, yup" "line is fine again" + +# Ensure URL/path on it's own line is fine +"https://foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo.com" +"NOT OK: https://foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo.com" +# https://foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo.com +# NOT OK: https://foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo.com +# +#: Okay +# This +# almost_empty_line_too_long + +# This +# almost_empty_line_too_long diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index 319a508..d1af992 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -318,7 +318,15 @@ def test_b950(self): filename = Path(__file__).absolute().parent / "b950.py" bbc = BugBearChecker(filename=str(filename)) errors = list(bbc.run()) - self.assertEqual(errors, self.errors(B950(6, 92, vars=(92, 79)))) + self.assertEqual( + errors, + self.errors( + B950(7, 92, vars=(92, 79)), + B950(12, 103, vars=(103, 79)), + B950(14, 103, vars=(103, 79)), + B950(21, 97, vars=(97, 79)), + ), + ) def test_selfclean_bugbear(self): filename = Path(__file__).absolute().parent.parent / "bugbear.py"