azdev 0.1.82__tar.gz → 0.1.84__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. {azdev-0.1.82 → azdev-0.1.84}/HISTORY.rst +8 -0
  2. {azdev-0.1.82/azdev.egg-info → azdev-0.1.84}/PKG-INFO +9 -1
  3. {azdev-0.1.82 → azdev-0.1.84}/azdev/__init__.py +1 -1
  4. azdev-0.1.84/azdev/operations/breaking_change/__init__.py +321 -0
  5. azdev-0.1.84/azdev/operations/breaking_change/markdown_template.jinja2 +28 -0
  6. {azdev-0.1.82 → azdev-0.1.84/azdev.egg-info}/PKG-INFO +9 -1
  7. {azdev-0.1.82 → azdev-0.1.84}/azdev.egg-info/SOURCES.txt +2 -0
  8. {azdev-0.1.82 → azdev-0.1.84}/setup.py +2 -0
  9. {azdev-0.1.82 → azdev-0.1.84}/LICENSE +0 -0
  10. {azdev-0.1.82 → azdev-0.1.84}/MANIFEST.in +0 -0
  11. {azdev-0.1.82 → azdev-0.1.84}/README.md +0 -0
  12. {azdev-0.1.82 → azdev-0.1.84}/README.rst +0 -0
  13. {azdev-0.1.82 → azdev-0.1.84}/azdev/__main__.py +0 -0
  14. {azdev-0.1.82 → azdev-0.1.84}/azdev/commands.py +0 -0
  15. {azdev-0.1.82 → azdev-0.1.84}/azdev/completer.py +0 -0
  16. {azdev-0.1.82 → azdev-0.1.84}/azdev/config/__init__.py +0 -0
  17. {azdev-0.1.82 → azdev-0.1.84}/azdev/config/cli.flake8 +0 -0
  18. {azdev-0.1.82 → azdev-0.1.84}/azdev/config/cli_pylintrc +0 -0
  19. {azdev-0.1.82 → azdev-0.1.84}/azdev/config/ext.flake8 +0 -0
  20. {azdev-0.1.82 → azdev-0.1.84}/azdev/config/ext_pylintrc +0 -0
  21. {azdev-0.1.82 → azdev-0.1.84}/azdev/help.py +0 -0
  22. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/HISTORY.rst +0 -0
  23. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/README.rst +0 -0
  24. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/_client_factory.py +0 -0
  25. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/_help.py +0 -0
  26. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/_params.py +0 -0
  27. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/_validators.py +0 -0
  28. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/azext_metadata.json +0 -0
  29. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/blank__init__.py +0 -0
  30. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/commands.py +0 -0
  31. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/custom.py +0 -0
  32. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/module__init__.py +0 -0
  33. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/pkg_declare__init__.py +0 -0
  34. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/setup.cfg +0 -0
  35. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/setup.py +0 -0
  36. {azdev-0.1.82 → azdev-0.1.84}/azdev/mod_templates/test_service_scenario.py +0 -0
  37. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/__init__.py +0 -0
  38. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/__init__.py +0 -0
  39. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/_macros.j2 +0 -0
  40. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/cmdcov.py +0 -0
  41. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/component.css +0 -0
  42. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/component.js +0 -0
  43. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/favicon.ico +0 -0
  44. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/index.j2 +0 -0
  45. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/index2.j2 +0 -0
  46. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/cmdcov/module.j2 +0 -0
  47. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/code_gen.py +0 -0
  48. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/command_change/__init__.py +0 -0
  49. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/command_change/custom.py +0 -0
  50. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/command_change/util.py +0 -0
  51. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/constant.py +0 -0
  52. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/extensions/__init__.py +0 -0
  53. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/extensions/util.py +0 -0
  54. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/extensions/version_upgrade.py +0 -0
  55. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/help/__init__.py +0 -0
  56. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/help/refdoc/__init__.py +0 -0
  57. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/help/refdoc/conf.py +0 -0
  58. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/legal.py +0 -0
  59. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/__init__.py +0 -0
  60. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/linter.py +0 -0
  61. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/pylint_checkers/__init__.py +0 -0
  62. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/pylint_checkers/show_command.py +0 -0
  63. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rule_decorators.py +0 -0
  64. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rules/__init__.py +0 -0
  65. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rules/ci_exclusions.yml +0 -0
  66. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rules/command_coverage_rules.py +0 -0
  67. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rules/command_group_rules.py +0 -0
  68. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rules/command_rules.py +0 -0
  69. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rules/help_rules.py +0 -0
  70. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rules/linter_exclusions.yml +0 -0
  71. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/rules/parameter_rules.py +0 -0
  72. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/linter/util.py +0 -0
  73. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/performance.py +0 -0
  74. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/pypi.py +0 -0
  75. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/python_sdk.py +0 -0
  76. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/regex.py +0 -0
  77. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/resource.py +0 -0
  78. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/secret.py +0 -0
  79. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/setup.py +0 -0
  80. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/statistics/__init__.py +0 -0
  81. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/statistics/util.py +0 -0
  82. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/style.py +0 -0
  83. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/testtool/__init__.py +0 -0
  84. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/testtool/incremental_strategy.py +0 -0
  85. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/testtool/profile_context.py +0 -0
  86. {azdev-0.1.82 → azdev-0.1.84}/azdev/operations/testtool/pytest_runner.py +0 -0
  87. {azdev-0.1.82 → azdev-0.1.84}/azdev/params.py +0 -0
  88. {azdev-0.1.82 → azdev-0.1.84}/azdev/transformers.py +0 -0
  89. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/__init__.py +0 -0
  90. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/command.py +0 -0
  91. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/config.py +0 -0
  92. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/const.py +0 -0
  93. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/display.py +0 -0
  94. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/git_util.py +0 -0
  95. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/path.py +0 -0
  96. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/pypi.py +0 -0
  97. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/testing.py +0 -0
  98. {azdev-0.1.82 → azdev-0.1.84}/azdev/utilities/tools.py +0 -0
  99. {azdev-0.1.82 → azdev-0.1.84}/azdev.egg-info/dependency_links.txt +0 -0
  100. {azdev-0.1.82 → azdev-0.1.84}/azdev.egg-info/entry_points.txt +0 -0
  101. {azdev-0.1.82 → azdev-0.1.84}/azdev.egg-info/requires.txt +0 -0
  102. {azdev-0.1.82 → azdev-0.1.84}/azdev.egg-info/top_level.txt +0 -0
  103. {azdev-0.1.82 → azdev-0.1.84}/pyproject.toml +0 -0
  104. {azdev-0.1.82 → azdev-0.1.84}/setup.cfg +0 -0
