Skip to content

Commit 7068b4b

Browse files
catalinaperaltacperaltahkristapratico
authored
[bct] Add more flexibility to suppression mechanism (#36226)
* wip suppression logic update * Improve suppression logic * add mgmt plane ignores * reword * switch loop order, match, deepcopy (#36249) * add named tuple for suppressions * update tests * update wording * clean up imports --------- Co-authored-by: Catalina Peralta <[email protected]> Co-authored-by: Krista Pratico <[email protected]>
1 parent 463b5ed commit 7068b4b

File tree

3 files changed

+103
-29
lines changed

3 files changed

+103
-29
lines changed

scripts/breaking_changes_checker/breaking_changes_allowlist.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,24 @@
1111

1212
# See Readme for ignore format
1313

14-
IGNORE_BREAKING_CHANGES = {}
14+
IGNORE_BREAKING_CHANGES = {
15+
"azure-mgmt-": [
16+
# Changes due to latest dpg design + need to support overloads in this tool
17+
("ChangedParameterOrdering", "*", "*", "__init__"),
18+
# Changes due to latest dpg design
19+
("ChangedParameterKind", "*", "*", "*", "top"),
20+
("ChangedParameterKind", "*", "*", "*", "filter"),
21+
("ChangedParameterKind", "*", "*", "*", "skip"),
22+
("RemovedOrRenamedPositionalParam", "*", "*", "*", "maxpagesize"),
23+
# Changes due to not using msrest anymore
24+
("RemovedOrRenamedClassMethod", "*", "*", "as_dict"),
25+
("RemovedOrRenamedClassMethod", "*", "*", "deserialize"),
26+
("RemovedOrRenamedClassMethod", "*", "*", "enable_additional_properties_sending"),
27+
("RemovedOrRenamedClassMethod", "*", "*", "from_dict"),
28+
("RemovedOrRenamedClassMethod", "*", "*", "is_xml_model"),
29+
("RemovedOrRenamedClassMethod", "*", "*", "serialize"),
30+
("RemovedOrRenamedPositionalParam", "*", "*", "as_dict", "key_transformer"),
31+
("RemovedOrRenamedPositionalParam", "*", "*", "as_dict"),
32+
("RemovedFunctionKwargs", "*", "*", "as_dict"),
33+
]
34+
}

scripts/breaking_changes_checker/breaking_changes_tracker.py

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@
55
# Licensed under the MIT License. See License.txt in the project root for license information.
66
# --------------------------------------------------------------------------------------------
77

8-
from enum import Enum
9-
from typing import Any, Dict, List, Union
108
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
1113
from breaking_changes_allowlist import IGNORE_BREAKING_CHANGES
1214

1315

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+
1423
class BreakingChangeType(str, Enum):
1524
REMOVED_OR_RENAMED_CLIENT = "RemovedOrRenamedClient"
1625
REMOVED_OR_RENAMED_CLIENT_METHOD = "RemovedOrRenamedClientMethod"
@@ -42,11 +51,11 @@ class BreakingChangesTracker:
4251
REMOVED_OR_RENAMED_MODULE_LEVEL_FUNCTION_MSG = \
4352
"The publicly exposed function '{}.{}' was deleted or renamed in the current version"
4453
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"
4655
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"
4857
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"
5059
ADDED_POSITIONAL_PARAM_TO_FUNCTION_MSG = \
5160
"The function '{}.{}' had a '{}' parameter '{}' inserted in the current version"
5261
REMOVED_OR_RENAMED_INSTANCE_ATTRIBUTE_FROM_CLIENT_MSG = \
@@ -444,15 +453,15 @@ def check_positional_parameter_removed_or_renamed(
444453
(
445454
self.REMOVED_OR_RENAMED_POSITIONAL_PARAM_OF_METHOD_MSG,
446455
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
448457
)
449458
)
450459
else:
451460
self.breaking_changes.append(
452461
(
453462
self.REMOVED_OR_RENAMED_POSITIONAL_PARAM_OF_FUNCTION_MSG,
454463
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
456465
)
457466
)
458467

@@ -559,25 +568,46 @@ def check_module_level_function_removed_or_renamed(self, function_components: Di
559568
)
560569
return True
561570

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+
563581
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
568592
class_name = args[0] if args else None
569593
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
576607

577608
def report_changes(self) -> None:
578609
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)
581611

582612
# If there are no breaking changes after the ignore check, return early
583613
if not self.breaking_changes:

scripts/breaking_changes_checker/tests/test_breaking_changes.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ def format_breaking_changes(breaking_changes):
2424
"(RemovedOrRenamedInstanceAttribute): The model or publicly exposed class 'azure.storage.queue.Metrics' had its instance variable 'retention_policy' deleted or renamed in the current version",
2525
"(RemovedOrRenamedInstanceAttribute): The client 'azure.storage.queue.QueueClient' had its instance variable 'queue_name' deleted or renamed in the current version",
2626
"(ChangedParameterKind): The class 'azure.storage.queue.QueueClient' method 'from_queue_url' had its parameter 'credential' changed from 'positional_or_keyword' to 'keyword_only' in the current version",
27-
"(AddedPositionalParam): The 'azure.storage.queue.QueueClient method 'get_queue_access_policy' had a 'positional_or_keyword' parameter 'queue_name' inserted in the current version",
28-
"(RemovedOrRenamedPositionalParam): The 'azure.storage.queue.QueueClient method 'set_queue_access_policy' had its 'positional_or_keyword' parameter 'signed_identifiers' deleted or renamed in the current version",
27+
"(AddedPositionalParam): The 'azure.storage.queue.QueueClient' method 'get_queue_access_policy' had a 'positional_or_keyword' parameter 'queue_name' inserted in the current version",
28+
"(RemovedOrRenamedPositionalParam): The 'azure.storage.queue.QueueClient' method 'set_queue_access_policy' had its parameter 'signed_identifiers' of kind 'positional_or_keyword' deleted or renamed in the current version",
2929
"(ChangedParameterDefaultValue): The class 'azure.storage.queue.QueueClient' method 'set_queue_metadata' had its parameter 'metadata' default value changed from 'None' to ''",
3030
"(RemovedOrRenamedClassMethod): The 'azure.storage.queue.QueueSasPermissions' method 'from_string' was deleted or renamed in the current version",
3131
"(RemovedFunctionKwargs): The class 'azure.storage.queue.QueueServiceClient' method 'set_service_properties' changed from accepting keyword arguments to not accepting them in the current version",
@@ -35,13 +35,13 @@ def format_breaking_changes(breaking_changes):
3535
"(ChangedParameterDefaultValue): The publicly exposed function 'azure.storage.queue.generate_queue_sas' had its parameter 'permission' default value changed from 'None' to ''",
3636
"(ChangedParameterKind): The function 'azure.storage.queue.generate_queue_sas' had its parameter 'ip' changed from 'positional_or_keyword' to 'keyword_only' in the current version",
3737
"(AddedPositionalParam): The function 'azure.storage.queue.generate_queue_sas' had a 'positional_or_keyword' parameter 'conn_str' inserted in the current version",
38-
"(RemovedOrRenamedPositionalParam): The function 'azure.storage.queue.generate_queue_sas' had its 'positional_or_keyword' parameter 'account_name' deleted or renamed in the current version",
38+
"(RemovedOrRenamedPositionalParam): The function 'azure.storage.queue.generate_queue_sas' had its parameter 'account_name' of kind 'positional_or_keyword' deleted or renamed in the current version",
3939
"(RemovedOrRenamedModuleLevelFunction): The publicly exposed function 'azure.storage.queue.generate_account_sas' was deleted or renamed in the current version",
4040
"(ChangedParameterKind): The class 'azure.storage.queue.aio.QueueClient' method 'from_queue_url' had its parameter 'credential' changed from 'positional_or_keyword' to 'keyword_only' in the current version",
4141
"(RemovedParameterDefaultValue): The class 'azure.storage.queue.aio.QueueClient' method 'update_message' had default value 'None' removed from its parameter 'pop_receipt' in the current version",
4242
"(ChangedFunctionKind): The class 'azure.storage.queue.aio.QueueServiceClient' method 'get_service_stats' changed from 'asynchronous' to 'synchronous' in the current version.",
43+
"(ChangedParameterOrdering): The class 'azure.storage.queue.QueueClient' method 'from_connection_string' had its parameters re-ordered from '['conn_str', 'queue_name', 'credential', 'kwargs']' to '['queue_name', 'conn_str', 'credential', 'kwargs']' in the current version",
4344
"(ChangedParameterOrdering): The class 'azure.storage.queue.aio.QueueClient' method 'from_connection_string' had its parameters re-ordered from '['conn_str', 'queue_name', 'credential', 'kwargs']' to '['queue_name', 'conn_str', 'credential', 'kwargs']' in the current version",
44-
"(ChangedParameterOrdering): The class 'azure.storage.queue.QueueClient' method 'from_connection_string' had its parameters re-ordered from '['conn_str', 'queue_name', 'credential', 'kwargs']' to '['queue_name', 'conn_str', 'credential', 'kwargs']' in the current version"
4545
]
4646

