|
5 | 5 | # Licensed under the MIT License. See License.txt in the project root for license information. |
6 | 6 | # -------------------------------------------------------------------------------------------- |
7 | 7 |
|
8 | | -from enum import Enum |
9 | | -from typing import Any, Dict, List, Union |
10 | 8 | import jsondiff |
| 9 | +import re |
| 10 | +from enum import Enum |
| 11 | +from typing import Any, Dict, List, Union, Optional, NamedTuple |
| 12 | +from copy import deepcopy |
11 | 13 | from breaking_changes_allowlist import IGNORE_BREAKING_CHANGES |
12 | 14 |
|
13 | 15 |
|
| 16 | +class Suppression(NamedTuple): |
| 17 | + change_type: str |
| 18 | + module: str |
| 19 | + class_name: Optional[str] = None |
| 20 | + function_name: Optional[str] = None |
| 21 | + parameter_or_property_name: Optional[str] = None |
| 22 | + |
14 | 23 | class BreakingChangeType(str, Enum): |
15 | 24 | REMOVED_OR_RENAMED_CLIENT = "RemovedOrRenamedClient" |
16 | 25 | REMOVED_OR_RENAMED_CLIENT_METHOD = "RemovedOrRenamedClientMethod" |
@@ -42,11 +51,11 @@ class BreakingChangesTracker: |
42 | 51 | REMOVED_OR_RENAMED_MODULE_LEVEL_FUNCTION_MSG = \ |
43 | 52 | "The publicly exposed function '{}.{}' was deleted or renamed in the current version" |
44 | 53 | REMOVED_OR_RENAMED_POSITIONAL_PARAM_OF_METHOD_MSG = \ |
45 | | - "The '{}.{} method '{}' had its '{}' parameter '{}' deleted or renamed in the current version" |
| 54 | + "The '{}.{}' method '{}' had its parameter '{}' of kind '{}' deleted or renamed in the current version" |
46 | 55 | REMOVED_OR_RENAMED_POSITIONAL_PARAM_OF_FUNCTION_MSG = \ |
47 | | - "The function '{}.{}' had its '{}' parameter '{}' deleted or renamed in the current version" |
| 56 | + "The function '{}.{}' had its parameter '{}' of kind '{}' deleted or renamed in the current version" |
48 | 57 | ADDED_POSITIONAL_PARAM_TO_METHOD_MSG = \ |
49 | | - "The '{}.{} method '{}' had a '{}' parameter '{}' inserted in the current version" |
| 58 | + "The '{}.{}' method '{}' had a '{}' parameter '{}' inserted in the current version" |
50 | 59 | ADDED_POSITIONAL_PARAM_TO_FUNCTION_MSG = \ |
51 | 60 | "The function '{}.{}' had a '{}' parameter '{}' inserted in the current version" |
52 | 61 | REMOVED_OR_RENAMED_INSTANCE_ATTRIBUTE_FROM_CLIENT_MSG = \ |
@@ -444,15 +453,15 @@ def check_positional_parameter_removed_or_renamed( |
444 | 453 | ( |
445 | 454 | self.REMOVED_OR_RENAMED_POSITIONAL_PARAM_OF_METHOD_MSG, |
446 | 455 | BreakingChangeType.REMOVED_OR_RENAMED_POSITIONAL_PARAM, |
447 | | - self.module_name, self.class_name, self.function_name, param_type, deleted |
| 456 | + self.module_name, self.class_name, self.function_name, deleted, param_type |
448 | 457 | ) |
449 | 458 | ) |
450 | 459 | else: |
451 | 460 | self.breaking_changes.append( |
452 | 461 | ( |
453 | 462 | self.REMOVED_OR_RENAMED_POSITIONAL_PARAM_OF_FUNCTION_MSG, |
454 | 463 | BreakingChangeType.REMOVED_OR_RENAMED_POSITIONAL_PARAM, |
455 | | - self.module_name, self.function_name, param_type, deleted |
| 464 | + self.module_name, self.function_name, deleted, param_type |
456 | 465 | ) |
457 | 466 | ) |
458 | 467 |
|
@@ -559,25 +568,46 @@ def check_module_level_function_removed_or_renamed(self, function_components: Di |
559 | 568 | ) |
560 | 569 | return True |
561 | 570 |
|
562 | | - # ----------------------------------- Report methods ----------------------------------- |
| 571 | + def match(self, bc, ignored): |
| 572 | + if bc == ignored: |
| 573 | + return True |
| 574 | + for b, i in zip(bc, ignored): |
| 575 | + if i == "*": |
| 576 | + continue |
| 577 | + if b != i: |
| 578 | + return False |
| 579 | + return True |
| 580 | + |
563 | 581 | def get_reportable_breaking_changes(self, ignore_changes: Dict) -> List: |
564 | | - reportable_changes = [] |
565 | | - ignored = ignore_changes[self.package_name] |
566 | | - for bc in self.breaking_changes: |
567 | | - msg, bc_type, module_name, *args = bc |
| 582 | + ignored = [] |
| 583 | + # Match all ignore rules that should apply to this package |
| 584 | + for ignored_package, ignore_rules in ignore_changes.items(): |
| 585 | + if re.findall(ignored_package, self.package_name): |
| 586 | + ignored.extend(ignore_rules) |
| 587 | + |
| 588 | + # Remove ignored breaking changes from list of reportable changes |
| 589 | + bc_copy = deepcopy(self.breaking_changes) |
| 590 | + for bc in bc_copy: |
| 591 | + _, bc_type, module_name, *args = bc |
568 | 592 | class_name = args[0] if args else None |
569 | 593 | function_name = args[1] if len(args) > 1 else None |
570 | | - if (bc_type, module_name) in ignored or \ |
571 | | - (bc_type, module_name, class_name) in ignored or \ |
572 | | - (bc_type, module_name, class_name, function_name) in ignored: |
573 | | - continue |
574 | | - reportable_changes.append(bc) |
575 | | - return reportable_changes |
| 594 | + parameter_name = args[2] if len(args) > 2 else None |
| 595 | + |
| 596 | + for rule in ignored: |
| 597 | + suppression = Suppression(*rule) |
| 598 | + |
| 599 | + if suppression.parameter_or_property_name is not None: |
| 600 | + # If the ignore rule is for a property or parameter, we should check up to that level on the original breaking change |
| 601 | + if self.match((bc_type, module_name, class_name, function_name, parameter_name), suppression): |
| 602 | + self.breaking_changes.remove(bc) |
| 603 | + break |
| 604 | + elif self.match((bc_type, module_name, class_name, function_name), suppression): |
| 605 | + self.breaking_changes.remove(bc) |
| 606 | + break |
576 | 607 |
|
577 | 608 | def report_changes(self) -> None: |
578 | 609 | ignore_changes = self.ignore if self.ignore else IGNORE_BREAKING_CHANGES |
579 | | - if self.package_name in ignore_changes: |
580 | | - self.breaking_changes = self.get_reportable_breaking_changes(ignore_changes) |
| 610 | + self.get_reportable_breaking_changes(ignore_changes) |
581 | 611 |
|
582 | 612 | # If there are no breaking changes after the ignore check, return early |
583 | 613 | if not self.breaking_changes: |
|
0 commit comments