|
| 1 | +# Breaking Changes Detector Tool |
| 2 | + |
| 3 | +The breaking changes tool compares the last stable/GA version of the library (if it exists) against the current code |
| 4 | +(in master) and reports any breaking changes found. |
| 5 | + |
| 6 | +## How to opt-in to running the tool in CI |
| 7 | + |
| 8 | +Add your package name to the `RUN_BREAKING_CHANGES_PACKAGES` found [here](https://github.com/Azure/azure-sdk-for-python/tree/master/scripts/breaking_changes_checker/breaking_changes_allowlist.py). |
| 9 | + |
| 10 | +## Run locally with tox |
| 11 | + |
| 12 | +**1) Install tox and tox-monorepo:** |
| 13 | + |
| 14 | +`pip install tox tox-monorepo` |
| 15 | + |
| 16 | +**2) Run the `breaking` environment.** |
| 17 | + |
| 18 | +Here we run the breaking changes tool against azure-storage-blob, for example: |
| 19 | + |
| 20 | +`C:\azure-sdk-for-python\sdk\storage\azure-storage-blob>tox -c ../../../eng/tox/tox.ini -e breaking` |
| 21 | + |
| 22 | + |
| 23 | +## Ignore a reported breaking change |
| 24 | + |
| 25 | +A breaking change reported by this tool may be an acceptable change. If it is an **approved** breaking change (signed off by architecture board) |
| 26 | +or a false positive, then you should add the breaking change to the ignore list. |
| 27 | + |
| 28 | +The ignore list is found [here](https://github.com/Azure/azure-sdk-for-python/tree/master/scripts/breaking_changes_checker/breaking_changes_allowlist.py). |
| 29 | + |
| 30 | +To add an ignore, you will need the identifier of the breaking change. This includes the breaking change type, |
| 31 | +module name, and optionally class and/or function name, in this order, e.g. |
| 32 | + |
| 33 | +`(breaking-change-type, module-name, class-name, function-name)` |
| 34 | + |
| 35 | +Add this signature as a list of tuples under your package name in the [IGNORE_BREAKING_CHANGES](https://github.com/Azure/azure-sdk-for-python/tree/master/scripts/breaking_changes_checker/breaking_changes_allowlist.py) dictionary. |
| 36 | +Note that the names used should be those from the _stable_ package. If I renamed my function from `begin_training` to |
| 37 | +`begin_train_model`, I would put `begin_training` as my function name. |
| 38 | + |
| 39 | +See ignore signature skeletons for each type of breaking change [below](#types-of-breaking-changes-detected) |
| 40 | + |
| 41 | +**Examples:** |
| 42 | + |
| 43 | +```python |
| 44 | +IGNORE_BREAKING_CHANGES = { |
| 45 | + "azure-ai-formrecognizer": [ |
| 46 | + ("RemovedOrRenamedClientMethod", "azure.ai.formrecognizer.aio", "FormTrainingClient", "begin_training"), |
| 47 | + ("RemovedOrRenamedClass", "azure.ai.formrecognizer", "FormElement"), |
| 48 | + ], |
| 49 | + "azure-storage-queue": [ |
| 50 | + ("RemovedOrRenamedModule", "azure.storage.queue.aio"), |
| 51 | + ("RemovedOrRenamedModuleLevelFunction", "azure.storage.queue", "generate_queue_sas") |
| 52 | + ] |
| 53 | +} |
| 54 | +``` |
| 55 | + |
| 56 | + |
| 57 | +## Types of Breaking Changes Detected |
| 58 | + |
| 59 | +> Note that this does not cover every kind of breaking change possible. |
| 60 | +
|
| 61 | +| Breaking Change Type | Explained (changes are relative to the stable/GA library version) | Ignore signature IF an approved breaking change or false positive | |
| 62 | +|------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| |
| 63 | +| RemovedOrRenamedModule | An entire module was removed or renamed in the current version. E.g. `aio` module was removed. | ("RemovedOrRenamedModule", "module-name") |
| 64 | +| RemovedOrRenamedClient | A client was removed or renamed in the current version. | ("RemovedOrRenamedClient", "module-name", "client-name") |
| 65 | +| RemovedOrRenamedClientMethod | A client method was removed or renamed in the current version. | ("RemovedOrRenamedClientMethod", "module-name", "client-name", "function-name") |
| 66 | +| RemovedOrRenamedClass | A model or publicly exposed class was removed or renamed in the current version. | ("RemovedOrRenamedClass", "module-name", "class-name") |
| 67 | +| RemovedOrRenamedClassMethod | A model or publicly exposed class' method was removed or renamed in the current version. | ("RemovedOrRenamedClassMethod", "module-name", "class-name", "function-name") |
| 68 | +| RemovedOrRenamedInstanceAttribute | An instance attribute was removed or renamed in the current version. | ("RemovedOrRenamedInstanceAttribute", "module-name", "class-name") |
| 69 | +| RemovedOrRenamedEnumValue | An enum value was removed or renamed in the current version | ("RemovedOrRenamedEnumValue", "module-name", "class-name") |
| 70 | +| RemovedOrRenamedModuleLevelFunction | A module level function was removed or renamed in the current version. | ("RemovedOrRenamedModuleLevelFunction", "module-name", "function-name") |
| 71 | +| RemovedOrRenamedPositionalParam | A positional parameter on a function was removed or renamed. | ("RemovedOrRenamedPositionalParam", "module-name", "class-name", "function-name") |
| 72 | +| AddedPositionalParam | `def my_function(param1) --> def my_function(param1, param2)` | ("AddedPositionalParam", "module-name", "class-name", "function-name") |
| 73 | +| RemovedParameterDefaultValue | `def my_function(param=None) --> def my_function(param)` | ("RemovedParameterDefaultValue", "module-name", "class-name", "function-name") |
| 74 | +| ChangedParameterDefaultValue | `def my_function(param="yellow") --> def my_function(param="blue")` | ("ChangedParameterDefaultValue", "module-name", "class-name", "function-name") |
| 75 | +| ChangedParameterOrdering | `def my_function(a, b, c=None) --> def my_function(b, c=None, a=None)` | ("ChangedParameterOrdering", "module-name", "class-name", "function-name") |
| 76 | +| RemovedFunctionKwargs | A function was changed to no longer accept keyword arguments. `def my_func(param, **kwargs) --> def my_func(param)` | ("RemovedFunctionKwargs", "module-name", "class-name", "function-name") |
| 77 | +| ChangedParameterKind | `def my_function(a, b, c) --> def my_function(a, b, *, c)` | ("ChangedParameterKind", "module-name", "class-name", "function-name") |
| 78 | +| ChangedFunctionKind | `async def my_function(param) -> def my_function(param)` | ("ChangedFunctionKind", "module-name", "class-name", "function-name") |
| 79 | + |
0 commit comments