1""" 2Helpful generators and other tools 3""" 4 5 6import fnmatch 7import re 8 9import salt.utils.files 10 11 12def split(orig, sep=None): 13 """ 14 Generator function for iterating through large strings, particularly useful 15 as a replacement for str.splitlines(). 16 17 See http://stackoverflow.com/a/3865367 18 """ 19 exp = re.compile(r"\s+" if sep is None else re.escape(sep)) 20 pos = 0 21 length = len(orig) 22 while True: 23 match = exp.search(orig, pos) 24 if not match: 25 if pos < length or sep is not None: 26 val = orig[pos:] 27 if val: 28 # Only yield a value if the slice was not an empty string, 29 # because if it is then we've reached the end. This keeps 30 # us from yielding an extra blank value at the end. 31 yield val 32 break 33 if pos < match.start() or sep is not None: 34 yield orig[pos : match.start()] 35 pos = match.end() 36 37 38def read_file(fh_, chunk_size=1048576): 39 """ 40 Generator that reads chunk_size bytes at a time from a file/filehandle and 41 yields it. 42 """ 43 try: 44 if chunk_size != int(chunk_size): 45 raise ValueError 46 except ValueError: 47 raise ValueError("chunk_size must be an integer") 48 try: 49 while True: 50 try: 51 chunk = fh_.read(chunk_size) 52 except AttributeError: 53 # Open the file and re-attempt the read 54 fh_ = salt.utils.files.fopen(fh_, "rb") # pylint: disable=W8470 55 chunk = fh_.read(chunk_size) 56 if not chunk: 57 break 58 yield chunk 59 finally: 60 try: 61 fh_.close() 62 except AttributeError: 63 pass 64 65 66def fnmatch_multiple(candidates, pattern): 67 """ 68 Convenience function which runs fnmatch.fnmatch() on each element of passed 69 iterable. The first matching candidate is returned, or None if there is no 70 matching candidate. 71 """ 72 # Make sure that candidates is iterable to avoid a TypeError when we try to 73 # iterate over its items. 74 try: 75 candidates_iter = iter(candidates) 76 except TypeError: 77 return None 78 79 for candidate in candidates_iter: 80 try: 81 if fnmatch.fnmatch(candidate, pattern): 82 return candidate 83 except TypeError: 84 pass 85 return None 86