@@ -2,6 +2,14 @@
2
2
 
3
3
  Release History
4
4
  ===============
5
+ 0.1.84
6
+ ++++++
7
+ * `azdev generate-breaking-change-report`: Fix `azdev -h` error caused by global importing `azure.cli.core` in `breaking-change.py` module.
8
+
9
+ 0.1.83
10
+ ++++++
11
+ * `azdev generate-breaking-change-report`: Fix `azdev.operations.breaking_change` not included in `setup.py`.
12
+
5
13
  0.1.82
6
14
  ++++++
7
15
  * `azdev generate-breaking-change-report`: New command to collect upcoming breaking changes from codebase.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azdev
3
- Version: 0.1.82
3
+ Version: 0.1.84
4
4
  Summary: Microsoft Azure CLI Developer Tools
5
5
  Home-page: https://github.com/Azure/azure-cli-dev-tools
6
6
  Author: Microsoft Corporation
@@ -148,6 +148,14 @@ License
148
148
 
149
149
  Release History
150
150
  ===============
151
+ 0.1.84
152
+ ++++++
153
+ * `azdev generate-breaking-change-report`: Fix `azdev -h` error caused by global importing `azure.cli.core` in `breaking-change.py` module.
154
+
155
+ 0.1.83
156
+ ++++++
157
+ * `azdev generate-breaking-change-report`: Fix `azdev.operations.breaking_change` not included in `setup.py`.
158
+
151
159
  0.1.82
152
160
  ++++++
153
161
  * `azdev generate-breaking-change-report`: New command to collect upcoming breaking changes from codebase.
