copier icon indicating copy to clipboard operation
copier copied to clipboard

Unordered pattern matching can lead to ignored negate conditions

Open andrew-glenn opened this issue 3 years ago • 0 comments

https://github.com/copier-org/copier/blob/fa3515eff2cc6b69048ce5c66b5baef3efd5543f/copier/main.py#L216-L221

Pathspec.match file leverages pathspec.util.match_file^1

	def match_file(self, file, separators=None):
		# type: (Union[Text, PathLike], Optional[Collection[Text]]) -> bool
		"""
		Matches the file to this path-spec.
		*file* (:class:`str` or :class:`~pathlib.PurePath`) is the file path
		to be matched against :attr:`self.patterns <PathSpec.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)

Pathspec.util.match_file^2 will iterate through each pattern until finished, and return a boolean (pattern match or not).

def match_file(patterns, file):
	# type: (Iterable[Pattern], Text) -> bool
	"""
	Matches the file to the patterns.
	*patterns* (:class:`~collections.abc.Iterable` of :class:`~pathspec.pattern.Pattern`)
	contains the patterns to use.
	*file* (:class:`str`) is the normalized file path to be matched
	against *patterns*.
	Returns :data:`True` if *file* matched; otherwise, :data:`False`.
	"""
	matched = False
	for pattern in patterns:
		if pattern.include is not None:
			if file in pattern.match((file,)):
				matched = pattern.include
	return matched

This leads to a situation where a negate-pattern placed before a wildcard is ignored.

Example:

>>> x = ["!foo.txt", "*.txt", "bar.env", "blah.other"]

The most straightforward way to handle this is to sort the patterns before passing them to the pathspec library (modifying copier.worker._path_matcher).

Previous example, sorted:

['*.txt', 'bar.env', 'blah.other', '!foo.txt']

A PR is forthcoming.

andrew-glenn avatar Aug 09 '22 16:08 andrew-glenn