patterns.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. # -*- coding: utf-8 -*-
  2. '''stuf search.'''
  3. from os import sep
  4. from functools import partial
  5. from parse import compile as pcompile
  6. from .utils import lru
  7. from .base import first
  8. from .six.moves import filterfalse # @UnresolvedImport
  9. from .six import isstring, filter, map, rcompile, rescape, rsub
  10. def globpattern(expr):
  11. '''Translate glob `expr` to regular expression.'''
  12. i, n = 0, len(expr)
  13. res = []
  14. rappend = res.append
  15. while i < n:
  16. c = expr[i]
  17. i += 1
  18. if c == '*':
  19. rappend('(.*)')
  20. elif c == '?':
  21. rappend('(.)')
  22. elif c == '[':
  23. j = i
  24. if j < n and expr[j] == '!':
  25. j += 1
  26. if j < n and expr[j] == ']':
  27. j += 1
  28. while j < n and expr[j] != ']':
  29. j += 1
  30. if j >= n:
  31. rappend('\\[')
  32. else:
  33. stuff = expr[i:j].replace('\\', '\\\\')
  34. i = j + 1
  35. if stuff[0] == '!':
  36. stuff = '^' + stuff[1:]
  37. elif stuff[0] == '^':
  38. stuff = '\\' + stuff
  39. rappend('[{0}]'.format(stuff))
  40. else:
  41. rappend(rescape(c))
  42. rappend('\Z(?ms)')
  43. return rsub(
  44. r'((?<!\\)(\\\\)*)\.',
  45. r'\1[^{0}]'.format(r'\\\\' if sep == '\\' else sep),
  46. r''.join(res),
  47. )
  48. # regular expression search
  49. regex = lambda expr, flag: rcompile(expr, flag).search
  50. # parse search
  51. parse = lambda expr, flag: pcompile(expr)._search_re.search
  52. # glob search
  53. glob = lambda expr, flag: rcompile(globpattern(expr), flag).search
  54. # search dictionary
  55. SEARCH = dict(parse=parse, glob=glob, regex=regex)
  56. @lru()
  57. def searcher(expr, flags=32):
  58. '''Build search function from `expr`.'''
  59. try:
  60. scheme, expr = expr.split(':', 1) if isstring(expr) else expr
  61. return SEARCH[scheme](expr, flags)
  62. except KeyError:
  63. raise TypeError('"{0}" is invalid search scheme'.format(scheme))
  64. def detect(patterns):
  65. '''Match item in sequence with pattern in `patterns`.'''
  66. return partial(
  67. lambda y, x: any(p(first(x)) for p in y),
  68. tuple(map(searcher, patterns)),
  69. )
  70. def _clude(filter, default, patterns):
  71. '''Create filter from `patterns`.'''
  72. if not patterns:
  73. # trivial case: *clude everything
  74. return default
  75. patterns = tuple(map(searcher, patterns))
  76. # handle general case for *clusion
  77. return partial(filter, lambda x: any(p(x) for p in patterns))
  78. # filter for exclusion `patterns`.
  79. exclude = partial(_clude, filterfalse, lambda x: x)
  80. # filter for inclusion `patterns`.
  81. include = partial(_clude, filter, lambda x: x[0:0])