Source code for fs.wildcard

"""Match wildcard filenames.
"""
# Adapted from https://hg.python.org/cpython/file/2.7/Lib/fnmatch.py

from __future__ import print_function, unicode_literals

import typing

import re
from functools import partial

from .lrucache import LRUCache

if typing.TYPE_CHECKING:
    from typing import Callable, Iterable, Pattern, Text, Tuple


_PATTERN_CACHE = LRUCache(1000)  # type: LRUCache[Tuple[Text, bool], Pattern]


[docs]def match(pattern, name): # type: (Text, Text) -> bool """Test whether a name matches a wildcard pattern. Arguments: pattern (str): A wildcard pattern, e.g. ``"*.py"``. name (str): A filename. Returns: bool: `True` if the filename matches the pattern. """ try: re_pat = _PATTERN_CACHE[(pattern, True)] except KeyError: res = "(?ms)" + _translate(pattern) + r"\Z" _PATTERN_CACHE[(pattern, True)] = re_pat = re.compile(res) return re_pat.match(name) is not None
[docs]def imatch(pattern, name): # type: (Text, Text) -> bool """Test whether a name matches a wildcard pattern (case insensitive). Arguments: pattern (str): A wildcard pattern, e.g. ``"*.py"``. name (bool): A filename. Returns: bool: `True` if the filename matches the pattern. """ try: re_pat = _PATTERN_CACHE[(pattern, False)] except KeyError: res = "(?ms)" + _translate(pattern, case_sensitive=False) + r"\Z" _PATTERN_CACHE[(pattern, False)] = re_pat = re.compile(res, re.IGNORECASE) return re_pat.match(name) is not None
[docs]def match_any(patterns, name): # type: (Iterable[Text], Text) -> bool """Test if a name matches any of a list of patterns. Will return `True` if ``patterns`` is an empty list. Arguments: patterns (list): A list of wildcard pattern, e.g ``["*.py", "*.pyc"]`` name (str): A filename. Returns: bool: `True` if the name matches at least one of the patterns. """ if not patterns: return True return any(match(pattern, name) for pattern in patterns)
[docs]def imatch_any(patterns, name): # type: (Iterable[Text], Text) -> bool """Test if a name matches any of a list of patterns (case insensitive). Will return `True` if ``patterns`` is an empty list. Arguments: patterns (list): A list of wildcard pattern, e.g ``["*.py", "*.pyc"]`` name (str): A filename. Returns: bool: `True` if the name matches at least one of the patterns. """ if not patterns: return True return any(imatch(pattern, name) for pattern in patterns)
[docs]def get_matcher(patterns, case_sensitive): # type: (Iterable[Text], bool) -> Callable[[Text], bool] """Get a callable that matches names against the given patterns. Arguments: patterns (list): A list of wildcard pattern. e.g. ``["*.py", "*.pyc"]`` case_sensitive (bool): If ``True``, then the callable will be case sensitive, otherwise it will be case insensitive. Returns: callable: a matcher that will return `True` if the name given as an argument matches any of the given patterns. Example: >>> from fs import wildcard >>> is_python = wildcard.get_matcher(['*.py'], True) >>> is_python('__init__.py') True >>> is_python('foo.txt') False """ if not patterns: return lambda name: True if case_sensitive: return partial(match_any, patterns) else: return partial(imatch_any, patterns)
def _translate(pattern, case_sensitive=True): # type: (Text, bool) -> Text """Translate a wildcard pattern to a regular expression. There is no way to quote meta-characters. Arguments: pattern (str): A wildcard pattern. case_sensitive (bool): Set to `False` to use a case insensitive regex (default `True`). Returns: str: A regex equivalent to the given pattern. """ if not case_sensitive: pattern = pattern.lower() i, n = 0, len(pattern) res = "" while i < n: c = pattern[i] i = i + 1 if c == "*": res = res + "[^/]*" elif c == "?": res = res + "." elif c == "[": j = i if j < n and pattern[j] == "!": j = j + 1 if j < n and pattern[j] == "]": j = j + 1 while j < n and pattern[j] != "]": j = j + 1 if j >= n: res = res + "\\[" else: stuff = pattern[i:j].replace("\\", "\\\\") i = j + 1 if stuff[0] == "!": stuff = "^" + stuff[1:] elif stuff[0] == "^": stuff = "\\" + stuff res = "%s[%s]" % (res, stuff) else: res = res + re.escape(c) return res