glob_matcher.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. # Copyright (c) 2021 Project CHIP Authors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. def _GlobMatch(glob: str, value: str) -> bool:
  15. """Does limited glob string matching with the following support:
  16. "*" matches 0 or more characters
  17. "?" matches any 1 character
  18. "{x,y,z}" matches x or y or z
  19. There are limitations in the current implementation:
  20. - no support for escapes (cannot match literal *, ? or {)
  21. - no support for nested {} (like "{a,foo{b,c}bar,d}" used
  22. to match "{a,foobbar,foocbar,d}")
  23. """
  24. glob = glob.replace('**', '*')
  25. while glob and value:
  26. if glob[0] == '?':
  27. glob, value = glob[1:], value[1:]
  28. elif glob[0] == '*':
  29. for idx in range(len(value)+1):
  30. if _GlobMatch(glob[1:], value[idx:]):
  31. return True
  32. return False
  33. elif glob[0] == '{':
  34. # Format is comma separated values between {}:
  35. # NOTE: this does NOT support nested {} at the moment
  36. closing_idx = glob.find('}')
  37. if not closing_idx:
  38. raise Exception("Malformed glob expression: missing '}'")
  39. for choice in glob[1: closing_idx].split(','):
  40. if _GlobMatch(choice + glob[closing_idx+1:], value):
  41. return True
  42. return False
  43. else:
  44. if glob[0] != value[0]:
  45. return False
  46. glob, value = glob[1:], value[1:]
  47. # if value is empty it has a chance to match subgroups
  48. if not value and glob.startswith('{') and glob.endswith('}'):
  49. for choice in glob[1: -1].split(','):
  50. if _GlobMatch(choice, value):
  51. return True
  52. return glob == '*' or (not glob and not value)
  53. class GlobMatcher:
  54. def __init__(self, glob: str):
  55. self.glob = glob
  56. def matches(self, value: str) -> bool:
  57. return _GlobMatch(self.glob, value)