From 1001e0f4bd3dc6be56f65b6727c3d5c71012bfd5 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 26 Feb 2020 22:27:32 +0000 Subject: [PATCH] Allow to install extensions in the system path via packages On Linux, check if there are extension in the system Python library path (eg: /usr/lib/python3/dist-packages/azure-cli-extensions). This allows extensions to be installed via system packages in dist-packages, and registered by simply creating an extension directory and symlinking the egg-info and the module. EG: $ ls -l /usr/lib/python3/dist-packages/azure-cli-extensions/azure-devops/ total 0 lrwxrwxrwx 1 root root 18 Feb 23 12:31 azext_devops -> ../../azext_devops lrwxrwxrwx 1 root root 34 Feb 23 12:31 azure_devops-0.17.0.egg-info -> ../../azure_devops-0.17.0.egg-info $ az extension list [ { "extensionType": "whl", "name": "azure-devops", "version": "0.17.0" } ] Locally installed extensions still have higher priority if available. --- .../azure/cli/core/extension/__init__.py | 20 +++++++++++++++++-- src/azure-cli-core/setup.py | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/extension/__init__.py b/src/azure-cli-core/azure/cli/core/extension/__init__.py index 574270c8cf0..43aa5ec2777 100644 --- a/src/azure-cli-core/azure/cli/core/extension/__init__.py +++ b/src/azure-cli-core/azure/cli/core/extension/__init__.py @@ -7,9 +7,11 @@ import traceback import json import re +import sys +import pkginfo from azure.cli.core._config import GLOBAL_CONFIG_DIR, ENV_VAR_PREFIX - +from distutils.sysconfig import get_python_lib from knack.config import CLIConfig from knack.log import get_logger @@ -19,6 +21,7 @@ EXTENSIONS_DIR = os.path.expanduser(_CUSTOM_EXT_DIR) if _CUSTOM_EXT_DIR else os.path.join(GLOBAL_CONFIG_DIR, 'cliextensions') DEV_EXTENSION_SOURCES = _DEV_EXTENSION_SOURCES.split(',') if _DEV_EXTENSION_SOURCES else [] +EXTENSIONS_SYS_DIR = os.path.join(get_python_lib(), 'azure-cli-extensions') if sys.platform.startswith('linux') else "" EXTENSIONS_MOD_PREFIX = 'azext_' @@ -33,7 +36,7 @@ WHEEL_INFO_RE = re.compile( r"""^(?P(?P.+?)(-(?P\d.+?))?) ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) - \.whl|\.dist-info)$""", + \.whl|\.dist-info|\.egg-info)$""", re.VERBOSE).match logger = get_logger(__name__) @@ -136,9 +139,14 @@ def get_metadata(self): if os.path.isfile(whl_metadata_filepath): with open(whl_metadata_filepath) as f: metadata.update(json.loads(f.read())) + elif os.path.isfile(os.path.join(dist_info_dirname, 'PKG-INFO')): + metadata.update(pkginfo.Develop(dist_info_dirname).__dict__) return metadata + def __eq__(self, other): + return other.name == self.name + @staticmethod def get_azext_metadata(ext_dir): azext_metadata = None @@ -162,6 +170,14 @@ def get_all(): pattern = os.path.join(ext_path, '*.*-info') if os.path.isdir(ext_path) and glob(pattern): exts.append(WheelExtension(ext_name, ext_path)) + if os.path.isdir(EXTENSIONS_SYS_DIR): + for ext_name in os.listdir(EXTENSIONS_SYS_DIR): + ext_path = os.path.join(EXTENSIONS_SYS_DIR, ext_name) + pattern = os.path.join(ext_path, '*.*-info') + if os.path.isdir(ext_path) and glob(pattern): + ext = WheelExtension(ext_name, ext_path) + if ext not in exts: + exts.append(ext) return exts diff --git a/src/azure-cli-core/setup.py b/src/azure-cli-core/setup.py index 08cddb8b267..79d3a8b211c 100644 --- a/src/azure-cli-core/setup.py +++ b/src/azure-cli-core/setup.py @@ -61,6 +61,7 @@ 'msrest>=0.4.4', 'msrestazure>=0.6.2', 'paramiko>=2.0.8,<3.0.0', + 'pkginfo', 'PyJWT', 'pyopenssl>=17.1.0', # https://github.com/pyca/pyopenssl/pull/612 'pyyaml',