diff --git a/CHANGELOG.md b/CHANGELOG.md index 44395053b..459e31910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Renamed `reduce` method of `Z3Parser` to `parse` - Renamed `test_expr_wrapper` to `test_z3_parser` - Added `is_feasible` attribute for `CFGEdge` and implemented update to edge feasibility based on lists of Z3 constraints +- Refactored codebase to use modern type annotations. Replaced `List` with `list`, `Dict` with `dict`, `Set` with `set`, and `Tuple` with `tuple` - Checked for variable reassignment in `AugAssign` and `AnnAssign` node in parsing edge Z3 constraints - Rendered logically infeasible control flow graph edges in light grey diff --git a/docs/checkers/index.md b/docs/checkers/index.md index a468285a8..8ecb1ca1b 100644 --- a/docs/checkers/index.md +++ b/docs/checkers/index.md @@ -683,7 +683,7 @@ example below is considered to have _six_ nested blocks, not seven. The code above can be fixed using a helper function: ```python -def drop_none(lst: List[Optional[int]]) -> List[int]: +def drop_none(lst: list[Optional[int]]) -> list[int]: """Return a copy of `lst` with all `None` elements removed.""" new_lst = [] for element in lst: @@ -692,8 +692,8 @@ def drop_none(lst: List[Optional[int]]) -> List[int]: return new_lst -def cross_join(x_list: List[Optional[int]], y_list: List[Optional[int]], - z_list: List[Optional[int]]) -> List[Tuple[int, int, int]]: +def cross_join(x_list: list[Optional[int]], y_list: list[Optional[int]], + z_list: list[Optional[int]]) -> list[tuple[int, int, int]]: """Perform an all-by-all join of all elements in the input lists.""" cross_join_list = [] for x in drop_none(x_list): @@ -706,8 +706,8 @@ def cross_join(x_list: List[Optional[int]], y_list: List[Optional[int]], or using list comprehension: ```python -def cross_join(x_list: List[Optional[int]], y_list: List[Optional[int]], - z_list: List[Optional[int]]) -> List[Tuple[int, int, int]]: +def cross_join(x_list: list[Optional[int]], y_list: list[Optional[int]], + z_list: list[Optional[int]]) -> list[tuple[int, int, int]]: """Perform an all-by-all join of all elements in the input lists.""" cross_join_list = [ (x, y, z) @@ -2349,7 +2349,7 @@ lines: 5-11 We can simplify the above code by changing the loop to go over the elements of the list directly: ```python -def sum_items(lst: List[int]) -> int: +def sum_items(lst: list[int]) -> int: """Return the sum of a list of numbers.""" s = 0 for x in lst: @@ -2381,7 +2381,7 @@ One common example is if we want to iterate over two lists in parallel: For loop: ```python -def print_sum(lst1: List[int], lst2: List[int]) -> None: +def print_sum(lst1: list[int], lst2: list[int]) -> None: """Print the sums of each corresponding pair of items in lst1 and lst2. Precondition: lst1 and lst2 have the same length. """ @@ -2392,7 +2392,7 @@ def print_sum(lst1: List[int], lst2: List[int]) -> None: Comprehension: ```python -def parallel_lst(lst1: List[int], lst2: List[int]) -> list: +def parallel_lst(lst1: list[int], lst2: list[int]) -> list: """Return a list of the concatenation of the values of lst1 and lst2 at index i. Precondition: lst1 and lst2 have the same length.""" return [lst1[i] + lst2[i] for i in range(len(lst1))] @@ -2427,7 +2427,7 @@ For example: For loop: ```python -def example1(lst: List[int]) -> int: +def example1(lst: list[int]) -> int: s = 0 for number in lst: # Fixed s += number @@ -2437,7 +2437,7 @@ def example1(lst: List[int]) -> int: Comprehension: ```python -def example7(lst: List[int]) -> List[int]: +def example7(lst: list[int]) -> list[int]: return [number for number in lst] # Fixed ``` @@ -2531,7 +2531,7 @@ Example: To fix these errors, one may make the following changes. ```python -from typing import List +from __future__ import annotations import datetime @@ -2541,7 +2541,7 @@ class Person: def add_two_numbers( x: int, - y: List[float], + y: list[float], z: type = complex ) -> int: return (x + y) * z @@ -2939,9 +2939,10 @@ then check for `None` inside the function body. For example, the following code output: ```python -from typing import List, Optional +from __future__ import annotations +from typing import Optional -def make_list(n: int, lst: Optional[List[int]]=None) -> List[int]: +def make_list(n: int, lst: Optional[list[int]]=None) -> list[int]: if lst is None: lst = [] for i in range(n): @@ -3122,7 +3123,7 @@ Corrected version: class Company: """A company with some employees.""" - def __init__(self, employees: List[str]) -> None: + def __init__(self, employees: list[str]) -> None: self._employees = employees def __len__(self) -> int: diff --git a/examples/custom_checkers/e9984_invalid_for_target.py b/examples/custom_checkers/e9984_invalid_for_target.py index 62e673a07..25a4f5e1a 100644 --- a/examples/custom_checkers/e9984_invalid_for_target.py +++ b/examples/custom_checkers/e9984_invalid_for_target.py @@ -1,8 +1,8 @@ """Examples for E9984: invalid-for-target.""" -from typing import List, Tuple +from __future__ import annotations -def example1(lst: List[int]) -> int: +def example1(lst: list[int]) -> int: """For loop target is an element of a list.""" s = 0 for lst[0] in lst: # Error on this line. lst[0] is highlighted. @@ -10,7 +10,7 @@ def example1(lst: List[int]) -> int: return s -def example2(lst: List[int]) -> List[int]: +def example2(lst: list[int]) -> list[int]: """For loop target is a slice of a list.""" s = [] for lst[0:1] in lst: # Error on this line. lst[0:1] is highlighted. @@ -18,7 +18,7 @@ def example2(lst: List[int]) -> List[int]: return s -def example3(lst: List[int]) -> int: +def example3(lst: list[int]) -> int: """For loop target is an object's attribute""" x = type("EmptyClass", (), {}) s = 0 @@ -27,7 +27,7 @@ def example3(lst: List[int]) -> int: return s -def example4(lst: List[Tuple[int, int]]) -> int: +def example4(lst: list[tuple[int, int]]) -> int: """There are more than one for loop targets.""" s = 0 for lst[0], i in lst: # Error on this line. lst[0] is highlighted. @@ -35,7 +35,7 @@ def example4(lst: List[Tuple[int, int]]) -> int: return s -def example5(lst: List[Tuple[int, int]]) -> int: +def example5(lst: list[tuple[int, int]]) -> int: """Multiple for loop targets are in a list""" s = 0 for [lst[0], i] in lst: # Error on this line. lst[0] is highlighted. @@ -43,7 +43,7 @@ def example5(lst: List[Tuple[int, int]]) -> int: return s -def example6(lst: List[Tuple[int, Tuple[int, int]]]) -> int: +def example6(lst: list[tuple[int, tuple[int, int]]]) -> int: """For loop targets nested in lists or tuples""" s = 0 for i, [j, lst[0]] in lst: # Error on this line. lst[0] is highlighted. @@ -51,17 +51,17 @@ def example6(lst: List[Tuple[int, Tuple[int, int]]]) -> int: return s -def example7(lst: List[int]) -> List[int]: +def example7(lst: list[int]) -> list[int]: """Comprehension target is an element of a list.""" return [lst[0] for lst[0] in lst] # Error on this line. lst[0] is highlighted. -def example8(lst: List[int]) -> List[int]: +def example8(lst: list[int]) -> list[int]: """Comprehension target is a slice of a list.""" return [lst[0] for lst[0:1] in lst] # Error on this line. lst[0:1] is highlighted. -def example9(lst: List[int]) -> list: +def example9(lst: list[int]) -> list: """Comprehension target is an object's attribute""" x = type("EmptyClass", (), {}) return [x.attr for x.attr in lst] # Error on this line. x.attr is highlighted diff --git a/examples/custom_checkers/e9988_shadowing_in_comprehension.py b/examples/custom_checkers/e9988_shadowing_in_comprehension.py index 373ea94b4..f0d9066cc 100644 --- a/examples/custom_checkers/e9988_shadowing_in_comprehension.py +++ b/examples/custom_checkers/e9988_shadowing_in_comprehension.py @@ -1,5 +1,5 @@ """Example for E9988: shadowing-in-comprehension.""" -from typing import List +from __future__ import annotations # There are four different types of comprehensions: @@ -7,7 +7,7 @@ # below are examples with each type of comprehensions in respective order -def num_lst(n: int) -> List[int]: +def num_lst(n: int) -> list[int]: """Return a list of integers from 0 to , in that order.""" return [n for n in range(n)] @@ -28,7 +28,7 @@ def common_items(lst1: list, lst2: list) -> int: return s -def print_pos(lst: List[int]) -> None: +def print_pos(lst: list[int]) -> None: """Print items in lst one by one if they are greater than 0.""" for k in (k for k in lst if k > 0): print(k) diff --git a/examples/custom_checkers/e9994_unnecessary_indexing.py b/examples/custom_checkers/e9994_unnecessary_indexing.py index 8c87481e7..b8eaef632 100644 --- a/examples/custom_checkers/e9994_unnecessary_indexing.py +++ b/examples/custom_checkers/e9994_unnecessary_indexing.py @@ -1,8 +1,8 @@ """Example for E9994: unnecessary-indexing.""" -from typing import List +from __future__ import annotations -def sum_items(lst: List[int]) -> int: +def sum_items(lst: list[int]) -> int: """Return the sum of a list of numbers.""" s = 0 for i in range(len(lst)): # Error on this line (i is highlighted). @@ -11,7 +11,7 @@ def sum_items(lst: List[int]) -> int: return s -def sum_items2(lst: List[int]) -> int: +def sum_items2(lst: list[int]) -> int: """Return the sum of a list of numbers.""" s = 0 for i in range(0, len(lst)): # Error on this line (i is highlighted). @@ -20,7 +20,7 @@ def sum_items2(lst: List[int]) -> int: return s -def sum_items3(lst: List[int]) -> int: +def sum_items3(lst: list[int]) -> int: """Return the sum of a list of numbers.""" s = 0 for i in range(0, len(lst), 1): # Error on this line (i is highlighted). @@ -29,7 +29,7 @@ def sum_items3(lst: List[int]) -> int: return s -def sum_pairs(lst1: List[int], lst2: List[int]) -> int: +def sum_pairs(lst1: list[int], lst2: list[int]) -> int: """Return the sum of corresponding products of two list of numbers.""" s = 0 # NO error reported; the loop index is used to index lst2 as well. @@ -39,7 +39,7 @@ def sum_pairs(lst1: List[int], lst2: List[int]) -> int: return s -def nested_sum(items: List[List[int]]) -> int: +def nested_sum(items: list[list[int]]) -> int: """Return a repeated sum of the items in the list.""" s = 0 for i in range(len(items)): # Error on this line (i is highlighted). @@ -81,7 +81,7 @@ def nested_comprehensions4(items: list) -> None: print([[items[j] for _ in range(10)] for j in [1, 2, 3]]) -def loop_variable_reassigned(items: List[int]) -> int: +def loop_variable_reassigned(items: list[int]) -> int: """Illustrate this checker on a loop where the loop variable is reassigned in the loop body.""" s = 0 diff --git a/examples/custom_checkers/e9995_type_is_assigned.py b/examples/custom_checkers/e9995_type_is_assigned.py index 9c1743f45..2f338af43 100644 --- a/examples/custom_checkers/e9995_type_is_assigned.py +++ b/examples/custom_checkers/e9995_type_is_assigned.py @@ -1,4 +1,4 @@ -from typing import List +from __future__ import annotations import datetime @@ -8,7 +8,7 @@ class Person: def add_two_numbers( x=int, # Error on this line - y=List[float], # Error on this line + y=list[float], # Error on this line z: type = complex # No error on this line ) -> int: return (x + y) * z diff --git a/examples/custom_checkers/r9711_missing_return_statement.py b/examples/custom_checkers/r9711_missing_return_statement.py index 6badbe6b3..a5b514327 100644 --- a/examples/custom_checkers/r9711_missing_return_statement.py +++ b/examples/custom_checkers/r9711_missing_return_statement.py @@ -1,7 +1,8 @@ -from typing import List, Optional +from __future__ import annotations +from typing import Optional -def index_of(numbers: List[int], n: int) -> Optional[int]: +def index_of(numbers: list[int], n: int) -> Optional[int]: """Return the index of the first occurrence of n in numbers, or None if n doesn't appear in the list. """ diff --git a/examples/ending_locations/subscript.py b/examples/ending_locations/subscript.py index e454f0d3d..3fb16c851 100644 --- a/examples/ending_locations/subscript.py +++ b/examples/ending_locations/subscript.py @@ -1,3 +1,5 @@ +from __future__ import annotations + y[0] z [ 0 ] a[:] @@ -16,8 +18,5 @@ x = m[ : ] -from typing import List - - -def add(numbers: List[int]) -> List[int]: +def add(numbers: list[int]) -> list[int]: return numbers + [1] diff --git a/examples/pylint/e0103_not_in_loop.py b/examples/pylint/e0103_not_in_loop.py index 2e7083b22..178dad80e 100644 --- a/examples/pylint/e0103_not_in_loop.py +++ b/examples/pylint/e0103_not_in_loop.py @@ -1,6 +1,6 @@ -from typing import List +from __future__ import annotations -def add(lst: List[int]) -> int: +def add(lst: list[int]) -> int: """Calculate the sum of the elements in the given list.""" temp = 0 for item in lst: diff --git a/examples/pylint/e0104_return_outside_function.py b/examples/pylint/e0104_return_outside_function.py index 4e85d38f1..955ddbee7 100644 --- a/examples/pylint/e0104_return_outside_function.py +++ b/examples/pylint/e0104_return_outside_function.py @@ -1,6 +1,6 @@ -from typing import List +from __future__ import annotations -def add(lst: List[int]) -> None: +def add(lst: list[int]) -> None: """Calculate the sum of the elements in the given list.""" temp = 0 for item in lst: diff --git a/examples/pylint/e0108_duplicate_argument_name.py b/examples/pylint/e0108_duplicate_argument_name.py index 0ace7d6e5..892a913aa 100644 --- a/examples/pylint/e0108_duplicate_argument_name.py +++ b/examples/pylint/e0108_duplicate_argument_name.py @@ -1,6 +1,6 @@ -from typing import List +from __future__ import annotations -def add(lst: List[int], lst: List[int]) -> int: # Error on this line +def add(lst: list[int], lst: list[int]) -> int: # Error on this line """Calculate the sum of the elements in the given list.""" temp = 0 for item in lst: diff --git a/examples/pylint/e0303_invalid_length_returned.py b/examples/pylint/e0303_invalid_length_returned.py index 7f0e4c5ec..32ac09e91 100644 --- a/examples/pylint/e0303_invalid_length_returned.py +++ b/examples/pylint/e0303_invalid_length_returned.py @@ -1,9 +1,9 @@ -from typing import List +from __future__ import annotations class Company: """A company with some employees.""" - def __init__(self, employees: List[str]) -> None: + def __init__(self, employees: list[str]) -> None: self._employees = employees def __len__(self) -> int: diff --git a/examples/pylint/e0304_invalid_bool_returned.py b/examples/pylint/e0304_invalid_bool_returned.py index 066421e20..aac279e43 100644 --- a/examples/pylint/e0304_invalid_bool_returned.py +++ b/examples/pylint/e0304_invalid_bool_returned.py @@ -1,9 +1,9 @@ -from typing import List +from __future__ import annotations class Company: """A company with some employees.""" - def __init__(self, employees: List[str]) -> None: + def __init__(self, employees: list[str]) -> None: self._employees = employees def __bool__(self) -> bool: diff --git a/examples/pylint/e0306_invalid_repr_returned.py b/examples/pylint/e0306_invalid_repr_returned.py index 404d888e6..a70c05d5c 100644 --- a/examples/pylint/e0306_invalid_repr_returned.py +++ b/examples/pylint/e0306_invalid_repr_returned.py @@ -1,9 +1,9 @@ -from typing import List +from __future__ import annotations class Company: """A company with some employees.""" - def __init__(self, employees: List[str]) -> None: + def __init__(self, employees: list[str]) -> None: self._employees = employees def __repr__(self) -> str: diff --git a/examples/pylint/e0307_invalid_str_returned.py b/examples/pylint/e0307_invalid_str_returned.py index a089812ae..ba4375802 100644 --- a/examples/pylint/e0307_invalid_str_returned.py +++ b/examples/pylint/e0307_invalid_str_returned.py @@ -1,9 +1,9 @@ -from typing import List +from __future__ import annotations class Company: """A company with some employees.""" - def __init__(self, employees: List[str]) -> None: + def __init__(self, employees: list[str]) -> None: self._employees = employees def __str__(self) -> str: diff --git a/examples/pylint/e0632_unbalanced_tuple_unpacking.py b/examples/pylint/e0632_unbalanced_tuple_unpacking.py index 9b5a86783..1fd17f7aa 100644 --- a/examples/pylint/e0632_unbalanced_tuple_unpacking.py +++ b/examples/pylint/e0632_unbalanced_tuple_unpacking.py @@ -1,6 +1,6 @@ -from typing import Tuple +from __future__ import annotations -def set_values() -> Tuple[int, int]: +def set_values() -> tuple[int, int]: """Return a tuple of two integers.""" var1 = 1 var2 = 2 diff --git a/examples/pylint/e1111_assignment_from_no_return.py b/examples/pylint/e1111_assignment_from_no_return.py index 9dd25802d..2deea45b7 100644 --- a/examples/pylint/e1111_assignment_from_no_return.py +++ b/examples/pylint/e1111_assignment_from_no_return.py @@ -1,6 +1,6 @@ -from typing import List +from __future__ import annotations -def add_fruit(fruit_basket: List[str], fruit: str) -> None: +def add_fruit(fruit_basket: list[str], fruit: str) -> None: """Add fruit to fruit_basket.""" fruit_basket.append(fruit) diff --git a/examples/pylint/e1128_assignment_from_none.py b/examples/pylint/e1128_assignment_from_none.py index bb73ba1dd..ee2014a0d 100644 --- a/examples/pylint/e1128_assignment_from_none.py +++ b/examples/pylint/e1128_assignment_from_none.py @@ -1,6 +1,6 @@ -from typing import List +from __future__ import annotations -def add_fruit(fruit_basket: List[str], fruit: str) -> None: +def add_fruit(fruit_basket: list[str], fruit: str) -> None: """Add fruit to fruit_basket.""" fruit_basket.append(fruit) return None diff --git a/examples/pylint/e1138_unsupported_delete_operation.py b/examples/pylint/e1138_unsupported_delete_operation.py index 3eec3e309..9a69da67d 100644 --- a/examples/pylint/e1138_unsupported_delete_operation.py +++ b/examples/pylint/e1138_unsupported_delete_operation.py @@ -1,9 +1,9 @@ -from typing import List +from __future__ import annotations class NamedList: """A contaner class for storing a list of named integers.""" - def __init__(self, names: List[str], values: List[int]) -> None: + def __init__(self, names: list[str], values: list[int]) -> None: self._names = names self._values = values diff --git a/examples/pylint/r1702_too_many_nested_blocks.py b/examples/pylint/r1702_too_many_nested_blocks.py index 83e545265..b2f2d4077 100644 --- a/examples/pylint/r1702_too_many_nested_blocks.py +++ b/examples/pylint/r1702_too_many_nested_blocks.py @@ -1,8 +1,9 @@ """Example for too many nested blocks""" -from typing import List, Tuple, Optional +from __future__ import annotations +from typing import Optional -def cross_join(x_list: List[Optional[int]], y_list: List[Optional[int]], - z_list: List[Optional[int]]) -> List[Tuple[int, int, int]]: +def cross_join(x_list: list[Optional[int]], y_list: list[Optional[int]], + z_list: list[Optional[int]]) -> list[tuple[int, int, int]]: """Perform an all-by-all join of all elements in the input lists. Note: This function skips elements which are None. diff --git a/examples/pylint/w0101_unreachable.py b/examples/pylint/w0101_unreachable.py index 8e28e61f2..4b915f376 100644 --- a/examples/pylint/w0101_unreachable.py +++ b/examples/pylint/w0101_unreachable.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations -def add(lst: List[int]) -> int: +def add(lst: list[int]) -> int: """Return the sum of the elements in the given list.""" temp = 0 for item in lst: diff --git a/examples/pylint/w0102_dangerous_default_value.py b/examples/pylint/w0102_dangerous_default_value.py index 5849a467d..5d3c13ff1 100644 --- a/examples/pylint/w0102_dangerous_default_value.py +++ b/examples/pylint/w0102_dangerous_default_value.py @@ -1,6 +1,6 @@ -from typing import List +from __future__ import annotations -def make_list(n: int, lst: List[int]=[]) -> List[int]: +def make_list(n: int, lst: list[int]=[]) -> list[int]: for i in range(n): lst.append(i) return lst diff --git a/examples/pylint/w0104_pointless_statement.py b/examples/pylint/w0104_pointless_statement.py index a251229c6..3cfb3c830 100644 --- a/examples/pylint/w0104_pointless_statement.py +++ b/examples/pylint/w0104_pointless_statement.py @@ -1,6 +1,6 @@ -from typing import List +from __future__ import annotations -def add(lst: List[int]) -> int: +def add(lst: list[int]) -> int: """Calculate the sum of the elements in the given list.""" temp = 0 for item in lst: diff --git a/examples/pylint/w0107_unnecessary_pass.py b/examples/pylint/w0107_unnecessary_pass.py index f6023a80a..b732d6df5 100644 --- a/examples/pylint/w0107_unnecessary_pass.py +++ b/examples/pylint/w0107_unnecessary_pass.py @@ -1,6 +1,6 @@ -from typing import List +from __future__ import annotations -def add(lst: List[int]) -> int: +def add(lst: list[int]) -> int: """Calculate the sum of the elements in the given list.""" temp = 0 for item in lst: diff --git a/examples/pylint/w1503_redundant_unittest_assert.py b/examples/pylint/w1503_redundant_unittest_assert.py index 43e45cac6..849903b33 100644 --- a/examples/pylint/w1503_redundant_unittest_assert.py +++ b/examples/pylint/w1503_redundant_unittest_assert.py @@ -1,7 +1,7 @@ -from typing import List +from __future__ import annotations import unittest -def is_sorted(lst: List[float]) -> bool: +def is_sorted(lst: list[float]) -> bool: """Check if is sorted in ascending order.""" return lst == sorted(lst) diff --git a/examples/sample_usage/pyta_stats.py b/examples/sample_usage/pyta_stats.py index 71a50c447..494de09dd 100644 --- a/examples/sample_usage/pyta_stats.py +++ b/examples/sample_usage/pyta_stats.py @@ -47,9 +47,9 @@ def pyta_statistics(directory, config=""): def _print_stats(individual_stats, summary_stats): """Pretty prints the statistics aggregated by summary. - @param OrderedDict[str, List] individual_stats: + @param OrderedDict[str, list] individual_stats: stats computed per student/group - @param OrderedDict[str, List | OrderedDict] summary_stats: + @param OrderedDict[str, list | OrderedDict] summary_stats: stats computed over all the students @rtype: dict @@ -157,7 +157,7 @@ def _print_stats(individual_stats, summary_stats): def _print_top_errors(stats, tabs=1, aggregate=True): """Pretty prints a list of most frequent errors and their counts. - @param List[Tuple[str, int | Tuple]] stats: the errors & counts to print + @param list[tuple[str, int | tuple]] stats: the errors & counts to print @param int tabs: the number of tabs to print before every line @param bool aggregate: whether stats are aggregate (i.e.: no percentages) @rtype: None diff --git a/examples/sample_usage/stats_analysis.py b/examples/sample_usage/stats_analysis.py index 0ebb12680..821ff7a62 100644 --- a/examples/sample_usage/stats_analysis.py +++ b/examples/sample_usage/stats_analysis.py @@ -8,9 +8,9 @@ def _individual_calc(error_msgs, style_msgs): Analyses the given lists of error and style Message objects error_msgs and style_msgs for an individual. - @param List[Message] error_msgs: all of this individual's code errors - @param List[Message] style_msgs: all of this individual's style issues - @rtype: List[Tuple[str, List]] + @param list[Message] error_msgs: all of this individual's code errors + @param list[Message] style_msgs: all of this individual's style issues + @rtype: list[tuple[str, list]] """ # {msg.symbol + "(" + msg.object + ")": count} all_msgs = error_msgs + style_msgs @@ -38,9 +38,9 @@ def summary(all_msgs): overall summary of the course's performance (if applicable). Called by pyta_statistics. - @param OrderedDict[str, Tuple[List[Message], List[Message]]] all_msgs: + @param OrderedDict[str, tuple[list[Message], list[Message]]] all_msgs: the tuple of code and error messages for each student's files - @rtype: Tuple[OrderedDict[str, List]]] + @rtype: tuple[OrderedDict[str, list]]] """ num_stu = len(all_msgs) @@ -110,8 +110,8 @@ def summary(all_msgs): def _calc_helper(msgs): """Returns frequent messages in numbers and in percentages. - @param List[Message] msgs: Message objects for all errors found by linters - @rtype: List[List] + @param list[Message] msgs: Message objects for all errors found by linters + @rtype: list[list] """ # get dict of values {id:int, id2:int} msgs_dict = _message_counter(msgs) @@ -128,8 +128,8 @@ def _calc_helper(msgs): def _message_counter(msgs): """Return the number of errors for every type of error in msgs. - @param List[Message] msgs: the messages to count - @rtype: Dict[str, int] + @param list[Message] msgs: the messages to count + @rtype: dict[str, int] """ msgs_dict = {} @@ -146,9 +146,9 @@ def _frequent_messages(comp_dict, top=5): list. Return most frequently occurring errors. - @type comp_dict: Dict[str, number] + @type comp_dict: dict[str, number] @type top: int - @rtype: List[Tuple[str, number]] + @rtype: list[tuple[str, number]] """ # get key-value pair in a list most_frequently = list(comp_dict.items()) diff --git a/python_ta/__init__.py b/python_ta/__init__.py index 8add9b9d3..a4a84f490 100644 --- a/python_ta/__init__.py +++ b/python_ta/__init__.py @@ -15,8 +15,9 @@ python_ta.check_all() """ -__version__ = "2.8.2.dev" # Version number +from __future__ import annotations +__version__ = "2.8.2.dev" # Version number # First, remove underscore from builtins if it has been bound in the REPL. # Must appear before other imports from pylint/python_ta. import builtins @@ -36,7 +37,7 @@ import webbrowser from builtins import FileNotFoundError from os import listdir -from typing import AnyStr, Generator, List, Optional, TextIO, Union +from typing import AnyStr, Generator, Optional, TextIO, Union import pylint.config import pylint.lint @@ -64,7 +65,7 @@ def check_errors( - module_name: Union[List[str], str] = "", + module_name: Union[list[str], str] = "", config: Union[dict, str] = "", output: Optional[TextIO] = None, load_default_config: bool = True, @@ -82,7 +83,7 @@ def check_errors( def check_all( - module_name: Union[List[str], str] = "", + module_name: Union[list[str], str] = "", config: Union[dict, str] = "", output: Optional[TextIO] = None, load_default_config: bool = True, @@ -100,7 +101,7 @@ def check_all( def _check( - module_name: Union[List[str], str] = "", + module_name: Union[list[str], str] = "", level: str = "all", local_config: Union[dict, str] = "", output: Optional[TextIO] = None, @@ -441,7 +442,7 @@ def _verify_pre_check(filepath: AnyStr, allow_pylint_comments: bool) -> bool: return True -def _get_valid_files_to_check(module_name: Union[List[str], str]) -> Generator[AnyStr, None, None]: +def _get_valid_files_to_check(module_name: Union[list[str], str]) -> Generator[AnyStr, None, None]: """A generator for all valid files to check.""" # Allow call to check with empty args if module_name == "": diff --git a/python_ta/__main__.py b/python_ta/__main__.py index 2c55b14e8..914abba5e 100644 --- a/python_ta/__main__.py +++ b/python_ta/__main__.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import sys from os import path -from typing import List, Optional +from typing import Optional import click @@ -41,7 +43,7 @@ def main( version: bool, config: Optional[str], errors_only: bool, - filenames: List[str], + filenames: list[str], exit_zero: bool, generate_config: bool, output_format: str, diff --git a/python_ta/cfg/cfg_generator.py b/python_ta/cfg/cfg_generator.py index 6a935bc43..e8b0189de 100644 --- a/python_ta/cfg/cfg_generator.py +++ b/python_ta/cfg/cfg_generator.py @@ -2,10 +2,12 @@ Provides a function to generate and display the control flow graph of a given module. """ +from __future__ import annotations + import importlib.util import os.path import sys -from typing import Any, Dict, Optional, Set +from typing import Any, Optional import graphviz from astroid import nodes @@ -21,7 +23,7 @@ def generate_cfg( - mod: str = "", auto_open: bool = False, visitor_options: Optional[Dict[str, Any]] = None + mod: str = "", auto_open: bool = False, visitor_options: Optional[dict[str, Any]] = None ) -> None: """Generate a control flow graph for the given module. @@ -45,7 +47,7 @@ def generate_cfg( def _generate( - mod: str = "", auto_open: bool = False, visitor_options: Optional[Dict[str, Any]] = None + mod: str = "", auto_open: bool = False, visitor_options: Optional[dict[str, Any]] = None ) -> None: """Generate a control flow graph for the given module. @@ -95,7 +97,7 @@ def _get_valid_file_path(mod: str = "") -> Optional[str]: def _display( - cfgs: Dict[nodes.NodeNG, ControlFlowGraph], filename: str, auto_open: bool = False + cfgs: dict[nodes.NodeNG, ControlFlowGraph], filename: str, auto_open: bool = False ) -> None: graph = graphviz.Digraph(name=filename, **GRAPH_OPTIONS) for node, cfg in cfgs.items(): @@ -119,7 +121,7 @@ def _display( graph.render(filename, view=auto_open) -def _visit(block: CFGBlock, graph: graphviz.Digraph, visited: Set[int], end: CFGBlock) -> None: +def _visit(block: CFGBlock, graph: graphviz.Digraph, visited: set[int], end: CFGBlock) -> None: """ Visit a CFGBlock and add it to the control flow graph. """ diff --git a/python_ta/checkers/global_variables_checker.py b/python_ta/checkers/global_variables_checker.py index 5b71a0e32..46a3f32b2 100644 --- a/python_ta/checkers/global_variables_checker.py +++ b/python_ta/checkers/global_variables_checker.py @@ -1,8 +1,9 @@ """Checker for global variables """ +from __future__ import annotations + import re -from typing import List from astroid import nodes from pylint.checkers import BaseChecker @@ -81,7 +82,7 @@ def _inspect_vars(self, node: nodes.NodeNG) -> None: self.add_message("forbidden-global-variables", node=node, args=args) -def _get_child_disallowed_global_var_nodes(node: nodes.NodeNG) -> List[nodes.NodeNG]: +def _get_child_disallowed_global_var_nodes(node: nodes.NodeNG) -> list[nodes.NodeNG]: """Return a list of all top-level Name or AssignName nodes for a given global, non-constant and non-type alias variable. """ diff --git a/python_ta/checkers/invalid_name_checker.py b/python_ta/checkers/invalid_name_checker.py index d5f47724e..450a74c22 100644 --- a/python_ta/checkers/invalid_name_checker.py +++ b/python_ta/checkers/invalid_name_checker.py @@ -1,7 +1,9 @@ """Checker used for identifying names that don't conform to Python naming conventions.""" +from __future__ import annotations + import re -from typing import List, Optional +from typing import Optional from astroid import nodes from pylint.checkers import BaseChecker, utils @@ -116,7 +118,7 @@ def _ignore_name(name: str, pattern: re.Pattern) -> bool: return pattern.pattern and pattern.match(name) is not None -def _check_module_name(_node_type: str, name: str) -> List[str]: +def _check_module_name(_node_type: str, name: str) -> list[str]: """Returns a list of strings, each detailing how `name` violates Python naming conventions for module names. @@ -132,7 +134,7 @@ def _check_module_name(_node_type: str, name: str) -> List[str]: return error_msgs -def _check_const_name(node_type: str, name: str) -> List[str]: +def _check_const_name(node_type: str, name: str) -> list[str]: """Returns a list of strings, each detailing how `name` violates Python naming conventions for constant and class constant names. @@ -152,7 +154,7 @@ def _check_const_name(node_type: str, name: str) -> List[str]: return error_msgs -def _check_class_name(_node_type: str, name: str) -> List[str]: +def _check_class_name(_node_type: str, name: str) -> list[str]: """Returns a list of strings, each detailing how `name` violates Python naming conventions for class names. @@ -170,7 +172,7 @@ class names. return error_msgs -def _check_function_and_variable_name(node_type: str, name: str) -> List[str]: +def _check_function_and_variable_name(node_type: str, name: str) -> list[str]: """Returns a list of strings, each detailing how `name` violates Python naming conventions for function and variable names. @@ -188,7 +190,7 @@ def _check_function_and_variable_name(node_type: str, name: str) -> List[str]: return error_msgs -def _check_method_and_attr_name(node_type: str, name: str) -> List[str]: +def _check_method_and_attr_name(node_type: str, name: str) -> list[str]: """Returns a list of strings, each detailing how `name` violates Python naming conventions for method and instance or class attribute names. @@ -208,7 +210,7 @@ def _check_method_and_attr_name(node_type: str, name: str) -> List[str]: return error_msgs -def _check_argument_name(_node_type: str, name: str) -> List[str]: +def _check_argument_name(_node_type: str, name: str) -> list[str]: """Returns a list of strings, each detailing how `name` violates Python naming conventions for argument names. @@ -226,7 +228,7 @@ def _check_argument_name(_node_type: str, name: str) -> List[str]: return error_msgs -def _check_typevar_name(_node_type: str, name: str) -> List[str]: +def _check_typevar_name(_node_type: str, name: str) -> list[str]: """Returns a list of strings, each detailing how `name` violates Python naming conventions for type variable names. @@ -243,7 +245,7 @@ def _check_typevar_name(_node_type: str, name: str) -> List[str]: return error_msgs -def _check_type_alias_name(_node_type: str, name: str) -> List[str]: +def _check_type_alias_name(_node_type: str, name: str) -> list[str]: """Returns a list of strings, each detailing how `name` violates Python naming conventions for type alias names. diff --git a/python_ta/checkers/possibly_undefined_checker.py b/python_ta/checkers/possibly_undefined_checker.py index ac487a3db..c9e576667 100644 --- a/python_ta/checkers/possibly_undefined_checker.py +++ b/python_ta/checkers/possibly_undefined_checker.py @@ -1,8 +1,10 @@ """Checker for variables that might not be defined in the program. """ +from __future__ import annotations + from itertools import chain -from typing import Generator, Set, Union +from typing import Generator, Union from astroid import nodes from pylint.checkers import BaseChecker, utils @@ -27,7 +29,7 @@ class PossiblyUndefinedChecker(BaseChecker): def __init__(self, linter=None) -> None: super().__init__(linter=linter) - self._possibly_undefined: Set[nodes.Name] = set() + self._possibly_undefined: set[nodes.Name] = set() @only_required_for_messages("possibly-undefined") def visit_name(self, node: nodes.Name) -> None: @@ -74,7 +76,7 @@ def _analyze(self, node: Union[nodes.Module, nodes.FunctionDef]) -> None: out_facts[b] = temp worklist.extend([succ.target for succ in b.successors]) - def _transfer(self, block: CFGBlock, in_facts: Set[str], local_vars: Set[str]) -> Set[str]: + def _transfer(self, block: CFGBlock, in_facts: set[str], local_vars: set[str]) -> set[str]: gen = in_facts.copy() kill = set() for statement in block.statements: @@ -97,7 +99,7 @@ def _transfer(self, block: CFGBlock, in_facts: Set[str], local_vars: Set[str]) - self._possibly_undefined.remove(node) return gen.difference(kill) - def _get_assigns(self, node: Union[nodes.FunctionDef, nodes.Module]) -> Set[str]: + def _get_assigns(self, node: Union[nodes.FunctionDef, nodes.Module]) -> set[str]: """Returns a set of all local and parameter variables that could be defined in the program (either a function or module). diff --git a/python_ta/checkers/pycodestyle_checker.py b/python_ta/checkers/pycodestyle_checker.py index 95a56876f..508a771a9 100644 --- a/python_ta/checkers/pycodestyle_checker.py +++ b/python_ta/checkers/pycodestyle_checker.py @@ -1,6 +1,6 @@ """Checker for the style of the file""" -from typing import List, Tuple +from __future__ import annotations import pycodestyle from astroid import nodes @@ -41,7 +41,7 @@ def process_module(self, node: nodes.NodeNG) -> None: class JSONReport(pycodestyle.StandardReport): - def get_file_results(self) -> List[Tuple]: + def get_file_results(self) -> list[tuple]: self._deferred_print.sort() return [ (line_number, f"line {line_number}, column {offset}: {text}", code) diff --git a/python_ta/checkers/redundant_assignment_checker.py b/python_ta/checkers/redundant_assignment_checker.py index 1f60b39f2..4a8380cb9 100644 --- a/python_ta/checkers/redundant_assignment_checker.py +++ b/python_ta/checkers/redundant_assignment_checker.py @@ -9,7 +9,9 @@ the behavior of the program. """ -from typing import Set, Union +from __future__ import annotations + +from typing import Union from astroid import nodes from pylint.checkers import BaseChecker @@ -40,7 +42,7 @@ class RedundantAssignmentChecker(BaseChecker): def __init__(self, linter=None) -> None: super().__init__(linter=linter) - self._redundant_assignment: Set[nodes.Assign] = set() + self._redundant_assignment: set[nodes.Assign] = set() @only_required_for_messages("redundant-assignment") def visit_assign(self, node: nodes.Assign) -> None: @@ -93,7 +95,7 @@ def _analyze(self, node: Union[nodes.Module, nodes.FunctionDef]) -> None: out_facts[b] = temp worklist.extend([pred.source for pred in b.predecessors if pred.source.reachable]) - def _transfer(self, block: CFGBlock, out_facts: Set[str]) -> Set[str]: + def _transfer(self, block: CFGBlock, out_facts: set[str]) -> set[str]: gen = out_facts.copy() kill = set() for statement in reversed(block.statements): @@ -131,7 +133,7 @@ def _transfer(self, block: CFGBlock, out_facts: Set[str]) -> Set[str]: return gen.difference(kill) - def _get_assigns(self, node: Union[nodes.FunctionDef, nodes.Module]) -> Set[str]: + def _get_assigns(self, node: Union[nodes.FunctionDef, nodes.Module]) -> set[str]: """Returns a set of all local and parameter variables that could be defined in the program (either a function or module). diff --git a/python_ta/checkers/unnecessary_indexing_checker.py b/python_ta/checkers/unnecessary_indexing_checker.py index 31e824b99..cc73d9e13 100644 --- a/python_ta/checkers/unnecessary_indexing_checker.py +++ b/python_ta/checkers/unnecessary_indexing_checker.py @@ -1,7 +1,9 @@ """Checker for unnecessary indexing in a loop. """ -from typing import List, Optional, Union +from __future__ import annotations + +from typing import Optional, Union from astroid import nodes from astroid.const import Context @@ -148,7 +150,7 @@ def _is_redundant( def _index_name_nodes( index: str, loop_node: Union[nodes.For, nodes.Comprehension] -) -> List[Union[nodes.AssignName, nodes.Name]]: +) -> list[Union[nodes.AssignName, nodes.Name]]: """Return a list of AssignName and Name nodes contained in the body of . Remove uses of variables that shadow . diff --git a/python_ta/contracts/__init__.py b/python_ta/contracts/__init__.py index 1e9889f6d..7ed95357b 100644 --- a/python_ta/contracts/__init__.py +++ b/python_ta/contracts/__init__.py @@ -10,12 +10,14 @@ [(assertion, compiled, return_val_var_name)]. """ +from __future__ import annotations + import inspect import logging import sys import typing from types import CodeType, FunctionType, ModuleType -from typing import Any, Callable, List, Optional, Set, Tuple, TypeVar, Union, overload +from typing import Any, Callable, Optional, TypeVar, Union, overload import wrapt from typeguard import CollectionCheckStrategy, TypeCheckError, check_type @@ -103,16 +105,16 @@ def _enable_function_contracts(wrapped, instance, args, kwargs): @overload def check_contracts( - func: FunctionType, module_names: Optional[Set[str]] = None + func: FunctionType, module_names: Optional[set[str]] = None ) -> FunctionType: ... @overload -def check_contracts(func: Class, module_names: Optional[Set[str]] = None) -> Class: ... +def check_contracts(func: Class, module_names: Optional[set[str]] = None) -> Class: ... def check_contracts( - func_or_class: Union[Class, FunctionType], module_names: Optional[Set[str]] = None + func_or_class: Union[Class, FunctionType], module_names: Optional[set[str]] = None ) -> Union[Class, FunctionType]: """A decorator to enable contract checking for a function or class. @@ -250,7 +252,7 @@ def _check_function_contracts(wrapped, instance, args, kwargs): # Check function preconditions if not hasattr(target, "__preconditions__"): - target.__preconditions__: List[Tuple[str, CodeType]] = [] + target.__preconditions__: list[tuple[str, CodeType]] = [] preconditions = parse_assertions(wrapped) for precondition in preconditions: try: @@ -280,7 +282,7 @@ def _check_function_contracts(wrapped, instance, args, kwargs): # Check function postconditions if not hasattr(target, "__postconditions__"): - target.__postconditions__: List[Tuple[str, CodeType, str]] = [] + target.__postconditions__: list[tuple[str, CodeType, str]] = [] return_val_var_name = _get_legal_return_val_var_name( {**wrapped.__globals__, **function_locals} ) @@ -511,7 +513,7 @@ def _check_assertions( ) -def parse_assertions(obj: Any, parse_token: str = "Precondition") -> List[str]: +def parse_assertions(obj: Any, parse_token: str = "Precondition") -> list[str]: """Return a list of preconditions/postconditions/representation invariants parsed from the given entity's docstring. Uses parse_token to determine what to look for. parse_token defaults to Precondition. @@ -638,7 +640,7 @@ def _debug(msg: str) -> None: def _set_invariants(klass: type) -> None: """Retrieve and set the representation invariants of this class""" # Update representation invariants from this class' docstring and those of its superclasses. - rep_invariants: List[Tuple[str, CodeType]] = [] + rep_invariants: list[tuple[str, CodeType]] = [] # Iterate over all inherited classes except builtins for cls in reversed(klass.__mro__): diff --git a/python_ta/contracts/__main__.py b/python_ta/contracts/__main__.py index 71565e1d5..35e2c621d 100644 --- a/python_ta/contracts/__main__.py +++ b/python_ta/contracts/__main__.py @@ -1,8 +1,10 @@ +from __future__ import annotations + import os.path import sys import traceback import types -from typing import List, TextIO, Tuple +from typing import TextIO import click @@ -13,7 +15,7 @@ @click.argument("file", type=click.File(mode="r")) @click.option("--extra-mod-name", "-e", multiple=True, help="Name of imported module to also check") @click.option("--no-decorate-main", is_flag=True, default=True, help="Disable decorating FILE") -def check_contracts(file: TextIO, extra_mod_name: Tuple, no_decorate_main: bool): +def check_contracts(file: TextIO, extra_mod_name: tuple, no_decorate_main: bool): """Run FILE as Python script with PythonTA's contract checking enabled. FILE the Python script as if you were to just run `python FILE` @@ -51,8 +53,8 @@ def __init__( self, name: str, main_lineno: int, - lines: List[str], - extra_mod_names: Tuple, + lines: list[str], + extra_mod_names: tuple, no_main: bool, ) -> None: super().__init__(name) diff --git a/python_ta/reporters/core.py b/python_ta/reporters/core.py index dfd720590..201847e37 100644 --- a/python_ta/reporters/core.py +++ b/python_ta/reporters/core.py @@ -1,12 +1,14 @@ """This module provides the core functionality for all PythonTA reporters. """ +from __future__ import annotations + import os.path import sys from collections import defaultdict from datetime import datetime from pathlib import Path -from typing import Dict, List, Optional, Tuple +from typing import Optional from astroid import NodeNG from pylint.message import Message @@ -28,7 +30,7 @@ def __init__(self, message: Message, node: NodeNG, snippet: Optional[str]) -> No def __getattr__(self, item): return getattr(self.message, item) - def to_dict(self) -> Dict: + def to_dict(self) -> dict: """Return a dictionary containing the fields of this message. Useful for JSON output. @@ -68,7 +70,7 @@ class PythonTaReporter(BaseReporter): _AFTER_NUM_SPACES = 2 # The error messages to report, mapping filename to a list of messages. - messages: Dict[str, List[Message]] + messages: dict[str, list[Message]] def __init__(self) -> None: """Initialize this reporter.""" @@ -129,8 +131,8 @@ def handle_node(self, msg_definition: MessageDefinition, node: NodeNG) -> None: curr_messages[-1] = NewMessage(msg, node, snippet) def group_messages( - self, messages: List[Message] - ) -> Tuple[Dict[str, List[Message]], Dict[str, List[Message]]]: + self, messages: list[Message] + ) -> tuple[dict[str, list[Message]], dict[str, list[Message]]]: """Group messages for the current file by their (error/style) and type (msg_id).""" error_msgs_by_type = defaultdict(list) style_msgs_by_type = defaultdict(list) diff --git a/python_ta/reporters/json_reporter.py b/python_ta/reporters/json_reporter.py index 76e3bdc79..a7e81d1b2 100644 --- a/python_ta/reporters/json_reporter.py +++ b/python_ta/reporters/json_reporter.py @@ -1,5 +1,6 @@ +from __future__ import annotations + import json -from typing import Dict, List from pylint.reporters.ureports.nodes import BaseLayout @@ -16,7 +17,7 @@ class JSONReporter(PythonTaReporter): OUTPUT_FILENAME = "pyta_report.json" - messages: Dict[str, List[NewMessage]] + messages: dict[str, list[NewMessage]] def display_messages(self, layout: BaseLayout) -> None: """Hook for displaying the messages of the reporter @@ -39,7 +40,7 @@ def display_messages(self, layout: BaseLayout) -> None: self.writeln(json.dumps(output, indent=4)) - def _output_messages(self, msgs: List[NewMessage]) -> List[Dict]: + def _output_messages(self, msgs: list[NewMessage]) -> list[dict]: """Returns a list of dictionaries containing formatted error messages.""" max_messages = self.linter.config.pyta_number_of_messages num_occurrences = {msg.message.msg_id: 0 for msg in msgs} diff --git a/python_ta/reporters/plain_reporter.py b/python_ta/reporters/plain_reporter.py index aaa0aa495..5a1f85836 100644 --- a/python_ta/reporters/plain_reporter.py +++ b/python_ta/reporters/plain_reporter.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from __future__ import annotations from .core import NewMessage, PythonTaReporter from .node_printers import LineType @@ -47,7 +47,7 @@ def print_messages(self, level: str = "all") -> None: self.writeln(result) - def _colour_messages_by_type(self, messages: Dict[str, List[NewMessage]]) -> str: + def _colour_messages_by_type(self, messages: dict[str, list[NewMessage]]) -> str: """ Return string of properly formatted members of the messages dict (error or style) indicated by style. diff --git a/python_ta/reporters/stat_reporter.py b/python_ta/reporters/stat_reporter.py index 25ea9231e..3eb5983e1 100644 --- a/python_ta/reporters/stat_reporter.py +++ b/python_ta/reporters/stat_reporter.py @@ -10,7 +10,7 @@ def __init__(self, source_lines=None): Clear the two class-level message lists. - @type source_lines: List[str] + @type source_lines: list[str] @rtype: None """ super().__init__(source_lines) diff --git a/python_ta/transforms/z3_visitor.py b/python_ta/transforms/z3_visitor.py index b31162687..e923ca66d 100644 --- a/python_ta/transforms/z3_visitor.py +++ b/python_ta/transforms/z3_visitor.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import astroid from astroid import AstroidError, nodes from astroid.transforms import TransformVisitor @@ -30,7 +32,7 @@ def set_function_def_z3_constraints(self, node: nodes.FunctionDef): for ann, arg in zip(annotations, arguments): if ann is None: continue - # TODO: what to do about subscripts ex. Set[int], List[Set[int]], ... + # TODO: what to do about subscripts ex. set[int], list[set[int]], ... inferred = safe_infer(ann) if isinstance(inferred, nodes.ClassDef): types[arg.name] = inferred.name diff --git a/python_ta/upload.py b/python_ta/upload.py index 811f3575a..f08d59c9a 100644 --- a/python_ta/upload.py +++ b/python_ta/upload.py @@ -1,12 +1,14 @@ +from __future__ import annotations + import hashlib import json import uuid -from typing import Dict, List, NamedTuple +from typing import NamedTuple import requests -def errors_to_dict(errors: List[NamedTuple]) -> Dict[str, List[str]]: +def errors_to_dict(errors: list[NamedTuple]) -> dict[str, list[str]]: """Convert PyTA errors from MessageSet format to a json format Dictionary.""" error_info = ["msg_id", "msg", "symbol", "module", "category", "line"] error_types = ["code", "style"] @@ -27,7 +29,7 @@ def errors_to_dict(errors: List[NamedTuple]) -> Dict[str, List[str]]: def upload_to_server( - errors: List[NamedTuple], paths: List[str], config: Dict[str, str], url: str, version: str + errors: list[NamedTuple], paths: list[str], config: dict[str, str], url: str, version: str ) -> None: """Send POST request to server with formatted data.""" unique_id = get_hashed_id() # Generates a device-specific ID diff --git a/python_ta/z3/z3_parser.py b/python_ta/z3/z3_parser.py index 07c955b85..70ea0cf80 100644 --- a/python_ta/z3/z3_parser.py +++ b/python_ta/z3/z3_parser.py @@ -1,4 +1,6 @@ -from typing import Dict, List, Optional, Union +from __future__ import annotations + +from typing import Optional, Union import astroid import z3 @@ -23,9 +25,10 @@ class Z3Parser: - types: dictionary mapping variable names in astroid expression to their type name or z3 variable. """ - types: Dict[str, Union[str, z3.ExprRef]] + node: astroid.NodeNG + types: dict[str, Union[str, z3.ExprRef]] - def __init__(self, types: Optional[Dict[str, Union[str, z3.ExprRef]]] = None): + def __init__(self, types: Optional[dict[str, Union[str, z3.ExprRef]]] = None): if types is None: types = {} self.types = types @@ -104,7 +107,7 @@ def apply_unary_op(self, left: z3.ExprRef, op: str) -> z3.ExprRef: return left def apply_bin_op( - self, left: z3.ExprRef, op: str, right: Union[z3.ExprRef, List[z3.ExprRef]] + self, left: z3.ExprRef, op: str, right: Union[z3.ExprRef, list[z3.ExprRef]] ) -> z3.ExprRef: """Given left, right, op, apply the binary operation.""" try: @@ -141,7 +144,7 @@ def apply_bin_op( except TypeError: raise Z3ParseException(f"Operation {op} incompatible with types {left} and {right}.") - def apply_bool_op(self, op: str, values: Union[z3.ExprRef, List[z3.ExprRef]]) -> z3.ExprRef: + def apply_bool_op(self, op: str, values: Union[z3.ExprRef, list[z3.ExprRef]]) -> z3.ExprRef: """Apply boolean operation given by op to values.""" op_to_z3 = { "and": z3.And, @@ -178,14 +181,14 @@ def parse_bool_op(self, node: astroid.BoolOp) -> z3.ExprRef: def parse_container_op( self, node: Union[nodes.List, astroid.Set, astroid.Tuple] - ) -> List[z3.ExprRef]: + ) -> list[z3.ExprRef]: """Convert an astroid List, Set, Tuple node to a list of z3 expressions.""" return [self.parse(element) for element in node.elts] def apply_in_op( self, left: Union[z3.ExprRef, str], - right: Union[z3.ExprRef, List[z3.ExprRef], str], + right: Union[z3.ExprRef, list[z3.ExprRef], str], negate: bool = False, ) -> z3.ExprRef: """ @@ -273,7 +276,7 @@ def parse_subscript_op(self, node: astroid.Subscript) -> z3.ExprRef: raise Z3ParseException(f"Unhandled subscript operator type {slice}") - def parse_arguments(self, node: astroid.Arguments) -> Dict[str, z3.ExprRef]: + def parse_arguments(self, node: astroid.Arguments) -> dict[str, z3.ExprRef]: """Convert an astroid Arguments node's parameters to z3 variables.""" z3_vars = {} diff --git a/tests/test_cfg/test_classdef_.py b/tests/test_cfg/test_classdef_.py index f0db400b8..e9174b6f8 100644 --- a/tests/test_cfg/test_classdef_.py +++ b/tests/test_cfg/test_classdef_.py @@ -1,4 +1,6 @@ -from typing import Dict, List, Tuple, Union +from __future__ import annotations + +from typing import Union import astroid from astroid import nodes @@ -6,14 +8,14 @@ from python_ta.cfg import CFGVisitor, ControlFlowGraph -def build_cfgs(src: str) -> Dict[Union[nodes.FunctionDef, nodes.Module], ControlFlowGraph]: +def build_cfgs(src: str) -> dict[Union[nodes.FunctionDef, nodes.Module], ControlFlowGraph]: mod = astroid.parse(src) t = CFGVisitor() mod.accept(t) return t.cfgs -def _extract_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.get_blocks()] diff --git a/tests/test_cfg/test_for_.py b/tests/test_cfg/test_for_.py index ec96c1901..377244839 100644 --- a/tests/test_cfg/test_for_.py +++ b/tests/test_cfg/test_for_.py @@ -1,4 +1,4 @@ -from typing import List +from __future__ import annotations import astroid @@ -12,11 +12,11 @@ def build_cfg(src: str) -> ControlFlowGraph: return t.cfgs[mod] -def _extract_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.get_blocks()] -def _extract_edges(cfg: ControlFlowGraph) -> List[List[List[str]]]: +def _extract_edges(cfg: ControlFlowGraph) -> list[list[list[str]]]: edges = [[edge.source.statements, edge.target.statements] for edge in cfg.get_edges()] expanded_edges = [ [[source.as_string() for source in edge[0]], [target.as_string() for target in edge[1]]] diff --git a/tests/test_cfg/test_functions_.py b/tests/test_cfg/test_functions_.py index aaf4f831a..a927a22a9 100644 --- a/tests/test_cfg/test_functions_.py +++ b/tests/test_cfg/test_functions_.py @@ -1,4 +1,6 @@ -from typing import Dict, List, Tuple, Union +from __future__ import annotations + +from typing import Union import astroid from astroid import nodes @@ -6,18 +8,18 @@ from python_ta.cfg import CFGVisitor, ControlFlowGraph -def build_cfgs(src: str) -> Dict[Union[nodes.FunctionDef, nodes.Module], ControlFlowGraph]: +def build_cfgs(src: str) -> dict[Union[nodes.FunctionDef, nodes.Module], ControlFlowGraph]: mod = astroid.parse(src) t = CFGVisitor() mod.accept(t) return t.cfgs -def _extract_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.get_blocks()] -def _extract_edges(cfg: ControlFlowGraph) -> List[List[List[str]]]: +def _extract_edges(cfg: ControlFlowGraph) -> list[list[list[str]]]: edges = [[edge.source.statements, edge.target.statements] for edge in cfg.get_edges()] expanded_edges = [ [[source.as_string() for source in edge[0]], [target.as_string() for target in edge[1]]] diff --git a/tests/test_cfg/test_functions_preconditions.py b/tests/test_cfg/test_functions_preconditions.py index ba2ff031a..5a1382bb1 100644 --- a/tests/test_cfg/test_functions_preconditions.py +++ b/tests/test_cfg/test_functions_preconditions.py @@ -1,11 +1,11 @@ -from typing import List, Tuple +from __future__ import annotations import astroid from python_ta.cfg import CFGVisitor, ControlFlowGraph -def build_cfg(src: str) -> List[ControlFlowGraph]: +def build_cfg(src: str) -> list[ControlFlowGraph]: """Build a CFG for testing and return all CFGs built.""" mod = astroid.parse(src) t = CFGVisitor() @@ -13,7 +13,7 @@ def build_cfg(src: str) -> List[ControlFlowGraph]: return list(t.cfgs.values()) -def _extract_edge_conditions(cfgs: List[ControlFlowGraph]) -> List[str]: +def _extract_edge_conditions(cfgs: list[ControlFlowGraph]) -> list[str]: """Return the edge conditions in the given list of cfgs as a list of strings representing the condition.""" conditions = [ edge.condition.as_string() diff --git a/tests/test_cfg/test_if.py b/tests/test_cfg/test_if.py index 27c128a35..897535088 100644 --- a/tests/test_cfg/test_if.py +++ b/tests/test_cfg/test_if.py @@ -1,4 +1,4 @@ -from typing import List +from __future__ import annotations import astroid @@ -22,7 +22,7 @@ def build_cfg_separate_conditions(src: str) -> ControlFlowGraph: return t.cfgs[mod] -def _extract_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.get_blocks()] diff --git a/tests/test_cfg/test_jump.py b/tests/test_cfg/test_jump.py index 254378dc5..6805ae815 100644 --- a/tests/test_cfg/test_jump.py +++ b/tests/test_cfg/test_jump.py @@ -1,4 +1,6 @@ -from typing import Dict, List, Tuple, Union +from __future__ import annotations + +from typing import Union import astroid from astroid import nodes @@ -6,14 +8,14 @@ from python_ta.cfg import CFGVisitor, ControlFlowGraph -def build_cfgs(src: str) -> Dict[Union[nodes.FunctionDef, nodes.Module], ControlFlowGraph]: +def build_cfgs(src: str) -> dict[Union[nodes.FunctionDef, nodes.Module], ControlFlowGraph]: mod = astroid.parse(src) t = CFGVisitor() mod.accept(t) return t.cfgs -def _extract_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.get_blocks()] diff --git a/tests/test_cfg/test_label_if.py b/tests/test_cfg/test_label_if.py index 2f44f0c95..4b8cf3a2d 100644 --- a/tests/test_cfg/test_label_if.py +++ b/tests/test_cfg/test_label_if.py @@ -1,4 +1,4 @@ -from typing import List, Tuple +from __future__ import annotations import astroid @@ -14,7 +14,7 @@ def build_cfg(src: str) -> ControlFlowGraph: return t.cfgs[mod] -def _extract_edge_labels(cfg: ControlFlowGraph) -> Tuple[int, int]: +def _extract_edge_labels(cfg: ControlFlowGraph) -> tuple[int, int]: """Return the number of True/False edge labels in the given cfg. The returned 2-tuple is of the form (# of True, # of False). @@ -23,7 +23,7 @@ def _extract_edge_labels(cfg: ControlFlowGraph) -> Tuple[int, int]: return labels.count("True"), labels.count("False") -def _extract_edge_conditions(cfg: ControlFlowGraph) -> List[str]: +def _extract_edge_conditions(cfg: ControlFlowGraph) -> list[str]: """Return the edge conditions in the given cfg as a list of strings representing the condition.""" conditions = [ diff --git a/tests/test_cfg/test_label_while.py b/tests/test_cfg/test_label_while.py index a8f9a121f..a6ec09177 100644 --- a/tests/test_cfg/test_label_while.py +++ b/tests/test_cfg/test_label_while.py @@ -1,4 +1,4 @@ -from typing import List, Set +from __future__ import annotations import astroid @@ -14,7 +14,7 @@ def build_cfg(src: str) -> ControlFlowGraph: return t.cfgs[mod] -def _extract_labels(cfg: ControlFlowGraph) -> Set[str]: +def _extract_labels(cfg: ControlFlowGraph) -> set[str]: """Return a set of all the labels in this cfg.""" labels = {edge.get_label() for edge in cfg.get_edges() if edge.get_label() is not None} return labels @@ -25,7 +25,7 @@ def _extract_num_labels(cfg: ControlFlowGraph) -> int: return sum(1 for edge in cfg.get_edges() if edge.get_label() is not None) -def _extract_edge_conditions(cfg: ControlFlowGraph) -> List[str]: +def _extract_edge_conditions(cfg: ControlFlowGraph) -> list[str]: """Return the edge conditions in the given cfg as a list of strings representing the condition.""" conditions = [ diff --git a/tests/test_cfg/test_tryexcept.py b/tests/test_cfg/test_tryexcept.py index f6e78e4dc..5adec1dad 100644 --- a/tests/test_cfg/test_tryexcept.py +++ b/tests/test_cfg/test_tryexcept.py @@ -1,4 +1,4 @@ -from typing import List +from __future__ import annotations import astroid @@ -13,11 +13,11 @@ def build_cfg(src: str) -> ControlFlowGraph: return t.cfgs[mod] -def _extract_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.get_blocks()] -def _extract_unreachable_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_unreachable_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.unreachable_blocks] diff --git a/tests/test_cfg/test_unreachable.py b/tests/test_cfg/test_unreachable.py index b0a0c9b3d..65b74032d 100644 --- a/tests/test_cfg/test_unreachable.py +++ b/tests/test_cfg/test_unreachable.py @@ -1,4 +1,6 @@ -from typing import Generator, Optional, Set, Tuple +from __future__ import annotations + +from typing import Generator, Optional import astroid @@ -16,7 +18,7 @@ def build_cfg(src: str, is_function: Optional[bool] = False) -> ControlFlowGraph return t.cfgs[mod] -def extract_blocks(cfg: ControlFlowGraph) -> Tuple[Set, Set]: +def extract_blocks(cfg: ControlFlowGraph) -> tuple[set, set]: """Returns (unreachable_set, reachable_set)""" reachable = (set(), set()) for block in get_blocks(cfg.end): @@ -30,7 +32,7 @@ def get_blocks(end_block: CFGBlock) -> Generator[CFGBlock, None, None]: yield from _get_blocks(end_block, set()) -def _get_blocks(block: CFGBlock, visited: Set[int]) -> Generator[CFGBlock, None, None]: +def _get_blocks(block: CFGBlock, visited: set[int]) -> Generator[CFGBlock, None, None]: if block.id in visited: return diff --git a/tests/test_cfg/test_while.py b/tests/test_cfg/test_while.py index 3484f7be5..8deccd775 100644 --- a/tests/test_cfg/test_while.py +++ b/tests/test_cfg/test_while.py @@ -1,4 +1,4 @@ -from typing import List, Tuple +from __future__ import annotations import astroid @@ -12,11 +12,11 @@ def build_cfg(src: str) -> ControlFlowGraph: return t.cfgs[mod] -def _extract_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.get_blocks()] -def _extract_edges(cfg: ControlFlowGraph) -> List[List[List[str]]]: +def _extract_edges(cfg: ControlFlowGraph) -> list[list[list[str]]]: edges = [[edge.source.statements, edge.target.statements] for edge in cfg.get_edges()] expanded_edges = [ [[source.as_string() for source in edge[0]], [target.as_string() for target in edge[1]]] diff --git a/tests/test_cfg/test_with.py b/tests/test_cfg/test_with.py index 5dee99d50..a08e02bdc 100644 --- a/tests/test_cfg/test_with.py +++ b/tests/test_cfg/test_with.py @@ -1,4 +1,4 @@ -from typing import List, Tuple +from __future__ import annotations import astroid @@ -12,11 +12,11 @@ def build_cfg(src: str) -> ControlFlowGraph: return t.cfgs[mod] -def _extract_blocks(cfg: ControlFlowGraph) -> List[List[str]]: +def _extract_blocks(cfg: ControlFlowGraph) -> list[list[str]]: return [[s.as_string() for s in block.statements] for block in cfg.get_blocks()] -def _extract_edges(cfg: ControlFlowGraph) -> List[List[List[str]]]: +def _extract_edges(cfg: ControlFlowGraph) -> list[list[list[str]]]: edges = [[edge.source.statements, edge.target.statements] for edge in cfg.get_edges()] expanded_edges = [ [[source.as_string() for source in edge[0]], [target.as_string() for target in edge[1]]] diff --git a/tests/test_config/file_fixtures/funcs_with_errors.py b/tests/test_config/file_fixtures/funcs_with_errors.py index c873d49ad..007d5443e 100644 --- a/tests/test_config/file_fixtures/funcs_with_errors.py +++ b/tests/test_config/file_fixtures/funcs_with_errors.py @@ -1,6 +1,6 @@ """Python script used for testing that the correct number of error occurrences are being displayed.""" -from typing import List +from __future__ import annotations # The following imports are used solely to trigger errors. import packaging @@ -9,7 +9,7 @@ import pylint -def sum_items(lst: List[int]) -> int: +def sum_items(lst: list[int]) -> int: """...""" s = 0 for i in range(len(lst)): @@ -17,7 +17,7 @@ def sum_items(lst: List[int]) -> int: return s -def sum_items2(lst: List[int]) -> int: +def sum_items2(lst: list[int]) -> int: """...""" s = 0 for i in range(0, len(lst)): @@ -25,7 +25,7 @@ def sum_items2(lst: List[int]) -> int: return s -def sum_items3(lst: List[int]) -> int: +def sum_items3(lst: list[int]) -> int: """...""" s = 0 for i in range(0, len(lst), 1): diff --git a/tests/test_contracts/test_class_contracts.py b/tests/test_contracts/test_class_contracts.py index 93dca8755..eb54262d4 100644 --- a/tests/test_contracts/test_class_contracts.py +++ b/tests/test_contracts/test_class_contracts.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import math from dataclasses import dataclass -from typing import Dict, List, Set, Tuple +from typing import List, Set, Tuple import pytest from nested_preconditions_example import Student @@ -383,7 +385,7 @@ class ThemedWidget: primary_color: str secondary_color: str - def __init__(self, theme: str, color_palette: Tuple[str, str], options: Dict = None) -> None: + def __init__(self, theme: str, color_palette: Tuple[str, str], options: dict = None) -> None: if options: self.setup_options(options) else: @@ -391,7 +393,7 @@ def __init__(self, theme: str, color_palette: Tuple[str, str], options: Dict = N self.apply_theme(theme) self.apply_color_palette(color_palette) - def setup_options(self, options: Dict) -> None: + def setup_options(self, options: dict) -> None: if "size" in options: self.setup_size(options["size"]) diff --git a/tests/test_contracts/test_contracts.py b/tests/test_contracts/test_contracts.py index ce70f10ac..97939e0fd 100644 --- a/tests/test_contracts/test_contracts.py +++ b/tests/test_contracts/test_contracts.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from typing import Dict, List, Set @@ -280,7 +282,7 @@ def test_my_sum_one_disable_contract_checking(disable_contract_checking) -> None # Checking to see if functions we defined are in-scope for preconditions -def is_even(lst: List[int]) -> bool: +def is_even(lst: list[int]) -> bool: return all([(not x & 1) for x in lst]) diff --git a/tests/test_custom_checkers/test_invalid_for_target_checker.py b/tests/test_custom_checkers/test_invalid_for_target_checker.py index a889622c6..230401bd5 100644 --- a/tests/test_custom_checkers/test_invalid_for_target_checker.py +++ b/tests/test_custom_checkers/test_invalid_for_target_checker.py @@ -1,4 +1,4 @@ -from typing import List +from __future__ import annotations import astroid import pylint.testutils @@ -365,7 +365,7 @@ def test_msg_comp_slicing(self) -> None: self.checker.visit_comprehension(comp_node) -def _extract_nodes(src: str, node_types: List[type]) -> List[nodes.NodeNG]: +def _extract_nodes(src: str, node_types: list[type]) -> list[nodes.NodeNG]: mod = astroid.parse(src) extracted_nodes = [] diff --git a/tests/test_custom_checkers/test_shadowing_in_comprehension_checker.py b/tests/test_custom_checkers/test_shadowing_in_comprehension_checker.py index eaf3de9d6..5f40db493 100644 --- a/tests/test_custom_checkers/test_shadowing_in_comprehension_checker.py +++ b/tests/test_custom_checkers/test_shadowing_in_comprehension_checker.py @@ -65,7 +65,7 @@ def switch_dict(_: dict) -> dict: def test_list_comp(self): """Test checker with a list comprehension""" src = ''' - def num_lst(n: int) -> List[int]: + def num_lst(n: int) -> list[int]: """Return a list of integers from 0 to , in that order.""" return [n for n in range(n)] ''' @@ -131,7 +131,7 @@ def common_items(lst1: list, lst2: list) -> int: def test_print_pos(self): """Test checker with a generator""" src = ''' - def print_pos(lst: List[int]) -> None: + def print_pos(lst: list[int]) -> None: """Print items in lst one by one if they are greater than 0.""" for k in (k for k in lst if k > 0): print(k) diff --git a/tests/test_custom_checkers/test_unnecessary_indexing_checker.py b/tests/test_custom_checkers/test_unnecessary_indexing_checker.py index 9d926e5aa..6a010d8ec 100644 --- a/tests/test_custom_checkers/test_unnecessary_indexing_checker.py +++ b/tests/test_custom_checkers/test_unnecessary_indexing_checker.py @@ -31,7 +31,7 @@ def f(lst: list) -> None: def test_sum_items_msg(self): """Return the sum of a list of numbers.""" src = """ - def sum_items(lst: List[int]) -> int: + def sum_items(lst: list[int]) -> int: s = 0 for i in range(len(lst)): #@ s += lst[i] @@ -53,7 +53,7 @@ def sum_items(lst: List[int]) -> int: def test_sum_items2_msg(self): """Return the sum of a list of numbers.""" src = """ - def sum_items2(lst: List[int]) -> int: + def sum_items2(lst: list[int]) -> int: s = 0 for i in range(0, len(lst)): #@ s += lst[i] @@ -75,7 +75,7 @@ def sum_items2(lst: List[int]) -> int: def test_sum_items3_msg(self): """Return the sum of a list of numbers.""" src = """ - def sum_items3(lst: List[int]) -> int: + def sum_items3(lst: list[int]) -> int: s = 0 for i in range(0, len(lst), 1): #@ s += lst[i] @@ -99,7 +99,7 @@ def test_sum_pairs_no_msg(self): NO error reported; the loop index is used to index lst2 as well.""" src = """ - def sum_pairs(lst1: List[int], lst2: List[int]) -> int: + def sum_pairs(lst1: list[int], lst2: list[int]) -> int: s = 0 for i in range(len(lst1)): s += lst1[i] * lst2[i] @@ -114,7 +114,7 @@ def sum_pairs(lst1: List[int], lst2: List[int]) -> int: def test_nested_sum_msg(self): """Return a repeated sum of the items in the list.""" src = """ - def nested_sum(items: List[List[int]]) -> int: + def nested_sum(items: list[list[int]]) -> int: s = 0 for i in range(len(items)): #@ s += sum([2 * x for x in items[i]]) @@ -209,7 +209,7 @@ def test_loop_variable_reassigned_no_msg(self): NO error reported; the loop variable assignment i is unused, but is not redundant.""" src = """ - def loop_variable_reassigned(items: List[int]) -> int: + def loop_variable_reassigned(items: list[int]) -> int: s = 0 for i in range(len(items)): i = 0 @@ -225,7 +225,7 @@ def loop_variable_reassigned(items: List[int]) -> int: def test_sum_items_no_msg(self): """Elements are accessed directly, no unnecessary indexing""" src = """ - def sum_items(items: List[int]) -> int: + def sum_items(items: list[int]) -> int: s = 0 for x in items: s += x @@ -240,7 +240,7 @@ def sum_items(items: List[int]) -> int: def test_iter_var_unused_no_msg(self): """Index variable i is unused in the code, no unnecessary indexing performed""" src = """ - def iter_var_unused(items: List[int]) -> int: + def iter_var_unused(items: list[int]) -> int: s = 0 for i in range(len(items)): s += 1 diff --git a/tests/test_examples.py b/tests/test_examples.py index 97bb80fad..a8443d17b 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,10 +1,12 @@ +from __future__ import annotations + import itertools import json import os import re import sys from io import StringIO -from typing import Dict, List, Set, Union +from typing import Union import pytest from pylint import lint @@ -37,7 +39,7 @@ ] -def get_file_paths(paths: Union[str, List[str]]) -> List[str]: +def get_file_paths(paths: Union[str, list[str]]) -> list[str]: """ Get all the Python files from the specified directories for testing. This will return the full file paths for each Python file, excluding those listed in IGNORED_TESTS. @@ -63,7 +65,7 @@ def get_file_paths(paths: Union[str, List[str]]) -> List[str]: return test_files -def _symbols_by_file_pyta(paths: List[str], include_msg: bool = False) -> Dict[str, Set[str]]: +def _symbols_by_file_pyta(paths: list[str], include_msg: bool = False) -> dict[str, set[str]]: """ Run python_ta.check_all() on files from specified directories and return the map of file name to the set of PythonTA messages it raises. If include_msg is set True, PythonTA message descriptions are @@ -104,7 +106,7 @@ def _symbols_by_file_pyta(paths: List[str], include_msg: bool = False) -> Dict[s @pytest.fixture(scope="session") -def pyta_examples_symbols() -> Dict[str, Set[str]]: +def pyta_examples_symbols() -> dict[str, set[str]]: """ A pytest fixture that runs once per test session. This fixture analyzes example files using python_ta and returns a dictionary mapping each file name @@ -116,7 +118,7 @@ def pyta_examples_symbols() -> Dict[str, Set[str]]: @pytest.fixture(scope="session") -def pyta_pycodestyle_symbols() -> Dict[str, Set[str]]: +def pyta_pycodestyle_symbols() -> dict[str, set[str]]: """ A pytest fixture that runs once per test session. This fixture analyzes pycodestyle error test cases using python_ta and returns a dictionary mapping each file name @@ -128,7 +130,7 @@ def pyta_pycodestyle_symbols() -> Dict[str, Set[str]]: @pytest.mark.parametrize("test_file", get_file_paths([_EXAMPLES_PATH, _CUSTOM_CHECKER_PATH])) -def test_examples_files_pyta(test_file: str, pyta_examples_symbols: Dict[str, Set[str]]) -> None: +def test_examples_files_pyta(test_file: str, pyta_examples_symbols: dict[str, set[str]]) -> None: """ Dynamically creates and runs unit tests for Python files in the examples and custom checker directories. This test function deduces the error type from the file name and checks if the expected error message is present @@ -151,7 +153,7 @@ def test_examples_files_pyta(test_file: str, pyta_examples_symbols: Dict[str, Se @pytest.mark.parametrize("test_file", get_file_paths(_PYCODESTYLE_PATH)) def test_pycodestyle_errors_pyta( - test_file: str, pyta_pycodestyle_symbols: Dict[str, Set[str]] + test_file: str, pyta_pycodestyle_symbols: dict[str, set[str]] ) -> None: """ Dynamically creates and runs unit tests for pycodestyle error test cases. diff --git a/tests/test_messages_config/testing_code.py b/tests/test_messages_config/testing_code.py index 279a23a4c..70cb76ac8 100644 --- a/tests/test_messages_config/testing_code.py +++ b/tests/test_messages_config/testing_code.py @@ -1,9 +1,9 @@ -from typing import List +from __future__ import annotations reversed(12345) # Error on this line -def add(lst: List[int]) -> int: +def add(lst: list[int]) -> int: """Return the sum of the elements in the given list.""" temp = 0 for item in lst: diff --git a/tests/test_setendings.py b/tests/test_setendings.py index 79db48bd3..3c22c792f 100644 --- a/tests/test_setendings.py +++ b/tests/test_setendings.py @@ -403,27 +403,27 @@ def test_starred(self): def test_subscript(self): expected = [ - (1, 1, 0, 4), - (2, 2, 0, 8), (3, 3, 0, 4), - (4, 4, 0, 9), - (5, 5, 0, 5), - (6, 6, 0, 14), - (7, 7, 0, 9), - (8, 8, 4, 31), - (9, 10, 0, 3), - (11, 11, 0, 8), - (11, 11, 0, 5), - (12, 12, 0, 8), - (12, 12, 0, 4), - (13, 13, 0, 7), - (13, 13, 0, 4), - (14, 14, 0, 5), - (15, 15, 0, 12), + (4, 4, 0, 8), + (5, 5, 0, 4), + (6, 6, 0, 9), + (7, 7, 0, 5), + (8, 8, 0, 14), + (9, 9, 0, 9), + (10, 10, 4, 31), + (11, 12, 0, 3), + (13, 13, 0, 8), + (13, 13, 0, 5), + (14, 14, 0, 8), + (14, 14, 0, 4), + (15, 15, 0, 7), (15, 15, 0, 4), - (16, 16, 4, 12), - (22, 22, 17, 26), - (22, 22, 31, 40), + (16, 16, 0, 5), + (17, 17, 0, 12), + (17, 17, 0, 4), + (18, 18, 4, 12), + (21, 21, 17, 26), + (21, 21, 31, 40), ] module = self.get_file_as_module("subscript.py") self.set_and_check(module, nodes.Subscript, expected) diff --git a/tests/test_subclass_contracts.py b/tests/test_subclass_contracts.py index 477fc83ef..15fbd1249 100644 --- a/tests/test_subclass_contracts.py +++ b/tests/test_subclass_contracts.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import List import pytest diff --git a/tests/test_validate_invariants.py b/tests/test_validate_invariants.py index 4bb0af3ae..e41503727 100644 --- a/tests/test_validate_invariants.py +++ b/tests/test_validate_invariants.py @@ -2,6 +2,8 @@ Test suite for checking the functionality of validate_invariants. """ +from __future__ import annotations + from typing import List import pytest