pathspec.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. """
  2. This module provides an object oriented interface for pattern matching of files.
  3. """
  4. from collections.abc import (
  5. Collection as CollectionType)
  6. from itertools import (
  7. zip_longest)
  8. from typing import (
  9. AnyStr,
  10. Callable, # Replaced by `collections.abc.Callable` in 3.9.
  11. Collection, # Replaced by `collections.abc.Collection` in 3.9.
  12. Iterable, # Replaced by `collections.abc.Iterable` in 3.9.
  13. Iterator, # Replaced by `collections.abc.Iterator` in 3.9.
  14. Optional, # Replaced by `X | None` in 3.10.
  15. Type, # Replaced by `type` in 3.9.
  16. TypeVar,
  17. Union) # Replaced by `X | Y` in 3.10.
  18. from . import util
  19. from .pattern import (
  20. Pattern)
  21. from .util import (
  22. CheckResult,
  23. StrPath,
  24. TStrPath,
  25. TreeEntry,
  26. _filter_check_patterns,
  27. _is_iterable,
  28. normalize_file)
  29. Self = TypeVar("Self", bound="PathSpec")
  30. """
  31. :class:`PathSpec` self type hint to support Python v<3.11 using PEP 673
  32. recommendation.
  33. """
  34. class PathSpec(object):
  35. """
  36. The :class:`PathSpec` class is a wrapper around a list of compiled
  37. :class:`.Pattern` instances.
  38. """
  39. def __init__(self, patterns: Iterable[Pattern]) -> None:
  40. """
  41. Initializes the :class:`PathSpec` instance.
  42. *patterns* (:class:`~collections.abc.Collection` or :class:`~collections.abc.Iterable`)
  43. yields each compiled pattern (:class:`.Pattern`).
  44. """
  45. if not isinstance(patterns, CollectionType):
  46. patterns = list(patterns)
  47. self.patterns: Collection[Pattern] = patterns
  48. """
  49. *patterns* (:class:`~collections.abc.Collection` of :class:`.Pattern`)
  50. contains the compiled patterns.
  51. """
  52. def __eq__(self, other: object) -> bool:
  53. """
  54. Tests the equality of this path-spec with *other* (:class:`PathSpec`)
  55. by comparing their :attr:`~PathSpec.patterns` attributes.
  56. """
  57. if isinstance(other, PathSpec):
  58. paired_patterns = zip_longest(self.patterns, other.patterns)
  59. return all(a == b for a, b in paired_patterns)
  60. else:
  61. return NotImplemented
  62. def __len__(self) -> int:
  63. """
  64. Returns the number of compiled patterns this path-spec contains
  65. (:class:`int`).
  66. """
  67. return len(self.patterns)
  68. def __add__(self: Self, other: "PathSpec") -> Self:
  69. """
  70. Combines the :attr:`Pathspec.patterns` patterns from two
  71. :class:`PathSpec` instances.
  72. """
  73. if isinstance(other, PathSpec):
  74. return self.__class__(self.patterns + other.patterns)
  75. else:
  76. return NotImplemented
  77. def __iadd__(self: Self, other: "PathSpec") -> Self:
  78. """
  79. Adds the :attr:`Pathspec.patterns` patterns from one :class:`PathSpec`
  80. instance to this instance.
  81. """
  82. if isinstance(other, PathSpec):
  83. self.patterns += other.patterns
  84. return self
  85. else:
  86. return NotImplemented
  87. def check_file(
  88. self,
  89. file: TStrPath,
  90. separators: Optional[Collection[str]] = None,
  91. ) -> CheckResult[TStrPath]:
  92. """
  93. Check the files against this path-spec.
  94. *file* (:class:`str` or :class:`os.PathLike`) is the file path to be
  95. matched against :attr:`self.patterns <PathSpec.patterns>`.
  96. *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
  97. :data:`None`) optionally contains the path separators to normalize. See
  98. :func:`~pathspec.util.normalize_file` for more information.
  99. Returns the file check result (:class:`~pathspec.util.CheckResult`).
  100. """
  101. norm_file = normalize_file(file, separators)
  102. include, index = self._match_file(enumerate(self.patterns), norm_file)
  103. return CheckResult(file, include, index)
  104. def check_files(
  105. self,
  106. files: Iterable[TStrPath],
  107. separators: Optional[Collection[str]] = None,
  108. ) -> Iterator[CheckResult[TStrPath]]:
  109. """
  110. Check the files against this path-spec.
  111. *files* (:class:`~collections.abc.Iterable` of :class:`str` or
  112. :class:`os.PathLike`) contains the file paths to be checked against
  113. :attr:`self.patterns <PathSpec.patterns>`.
  114. *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
  115. :data:`None`) optionally contains the path separators to normalize. See
  116. :func:`~pathspec.util.normalize_file` for more information.
  117. Returns an :class:`~collections.abc.Iterator` yielding each file check
  118. result (:class:`~pathspec.util.CheckResult`).
  119. """
  120. if not _is_iterable(files):
  121. raise TypeError(f"files:{files!r} is not an iterable.")
  122. use_patterns = _filter_check_patterns(self.patterns)
  123. for orig_file in files:
  124. norm_file = normalize_file(orig_file, separators)
  125. include, index = self._match_file(use_patterns, norm_file)
  126. yield CheckResult(orig_file, include, index)
  127. def check_tree_files(
  128. self,
  129. root: StrPath,
  130. on_error: Optional[Callable[[OSError], None]] = None,
  131. follow_links: Optional[bool] = None,
  132. ) -> Iterator[CheckResult[str]]:
  133. """
  134. Walks the specified root path for all files and checks them against this
  135. path-spec.
  136. *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
  137. search for files.
  138. *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
  139. is the error handler for file-system exceptions. It will be called with the
  140. exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
  141. is :data:`None` to ignore file-system exceptions.
  142. *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
  143. symbolic links that resolve to directories. Default is :data:`None` for
  144. :data:`True`.
  145. *negate* (:class:`bool` or :data:`None`) is whether to negate the match
  146. results of the patterns. If :data:`True`, a pattern matching a file will
  147. exclude the file rather than include it. Default is :data:`None` for
  148. :data:`False`.
  149. Returns an :class:`~collections.abc.Iterator` yielding each file check
  150. result (:class:`~pathspec.util.CheckResult`).
  151. """
  152. files = util.iter_tree_files(root, on_error=on_error, follow_links=follow_links)
  153. yield from self.check_files(files)
  154. @classmethod
  155. def from_lines(
  156. cls: Type[Self],
  157. pattern_factory: Union[str, Callable[[AnyStr], Pattern]],
  158. lines: Iterable[AnyStr],
  159. ) -> Self:
  160. """
  161. Compiles the pattern lines.
  162. *pattern_factory* can be either the name of a registered pattern factory
  163. (:class:`str`), or a :class:`~collections.abc.Callable` used to compile
  164. patterns. It must accept an uncompiled pattern (:class:`str`) and return the
  165. compiled pattern (:class:`.Pattern`).
  166. *lines* (:class:`~collections.abc.Iterable`) yields each uncompiled pattern
  167. (:class:`str`). This simply has to yield each line so that it can be a
  168. :class:`io.TextIOBase` (e.g., from :func:`open` or :class:`io.StringIO`) or
  169. the result from :meth:`str.splitlines`.
  170. Returns the :class:`PathSpec` instance.
  171. """
  172. if isinstance(pattern_factory, str):
  173. pattern_factory = util.lookup_pattern(pattern_factory)
  174. if not callable(pattern_factory):
  175. raise TypeError(f"pattern_factory:{pattern_factory!r} is not callable.")
  176. if not _is_iterable(lines):
  177. raise TypeError(f"lines:{lines!r} is not an iterable.")
  178. patterns = [pattern_factory(line) for line in lines if line]
  179. return cls(patterns)
  180. def match_entries(
  181. self,
  182. entries: Iterable[TreeEntry],
  183. separators: Optional[Collection[str]] = None,
  184. *,
  185. negate: Optional[bool] = None,
  186. ) -> Iterator[TreeEntry]:
  187. """
  188. Matches the entries to this path-spec.
  189. *entries* (:class:`~collections.abc.Iterable` of :class:`~pathspec.util.TreeEntry`)
  190. contains the entries to be matched against :attr:`self.patterns <PathSpec.patterns>`.
  191. *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
  192. :data:`None`) optionally contains the path separators to normalize. See
  193. :func:`~pathspec.util.normalize_file` for more information.
  194. *negate* (:class:`bool` or :data:`None`) is whether to negate the match
  195. results of the patterns. If :data:`True`, a pattern matching a file will
  196. exclude the file rather than include it. Default is :data:`None` for
  197. :data:`False`.
  198. Returns the matched entries (:class:`~collections.abc.Iterator` of
  199. :class:`~pathspec.util.TreeEntry`).
  200. """
  201. if not _is_iterable(entries):
  202. raise TypeError(f"entries:{entries!r} is not an iterable.")
  203. use_patterns = _filter_check_patterns(self.patterns)
  204. for entry in entries:
  205. norm_file = normalize_file(entry.path, separators)
  206. include, _index = self._match_file(use_patterns, norm_file)
  207. if negate:
  208. include = not include
  209. if include:
  210. yield entry
  211. _match_file = staticmethod(util.check_match_file)
  212. """
  213. Match files using the `check_match_file()` utility function. Subclasses may
  214. override this method as an instance method. It does not have to be a static
  215. method. The signature for this method is subject to change.
  216. """
  217. def match_file(
  218. self,
  219. file: StrPath,
  220. separators: Optional[Collection[str]] = None,
  221. ) -> bool:
  222. """
  223. Matches the file to this path-spec.
  224. *file* (:class:`str` or :class:`os.PathLike`) is the file path to be
  225. matched against :attr:`self.patterns <PathSpec.patterns>`.
  226. *separators* (:class:`~collections.abc.Collection` of :class:`str`)
  227. optionally contains the path separators to normalize. See
  228. :func:`~pathspec.util.normalize_file` for more information.
  229. Returns :data:`True` if *file* matched; otherwise, :data:`False`.
  230. """
  231. norm_file = normalize_file(file, separators)
  232. include, _index = self._match_file(enumerate(self.patterns), norm_file)
  233. return bool(include)
  234. def match_files(
  235. self,
  236. files: Iterable[StrPath],
  237. separators: Optional[Collection[str]] = None,
  238. *,
  239. negate: Optional[bool] = None,
  240. ) -> Iterator[StrPath]:
  241. """
  242. Matches the files to this path-spec.
  243. *files* (:class:`~collections.abc.Iterable` of :class:`str` or
  244. :class:`os.PathLike`) contains the file paths to be matched against
  245. :attr:`self.patterns <PathSpec.patterns>`.
  246. *separators* (:class:`~collections.abc.Collection` of :class:`str`; or
  247. :data:`None`) optionally contains the path separators to normalize. See
  248. :func:`~pathspec.util.normalize_file` for more information.
  249. *negate* (:class:`bool` or :data:`None`) is whether to negate the match
  250. results of the patterns. If :data:`True`, a pattern matching a file will
  251. exclude the file rather than include it. Default is :data:`None` for
  252. :data:`False`.
  253. Returns the matched files (:class:`~collections.abc.Iterator` of
  254. :class:`str` or :class:`os.PathLike`).
  255. """
  256. if not _is_iterable(files):
  257. raise TypeError(f"files:{files!r} is not an iterable.")
  258. use_patterns = _filter_check_patterns(self.patterns)
  259. for orig_file in files:
  260. norm_file = normalize_file(orig_file, separators)
  261. include, _index = self._match_file(use_patterns, norm_file)
  262. if negate:
  263. include = not include
  264. if include:
  265. yield orig_file
  266. def match_tree_entries(
  267. self,
  268. root: StrPath,
  269. on_error: Optional[Callable[[OSError], None]] = None,
  270. follow_links: Optional[bool] = None,
  271. *,
  272. negate: Optional[bool] = None,
  273. ) -> Iterator[TreeEntry]:
  274. """
  275. Walks the specified root path for all files and matches them to this
  276. path-spec.
  277. *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
  278. search.
  279. *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
  280. is the error handler for file-system exceptions. It will be called with the
  281. exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
  282. is :data:`None` to ignore file-system exceptions.
  283. *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
  284. symbolic links that resolve to directories. Default is :data:`None` for
  285. :data:`True`.
  286. *negate* (:class:`bool` or :data:`None`) is whether to negate the match
  287. results of the patterns. If :data:`True`, a pattern matching a file will
  288. exclude the file rather than include it. Default is :data:`None` for
  289. :data:`False`.
  290. Returns the matched files (:class:`~collections.abc.Iterator` of
  291. :class:`.TreeEntry`).
  292. """
  293. entries = util.iter_tree_entries(root, on_error=on_error, follow_links=follow_links)
  294. yield from self.match_entries(entries, negate=negate)
  295. def match_tree_files(
  296. self,
  297. root: StrPath,
  298. on_error: Optional[Callable[[OSError], None]] = None,
  299. follow_links: Optional[bool] = None,
  300. *,
  301. negate: Optional[bool] = None,
  302. ) -> Iterator[str]:
  303. """
  304. Walks the specified root path for all files and matches them to this
  305. path-spec.
  306. *root* (:class:`str` or :class:`os.PathLike`) is the root directory to
  307. search for files.
  308. *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally
  309. is the error handler for file-system exceptions. It will be called with the
  310. exception (:exc:`OSError`). Reraise the exception to abort the walk. Default
  311. is :data:`None` to ignore file-system exceptions.
  312. *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk
  313. symbolic links that resolve to directories. Default is :data:`None` for
  314. :data:`True`.
  315. *negate* (:class:`bool` or :data:`None`) is whether to negate the match
  316. results of the patterns. If :data:`True`, a pattern matching a file will
  317. exclude the file rather than include it. Default is :data:`None` for
  318. :data:`False`.
  319. Returns the matched files (:class:`~collections.abc.Iterable` of
  320. :class:`str`).
  321. """
  322. files = util.iter_tree_files(root, on_error=on_error, follow_links=follow_links)
  323. yield from self.match_files(files, negate=negate)
  324. # Alias `match_tree_files()` as `match_tree()` for backward compatibility
  325. # before v0.3.2.
  326. match_tree = match_tree_files