1import os 2import re 3 4 5def translate(pat, match_end=r"\Z"): 6 """Translate a shell-style pattern to a regular expression. 7 8 The pattern may include ``**<sep>`` (<sep> stands for the platform-specific path separator; "/" on POSIX systems) for 9 matching zero or more directory levels and "*" for matching zero or more arbitrary characters with the exception of 10 any path separator. Wrap meta-characters in brackets for a literal match (i.e. "[?]" to match the literal character 11 "?"). 12 13 Using match_end=regex one can give a regular expression that is used to match after the regex that is generated from 14 the pattern. The default is to match the end of the string. 15 16 This function is derived from the "fnmatch" module distributed with the Python standard library. 17 18 Copyright (C) 2001-2016 Python Software Foundation. All rights reserved. 19 20 TODO: support {alt1,alt2} shell-style alternatives 21 22 """ 23 sep = os.path.sep 24 n = len(pat) 25 i = 0 26 res = "" 27 28 while i < n: 29 c = pat[i] 30 i += 1 31 32 if c == "*": 33 if i + 1 < n and pat[i] == "*" and pat[i + 1] == sep: 34 # **/ == wildcard for 0+ full (relative) directory names with trailing slashes; the forward slash stands 35 # for the platform-specific path separator 36 res += r"(?:[^\%s]*\%s)*" % (sep, sep) 37 i += 2 38 else: 39 # * == wildcard for name parts (does not cross path separator) 40 res += r"[^\%s]*" % sep 41 elif c == "?": 42 # ? == any single character excluding path separator 43 res += r"[^\%s]" % sep 44 elif c == "[": 45 j = i 46 if j < n and pat[j] == "!": 47 j += 1 48 if j < n and pat[j] == "]": 49 j += 1 50 while j < n and pat[j] != "]": 51 j += 1 52 if j >= n: 53 res += "\\[" 54 else: 55 stuff = pat[i:j].replace("\\", "\\\\") 56 i = j + 1 57 if stuff[0] == "!": 58 stuff = "^" + stuff[1:] 59 elif stuff[0] == "^": 60 stuff = "\\" + stuff 61 res += "[%s]" % stuff 62 else: 63 res += re.escape(c) 64 65 return "(?ms)" + res + match_end 66