azdev/operations/regex.py (153 lines of code) (raw):

# ----------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for # license information. # ----------------------------------------------------------------------------- import json import os import re import requests import yaml from knack.log import get_logger from azdev.utilities.path import get_cli_repo_path logger = get_logger(__name__) try: with open(os.path.join(get_cli_repo_path(), 'scripts', 'ci', 'cmdcov.yml'), 'r') as file: config = yaml.safe_load(file) # pylint: disable=broad-exception-caught except Exception: url = "https://raw.githubusercontent.com/Azure/azure-cli/dev/scripts/ci/cmdcov.yml" response = requests.get(url) config = yaml.safe_load(response.text) CMD_PATTERN = config['CMD_PATTERN'] QUO_PATTERN = config['QUO_PATTERN'] END_PATTERN = config['END_PATTERN'] DOCS_END_PATTERN = config['DOCS_END_PATTERN'] NOT_END_PATTERN = config['NOT_END_PATTERN'] NUMBER_SIGN_PATTERN = config['NUMBER_SIGN_PATTERN'] def get_all_tested_commands_from_regex(lines): """ get all tested commands from test_*.py """ # pylint: disable=too-many-nested-blocks ref = [] total_lines = len(lines) row_num = 0 count = 1 while row_num < total_lines: re_idx = None if re.findall(NUMBER_SIGN_PATTERN, lines[row_num]): row_num += 1 continue if re.findall(CMD_PATTERN[0], lines[row_num]): re_idx = 0 if re_idx is None and re.findall(CMD_PATTERN[1], lines[row_num]): re_idx = 1 if re_idx is None and re.findall(CMD_PATTERN[2], lines[row_num]): re_idx = 2 if re_idx is None and re.findall(CMD_PATTERN[3], lines[row_num]): re_idx = 3 if re_idx is not None: matches = re.findall(CMD_PATTERN[re_idx], lines[row_num]) command = matches[0] if matches else '' while row_num < total_lines: if (re_idx in [0, 1] and not re.findall(END_PATTERN, lines[row_num])) or \ (re_idx == 2 and (row_num + 1) < total_lines and re.findall(NOT_END_PATTERN, lines[row_num + 1])): row_num += 1 cmd = re.findall(QUO_PATTERN, lines[row_num]) if cmd: command += cmd[0][1] elif re_idx == 3 and (row_num + 1) < total_lines \ and not re.findall(DOCS_END_PATTERN, lines[row_num]): row_num += 1 command += lines[row_num][:-1] else: command = command + ' ' + str(count) ref.append(command) row_num += 1 count += 1 break else: command = command + ' ' + str(count) ref.append(command) row_num += 1 count += 1 break else: row_num += 1 return ref def search_argument_context(row_num, lines): cmds = [] while row_num > 0: row_num -= 1 # Match `with self.argument_context(['"]['"]) as c:` sub_pattern0 = r'with self.argument_context\([\'\"](.*?)[\'\"][\),]' # Match `with self.argument_context(scope) as c:` sub_pattern1 = r'with self.argument_context\(scope[\),]' # Match `with self.argument_context(['"]{} stop['"].format(scope)) as c:', sub_pattern2 = r'with self.argument_context\([\'\"](.*)[\'\"].format\(scope\)\)' # There are many matching patterns, but their proportion is very small. Ignore these commands. ref0 = re.findall(sub_pattern0, lines[row_num]) ref1 = re.findall(sub_pattern1, lines[row_num]) ref2 = re.findall(sub_pattern2, lines[row_num]) # Match `with self.argument_context(['"]['"]) as c:` if ref0: cmds = ref0 break # Match `with self.argument_context(scope) as c:` if ref1: sub_pattern = r'for scope in (.*):' matches = re.findall(sub_pattern, lines[row_num - 1]) if matches: cmds = json.loads(matches[0].replace('\'', '"')) break # Match `with self.argument_context(['"]{} stop['"].format(scope)) as c:', if ref2: sub_pattern = r'for scope in (.*):' format_strings = '' matches = re.findall(sub_pattern, lines[row_num - 1]) if matches: format_strings = json.loads(matches[0].replace('\'', '"')) for c in ref2: for f in format_strings: cmds.append(c.replace('{}', f)) break return cmds def search_argument(line): params = [] param_name = '' # Match ` + c.argument('xxx')?` pattern = r'\+\s+c.argument\((.*)\)?' ref = re.findall(pattern, line) if ref: # strip ' and \' and ) param_name = ref[0].split(',')[0].strip(r"'\'\)") if 'options_list' in ref[0]: # Match ` options_list=xxx, or options_list=xxx)` sub_pattern = r'options_list=\[(.*?)\]' ref2 = re.findall(sub_pattern, ref[0]) if ref2: params = ref2[0].replace('\'', '').replace('"', '').replace(' ', '').split(',') else: # if options_list not exist, generate by parameter name params = ['--' + param_name.replace('_', '-')] return params, param_name def search_command_group(row_num, lines, command): cmd = '' while row_num > 0: row_num = len(lines) - 1 if row_num >= len(lines) else row_num row_num -= 1 # Match `with self.command_group('local-context',` and `with self.command_group('xxx')` sub_pattern = r'with self.command_group\(\'(.*?)\',?' group = re.findall(sub_pattern, lines[row_num]) if group: cmd = group[0] + ' ' + command break return cmd def search_command(line): command = '' # Match `+ g.*command(xxx)` pattern = r'\+\s+g.(?:\w+)?command\((.*)\)' ref = re.findall(pattern, line) if ref: command = ref[0].split(',')[0].strip("'") return command def search_deleted_command(line): command = '' # Match `[-!] g.*command(xxx)` pattern = r'[-!]\s+g.(?:\w+)?command\((.*)\)' ref = re.findall(pattern, line) if ref: command = ref[0].split(',')[0].strip("'") return command def search_aaz_custom_command(line): """ re match pattern + self.command_table['monitor autoscale update'] = AutoScaleUpdate(loader=self) """ cmd = '' aaz_custom_cmd_pattern = r"\+.*\.command_table\[['\"](.*?)['\"]\]" ref = re.findall(aaz_custom_cmd_pattern, line) if ref: cmd = ref[0].strip() return cmd def search_aaz_raw_command(lines): """ re match pattern +@register_command( + "monitor autoscale update", +) """ cmd = '' aaz_raw_cmd_pattern = r"\+@register_command\([\s\S]*?\+.*?['\"](.*?)['\"]" ref = re.findall(aaz_raw_cmd_pattern, str(lines)) if ref: cmd = ref[0].strip() return cmd