@@ -4,4 +4,4 @@
4
4
  # license information.
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __VERSION__ = '0.1.82'
7
+ __VERSION__ = '0.1.84'
@@ -0,0 +1,321 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # -----------------------------------------------------------------------------
6
+ import re
7
+ import time
8
+ from collections import defaultdict
9
+ from importlib import import_module
10
+
11
+ import packaging.version
12
+ from knack.log import get_logger
13
+
14
+ from azdev.operations.statistics import _create_invoker_and_load_cmds # pylint: disable=protected-access
15
+ from azdev.utilities import require_azure_cli, display, heading, output, calc_selected_mod_names
16
+
17
+ # pylint: disable=no-else-return
18
+
19
+ logger = get_logger(__name__)
20
+
21
+
22
+ class BreakingChangeItem:
23
+ def __init__(self, module, command, detail, target_version):
24
+ self.module = module
25
+ self.command = command
26
+ self.detail = detail
27
+ self.target_version = target_version
28
+
29
+
30
+ def _load_commands():
31
+ start = time.time()
32
+ display('Initializing with loading command table...')
33
+ from azure.cli.core import get_default_cli # pylint: disable=import-error
34
+ az_cli = get_default_cli()
35
+
36
+ # load commands, args, and help
37
+ # The arguments must be loaded before the `EVENT_INVOKER_POST_CMD_TBL_CREATE` event.
38
+ # This is because we generate the `deprecate_info` and `upcoming_breaking_change` tags from pre-announcement data
39
+ # during the event.
40
+ # If the arguments are not loaded beforehand, this information will not be included.
41
+ _create_invoker_and_load_cmds(az_cli, load_arguments=True)
42
+
43
+ stop = time.time()
44
+ logger.info('Commands loaded in %i sec', stop - start)
45
+ display('Commands loaded in {} sec'.format(stop - start))
46
+ command_loader = az_cli.invocation.commands_loader
47
+
48
+ if not command_loader.command_table:
49
+ logger.warning('No commands selected to check.')
50
+ return command_loader
51
+
52
+
53
+ def _handle_custom_breaking_changes(module, command):
54
+ """
55
+ Collect Custom Pre-Announcement defined in `_breaking_change.py`
56
+ :param module: module name
57
+ :param command: command name
58
+ :return: A generated returns Custom Pre-Announcements defined in `_breaking_change.py`
59
+ """
60
+ from azure.cli.core.breaking_change import upcoming_breaking_changes
61
+ yield from _handle_custom_breaking_change(module, command, upcoming_breaking_changes.get(command))
62
+ for key in upcoming_breaking_changes:
63
+ if key.startswith(command + '.'):
64
+ yield from _handle_custom_breaking_change(module, command, upcoming_breaking_changes[key])
65
+
66
+
67
+ def _handle_custom_breaking_change(module, command, breaking_change):
68
+ """
69
+ Handle a BreakingChange item defined in `_breaking_change.py`. We need this method because the item stored could
70
+ be a list or object
71
+ """
72
+ from azure.cli.core.breaking_change import BreakingChange
73
+ if isinstance(breaking_change, str):
74
+ yield BreakingChangeItem(module, command, breaking_change, None)
75
+ elif isinstance(breaking_change, BreakingChange):
76
+ yield BreakingChangeItem(module, command, breaking_change.message, breaking_change.target_version.version())
77
+ elif isinstance(breaking_change, list):
78
+ for bc in breaking_change:
79
+ yield from _handle_custom_breaking_change(module, command, bc)
80
+
81
+
82
+ def _handle_status_tag(module, command, status_tag):
83
+ from knack.deprecation import Deprecated
84
+ from azure.cli.core.breaking_change import MergedStatusTag, UpcomingBreakingChangeTag, TargetVersion
85
+
86
+ if isinstance(status_tag, MergedStatusTag):
87
+ for tag in status_tag.tags:
88
+ yield from _handle_status_tag(module, command, tag)
89
+ else:
90
+ detail = status_tag._get_message(status_tag) # pylint: disable=protected-access
91
+ version = None
92
+ if isinstance(status_tag, Deprecated):
93
+ version = status_tag.expiration
94
+ elif isinstance(status_tag, UpcomingBreakingChangeTag):
95
+ if isinstance(status_tag.target_version, TargetVersion):
96
+ version = status_tag.target_version.version()
97
+ elif isinstance(status_tag.target_version, str):
98
+ version = status_tag.target_version
99
+ if version is None:
100
+ version_match = re.search(r'\d+\.\d+\.\d+', detail)
101
+ if version_match:
102
+ version = version_match.group(0)
103
+ yield BreakingChangeItem(module, command, detail, version)
104
+
105
+
106
+ def _handle_command_deprecation(module, command, deprecate_info):
107
+ yield from _handle_status_tag(module, command, deprecate_info)
108
+
109
+
110
+ def _calc_target_of_arg_deprecation(arg_name, arg_settings):
111
+ from knack.deprecation import Deprecated
112
+
113
+ option_str_list = []
114
+ depr = arg_settings.get('deprecate_info')
115
+ for option in arg_settings.get('option_list', []):
116
+ if isinstance(option, str):
117
+ option_str_list.append(option)
118
+ elif isinstance(option, Deprecated):
119
+ option_str_list.append(option.target)
120
+ if option_str_list:
121
+ return '/'.join(option_str_list)
122
+ elif hasattr(depr, 'target'):
123
+ return depr.target
124
+ else:
125
+ return arg_name
126
+
127
+
128
+ def _handle_arg_deprecation(module, command, target, deprecation_info):
129
+ deprecation_info.target = target
130
+ yield from _handle_status_tag(module, command, deprecation_info)
131
+
132
+
133
+ def _handle_options_deprecation(module, command, options):
134
+ from knack.deprecation import Deprecated
135
+
136
+ deprecate_option_map = defaultdict(lambda: [])
137
+ for option in options:
138
+ if isinstance(option, Deprecated):
139
+ key = f'{option.redirect}|{option.expiration}|{option.hide}'
140
+ deprecate_option_map[key].append(option)
141
+ for _, depr_list in deprecate_option_map.items():
142
+ target = '/'.join([depr.target for depr in depr_list])
143
+ depr = depr_list[0]
144
+ depr.target = target
145
+ yield from _handle_status_tag(module, command, depr)
146
+
147
+
148
+ def _handle_command_breaking_changes(module, command, command_info, source):
149
+ if source == "deprecate_info":
150
+ if hasattr(command_info, "deprecate_info") and command_info.deprecate_info:
151
+ yield from _handle_command_deprecation(module, command, command_info.deprecate_info)
152
+
153
+ for argument_name, argument in command_info.arguments.items():
154
+ arg_settings = argument.type.settings
155
+ depr = arg_settings.get('deprecate_info')
156
+ if depr:
157
+ bc_target = _calc_target_of_arg_deprecation(argument_name, arg_settings)
158
+ yield from _handle_arg_deprecation(module, command, bc_target, depr)
159
+ yield from _handle_options_deprecation(module, command, arg_settings.get('options_list', []))
160
+ if source == "pre_announce":
161
+ yield from _handle_custom_breaking_changes(module, command)
162
+
163
+
164
+ def _handle_command_group_deprecation(module, command, deprecate_info):
165
+ yield from _handle_status_tag(module, command, deprecate_info)
166
+
167
+
168
+ def _handle_command_group_breaking_changes(module, command_group_name, command_group_info, source):
169
+ if source == "deprecate_info":
170
+ if hasattr(command_group_info, 'group_kwargs') and command_group_info.group_kwargs.get('deprecate_info'):
171
+ yield from _handle_command_group_deprecation(module, command_group_name,
172
+ command_group_info.group_kwargs.get('deprecate_info'))
173
+
174
+ if source == "pre_announce":
175
+ yield from _handle_custom_breaking_changes(module, command_group_name)
176
+
177
+
178
+ def _get_mod_ext_name(loader):
179
+ # There could be different name with module name in extension.
180
+ # For example, module name of `application-insights` is azext_applicationinsights
181
+ try:
182
+ module_source = next(iter(loader.command_table.values())).command_source
183
+ if isinstance(module_source, str):
184
+ return module_source
185
+ else:
186
+ return module_source.extension_name
187
+ except StopIteration:
188
+ logger.warning('There is no command in Loader(%s)', loader)
189
+ mod_path = loader.__class__.__module__
190
+ mod_name = mod_path.rsplit('.', maxsplit=1)[-1]
191
+ mod_name = mod_name.replace('azext_', '', 1)
192
+ return mod_name
193
+
194
+
195
+ def _iter_and_prepare_module_loader(command_loader, selected_mod_names):
196
+ for loader in command_loader.loaders:
197
+ module_path = loader.__class__.__module__
198
+ module_name = module_path.rsplit('.', maxsplit=1)[-1]
199
+ if module_name and module_name not in selected_mod_names:
200
+ continue
201
+
202
+ _breaking_change_module = f'{module_path}._breaking_change'
203
+ try:
204
+ import_module(_breaking_change_module)
205
+ except ImportError:
206
+ pass
207
+ loader.skip_applicability = True
208
+
209
+ yield module_name, loader
210
+
211
+
212
+ def _handle_module(module, loader, source):
213
+ start = time.time()
214
+
215
+ for command, command_info in loader.command_table.items():
216
+ yield from _handle_command_breaking_changes(module, command, command_info, source)
217
+
218
+ for command_group_name, command_group in loader.command_group_table.items():
219
+ yield from _handle_command_group_breaking_changes(module, command_group_name, command_group, source)
220
+
221
+ stop = time.time()
222
+ logger.info('Module %s finished in %i sec', module, stop - start)
223
+ display('Module {} finished loaded in {} sec'.format(module, stop - start))
224
+
225
+
226
+ def _handle_core(source):
227
+ start = time.time()
228
+ if source == "pre_announce":
229
+ core_module = 'azure.cli.core'
230
+ _breaking_change_module = f'{core_module}._breaking_change'
231
+ try:
232
+ import_module(_breaking_change_module)
233
+ except ImportError:
234
+ pass
235
+
236
+ yield from _handle_custom_breaking_changes('core', 'core')
237
+
238
+ stop = time.time()
239
+ logger.info('Core finished in %i sec', stop - start)
240
+ display('Core finished loaded in {} sec'.format(stop - start))
241
+
242
+
243
+ def _handle_upcoming_breaking_changes(selected_mod_names, source):
244
+ command_loader = _load_commands()
245
+
246
+ if 'core' in selected_mod_names or 'azure-cli-core' in selected_mod_names:
247
+ yield from _handle_core(source)
248
+
249
+ for module, loader in _iter_and_prepare_module_loader(command_loader, selected_mod_names):
250
+ yield from _handle_module(module, loader, source)
251
+
252
+
253
+ def _filter_breaking_changes(iterator, max_version=None):
254
+ if not max_version:
255
+ yield from iterator
256
+ return
257
+ try:
258
+ parsed_max_version = packaging.version.parse(max_version)
259
+ except packaging.version.InvalidVersion:
260
+ logger.warning('Invalid target version: %s; '
261
+ 'Will present all upcoming breaking changes as alternative.', max_version)
262
+ yield from iterator
263
+ return
264
+ for item in iterator:
265
+ if item.target_version:
266
+ try:
267
+ target_version = packaging.version.parse(item.target_version)
268
+ if target_version <= parsed_max_version:
269
+ yield item
270
+ except packaging.version.InvalidVersion:
271
+ logger.warning('Invalid version from `%s`: %s', item.command, item.target_version)
272
+
273
+
274
+ # pylint: disable=unnecessary-lambda-assignment
275
+ def _group_breaking_change_items(iterator, group_by_version=False):
276
+ if group_by_version:
277
+ upcoming_breaking_changes = defaultdict( # module to command
278
+ lambda: defaultdict( # command to version
279
+ lambda: defaultdict( # version to list of breaking changes
280
+ lambda: [])))
281
+ else:
282
+ upcoming_breaking_changes = defaultdict( # module to command
283
+ lambda: defaultdict( # command to list of breaking changes
284
+ lambda: []))
285
+ for item in iterator:
286
+ version = item.target_version if item.target_version else 'Unspecific'
287
+ if group_by_version:
288
+ upcoming_breaking_changes[item.module][item.command][version].append(item.detail)
289
+ else:
290
+ upcoming_breaking_changes[item.module][item.command].append(item.detail)
291
+ return upcoming_breaking_changes
292
+
293
+
294
+ def collect_upcoming_breaking_changes(modules=None, target_version='NextWindow', source=None, group_by_version=None,
295
+ output_format='structure'):
296
+ if target_version == 'NextWindow':
297
+ from azure.cli.core.breaking_change import NEXT_BREAKING_CHANGE_RELEASE
298
+ target_version = NEXT_BREAKING_CHANGE_RELEASE
299
+ elif target_version.lower() == 'none':
300
+ target_version = None
301
+
302
+ require_azure_cli()
303
+
304
+ selected_mod_names = calc_selected_mod_names(modules)
305
+
306
+ if selected_mod_names:
307
+ display('Modules selected: {}\n'.format(', '.join(selected_mod_names)))
308
+
309
+ heading('Collecting Breaking Change Pre-announcement')
310
+ breaking_changes = _handle_upcoming_breaking_changes(selected_mod_names, source)
311
+ breaking_changes = _filter_breaking_changes(breaking_changes, target_version)
312
+ breaking_changes = _group_breaking_change_items(breaking_changes, group_by_version)
313
+ if output_format == 'structure':
314
+ return breaking_changes
315
+ elif output_format == 'markdown':
316
+ from jinja2 import Environment, PackageLoader
317
+ env = Environment(loader=PackageLoader('azdev', 'operations/breaking_change'),
318
+ trim_blocks=True)
319
+ template = env.get_template('markdown_template.jinja2')
320
+ output(template.render({'module_bc': breaking_changes}))
321
+ return None
@@ -0,0 +1,28 @@
1
+ # Upcoming breaking changes in Azure CLI
2
+
3
+ {% for module, command_bc in module_bc.items() -%}
4
+ ## {{ module }}
5
+
6
+ {% for command, multi_version_bcs in command_bc.items() -%}
7
+ {% if not (module == 'core' and command == 'core') -%}
8
+ ### `{{ command }}`
9
+
10
+ {% endif -%}
11
+ {% if multi_version_bcs is mapping -%}
12
+ {% for version, bcs in multi_version_bcs | dictsort -%}
13
+ ###{%- if not (module == 'core' and command == 'core') -%}#{%- endif %} Deprecated in {{ version }}
14
+
15
+ {% for bc in bcs -%}
16
+ - {{ bc }}
17
+ {% endfor %}
18
+
19
+ {% endfor -%}
20
+ {% else -%}
21
+
22
+ {% for bc in multi_version_bcs -%}
23
+ - {{ bc }}
24
+ {% endfor %}
25
+
26
+ {% endif -%}
27
+ {% endfor -%}
28
+ {% endfor -%}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azdev
3
- Version: 0.1.82
3
+ Version: 0.1.84
4
4
  Summary: Microsoft Azure CLI Developer Tools
