Preview extension notation & Simple default view for extension list-a… (#5882)

* Preview extension notation & Simple default view for extension list-available

- Show message for extensions marked as preview on -h

* Update HISTORY.rst

* Add more tests

* fix style
This commit is contained in:
Derek Bekoe 2018-03-22 15:50:13 -07:00 коммит произвёл GitHub
Родитель 28ede753f2
Коммит e4339a7dac
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 107 добавлений и 18 удалений

Просмотреть файл

@ -50,4 +50,9 @@ Type: `string`
Example: `"azext.maxCliCoreVersion": "2.0.15"`
### azext.isPreview
Description: Indicate that the extension is in preview.
Type: `boolean`
Example: `"azext.isPreview": true`

Просмотреть файл

@ -6,7 +6,7 @@ Release History
2.0.30
++++++
* Minor fixes
* Show message for extensions marked as preview on -h.
2.0.29
++++++

Просмотреть файл

@ -163,7 +163,8 @@ class MainCommandsLoader(CLICommandsLoader):
if extensions:
logger.debug("Found %s extensions: %s", len(extensions), [e.name for e in extensions])
allowed_extensions = _handle_extension_suppressions(extensions)
for ext_name in [e.name for e in allowed_extensions]:
for ext in allowed_extensions:
ext_name = ext.name
ext_dir = get_extension_path(ext_name)
sys.path.append(ext_dir)
try:
@ -177,7 +178,8 @@ class MainCommandsLoader(CLICommandsLoader):
for cmd_name, cmd in extension_command_table.items():
cmd.command_source = ExtensionCommandSource(
extension_name=ext_name,
overrides_command=cmd_name in cmd_to_mod_map)
overrides_command=cmd_name in cmd_to_mod_map,
preview=ext.preview)
self.command_table.update(extension_command_table)
elapsed_time = timeit.default_timer() - start_time

Просмотреть файл

@ -56,6 +56,8 @@ class AzCliHelp(CLIHelp):
return
if help_file.command_source and isinstance(help_file.command_source, ExtensionCommandSource):
logger.warning(help_file.command_source.get_command_warn_msg())
if help_file.command_source.preview:
logger.warning(help_file.command_source.get_preview_warn_msg())
@classmethod
def print_detailed_help(cls, cli_name, help_file):

Просмотреть файл

@ -568,11 +568,12 @@ def _load_module_command_loader(loader, args, mod):
class ExtensionCommandSource(object):
""" Class for commands contributed by an extension """
def __init__(self, overrides_command=False, extension_name=None):
def __init__(self, overrides_command=False, extension_name=None, preview=False):
super(ExtensionCommandSource, self).__init__()
# True if the command overrides a CLI command
self.overrides_command = overrides_command
self.extension_name = extension_name
self.preview = preview
def get_command_warn_msg(self):
if self.overrides_command:
@ -585,6 +586,11 @@ class ExtensionCommandSource(object):
return "This command is from the following extension: {}".format(self.extension_name)
return "This command is from an extension."
def get_preview_warn_msg(self):
if self.preview:
return "The extension is in preview"
return None
def _load_client_exception_class():
# Since loading msrest is expensive, we avoid it until we have to

Просмотреть файл

@ -22,6 +22,7 @@ AZEXT_METADATA_FILENAME = 'azext_metadata.json'
EXT_METADATA_MINCLICOREVERSION = 'azext.minCliCoreVersion'
EXT_METADATA_MAXCLICOREVERSION = 'azext.maxCliCoreVersion'
EXT_METADATA_ISPREVIEW = 'azext.isPreview'
logger = get_logger(__name__)
@ -42,6 +43,7 @@ class Extension(object):
self.ext_type = ext_type
self._version = None
self._metadata = None
self._preview = None
@property
def version(self):
@ -67,6 +69,19 @@ class Extension(object):
logger.debug("Unable to get extension metadata: %s", traceback.format_exc())
return self._metadata
@property
def preview(self):
"""
Lazy load preview status.
Returns the preview status of the extension.
"""
try:
if not isinstance(self._preview, bool):
self._preview = bool(self.metadata.get(EXT_METADATA_ISPREVIEW))
except Exception: # pylint: disable=broad-except
logger.debug("Unable to get extension preview status: %s", traceback.format_exc())
return self._preview
def get_version(self):
raise NotImplementedError()

Просмотреть файл

@ -152,9 +152,9 @@ class TestCommandRegistration(unittest.TestCase):
return ext_name
def _mock_get_extensions():
MockExtension = namedtuple('Extension', ['name'])
return [MockExtension(name=__name__ + '.ExtCommandsLoader'),
MockExtension(name=__name__ + '.Ext2CommandsLoader')]
MockExtension = namedtuple('Extension', ['name', 'preview'])
return [MockExtension(name=__name__ + '.ExtCommandsLoader', preview=False),
MockExtension(name=__name__ + '.Ext2CommandsLoader', preview=False)]
def _mock_load_command_loader(loader, args, name, prefix):

Просмотреть файл

@ -5,7 +5,9 @@ Release History
0.0.11
++++++
* Minor fixes
* Preview extensions: Show message on `az extension add` if extension is in preview
* BC: `az extension list-available` - The full extension data is now available with `--show-details`
* `az extension list-available` - A simplified view of the extensions available is now shown by default
0.0.10
+++++++

Просмотреть файл

