azdev 0.2.13__tar.gz → 0.2.14__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 (110) hide show
  1. {azdev-0.2.13 → azdev-0.2.14}/HISTORY.rst +18 -0
  2. {azdev-0.2.13/azdev.egg-info → azdev-0.2.14}/PKG-INFO +27 -19
  3. {azdev-0.2.13 → azdev-0.2.14}/azdev/__init__.py +1 -1
  4. azdev-0.2.14/azdev/mod_templates/pyproject.toml +49 -0
  5. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/__init__.py +1 -2
  6. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/cmdcov.py +1 -2
  7. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/code_gen.py +1 -3
  8. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/command_change/__init__.py +3 -14
  9. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/command_change/custom.py +28 -0
  10. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/extensions/__init__.py +12 -29
  11. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/extensions/metadata.py +21 -4
  12. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/latest_index.py +1 -4
  13. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/pypi.py +56 -50
  14. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/regex.py +1 -2
  15. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/statistics/__init__.py +3 -14
  16. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/__init__.py +12 -0
  17. azdev-0.2.14/azdev/utilities/packaging.py +184 -0
  18. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/path.py +3 -2
  19. {azdev-0.2.13 → azdev-0.2.14/azdev.egg-info}/PKG-INFO +27 -19
  20. {azdev-0.2.13 → azdev-0.2.14}/azdev.egg-info/SOURCES.txt +2 -3
  21. {azdev-0.2.13 → azdev-0.2.14}/azdev.egg-info/requires.txt +6 -2
  22. azdev-0.2.14/pyproject.toml +111 -0
  23. azdev-0.2.13/azdev/mod_templates/setup.cfg +0 -0
  24. azdev-0.2.13/azdev/mod_templates/setup.py +0 -60
  25. azdev-0.2.13/pyproject.toml +0 -9
  26. azdev-0.2.13/setup.py +0 -112
  27. {azdev-0.2.13 → azdev-0.2.14}/LICENSE +0 -0
  28. {azdev-0.2.13 → azdev-0.2.14}/MANIFEST.in +0 -0
  29. {azdev-0.2.13 → azdev-0.2.14}/README.md +0 -0
  30. {azdev-0.2.13 → azdev-0.2.14}/README.rst +0 -0
  31. {azdev-0.2.13 → azdev-0.2.14}/azdev/__main__.py +0 -0
  32. {azdev-0.2.13 → azdev-0.2.14}/azdev/commands.py +0 -0
  33. {azdev-0.2.13 → azdev-0.2.14}/azdev/completer.py +0 -0
  34. {azdev-0.2.13 → azdev-0.2.14}/azdev/config/__init__.py +0 -0
  35. {azdev-0.2.13 → azdev-0.2.14}/azdev/config/cli.flake8 +0 -0
  36. {azdev-0.2.13 → azdev-0.2.14}/azdev/config/cli_pylintrc +0 -0
  37. {azdev-0.2.13 → azdev-0.2.14}/azdev/config/ext.flake8 +0 -0
  38. {azdev-0.2.13 → azdev-0.2.14}/azdev/config/ext_pylintrc +0 -0
  39. {azdev-0.2.13 → azdev-0.2.14}/azdev/help.py +0 -0
  40. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/HISTORY.rst +0 -0
  41. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/README.rst +0 -0
  42. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/_client_factory.py +0 -0
  43. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/_help.py +0 -0
  44. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/_params.py +0 -0
  45. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/_validators.py +0 -0
  46. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/azext_metadata.json +0 -0
  47. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/blank__init__.py +0 -0
  48. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/commands.py +0 -0
  49. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/custom.py +0 -0
  50. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/module__init__.py +0 -0
  51. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/pkg_declare__init__.py +0 -0
  52. {azdev-0.2.13 → azdev-0.2.14}/azdev/mod_templates/test_service_scenario.py +0 -0
  53. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/__init__.py +0 -0
  54. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/breaking_change/__init__.py +0 -0
  55. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/breaking_change/markdown_template.jinja2 +0 -0
  56. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/_macros.j2 +0 -0
  57. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/component.css +0 -0
  58. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/component.js +0 -0
  59. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/favicon.ico +0 -0
  60. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/index.j2 +0 -0
  61. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/index2.j2 +0 -0
  62. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/cmdcov/module.j2 +0 -0
  63. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/command_change/util.py +0 -0
  64. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/constant.py +0 -0
  65. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/extensions/util.py +0 -0
  66. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/extensions/version_upgrade.py +0 -0
  67. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/help/__init__.py +0 -0
  68. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/help/refdoc/__init__.py +0 -0
  69. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/help/refdoc/conf.py +0 -0
  70. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/legal.py +0 -0
  71. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/__init__.py +0 -0
  72. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/data/cmd_example_config.json +0 -0
  73. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/linter.py +0 -0
  74. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/pylint_checkers/__init__.py +0 -0
  75. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/pylint_checkers/show_command.py +0 -0
  76. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rule_decorators.py +0 -0
  77. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rules/__init__.py +0 -0
  78. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rules/ci_exclusions.yml +0 -0
  79. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rules/command_coverage_rules.py +0 -0
  80. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rules/command_group_rules.py +0 -0
  81. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rules/command_rules.py +0 -0
  82. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rules/help_rules.py +0 -0
  83. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rules/linter_exclusions.yml +0 -0
  84. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/rules/parameter_rules.py +0 -0
  85. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/linter/util.py +0 -0
  86. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/performance.py +0 -0
  87. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/python_sdk.py +0 -0
  88. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/resource.py +0 -0
  89. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/secret.py +0 -0
  90. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/setup.py +0 -0
  91. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/statistics/util.py +0 -0
  92. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/style.py +0 -0
  93. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/testtool/__init__.py +0 -0
  94. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/testtool/incremental_strategy.py +0 -0
  95. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/testtool/profile_context.py +0 -0
  96. {azdev-0.2.13 → azdev-0.2.14}/azdev/operations/testtool/pytest_runner.py +0 -0
  97. {azdev-0.2.13 → azdev-0.2.14}/azdev/params.py +0 -0
  98. {azdev-0.2.13 → azdev-0.2.14}/azdev/transformers.py +0 -0
  99. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/command.py +0 -0
  100. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/config.py +0 -0
  101. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/const.py +0 -0
  102. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/display.py +0 -0
  103. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/git_util.py +0 -0
  104. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/pypi.py +0 -0
  105. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/testing.py +0 -0
  106. {azdev-0.2.13 → azdev-0.2.14}/azdev/utilities/tools.py +0 -0
  107. {azdev-0.2.13 → azdev-0.2.14}/azdev.egg-info/dependency_links.txt +0 -0
  108. {azdev-0.2.13 → azdev-0.2.14}/azdev.egg-info/entry_points.txt +0 -0
  109. {azdev-0.2.13 → azdev-0.2.14}/azdev.egg-info/top_level.txt +0 -0
  110. {azdev-0.2.13 → azdev-0.2.14}/setup.cfg +0 -0
