"""Filename matching with shell patterns. fnmatch(FILENAME, PATTERN) matches according to the local convention. fnmatchcase(FILENAME, PATTERN) always takes case in account. The functions operate by translating the pattern into a regular expression. They cache the compiled regular expressions for speed. The function translate(PATTERN) returns a regular expression corresponding to PATTERN. (It does not compile it.) """ import re __all__ = ["filter", "fnmatch","fnmatchcase","translate"] _cache = {} def fnmatch(name, pat): """Test whether FILENAME matches PATTERN. Patterns are Unix shell style: * matches zero or more characters ? matches a single character [chars] matches any single character in chars [!chars] matches any single character not in chars {abc,def,...} matches "abc" or "def" and so on. For example: {.htm,.html} You cannot quote meta-characters or match ] inside [chars]. An initial period in FILENAME is not special. Both FILENAME and PATTERN are first case-normalized if the operating system requires it. If you don't want this, use fnmatchcase(FILENAME, PATTERN). """ import os name = os.path.normcase(name) pat = os.path.normcase(pat) return fnmatchcase(name, pat) def filter(names, pat): """Return the subset of the list NAMES that match PAT""" import os,posixpath result=[] pat=os.path.normcase(pat) if not pat in _cache: res = translate(pat) _cache[pat] = re.compile(res) match=_cache[pat].match if os.path is posixpath: # normcase on posix is NOP. Optimize it away from the loop. for name in names: if match(name): result.append(name) else: for name in names: if match(os.path.normcase(name)): result.append(name) return result def fnmatchcase(name, pat): """Test whether FILENAME matches PATTERN, including case. This is a version of fnmatch() which doesn't case-normalize its arguments. """ if not pat in _cache: res = translate(pat) _cache[pat] = re.compile(res) return _cache[pat].match(name) is not None def translate(pattern): """Translate a shell PATTERN to a regular expression. There is no way to quote meta-characters. """ NORMAL, INBRACKETS, INBRACES = range(3) state = NORMAL rx = [] for c in pattern: if state == NORMAL: if c == "*": rx.append(".*") elif c == "?": rx.append(".") elif c == "[": rx.append("[") state = INBRACKETS elif c == "{": rx.append("(?:") state = INBRACES else: rx.append(re.escape(c)) elif state == INBRACKETS: if rx[-1] == "[" and c == "!": rx.append("^") elif c == "]": rx.append("]") state = NORMAL elif c == "-": rx.append("-") else: rx.append(re.escape(c)) elif state == INBRACES: if c == ",": rx.append("|") elif c == "}": rx.append(")") state = NORMAL else: rx.append(re.escape(c)) rx.append("$") return "".join(rx)