@ -27,7 +27,10 @@ class ExtensionCommandsLoader(AzCommandsLoader):
return bool(not command_args.get('source') or prompt_y_n('Are you sure you want to install this extension?'))
def transform_extension_list_available(results):
return [OrderedDict([('Name', r)]) for r in results]
if isinstance(results, dict):
# For --show-details, transform the table
return [OrderedDict([('Name', r)]) for r in results]
return results
def validate_extension_add(namespace):
if (namespace.extension_name and namespace.source) or (not namespace.extension_name and not namespace.source):
@ -67,5 +70,8 @@ class ExtensionCommandsLoader(AzCommandsLoader):
c.argument('source', options_list=['--source', '-s'], help='Filepath or URL to an extension', completer=FilesCompleter())
c.argument('yes', options_list=['--yes', '-y'], action='store_true', help='Do not prompt for confirmation.')
with self.argument_context('extension list-available') as c:
c.argument('show_details', options_list=['--show-details', '-d'], action='store_true', help='Show the raw data from the extension index.')
COMMAND_LOADER_CLS = ExtensionCommandsLoader

Просмотреть файл

@ -11,14 +11,17 @@ import traceback
import hashlib
from subprocess import check_output, STDOUT, CalledProcessError
from six.moves.urllib.parse import urlparse # pylint: disable=import-error
from collections import OrderedDict
import requests
from wheel.install import WHEEL_INFO_RE
from pkg_resources import parse_version
from knack.log import get_logger
from azure.cli.core.util import CLIError
from azure.cli.core.extension import (extension_exists, get_extension_path, get_extensions,
get_extension, ext_compat_with_cli,
get_extension, ext_compat_with_cli, EXT_METADATA_ISPREVIEW,
WheelExtension, ExtensionNotInstalledException)
from azure.cli.core.telemetry import set_extension_management_detail
@ -200,6 +203,11 @@ def add_extension(source=None, extension_name=None, index_url=None, yes=None, #
raise CLIError("No matching extensions for '{}'. Use --debug for more information.".format(extension_name))
_add_whl_ext(source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy)
_augment_telemetry_with_ext_info(extension_name)
try:
if extension_name and get_extension(extension_name).preview:
logger.warning("The installed extension '%s' is in preview.", extension_name)
except ExtensionNotInstalledException:
pass
def remove_extension(extension_name):
@ -263,8 +271,28 @@ def update_extension(extension_name, index_url=None, pip_extra_index_urls=None,
raise CLIError(e)
def list_available_extensions(index_url=None):
return get_index_extensions(index_url=index_url)
def list_available_extensions(index_url=None, show_details=False):
index_data = get_index_extensions(index_url=index_url)
if show_details:
return index_data
installed_extensions = get_extensions()
installed_extension_names = [e.name for e in installed_extensions]
results = []
for name, items in OrderedDict(sorted(index_data.items())).items():
latest = sorted(items, key=lambda c: parse_version(c['metadata']['version']), reverse=True)[0]
installed = False
if name in installed_extension_names:
installed = True
if parse_version(latest['metadata']['version']) > parse_version(get_extension(name).version):
installed = str(True) + ' (upgrade available)'
results.append({
'name': name,
'version': latest['metadata']['version'],
'summary': latest['metadata']['summary'],
'preview': latest['metadata'].get(EXT_METADATA_ISPREVIEW, False),
'installed': installed
})
return results
def get_lsb_release():
@ -296,14 +324,14 @@ def check_distro_consistency():
except Exception as err: # pylint: disable=broad-except
current_linux_dist_name = None
stored_linux_dist_name = None
logger.debug('Linux distro check: An error occurred while checking \
linux distribution version source list consistency.')
logger.debug('Linux distro check: An error occurred while checking '
'linux distribution version source list consistency.')
logger.debug(err)
if current_linux_dist_name != stored_linux_dist_name:
logger.warning("Linux distro check: Mismatch distribution \
name in %s file", LIST_FILE_PATH)
logger.warning("Linux distro check: If command fails, install the appropriate package \
for your distribution or change the above file accordingly.")
logger.warning("Linux distro check: Mismatch distribution "
"name in %s file", LIST_FILE_PATH)
logger.warning("Linux distro check: If command fails, install the appropriate package "
"for your distribution or change the above file accordingly.")
logger.warning("Linux distro check: %s has '%s', current distro is '%s'",
LIST_FILE_PATH, stored_linux_dist_name, current_linux_dist_name)

Просмотреть файл

@ -251,6 +251,29 @@ class TestExtensionCommands(unittest.TestCase):
list_available_extensions(index_url=index_url)
c.assert_called_once_with(index_url)
def test_list_available_extensions_show_details(self):
with mock.patch('azure.cli.command_modules.extension.custom.get_index_extensions', autospec=True) as c:
list_available_extensions(show_details=True)
c.assert_called_once_with(None)
def test_list_available_extensions_no_show_details(self):
sample_index_extensions = {
'test_sample_extension1': [{
'metadata': {
'name': 'test_sample_extension1',
'summary': 'my summary',
'version': '0.1.0'
}}]
}
with mock.patch('azure.cli.command_modules.extension.custom.get_index_extensions', return_value=sample_index_extensions):
res = list_available_extensions()
self.assertIsInstance(res, list)
self.assertEqual(len(res), len(sample_index_extensions))
self.assertEqual(res[0]['name'], 'test_sample_extension1')
self.assertEqual(res[0]['summary'], 'my summary')
self.assertEqual(res[0]['version'], '0.1.0')
self.assertEqual(res[0]['preview'], False)
def test_add_list_show_remove_extension_extra_index_url(self):
"""
Tests extension addition while specifying --extra-index-url parameter.