@@ -2,6 +2,24 @@
2
2
 
3
3
  Release History
4
4
  ===============
5
+ 0.2.14
6
+ ++++++
7
+ * `azdev extension add`/`list`/`build`: discover extensions by packaging configuration instead of globbing for `setup.py`, so extensions that have migrated to `pyproject.toml` are found. Previously a migrated extension was reported as `extension(s) not found`.
8
+ * `azdev extension build`: build through the PEP 517 frontend for `pyproject.toml` extensions, keeping `setup.py bdist_wheel` for the rest. `--no-isolation` preserves the previous behaviour for extensions whose build imports their own `azext_*` package.
9
+ * `azdev extension add`/`azdev setup`: generate `*.egg-info` without `setup.py`, so `az` can still discover a migrated extension as a development extension. `azure-cli-core` finds dev extensions by globbing `*.egg-info`.
10
+ * `azdev extension create`: scaffold `pyproject.toml` instead of `setup.py` and `setup.cfg`.
11
+ * `azdev extension update-index`: keep the home page under the `Home` label for `pyproject.toml` extensions, which declare `[project.urls] Homepage` rather than `url=`, so `index.json` entries do not change shape on migration. Also accept `License-Expression` in place of `License`.
12
+ * `azdev extension build`: fix the build-failure handler reading `CommandResultItem.output`, which does not exist. A failed build raised `AttributeError` instead of showing the build log.
13
+ * Migrate azdev and `azure-cli-diff-tool` to PEP 621 `pyproject.toml`. Both `setup.py` files are gone; wheel contents are unchanged.
14
+ * Build with `python -m build` in CI, and check metadata with `twine check` instead of `setup.py check -r -s`.
15
+ * Drop the `setuptools<80` cap. It guarded `setup.py develop`, which azdev stopped calling in 0.2.12. The `>=78.1.1` floor for CVE-2025-47273 stays.
16
+ * Fix `azdev cli check-versions` running `python -m setup.py bdist_wheel`, which could never work.
17
+ * Fix `azure-cli-diff-tool`'s sdist missing `HISTORY.rst`, which broke building a wheel from it.
18
+ * Fix `scripts/ci/extract_version.sh` producing an empty version.
19
+ * Set `Description-Content-Type` and stop reading the long description through `codecs.open`, which wrote `\r\r\n` line endings on Windows.
20
+ * `azdev command-change meta-export`: record `action` for parameters that take no value. Flag actions are written as `store_true`, `store_false`, `store_const` or `count`, including preview, experimental and deprecated arguments, whose action knack wraps in a subclass. Other actions registered with `nargs=0`, such as the one for `--acquire-policy-token`, are written as `store_true`. Readers of the metadata can now tell a flag such as `--enable-fips` from an option that takes a value. Other parameters get no `action` key, and `meta-diff` doesn't compare it.
21
+ * `azdev test`: cap `pytest` below 9.2. On Linux and macOS `azdev test` runs pytest with `--forked`, and pytest-forked 1.7.5 can't serialise pytest 9.2 reports, so every test failed with "EXITED with status 3". The cap stays until pytest-forked supports pytest 9.2 (pytest-dev/pytest-forked#113).
22
+
5
23
  0.2.13
6
24
  ++++++
