@@ -35,6 +35,9 @@ class ChangeType(str, Enum):
35
35
ADDED_CLIENT_METHOD = "AddedClientMethod"
36
36
ADDED_CLASS = "AddedClass"
37
37
ADDED_CLASS_METHOD = "AddedClassMethod"
38
+ ADDED_CLASS_METHOD_PARAMETER = "AddedClassMethodParameter"
39
+ ADDED_CLASS_PROPERTY = "AddedClassProperty"
40
+ ADDED_FUNCTION_PARAMETER = "AddedFunctionParameter"
38
41
39
42
class BreakingChangesTracker :
40
43
REMOVED_OR_RENAMED_CLIENT_MSG = \
@@ -103,6 +106,12 @@ class BreakingChangesTracker:
103
106
"The model or publicly exposed class '{}.{}' was added in the current version"
104
107
ADDED_CLASS_METHOD_MSG = \
105
108
"The '{}.{}' method '{}' was added in the current version"
109
+ ADDED_CLASS_METHOD_PARAMETER_MSG = \
110
+ "The model or publicly exposed class '{}.{}' had property '{}' added in the {} method in the current version"
111
+ ADDED_FUNCTION_PARAMETER_MSG = \
112
+ "The function '{}.{}' had parameter '{}' added in the current version"
113
+ ADDED_CLASS_PROPERTY_MSG = \
114
+ "The model or publicly exposed class '{}.{}' had property '{}' added in the current version"
106
115
107
116
108
117
def __init__ (self , stable : Dict , current : Dict , diff : Dict , package_name : str , ** kwargs : Any ) -> None :
@@ -183,24 +192,51 @@ def run_non_breaking_class_level_diff_checks(self, module: Dict) -> None:
183
192
stable_methods_node = stable_class_nodes [self .class_name ]["methods" ]
184
193
for method_name , method_components in class_components .get ("methods" , {}).items ():
185
194
self .function_name = method_name
186
- if self .function_name not in stable_methods_node and \
187
- not isinstance (self .function_name , jsondiff .Symbol ):
188
- if self .class_name .endswith ("Client" ):
189
- # This is a new client method
190
- fa = (
191
- self .ADDED_CLIENT_METHOD_MSG ,
192
- ChangeType .ADDED_CLIENT_METHOD ,
193
- self .module_name , self .class_name , method_name
194
- )
195
- self .features_added .append (fa )
195
+ if not isinstance (self .function_name , jsondiff .Symbol ):
196
+ if self .function_name not in stable_methods_node :
197
+ if self .class_name .endswith ("Client" ):
198
+ # This is a new client method
199
+ fa = (
200
+ self .ADDED_CLIENT_METHOD_MSG ,
201
+ ChangeType .ADDED_CLIENT_METHOD ,
202
+ self .module_name , self .class_name , method_name
203
+ )
204
+ self .features_added .append (fa )
205
+ else :
206
+ # This is a new class method
207
+ fa = (
208
+ self .ADDED_CLASS_METHOD_MSG ,
209
+ ChangeType .ADDED_CLASS_METHOD ,
210
+ self .module_name , class_name , method_name
211
+ )
212
+ self .features_added .append (fa )
196
213
else :
197
- # This is a new class method
198
- fa = (
199
- self .ADDED_CLASS_METHOD_MSG ,
200
- ChangeType .ADDED_CLASS_METHOD ,
201
- self .module_name , class_name , method_name
202
- )
203
- self .features_added .append (fa )
214
+ # Check existing methods for new parameters
215
+ stable_parameters_node = stable_methods_node [self .function_name ]["parameters" ]
216
+ current_parameters_node = self .current [self .module_name ]["class_nodes" ][self .class_name ]["methods" ][self .function_name ]["parameters" ]
217
+ for param_name , param_components in method_components .get ("parameters" , {}).items ():
218
+ self .parameter_name = param_name
219
+ if self .parameter_name not in stable_parameters_node and \
220
+ not isinstance (self .parameter_name , jsondiff .Symbol ):
221
+ if self .function_name == "__init__" :
222
+ # If this is a new class property skip reporting it here and let the class properties check handle it.
223
+ # This is because we'll get multiple reports for the same new property if it's a parameter in __init__
224
+ # and a class level attribute.
225
+ if self .parameter_name in class_components .get ("properties" , {}).keys ():
226
+ continue
227
+ self .check_non_positional_parameter_added (
228
+ current_parameters_node [param_name ]
229
+ )
230
+ stable_property_node = stable_class_nodes [self .class_name ]["properties" ]
231
+ for property_name , property_components in class_components .get ("properties" , {}).items ():
232
+ if property_name not in stable_property_node and \
233
+ not isinstance (property_name , jsondiff .Symbol ):
234
+ fa = (
235
+ self .ADDED_CLASS_PROPERTY_MSG ,
236
+ ChangeType .ADDED_CLASS_PROPERTY ,
237
+ self .module_name , class_name , property_name
238
+ )
239
+ self .features_added .append (fa )
204
240
205
241
206
242
def run_class_level_diff_checks (self , module : Dict ) -> None :
@@ -504,6 +540,23 @@ def check_positional_parameter_added(self, current_parameters_node: Dict) -> Non
504
540
)
505
541
)
506
542
543
+ def check_non_positional_parameter_added (self , current_parameters_node : Dict ) -> None :
544
+ if current_parameters_node ["param_type" ] != "positional_or_keyword" :
545
+ if self .class_name :
546
+ self .features_added .append (
547
+ (
548
+ self .ADDED_CLASS_METHOD_PARAMETER_MSG , ChangeType .ADDED_CLASS_METHOD_PARAMETER ,
549
+ self .module_name , self .class_name , self .parameter_name , self .function_name
550
+ )
551
+ )
552
+ else :
553
+ self .features_added .append (
554
+ (
555
+ self .ADDED_FUNCTION_PARAMETER_MSG , ChangeType .ADDED_FUNCTION_PARAMETER ,
556
+ self .module_name , self .function_name , self .parameter_name
557
+ )
558
+ )
559
+
507
560
def check_positional_parameter_removed_or_renamed (
508
561
self , param_type : str ,
509
562
deleted : str ,
0 commit comments