diff --git a/src/connectedvmware/HISTORY.rst b/src/connectedvmware/HISTORY.rst index 1891096ad45..23cc1c9ce7e 100644 --- a/src/connectedvmware/HISTORY.rst +++ b/src/connectedvmware/HISTORY.rst @@ -4,6 +4,11 @@ Release History =============== +0.1.11 +++++++ +Including pwinput in code to workaround the issue with azure cli version >= 2.42.0 in windows installed using MSI. +Issue link: https://github.com/Azure/azure-cli/issues/24781 + 0.1.10 ++++++ * Bug Fix: Wait for SystemAssigned Identity PATCH to complete before enabling Guest management on VM. diff --git a/src/connectedvmware/azext_connectedvmware/custom.py b/src/connectedvmware/azext_connectedvmware/custom.py index b92262d397f..5c7eb726d97 100644 --- a/src/connectedvmware/azext_connectedvmware/custom.py +++ b/src/connectedvmware/azext_connectedvmware/custom.py @@ -5,9 +5,9 @@ # pylint: disable= too-many-lines, too-many-locals, unused-argument, too-many-branches, too-many-statements # pylint: disable= consider-using-dict-items, consider-using-f-string -from pwinput import pwinput from knack.util import CLIError from azure.cli.core.util import sdk_no_wait +from azext_connectedvmware.pwinput import pwinput from azext_connectedvmware.vmware_utils import get_resource_id from .vmware_constants import ( VMWARE_NAMESPACE, diff --git a/src/connectedvmware/azext_connectedvmware/pwinput.py b/src/connectedvmware/azext_connectedvmware/pwinput.py new file mode 100644 index 00000000000..b6003bb245a --- /dev/null +++ b/src/connectedvmware/azext_connectedvmware/pwinput.py @@ -0,0 +1,164 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +# pylint: disable-all +# flake8: noqa + +# Reference file: +# https://github.com/asweigart/pwinput/blob/48c7e04c1152e2090ff95d8483cbe4faf970e15d/src/pwinput/__init__.py + +"""PWInput +By Al Sweigart al@inventwithpython.com + +A cross-platform Python module that displays **** for password input. Works on Windows, unlike getpass. Formerly called stdiomask.""" + +import sys + + +"""Notes about making this code backwards-compatible with Python 2: +sys.stdout.write() can only write unicode strings, not Python 2 str strings. +I create STR_TYPE to use for isinstance() checks. Also, the u prefix for +unicode strings causes syntax errors on Python 3.1 and 3.2, so instead I +pass those strings to STR_TYPE, which is set to unicode() on Python 2, +which effectively does the same thing as the u prefix. +""" +STR_TYPE = str # type: type +RUNNING_PYTHON_2 = sys.version_info[0] == 2 # type: bool +if RUNNING_PYTHON_2: + STR_TYPE = unicode + + +try: + from typing import List +except ImportError: + pass # There is no typing module on Python 2, but that's fine because we use the comment-style of type hints. + +if sys.platform == 'win32': + # For some reason, mypy reports that msvcrt doesn't have getch, ignore this warning: + from msvcrt import getch # type: ignore + + def pwinput(prompt='Password: ', mask='*'): + # type: (str, str) -> str + + if RUNNING_PYTHON_2: + # On Python 2, convert `prompt` and `mask` from str to unicode because sys.stdout.write requires unicode. + if isinstance(prompt, str): + # Mypy in Python 3 mode (the default mode) will complain about the following line: + prompt = prompt.decode('utf-8') # type: ignore + if isinstance(mask, str): + # Mypy in Python 3 mode (the default mode) will complain about the following line: + mask = mask.decode('utf-8') # type: ignore + + if not isinstance(prompt, STR_TYPE): + raise TypeError('prompt argument must be a str, not %s' % (type(prompt).__name__)) + if not isinstance(mask, STR_TYPE): + raise TypeError('mask argument must be a zero- or one-character str, not %s' % (type(prompt).__name__)) + if len(mask) > 1: + raise ValueError('mask argument must be a zero- or one-character str') + + if mask == '' or sys.stdin is not sys.__stdin__: + # Fall back on getpass if a mask is not needed. + import getpass as gp + return gp.getpass(prompt) + + enteredPassword = [] # type: List[str] + sys.stdout.write(prompt) + sys.stdout.flush() + while True: + key = ord(getch()) + if key == 13: # Enter key pressed. + if RUNNING_PYTHON_2: + sys.stdout.write(STR_TYPE('\n')) + else: + sys.stdout.write('\n') + return ''.join(enteredPassword) + elif key in (8, 127): # Backspace/Del key erases previous output. + if len(enteredPassword) > 0: + # Erases previous character. + if RUNNING_PYTHON_2: + sys.stdout.write(STR_TYPE('\b \b')) # \b doesn't erase the character, it just moves the cursor back. + else: + sys.stdout.write('\b \b') # \b doesn't erase the character, it just moves the cursor back. + sys.stdout.flush() + enteredPassword = enteredPassword[:-1] + elif 0 <= key <= 31: + # Do nothing for unprintable characters. + # TODO: Handle Esc, F1-F12, arrow keys, home, end, insert, del, pgup, pgdn + pass + else: + # Key is part of the password; display the mask character. + char = chr(key) + sys.stdout.write(mask) + sys.stdout.flush() + enteredPassword.append(char) + +else: # macOS and Linux + import tty + import termios + + def getch(): + # type: () -> str + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + return ch + + def pwinput(prompt='Password: ', mask='*'): + # type: (str, str) -> str + + if RUNNING_PYTHON_2: + # On Python 2, convert `prompt` and `mask` from str to unicode because sys.stdout.write requires unicode. + if isinstance(prompt, str): + # Mypy in Python 3 mode (the default mode) will complain about the following line: + prompt = prompt.decode('utf-8') # type: ignore + if isinstance(mask, str): + # Mypy in Python 3 mode (the default mode) will complain about the following line: + mask = mask.decode('utf-8') # type: ignore + + if not isinstance(prompt, STR_TYPE): + raise TypeError('prompt argument must be a str, not %s' % (type(prompt).__name__)) + if not isinstance(mask, STR_TYPE): + raise TypeError('mask argument must be a zero- or one-character str, not %s' % (type(prompt).__name__)) + if len(mask) > 1: + raise ValueError('mask argument must be a zero- or one-character str') + + if mask == '' or sys.stdin is not sys.__stdin__: + # Fall back on getpass if a mask is not needed. + import getpass as gp + return gp.getpass(prompt) + + enteredPassword = [] # List[str] + sys.stdout.write(prompt) + sys.stdout.flush() + while True: + key = ord(getch()) + if key == 13: # Enter key pressed. + if RUNNING_PYTHON_2: + sys.stdout.write(STR_TYPE('\n')) + else: + sys.stdout.write('\n') + return ''.join(enteredPassword) + elif key in (8, 127): # Backspace/Del key erases previous output. + if len(enteredPassword) > 0: + # Erases previous character. + if RUNNING_PYTHON_2: + sys.stdout.write(STR_TYPE('\b \b')) # \b doesn't erase the character, it just moves the cursor back. + else: + sys.stdout.write('\b \b') # \b doesn't erase the character, it just moves the cursor back. + sys.stdout.flush() + enteredPassword = enteredPassword[:-1] + elif 0 <= key <= 31: + # Do nothing for unprintable characters. + # TODO: Handle Esc, F1-F12, arrow keys, home, end, insert, del, pgup, pgdn + pass + else: + # Key is part of the password; display the mask character. + char = chr(key) + sys.stdout.write(mask) + sys.stdout.flush() + enteredPassword.append(char) diff --git a/src/connectedvmware/setup.py b/src/connectedvmware/setup.py index dd5d718776c..09d8bf85142 100644 --- a/src/connectedvmware/setup.py +++ b/src/connectedvmware/setup.py @@ -19,7 +19,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '0.1.10' +VERSION = '0.1.11' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers @@ -36,7 +36,7 @@ ] # TODO: Add any additional SDK dependencies here -DEPENDENCIES = ["pwinput"] +DEPENDENCIES = [] with open('README.rst', 'r', encoding='utf-8') as f: README = f.read()