5
5
  Home-page: https://github.com/Azure/azure-cli-dev-tools
6
6
  Author: Microsoft Corporation
@@ -148,6 +148,14 @@ License
148
148
 
149
149
  Release History
150
150
  ===============
151
+ 0.1.84
152
+ ++++++
153
+ * `azdev generate-breaking-change-report`: Fix `azdev -h` error caused by global importing `azure.cli.core` in `breaking-change.py` module.
154
+
155
+ 0.1.83
156
+ ++++++
157
+ * `azdev generate-breaking-change-report`: Fix `azdev.operations.breaking_change` not included in `setup.py`.
158
+
151
159
  0.1.82
152
160
  ++++++
153
161
  * `azdev generate-breaking-change-report`: New command to collect upcoming breaking changes from codebase.
@@ -51,6 +51,8 @@ azdev/operations/resource.py
51
51
  azdev/operations/secret.py
52
52
  azdev/operations/setup.py
53
53
  azdev/operations/style.py
54
+ azdev/operations/breaking_change/__init__.py
55
+ azdev/operations/breaking_change/markdown_template.jinja2
54
56
  azdev/operations/cmdcov/__init__.py
55
57
  azdev/operations/cmdcov/_macros.j2
56
58
  azdev/operations/cmdcov/cmdcov.py
@@ -62,6 +62,7 @@ setup(
62
62
  'azdev.operations.extensions',
63
63
  'azdev.operations.statistics',
64
64
  'azdev.operations.command_change',
65
+ 'azdev.operations.breaking_change',
65
66
  'azdev.operations.cmdcov',
66
67
  'azdev.utilities',
67
68
  ],
@@ -93,6 +94,7 @@ setup(
93
94
  'azdev.mod_templates': ['*.*'],
94
95
  'azdev.operations.linter.rules': ['ci_exclusions.yml'],
95
96
  'azdev.operations.cmdcov': ['*.*'],
97
+ 'azdev.operations.breaking_change': ['*.*'],
96
98
  },
97
99
  include_package_data=True,
98
100
  entry_points={
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes