1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (c) 2022 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17 18import itertools 19import os 20import re 21import glob 22from typing import * 23import unittest 24 25__all__ = ["translate_str_unit", "BasicTool", "do_nothing", "get_unit", "unit_adaptive"] 26 27 28def unit_adaptive(size: int) -> str: 29 unit_list = ["Byte", "KB", "MB", "GB"] 30 index = 0 31 while index < len(unit_list) and size >= 1024: 32 size /= 1024 33 index += 1 34 if index == len(unit_list): 35 index = len(unit_list)-1 36 size *= 1024 37 return str(round(size))+unit_list[index] 38 39def get_unit(x: str) -> str: 40 pattern = r"[a-z|A-Z]*$" 41 unit = re.search(pattern, x).group() 42 return unit 43 44 45def translate_str_unit(x: str, dest: str, prefix: str = "~") -> float: 46 src_unit = get_unit(x) 47 trans_dict: Dict[str, int] = { 48 "Byte": 1, 49 "byte": 1, 50 "KB": 1024, 51 "kb": 1024, 52 "MB": 1024*1024, 53 "M": 1024*1024, 54 "GB": 1024*1024*1024, 55 "G": 1024*1024*1024 56 } 57 if src_unit not in trans_dict.keys(): 58 raise Exception( 59 f"unsupport unit: {src_unit}. only support {list(trans_dict.keys())}") 60 x = float(x.lstrip(prefix).rstrip(src_unit)) 61 return round(x*(trans_dict.get(src_unit)/trans_dict.get(dest)), 2) 62 63 64def do_nothing(x: Any) -> Any: 65 return x 66 67 68class BasicTool: 69 @classmethod 70 def find_files_with_pattern(cls, folder: str, pattern: str = "/**", recursive: bool = True, apply_abs: bool = True, 71 real_path: bool = True, de_duplicate: bool = True, is_sort: bool = True, 72 post_handler: Callable[[List[str]], List[str]] = None) -> list: 73 """ 74 根据指定paatern匹配folder下的所有文件,默认递归地查找所有文件 75 folder:要查找的目录,会先经过cls.abspath处理(结尾不带/) 76 pattern:要查找的模式,需要以/开头,因为会和folder拼接 77 recursive:是否递归查找 78 apply_abs:是否将路径转换为绝对路径 79 real_path:如果是软链接,是否转换为真正的路径 80 de_duplicate:是否去重 81 is_sort:是否排序 82 post_handler: 对文件进行额外处理的方法,参数为文件名的List,返回值为字符串列表 83 FIXME 有可能会卡住,可能原因为符号链接 84 """ 85 file_list = glob.glob(cls.abspath(folder)+pattern, recursive=recursive) 86 if apply_abs: 87 file_list = list(map(lambda x: cls.abspath(x), file_list)) 88 if real_path: 89 file_list = list(map(lambda x: os.path.realpath(x), file_list)) 90 if de_duplicate: 91 file_list = list(set(file_list)) 92 if is_sort: 93 file_list = sorted(file_list, key=str.lower) 94 if post_handler: 95 file_list = post_handler(file_list) 96 if folder in file_list: 97 file_list.remove(folder) 98 return file_list 99 100 @classmethod 101 def match_paragraph(cls, content: str, start_pattern: str = r"\w+\(\".*?\"\) *{", end_pattern: str = "\}") -> \ 102 Iterator[re.Match]: 103 """ 104 匹配代码段,支持单行 105 注意:ptrn中已经包含前面的空格,所以start_pattern中可以省略 106 :param content: 被匹配的字符串 107 :param start_pattern: 模式的开头 108 :param end_pattern: 模式的结尾 109 :return: 匹配到的段落的迭代器 110 """ 111 ptrn = r'^( *){s}(?#匹配开头).*?(?#中间非贪婪)\1(?#如果开头前面有空格,则结尾的前面应该有相同数量的空格)?{e}$(?#匹配结尾)'.format( 112 s=start_pattern, e=end_pattern) 113 ptrn = re.compile(ptrn, re.M | re.S) 114 result = re.finditer(ptrn, content) 115 return result 116 117 @classmethod 118 def re_group_1(cls, content: str, pattern: str, **kwargs) -> str: 119 """ 120 匹配正则表达式,如果有匹配到内容,返回group(1)的内容 121 :param content: 要被匹配的内容 122 :param pattern: 进行匹配的模式 123 :return: 匹配到的结果(group(1)) 124 TODO 对()的检查应该更严格 125 """ 126 if not (r'(' in pattern and r')' in pattern): 127 raise ValueError("parentheses'()' must in the pattern") 128 result = re.search(pattern, content, **kwargs) 129 if result: 130 return result.group(1) 131 return str() 132 133 @classmethod 134 def abspath(cls, path: str) -> str: 135 """ 136 将路径转换为绝对路径,如果有~,展开 137 :param path: 要被转换的路径 138 :return: 绝对路径 139 """ 140 return os.path.abspath(os.path.expanduser(path)) 141 142 @classmethod 143 def grep_ern(cls, pattern: str, path: str, include: str = str(), exclude: tuple = tuple(), 144 post_handler: Callable[[Text], Any] = do_nothing) -> Any: 145 """ 146 执行grep命令来查找内容 147 :param exclude: 不搜索path下的的exclude目录 148 :param include: 指定要搜索的文件 149 :param pattern: 使用pattern进行grep 150 :param path: 搜索路径 151 :param post_handler: 对grep的结果进行后处理 152 :return: post_handler对grep结果进行处理后的结果 153 TODO 将cls.execute用subprocess代替 154 """ 155 cmd = f"grep -Ern '{pattern}' '{cls.abspath(path)}'" 156 # E:启用正则表达式 r:递归搜索 n:显示行号 157 if include: 158 cmd += f" --include='{include}'" 159 for e in exclude: 160 cmd += f" --exclude-dir='{e}'" 161 o = cls.execute(cmd) 162 if post_handler: 163 o = post_handler(o) 164 return o 165 166 @classmethod 167 def execute(cls, cmd: str, post_processor: Callable[[Text], Text] = do_nothing) -> Any: 168 """ 169 封装popen,返回标准输出的列表 170 :param post_processor: 对执行结果进行处理 171 :param cmd: 待执行的命令 172 :return: 经处理过后的字符串列表 173 174 """ 175 output = os.popen(cmd).read() 176 output = post_processor(output) 177 return output 178