1# 2# Copyright (C) 2016 The Android Open Source Project 3# 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 17import itertools 18import os 19 20DEFAULT_COMMENT_CHAR = '#' 21 22 23def ItemsToStr(input_list): 24 '''Convert item in a list to string. 25 26 Args: 27 input_list: list of objects, the list to convert 28 29 Return: 30 A list of string where objects were converted to string using str function. 31 None if input list is None. 32 ''' 33 if not input_list: 34 return input_list 35 return list(map(str, input_list)) 36 37 38def ExpandItemDelimiters(input_list, 39 delimiter, 40 strip=False, 41 to_str=False, 42 remove_empty=True): 43 '''Expand list items that contain the given delimiter. 44 45 Args: 46 input_list: list of string, a list whose item may contain a delimiter 47 delimiter: string 48 strip: bool, whether to strip items after expanding. Default is False 49 to_str: bool, whether to convert output items in string. 50 Default is False 51 remove_empty: bool, whether to remove empty string in result list. 52 Will not remove None items. Default: True 53 54 Returns: 55 The expended list, which may be the same with input list 56 if no delimiter found; None if input list is None 57 ''' 58 if input_list is None: 59 return None 60 61 do_strip = lambda s: s.strip() if strip else s 62 do_str = lambda s: str(s) if to_str else s 63 64 expended_list_generator = (item.split(delimiter) for item in input_list) 65 result = [ 66 do_strip(do_str(s)) 67 for s in itertools.chain.from_iterable(expended_list_generator) 68 ] 69 return filter(lambda s: str(s) != '', result) if remove_empty else result 70 71 72def DeduplicateKeepOrder(input): 73 '''Remove duplicate items from a sequence while keeping the item order. 74 75 Args: 76 input: a sequence that might have duplicated items. 77 78 Returns: 79 A deduplicated list where item order is kept. 80 ''' 81 return MergeUniqueKeepOrder(input) 82 83 84def MergeUniqueKeepOrder(*lists): 85 '''Merge two list, remove duplicate items, and order. 86 87 Args: 88 lists: any number of lists 89 90 Returns: 91 A merged list where items are unique and original order is kept. 92 ''' 93 seen = set() 94 return [ 95 x for x in itertools.chain(*lists) if not (x in seen or seen.add(x)) 96 ] 97 98 99def LoadListFromCommentedTextFile(file_path, 100 to_str=True, 101 to_strip=True, 102 exclude_empty_line=True, 103 exclude_comment_line=True, 104 exclude_trailing_comment=True, 105 remove_duplicates=False, 106 remove_line_breaks=True, 107 comment_char=DEFAULT_COMMENT_CHAR): 108 '''Read commented text file into a list of lines. 109 110 Comments or empty lines will be excluded by default. 111 112 Args: 113 file_path: string, path to file 114 to_str: bool, whether to convert lines to string in result list. 115 Default value is True. 116 to_strip: bool, whether to strip lines in result list. 117 Default value is True. 118 exclude_empty_line: bool, whether to exclude empty items in result list 119 Default value is True. 120 exclude_comment_line: bool, whether to exclude lines that only contains comments. 121 If a line starts with spaces and ends with comments it 122 will still be excluded even if to_trim is False. 123 Default value is True. 124 exclude_trailing_comment: bool, whether to remove trailing comments 125 from result items. 126 Default value is True. 127 remove_duplicates: bool, whether to remove duplicate items in output list. 128 Default value is False. 129 remove_line_breaks: bool, whether to remove trailing trailing 130 new line characters from result items. 131 Default value is True. 132 comment_char: string, character to denote comment. 133 Default value is pound (#). 134 135 Returns: 136 a list of string. None if file does not exist. 137 ''' 138 if not os.path.isfile(file_path): 139 logging.error('The path provided is not a file or does not exist: %s', 140 file_path) 141 return None 142 143 with open(file_path, 'r') as f: 144 return LoadListFromCommentedText( 145 f.read(), 146 to_str, 147 to_strip, 148 exclude_empty_line, 149 exclude_comment_line, 150 exclude_trailing_comment, 151 remove_duplicates, 152 remove_line_breaks, 153 comment_char=DEFAULT_COMMENT_CHAR) 154 155 156def LoadListFromCommentedText(text, 157 to_str=True, 158 to_strip=True, 159 exclude_empty_line=True, 160 exclude_comment_line=True, 161 exclude_trailing_comment=True, 162 remove_duplicates=False, 163 remove_line_breaks=True, 164 comment_char=DEFAULT_COMMENT_CHAR): 165 '''Read commented text into a list of lines. 166 167 Comments or empty lines will be excluded by default. 168 169 Args: 170 text: string, text to parse 171 to_str: bool, whether to convert lines to string in result list. 172 Default value is True. 173 to_strip: bool, whether to strip lines in result list. 174 Default value is True. 175 exclude_empty_line: bool, whether to exclude empty items in result list 176 Default value is True. 177 exclude_comment_line: bool, whether to exclude lines that only contains comments. 178 If a line starts with spaces and ends with comments it 179 will still be excluded even if to_trim is False. 180 Default value is True. 181 exclude_trailing_comment: bool, whether to remove trailing comments 182 from result items. 183 Default value is True. 184 remove_duplicates: bool, whether to remove duplicate items in output list. 185 Default value is False. 186 remove_line_breaks: bool, whether to remove trailing trailing 187 new line characters from result items. 188 Default value is True. 189 comment_char: string, character to denote comment. 190 Default value is pound (#). 191 192 Returns: 193 a list of string. 194 ''' 195 lines = text.splitlines(not remove_line_breaks) 196 197 if to_str: 198 lines = map(str, lines) 199 200 if exclude_trailing_comment: 201 202 def RemoveComment(line): 203 idx = line.find(comment_char) 204 if idx < 0: 205 return line 206 else: 207 return line[:idx] 208 209 lines = map(RemoveComment, lines) 210 211 if to_strip: 212 lines = map(lambda line: line.strip(), lines) 213 214 if exclude_comment_line: 215 lines = filter(lambda line: not line.strip().startswith(comment_char), 216 lines) 217 218 if exclude_empty_line: 219 lines = filter(bool, lines) 220 221 if remove_duplicates: 222 lines = DeduplicateKeepOrder(lines) 223 224 return lines 225