ansible-later/env_27/lib/python2.7/site-packages/globmatch/translation.py
2019-04-11 13:00:36 +02:00

105 lines
3.2 KiB
Python

#!/usr/bin/env python
# coding: utf-8
# Copyright (c) Simula Research Laboratory.
# Distributed under the terms of the Modified BSD License.
"""Utilities for matching a path against globs."""
import os
import re
try:
from functools import lru_cache
except ImportError:
from backports.functools_lru_cache import lru_cache
from .pathutils import iexplode_path, SEPARATORS
@lru_cache(maxsize=256, typed=True)
def compile_pattern(pat):
"""Translate and compile a glob pattern to a regular expression matcher."""
if isinstance(pat, bytes):
pat_str = pat.decode('ISO-8859-1')
res_str = translate_glob(os.path.normcase(pat_str))
res = res_str.encode('ISO-8859-1')
else:
res = translate_glob(os.path.normcase(pat))
return re.compile(res).match
def translate_glob(pat):
"""Translate a glob PATTERN to a regular expression."""
translated_parts = []
for part in iexplode_path(pat):
translated_parts.append(translate_glob_part(part))
os_sep_class = '[%s]' % re.escape(SEPARATORS)
res = join_translated(translated_parts, os_sep_class)
res = '{res}({os_sep_class}?.*)?\\Z(?ms)'.format(res=res, os_sep_class=os_sep_class)
# in the future (py 3.6+):
# res = f'{res}({os_sep_class}?.*)?\\Z(?ms)'
return res
def join_translated(translated_parts, os_sep_class):
"""Join translated glob pattern parts.
This is different from a simple join, as care need to be taken
to allow ** to match ZERO or more directories.
"""
if len(translated_parts) < 2:
return translated_parts[0]
res = ''
for part in translated_parts[:-1]:
if part == '.*':
# drop separator, since it is optional
# (** matches ZERO or more dirs)
res += part
else:
res += part + os_sep_class
if translated_parts[-1] == '.*':
# Final part is **, undefined behavior since we don't check against filesystem
res += translated_parts[-1]
else:
res += translated_parts[-1]
return res
def translate_glob_part(pat):
"""Translate a glob PATTERN PART to a regular expression."""
# Code modified from Python 3 standard lib fnmatch:
if pat == '**':
return '.*'
i, n = 0, len(pat)
res = []
while i < n:
c = pat[i]
i = i+1
if c == '*':
# Match anything but path separators:
res.append('[^%s]*' % SEPARATORS)
elif c == '?':
res.append('[^%s]?' % SEPARATORS)
elif c == '[':
j = i
if j < n and pat[j] == '!':
j = j+1
if j < n and pat[j] == ']':
j = j+1
while j < n and pat[j] != ']':
j = j+1
if j >= n:
res.append('\\[')
else:
stuff = pat[i:j].replace('\\', '\\\\')
i = j+1
if stuff[0] == '!':
stuff = '^' + stuff[1:]
elif stuff[0] == '^':
stuff = '\\' + stuff
res.append('[%s]' % stuff)
else:
res.append(re.escape(c))
return ''.join(res)