diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 59af3e059f..2de0c8d4a8 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -130,10 +130,10 @@ contributors: - Eli Fine (eli88fine): Fixed false positive duplicate code warning for lines with symbols only - Andrew Haigh (nelfin) - Émile Crater +- Yilei "Dolee" Yang - Pavel Roskin - David Gilman - へーさん -- Yilei "Dolee" Yang - Thomas Hisch - Marianna Polatoglou : minor contribution for wildcard import check - Manuel Vázquez Acosta @@ -206,6 +206,7 @@ contributors: - chohner - Tiago Honorato <61059243+tiagohonorato@users.noreply.github.com> - Steven M. Vascellaro +- Rogdham - Robin Tweedie <70587124+robin-wayve@users.noreply.github.com> - Roberto Leinardi : PyCharm plugin maintainer - Ricardo Gemignani @@ -254,7 +255,6 @@ contributors: - Scott Worley - Saugat Pachhai - Rémi Cardona -- Rogdham - Raphael Gaschignard - Ram Rachum (cool-RR) - Radostin Stoyanov diff --git a/doc/whatsnew/2/2.17/index.rst b/doc/whatsnew/2/2.17/index.rst index cd06bbc2e7..974ebb5012 100644 --- a/doc/whatsnew/2/2.17/index.rst +++ b/doc/whatsnew/2/2.17/index.rst @@ -29,6 +29,64 @@ so we find problems before the actual release. .. towncrier release notes start +What's new in Pylint 2.17.3? +---------------------------- +Release date: 2023-04-24 + + +False Positives Fixed +--------------------- + +- Fix `unused-argument` false positive when `__new__` does not use all the + arguments of `__init__`. + + Closes #3670 (`#3670 `_) + +- Fix ``unused-import`` false positive for usage of ``six.with_metaclass``. + + Closes #7506 (`#7506 `_) + +- `logging-not-lazy` is not longer emitted for explicitly concatenated string + arguments. + + Closes #8410 (`#8410 `_) + +- Fix false positive for isinstance-second-argument-not-valid-type when union + types contains None. + + Closes #8424 (`#8424 `_) + +- Fixed `unused-import` so that it observes the `dummy-variables-rgx` option. + + Closes #8500 (`#8500 `_) + +- `Union` typed variables without assignment are no longer treated as + `TypeAlias`. + + Closes #8540 (`#8540 `_) + +- Fix false positive for ``positional-only-arguments-expected`` when a function + contains both a positional-only parameter that has a default value, and + ``**kwargs``. + + Closes #8555 (`#8555 `_) + +- Fix false positive for ``keyword-arg-before-vararg`` when a positional-only + parameter with a default value precedes ``*args``. + + Closes #8570 (`#8570 `_) + + + +Other Bug Fixes +--------------- + +- Improve output of ``consider-using-generator`` message for ``min()` calls + with ``default`` keyword. + + Closes #8563 (`#8563 `_) + + What's new in Pylint 2.17.2? ---------------------------- Release date: 2023-04-03 diff --git a/doc/whatsnew/fragments/7506.false_positive b/doc/whatsnew/fragments/7506.false_positive deleted file mode 100644 index c6424e1f2e..0000000000 --- a/doc/whatsnew/fragments/7506.false_positive +++ /dev/null @@ -1,3 +0,0 @@ -Fix ``unused-import`` false positive for usage of ``six.with_metaclass``. - -Closes #7506 diff --git a/pylint/__pkginfo__.py b/pylint/__pkginfo__.py index beebd2492a..a140a97917 100644 --- a/pylint/__pkginfo__.py +++ b/pylint/__pkginfo__.py @@ -9,7 +9,7 @@ from __future__ import annotations -__version__ = "2.17.2" +__version__ = "2.17.3" def get_numversion_from_version(v: str) -> tuple[int, int, int]: diff --git a/pylint/checkers/base/name_checker/checker.py b/pylint/checkers/base/name_checker/checker.py index c2b615a481..58f7198ef0 100644 --- a/pylint/checkers/base/name_checker/checker.py +++ b/pylint/checkers/base/name_checker/checker.py @@ -602,10 +602,7 @@ def _assigns_typealias(node: nodes.NodeNG | None) -> bool: # Union is a special case because it can be used as a type alias # or as a type annotation. We only want to check the former. assert node is not None - return not ( - isinstance(node.parent, nodes.AnnAssign) - and node.parent.value is not None - ) + return not isinstance(node.parent, nodes.AnnAssign) elif isinstance(inferred, nodes.FunctionDef): if inferred.qname() == "typing.TypeAlias": return True diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index f7568a3a44..3064dc9268 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -246,7 +246,7 @@ def _check_log_method(self, node: nodes.Call, name: str) -> None: if isinstance(format_arg, nodes.BinOp): binop = format_arg emit = binop.op == "%" - if binop.op == "+": + if binop.op == "+" and not self._is_node_explicit_str_concatenation(binop): total_number_of_strings = sum( 1 for operand in (binop.left, binop.right) @@ -294,6 +294,19 @@ def _is_operand_literal_str(operand: InferenceResult | None) -> bool: """Return True if the operand in argument is a literal string.""" return isinstance(operand, nodes.Const) and operand.name == "str" + @staticmethod + def _is_node_explicit_str_concatenation(node: nodes.NodeNG) -> bool: + """Return True if the node represents an explicitly concatenated string.""" + if not isinstance(node, nodes.BinOp): + return False + return ( + LoggingChecker._is_operand_literal_str(node.left) + or LoggingChecker._is_node_explicit_str_concatenation(node.left) + ) and ( + LoggingChecker._is_operand_literal_str(node.right) + or LoggingChecker._is_node_explicit_str_concatenation(node.right) + ) + def _check_call_func(self, node: nodes.Call) -> None: """Checks that function call is not format_string.format().""" func = utils.safe_infer(node.func) diff --git a/pylint/checkers/method_args.py b/pylint/checkers/method_args.py index e882482190..8862328e39 100644 --- a/pylint/checkers/method_args.py +++ b/pylint/checkers/method_args.py @@ -111,6 +111,8 @@ def _check_positional_only_arguments_expected(self, node: nodes.Call) -> None: and inferred_func.args.posonlyargs ): return + if inferred_func.args.kwarg: + return pos_args = [a.name for a in inferred_func.args.posonlyargs] kws = [k.arg for k in node.keywords if k.arg in pos_args] if not kws: diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py index b0a87b4c98..ec4d3d71eb 100644 --- a/pylint/checkers/refactoring/refactoring_checker.py +++ b/pylint/checkers/refactoring/refactoring_checker.py @@ -1070,11 +1070,15 @@ def _check_consider_using_generator(self, node: nodes.Call) -> None: and isinstance(node.func, nodes.Name) and node.func.name in checked_call ): - # functions in checked_calls take exactly one argument + # functions in checked_calls take exactly one positional argument # check whether the argument is list comprehension if len(node.args) == 1 and isinstance(node.args[0], nodes.ListComp): # remove square brackets '[]' inside_comp = node.args[0].as_string()[1:-1] + if node.keywords: + inside_comp = f"({inside_comp})" + inside_comp += ", " + inside_comp += ", ".join(kw.as_string() for kw in node.keywords) call_name = node.func.name if call_name in {"any", "all"}: self.add_message( diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 8e00384d7c..1a628f2312 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -37,6 +37,7 @@ is_mapping, is_module_ignored, is_node_in_type_annotation_context, + is_none, is_overload_stub, is_postponed_evaluation_enabled, is_super, @@ -798,8 +799,9 @@ def _is_c_extension(module_node: InferenceResult) -> bool: def _is_invalid_isinstance_type(arg: nodes.NodeNG) -> bool: # Return True if we are sure that arg is not a type if PY310_PLUS and isinstance(arg, nodes.BinOp) and arg.op == "|": - return _is_invalid_isinstance_type(arg.left) or _is_invalid_isinstance_type( - arg.right + return any( + _is_invalid_isinstance_type(elt) and not is_none(elt) + for elt in (arg.left, arg.right) ) inferred = utils.safe_infer(arg) if not inferred: @@ -812,9 +814,10 @@ def _is_invalid_isinstance_type(arg: nodes.NodeNG) -> bool: if isinstance(inferred, astroid.Instance) and inferred.qname() == BUILTIN_TUPLE: return False if PY310_PLUS and isinstance(inferred, bases.UnionType): - return _is_invalid_isinstance_type( - inferred.left - ) or _is_invalid_isinstance_type(inferred.right) + return any( + _is_invalid_isinstance_type(elt) and not is_none(elt) + for elt in (inferred.left, inferred.right) + ) return True @@ -998,8 +1001,14 @@ def _compiled_generated_members(self) -> tuple[Pattern[str], ...]: @only_required_for_messages("keyword-arg-before-vararg") def visit_functiondef(self, node: nodes.FunctionDef) -> None: - # check for keyword arg before varargs + # check for keyword arg before varargs. + if node.args.vararg and node.args.defaults: + # When `positional-only` parameters are present then only + # `positional-or-keyword` parameters are checked. I.e: + # >>> def name(pos_only_params, /, pos_or_keyword_params, *args): ... + if node.args.posonlyargs and not node.args.args: + return self.add_message("keyword-arg-before-vararg", node=node, args=(node.name)) visit_asyncfunctiondef = visit_functiondef diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index f55b712648..8cd1ed83cb 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -2602,6 +2602,16 @@ def _check_is_unused( argnames = node.argnames() # Care about functions with unknown argument (builtins) if name in argnames: + if node.name == "__new__": + is_init_def = False + # Look for the `__init__` method in all the methods of the same class. + for n in node.parent.get_children(): + is_init_def = hasattr(n, "name") and (n.name == "__init__") + if is_init_def: + break + # Ignore unused arguments check for `__new__` if `__init__` is defined. + if is_init_def: + return self._check_unused_arguments(name, node, stmt, argnames, nonlocal_names) else: if stmt.parent and isinstance( @@ -3052,6 +3062,13 @@ def _check_imports(self, not_consumed: dict[str, list[nodes.NodeNG]]) -> None: imported_name in self._type_annotation_names or as_name in self._type_annotation_names ) + + is_dummy_import = ( + as_name + and self.linter.config.dummy_variables_rgx + and self.linter.config.dummy_variables_rgx.match(as_name) + ) + if isinstance(stmt, nodes.Import) or ( isinstance(stmt, nodes.ImportFrom) and not stmt.modname ): @@ -3062,12 +3079,11 @@ def _check_imports(self, not_consumed: dict[str, list[nodes.NodeNG]]) -> None: # because they can be imported for exporting. continue - if is_type_annotation_import: + if is_type_annotation_import or is_dummy_import: # Most likely a typing import if it wasn't used so far. + # Also filter dummy variables. continue - if as_name == "_": - continue if as_name is None: msg = f"import {imported_name}" else: @@ -3085,8 +3101,9 @@ def _check_imports(self, not_consumed: dict[str, list[nodes.NodeNG]]) -> None: # __future__ import in another module. continue - if is_type_annotation_import: + if is_type_annotation_import or is_dummy_import: # Most likely a typing import if it wasn't used so far. + # Also filter dummy variables. continue if imported_name == "*": diff --git a/pyproject.toml b/pyproject.toml index 559341011d..bd542646b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ dependencies = [ # Also upgrade requirements_test_min.txt. # Pinned to dev of second minor update to allow editable installs and fix primer issues, # see https://github.com/pylint-dev/astroid/issues/1341 - "astroid>=2.15.2,<=2.17.0-dev0", + "astroid>=2.15.4,<=2.17.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 08321b54fd..499d287f64 100644 --- a/requirements_test_min.txt +++ b/requirements_test_min.txt @@ -1,6 +1,6 @@ -e .[testutils,spelling] # astroid dependency is also defined in pyproject.toml -astroid==2.15.2 # Pinned to a specific version for tests +astroid==2.15.4 # Pinned to a specific version for tests typing-extensions~=4.5 py~=1.11.0 pytest~=7.2 diff --git a/tbump.toml b/tbump.toml index 1f6594b2e7..62cfd54988 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,7 +1,7 @@ github_url = "https://github.com/PyCQA/pylint" [version] -current = "2.17.2" +current = "2.17.3" regex = ''' ^(?P0|[1-9]\d*) \. diff --git a/tests/checkers/unittest_typecheck.py b/tests/checkers/unittest_typecheck.py index afcc2dc2e7..dba69eaf34 100644 --- a/tests/checkers/unittest_typecheck.py +++ b/tests/checkers/unittest_typecheck.py @@ -10,7 +10,7 @@ from pylint.testutils import CheckerTestCase, MessageTest, set_config try: - from coverage import tracer as _ # pylint: disable=unused-import + from coverage import tracer as _ C_EXTENTIONS_AVAILABLE = True except ImportError: diff --git a/tests/functional/c/consider/consider_using_generator.py b/tests/functional/c/consider/consider_using_generator.py index e5ac31f323..ee561a7e97 100644 --- a/tests/functional/c/consider/consider_using_generator.py +++ b/tests/functional/c/consider/consider_using_generator.py @@ -18,3 +18,8 @@ sum(x*x for x in range(10)) min(x*x for x in range(10)) max(x*x for x in range(10)) + +# Keyword arguments +# https://github.com/pylint-dev/pylint/issues/8563 +min([x*x for x in range(10)], default=42) # [consider-using-generator] +min((x*x for x in range(10)), default=42) diff --git a/tests/functional/c/consider/consider_using_generator.txt b/tests/functional/c/consider/consider_using_generator.txt index 2c89a8d7e2..8a2a4ec6d1 100644 --- a/tests/functional/c/consider/consider_using_generator.txt +++ b/tests/functional/c/consider/consider_using_generator.txt @@ -3,3 +3,4 @@ consider-using-generator:11:0:11:35::Consider using a generator instead 'tuple(0 consider-using-generator:12:0:12:29::Consider using a generator instead 'sum(x * x for x in range(10))':UNDEFINED consider-using-generator:13:0:13:29::Consider using a generator instead 'min(x * x for x in range(10))':UNDEFINED consider-using-generator:14:0:14:29::Consider using a generator instead 'max(x * x for x in range(10))':UNDEFINED +consider-using-generator:24:0:24:41::Consider using a generator instead 'min((x * x for x in range(10)), default=42)':UNDEFINED diff --git a/tests/functional/i/import_dummy.py b/tests/functional/i/import_dummy.py new file mode 100644 index 0000000000..eecfa0d8a6 --- /dev/null +++ b/tests/functional/i/import_dummy.py @@ -0,0 +1,5 @@ +"""Testing importing module as dummy variable.""" +import gettext as _ +import sys as __ +import typing as ___dummy +from base64 import b64encode as ____dummy diff --git a/tests/functional/i/isinstance_second_argument_py310.py b/tests/functional/i/isinstance_second_argument_py310.py index 8a0c17af58..ad2033b310 100644 --- a/tests/functional/i/isinstance_second_argument_py310.py +++ b/tests/functional/i/isinstance_second_argument_py310.py @@ -1,13 +1,17 @@ -'''Tests for invalid isinstance with compound types''' +"""Tests for invalid isinstance with compound types""" # True negatives isinstance(0, int | str) isinstance(0, int | int | int) isinstance(0, int | str | list | float) isinstance(0, (int | str) | (list | float)) +isinstance(0, int | None) +isinstance(0, None | int) IntOrStr = int | str isinstance(0, IntOrStr) +IntOrNone = int | None +isinstance(0, IntOrNone) ListOrDict = list | dict isinstance(0, (float | ListOrDict) | IntOrStr) diff --git a/tests/functional/i/isinstance_second_argument_py310.txt b/tests/functional/i/isinstance_second_argument_py310.txt index 776bf3c2e1..aa50da29d4 100644 --- a/tests/functional/i/isinstance_second_argument_py310.txt +++ b/tests/functional/i/isinstance_second_argument_py310.txt @@ -1,3 +1,3 @@ -isinstance-second-argument-not-valid-type:15:0:15:22::Second argument of isinstance is not a type:INFERENCE -isinstance-second-argument-not-valid-type:16:0:16:28::Second argument of isinstance is not a type:INFERENCE -isinstance-second-argument-not-valid-type:18:0:18:24::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:19:0:19:22::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:20:0:20:28::Second argument of isinstance is not a type:INFERENCE +isinstance-second-argument-not-valid-type:22:0:22:24::Second argument of isinstance is not a type:INFERENCE diff --git a/tests/functional/k/keyword_arg_before_vararg_positional_only.py b/tests/functional/k/keyword_arg_before_vararg_positional_only.py new file mode 100644 index 0000000000..70b5f9929f --- /dev/null +++ b/tests/functional/k/keyword_arg_before_vararg_positional_only.py @@ -0,0 +1,13 @@ +"""Test `keyword-arg-before-vararg` in the context of positional-only parameters""" + +# pylint: disable=missing-function-docstring, unused-argument + + +def name1(param1, /, param2=True, *args): ... # [keyword-arg-before-vararg] +def name2(param1=True, /, param2=True, *args): ... # [keyword-arg-before-vararg] +def name3(param1, param2=True, /, param3=True, *args): ... # [keyword-arg-before-vararg] +def name4(param1, /, *args): ... +def name5(param1=True, /, *args): ... +def name6(param1, /, *args, param2=True): ... +def name7(param1=True, /, *args, param2=True): ... +def name8(param1, param2=True, /, *args, param3=True): ... diff --git a/tests/functional/k/keyword_arg_before_vararg_positional_only.rc b/tests/functional/k/keyword_arg_before_vararg_positional_only.rc new file mode 100644 index 0000000000..85fc502b37 --- /dev/null +++ b/tests/functional/k/keyword_arg_before_vararg_positional_only.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.8 diff --git a/tests/functional/k/keyword_arg_before_vararg_positional_only.txt b/tests/functional/k/keyword_arg_before_vararg_positional_only.txt new file mode 100644 index 0000000000..18700e2e42 --- /dev/null +++ b/tests/functional/k/keyword_arg_before_vararg_positional_only.txt @@ -0,0 +1,3 @@ +keyword-arg-before-vararg:6:0:6:9:name1:Keyword argument before variable positional arguments list in the definition of name1 function:UNDEFINED +keyword-arg-before-vararg:7:0:7:9:name2:Keyword argument before variable positional arguments list in the definition of name2 function:UNDEFINED +keyword-arg-before-vararg:8:0:8:9:name3:Keyword argument before variable positional arguments list in the definition of name3 function:UNDEFINED diff --git a/tests/functional/l/logging/logging_not_lazy.py b/tests/functional/l/logging/logging_not_lazy.py index ab1ce008c7..aa2401dbc6 100644 --- a/tests/functional/l/logging/logging_not_lazy.py +++ b/tests/functional/l/logging/logging_not_lazy.py @@ -9,10 +9,10 @@ var_name = "Var:" # Statements that should be flagged: renamed_logging.warn("%s, %s" % (4, 5)) # [logging-not-lazy] +renamed_logging.warn("Var: " + var) # [logging-not-lazy] renamed_logging.exception("%s" % "Exceptional!") # [logging-not-lazy] renamed_logging.log(renamed_logging.INFO, "msg: %s" % "Run!") # [logging-not-lazy] renamed_logging.log(renamed_logging.INFO, "Var: " + var) # [logging-not-lazy] -renamed_logging.warn("%s" + " the rest of a single string") # [logging-not-lazy] renamed_logging.log(renamed_logging.INFO, var_name + var) # [logging-not-lazy] # Statements that should not be flagged: @@ -21,6 +21,11 @@ logging.warn("%s, %s" % (4, 5)) logging.log(logging.INFO, "msg: %s" % "Run!") logging.log("Var: " + var) +# Explicit string concatenations are fine: +renamed_logging.warn("%s" + " the rest of a single string") +renamed_logging.warn("Msg: " + "%s", "first piece " + "second piece") +renamed_logging.warn("first" + "second" + "third %s", "parameter") +renamed_logging.warn(("first" + "second" + "third %s")) # Regression crash test for incorrect format call renamed_logging.error( diff --git a/tests/functional/l/logging/logging_not_lazy.txt b/tests/functional/l/logging/logging_not_lazy.txt index 564fa65c3b..774082eb77 100644 --- a/tests/functional/l/logging/logging_not_lazy.txt +++ b/tests/functional/l/logging/logging_not_lazy.txt @@ -1,8 +1,8 @@ logging-not-lazy:11:0:11:39::Use lazy % formatting in logging functions:UNDEFINED -logging-not-lazy:12:0:12:48::Use lazy % formatting in logging functions:UNDEFINED -logging-not-lazy:13:0:13:61::Use lazy % formatting in logging functions:UNDEFINED -logging-not-lazy:14:0:14:56::Use lazy % formatting in logging functions:UNDEFINED -logging-not-lazy:15:0:15:59::Use lazy % formatting in logging functions:UNDEFINED +logging-not-lazy:12:0:12:35::Use lazy % formatting in logging functions:UNDEFINED +logging-not-lazy:13:0:13:48::Use lazy % formatting in logging functions:UNDEFINED +logging-not-lazy:14:0:14:61::Use lazy % formatting in logging functions:UNDEFINED +logging-not-lazy:15:0:15:56::Use lazy % formatting in logging functions:UNDEFINED logging-not-lazy:16:0:16:57::Use lazy % formatting in logging functions:UNDEFINED -bad-format-string:27:4:27:27::Invalid format string:UNDEFINED -logging-format-interpolation:27:4:27:27::Use lazy % formatting in logging functions:UNDEFINED +bad-format-string:32:4:32:27::Invalid format string:UNDEFINED +logging-format-interpolation:32:4:32:27::Use lazy % formatting in logging functions:UNDEFINED diff --git a/tests/functional/p/positional_only_arguments_expected.py b/tests/functional/p/positional_only_arguments_expected.py index 7bde59ab84..98a2d65f5c 100644 --- a/tests/functional/p/positional_only_arguments_expected.py +++ b/tests/functional/p/positional_only_arguments_expected.py @@ -16,3 +16,21 @@ def nihon(self, a, r, i, /, cheese=False): cake.nihon(1, r=2, i=3) # [positional-only-arguments-expected] cake.nihon(a=1, r=2, i=3) # [positional-only-arguments-expected] cake.nihon(1, r=2, i=3, cheese=True) # [positional-only-arguments-expected] + + +def function_with_kwargs(apple, banana="Yellow banana", /, **kwargs): + """ + Calling this function with the `banana` keyword should not emit + `positional-only-arguments-expected` since it is added to `**kwargs`. + + >>> function_with_kwargs("Red apple", banana="Green banana") + >>> "Red apple" + >>> "Yellow banana" + >>> {"banana": "Green banana"} + """ + print(apple) + print(banana) + print(kwargs) + + +function_with_kwargs("Red apple", banana="Green banana") diff --git a/tests/functional/r/regression/regression_4439.py b/tests/functional/r/regression/regression_4439.py index 257a20a866..dd9c392533 100644 --- a/tests/functional/r/regression/regression_4439.py +++ b/tests/functional/r/regression/regression_4439.py @@ -7,7 +7,7 @@ from typing import Optional -from attr import attrib, attrs +from attr import attrib, attrs # pylint: disable=import-error @attrs() diff --git a/tests/functional/t/typealias_naming_style_default.py b/tests/functional/t/typealias_naming_style_default.py index 6b27c14a01..8baabb49cd 100644 --- a/tests/functional/t/typealias_naming_style_default.py +++ b/tests/functional/t/typealias_naming_style_default.py @@ -26,5 +26,8 @@ ANOTHERBADNAME = Union[int, str] # [invalid-name] # Regression tests -# This is not a TypeAlias, and thus shouldn't flag the message +# They are not TypeAlias, and thus shouldn't flag the message x: Union[str, int] = 42 +y: Union[str, int] +# But the following, using a good TypeAlias name, is: +GoodTypeAliasToUnion: TypeAlias = Union[str, int] diff --git a/tests/functional/t/typealias_naming_style_rgx.py b/tests/functional/t/typealias_naming_style_rgx.py index 0910644dab..bc43b930d5 100644 --- a/tests/functional/t/typealias_naming_style_rgx.py +++ b/tests/functional/t/typealias_naming_style_rgx.py @@ -3,8 +3,8 @@ # Valid TypeAliasShouldBeLikeThis: TypeAlias = int -_TypeAliasShouldBeLikeThis: Union[str, int] +_TypeAliasShouldBeLikeThis = Union[str, int] # Invalid TypeAliasShouldntBeLikeThis: TypeAlias = int # [invalid-name] -_TypeAliasShouldntBeLikeThis: Union[str, int] # [invalid-name] +_TypeAliasShouldntBeLikeThis = Union[str, int] # [invalid-name] diff --git a/tests/functional/u/unused/unused_argument.py b/tests/functional/u/unused/unused_argument.py index b46c1e4d75..69c37feef4 100644 --- a/tests/functional/u/unused/unused_argument.py +++ b/tests/functional/u/unused/unused_argument.py @@ -107,3 +107,24 @@ class Descendant(Ancestor): def set_thing(self, thing, *, other=None): """Subclass does not raise unused-argument""" self.thing = thing + + +# Test that Class with both `__init__` and `__new__` don't check +# on `__new__` for unused arguments + +# pylint: disable=invalid-name + +class TestClassWithInitAndNew: + def __init__(self, argA, argB): + self.argA = argA + self.argB = argB + + def __new__(cls, argA, argB): + return object.__new__(cls) + +# Test that `__new__` method is checked for unused arguments +# when `__init__` is not in the Class + +class TestClassWithOnlyNew: + def __new__(cls, argA, argB): # [unused-argument, unused-argument] + return object.__new__(cls) diff --git a/tests/functional/u/unused/unused_argument.txt b/tests/functional/u/unused/unused_argument.txt index 19c4393046..bca2700acc 100644 --- a/tests/functional/u/unused/unused_argument.txt +++ b/tests/functional/u/unused/unused_argument.txt @@ -7,3 +7,5 @@ unused-argument:73:0:None:None:AAAA.selected:Unused argument 'args':INFERENCE unused-argument:73:0:None:None:AAAA.selected:Unused argument 'kwargs':INFERENCE unused-argument:92:23:92:26:BBBB.__init__:Unused argument 'arg':INFERENCE unused-argument:103:34:103:39:Ancestor.set_thing:Unused argument 'other':INFERENCE +unused-argument:129:21:129:25:TestClassWithOnlyNew.__new__:Unused argument 'argA':INFERENCE +unused-argument:129:27:129:31:TestClassWithOnlyNew.__new__:Unused argument 'argB':INFERENCE diff --git a/towncrier.toml b/towncrier.toml index 35ce272433..fce326ad61 100644 --- a/towncrier.toml +++ b/towncrier.toml @@ -1,5 +1,5 @@ [tool.towncrier] -version = "2.17.2" +version = "2.17.3" directory = "doc/whatsnew/fragments" filename = "doc/whatsnew/2/2.17/index.rst" template = "doc/whatsnew/fragments/_template.rst"