diff --git a/doc/whatsnew/fragments/10149.feature b/doc/whatsnew/fragments/10149.feature new file mode 100644 index 0000000000..ed71297170 --- /dev/null +++ b/doc/whatsnew/fragments/10149.feature @@ -0,0 +1,3 @@ +Handle deferred evaluation of annotations in Python 3.14. + +Closes #10149 diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 7953e640b4..30521c225d 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -976,8 +976,15 @@ class TypeChecker(BaseChecker): def open(self) -> None: py_version = self.linter.config.py_version self._py310_plus = py_version >= (3, 10) + self._py314_plus = py_version >= (3, 14) + self._postponed_evaluation_enabled = False self._mixin_class_rgx = self.linter.config.mixin_class_rgx + def visit_module(self, node: nodes.Module) -> None: + self._postponed_evaluation_enabled = ( + self._py314_plus or is_postponed_evaluation_enabled(node) + ) + @cached_property def _compiled_generated_members(self) -> tuple[Pattern[str], ...]: # do this lazily since config not fully initialized in __init__ @@ -1066,7 +1073,7 @@ def visit_attribute( ): return - if is_postponed_evaluation_enabled(node) and is_node_in_type_annotation_context( + if self._postponed_evaluation_enabled and is_node_in_type_annotation_context( node ): return @@ -1950,9 +1957,10 @@ def _detect_unsupported_alternative_union_syntax(self, node: nodes.BinOp) -> Non if self._py310_plus: # 310+ supports the new syntax return - if isinstance( - node.parent, TYPE_ANNOTATION_NODES_TYPES - ) and not is_postponed_evaluation_enabled(node): + if ( + isinstance(node.parent, TYPE_ANNOTATION_NODES_TYPES) + and not self._postponed_evaluation_enabled + ): # Use in type annotations only allowed if # postponed evaluation is enabled. self._check_unsupported_alternative_union_syntax(node) @@ -1974,7 +1982,7 @@ def _detect_unsupported_alternative_union_syntax(self, node: nodes.BinOp) -> Non # Make sure to filter context if postponed evaluation is enabled # and parent is allowed node type. allowed_nested_syntax = False - if is_postponed_evaluation_enabled(node): + if self._postponed_evaluation_enabled: parent_node = node.parent while True: if isinstance(parent_node, TYPE_ANNOTATION_NODES_TYPES): diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 7a55bc68b2..38af7d76df 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1300,6 +1300,10 @@ def __init__(self, linter: PyLinter) -> None: ] = {} self._postponed_evaluation_enabled = False + def open(self) -> None: + py_version = self.linter.config.py_version + self._py314_plus = py_version >= (3, 14) + @utils.only_required_for_messages( "unbalanced-dict-unpacking", ) @@ -1363,7 +1367,9 @@ def visit_module(self, node: nodes.Module) -> None: checks globals doesn't overrides builtins. """ self._to_consume = [NamesConsumer(node, "module")] - self._postponed_evaluation_enabled = is_postponed_evaluation_enabled(node) + self._postponed_evaluation_enabled = ( + self._py314_plus or is_postponed_evaluation_enabled(node) + ) for name, stmts in node.locals.items(): if utils.is_builtin(name): @@ -2489,8 +2495,8 @@ def _is_only_type_assignment( parent = parent_scope.parent return True - @staticmethod def _is_first_level_self_reference( + self, node: nodes.Name, defstmt: nodes.ClassDef, found_nodes: list[nodes.NodeNG], @@ -2502,7 +2508,7 @@ def _is_first_level_self_reference( # Check if used as type annotation # Break if postponed evaluation is enabled if utils.is_node_in_type_annotation_context(node): - if not utils.is_postponed_evaluation_enabled(node): + if not self._postponed_evaluation_enabled: return (VariableVisitConsumerAction.CONTINUE, None) return (VariableVisitConsumerAction.RETURN, None) # Check if used as default value by calling the class diff --git a/pylint/extensions/typing.py b/pylint/extensions/typing.py index 87fa3a4c22..7291e6e775 100644 --- a/pylint/extensions/typing.py +++ b/pylint/extensions/typing.py @@ -186,6 +186,8 @@ def open(self) -> None: self._py39_plus = py_version >= (3, 9) self._py310_plus = py_version >= (3, 10) self._py313_plus = py_version >= (3, 13) + self._py314_plus = py_version >= (3, 14) + self._postponed_evaluation_enabled = False self._should_check_typing_alias = self._py39_plus or ( self._py37_plus and self.linter.config.runtime_typing is False @@ -197,6 +199,11 @@ def open(self) -> None: self._should_check_noreturn = py_version < (3, 7, 2) self._should_check_callable = py_version < (3, 9, 2) + def visit_module(self, node: nodes.Module) -> None: + self._postponed_evaluation_enabled = ( + self._py314_plus or is_postponed_evaluation_enabled(node) + ) + def _msg_postponed_eval_hint(self, node: nodes.NodeNG) -> str: """Message hint if postponed evaluation isn't enabled.""" if self._py310_plus or "annotations" in node.root().future_imports: @@ -474,7 +481,7 @@ def _check_broken_noreturn(self, node: nodes.Name | nodes.Attribute) -> None: return if in_type_checking_block(node) or ( - is_postponed_evaluation_enabled(node) + self._postponed_evaluation_enabled and is_node_in_type_annotation_context(node) ): return @@ -511,7 +518,7 @@ def _check_broken_callable(self, node: nodes.Name | nodes.Attribute) -> None: def _broken_callable_location(self, node: nodes.Name | nodes.Attribute) -> bool: """Check if node would be a broken location for collections.abc.Callable.""" if in_type_checking_block(node) or ( - is_postponed_evaluation_enabled(node) + self._postponed_evaluation_enabled and is_node_in_type_annotation_context(node) ): return False diff --git a/pylint/testutils/functional/test_file.py b/pylint/testutils/functional/test_file.py index 37ba3a5fc6..402ee065eb 100644 --- a/pylint/testutils/functional/test_file.py +++ b/pylint/testutils/functional/test_file.py @@ -5,6 +5,8 @@ from __future__ import annotations import configparser +import os +import sys from collections.abc import Callable from os.path import basename, exists, join from typing import TypedDict @@ -99,7 +101,23 @@ def module(self) -> str: @property def expected_output(self) -> str: - return self._file_type(".txt", check_exists=False) + files = list( + filter( + lambda s: s.startswith(f"{self.base}.") and s.endswith(".txt"), + os.listdir(self._directory), + ) + ) + # pylint: disable-next=bad-builtin + current_version = int("".join(map(str, sys.version_info[:2]))) + output_options = [ + int(version) + for s in files + if s.count(".") == 2 and (version := s.rsplit(".", maxsplit=2)[1]).isalnum() + ] + for opt in sorted(output_options, reverse=True): + if current_version >= opt: + return join(self._directory, f"{self.base}.{opt}.txt") + return join(self._directory, self.base + ".txt") @property def source(self) -> str: diff --git a/tests/functional/ext/typing/unnecessary_default_type_args_py313.txt b/tests/functional/ext/typing/unnecessary_default_type_args.313.txt similarity index 100% rename from tests/functional/ext/typing/unnecessary_default_type_args_py313.txt rename to tests/functional/ext/typing/unnecessary_default_type_args.313.txt diff --git a/tests/functional/ext/typing/unnecessary_default_type_args.py b/tests/functional/ext/typing/unnecessary_default_type_args.py index e2d1d700de..7b43e23344 100644 --- a/tests/functional/ext/typing/unnecessary_default_type_args.py +++ b/tests/functional/ext/typing/unnecessary_default_type_args.py @@ -3,10 +3,10 @@ import typing as t a1: t.Generator[int, str, str] -a2: t.Generator[int, None, None] +a2: t.Generator[int, None, None] # >=3.13:[unnecessary-default-type-args] a3: t.Generator[int] b1: t.AsyncGenerator[int, str] -b2: t.AsyncGenerator[int, None] +b2: t.AsyncGenerator[int, None] # >=3.13:[unnecessary-default-type-args] b3: t.AsyncGenerator[int] c1: ca.Generator[int, str, str] diff --git a/tests/functional/ext/typing/unnecessary_default_type_args.rc b/tests/functional/ext/typing/unnecessary_default_type_args.rc index 63e11a4e6b..910f36995a 100644 --- a/tests/functional/ext/typing/unnecessary_default_type_args.rc +++ b/tests/functional/ext/typing/unnecessary_default_type_args.rc @@ -1,3 +1,5 @@ [main] -py-version=3.10 load-plugins=pylint.extensions.typing + +[testoptions] +min_pyver=3.10 diff --git a/tests/functional/ext/typing/unnecessary_default_type_args_py313.py b/tests/functional/ext/typing/unnecessary_default_type_args_py313.py deleted file mode 100644 index 9dec4c4075..0000000000 --- a/tests/functional/ext/typing/unnecessary_default_type_args_py313.py +++ /dev/null @@ -1,17 +0,0 @@ -# pylint: disable=missing-docstring,deprecated-typing-alias -import collections.abc as ca -import typing as t - -a1: t.Generator[int, str, str] -a2: t.Generator[int, None, None] # [unnecessary-default-type-args] -a3: t.Generator[int] -b1: t.AsyncGenerator[int, str] -b2: t.AsyncGenerator[int, None] # [unnecessary-default-type-args] -b3: t.AsyncGenerator[int] - -c1: ca.Generator[int, str, str] -c2: ca.Generator[int, None, None] # [unnecessary-default-type-args] -c3: ca.Generator[int] -d1: ca.AsyncGenerator[int, str] -d2: ca.AsyncGenerator[int, None] # [unnecessary-default-type-args] -d3: ca.AsyncGenerator[int] diff --git a/tests/functional/ext/typing/unnecessary_default_type_args_py313.rc b/tests/functional/ext/typing/unnecessary_default_type_args_py313.rc deleted file mode 100644 index d2db5fe7ca..0000000000 --- a/tests/functional/ext/typing/unnecessary_default_type_args_py313.rc +++ /dev/null @@ -1,3 +0,0 @@ -[main] -py-version=3.13 -load-plugins=pylint.extensions.typing diff --git a/tests/functional/p/postponed/postponed_evaluation_not_activated.314.txt b/tests/functional/p/postponed/postponed_evaluation_not_activated.314.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/functional/p/postponed/postponed_evaluation_not_activated.py b/tests/functional/p/postponed/postponed_evaluation_not_activated.py index 76e564a9a8..d13be1593b 100644 --- a/tests/functional/p/postponed/postponed_evaluation_not_activated.py +++ b/tests/functional/p/postponed/postponed_evaluation_not_activated.py @@ -3,10 +3,10 @@ class Class: @classmethod - def from_string(cls, source) -> Class: # [undefined-variable] + def from_string(cls, source) -> Class: # <3.14:[undefined-variable] ... - def validate_b(self, obj: OtherClass) -> bool: # [used-before-assignment] + def validate_b(self, obj: OtherClass) -> bool: # <3.14:[used-before-assignment] ... diff --git a/tests/functional/u/undefined/undefined_variable.314.txt b/tests/functional/u/undefined/undefined_variable.314.txt new file mode 100644 index 0000000000..de83d83369 --- /dev/null +++ b/tests/functional/u/undefined/undefined_variable.314.txt @@ -0,0 +1,35 @@ +undefined-variable:12:19:12:26::Undefined variable 'unknown':UNDEFINED +undefined-variable:18:10:18:21:in_method:Undefined variable 'nomoreknown':UNDEFINED +undefined-variable:21:19:21:31::Undefined variable '__revision__':UNDEFINED +undefined-variable:23:8:23:20::Undefined variable '__revision__':UNDEFINED +undefined-variable:27:29:27:37:bad_default:Undefined variable 'unknown2':UNDEFINED +undefined-variable:30:10:30:14:bad_default:Undefined variable 'xxxx':UNDEFINED +undefined-variable:31:4:31:10:bad_default:Undefined variable 'augvar':UNDEFINED +undefined-variable:32:8:32:14:bad_default:Undefined variable 'vardel':UNDEFINED +undefined-variable:34:19:34:31::Undefined variable 'doesnotexist':UNDEFINED +undefined-variable:35:23:35:24::Undefined variable 'z':UNDEFINED +used-before-assignment:38:4:38:9::Using variable 'POUET' before assignment:CONTROL_FLOW +used-before-assignment:43:4:43:10::Using variable 'POUETT' before assignment:CONTROL_FLOW +used-before-assignment:48:4:48:11::Using variable 'POUETTT' before assignment:CONTROL_FLOW +used-before-assignment:56:4:56:9::Using variable 'PLOUF' before assignment:CONTROL_FLOW +used-before-assignment:65:11:65:14:if_branch_test:Using variable 'xxx' before assignment:HIGH +used-before-assignment:91:23:91:32:test_arguments:Using variable 'TestClass' before assignment:HIGH +used-before-assignment:95:16:95:24:TestClass:Using variable 'Ancestor' before assignment:HIGH +used-before-assignment:98:26:98:35:TestClass.MissingAncestor:Using variable 'Ancestor1' before assignment:HIGH +used-before-assignment:105:36:105:41:TestClass.test1.UsingBeforeDefinition:Using variable 'Empty' before assignment:HIGH +undefined-variable:119:10:119:14:Self:Undefined variable 'Self':UNDEFINED +undefined-variable:135:7:135:10::Undefined variable 'BAT':UNDEFINED +undefined-variable:136:4:136:7::Undefined variable 'BAT':UNDEFINED +used-before-assignment:146:31:146:38:KeywordArgument.test1:Using variable 'enabled' before assignment:HIGH +undefined-variable:149:32:149:40:KeywordArgument.test2:Undefined variable 'disabled':UNDEFINED +undefined-variable:154:22:154:25:KeywordArgument.:Undefined variable 'arg':UNDEFINED +undefined-variable:166:4:166:13::Undefined variable 'unicode_2':UNDEFINED +undefined-variable:171:4:171:13::Undefined variable 'unicode_3':UNDEFINED +undefined-variable:226:25:226:37:LambdaClass4.:Undefined variable 'LambdaClass4':UNDEFINED +undefined-variable:234:25:234:37:LambdaClass5.:Undefined variable 'LambdaClass5':UNDEFINED +undefined-variable:291:18:291:24:not_using_loop_variable_accordingly:Undefined variable 'iteree':UNDEFINED +undefined-variable:308:27:308:28:undefined_annotation:Undefined variable 'x':UNDEFINED +used-before-assignment:309:7:309:8:undefined_annotation:Using variable 'x' before assignment:HIGH +undefined-variable:339:11:339:12:decorated3:Undefined variable 'x':UNDEFINED +undefined-variable:344:19:344:20:decorated4:Undefined variable 'y':UNDEFINED +undefined-variable:365:10:365:20:global_var_mixed_assignment:Undefined variable 'GLOBAL_VAR':HIGH diff --git a/tests/functional/u/undefined/undefined_variable.py b/tests/functional/u/undefined/undefined_variable.py index e1349c2610..d3190735c2 100644 --- a/tests/functional/u/undefined/undefined_variable.py +++ b/tests/functional/u/undefined/undefined_variable.py @@ -252,7 +252,7 @@ def onclick(event): from datetime import datetime -def func_should_fail(_dt: datetime): # [used-before-assignment] +def func_should_fail(_dt: datetime): # <3.14:[used-before-assignment] pass @@ -374,13 +374,13 @@ def global_var_mixed_assignment(): class RepeatedReturnAnnotations: - def x(self, o: RepeatedReturnAnnotations) -> bool: # [undefined-variable] + def x(self, o: RepeatedReturnAnnotations) -> bool: # <3.14:[undefined-variable] pass - def y(self) -> RepeatedReturnAnnotations: # [undefined-variable] + def y(self) -> RepeatedReturnAnnotations: # <3.14:[undefined-variable] pass - def z(self) -> RepeatedReturnAnnotations: # [undefined-variable] + def z(self) -> RepeatedReturnAnnotations: # <3.14:[undefined-variable] pass class A: - def say_hello(self) -> __module__: # [undefined-variable] + def say_hello(self) -> __module__: # <3.14:[undefined-variable] ... diff --git a/tests/functional/u/undefined/undefined_variable_py30.314.txt b/tests/functional/u/undefined/undefined_variable_py30.314.txt new file mode 100644 index 0000000000..81c794cfdb --- /dev/null +++ b/tests/functional/u/undefined/undefined_variable_py30.314.txt @@ -0,0 +1,9 @@ +undefined-variable:33:34:33:39:Undefined1.InnerScope.test_undefined:Undefined variable 'Undef':UNDEFINED +undefined-variable:36:25:36:28:Undefined1.InnerScope.test1:Undefined variable 'ABC':UNDEFINED +undefined-variable:51:28:51:32:FalsePositive342.test_bad:Undefined variable 'trop':UNDEFINED +undefined-variable:54:31:54:36:FalsePositive342.test_bad1:Undefined variable 'trop1':UNDEFINED +undefined-variable:57:31:57:36:FalsePositive342.test_bad2:Undefined variable 'trop2':UNDEFINED +undefined-variable:63:0:63:9:Bad:Undefined variable 'ABCMet':UNDEFINED +undefined-variable:66:0:66:15:SecondBad:Undefined variable 'ab':UNDEFINED +undefined-variable:97:53:97:61:InheritingClass:Undefined variable 'variable':UNDEFINED +undefined-variable:103:0:103:15:Inheritor:Undefined variable 'DefinedTooLate':UNDEFINED diff --git a/tests/functional/u/undefined/undefined_variable_py30.py b/tests/functional/u/undefined/undefined_variable_py30.py index 723e275a06..0bf6fc7f3b 100644 --- a/tests/functional/u/undefined/undefined_variable_py30.py +++ b/tests/functional/u/undefined/undefined_variable_py30.py @@ -5,7 +5,7 @@ class Undefined: """ test various annotation problems. """ - def test(self)->Undefined: # [undefined-variable] + def test(self)->Undefined: # <3.14:[undefined-variable] """ used Undefined, which is Undefined in this scope. """ Undefined = True diff --git a/tests/functional/u/used/used_before_assignment_nonlocal.314.txt b/tests/functional/u/used/used_before_assignment_nonlocal.314.txt new file mode 100644 index 0000000000..35ff30f1a2 --- /dev/null +++ b/tests/functional/u/used/used_before_assignment_nonlocal.314.txt @@ -0,0 +1,7 @@ +used-before-assignment:17:14:17:17:test_fail.wrap:Using variable 'cnt' before assignment:HIGH +used-before-assignment:26:14:26:17:test_fail2.wrap:Using variable 'cnt' before assignment:HIGH +used-before-assignment:90:10:90:18:type_annotation_never_gets_value_despite_nonlocal:Using variable 'some_num' before assignment:HIGH +used-before-assignment:96:14:96:18:inner_function_lacks_access_to_outer_args.inner:Using variable 'args' before assignment:HIGH +used-before-assignment:117:18:117:21:nonlocal_in_outer_frame_fail.outer.inner:Using variable 'num' before assignment:HIGH +possibly-used-before-assignment:149:20:149:28:nonlocal_in_distant_outer_frame_fail.outer.intermediate.inner:Possibly using variable 'callback' before assignment:CONTROL_FLOW +used-before-assignment:163:14:163:17:nonlocal_after_bad_usage_fail.inner:Using variable 'num' before assignment:HIGH diff --git a/tests/functional/u/used/used_before_assignment_nonlocal.py b/tests/functional/u/used/used_before_assignment_nonlocal.py index a3d8ca6517..518f5c27b6 100644 --- a/tests/functional/u/used/used_before_assignment_nonlocal.py +++ b/tests/functional/u/used/used_before_assignment_nonlocal.py @@ -26,17 +26,17 @@ def wrap(): cnt = cnt + 1 # [used-before-assignment] wrap() -def test_fail3(arg: test_fail4): # [used-before-assignment] +def test_fail3(arg: test_fail4): # <3.14:[used-before-assignment] """ Depends on `test_fail4`, in argument annotation. """ return arg -# +1: [used-before-assignment, used-before-assignment] +# +1:<3.14: [used-before-assignment, used-before-assignment] def test_fail4(*args: test_fail5, **kwargs: undefined): """ Depends on `test_fail5` and `undefined` in variable and named arguments annotations. """ return args, kwargs -def test_fail5()->undefined1: # [used-before-assignment] +def test_fail5()->undefined1: # <3.14:[used-before-assignment] """ Depends on `undefined1` in function return annotation. """ def undefined(): diff --git a/tests/functional/u/used/used_before_assignment_typing.314.txt b/tests/functional/u/used/used_before_assignment_typing.314.txt new file mode 100644 index 0000000000..b20844251f --- /dev/null +++ b/tests/functional/u/used/used_before_assignment_typing.314.txt @@ -0,0 +1,15 @@ +undefined-variable:79:20:79:27:MyClass.incorrect_default_method:Undefined variable 'MyClass':UNDEFINED +possibly-used-before-assignment:171:14:171:20:TypeCheckingMultiBranch.defined_in_elif_branch:Possibly using variable 'bisect' before assignment:INFERENCE +possibly-used-before-assignment:172:15:172:23:TypeCheckingMultiBranch.defined_in_elif_branch:Possibly using variable 'calendar' before assignment:INFERENCE +used-before-assignment:175:14:175:22:TypeCheckingMultiBranch.defined_in_else_branch:Using variable 'zoneinfo' before assignment:INFERENCE +used-before-assignment:176:14:176:20:TypeCheckingMultiBranch.defined_in_else_branch:Using variable 'pprint' before assignment:INFERENCE +used-before-assignment:177:14:177:25:TypeCheckingMultiBranch.defined_in_else_branch:Using variable 'collections' before assignment:INFERENCE +possibly-used-before-assignment:181:14:181:19:TypeCheckingMultiBranch.defined_in_nested_if_else:Possibly using variable 'heapq' before assignment:INFERENCE +used-before-assignment:185:14:185:19:TypeCheckingMultiBranch.defined_in_try_except:Using variable 'types' before assignment:INFERENCE +used-before-assignment:186:14:186:18:TypeCheckingMultiBranch.defined_in_try_except:Using variable 'copy' before assignment:INFERENCE +used-before-assignment:187:14:187:21:TypeCheckingMultiBranch.defined_in_try_except:Using variable 'numbers' before assignment:INFERENCE +used-before-assignment:188:15:188:20:TypeCheckingMultiBranch.defined_in_try_except:Using variable 'array' before assignment:INFERENCE +used-before-assignment:191:14:191:19:TypeCheckingMultiBranch.defined_in_loops:Using variable 'email' before assignment:INFERENCE +used-before-assignment:192:14:192:21:TypeCheckingMultiBranch.defined_in_loops:Using variable 'mailbox' before assignment:INFERENCE +used-before-assignment:193:14:193:23:TypeCheckingMultiBranch.defined_in_loops:Using variable 'mimetypes' before assignment:INFERENCE +used-before-assignment:197:14:197:22:TypeCheckingMultiBranch.defined_in_with:Using variable 'binascii' before assignment:INFERENCE diff --git a/tests/functional/u/used/used_before_assignment_typing.py b/tests/functional/u/used/used_before_assignment_typing.py index 2b306cea07..37c6695b0c 100644 --- a/tests/functional/u/used/used_before_assignment_typing.py +++ b/tests/functional/u/used/used_before_assignment_typing.py @@ -1,6 +1,6 @@ """Tests for used-before-assignment for typing related issues""" # pylint: disable=missing-function-docstring,ungrouped-imports,invalid-name - +# pylint: disable=line-too-long from typing import List, NamedTuple, Optional, TYPE_CHECKING @@ -66,12 +66,12 @@ class MyClass: """Type annotation or default values for first level methods can't refer to their own class""" def incorrect_typing_method( - self, other: MyClass # [undefined-variable] + self, other: MyClass # <3.14:[undefined-variable] ) -> bool: return self == other def incorrect_nested_typing_method( - self, other: List[MyClass] # [undefined-variable] + self, other: List[MyClass] # <3.14:[undefined-variable] ) -> bool: return self == other[0] @@ -137,7 +137,7 @@ class MyFourthClass: # pylint: disable=too-few-public-methods """Class to test conditional imports guarded by TYPE_CHECKING two levels up then used in function annotation. See https://github.com/pylint-dev/pylint/issues/7539""" - def is_close(self, comparator: math.isclose, first, second): # [used-before-assignment] + def is_close(self, comparator: math.isclose, first, second): # <3.14:[used-before-assignment] """Conditional imports guarded are only valid for variable annotations.""" comparator(first, second) @@ -150,7 +150,7 @@ class VariableAnnotationsGuardedByTypeChecking: # pylint: disable=too-few-publi and https://github.com/pylint-dev/pylint/issues/7882 """ - still_an_error: datetime.date # [used-before-assignment] + still_an_error: datetime.date # <3.14:[used-before-assignment] def print_date(self, date) -> None: date: datetime.date = date @@ -167,9 +167,9 @@ class ConditionalImportGuardedWhenUsed: # pylint: disable=too-few-public-method class TypeCheckingMultiBranch: # pylint: disable=too-few-public-methods,unused-variable """Test for defines in TYPE_CHECKING if/elif/else branching""" - def defined_in_elif_branch(self) -> calendar.Calendar: # [possibly-used-before-assignment] + def defined_in_elif_branch(self) -> calendar.Calendar: # <3.14:[possibly-used-before-assignment] print(bisect) # [possibly-used-before-assignment] - return calendar.Calendar() + return calendar.Calendar() # >=3.14:[possibly-used-before-assignment] def defined_in_else_branch(self) -> urlopen: print(zoneinfo) # [used-before-assignment] @@ -177,23 +177,23 @@ def defined_in_else_branch(self) -> urlopen: print(collections()) # [used-before-assignment] return urlopen - def defined_in_nested_if_else(self) -> heapq: # [possibly-used-before-assignment] - print(heapq) + def defined_in_nested_if_else(self) -> heapq: # <3.14:[possibly-used-before-assignment] + print(heapq) # >=3.14:[possibly-used-before-assignment] return heapq - def defined_in_try_except(self) -> array: # [used-before-assignment] + def defined_in_try_except(self) -> array: # <3.14:[used-before-assignment] print(types) # [used-before-assignment] print(copy) # [used-before-assignment] print(numbers) # [used-before-assignment] - return array + return array # >=3.14:[used-before-assignment] - def defined_in_loops(self) -> json: # [used-before-assignment] + def defined_in_loops(self) -> json: # <3.14:[used-before-assignment] print(email) # [used-before-assignment] print(mailbox) # [used-before-assignment] print(mimetypes) # [used-before-assignment] return json - def defined_in_with(self) -> base64: # [used-before-assignment] + def defined_in_with(self) -> base64: # <3.14:[used-before-assignment] print(binascii) # [used-before-assignment] return base64