4747

@@ -86,6 +86,30 @@ def test_ignore_checks():
8686
assert changes == expected_msg
8787

8888

89+
def test_ignore_with_wildcard_checks():
90+
with open(os.path.join(os.path.dirname(__file__), "test_stable.json"), "r") as fd:
91+
stable = json.load(fd)
92+
with open(os.path.join(os.path.dirname(__file__), "test_current.json"), "r") as fd:
93+
current = json.load(fd)
94+
diff = jsondiff.diff(stable, current)
95+
96+
ignore = {
97+
"azure-storage-queue": [
98+
("ChangedParameterOrdering", "*", "*", "from_connection_string"),
99+
("ChangedFunctionKind", "azure.storage.queue.aio", "QueueServiceClient", "get_service_stats"),
100+
]
101+
}
102+
103+
bc = BreakingChangesTracker(stable, current, diff, "azure-storage-queue", ignore=ignore)
104+
bc.run_checks()
105+
106+
changes = bc.report_changes()
107+
108+
expected_msg = format_breaking_changes(EXPECTED[:-3])
109+
assert len(bc.breaking_changes)+3 == len(EXPECTED)
110+
assert changes == expected_msg
111+
112+
89113
def test_replace_all_params():
90114
current = {
91115
"azure.ai.formrecognizer": {
@@ -157,10 +181,10 @@ def test_replace_all_params():
157181
}
158182

159183
EXPECTED = [
160-
"(RemovedOrRenamedPositionalParam): The 'azure.ai.formrecognizer.class_name method 'one' had its 'positional_or_keyword' parameter 'testing' deleted or renamed in the current version",
161-
"(RemovedOrRenamedPositionalParam): The 'azure.ai.formrecognizer.class_name method 'two' had its 'positional_or_keyword' parameter 'testing2' deleted or renamed in the current version",
162-
"(RemovedOrRenamedPositionalParam): The function 'azure.ai.formrecognizer.my_function_name' had its 'positional_or_keyword' parameter 'testing' deleted or renamed in the current version",
163-
"(RemovedOrRenamedPositionalParam): The function 'azure.ai.formrecognizer.my_function_name' had its 'positional_or_keyword' parameter 'testing2' deleted or renamed in the current version"
184+
"(RemovedOrRenamedPositionalParam): The 'azure.ai.formrecognizer.class_name' method 'one' had its parameter 'testing' of kind 'positional_or_keyword' deleted or renamed in the current version",
185+
"(RemovedOrRenamedPositionalParam): The 'azure.ai.formrecognizer.class_name' method 'two' had its parameter 'testing2' of kind 'positional_or_keyword' deleted or renamed in the current version",
186+
"(RemovedOrRenamedPositionalParam): The function 'azure.ai.formrecognizer.my_function_name' had its parameter 'testing' of kind 'positional_or_keyword' deleted or renamed in the current version",
187+
"(RemovedOrRenamedPositionalParam): The function 'azure.ai.formrecognizer.my_function_name' had its parameter 'testing2' of kind 'positional_or_keyword' deleted or renamed in the current version"
164188
]
165189

166190
diff = jsondiff.diff(stable, current)

0 commit comments

Comments
 (0)