From d1953bf2559faa1986411cecde28cfd12f66ce5c Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 17:23:16 -0700 Subject: [PATCH 01/19] add some tests for 12342 --- test-data/unit/check-inline-config.test | 701 +++++++++++++----------- 1 file changed, 372 insertions(+), 329 deletions(-) diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index c81dcac94afde..101a0403a168b 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -1,329 +1,372 @@ --- Checks for 'mypy: option' directives inside files - -[case testInlineSimple1] - -# mypy: disallow-any-generics, no-warn-no-return - -from typing import List, Optional -def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" - 20 - -[builtins fixtures/list.pyi] - -[case testInlineSimple2] - -# mypy: disallow-any-generics -# mypy: no-warn-no-return - -from typing import List, Optional -def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" - 20 - -[builtins fixtures/list.pyi] - -[case testInlineSimple3] - -# mypy: disallow-any-generics=true, warn-no-return=0 - -from typing import List, Optional -def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" - 20 - -[builtins fixtures/list.pyi] - - -[case testInlineSimple4] - -# mypy: disallow-any-generics = true, warn-no-return = 0 - -from typing import List, Optional -def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" - 20 - -[builtins fixtures/list.pyi] - -[case testInlineList] - -# mypy: disallow-any-generics,always-false="FOO,BAR" - -from typing import List - -def foo(FOO: bool, BAR: bool) -> List: # E: Missing type parameters for generic type "List" - if FOO or BAR: - 1+'lol' - return [] - -[builtins fixtures/list.pyi] - -[case testInlineInvert1] -# flags: --disallow-any-generics --allow-untyped-globals -import a -[file a.py] -# mypy: allow-any-generics, disallow-untyped-globals - -x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") - -from typing import List -def foo() -> List: - ... - -[builtins fixtures/list.pyi] - -[case testInlineInvert2] - -import a -[file a.py] -# mypy: no-always-true - -[out] -tmp/a.py:1: error: Can not invert non-boolean key always_true - -[case testInlineIncremental1] - -import a -[file a.py] -# mypy: disallow-any-generics, no-warn-no-return - -from typing import List, Optional -def foo() -> Optional[List]: - 20 - -[file a.py.2] -# mypy: no-warn-no-return - -from typing import List, Optional -def foo() -> Optional[List]: - 20 - -[file a.py.3] -from typing import List, Optional -def foo() -> Optional[List]: - 20 -[out] -tmp/a.py:4: error: Missing type parameters for generic type "List" -[out2] -[out3] -tmp/a.py:2: error: Missing return statement - -[builtins fixtures/list.pyi] - -[case testInlineIncremental2] - -# flags2: --disallow-any-generics -import a -[file a.py] -# mypy: no-warn-no-return - -from typing import Optional, List -def foo() -> Optional[List]: - 20 - -[file b.py.2] -# no changes to a.py, but flag change should cause recheck - -[out] -[out2] -tmp/a.py:4: error: Missing type parameters for generic type "List" - -[builtins fixtures/list.pyi] - -[case testInlineIncremental3] -import a, b -[file a.py] -# mypy: no-warn-no-return -from typing import Optional - -def foo() -> Optional[int]: - 20 - -[file b.py] -[file b.py.2] -# no changes to a.py and we want to make sure it isn't rechecked -[out] -[out2] -[rechecked b] - -[case testInlineError1] -# mypy: invalid-whatever -# mypy: no-warn-no-return; no-strict-optional -# mypy: always-true=FOO,BAR -# mypy: always-true="FOO,BAR -[out] -main:1: error: Unrecognized option: invalid_whatever = True -main:2: error: Unrecognized option: no_warn_no_return; no_strict_optional = True -main:3: error: Unrecognized option: bar = True -main:4: error: Unterminated quote in configuration comment - -[case testInlineError2] -# mypy: skip-file -[out] -main:1: error: Unrecognized option: skip_file = True - -[case testInlineStrict] -# mypy: strict -[out] -main:1: error: Setting "strict" not supported in inline configuration: specify it in a configuration file instead, or set individual inline flags (see "mypy -h" for the list of flags enabled in strict mode) - -[case testInlineErrorCodes] -# mypy: enable-error-code="ignore-without-code,truthy-bool" -class Foo: - pass - -foo = Foo() -if foo: ... # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) - -[case testInlineErrorCodesOverrideConfig] -# flags: --config-file tmp/mypy.ini -import foo -import tests.bar -import tests.baz -[file foo.py] -# mypy: disable-error-code="truthy-bool" -class Foo: - pass - -foo = Foo() -if foo: ... -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) - -[file tests/__init__.py] -[file tests/bar.py] -# mypy: enable-error-code="ignore-without-code" - -def foo() -> int: ... -if foo: ... # E: Function "foo" could always be true in boolean context -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) - -[file tests/baz.py] -# mypy: disable-error-code="truthy-bool" -class Foo: - pass - -foo = Foo() -if foo: ... -42 + "no" # type: ignore - -[file mypy.ini] -\[mypy] -enable_error_code = ignore-without-code, truthy-bool - -\[mypy-tests.*] -disable_error_code = ignore-without-code - -[case testIgnoreErrorsSimple] -# mypy: ignore-errors=True - -def f() -> None: - while 1(): - pass - -[case testIgnoreErrorsInImportedModule] -from m import C -c = C() -reveal_type(c.x) # N: Revealed type is "builtins.int" - -[file m.py] -# mypy: ignore-errors=True - -class C: - def f(self) -> None: - self.x = 1 - -[case testIgnoreErrorsWithLambda] -# mypy: ignore-errors=True - -def f(self, x=lambda: 1) -> None: - pass - -class C: - def f(self) -> None: - l = lambda: 1 - self.x = 1 - -[case testIgnoreErrorsWithUnsafeSuperCall_no_empty] - -from m import C - -class D(C): - def m(self) -> None: - super().m1() - super().m2() \ - # E: Call to abstract method "m2" of "C" with trivial body via super() is unsafe - super().m3() \ - # E: Call to abstract method "m3" of "C" with trivial body via super() is unsafe - super().m4() \ - # E: Call to abstract method "m4" of "C" with trivial body via super() is unsafe - super().m5() \ - # E: Call to abstract method "m5" of "C" with trivial body via super() is unsafe - super().m6() \ - # E: Call to abstract method "m6" of "C" with trivial body via super() is unsafe - super().m7() - - def m1(self) -> int: - return 0 - - def m2(self) -> int: - return 0 - - def m3(self) -> int: - return 0 - - def m4(self) -> int: - return 0 - - def m5(self) -> int: - return 0 - - def m6(self) -> int: - return 0 - -[file m.py] -# mypy: ignore-errors=True -import abc - -class C: - @abc.abstractmethod - def m1(self) -> int: - """x""" - return 0 - - @abc.abstractmethod - def m2(self) -> int: - """doc""" - - @abc.abstractmethod - def m3(self) -> int: - pass - - @abc.abstractmethod - def m4(self) -> int: ... - - @abc.abstractmethod - def m5(self) -> int: - """doc""" - ... - - @abc.abstractmethod - def m6(self) -> int: - raise NotImplementedError() - - @abc.abstractmethod - def m7(self) -> int: - raise NotImplementedError() - pass - -[builtins fixtures/exception.pyi] - -[case testInlineErrorCodesMultipleCodes] -# mypy: disable-error-code="truthy-bool, ignore-without-code" -class Foo: - pass - -foo = Foo() -if foo: ... -42 + "no" # type: ignore - - -[case testInlinePythonVersion] -# mypy: python-version=3.10 # E: python_version not supported in inline configuration +-- Checks for 'mypy: option' directives inside files + +[case testInlineSimple1] + +# mypy: disallow-any-generics, no-warn-no-return + +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" + 20 + +[builtins fixtures/list.pyi] + +[case testInlineSimple2] + +# mypy: disallow-any-generics +# mypy: no-warn-no-return + +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" + 20 + +[builtins fixtures/list.pyi] + +[case testInlineSimple3] + +# mypy: disallow-any-generics=true, warn-no-return=0 + +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" + 20 + +[builtins fixtures/list.pyi] + + +[case testInlineSimple4] + +# mypy: disallow-any-generics = true, warn-no-return = 0 + +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" + 20 + +[builtins fixtures/list.pyi] + +[case testInlineList] + +# mypy: disallow-any-generics,always-false="FOO,BAR" + +from typing import List + +def foo(FOO: bool, BAR: bool) -> List: # E: Missing type parameters for generic type "List" + if FOO or BAR: + 1+'lol' + return [] + +[builtins fixtures/list.pyi] + +[case testInlineInvert1] +# flags: --disallow-any-generics --allow-untyped-globals +import a +[file a.py] +# mypy: allow-any-generics, disallow-untyped-globals + +x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") + +from typing import List +def foo() -> List: + ... + +[builtins fixtures/list.pyi] + +[case testInlineInvert2] + +import a +[file a.py] +# mypy: no-always-true + +[out] +tmp/a.py:1: error: Can not invert non-boolean key always_true + +[case testInlineIncremental1] + +import a +[file a.py] +# mypy: disallow-any-generics, no-warn-no-return + +from typing import List, Optional +def foo() -> Optional[List]: + 20 + +[file a.py.2] +# mypy: no-warn-no-return + +from typing import List, Optional +def foo() -> Optional[List]: + 20 + +[file a.py.3] +from typing import List, Optional +def foo() -> Optional[List]: + 20 +[out] +tmp/a.py:4: error: Missing type parameters for generic type "List" +[out2] +[out3] +tmp/a.py:2: error: Missing return statement + +[builtins fixtures/list.pyi] + +[case testInlineIncremental2] + +# flags2: --disallow-any-generics +import a +[file a.py] +# mypy: no-warn-no-return + +from typing import Optional, List +def foo() -> Optional[List]: + 20 + +[file b.py.2] +# no changes to a.py, but flag change should cause recheck + +[out] +[out2] +tmp/a.py:4: error: Missing type parameters for generic type "List" + +[builtins fixtures/list.pyi] + +[case testInlineIncremental3] +import a, b +[file a.py] +# mypy: no-warn-no-return +from typing import Optional + +def foo() -> Optional[int]: + 20 + +[file b.py] +[file b.py.2] +# no changes to a.py and we want to make sure it isn't rechecked +[out] +[out2] +[rechecked b] + +[case testInlineError1] +# mypy: invalid-whatever +# mypy: no-warn-no-return; no-strict-optional +# mypy: always-true=FOO,BAR +# mypy: always-true="FOO,BAR +[out] +main:1: error: Unrecognized option: invalid_whatever = True +main:2: error: Unrecognized option: no_warn_no_return; no_strict_optional = True +main:3: error: Unrecognized option: bar = True +main:4: error: Unterminated quote in configuration comment + +[case testInlineError2] +# mypy: skip-file +[out] +main:1: error: Unrecognized option: skip_file = True + +[case testInlineStrict] +# mypy: strict +[out] +main:1: error: Setting "strict" not supported in inline configuration: specify it in a configuration file instead, or set individual inline flags (see "mypy -h" for the list of flags enabled in strict mode) + +[case testInlineErrorCodes] +# mypy: enable-error-code="ignore-without-code,truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[case testInlineErrorCodesOverrideConfig] +# flags: --config-file tmp/mypy.ini +import foo +import tests.bar +import tests.baz +[file foo.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[file tests/__init__.py] +[file tests/bar.py] +# mypy: enable-error-code="ignore-without-code" + +def foo() -> int: ... +if foo: ... # E: Function "foo" could always be true in boolean context +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[file tests/baz.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 + "no" # type: ignore + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +disable_error_code = ignore-without-code + +[case testIgnoreErrorsSimple] +# mypy: ignore-errors=True + +def f() -> None: + while 1(): + pass + +[case testIgnoreErrorsInImportedModule] +from m import C +c = C() +reveal_type(c.x) # N: Revealed type is "builtins.int" + +[file m.py] +# mypy: ignore-errors=True + +class C: + def f(self) -> None: + self.x = 1 + +[case testIgnoreErrorsWithLambda] +# mypy: ignore-errors=True + +def f(self, x=lambda: 1) -> None: + pass + +class C: + def f(self) -> None: + l = lambda: 1 + self.x = 1 + +[case testIgnoreErrorsWithUnsafeSuperCall_no_empty] + +from m import C + +class D(C): + def m(self) -> None: + super().m1() + super().m2() \ + # E: Call to abstract method "m2" of "C" with trivial body via super() is unsafe + super().m3() \ + # E: Call to abstract method "m3" of "C" with trivial body via super() is unsafe + super().m4() \ + # E: Call to abstract method "m4" of "C" with trivial body via super() is unsafe + super().m5() \ + # E: Call to abstract method "m5" of "C" with trivial body via super() is unsafe + super().m6() \ + # E: Call to abstract method "m6" of "C" with trivial body via super() is unsafe + super().m7() + + def m1(self) -> int: + return 0 + + def m2(self) -> int: + return 0 + + def m3(self) -> int: + return 0 + + def m4(self) -> int: + return 0 + + def m5(self) -> int: + return 0 + + def m6(self) -> int: + return 0 + +[file m.py] +# mypy: ignore-errors=True +import abc + +class C: + @abc.abstractmethod + def m1(self) -> int: + """x""" + return 0 + + @abc.abstractmethod + def m2(self) -> int: + """doc""" + + @abc.abstractmethod + def m3(self) -> int: + pass + + @abc.abstractmethod + def m4(self) -> int: ... + + @abc.abstractmethod + def m5(self) -> int: + """doc""" + ... + + @abc.abstractmethod + def m6(self) -> int: + raise NotImplementedError() + + @abc.abstractmethod + def m7(self) -> int: + raise NotImplementedError() + pass + +[builtins fixtures/exception.pyi] + +[case testInlineErrorCodesMultipleCodes] +# mypy: disable-error-code="truthy-bool, ignore-without-code" +class Foo: + pass + +foo = Foo() +if foo: ... +42 + "no" # type: ignore + + +[case testInlinePythonVersion] +# mypy: python-version=3.10 # E: python_version not supported in inline configuration + +[case testInline12342base] +# mypy: disable_error_code = name-defined +a + +[case testInline12342] +# https://github.com/python/mypy/issues/12342 +# mypy: AMONGUS +# mypy: sussy=baka +# mypy: disable-error-code=name-defined python-verion="3.6" +# mypy: disable-error-code=name-defined +# mypy: disable-error-code = name-defined +# mypy: python-verion="3.6" +a +[out] +main:2: error: Unrecognized option: amongus = True +main:3: error: Unrecognized option: sussy = baka +main:4: error: disable_error_code: Invalid error code(s): name-defined python-verion=3.6 +??? +??? +main:7: error: Unrecognized option: python_verion = 3.6 +main:8: error: Name "a" is not defined + +[case testInline12342Two] +# flags: --config-file tmp/mypy.ini --show-error-codes +a +[file mypy.ini] +\[mypy] +disable_error_code = name-defined +python_verion="3.6" + +[case testInline12342Three] +# flags: --config-file tmp/mypy.ini --show-error-codes +a # E: Name "a" is not defined [name-defined] +[file mypy.ini] +\[mypy] +# It's unclear to me if something in quotes is, like, *supposed to* work. +# I'd also like to record the fact that it has a warning, +# > tmp/mypy.ini: [mypy]: disable_error_code: Invalid error code(s): "name-defined" +# but there doesn't seem to be a way. +disable_error_code = "name-defined" +python_verion="3.6" + From 98a127349ab728ebf18085bb3414af650142f7cf Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 18:43:00 -0700 Subject: [PATCH 02/19] manual debugging --- mypy/build.py | 1 + mypy/config_parser.py | 8 +++-- test-data/unit/check-inline-config.test | 45 ++++++------------------- 3 files changed, 16 insertions(+), 38 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 355ba861385e4..c2f51a640703c 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2209,6 +2209,7 @@ def parse_file(self, *, temporary: bool = False) -> None: def parse_inline_configuration(self, source: str) -> None: """Check for inline mypy: options directive and parse them.""" flags = get_mypy_comments(source) + print(f"{flags=}") if flags: changes, config_errors = parse_mypy_comments(flags, self.options) self.options = self.options.apply_changes(changes) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 0e033471d2e92..1b0db7489a9c1 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -639,7 +639,7 @@ def parse_mypy_comments( Returns a dictionary of options to be applied and a list of error messages generated. """ - + print("args", args) errors: list[tuple[int, str]] = [] sections = {} @@ -649,7 +649,7 @@ def parse_mypy_comments( # method is to create a config parser. parser = configparser.RawConfigParser() options, parse_errors = mypy_comments_to_config_map(line, template) - + print("options", options) if "python_version" in options: errors.append((lineno, "python_version not supported in inline configuration")) del options["python_version"] @@ -667,6 +667,7 @@ def set_strict_flags() -> None: new_sections, reports = parse_section( "", template, set_strict_flags, parser["dummy"], ini_config_types, stderr=stderr ) + print(f"{new_sections=}") errors.extend((lineno, x) for x in stderr.getvalue().strip().split("\n") if x) if reports: errors.append((lineno, "Reports not supported in inline configuration")) @@ -681,7 +682,8 @@ def set_strict_flags() -> None: ) sections.update(new_sections) - + print(f"{sections_updated=}") + print(f"{sections=}") return sections, errors diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index 101a0403a168b..57a0c51529e5b 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -328,45 +328,20 @@ if foo: ... [case testInlinePythonVersion] # mypy: python-version=3.10 # E: python_version not supported in inline configuration -[case testInline12342base] -# mypy: disable_error_code = name-defined -a - [case testInline12342] # https://github.com/python/mypy/issues/12342 -# mypy: AMONGUS -# mypy: sussy=baka -# mypy: disable-error-code=name-defined python-verion="3.6" # mypy: disable-error-code=name-defined -# mypy: disable-error-code = name-defined -# mypy: python-verion="3.6" a -[out] -main:2: error: Unrecognized option: amongus = True -main:3: error: Unrecognized option: sussy = baka -main:4: error: disable_error_code: Invalid error code(s): name-defined python-verion=3.6 -??? -??? -main:7: error: Unrecognized option: python_verion = 3.6 -main:8: error: Name "a" is not defined - [case testInline12342Two] -# flags: --config-file tmp/mypy.ini --show-error-codes +# mypy: disable-error-code=name-defined +# mypy: AMONGUS a -[file mypy.ini] -\[mypy] -disable_error_code = name-defined -python_verion="3.6" - +[out] +main:2: error: Unrecognized option: amongus = True [case testInline12342Three] -# flags: --config-file tmp/mypy.ini --show-error-codes -a # E: Name "a" is not defined [name-defined] -[file mypy.ini] -\[mypy] -# It's unclear to me if something in quotes is, like, *supposed to* work. -# I'd also like to record the fact that it has a warning, -# > tmp/mypy.ini: [mypy]: disable_error_code: Invalid error code(s): "name-defined" -# but there doesn't seem to be a way. -disable_error_code = "name-defined" -python_verion="3.6" - +# mypy: AMONGUS +# mypy: disable-error-code=name-defined +a +[out] +main:1: error: Unrecognized option: amongus = True +asdfasdfasdf \ No newline at end of file From 1ee4784c0e9b45992fdbba6479e3d879bef0d4e1 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 18:58:18 -0700 Subject: [PATCH 03/19] hone in on problem --- mypy/config_parser.py | 4 ++-- test-data/unit/check-inline-config.test | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 1b0db7489a9c1..92615b25193e1 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -663,7 +663,7 @@ def parse_mypy_comments( def set_strict_flags() -> None: nonlocal strict_found strict_found = True - + # HERE! vvv new_sections, reports = parse_section( "", template, set_strict_flags, parser["dummy"], ini_config_types, stderr=stderr ) @@ -682,7 +682,7 @@ def set_strict_flags() -> None: ) sections.update(new_sections) - print(f"{sections_updated=}") + print(f"sections_updated={sections}") print(f"{sections=}") return sections, errors diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index 57a0c51529e5b..0bfa42169d679 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -344,4 +344,13 @@ main:2: error: Unrecognized option: amongus = True a [out] main:1: error: Unrecognized option: amongus = True -asdfasdfasdf \ No newline at end of file +asdfasdfasdf +[case testInline12342Four] +# mypy: warn_unused_ignores +x = 1 # type: ignore # E: Unused "type: ignore" comment +[case testInline12342Five] +# mypy: warn_unused_ignores +# mypy: AMONGUS +x = 1 # type: ignore # E: Unused "type: ignore" comment +[out] +main:2: error: Unrecognized option: amongus = True \ No newline at end of file From 61b226ef30e60fc3df5d92ea195b7ccd736dba07 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 19:00:59 -0700 Subject: [PATCH 04/19] hone in on problem --- test-data/unit/check-inline-config.test | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index 0bfa42169d679..cde5c83b95aae 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -353,4 +353,8 @@ x = 1 # type: ignore # E: Unused "type: ignore" comment # mypy: AMONGUS x = 1 # type: ignore # E: Unused "type: ignore" comment [out] -main:2: error: Unrecognized option: amongus = True \ No newline at end of file +main:2: error: Unrecognized option: amongus = True +[case 12342] +# mypy: disable-error-code=name-defined +# mypy: warn_unused_ignores +x = 1 # type: ignore # E: Unused "type: ignore" comment From 14eca24e5fa0be7505f792799fefe37de416d217 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 19:41:40 -0700 Subject: [PATCH 05/19] solve the problem --- mypy/config_parser.py | 9 ++++-- test-data/unit/check-inline-config.test | 38 +++++++++++++++++-------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 92615b25193e1..82ab30e05b987 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -663,7 +663,7 @@ def parse_mypy_comments( def set_strict_flags() -> None: nonlocal strict_found strict_found = True - # HERE! vvv + new_sections, reports = parse_section( "", template, set_strict_flags, parser["dummy"], ini_config_types, stderr=stderr ) @@ -680,7 +680,12 @@ def set_strict_flags() -> None: '(see "mypy -h" for the list of flags enabled in strict mode)', ) ) - + # Because this is currently special-cased + # (the new_sections for an inline config without these two options always includes + # {'disable_error_code': [], 'enable_error_code': []}, which overwrites the old ones), + # we have to manipulate them specially. + new_sections['enable_error_code'] = list(set(new_sections['enable_error_code'] + sections.get('enable_error_code', []))) + new_sections['disable_error_code'] = list(set(new_sections['disable_error_code'] + sections.get('disable_error_code', []))) sections.update(new_sections) print(f"sections_updated={sections}") print(f"{sections=}") diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index cde5c83b95aae..c808950c807a2 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -324,37 +324,51 @@ foo = Foo() if foo: ... 42 + "no" # type: ignore - [case testInlinePythonVersion] # mypy: python-version=3.10 # E: python_version not supported in inline configuration -[case testInline12342] -# https://github.com/python/mypy/issues/12342 +[case testInlineErrorCodesArentRuinedByOthersBaseCase] # mypy: disable-error-code=name-defined a -[case testInline12342Two] + +[case testInlineErrorCodesArentRuinedByOthersInvalid] # mypy: disable-error-code=name-defined # mypy: AMONGUS a [out] main:2: error: Unrecognized option: amongus = True -[case testInline12342Three] + +[case testInlineErrorCodesArentRuinedByOthersInvalidBefore] # mypy: AMONGUS # mypy: disable-error-code=name-defined a [out] main:1: error: Unrecognized option: amongus = True -asdfasdfasdf -[case testInline12342Four] + +[case testInlineErrorCodesArentRuinedByOthersSe] +# mypy: disable-error-code=name-defined +# mypy: strict-equality +def is_magic(x: bytes) -> bool: + y + return x == 'magic' # E: Unsupported left operand type for == ("bytes") + +[case testInlineConfigErrorCodesOffAndOn] +# mypy: disable-error-code=name-defined +# mypy: enable-error-code=name-defined +a # E: Name "a" is not defined + +[case testInlineConfigErrorCodesOnAndOff] +# mypy: enable-error-code=name-defined +# mypy: disable-error-code=name-defined +a # E: Name "a" is not defined + +[case testInlineConfigBaseCaseWui] # mypy: warn_unused_ignores x = 1 # type: ignore # E: Unused "type: ignore" comment -[case testInline12342Five] + +[case testInlineConfigIsntRuinedByOthersInvalidWui] # mypy: warn_unused_ignores # mypy: AMONGUS x = 1 # type: ignore # E: Unused "type: ignore" comment [out] main:2: error: Unrecognized option: amongus = True -[case 12342] -# mypy: disable-error-code=name-defined -# mypy: warn_unused_ignores -x = 1 # type: ignore # E: Unused "type: ignore" comment From 45566a43580512eabc9c4f523aa86b62b385cef1 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 19:46:00 -0700 Subject: [PATCH 06/19] cleanup --- mypy/build.py | 1 - mypy/config_parser.py | 17 ++++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index c2f51a640703c..355ba861385e4 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2209,7 +2209,6 @@ def parse_file(self, *, temporary: bool = False) -> None: def parse_inline_configuration(self, source: str) -> None: """Check for inline mypy: options directive and parse them.""" flags = get_mypy_comments(source) - print(f"{flags=}") if flags: changes, config_errors = parse_mypy_comments(flags, self.options) self.options = self.options.apply_changes(changes) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 82ab30e05b987..bfa216923c358 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -639,7 +639,6 @@ def parse_mypy_comments( Returns a dictionary of options to be applied and a list of error messages generated. """ - print("args", args) errors: list[tuple[int, str]] = [] sections = {} @@ -649,7 +648,6 @@ def parse_mypy_comments( # method is to create a config parser. parser = configparser.RawConfigParser() options, parse_errors = mypy_comments_to_config_map(line, template) - print("options", options) if "python_version" in options: errors.append((lineno, "python_version not supported in inline configuration")) del options["python_version"] @@ -667,7 +665,6 @@ def set_strict_flags() -> None: new_sections, reports = parse_section( "", template, set_strict_flags, parser["dummy"], ini_config_types, stderr=stderr ) - print(f"{new_sections=}") errors.extend((lineno, x) for x in stderr.getvalue().strip().split("\n") if x) if reports: errors.append((lineno, "Reports not supported in inline configuration")) @@ -681,14 +678,16 @@ def set_strict_flags() -> None: ) ) # Because this is currently special-cased - # (the new_sections for an inline config without these two options always includes - # {'disable_error_code': [], 'enable_error_code': []}, which overwrites the old ones), + # (the new_sections for an inline config *always* includes 'disable_error_code' and + # 'enable_error_code' fields, usually empty, which overwrite the old ones), # we have to manipulate them specially. - new_sections['enable_error_code'] = list(set(new_sections['enable_error_code'] + sections.get('enable_error_code', []))) - new_sections['disable_error_code'] = list(set(new_sections['disable_error_code'] + sections.get('disable_error_code', []))) + new_sections["enable_error_code"] = list( + set(new_sections["enable_error_code"] + sections.get("enable_error_code", [])) + ) + new_sections["disable_error_code"] = list( + set(new_sections["disable_error_code"] + sections.get("disable_error_code", [])) + ) sections.update(new_sections) - print(f"sections_updated={sections}") - print(f"{sections=}") return sections, errors From 3f51ebb19582417e56aab94f0763ee93c46d3f07 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 19:58:41 -0700 Subject: [PATCH 07/19] add another test, for good luck --- test-data/unit/check-inline-config.test | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index c808950c807a2..ac3e32f3c7afc 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -362,6 +362,16 @@ a # E: Name "a" is not defined # mypy: disable-error-code=name-defined a # E: Name "a" is not defined +[case testConfigFileErrorCodesOnAndOff] +# flags: --config-file tmp/mypy.ini +import foo +[file foo.py] +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code +disable_error_code = ignore-without-code + [case testInlineConfigBaseCaseWui] # mypy: warn_unused_ignores x = 1 # type: ignore # E: Unused "type: ignore" comment From 0253c0519b1aa1ea8ed63a26a41394af28e6a789 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 20:25:52 -0700 Subject: [PATCH 08/19] oh, right, I need types or whatever --- mypy/config_parser.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index bfa216923c358..dfdb40069529d 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -533,7 +533,7 @@ def parse_section( else: continue ct = type(dv) - v: Any = None + v = None try: if ct is bool: if isinstance(section, dict): @@ -681,6 +681,10 @@ def set_strict_flags() -> None: # (the new_sections for an inline config *always* includes 'disable_error_code' and # 'enable_error_code' fields, usually empty, which overwrite the old ones), # we have to manipulate them specially. + if "enable_error_code" in new_sections: + assert isinstance(new_sections["enable_error_code"], list) + if "disable_error_code" in new_sections: + assert isinstance(new_sections["disable_error_code"], list) new_sections["enable_error_code"] = list( set(new_sections["enable_error_code"] + sections.get("enable_error_code", [])) ) From 000f25fd8cf83ae62afb3a19fe4c40fe817dae9c Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 20:53:03 -0700 Subject: [PATCH 09/19] solve the typing problem --- mypy/config_parser.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index dfdb40069529d..a5f9154f611c8 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -16,7 +16,7 @@ import tomli as tomllib from collections.abc import Mapping, MutableMapping, Sequence -from typing import Any, Callable, Final, TextIO, Union +from typing import Any, Callable, Final, TextIO, TypedDict, Union from typing_extensions import TypeAlias as _TypeAlias from mypy import defaults @@ -640,7 +640,7 @@ def parse_mypy_comments( generated. """ errors: list[tuple[int, str]] = [] - sections = {} + sections: dict[str, object] = {"enable_error_code": [], "disable_error_code": []} for lineno, line in args: # In order to easily match the behavior for bools, we abuse configparser. @@ -681,16 +681,12 @@ def set_strict_flags() -> None: # (the new_sections for an inline config *always* includes 'disable_error_code' and # 'enable_error_code' fields, usually empty, which overwrite the old ones), # we have to manipulate them specially. - if "enable_error_code" in new_sections: - assert isinstance(new_sections["enable_error_code"], list) - if "disable_error_code" in new_sections: - assert isinstance(new_sections["disable_error_code"], list) - new_sections["enable_error_code"] = list( - set(new_sections["enable_error_code"] + sections.get("enable_error_code", [])) - ) - new_sections["disable_error_code"] = list( - set(new_sections["disable_error_code"] + sections.get("disable_error_code", [])) - ) + assert isinstance(neec:=new_sections.get("enable_error_code", []), list) + assert isinstance(eec:=sections.get("enable_error_code", []), list) + assert isinstance(ndec:=new_sections.get("disable_error_code", []), list) + assert isinstance(dec:=new_sections.get("disable_error_code", []), list) + new_sections["enable_error_code"] = list(set(neec + eec)) + new_sections["disable_error_code"] = list(set(ndec + dec)) sections.update(new_sections) return sections, errors From cecae56414d38800a96a8b694874ce70a4d0cf40 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Sun, 11 May 2025 20:55:51 -0700 Subject: [PATCH 10/19] final(?) touches --- mypy/config_parser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index a5f9154f611c8..4b8c375405e6d 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -16,7 +16,7 @@ import tomli as tomllib from collections.abc import Mapping, MutableMapping, Sequence -from typing import Any, Callable, Final, TextIO, TypedDict, Union +from typing import Any, Callable, Final, TextIO, Union from typing_extensions import TypeAlias as _TypeAlias from mypy import defaults @@ -681,6 +681,7 @@ def set_strict_flags() -> None: # (the new_sections for an inline config *always* includes 'disable_error_code' and # 'enable_error_code' fields, usually empty, which overwrite the old ones), # we have to manipulate them specially. + # This could use a refactor, but so could the whole subsystem. assert isinstance(neec:=new_sections.get("enable_error_code", []), list) assert isinstance(eec:=sections.get("enable_error_code", []), list) assert isinstance(ndec:=new_sections.get("disable_error_code", []), list) From b6a12df6521cbfa431d6c1a1023b6fdb741f2af4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 03:58:12 +0000 Subject: [PATCH 11/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/config_parser.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 4b8c375405e6d..635600492c797 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -682,10 +682,10 @@ def set_strict_flags() -> None: # 'enable_error_code' fields, usually empty, which overwrite the old ones), # we have to manipulate them specially. # This could use a refactor, but so could the whole subsystem. - assert isinstance(neec:=new_sections.get("enable_error_code", []), list) - assert isinstance(eec:=sections.get("enable_error_code", []), list) - assert isinstance(ndec:=new_sections.get("disable_error_code", []), list) - assert isinstance(dec:=new_sections.get("disable_error_code", []), list) + assert isinstance(neec := new_sections.get("enable_error_code", []), list) + assert isinstance(eec := sections.get("enable_error_code", []), list) + assert isinstance(ndec := new_sections.get("disable_error_code", []), list) + assert isinstance(dec := new_sections.get("disable_error_code", []), list) new_sections["enable_error_code"] = list(set(neec + eec)) new_sections["disable_error_code"] = list(set(ndec + dec)) sections.update(new_sections) From 8a772092bb756d5cf148148b48e597fa0262f66a Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Mon, 12 May 2025 02:01:15 -0700 Subject: [PATCH 12/19] fix the type problem more reliably --- mypy/config_parser.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 635600492c797..f8d332266e882 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -682,12 +682,18 @@ def set_strict_flags() -> None: # 'enable_error_code' fields, usually empty, which overwrite the old ones), # we have to manipulate them specially. # This could use a refactor, but so could the whole subsystem. - assert isinstance(neec := new_sections.get("enable_error_code", []), list) - assert isinstance(eec := sections.get("enable_error_code", []), list) - assert isinstance(ndec := new_sections.get("disable_error_code", []), list) - assert isinstance(dec := new_sections.get("disable_error_code", []), list) - new_sections["enable_error_code"] = list(set(neec + eec)) - new_sections["disable_error_code"] = list(set(ndec + dec)) + if ( + "enable_error_code" in new_sections + and isinstance(neec := new_sections["enable_error_code"], list) + and isinstance(eec := sections.get("enable_error_code", []), list) + ): + new_sections["enable_error_code"] = list(set(neec + eec)) + if ( + "disable_error_code" in new_sections + and isinstance(ndec := new_sections["enable_error_code"], list) + and isinstance(dec := sections.get("enable_error_code", []), list) + ): + new_sections["enable_error_code"] = list(set(ndec + dec)) sections.update(new_sections) return sections, errors From 4bac4bd2ca2d2337d455a6f64176d8e4bd6f61dd Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Mon, 12 May 2025 03:29:05 -0700 Subject: [PATCH 13/19] was this a... loadbearing any?? --- mypy/config_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index f8d332266e882..ff56b9ba2615a 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -533,7 +533,7 @@ def parse_section( else: continue ct = type(dv) - v = None + v: Any = None try: if ct is bool: if isinstance(section, dict): From e44f888a20298b1e3805da8c1cafc572c0c41880 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Mon, 12 May 2025 10:20:08 -0700 Subject: [PATCH 14/19] fix the problem again --- mypy/config_parser.py | 13 ++-- mypy/errorcodes.py | 3 + mypy/options.py | 3 +- test-data/unit/check-inline-config.test | 93 +++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index ff56b9ba2615a..7060f0de65c19 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -665,6 +665,7 @@ def set_strict_flags() -> None: new_sections, reports = parse_section( "", template, set_strict_flags, parser["dummy"], ini_config_types, stderr=stderr ) + print("new_sections", new_sections) errors.extend((lineno, x) for x in stderr.getvalue().strip().split("\n") if x) if reports: errors.append((lineno, "Reports not supported in inline configuration")) @@ -687,14 +688,18 @@ def set_strict_flags() -> None: and isinstance(neec := new_sections["enable_error_code"], list) and isinstance(eec := sections.get("enable_error_code", []), list) ): - new_sections["enable_error_code"] = list(set(neec + eec)) + print("bing", neec, eec) + new_sections["enable_error_code"] = sorted(set(neec + eec)) if ( "disable_error_code" in new_sections - and isinstance(ndec := new_sections["enable_error_code"], list) - and isinstance(dec := sections.get("enable_error_code", []), list) + and isinstance(ndec := new_sections["disable_error_code"], list) + and isinstance(dec := sections.get("disable_error_code", []), list) ): - new_sections["enable_error_code"] = list(set(ndec + dec)) + print("bong", ndec, dec) + new_sections["disable_error_code"] = sorted(set(ndec + dec)) sections.update(new_sections) + print("sections", sections) + print("sections final", sections) return sections, errors diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 8f650aa30605f..485251408496d 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -37,6 +37,9 @@ def __init__( def __str__(self) -> str: return f"" + def __repr__(self) -> str: + """This doesn't fulfill the goals of repr but it's better than the default view.""" + return self.code def __eq__(self, other: object) -> bool: if not isinstance(other, ErrorCode): return False diff --git a/mypy/options.py b/mypy/options.py index 52afd27211ed0..bfabf8d517a13 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -492,6 +492,7 @@ def apply_changes(self, changes: dict[str, object]) -> Options: # Similar to global codes enabling overrides disabling, so we start from latter. new_options.disabled_error_codes = self.disabled_error_codes.copy() new_options.enabled_error_codes = self.enabled_error_codes.copy() + print(f"{(new_options.disabled_error_codes, new_options.disable_error_code, new_options.enabled_error_codes, new_options.enable_error_code)=}") for code_str in new_options.disable_error_code: code = error_codes[code_str] new_options.disabled_error_codes.add(code) @@ -500,7 +501,7 @@ def apply_changes(self, changes: dict[str, object]) -> Options: code = error_codes[code_str] new_options.enabled_error_codes.add(code) new_options.disabled_error_codes.discard(code) - + print(f"{(new_options.disabled_error_codes, new_options.enabled_error_codes)=}") return new_options def compare_stable(self, other_snapshot: dict[str, object]) -> bool: diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index ac3e32f3c7afc..e69b80d54da8c 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -211,6 +211,99 @@ enable_error_code = ignore-without-code, truthy-bool \[mypy-tests.*] disable_error_code = ignore-without-code +[case testInlineErrorCodesOverrideConfigSmall] +# flags: --config-file tmp/mypy.ini +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +disable_error_code = ignore-without-code + +[case testInlineErrorCodesOverrideConfigSmall2] +# flags: --config-file tmp/mypy.ini +import tests.bar +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore +[file tests/bar.py] +# mypy: enable-error-code="ignore-without-code" + +def foo() -> int: ... +if foo: ... # E: Function "foo" could always be true in boolean context +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +disable_error_code = ignore-without-code + + +[case testInlineErrorCodesOverrideConfigSmallBackward] +# flags: --config-file tmp/mypy.ini +import tests.bar +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) +[file tests/bar.py] +# mypy: disable-error-code="ignore-without-code" +42 + "no" # type: ignore + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +enable_error_code = ignore-without-code + +[case testInlineOverrideConfig] +# flags: --config-file tmp/mypy.ini +import foo +import tests.bar +import tests.baz +[file foo.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 # type: ignore # E: Unused "type: ignore" comment + +[file tests/__init__.py] +[file tests/bar.py] +# mypy: warn_unused_ignores + +def foo() -> int: ... +if foo: ... # E: Function "foo" could always be true in boolean context +42 # type: ignore # E: Unused "type: ignore" comment + +[file tests/baz.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 # type: ignore + +[file mypy.ini] +\[mypy] +warn_unused_ignores = True + +\[mypy-tests.*] +warn_unused_ignores = False + + [case testIgnoreErrorsSimple] # mypy: ignore-errors=True From 95a8223069396c3f9afa78cc4f92be2ab436f19d Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Mon, 12 May 2025 10:22:37 -0700 Subject: [PATCH 15/19] clean up print statements --- mypy/config_parser.py | 5 ----- mypy/options.py | 2 -- 2 files changed, 7 deletions(-) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 7060f0de65c19..8ada374f1dfaa 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -665,7 +665,6 @@ def set_strict_flags() -> None: new_sections, reports = parse_section( "", template, set_strict_flags, parser["dummy"], ini_config_types, stderr=stderr ) - print("new_sections", new_sections) errors.extend((lineno, x) for x in stderr.getvalue().strip().split("\n") if x) if reports: errors.append((lineno, "Reports not supported in inline configuration")) @@ -688,18 +687,14 @@ def set_strict_flags() -> None: and isinstance(neec := new_sections["enable_error_code"], list) and isinstance(eec := sections.get("enable_error_code", []), list) ): - print("bing", neec, eec) new_sections["enable_error_code"] = sorted(set(neec + eec)) if ( "disable_error_code" in new_sections and isinstance(ndec := new_sections["disable_error_code"], list) and isinstance(dec := sections.get("disable_error_code", []), list) ): - print("bong", ndec, dec) new_sections["disable_error_code"] = sorted(set(ndec + dec)) sections.update(new_sections) - print("sections", sections) - print("sections final", sections) return sections, errors diff --git a/mypy/options.py b/mypy/options.py index bfabf8d517a13..eb059d1b4dc8f 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -492,7 +492,6 @@ def apply_changes(self, changes: dict[str, object]) -> Options: # Similar to global codes enabling overrides disabling, so we start from latter. new_options.disabled_error_codes = self.disabled_error_codes.copy() new_options.enabled_error_codes = self.enabled_error_codes.copy() - print(f"{(new_options.disabled_error_codes, new_options.disable_error_code, new_options.enabled_error_codes, new_options.enable_error_code)=}") for code_str in new_options.disable_error_code: code = error_codes[code_str] new_options.disabled_error_codes.add(code) @@ -501,7 +500,6 @@ def apply_changes(self, changes: dict[str, object]) -> Options: code = error_codes[code_str] new_options.enabled_error_codes.add(code) new_options.disabled_error_codes.discard(code) - print(f"{(new_options.disabled_error_codes, new_options.enabled_error_codes)=}") return new_options def compare_stable(self, other_snapshot: dict[str, object]) -> bool: From 6c0b38bbd530f7b505e1c553366b9601279daa09 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Mon, 12 May 2025 10:25:04 -0700 Subject: [PATCH 16/19] unix line-endings in the tests... --- test-data/unit/check-inline-config.test | 954 ++++++++++++------------ 1 file changed, 477 insertions(+), 477 deletions(-) diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index e69b80d54da8c..048dd8522847b 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -1,477 +1,477 @@ --- Checks for 'mypy: option' directives inside files - -[case testInlineSimple1] - -# mypy: disallow-any-generics, no-warn-no-return - -from typing import List, Optional -def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" - 20 - -[builtins fixtures/list.pyi] - -[case testInlineSimple2] - -# mypy: disallow-any-generics -# mypy: no-warn-no-return - -from typing import List, Optional -def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" - 20 - -[builtins fixtures/list.pyi] - -[case testInlineSimple3] - -# mypy: disallow-any-generics=true, warn-no-return=0 - -from typing import List, Optional -def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" - 20 - -[builtins fixtures/list.pyi] - - -[case testInlineSimple4] - -# mypy: disallow-any-generics = true, warn-no-return = 0 - -from typing import List, Optional -def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" - 20 - -[builtins fixtures/list.pyi] - -[case testInlineList] - -# mypy: disallow-any-generics,always-false="FOO,BAR" - -from typing import List - -def foo(FOO: bool, BAR: bool) -> List: # E: Missing type parameters for generic type "List" - if FOO or BAR: - 1+'lol' - return [] - -[builtins fixtures/list.pyi] - -[case testInlineInvert1] -# flags: --disallow-any-generics --allow-untyped-globals -import a -[file a.py] -# mypy: allow-any-generics, disallow-untyped-globals - -x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") - -from typing import List -def foo() -> List: - ... - -[builtins fixtures/list.pyi] - -[case testInlineInvert2] - -import a -[file a.py] -# mypy: no-always-true - -[out] -tmp/a.py:1: error: Can not invert non-boolean key always_true - -[case testInlineIncremental1] - -import a -[file a.py] -# mypy: disallow-any-generics, no-warn-no-return - -from typing import List, Optional -def foo() -> Optional[List]: - 20 - -[file a.py.2] -# mypy: no-warn-no-return - -from typing import List, Optional -def foo() -> Optional[List]: - 20 - -[file a.py.3] -from typing import List, Optional -def foo() -> Optional[List]: - 20 -[out] -tmp/a.py:4: error: Missing type parameters for generic type "List" -[out2] -[out3] -tmp/a.py:2: error: Missing return statement - -[builtins fixtures/list.pyi] - -[case testInlineIncremental2] - -# flags2: --disallow-any-generics -import a -[file a.py] -# mypy: no-warn-no-return - -from typing import Optional, List -def foo() -> Optional[List]: - 20 - -[file b.py.2] -# no changes to a.py, but flag change should cause recheck - -[out] -[out2] -tmp/a.py:4: error: Missing type parameters for generic type "List" - -[builtins fixtures/list.pyi] - -[case testInlineIncremental3] -import a, b -[file a.py] -# mypy: no-warn-no-return -from typing import Optional - -def foo() -> Optional[int]: - 20 - -[file b.py] -[file b.py.2] -# no changes to a.py and we want to make sure it isn't rechecked -[out] -[out2] -[rechecked b] - -[case testInlineError1] -# mypy: invalid-whatever -# mypy: no-warn-no-return; no-strict-optional -# mypy: always-true=FOO,BAR -# mypy: always-true="FOO,BAR -[out] -main:1: error: Unrecognized option: invalid_whatever = True -main:2: error: Unrecognized option: no_warn_no_return; no_strict_optional = True -main:3: error: Unrecognized option: bar = True -main:4: error: Unterminated quote in configuration comment - -[case testInlineError2] -# mypy: skip-file -[out] -main:1: error: Unrecognized option: skip_file = True - -[case testInlineStrict] -# mypy: strict -[out] -main:1: error: Setting "strict" not supported in inline configuration: specify it in a configuration file instead, or set individual inline flags (see "mypy -h" for the list of flags enabled in strict mode) - -[case testInlineErrorCodes] -# mypy: enable-error-code="ignore-without-code,truthy-bool" -class Foo: - pass - -foo = Foo() -if foo: ... # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) - -[case testInlineErrorCodesOverrideConfig] -# flags: --config-file tmp/mypy.ini -import foo -import tests.bar -import tests.baz -[file foo.py] -# mypy: disable-error-code="truthy-bool" -class Foo: - pass - -foo = Foo() -if foo: ... -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) - -[file tests/__init__.py] -[file tests/bar.py] -# mypy: enable-error-code="ignore-without-code" - -def foo() -> int: ... -if foo: ... # E: Function "foo" could always be true in boolean context -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) - -[file tests/baz.py] -# mypy: disable-error-code="truthy-bool" -class Foo: - pass - -foo = Foo() -if foo: ... -42 + "no" # type: ignore - -[file mypy.ini] -\[mypy] -enable_error_code = ignore-without-code, truthy-bool - -\[mypy-tests.*] -disable_error_code = ignore-without-code - -[case testInlineErrorCodesOverrideConfigSmall] -# flags: --config-file tmp/mypy.ini -import tests.baz -[file tests/__init__.py] -[file tests/baz.py] -42 + "no" # type: ignore - -[file mypy.ini] -\[mypy] -enable_error_code = ignore-without-code, truthy-bool - -\[mypy-tests.*] -disable_error_code = ignore-without-code - -[case testInlineErrorCodesOverrideConfigSmall2] -# flags: --config-file tmp/mypy.ini -import tests.bar -import tests.baz -[file tests/__init__.py] -[file tests/baz.py] -42 + "no" # type: ignore -[file tests/bar.py] -# mypy: enable-error-code="ignore-without-code" - -def foo() -> int: ... -if foo: ... # E: Function "foo" could always be true in boolean context -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) - -[file mypy.ini] -\[mypy] -enable_error_code = ignore-without-code, truthy-bool - -\[mypy-tests.*] -disable_error_code = ignore-without-code - - -[case testInlineErrorCodesOverrideConfigSmallBackward] -# flags: --config-file tmp/mypy.ini -import tests.bar -import tests.baz -[file tests/__init__.py] -[file tests/baz.py] -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) -[file tests/bar.py] -# mypy: disable-error-code="ignore-without-code" -42 + "no" # type: ignore - -[file mypy.ini] -\[mypy] -enable_error_code = ignore-without-code, truthy-bool - -\[mypy-tests.*] -enable_error_code = ignore-without-code - -[case testInlineOverrideConfig] -# flags: --config-file tmp/mypy.ini -import foo -import tests.bar -import tests.baz -[file foo.py] -# mypy: disable-error-code="truthy-bool" -class Foo: - pass - -foo = Foo() -if foo: ... -42 # type: ignore # E: Unused "type: ignore" comment - -[file tests/__init__.py] -[file tests/bar.py] -# mypy: warn_unused_ignores - -def foo() -> int: ... -if foo: ... # E: Function "foo" could always be true in boolean context -42 # type: ignore # E: Unused "type: ignore" comment - -[file tests/baz.py] -# mypy: disable-error-code="truthy-bool" -class Foo: - pass - -foo = Foo() -if foo: ... -42 # type: ignore - -[file mypy.ini] -\[mypy] -warn_unused_ignores = True - -\[mypy-tests.*] -warn_unused_ignores = False - - -[case testIgnoreErrorsSimple] -# mypy: ignore-errors=True - -def f() -> None: - while 1(): - pass - -[case testIgnoreErrorsInImportedModule] -from m import C -c = C() -reveal_type(c.x) # N: Revealed type is "builtins.int" - -[file m.py] -# mypy: ignore-errors=True - -class C: - def f(self) -> None: - self.x = 1 - -[case testIgnoreErrorsWithLambda] -# mypy: ignore-errors=True - -def f(self, x=lambda: 1) -> None: - pass - -class C: - def f(self) -> None: - l = lambda: 1 - self.x = 1 - -[case testIgnoreErrorsWithUnsafeSuperCall_no_empty] - -from m import C - -class D(C): - def m(self) -> None: - super().m1() - super().m2() \ - # E: Call to abstract method "m2" of "C" with trivial body via super() is unsafe - super().m3() \ - # E: Call to abstract method "m3" of "C" with trivial body via super() is unsafe - super().m4() \ - # E: Call to abstract method "m4" of "C" with trivial body via super() is unsafe - super().m5() \ - # E: Call to abstract method "m5" of "C" with trivial body via super() is unsafe - super().m6() \ - # E: Call to abstract method "m6" of "C" with trivial body via super() is unsafe - super().m7() - - def m1(self) -> int: - return 0 - - def m2(self) -> int: - return 0 - - def m3(self) -> int: - return 0 - - def m4(self) -> int: - return 0 - - def m5(self) -> int: - return 0 - - def m6(self) -> int: - return 0 - -[file m.py] -# mypy: ignore-errors=True -import abc - -class C: - @abc.abstractmethod - def m1(self) -> int: - """x""" - return 0 - - @abc.abstractmethod - def m2(self) -> int: - """doc""" - - @abc.abstractmethod - def m3(self) -> int: - pass - - @abc.abstractmethod - def m4(self) -> int: ... - - @abc.abstractmethod - def m5(self) -> int: - """doc""" - ... - - @abc.abstractmethod - def m6(self) -> int: - raise NotImplementedError() - - @abc.abstractmethod - def m7(self) -> int: - raise NotImplementedError() - pass - -[builtins fixtures/exception.pyi] - -[case testInlineErrorCodesMultipleCodes] -# mypy: disable-error-code="truthy-bool, ignore-without-code" -class Foo: - pass - -foo = Foo() -if foo: ... -42 + "no" # type: ignore - -[case testInlinePythonVersion] -# mypy: python-version=3.10 # E: python_version not supported in inline configuration - -[case testInlineErrorCodesArentRuinedByOthersBaseCase] -# mypy: disable-error-code=name-defined -a - -[case testInlineErrorCodesArentRuinedByOthersInvalid] -# mypy: disable-error-code=name-defined -# mypy: AMONGUS -a -[out] -main:2: error: Unrecognized option: amongus = True - -[case testInlineErrorCodesArentRuinedByOthersInvalidBefore] -# mypy: AMONGUS -# mypy: disable-error-code=name-defined -a -[out] -main:1: error: Unrecognized option: amongus = True - -[case testInlineErrorCodesArentRuinedByOthersSe] -# mypy: disable-error-code=name-defined -# mypy: strict-equality -def is_magic(x: bytes) -> bool: - y - return x == 'magic' # E: Unsupported left operand type for == ("bytes") - -[case testInlineConfigErrorCodesOffAndOn] -# mypy: disable-error-code=name-defined -# mypy: enable-error-code=name-defined -a # E: Name "a" is not defined - -[case testInlineConfigErrorCodesOnAndOff] -# mypy: enable-error-code=name-defined -# mypy: disable-error-code=name-defined -a # E: Name "a" is not defined - -[case testConfigFileErrorCodesOnAndOff] -# flags: --config-file tmp/mypy.ini -import foo -[file foo.py] -42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) -[file mypy.ini] -\[mypy] -enable_error_code = ignore-without-code -disable_error_code = ignore-without-code - -[case testInlineConfigBaseCaseWui] -# mypy: warn_unused_ignores -x = 1 # type: ignore # E: Unused "type: ignore" comment - -[case testInlineConfigIsntRuinedByOthersInvalidWui] -# mypy: warn_unused_ignores -# mypy: AMONGUS -x = 1 # type: ignore # E: Unused "type: ignore" comment -[out] -main:2: error: Unrecognized option: amongus = True +-- Checks for 'mypy: option' directives inside files + +[case testInlineSimple1] + +# mypy: disallow-any-generics, no-warn-no-return + +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" + 20 + +[builtins fixtures/list.pyi] + +[case testInlineSimple2] + +# mypy: disallow-any-generics +# mypy: no-warn-no-return + +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" + 20 + +[builtins fixtures/list.pyi] + +[case testInlineSimple3] + +# mypy: disallow-any-generics=true, warn-no-return=0 + +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" + 20 + +[builtins fixtures/list.pyi] + + +[case testInlineSimple4] + +# mypy: disallow-any-generics = true, warn-no-return = 0 + +from typing import List, Optional +def foo() -> Optional[List]: # E: Missing type parameters for generic type "List" + 20 + +[builtins fixtures/list.pyi] + +[case testInlineList] + +# mypy: disallow-any-generics,always-false="FOO,BAR" + +from typing import List + +def foo(FOO: bool, BAR: bool) -> List: # E: Missing type parameters for generic type "List" + if FOO or BAR: + 1+'lol' + return [] + +[builtins fixtures/list.pyi] + +[case testInlineInvert1] +# flags: --disallow-any-generics --allow-untyped-globals +import a +[file a.py] +# mypy: allow-any-generics, disallow-untyped-globals + +x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") + +from typing import List +def foo() -> List: + ... + +[builtins fixtures/list.pyi] + +[case testInlineInvert2] + +import a +[file a.py] +# mypy: no-always-true + +[out] +tmp/a.py:1: error: Can not invert non-boolean key always_true + +[case testInlineIncremental1] + +import a +[file a.py] +# mypy: disallow-any-generics, no-warn-no-return + +from typing import List, Optional +def foo() -> Optional[List]: + 20 + +[file a.py.2] +# mypy: no-warn-no-return + +from typing import List, Optional +def foo() -> Optional[List]: + 20 + +[file a.py.3] +from typing import List, Optional +def foo() -> Optional[List]: + 20 +[out] +tmp/a.py:4: error: Missing type parameters for generic type "List" +[out2] +[out3] +tmp/a.py:2: error: Missing return statement + +[builtins fixtures/list.pyi] + +[case testInlineIncremental2] + +# flags2: --disallow-any-generics +import a +[file a.py] +# mypy: no-warn-no-return + +from typing import Optional, List +def foo() -> Optional[List]: + 20 + +[file b.py.2] +# no changes to a.py, but flag change should cause recheck + +[out] +[out2] +tmp/a.py:4: error: Missing type parameters for generic type "List" + +[builtins fixtures/list.pyi] + +[case testInlineIncremental3] +import a, b +[file a.py] +# mypy: no-warn-no-return +from typing import Optional + +def foo() -> Optional[int]: + 20 + +[file b.py] +[file b.py.2] +# no changes to a.py and we want to make sure it isn't rechecked +[out] +[out2] +[rechecked b] + +[case testInlineError1] +# mypy: invalid-whatever +# mypy: no-warn-no-return; no-strict-optional +# mypy: always-true=FOO,BAR +# mypy: always-true="FOO,BAR +[out] +main:1: error: Unrecognized option: invalid_whatever = True +main:2: error: Unrecognized option: no_warn_no_return; no_strict_optional = True +main:3: error: Unrecognized option: bar = True +main:4: error: Unterminated quote in configuration comment + +[case testInlineError2] +# mypy: skip-file +[out] +main:1: error: Unrecognized option: skip_file = True + +[case testInlineStrict] +# mypy: strict +[out] +main:1: error: Setting "strict" not supported in inline configuration: specify it in a configuration file instead, or set individual inline flags (see "mypy -h" for the list of flags enabled in strict mode) + +[case testInlineErrorCodes] +# mypy: enable-error-code="ignore-without-code,truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... # E: "__main__.foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[case testInlineErrorCodesOverrideConfig] +# flags: --config-file tmp/mypy.ini +import foo +import tests.bar +import tests.baz +[file foo.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[file tests/__init__.py] +[file tests/bar.py] +# mypy: enable-error-code="ignore-without-code" + +def foo() -> int: ... +if foo: ... # E: Function "foo" could always be true in boolean context +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[file tests/baz.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 + "no" # type: ignore + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +disable_error_code = ignore-without-code + +[case testInlineErrorCodesOverrideConfigSmall] +# flags: --config-file tmp/mypy.ini +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +disable_error_code = ignore-without-code + +[case testInlineErrorCodesOverrideConfigSmall2] +# flags: --config-file tmp/mypy.ini +import tests.bar +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore +[file tests/bar.py] +# mypy: enable-error-code="ignore-without-code" + +def foo() -> int: ... +if foo: ... # E: Function "foo" could always be true in boolean context +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +disable_error_code = ignore-without-code + + +[case testInlineErrorCodesOverrideConfigSmallBackward] +# flags: --config-file tmp/mypy.ini +import tests.bar +import tests.baz +[file tests/__init__.py] +[file tests/baz.py] +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) +[file tests/bar.py] +# mypy: disable-error-code="ignore-without-code" +42 + "no" # type: ignore + +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code, truthy-bool + +\[mypy-tests.*] +enable_error_code = ignore-without-code + +[case testInlineOverrideConfig] +# flags: --config-file tmp/mypy.ini +import foo +import tests.bar +import tests.baz +[file foo.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 # type: ignore # E: Unused "type: ignore" comment + +[file tests/__init__.py] +[file tests/bar.py] +# mypy: warn_unused_ignores + +def foo() -> int: ... +if foo: ... # E: Function "foo" could always be true in boolean context +42 # type: ignore # E: Unused "type: ignore" comment + +[file tests/baz.py] +# mypy: disable-error-code="truthy-bool" +class Foo: + pass + +foo = Foo() +if foo: ... +42 # type: ignore + +[file mypy.ini] +\[mypy] +warn_unused_ignores = True + +\[mypy-tests.*] +warn_unused_ignores = False + + +[case testIgnoreErrorsSimple] +# mypy: ignore-errors=True + +def f() -> None: + while 1(): + pass + +[case testIgnoreErrorsInImportedModule] +from m import C +c = C() +reveal_type(c.x) # N: Revealed type is "builtins.int" + +[file m.py] +# mypy: ignore-errors=True + +class C: + def f(self) -> None: + self.x = 1 + +[case testIgnoreErrorsWithLambda] +# mypy: ignore-errors=True + +def f(self, x=lambda: 1) -> None: + pass + +class C: + def f(self) -> None: + l = lambda: 1 + self.x = 1 + +[case testIgnoreErrorsWithUnsafeSuperCall_no_empty] + +from m import C + +class D(C): + def m(self) -> None: + super().m1() + super().m2() \ + # E: Call to abstract method "m2" of "C" with trivial body via super() is unsafe + super().m3() \ + # E: Call to abstract method "m3" of "C" with trivial body via super() is unsafe + super().m4() \ + # E: Call to abstract method "m4" of "C" with trivial body via super() is unsafe + super().m5() \ + # E: Call to abstract method "m5" of "C" with trivial body via super() is unsafe + super().m6() \ + # E: Call to abstract method "m6" of "C" with trivial body via super() is unsafe + super().m7() + + def m1(self) -> int: + return 0 + + def m2(self) -> int: + return 0 + + def m3(self) -> int: + return 0 + + def m4(self) -> int: + return 0 + + def m5(self) -> int: + return 0 + + def m6(self) -> int: + return 0 + +[file m.py] +# mypy: ignore-errors=True +import abc + +class C: + @abc.abstractmethod + def m1(self) -> int: + """x""" + return 0 + + @abc.abstractmethod + def m2(self) -> int: + """doc""" + + @abc.abstractmethod + def m3(self) -> int: + pass + + @abc.abstractmethod + def m4(self) -> int: ... + + @abc.abstractmethod + def m5(self) -> int: + """doc""" + ... + + @abc.abstractmethod + def m6(self) -> int: + raise NotImplementedError() + + @abc.abstractmethod + def m7(self) -> int: + raise NotImplementedError() + pass + +[builtins fixtures/exception.pyi] + +[case testInlineErrorCodesMultipleCodes] +# mypy: disable-error-code="truthy-bool, ignore-without-code" +class Foo: + pass + +foo = Foo() +if foo: ... +42 + "no" # type: ignore + +[case testInlinePythonVersion] +# mypy: python-version=3.10 # E: python_version not supported in inline configuration + +[case testInlineErrorCodesArentRuinedByOthersBaseCase] +# mypy: disable-error-code=name-defined +a + +[case testInlineErrorCodesArentRuinedByOthersInvalid] +# mypy: disable-error-code=name-defined +# mypy: AMONGUS +a +[out] +main:2: error: Unrecognized option: amongus = True + +[case testInlineErrorCodesArentRuinedByOthersInvalidBefore] +# mypy: AMONGUS +# mypy: disable-error-code=name-defined +a +[out] +main:1: error: Unrecognized option: amongus = True + +[case testInlineErrorCodesArentRuinedByOthersSe] +# mypy: disable-error-code=name-defined +# mypy: strict-equality +def is_magic(x: bytes) -> bool: + y + return x == 'magic' # E: Unsupported left operand type for == ("bytes") + +[case testInlineConfigErrorCodesOffAndOn] +# mypy: disable-error-code=name-defined +# mypy: enable-error-code=name-defined +a # E: Name "a" is not defined + +[case testInlineConfigErrorCodesOnAndOff] +# mypy: enable-error-code=name-defined +# mypy: disable-error-code=name-defined +a # E: Name "a" is not defined + +[case testConfigFileErrorCodesOnAndOff] +# flags: --config-file tmp/mypy.ini +import foo +[file foo.py] +42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) +[file mypy.ini] +\[mypy] +enable_error_code = ignore-without-code +disable_error_code = ignore-without-code + +[case testInlineConfigBaseCaseWui] +# mypy: warn_unused_ignores +x = 1 # type: ignore # E: Unused "type: ignore" comment + +[case testInlineConfigIsntRuinedByOthersInvalidWui] +# mypy: warn_unused_ignores +# mypy: AMONGUS +x = 1 # type: ignore # E: Unused "type: ignore" comment +[out] +main:2: error: Unrecognized option: amongus = True From b737e434ee0f64738847c5a0b5472d73303e825e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 17:26:44 +0000 Subject: [PATCH 17/19] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/errorcodes.py | 1 + test-data/unit/check-inline-config.test | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 485251408496d..b02663904a33c 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -40,6 +40,7 @@ def __str__(self) -> str: def __repr__(self) -> str: """This doesn't fulfill the goals of repr but it's better than the default view.""" return self.code + def __eq__(self, other: object) -> bool: if not isinstance(other, ErrorCode): return False diff --git a/test-data/unit/check-inline-config.test b/test-data/unit/check-inline-config.test index 048dd8522847b..0dbee3a70e6cd 100644 --- a/test-data/unit/check-inline-config.test +++ b/test-data/unit/check-inline-config.test @@ -256,7 +256,7 @@ import tests.baz 42 + "no" # type: ignore # E: "type: ignore" comment without error code (consider "type: ignore[operator]" instead) [file tests/bar.py] # mypy: disable-error-code="ignore-without-code" -42 + "no" # type: ignore +42 + "no" # type: ignore [file mypy.ini] \[mypy] From 2bf9c7b8bb64200ba0f310253af2d3408b008750 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Thu, 3 Jul 2025 21:25:18 -0700 Subject: [PATCH 18/19] Update mypy/errorcodes.py as suggested Co-authored-by: Stanislav Terliakov <50529348+sterliakov@users.noreply.github.com> --- mypy/errorcodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index b02663904a33c..0527af772c289 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -39,7 +39,7 @@ def __str__(self) -> str: def __repr__(self) -> str: """This doesn't fulfill the goals of repr but it's better than the default view.""" - return self.code + return f"" def __eq__(self, other: object) -> bool: if not isinstance(other, ErrorCode): From a279c46dd69fff9fe45af60a871aadf12ee7cd14 Mon Sep 17 00:00:00 2001 From: wyattscarpenter Date: Fri, 4 Jul 2025 01:39:16 -0400 Subject: [PATCH 19/19] Update mypy/errorcodes.py: add category there --- mypy/errorcodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 0527af772c289..9581ce64c89be 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -39,7 +39,7 @@ def __str__(self) -> str: def __repr__(self) -> str: """This doesn't fulfill the goals of repr but it's better than the default view.""" - return f"" + return f"" def __eq__(self, other: object) -> bool: if not isinstance(other, ErrorCode):