From 8f7ef5ad27993f3782ff3744ba5457cbcba0f84a Mon Sep 17 00:00:00 2001 From: sweeneyde Date: Sat, 24 Apr 2021 23:09:04 -0400 Subject: [PATCH 1/4] Require suggestions be 50% similar --- Lib/test/test_exceptions.py | 55 +++++++++++++++++++++++++++++++++++++ Python/suggestions.c | 5 +++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index b3c00b04a37621..8222d3e8429d01 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1507,6 +1507,61 @@ def f(): self.assertNotIn("somethingverywronghehe", err.getvalue()) + def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self): + vvv = mom = w = id = python = None + + with self.subTest(name="b"): + try: + b + except NameError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + self.assertNotIn("you mean", err.getvalue()) + self.assertNotIn("vvv", err.getvalue()) + self.assertNotIn("mom", err.getvalue()) + self.assertNotIn("'id'", err.getvalue()) + self.assertNotIn("'w'", err.getvalue()) + self.assertNotIn("'python'", err.getvalue()) + + with self.subTest(name="v"): + try: + v + except NameError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + self.assertNotIn("you mean", err.getvalue()) + self.assertNotIn("vvv", err.getvalue()) + self.assertNotIn("mom", err.getvalue()) + self.assertNotIn("'id'", err.getvalue()) + self.assertNotIn("'w'", err.getvalue()) + self.assertNotIn("'python'", err.getvalue()) + + with self.subTest(name="m"): + try: + m + except NameError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + self.assertNotIn("you mean", err.getvalue()) + self.assertNotIn("vvv", err.getvalue()) + self.assertNotIn("mom", err.getvalue()) + self.assertNotIn("'id'", err.getvalue()) + self.assertNotIn("'w'", err.getvalue()) + self.assertNotIn("'python'", err.getvalue()) + + with self.subTest(name="py"): + try: + py + except NameError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + self.assertNotIn("you mean", err.getvalue()) + self.assertNotIn("vvv", err.getvalue()) + self.assertNotIn("mom", err.getvalue()) + self.assertNotIn("'id'", err.getvalue()) + self.assertNotIn("'w'", err.getvalue()) + self.assertNotIn("'python'", err.getvalue()) + def test_name_error_suggestions_do_not_trigger_for_too_many_locals(self): def f(): # Mutating locals() is unreliable, so we need to do it by hand diff --git a/Python/suggestions.c b/Python/suggestions.c index d4e9dc22bbc7b9..2fd6714e847874 100644 --- a/Python/suggestions.c +++ b/Python/suggestions.c @@ -102,7 +102,10 @@ calculate_suggestions(PyObject *dir, if (current_distance == -1) { return NULL; } - if (current_distance == 0 || current_distance > MAX_DISTANCE) { + if (current_distance == 0 || + current_distance > MAX_DISTANCE || + current_distance * 2 > name_size) + { continue; } if (!suggestion || current_distance < suggestion_distance) { From bd1ad7a743f11d670227b3ad129afe790311dd4c Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 25 Apr 2021 03:18:43 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst b/Misc/NEWS.d/next/Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst new file mode 100644 index 00000000000000..0f55224f4b3e4f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst @@ -0,0 +1 @@ +``NameError`` suggestions now require at least 50% similarity, eliminating false positives for short variable names. \ No newline at end of file From 2d6233ea06b13b57ad9bcb5f4c00af11ecb46a35 Mon Sep 17 00:00:00 2001 From: sweeneyde Date: Sat, 24 Apr 2021 23:48:57 -0400 Subject: [PATCH 3/4] =?UTF-8?q?Revert=20"=F0=9F=93=9C=F0=9F=A4=96=20Added?= =?UTF-8?q?=20by=20blurb=5Fit."?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bd1ad7a743f11d670227b3ad129afe790311dd4c. --- .../Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst b/Misc/NEWS.d/next/Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst deleted file mode 100644 index 0f55224f4b3e4f..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2021-04-25-03-18-41.bpo-38530.dPwLC2.rst +++ /dev/null @@ -1 +0,0 @@ -``NameError`` suggestions now require at least 50% similarity, eliminating false positives for short variable names. \ No newline at end of file From c88fd85b50f91f3808fe1d05499b6ccd39032716 Mon Sep 17 00:00:00 2001 From: sweeneyde Date: Sat, 24 Apr 2021 23:53:23 -0400 Subject: [PATCH 4/4] Add similar test for attribute errors --- Lib/test/test_exceptions.py | 67 ++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 8222d3e8429d01..590935cb6cd624 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1508,7 +1508,7 @@ def f(): self.assertNotIn("somethingverywronghehe", err.getvalue()) def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self): - vvv = mom = w = id = python = None + vvv = mom = w = id = pytho = None with self.subTest(name="b"): try: @@ -1521,7 +1521,7 @@ def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self): self.assertNotIn("mom", err.getvalue()) self.assertNotIn("'id'", err.getvalue()) self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'python'", err.getvalue()) + self.assertNotIn("'pytho'", err.getvalue()) with self.subTest(name="v"): try: @@ -1534,7 +1534,7 @@ def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self): self.assertNotIn("mom", err.getvalue()) self.assertNotIn("'id'", err.getvalue()) self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'python'", err.getvalue()) + self.assertNotIn("'pytho'", err.getvalue()) with self.subTest(name="m"): try: @@ -1547,7 +1547,7 @@ def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self): self.assertNotIn("mom", err.getvalue()) self.assertNotIn("'id'", err.getvalue()) self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'python'", err.getvalue()) + self.assertNotIn("'pytho'", err.getvalue()) with self.subTest(name="py"): try: @@ -1560,7 +1560,7 @@ def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self): self.assertNotIn("mom", err.getvalue()) self.assertNotIn("'id'", err.getvalue()) self.assertNotIn("'w'", err.getvalue()) - self.assertNotIn("'python'", err.getvalue()) + self.assertNotIn("'pytho'", err.getvalue()) def test_name_error_suggestions_do_not_trigger_for_too_many_locals(self): def f(): @@ -1716,6 +1716,63 @@ class A: self.assertNotIn("blech", err.getvalue()) + def test_getattr_error_bad_suggestions_do_not_trigger_for_small_names(self): + class MyClass: + vvv = mom = w = id = pytho = None + + with self.subTest(name="b"): + try: + MyClass.b + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + self.assertNotIn("you mean", err.getvalue()) + self.assertNotIn("vvv", err.getvalue()) + self.assertNotIn("mom", err.getvalue()) + self.assertNotIn("'id'", err.getvalue()) + self.assertNotIn("'w'", err.getvalue()) + self.assertNotIn("'pytho'", err.getvalue()) + + with self.subTest(name="v"): + try: + MyClass.v + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + self.assertNotIn("you mean", err.getvalue()) + self.assertNotIn("vvv", err.getvalue()) + self.assertNotIn("mom", err.getvalue()) + self.assertNotIn("'id'", err.getvalue()) + self.assertNotIn("'w'", err.getvalue()) + self.assertNotIn("'pytho'", err.getvalue()) + + with self.subTest(name="m"): + try: + MyClass.m + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + self.assertNotIn("you mean", err.getvalue()) + self.assertNotIn("vvv", err.getvalue()) + self.assertNotIn("mom", err.getvalue()) + self.assertNotIn("'id'", err.getvalue()) + self.assertNotIn("'w'", err.getvalue()) + self.assertNotIn("'pytho'", err.getvalue()) + + with self.subTest(name="py"): + try: + MyClass.py + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + self.assertNotIn("you mean", err.getvalue()) + self.assertNotIn("vvv", err.getvalue()) + self.assertNotIn("mom", err.getvalue()) + self.assertNotIn("'id'", err.getvalue()) + self.assertNotIn("'w'", err.getvalue()) + self.assertNotIn("'pytho'", err.getvalue()) + + def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): class A: blech = None