diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 6e5d6f6e38..a11bfe1cb5 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -6,7 +6,7 @@ on: env: # Also change CACHE_VERSION in the other workflows - CACHE_VERSION: 26 + CACHE_VERSION: 27 DEFAULT_PYTHON: "3.10" jobs: diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index a73594185c..1b928c3338 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -8,7 +8,7 @@ on: pull_request: ~ env: - CACHE_VERSION: 26 + CACHE_VERSION: 27 DEFAULT_PYTHON: "3.10" PRE_COMMIT_CACHE: ~/.cache/pre-commit diff --git a/.github/workflows/primer-test.yaml b/.github/workflows/primer-test.yaml index 0f81b7ee1e..e1403a1d3d 100644 --- a/.github/workflows/primer-test.yaml +++ b/.github/workflows/primer-test.yaml @@ -13,7 +13,7 @@ on: - ".github/workflows/primer-test.yaml" env: - CACHE_VERSION: 26 + CACHE_VERSION: 27 concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/primer_comment.yaml b/.github/workflows/primer_comment.yaml index 02677d4102..5379311713 100644 --- a/.github/workflows/primer_comment.yaml +++ b/.github/workflows/primer_comment.yaml @@ -14,7 +14,7 @@ on: env: # This needs to be the SAME as in the Main and PR job - CACHE_VERSION: 26 + CACHE_VERSION: 27 permissions: contents: read diff --git a/.github/workflows/primer_run_main.yaml b/.github/workflows/primer_run_main.yaml index 1214057f07..56f08f6777 100644 --- a/.github/workflows/primer_run_main.yaml +++ b/.github/workflows/primer_run_main.yaml @@ -16,7 +16,7 @@ concurrency: env: # This needs to be the SAME as in the PR and comment job - CACHE_VERSION: 26 + CACHE_VERSION: 27 jobs: run-primer: diff --git a/.github/workflows/primer_run_pr.yaml b/.github/workflows/primer_run_pr.yaml index 50c6454b10..7fa785ce21 100644 --- a/.github/workflows/primer_run_pr.yaml +++ b/.github/workflows/primer_run_pr.yaml @@ -25,7 +25,7 @@ concurrency: env: # This needs to be the SAME as in the Main and comment job - CACHE_VERSION: 26 + CACHE_VERSION: 27 jobs: run-primer: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index e07f6a903e..13a720fa50 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -10,7 +10,7 @@ on: - doc/data/messages/** env: - CACHE_VERSION: 26 + CACHE_VERSION: 27 jobs: tests-linux: diff --git a/doc/whatsnew/2/2.15/index.rst b/doc/whatsnew/2/2.15/index.rst index b70ca4dba3..0f1bb04238 100644 --- a/doc/whatsnew/2/2.15/index.rst +++ b/doc/whatsnew/2/2.15/index.rst @@ -29,6 +29,30 @@ Marc Byrne became a maintainer, welcome to the team ! .. towncrier release notes start +What's new in Pylint 2.15.3? +---------------------------- +Release date: 2022-09-19 + + +- Fixed a crash in the ``unhashable-member`` checker when using a ``lambda`` as a dict key. + + Closes #7453 (`#7453 `_) +- Fix a crash in the ``modified-iterating-dict`` checker involving instance attributes. + + Closes #7461 (`#7461 `_) +- ``invalid-class-object`` does not crash anymore when ``__class__`` is assigned alongside another variable. + + Closes #7467 (`#7467 `_) +- Fix false positive for ``global-variable-not-assigned`` when a global variable is re-assigned via an ``ImportFrom`` node. + + Closes #4809 (`#4809 `_) +- Fix false positive for ``undefined-loop-variable`` in ``for-else`` loops that use a function + having a return type annotation of ``NoReturn`` or ``Never``. + + Closes #7311 (`#7311 `_) +- ``--help-msg`` now accepts a comma-separated list of message IDs again. + + Closes #7471 (`#7471 `_) What's new in Pylint 2.15.2? ---------------------------- diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index fe000c1ada..93f237ecb0 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -9,7 +9,7 @@ from __future__ import annotations -__version__ = "2.15.2" +__version__ = "2.15.3" def get_numversion_from_version(v: str) -> tuple[int, int, int]: diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py index 5ae558822f..059baa0583 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -1558,7 +1558,18 @@ def visit_assignattr(self, node: nodes.AssignAttr) -> None: def _check_invalid_class_object(self, node: nodes.AssignAttr) -> None: if not node.attrname == "__class__": return - inferred = safe_infer(node.parent.value) + if isinstance(node.parent, nodes.Tuple): + class_index = -1 + for i, elt in enumerate(node.parent.elts): + if hasattr(elt, "attrname") and elt.attrname == "__class__": + class_index = i + if class_index == -1: + # This should not happen because we checked that the node name + # is '__class__' earlier, but let's not be too confident here + return # pragma: no cover + inferred = safe_infer(node.parent.parent.value.elts[class_index]) + else: + inferred = safe_infer(node.parent.value) if ( isinstance(inferred, nodes.ClassDef) or inferred is astroid.Uninferable diff --git a/pylint/checkers/modified_iterating_checker.py b/pylint/checkers/modified_iterating_checker.py index 62e887a954..d32bb56ef0 100644 --- a/pylint/checkers/modified_iterating_checker.py +++ b/pylint/checkers/modified_iterating_checker.py @@ -128,7 +128,7 @@ def _is_node_assigns_subscript_name(node: nodes.NodeNG) -> bool: ) def _modified_iterating_list_cond( - self, node: nodes.NodeNG, iter_obj: nodes.NodeNG + self, node: nodes.NodeNG, iter_obj: nodes.Name | nodes.Attribute ) -> bool: if not self._is_node_expr_that_calls_attribute_name(node): return False @@ -141,7 +141,7 @@ def _modified_iterating_list_cond( ) def _modified_iterating_dict_cond( - self, node: nodes.NodeNG, iter_obj: nodes.NodeNG + self, node: nodes.NodeNG, iter_obj: nodes.Name | nodes.Attribute ) -> bool: if not self._is_node_assigns_subscript_name(node): return False @@ -159,10 +159,14 @@ def _modified_iterating_dict_cond( return False if infer_val != utils.safe_infer(iter_obj): return False - return node.targets[0].value.name == iter_obj.name + if isinstance(iter_obj, nodes.Attribute): + iter_obj_name = iter_obj.attrname + else: + iter_obj_name = iter_obj.name + return node.targets[0].value.name == iter_obj_name def _modified_iterating_set_cond( - self, node: nodes.NodeNG, iter_obj: nodes.NodeNG + self, node: nodes.NodeNG, iter_obj: nodes.Name | nodes.Attribute ) -> bool: if not self._is_node_expr_that_calls_attribute_name(node): return False diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 68452db0e4..d00b8a6537 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -1942,6 +1942,8 @@ def is_hashable(node: nodes.NodeNG) -> bool: for inferred in node.infer(): if inferred is astroid.Uninferable: return True + if not hasattr(inferred, "igetattr"): + return True hash_fn = next(inferred.igetattr("__hash__")) if hash_fn.parent is inferred: return True diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 0644b4d07d..632f75447a 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -19,7 +19,7 @@ from typing import TYPE_CHECKING, Any, NamedTuple import astroid -from astroid import extract_node, nodes +from astroid import bases, extract_node, nodes from astroid.typing import InferenceResult from pylint.checkers import BaseChecker, utils @@ -27,7 +27,12 @@ in_type_checking_block, is_postponed_evaluation_enabled, ) -from pylint.constants import PY39_PLUS, TYPING_TYPE_CHECKS_GUARDS +from pylint.constants import ( + PY39_PLUS, + TYPING_NEVER, + TYPING_NORETURN, + TYPING_TYPE_CHECKS_GUARDS, +) from pylint.interfaces import CONTROL_FLOW, HIGH, INFERENCE, INFERENCE_FAILURE from pylint.typing import MessageDefinitionTuple @@ -1311,7 +1316,8 @@ def visit_global(self, node: nodes.Global) -> None: assign_nodes = [] not_defined_locally_by_import = not any( - isinstance(local, nodes.Import) for local in locals_.get(name, ()) + isinstance(local, (nodes.Import, nodes.ImportFrom)) + for local in locals_.get(name, ()) ) if ( not utils.is_reassigned_after_current(node, name) @@ -2244,13 +2250,35 @@ def _loopvar_name(self, node: astroid.Name) -> None: if not isinstance(assign, nodes.For): self.add_message("undefined-loop-variable", args=node.name, node=node) return - if any( - isinstance( + for else_stmt in assign.orelse: + if isinstance( else_stmt, (nodes.Return, nodes.Raise, nodes.Break, nodes.Continue) - ) - for else_stmt in assign.orelse - ): - return + ): + return + # TODO: 2.16: Consider using RefactoringChecker._is_function_def_never_returning + if isinstance(else_stmt, nodes.Expr) and isinstance( + else_stmt.value, nodes.Call + ): + inferred_func = utils.safe_infer(else_stmt.value.func) + if ( + isinstance(inferred_func, nodes.FunctionDef) + and inferred_func.returns + ): + inferred_return = utils.safe_infer(inferred_func.returns) + if isinstance( + inferred_return, nodes.FunctionDef + ) and inferred_return.qname() in { + *TYPING_NORETURN, + *TYPING_NEVER, + "typing._SpecialForm", + }: + return + # typing_extensions.NoReturn returns a _SpecialForm + if ( + isinstance(inferred_return, bases.Instance) + and inferred_return.qname() == "typing._SpecialForm" + ): + return maybe_walrus = utils.get_node_first_ancestor_of_type(node, nodes.NamedExpr) if maybe_walrus: diff --git a/pylint/config/callback_actions.py b/pylint/config/callback_actions.py index 5c27bca35b..242629a88e 100644 --- a/pylint/config/callback_actions.py +++ b/pylint/config/callback_actions.py @@ -158,7 +158,11 @@ def __call__( option_string: str | None = "--help-msg", ) -> None: assert isinstance(values, (list, tuple)) - self.run.linter.msgs_store.help_message(values) + values_to_print: list[str] = [] + for msg in values: + assert isinstance(msg, str) + values_to_print += utils._check_csv(msg) + self.run.linter.msgs_store.help_message(values_to_print) sys.exit(0) diff --git a/pylint/constants.py b/pylint/constants.py index a609f9cd6f..6ad5b82d35 100644 --- a/pylint/constants.py +++ b/pylint/constants.py @@ -155,3 +155,16 @@ def _get_pylint_home() -> str: PYLINT_HOME = _get_pylint_home() + +TYPING_NORETURN = frozenset( + ( + "typing.NoReturn", + "typing_extensions.NoReturn", + ) +) +TYPING_NEVER = frozenset( + ( + "typing.Never", + "typing_extensions.Never", + ) +) diff --git a/pylint/extensions/typing.py b/pylint/extensions/typing.py index 615f40a48e..5f89cd5613 100644 --- a/pylint/extensions/typing.py +++ b/pylint/extensions/typing.py @@ -17,6 +17,7 @@ only_required_for_messages, safe_infer, ) +from pylint.constants import TYPING_NORETURN from pylint.interfaces import INFERENCE if TYPE_CHECKING: @@ -75,12 +76,6 @@ class TypingAlias(NamedTuple): ALIAS_NAMES = frozenset(key.split(".")[1] for key in DEPRECATED_TYPING_ALIASES) UNION_NAMES = ("Optional", "Union") -TYPING_NORETURN = frozenset( - ( - "typing.NoReturn", - "typing_extensions.NoReturn", - ) -) class DeprecatedTypingAliasMsg(NamedTuple): diff --git a/pyproject.toml b/pyproject.toml index 01a284210e..5950fdffd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,10 +34,11 @@ requires-python = ">=3.7.2" dependencies = [ "dill>=0.2", "platformdirs>=2.2.0", - # Also upgrade requirements_test_min.txt if you are bumping astroid. + # Also upgrade requirements_test_min.txt and all the CACHE_VERSION in + # github actions if you are bumping astroid. # Pinned to dev of second minor update to allow editable installs and fix primer issues, # see https://github.com/PyCQA/astroid/issues/1341 - "astroid>=2.12.9,<=2.14.0-dev0", + "astroid>=2.12.10,<=2.14.0-dev0", "isort>=4.2.5,<6", "mccabe>=0.6,<0.8", "tomli>=1.1.0;python_version<'3.11'", diff --git a/requirements_test_min.txt b/requirements_test_min.txt index 800d85755c..3c917bd875 100644 --- a/requirements_test_min.txt +++ b/requirements_test_min.txt @@ -1,6 +1,7 @@ -e .[testutils,spelling] # astroid dependency is also defined in pyproject.toml -astroid==2.12.9 # Pinned to a specific version for tests +# You need to increment the CACHE_VERSION in github actions too +astroid==2.12.10 # Pinned to a specific version for tests typing-extensions~=4.3 pytest~=7.1 pytest-benchmark~=3.4 diff --git a/tbump.toml b/tbump.toml index 5c34271ce2..3c7efb0f6b 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/PyCQA/pylint" [version] -current = "2.15.2" +current = "2.15.3" regex = ''' ^(?P0|[1-9]\d*) \. diff --git a/tests/functional/g/globals.py b/tests/functional/g/globals.py index 78538c0426..3960df6548 100644 --- a/tests/functional/g/globals.py +++ b/tests/functional/g/globals.py @@ -31,9 +31,15 @@ def define_constant(): def global_with_import(): - """should only warn for global-statement""" + """should only warn for global-statement when using `Import` node""" global sys # [global-statement] - import sys # pylint: disable=import-outside-toplevel + import sys + + +def global_with_import_from(): + """should only warn for global-statement when using `ImportFrom` node""" + global namedtuple # [global-statement] + from collections import namedtuple def global_no_assign(): @@ -75,11 +81,6 @@ def FUNC(): FUNC() -def func(): - """Overriding a global with an import should only throw a global statement error""" - global sys # [global-statement] - - import sys def override_class(): """Overriding a class should only throw a global statement error""" diff --git a/tests/functional/g/globals.txt b/tests/functional/g/globals.txt index 8c1db54fdf..a378720939 100644 --- a/tests/functional/g/globals.txt +++ b/tests/functional/g/globals.txt @@ -5,10 +5,10 @@ global-variable-not-assigned:23:4:23:14:other:Using global for 'HOP' but no assi undefined-variable:24:10:24:13:other:Undefined variable 'HOP':UNDEFINED global-variable-undefined:29:4:29:18:define_constant:Global variable 'SOMEVAR' undefined at the module level:UNDEFINED global-statement:35:4:35:14:global_with_import:Using the global statement:UNDEFINED -global-variable-not-assigned:41:4:41:19:global_no_assign:Using global for 'CONSTANT' but no assignment is done:UNDEFINED -global-statement:47:4:47:19:global_del:Using the global statement:UNDEFINED -global-statement:54:4:54:19:global_operator_assign:Using the global statement:UNDEFINED -global-statement:61:4:61:19:global_function_assign:Using the global statement:UNDEFINED -global-statement:71:4:71:15:override_func:Using the global statement:UNDEFINED -global-statement:80:4:80:14:func:Using the global statement:UNDEFINED -global-statement:86:4:86:16:override_class:Using the global statement:UNDEFINED +global-statement:41:4:41:21:global_with_import_from:Using the global statement:UNDEFINED +global-variable-not-assigned:47:4:47:19:global_no_assign:Using global for 'CONSTANT' but no assignment is done:UNDEFINED +global-statement:53:4:53:19:global_del:Using the global statement:UNDEFINED +global-statement:60:4:60:19:global_operator_assign:Using the global statement:UNDEFINED +global-statement:67:4:67:19:global_function_assign:Using the global statement:UNDEFINED +global-statement:77:4:77:15:override_func:Using the global statement:UNDEFINED +global-statement:87:4:87:16:override_class:Using the global statement:UNDEFINED diff --git a/tests/functional/i/invalid/invalid_class_object.py b/tests/functional/i/invalid/invalid_class_object.py index 7c08ebae81..b7363e9c87 100644 --- a/tests/functional/i/invalid/invalid_class_object.py +++ b/tests/functional/i/invalid/invalid_class_object.py @@ -1,12 +1,15 @@ # pylint: disable=missing-docstring,too-few-public-methods,invalid-name from collections import defaultdict + class A: pass + class B: pass + A.__class__ = B A.__class__ = str A.__class__ = float @@ -30,3 +33,43 @@ def __deepcopy__(self, memo): obj = C() obj.__class__ = self.__class__ return obj + + +class AnotherClass: + ... + + +class Pylint7429Good: + """See https://github.com/PyCQA/pylint/issues/7467""" + + def class_defining_function_good(self): + self.__class__, myvar = AnotherClass, "myvalue" + print(myvar) + + def class_defining_function_bad(self): + self.__class__, myvar = 1, "myvalue" # [invalid-class-object] + print(myvar) + + def class_defining_function_good_inverted(self): + myvar, self.__class__ = "myvalue", AnotherClass + print(myvar) + + def class_defining_function_bad_inverted(self): + myvar, self.__class__ = "myvalue", 1 # [invalid-class-object] + print(myvar) + + def class_defining_function_complex_bad(self): + myvar, self.__class__, other = ( # [invalid-class-object] + "myvalue", + 1, + "othervalue", + ) + print(myvar, other) + + def class_defining_function_complex_good(self): + myvar, self.__class__, other = ( + "myvalue", + str, + "othervalue", + ) + print(myvar, other) diff --git a/tests/functional/i/invalid/invalid_class_object.txt b/tests/functional/i/invalid/invalid_class_object.txt index 221431b48b..793a5de691 100644 --- a/tests/functional/i/invalid/invalid_class_object.txt +++ b/tests/functional/i/invalid/invalid_class_object.txt @@ -1,2 +1,5 @@ -invalid-class-object:17:0:17:11::Invalid __class__ object:UNDEFINED -invalid-class-object:18:0:18:11::Invalid __class__ object:UNDEFINED +invalid-class-object:20:0:20:11::Invalid __class__ object:UNDEFINED +invalid-class-object:21:0:21:11::Invalid __class__ object:UNDEFINED +invalid-class-object:50:8:50:22:Pylint7429Good.class_defining_function_bad:Invalid __class__ object:UNDEFINED +invalid-class-object:58:15:58:29:Pylint7429Good.class_defining_function_bad_inverted:Invalid __class__ object:UNDEFINED +invalid-class-object:62:15:62:29:Pylint7429Good.class_defining_function_complex_bad:Invalid __class__ object:UNDEFINED diff --git a/tests/functional/m/modified_iterating.py b/tests/functional/m/modified_iterating.py index 527ce358cb..d9aad394ab 100644 --- a/tests/functional/m/modified_iterating.py +++ b/tests/functional/m/modified_iterating.py @@ -105,3 +105,15 @@ def my_method(self): """This should raise as we are deleting.""" for var in self.attribute: del var # [modified-iterating-list] + + +class MyClass2: + """Regression test for https://github.com/PyCQA/pylint/issues/7461""" + def __init__(self) -> None: + self.attribute = {} + + def my_method(self): + """This should not raise, as a copy was made.""" + for key in self.attribute: + tmp = self.attribute.copy() + tmp[key] = None diff --git a/tests/functional/u/undefined/undefined_loop_variable.py b/tests/functional/u/undefined/undefined_loop_variable.py index 3e096472d9..9ab08d5953 100644 --- a/tests/functional/u/undefined/undefined_loop_variable.py +++ b/tests/functional/u/undefined/undefined_loop_variable.py @@ -1,5 +1,13 @@ # pylint: disable=missing-docstring,redefined-builtin, consider-using-f-string, unnecessary-direct-lambda-call +import sys + +if sys.version_info >= (3, 8): + from typing import NoReturn +else: + from typing_extensions import NoReturn + + def do_stuff(some_random_list): for var in some_random_list: pass @@ -125,6 +133,18 @@ def for_else_continue(iterable): print(thing) +def for_else_no_return(iterable): + def fail() -> NoReturn: + ... + + while True: + for thing in iterable: + break + else: + fail() + print(thing) + + lst = [] lst2 = [1, 2, 3] diff --git a/tests/functional/u/undefined/undefined_loop_variable.txt b/tests/functional/u/undefined/undefined_loop_variable.txt index dce1300ce3..e10c9e0021 100644 --- a/tests/functional/u/undefined/undefined_loop_variable.txt +++ b/tests/functional/u/undefined/undefined_loop_variable.txt @@ -1,4 +1,4 @@ -undefined-loop-variable:6:11:6:14:do_stuff:Using possibly undefined loop variable 'var':UNDEFINED -undefined-loop-variable:25:7:25:11::Using possibly undefined loop variable 'var1':UNDEFINED -undefined-loop-variable:75:11:75:14:do_stuff_with_redefined_range:Using possibly undefined loop variable 'var':UNDEFINED -undefined-loop-variable:181:11:181:20:find_even_number:Using possibly undefined loop variable 'something':UNDEFINED +undefined-loop-variable:14:11:14:14:do_stuff:Using possibly undefined loop variable 'var':UNDEFINED +undefined-loop-variable:33:7:33:11::Using possibly undefined loop variable 'var1':UNDEFINED +undefined-loop-variable:83:11:83:14:do_stuff_with_redefined_range:Using possibly undefined loop variable 'var':UNDEFINED +undefined-loop-variable:201:11:201:20:find_even_number:Using possibly undefined loop variable 'something':UNDEFINED diff --git a/tests/functional/u/undefined/undefined_loop_variable_py311.py b/tests/functional/u/undefined/undefined_loop_variable_py311.py new file mode 100644 index 0000000000..93b43a5468 --- /dev/null +++ b/tests/functional/u/undefined/undefined_loop_variable_py311.py @@ -0,0 +1,17 @@ +"""Tests for undefined-loop-variable using Python 3.11 syntax.""" + +from typing import Never + + +def for_else_never(iterable): + """Test for-else with Never type.""" + + def idontreturn() -> Never: + """This function never returns.""" + + while True: + for thing in iterable: + break + else: + idontreturn() + print(thing) diff --git a/tests/functional/u/undefined/undefined_loop_variable_py311.rc b/tests/functional/u/undefined/undefined_loop_variable_py311.rc new file mode 100644 index 0000000000..56e6770585 --- /dev/null +++ b/tests/functional/u/undefined/undefined_loop_variable_py311.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.11 diff --git a/tests/functional/u/unhashable_member.py b/tests/functional/u/unhashable_member.py index 1225543756..a9100b9058 100644 --- a/tests/functional/u/unhashable_member.py +++ b/tests/functional/u/unhashable_member.py @@ -21,3 +21,4 @@ class Unhashable: {[1, 2, 3]} # [unhashable-member] {"tomato": "tomahto"} {dict: {}} +{lambda x: x: "tomato"} # pylint: disable=unnecessary-lambda diff --git a/tests/functional/u/unused/unused_variable.py b/tests/functional/u/unused/unused_variable.py index c811d00b1c..f81fddefda 100644 --- a/tests/functional/u/unused/unused_variable.py +++ b/tests/functional/u/unused/unused_variable.py @@ -94,7 +94,7 @@ def test_global(): variables through imports. """ # pylint: disable=redefined-outer-name - global PATH, OS, collections, deque # [global-variable-not-assigned, global-variable-not-assigned] + global PATH, OS, collections, deque # [global-statement] from os import path as PATH import os as OS import collections diff --git a/tests/functional/u/unused/unused_variable.txt b/tests/functional/u/unused/unused_variable.txt index 1bea139771..7b1fa834db 100644 --- a/tests/functional/u/unused/unused_variable.txt +++ b/tests/functional/u/unused/unused_variable.txt @@ -13,8 +13,7 @@ unused-import:55:4:55:38:unused_import_from:Unused namedtuple imported from coll unused-import:59:4:59:40:unused_import_in_function:Unused hexdigits imported from string:UNDEFINED unused-variable:64:4:64:10:hello:Unused variable 'my_var':UNDEFINED unused-variable:75:4:75:8:function:Unused variable 'aaaa':UNDEFINED -global-variable-not-assigned:97:4:97:39:test_global:Using global for 'PATH' but no assignment is done:UNDEFINED -global-variable-not-assigned:97:4:97:39:test_global:Using global for 'deque' but no assignment is done:UNDEFINED +global-statement:97:4:97:39:test_global:Using the global statement:UNDEFINED unused-import:103:4:103:28:test_global:Unused platform imported from sys:UNDEFINED unused-import:104:4:104:38:test_global:Unused version imported from sys as VERSION:UNDEFINED unused-import:105:4:105:15:test_global:Unused import this:UNDEFINED diff --git a/tests/test_self.py b/tests/test_self.py index f2587c5c73..c382edb196 100644 --- a/tests/test_self.py +++ b/tests/test_self.py @@ -1229,6 +1229,7 @@ def test_output_of_callback_options( [["--help-msg", "W0101"], ":unreachable (W0101)", False], [["--help-msg", "WX101"], "No such message id", False], [["--help-msg"], "--help-msg: expected at least one argumen", True], + [["--help-msg", "C0102,C0103"], ":invalid-name (C0103):", False], ], ) def test_help_msg(