@@ -35,6 +35,9 @@ class ChangeType(str, Enum):
3535 ADDED_CLIENT_METHOD = "AddedClientMethod"
3636 ADDED_CLASS = "AddedClass"
3737 ADDED_CLASS_METHOD = "AddedClassMethod"
38+ ADDED_CLASS_METHOD_PARAMETER = "AddedClassMethodParameter"
39+ ADDED_CLASS_PROPERTY = "AddedClassProperty"
40+ ADDED_FUNCTION_PARAMETER = "AddedFunctionParameter"
3841
3942class BreakingChangesTracker :
4043 REMOVED_OR_RENAMED_CLIENT_MSG = \
@@ -103,6 +106,12 @@ class BreakingChangesTracker:
103106 "The model or publicly exposed class '{}.{}' was added in the current version"
104107 ADDED_CLASS_METHOD_MSG = \
105108 "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"
106115
107116
108117 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:
183192 stable_methods_node = stable_class_nodes [self .class_name ]["methods" ]
184193 for method_name , method_components in class_components .get ("methods" , {}).items ():
185194 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 )
196213 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 )
204240
205241
206242 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
504540 )
505541 )
506542
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+
507560 def check_positional_parameter_removed_or_renamed (
508561 self , param_type : str ,
509562 deleted : str ,
0 commit comments