7
25
  * `azdev extension add` / `azdev setup`: regenerate `*.egg-info` after the editable install so development extensions stay discoverable. `--no-build-isolation` (0.2.12) means pip no longer leaves an `*.egg-info` in the source tree, which made `azdev extension add` a silent no-op for discovery. (#554)
@@ -1,17 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: azdev
3
- Version: 0.2.13
3
+ Version: 0.2.14
4
4
  Summary: Microsoft Azure CLI Developer Tools
5
- Home-page: https://github.com/Azure/azure-cli-dev-tools
6
- Author: Microsoft Corporation
7
- Author-email: azpycli@microsoft.com
8
- License: MIT
5
+ Author-email: Microsoft Corporation <azpycli@microsoft.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Azure/azure-cli-dev-tools
9
8
  Keywords: azure
10
9
  Classifier: Development Status :: 5 - Production/Stable
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: Topic :: Software Development :: Build Tools
13
12
  Classifier: Environment :: Console
14
- Classifier: License :: OSI Approved :: MIT License
15
13
  Classifier: Natural Language :: English
16
14
  Classifier: Programming Language :: Python :: 3.10
17
15
  Classifier: Programming Language :: Python :: 3.11
@@ -19,8 +17,10 @@ Classifier: Programming Language :: Python :: 3.12
19
17
  Classifier: Programming Language :: Python :: 3.13
20
18
  Classifier: Programming Language :: Python :: 3.14
21
19
  Requires-Python: >=3.10
20
+ Description-Content-Type: text/x-rst
22
21
  License-File: LICENSE
23
22
  Requires-Dist: azure-multiapi-storage
23
+ Requires-Dist: build
24
24
  Requires-Dist: docutils
25
25
  Requires-Dist: flake8
26
26
  Requires-Dist: gitpython
@@ -29,7 +29,7 @@ Requires-Dist: knack
29
29
  Requires-Dist: pylint<5,>=4
30
30
  Requires-Dist: pytest-xdist
31
31
  Requires-Dist: pytest-forked
32
- Requires-Dist: pytest>=5.0.0
32
+ Requires-Dist: pytest<9.2,>=5.0.0
33
33
  Requires-Dist: pyyaml
34
34
  Requires-Dist: requests
35
35
  Requires-Dist: sphinx==1.6.7
@@ -39,21 +39,12 @@ Requires-Dist: deepdiff~=8.6.1
39
39
  Requires-Dist: azure-cli-diff-tool~=0.1.1
40
40
  Requires-Dist: packaging
41
41
  Requires-Dist: pkginfo
42
+ Requires-Dist: tomli; python_version < "3.11"
42
43
  Requires-Dist: tqdm
43
- Requires-Dist: setuptools<80,>=78.1.1
44
+ Requires-Dist: setuptools>=78.1.1
44
45
  Requires-Dist: wheel
45
46
  Requires-Dist: microsoft-security-utilities-secret-masker~=1.0.0b4
46
- Dynamic: author
47
- Dynamic: author-email
48
- Dynamic: classifier
49
- Dynamic: description
50
- Dynamic: home-page
51
- Dynamic: keywords
52
- Dynamic: license
53
47
  Dynamic: license-file
54
- Dynamic: requires-dist
55
- Dynamic: requires-python
56
- Dynamic: summary
57
48
 
58
49
  Microsoft Azure CLI Dev Tools (azdev)
59
50
  =====================================
@@ -199,11 +190,28 @@ License
199
190
 
200
191
  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.::
201
192
 
202
-
203
193
  .. :changelog:
204
194
 
205
195
  Release History
206
196
  ===============
197
+ 0.2.14
198
+ ++++++
199
+ * `azdev extension add`/`list`/`build`: discover extensions by packaging configuration instead of globbing for `setup.py`, so extensions that have migrated to `pyproject.toml` are found. Previously a migrated extension was reported as `extension(s) not found`.
200
+ * `azdev extension build`: build through the PEP 517 frontend for `pyproject.toml` extensions, keeping `setup.py bdist_wheel` for the rest. `--no-isolation` preserves the previous behaviour for extensions whose build imports their own `azext_*` package.
201
+ * `azdev extension add`/`azdev setup`: generate `*.egg-info` without `setup.py`, so `az` can still discover a migrated extension as a development extension. `azure-cli-core` finds dev extensions by globbing `*.egg-info`.
202
+ * `azdev extension create`: scaffold `pyproject.toml` instead of `setup.py` and `setup.cfg`.
203
+ * `azdev extension update-index`: keep the home page under the `Home` label for `pyproject.toml` extensions, which declare `[project.urls] Homepage` rather than `url=`, so `index.json` entries do not change shape on migration. Also accept `License-Expression` in place of `License`.
204
+ * `azdev extension build`: fix the build-failure handler reading `CommandResultItem.output`, which does not exist. A failed build raised `AttributeError` instead of showing the build log.
205
+ * Migrate azdev and `azure-cli-diff-tool` to PEP 621 `pyproject.toml`. Both `setup.py` files are gone; wheel contents are unchanged.
206
+ * Build with `python -m build` in CI, and check metadata with `twine check` instead of `setup.py check -r -s`.
207
+ * Drop the `setuptools<80` cap. It guarded `setup.py develop`, which azdev stopped calling in 0.2.12. The `>=78.1.1` floor for CVE-2025-47273 stays.
208
+ * Fix `azdev cli check-versions` running `python -m setup.py bdist_wheel`, which could never work.
209
+ * Fix `azure-cli-diff-tool`'s sdist missing `HISTORY.rst`, which broke building a wheel from it.
210
+ * Fix `scripts/ci/extract_version.sh` producing an empty version.
211
+ * Set `Description-Content-Type` and stop reading the long description through `codecs.open`, which wrote `\r\r\n` line endings on Windows.
212
+ * `azdev command-change meta-export`: record `action` for parameters that take no value. Flag actions are written as `store_true`, `store_false`, `store_const` or `count`, including preview, experimental and deprecated arguments, whose action knack wraps in a subclass. Other actions registered with `nargs=0`, such as the one for `--acquire-policy-token`, are written as `store_true`. Readers of the metadata can now tell a flag such as `--enable-fips` from an option that takes a value. Other parameters get no `action` key, and `meta-diff` doesn't compare it.
213
+ * `azdev test`: cap `pytest` below 9.2. On Linux and macOS `azdev test` runs pytest with `--forked`, and pytest-forked 1.7.5 can't serialise pytest 9.2 reports, so every test failed with "EXITED with status 3". The cap stays until pytest-forked supports pytest 9.2 (pytest-dev/pytest-forked#113).
214
+
207
215
  0.2.13
208
216
  ++++++
209
217
  * `azdev extension add` / `azdev setup`: regenerate `*.egg-info` after the editable install so development extensions stay discoverable. `--no-build-isolation` (0.2.12) means pip no longer leaves an `*.egg-info` in the source tree, which made `azdev extension add` a silent no-op for discovery. (#554)
@@ -4,4 +4,4 @@
4
4
  # license information.
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __VERSION__ = '0.2.13'
7
+ __VERSION__ = '0.2.14'
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=70.1.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "{{ pkg_name }}"
7
+ # TODO: Confirm this is the right version number you want and it matches your
8
+ # HISTORY.rst entry.
9
+ version = "0.1.0"
10
+ description = "Microsoft Azure Command-Line Tools {{ display_name }} {{ 'Extension' if is_ext else 'Command Module' }}"
11
+ # TODO: Update author and email, if applicable
12
+ authors = [{ name = "Microsoft Corporation", email = "azpycli@microsoft.com" }]
13
+ license = { text = "MIT" }
14
+ # The full list of classifiers is available at
15
+ # https://pypi.python.org/pypi?%3Aaction=list_classifiers
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Intended Audience :: System Administrators",
20
+ "Programming Language :: Python",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "License :: OSI Approved :: MIT License",
27
+ ]
28
+ # TODO: Add any additional SDK dependencies here
29
+ dependencies = {% if dependencies %}[
30
+ {{ dependencies|join(',\n '|safe) }}
31
+ ]{% else %}[]{% endif %}
32
+ # `readme` is the concatenation of README.rst and HISTORY.rst, which PEP 621
33
+ # cannot express statically.
34
+ dynamic = ["readme"]
35
+
36
+ [project.urls]
37
+ # TODO: change to your extension source code repo if the code will not be put in azure-cli-extensions repo
38
+ Homepage = "https://github.com/Azure/{% if is_ext %}azure-cli-extensions/tree/main/src/{{ pkg_name }}{% else %}azure-cli{% endif %}"
39
+
40
+ [tool.setuptools.dynamic]
41
+ readme = { file = ["README.rst", "HISTORY.rst"], content-type = "text/x-rst" }
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["."]
45
+ exclude = ["tests*"]
46
+ {% if is_ext %}
47
+ [tool.setuptools.package-data]
48
+ {{ ext_long_name }} = ["azext_metadata.json"]
49
+ {% endif %}
@@ -21,8 +21,7 @@ logger = get_logger(__name__)
21
21
  try:
22
22
  with open(os.path.join(get_cli_repo_path(), 'scripts', 'ci', 'cmdcov.yml'), 'r') as file:
23
23
  config = yaml.safe_load(file)
24
- # pylint: disable=broad-exception-caught
25
- except Exception:
24
+ except Exception: # pylint: disable=broad-exception-caught
26
25
  url = "https://raw.githubusercontent.com/Azure/azure-cli/dev/scripts/ci/cmdcov.yml"
27
26
  response = requests.get(url)
28
27
  config = yaml.safe_load(response.text)
@@ -22,8 +22,7 @@ logger = get_logger(__name__)
22
22
  try:
23
23
  with open(os.path.join(get_cli_repo_path(), 'scripts', 'ci', 'cmdcov.yml'), 'r') as file:
24
24
  config = yaml.safe_load(file)
25
- # pylint: disable=broad-exception-caught
26
- except Exception:
25
+ except Exception: # pylint: disable=broad-exception-caught
27
26
  url = "https://raw.githubusercontent.com/Azure/azure-cli/dev/scripts/ci/cmdcov.yml"
28
27
  response = requests.get(url)
29
28
  config = yaml.safe_load(response.text)
@@ -30,7 +30,6 @@ def _ensure_dir(path):
30
30
 
31
31
 
32
32
  def _generate_files(env, generation_kwargs, file_list, dest_path):
33
-
34
33
  # allow sending a single item
35
34
  if not isinstance(file_list, list):
36
35
  file_list = [file_list]
@@ -269,8 +268,7 @@ def _create_package(prefix, repo_path, is_ext, name='test', display_name=None, d
269
268
  root_files = [
270
269
  'HISTORY.rst',
271
270
  'README.rst',
272
- 'setup.cfg',
273
- 'setup.py'
271
+ 'pyproject.toml'
274
272
  ]
275
273
  _generate_files(env, kwargs, root_files, dest_path)
276
274
 
@@ -10,8 +10,9 @@ import time
10
10
 
11
11
  from knack.log import get_logger
12
12
  import azure_cli_diff_tool
13
- from azdev.utilities import display, require_azure_cli, heading, get_path_table, filter_by_git_diff, \
13
+ from azdev.utilities import display, require_azure_cli, heading, filter_by_git_diff, \
14
14
  calc_selected_mod_names
15
+ from azdev.utilities.path import calc_selected_modules
15
16
  from .custom import DiffExportFormat, get_commands_meta, STORED_DEPRECATION_KEY
16
17
  from .util import export_commands_meta, dump_command_tree, add_to_command_tree
17
18
  from ..statistics import _create_invoker_and_load_cmds, _get_command_source, \
@@ -32,19 +33,7 @@ def export_command_meta(modules=None, git_source=None, git_target=None, git_repo
32
33
  meta_output_path=None):
33
34
  require_azure_cli()
34
35
 
35
- # allow user to run only on CLI or extensions
36
- cli_only = modules == ['CLI']
37
- ext_only = modules == ['EXT']
38
- if cli_only or ext_only:
39
- modules = None
40
-
41
- selected_modules = get_path_table(include_only=modules, include_whl_extensions=include_whl_extensions)
42
-
43
- if cli_only:
44
- selected_modules['ext'] = {}
45
- if ext_only:
46
- selected_modules['core'] = {}
47
- selected_modules['mod'] = {}
36
+ selected_modules = calc_selected_modules(modules, include_whl_extensions=include_whl_extensions)
48
37
 
49
38
  # filter down to only modules that have changed based on git diff
50
39
  selected_modules = filter_by_git_diff(selected_modules, git_source, git_target, git_repo)
@@ -4,6 +4,7 @@
4
4
  # license information.
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
+ import argparse
7
8
  from enum import Enum
8
9
 
9
10
  from knack.log import get_logger
@@ -13,6 +14,17 @@ logger = get_logger(__name__)
13
14
 
14
15
  STORED_DEPRECATION_KEY = ["expiration", "target", "redirect", "hide"]
15
16
 
17
+ # actions that take no value, so a reader of the metadata can tell a flag from an option that needs one
18
+ FLAG_ACTIONS = ["store_true", "store_false", "store_const", "count"]
19
+ # knack wraps the action in a subclass for preview, experimental and deprecated arguments,
20
+ # so match classes by base class, checking subclasses before their bases
21
+ FLAG_ACTION_CLASSES = [
22
+ (argparse._StoreTrueAction, "store_true"), # pylint: disable=protected-access
23
+ (argparse._StoreFalseAction, "store_false"), # pylint: disable=protected-access
24
+ (argparse._StoreConstAction, "store_const"), # pylint: disable=protected-access
25
+ (argparse._CountAction, "count"), # pylint: disable=protected-access
26
+ ]
27
+
16
28
 
17
29
  class DiffExportFormat(Enum):
18
30
  DICT = "dict"
@@ -98,6 +110,21 @@ def process_arg_type(argument_settings, para):
98
110
  para["type"] = raw_type if raw_type in ["str", "int", "float", "bool", "file_type"] else "custom_type"
99
111
 
100
112
 
113
+ def process_arg_action(argument_settings, para):
114
+ action = argument_settings.get("action", None)
115
+ if isinstance(action, str):
116
+ if action in FLAG_ACTIONS:
117
+ para["action"] = action
118
+ elif isinstance(action, type):
119
+ for action_class, action_name in FLAG_ACTION_CLASSES:
120
+ if issubclass(action, action_class):
121
+ para["action"] = action_name
122
+ break
123
+ # a custom action that takes no value, such as the one for --acquire-policy-token
124
+ if "action" not in para and argument_settings.get("nargs", None) == 0:
125
+ para["action"] = "store_true"
126
+
127
+
101
128
  def normalize_para_types(para):
102
129
  type_string_opts = ["string", "str", "aazstrarg",
103
130
  "aazresourcelocationarg", "aazresourcegroupnamearg", "aazresourceidarg",
@@ -159,6 +186,7 @@ def gen_command_meta(command_info, with_help=False, with_example=False):
159
186
  process_arg_options(settings, para)
160
187
  process_arg_options_deprecation(settings, para)
161
188
  process_arg_type(settings, para)
189
+ process_arg_action(settings, para)
162
190
  if settings.get("required", False):
163
191
  para["required"] = True
164
192
  if settings.get("choices", None):
@@ -15,8 +15,9 @@ from knack.prompting import prompt_y_n
15
15
  from knack.util import CLIError
16
16
 
17
17
  from azdev.utilities import (
18
- cmd, py_cmd, pip_cmd, display, get_ext_repo_paths, find_files, get_azure_config, get_azdev_config,
19
- get_azure_config_dir, require_azure_cli, heading, subheading, quote_arg, EXTENSION_PREFIX)
18
+ cmd, pip_cmd, display, get_ext_repo_paths, find_files, get_azure_config, get_azdev_config,
19
+ get_azure_config_dir, require_azure_cli, heading, subheading, quote_arg, EXTENSION_PREFIX,
20
+ build_package_wheel, find_package_configs_recursive, generate_egg_info)
20
21
  from .version_upgrade import VersionUpgradeMod
21
22
 
22
23
  logger = get_logger(__name__)
@@ -56,12 +57,7 @@ def _ensure_egg_info(path):
56
57
  # extension is invisible even though pip succeeded. Regenerate it with the env's setuptools.
57
58
  if find_files(path, '*.egg-info'):
58
59
  return
59
- result = py_cmd(
60
- 'setup.py egg_info',
61
- "Generating metadata for '{}'...".format(path),
62
- is_module=False,
63
- cwd=path,
64
- )
60
+ result = generate_egg_info(path, "Generating metadata for '{}'...".format(path))
65
61
  if result.error:
66
62
  raise result.error # pylint: disable=raising-bad-type
67
63
 
@@ -71,7 +67,7 @@ def add_extension(extensions):
71
67
  ext_paths = get_ext_repo_paths()
72
68
  if not ext_paths or ext_paths == ['_NONE_']:
73
69
  raise CLIError('Extension repo path is empty. Please try `azdev extension repo add` to add an extension repo')
74
- all_extensions = find_files(ext_paths, 'setup.py')
70
+ all_extensions = find_package_configs_recursive(ext_paths)
75
71
 
76
72
  if extensions == ['*']:
77
73
  paths_to_add = [os.path.dirname(path) for path in all_extensions
@@ -168,7 +164,7 @@ def list_extensions():
168
164
  installed_names = [x['name'] for x in installed]
169
165
  results = []
170
166
 
171
- for ext_path in find_files(dev_sources, 'setup.py'):
167
+ for ext_path in find_package_configs_recursive(dev_sources):
172
168
  # skip non-extension packages that may be in the extension folder (for example, from a virtual environment)
173
169
  try:
174
170
  glob_pattern = os.path.join(os.path.split(ext_path)[0], '{}*'.format(EXTENSION_PREFIX))
@@ -301,9 +297,8 @@ def update_extension_index(extensions):
301
297
  except IndexError:
302
298
  raise CLIError('unable to parse extension name')
303
299
 
304
- # TODO: Update this!
305
- extensions_dir = tempfile.mkdtemp()
306
- ext_dir = tempfile.mkdtemp(dir=extensions_dir)
300
+ # Create temporary directories for extraction and caching
301
+ ext_dir = tempfile.mkdtemp()
307
302
  whl_cache_dir = tempfile.mkdtemp()
308
303
  whl_cache = {}
309
304
  ext_file = get_whl_from_url(ext_path, ext_path.split("/")[-1], whl_cache_dir, whl_cache)
@@ -334,7 +329,7 @@ def build_extensions(extensions, dist_dir='dist'):
334
329
  ext_paths = get_ext_repo_paths()
335
330
  if not ext_paths or ext_paths == ['_NONE_']:
336
331
  raise CLIError('Extension repo path is empty. Please try `azdev extension repo add` to add an extension repo')
337
- all_extensions = find_files(ext_paths, 'setup.py')
332
+ all_extensions = find_package_configs_recursive(ext_paths)
338
333
 
339
334
  paths_to_build = []
340
335
  for path in all_extensions:
@@ -347,22 +342,10 @@ def build_extensions(extensions, dist_dir='dist'):
347
342
  if extensions:
348
343
  raise CLIError('extension(s) not found: {}'.format(' '.join(extensions)))
349
344
 
350
- original_cwd = os.getcwd()
351
- dist_dir = os.path.join(original_cwd, dist_dir)
345
+ dist_dir = os.path.join(os.getcwd(), dist_dir)
352
346
  for path in paths_to_build:
353
- os.chdir(path)
354
- command = 'setup.py bdist_wheel -b bdist -d {}'.format(dist_dir)
355
- result = py_cmd(command, "Building extension '{}'...".format(path), is_module=False)
356
- if result.error:
357
- # py_cmd captures the build output; surface it so the real setup.py failure is visible
358
- # instead of only the opaque CalledProcessError.
359
- build_output = result.output
360
- if isinstance(build_output, (bytes, bytearray)):
361
- build_output = build_output.decode('utf-8', 'ignore')
362
- if build_output:
363
- logger.error(build_output)
364
- raise result.error # pylint: disable=raising-bad-type
365
- os.chdir(original_cwd)
347
+ display("Building extension '{}'...".format(path))
348
+ build_package_wheel(path, dist_dir)
366
349
 
367
350
 
368
351
  def publish_extensions(extensions, storage_account, storage_account_key, storage_container,
@@ -82,6 +82,7 @@ def read_pkginfo(wheel_path: Path) -> Dict[str, Any]:
82
82
  "description": whl.description,
83
83
  "description_content_type": whl.description_content_type,
84
84
  "license": whl.license,
85
+ "license_expression": getattr(whl, "license_expression", None),
85
86
  "classifiers": list(whl.classifiers or []),
86
87
  "requires_dist": list(whl.requires_dist or []),
87
88
  "requires_python": whl.requires_python,
@@ -149,13 +150,27 @@ def _coerce_run_requires(requires_dist: List[str]) -> List[Dict[str, Any]]:
149
150
 
150
151
 
151
152
  def _coerce_project_urls(project_urls: List[str], home_page: Optional[str]) -> Dict[str, str]:
153
+ """Build the `project_urls` block, normalizing the home page label.
154
+
155
+ A `setup.py` package declares `url=`, which lands in the `Home-page` METADATA
156
+ field and has always been stored in `index.json` under the `Home` label.
157
+ PEP 621 has no `url` equivalent: a pyproject package declares
158
+ `[project.urls] Homepage`, which lands in `Project-URL` instead. Both are
159
+ mapped to `Home` so an extension's index entry keeps its shape when it
160
+ migrates.
161
+ """
152
162
  out: Dict[str, str] = {}
153
163
  if home_page:
154
164
  out["Home"] = home_page
155
165
  for entry in project_urls or []:
156
- if "," in entry:
157
- label, url = entry.split(",", 1)
158
- out[label.strip()] = url.strip()
166
+ if "," not in entry:
167
+ continue
168
+ label, url = entry.split(",", 1)
169
+ label, url = label.strip(), url.strip()
170
+ if label.lower() in ("home", "homepage", "home-page"):
171
+ out.setdefault("Home", url)
172
+ continue
173
+ out[label] = url
159
174
  return out
160
175
 
161
176
 
@@ -180,7 +195,9 @@ def merge_to_index_metadata(pkg: Dict[str, Any], azext: Dict[str, Any]) -> Dict[
180
195
  metadata["name"] = pkg.get("name")
181
196
  metadata["version"] = pkg.get("version")
182
197
  metadata["summary"] = pkg.get("summary")
183
- metadata["license"] = pkg.get("license")
198
+ # PEP 639 wheels carry the license as `License-Expression` rather than
199
+ # `License`, so accept either and keep a single `license` key in the index.
200
+ metadata["license"] = pkg.get("license") or pkg.get("license_expression")
184
201
  metadata["metadata_version"] = pkg.get("metadata_version")
185
202
  metadata["classifiers"] = pkg.get("classifiers") or []
186
203
  metadata["extras"] = []
@@ -17,10 +17,7 @@ _LATEST_INDEX_SCRIPT = os.path.join('scripts', 'generate_latest_indices.py')
17
17
 
18
18
 
19
19
  def _resolve_cli_repo_path(cli_path):
20
- if cli_path:
21
- resolved = os.path.abspath(os.path.expanduser(cli_path))
22
- else:
23
- resolved = get_cli_repo_path()
20
+ resolved = os.path.abspath(os.path.expanduser(cli_path)) if cli_path else get_cli_repo_path()
24
21
 
25
22
  if not resolved or resolved == '_NONE_':
26
23
  raise CLIError('Azure CLI repo path is not configured. Specify `--cli` or run `azdev setup`.')
@@ -7,6 +7,7 @@
7
7
  import os
8
8
  import re
9
9
  import sys
10
+ import tempfile
10
11
 
11
12
  from docutils import core, io
12
13
 
@@ -14,16 +15,14 @@ from knack.log import get_logger
14
15
  from knack.util import CLIError
15
16
 
16
17
  from azdev.utilities import (
17
- display, heading, subheading, cmd, py_cmd, get_path_table,
18
- pip_cmd, COMMAND_MODULE_PREFIX, require_azure_cli, find_files)
18
+ build_package_wheel, display, heading, subheading, cmd, get_package_config,
19
+ get_path_table, pip_cmd, COMMAND_MODULE_PREFIX, require_azure_cli)
19
20
 
20
21
  logger = get_logger(__name__)
21
22
 
22
23
 
23
24
  HISTORY_NAME = 'HISTORY.rst'
24
25
  RELEASE_HISTORY_TITLE = 'Release History'
25
- SETUP_PY_NAME = 'setup.py'
26
-
27
26
  # modules which should not be included in setup.py
28
27
  # because they aren't on PyPI
29
28
  EXCLUDED_MODULES = ['azure-cli-testsdk']
@@ -59,7 +58,20 @@ def check_history():
59
58
  display('OK')
60
59
 
61
60
 
62
- def _check_history_headings(mod_path):
61
+ def _get_built_package_metadata(mod_path):
62
+ from pkginfo import Wheel
63
+
64
+ with tempfile.TemporaryDirectory(prefix='azdev-package-metadata-') as output_dir:
65
+ wheel_path = build_package_wheel(mod_path, output_dir)
66
+ metadata = Wheel(wheel_path)
67
+ return {
68
+ 'description': metadata.description,
69
+ 'description_content_type': metadata.description_content_type,
70
+ 'version': metadata.version,
71
+ }
72
+
73
+
74
+ def _check_history_headings(mod_path, actual_version=None):
63
75
  history_path = os.path.join(mod_path, HISTORY_NAME)
64
76
 
65
77
  source_path = None
@@ -93,41 +105,57 @@ def _check_history_headings(mod_path):
93
105
  "line after the 'Release History' heading.".format(history_path))
94
106
 
95
107
  first_version_history = all_versions[0]
96
- actual_version = cmd(sys.executable + ' setup.py --version', cwd=mod_path)
97
- # command can output warnings as well, so we just want the last line, which should have the version
98
- actual_version = actual_version.result.splitlines()[-1].strip()
108
+ if actual_version is None:
109
+ actual_version_result = cmd(sys.executable + ' setup.py --version', cwd=mod_path)
110
+ # command can output warnings as well, so we just want the last line, which should have the version
111
+ actual_version = actual_version_result.result.splitlines()[-1].strip()
99
112
  if first_version_history != actual_version:
100
- errors.append("The topmost version in {} does not match version {} defined in setup.py.".format(
113
+ errors.append("The topmost version in {} does not match package version {}.".format(
101
114
  history_path, actual_version))
102
115
  return errors
103
116
 
104
117
 
105
118
  def _check_readme_render(mod_path):
106
119
  errors = []
107
- result = cmd(sys.executable + ' setup.py check -r -s', cwd=mod_path)
108
- if result.exit_code:
109
- # this outputs some warnings we don't care about
110
- error_lines = []
111
- target_line = 'The following syntax errors were detected'
112
- suppress = True
113
- logger.debug(result.error.output)
114
-
115
- # TODO: Checks for syntax errors but potentially not other things
116
- for line in result.error.output.splitlines():
117
- line = str(line).strip()
118
- if not suppress and line:
119
- error_lines.append(line)
120
- if target_line in line:
121
- suppress = False
122
- errors.append(os.linesep.join(error_lines))
123
- errors += _check_history_headings(mod_path)
120
+ package_config = get_package_config(mod_path)
121
+ if package_config and os.path.basename(package_config) == 'setup.py':
122
+ result = cmd(sys.executable + ' setup.py check -r -s', cwd=mod_path)
123
+ if result.exit_code:
124
+ # this outputs some warnings we don't care about
125
+ error_lines = []
126
+ target_line = 'The following syntax errors were detected'
127
+ suppress = True
128
+ logger.debug(result.error.output)
129
+
130
+ # TODO: Checks for syntax errors but potentially not other things
131
+ for line in result.error.output.splitlines():
132
+ line = str(line).strip()
133
+ if not suppress and line:
134
+ error_lines.append(line)
135
+ if target_line in line:
136
+ suppress = False
137
+ errors.append(os.linesep.join(error_lines))
138
+ errors += _check_history_headings(mod_path)
139
+ return errors
140
+
141
+ metadata = _get_built_package_metadata(mod_path)
142
+ content_type = (metadata.get('description_content_type') or 'text/x-rst').split(';', 1)[0].strip().lower()
143
+ if content_type == 'text/x-rst' and metadata.get('description'):
144
+ _, publication = core.publish_programmatically(
145
+ source_class=io.StringInput, source=metadata['description'],
146
+ source_path=None, destination_class=io.NullOutput, destination=None,
147
+ destination_path=None, reader=None, reader_name='standalone', parser=None,
148
+ parser_name='restructuredtext', writer=None, writer_name='null', settings=None,
149
+ settings_spec=None, settings_overrides={}, config_section=None, enable_exit_status=None)
150
+ if publication.writer.document.reporter.max_level >= 2:
151
+ errors.append('The package long description contains reStructuredText warnings or errors.')
152
+ errors += _check_history_headings(mod_path, actual_version=metadata['version'])
124
153
  return errors
125
154
  # endregion
126
155
 
127
156
 
128
157
  # region verify PyPI versions
129
158
  def verify_versions():
130
- import tempfile
131
159
  import shutil
132
160
 
133
161
  require_azure_cli()
@@ -174,25 +202,6 @@ def verify_versions():
174
202
  display('OK!')
175
203
 
176
204
 
177
- def _get_module_versions(results, modules):
178
-
179
- version_pattern = re.compile(r'.*(?P<ver>\d+.\d+.\d+).*')
180
-
181
- for mod, mod_path in modules:
182
- if not mod.startswith(COMMAND_MODULE_PREFIX) and mod != 'azure-cli':
183
- mod = '{}{}'.format(COMMAND_MODULE_PREFIX, mod)
184
-
185
- setup_path = find_files(mod_path, 'setup.py')
186
- with open(setup_path[0], 'r') as f:
187
- local_version = 'Unknown'
188
- for line in f.readlines():
189
- if line.strip().startswith('VERSION'):
190
- local_version = version_pattern.match(line).group('ver')
191
- break
192
- results[mod]['local_version'] = local_version
193
- return results
194
-
195
-
196
205
  # pylint: disable=too-many-statements
197
206
  def _compare_module_against_pypi(results, root_dir, mod, mod_path):
198
207
  import zipfile
@@ -230,10 +239,7 @@ def _compare_module_against_pypi(results, root_dir, mod, mod_path):
230
239
  # build from source and extract the version
231
240
  setup_path = os.path.normpath(mod_path.strip())
232
241
  os.chdir(setup_path)
233
- py_cmd('setup.py bdist_wheel -d {}'.format(build_dir))
234
- if len(os.listdir(build_dir)) != 1:
235
- raise CLIError('Unexpectedly found multiple build files found in {}.'.format(build_dir))
236
- build_path = os.path.join(build_dir, os.listdir(build_dir)[0])
242
+ build_path = build_package_wheel(setup_path, build_dir)
237
243
  build_version = version_pattern.match(build_path).group(1)
238
244
 
239
245
  results[mod].update({
@@ -18,8 +18,7 @@ logger = get_logger(__name__)
18
18
  try:
19
19
  with open(os.path.join(get_cli_repo_path(), 'scripts', 'ci', 'cmdcov.yml'), 'r') as file:
20
20
  config = yaml.safe_load(file)
21
- # pylint: disable=broad-exception-caught
22
- except Exception:
21
+ except Exception: # pylint: disable=broad-exception-caught
23
22
  url = "https://raw.githubusercontent.com/Azure/azure-cli/dev/scripts/ci/cmdcov.yml"
24
23
  response = requests.get(url)
25
24
  config = yaml.safe_load(response.text)