# encoding: utf-8 """ This module provides an object oriented interface for pattern matching of files. """ from . import util from .compat import collection_type, iterkeys, izip_longest, string_types, unicode class PathSpec(object): """ The :class:`PathSpec` class is a wrapper around a list of compiled :class:`.Pattern` instances. """ def __init__(self, patterns): """ Initializes the :class:`PathSpec` instance. *patterns* (:class:`~collections.abc.Collection` or :class:`~collections.abc.Iterable`) yields each compiled pattern (:class:`.Pattern`). """ self.patterns = patterns if isinstance(patterns, collection_type) else list(patterns) """ *patterns* (:class:`~collections.abc.Collection` of :class:`.Pattern`) contains the compiled patterns. """ def __eq__(self, other): """ Tests the equality of this path-spec with *other* (:class:`PathSpec`) by comparing their :attr:`~PathSpec.patterns` attributes. """ if isinstance(other, PathSpec): paired_patterns = izip_longest(self.patterns, other.patterns) return all(a == b for a, b in paired_patterns) else: return NotImplemented def __len__(self): """ Returns the number of compiled patterns this path-spec contains (:class:`int`). """ return len(self.patterns) @classmethod def from_lines(cls, pattern_factory, lines): """ Compiles the pattern lines. *pattern_factory* can be either the name of a registered pattern factory (:class:`str`), or a :class:`~collections.abc.Callable` used to compile patterns. It must accept an uncompiled pattern (:class:`str`) and return the compiled pattern (:class:`.Pattern`). *lines* (:class:`~collections.abc.Iterable`) yields each uncompiled pattern (:class:`str`). This simply has to yield each line so it can be a :class:`file` (e.g., from :func:`open` or :class:`io.StringIO`) or the result from :meth:`str.splitlines`. Returns the :class:`PathSpec` instance. """ if isinstance(pattern_factory, string_types): pattern_factory = util.lookup_pattern(pattern_factory) if not callable(pattern_factory): raise TypeError("pattern_factory:{0!r} is not callable.".format(pattern_factory)) if isinstance(lines, (bytes, unicode)): raise TypeError("lines:{0!r} is not an iterable.".format(lines)) lines = [pattern_factory(line) for line in lines if line] return cls(lines) def match_file(self, file, separators=None): """ Matches the file to this path-spec. *file* (:class:`str`) is the file path to be matched against :attr:`self.patterns `. *separators* (:class:`~collections.abc.Collection` of :class:`str`) optionally contains the path separators to normalize. See :func:`~pathspec.util.normalize_file` for more information. Returns :data:`True` if *file* matched; otherwise, :data:`False`. """ norm_file = util.normalize_file(file, separators=separators) return util.match_file(self.patterns, norm_file) def match_files(self, files, separators=None): """ Matches the files to this path-spec. *files* (:class:`~collections.abc.Iterable` of :class:`str`) contains the file paths to be matched against :attr:`self.patterns `. *separators* (:class:`~collections.abc.Collection` of :class:`str`; or :data:`None`) optionally contains the path separators to normalize. See :func:`~pathspec.util.normalize_file` for more information. Returns the matched files (:class:`~collections.abc.Iterable` of :class:`str`). """ if isinstance(files, (bytes, unicode)): raise TypeError("files:{0!r} is not an iterable.".format(files)) file_map = util.normalize_files(files, separators=separators) matched_files = util.match_files(self.patterns, iterkeys(file_map)) for path in matched_files: yield file_map[path] def match_tree(self, root, on_error=None, follow_links=None): """ Walks the specified root path for all files and matches them to this path-spec. *root* (:class:`str`) is the root directory to search for files. *on_error* (:class:`~collections.abc.Callable` or :data:`None`) optionally is the error handler for file-system exceptions. See :func:`~pathspec.util.iter_tree` for more information. *follow_links* (:class:`bool` or :data:`None`) optionally is whether to walk symbolik links that resolve to directories. See :func:`~pathspec.util.iter_tree` for more information. Returns the matched files (:class:`~collections.abc.Iterable` of :class:`str`). """ files = util.iter_tree(root, on_error=on_error, follow_links=follow_links) return self.match_files(files)