Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pathspec/gitignore.py: 56%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

50 statements  

1""" 

2This module provides :class:`.GitIgnoreSpec` which replicates *.gitignore* 

3behavior, and handles edge-cases where Git's behavior differs from what's 

4documented. Git allows including files from excluded directories which directly 

5contradicts the documentation. This uses :class:`.GitIgnoreSpecPattern` to fully 

6replicate Git's handling. 

7""" 

8from __future__ import annotations 

9 

10from collections.abc import ( 

11 Iterable, 

12 Sequence) 

13from typing import ( 

14 Callable, # Replaced by `collections.abc.Callable` in 3.9.2. 

15 Optional, # Replaced by `X | None` in 3.10. 

16 TypeVar, 

17 Union, # Replaced by `X | Y` in 3.10. 

18 cast, 

19 overload) 

20try: 

21 from typing import Self # Added in 3.11. 

22except ImportError: 

23 Self = TypeVar("Self", bound='GitIgnoreSpec') 

24 

25from pathspec.backend import ( 

26 BackendNamesHint, 

27 _Backend) 

28from pathspec._backends.agg import ( 

29 make_gitignore_backend) 

30from pathspec.pathspec import ( 

31 PathSpec) 

32from pathspec.pattern import ( 

33 Pattern) 

34from pathspec.patterns.gitignore.basic import ( 

35 GitIgnoreBasicPattern) 

36from pathspec.patterns.gitignore.spec import ( 

37 GitIgnoreSpecPattern) 

38from pathspec._typing import ( 

39 AnyStr, # Removed in 3.18. 

40 override) # Added in 3.12. 

41from pathspec.util import ( 

42 _is_iterable, 

43 lookup_pattern) 

44 

45 

46class GitIgnoreSpec(PathSpec): 

47 """ 

48 The :class:`GitIgnoreSpec` class extends :class:`.PathSpec` to replicate 

49 *gitignore* behavior. This is uses :class:`.GitIgnoreSpecPattern` to fully 

50 replicate Git's handling. 

51 """ 

52 

53 def __eq__(self, other: object) -> bool: 

54 """ 

55 Tests the equality of this gitignore-spec with *other* (:class:`.GitIgnoreSpec`) 

56 by comparing their :attr:`self.patterns <.PathSpec.patterns>` attributes. A 

57 non-:class:`GitIgnoreSpec` will not compare equal. 

58 """ 

59 if isinstance(other, GitIgnoreSpec): 

60 return super().__eq__(other) 

61 elif isinstance(other, PathSpec): 

62 return False 

63 else: 

64 return NotImplemented 

65 

66 # Support reversed order of arguments from PathSpec. 

67 @overload 

68 @classmethod 

69 def from_lines( 

70 cls: type[Self], 

71 pattern_factory: Union[str, Callable[[AnyStr], Pattern], None], 

72 lines: Iterable[AnyStr], 

73 *, 

74 backend: Union[BackendNamesHint, str, None] = None, 

75 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], _Backend]] = None, 

76 ) -> Self: 

77 ... 

78 

79 @overload 

80 @classmethod 

81 def from_lines( 

82 cls: type[Self], 

83 lines: Iterable[AnyStr], 

84 pattern_factory: Union[str, Callable[[AnyStr], Pattern], None] = None, 

85 *, 

86 backend: Union[BackendNamesHint, str, None] = None, 

87 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], _Backend]] = None, 

88 ) -> Self: 

89 ... 

90 

91 @override 

92 @classmethod 

93 def from_lines( 

94 cls: type[Self], 

95 lines: Iterable[AnyStr], 

96 pattern_factory: Union[str, Callable[[AnyStr], Pattern], None] = None, 

97 *, 

98 backend: Union[BackendNamesHint, str, None] = None, 

99 _test_backend_factory: Optional[Callable[[Sequence[Pattern]], _Backend]] = None, 

100 ) -> Self: 

101 """ 

102 Compiles the pattern lines. 

103 

104 *lines* (:class:`~collections.abc.Iterable`) yields each uncompiled pattern 

105 (:class:`str`). This simply has to yield each line, so it can be a 

106 :class:`io.TextIOBase` (e.g., from :func:`open` or :class:`io.StringIO`) or 

107 the result from :meth:`str.splitlines`. 

108 

109 *pattern_factory* does not need to be set for :class:`GitIgnoreSpec`. If 

110 set, it should be either ``"gitignore"`` or :class:`.GitIgnoreSpecPattern`. 

111 There is no guarantee it will work with any other pattern class. Default is 

112 :data:`None` for :class:`.GitIgnoreSpecPattern`. 

113 

114 *backend* (:class:`str` or :data:`None`) is the pattern (regular expression) 

115 matching backend to use. Default is :data:`None` for "best" to use the best 

116 available backend. Priority of backends is: "re2", "hyperscan", "simple". 

117 The "simple" backend is always available. 

118 

119 Returns the :class:`GitIgnoreSpec` instance. 

120 """ 

121 if (isinstance(lines, (str, bytes)) or callable(lines)) and _is_iterable(pattern_factory): 

122 # Support reversed order of arguments from PathSpec. 

123 pattern_factory, lines = lines, pattern_factory 

124 

125 if pattern_factory is None: 

126 pattern_factory = GitIgnoreSpecPattern 

127 elif pattern_factory == 'gitignore': 

128 # Force use of GitIgnoreSpecPattern for "gitignore" to handle edge-cases. 

129 # This makes usage easier. 

130 pattern_factory = GitIgnoreSpecPattern 

131 

132 if isinstance(pattern_factory, str): 

133 pattern_factory = lookup_pattern(pattern_factory) 

134 

135 if issubclass(pattern_factory, GitIgnoreBasicPattern): 

136 raise TypeError(( 

137 f"{pattern_factory=!r} cannot be {GitIgnoreBasicPattern} because it " 

138 f"will give unexpected results." 

139 )) # TypeError 

140 

141 self = super().from_lines(pattern_factory, lines, backend=backend, _test_backend_factory=_test_backend_factory) 

142 return cast(Self, self) 

143 

144 @override 

145 @staticmethod 

146 def _make_backend( 

147 name: BackendNamesHint, 

148 patterns: Sequence[Pattern], 

149 ) -> _Backend: 

150 """ 

151 .. warning:: This method is not part of the public API. It is subject to 

152 change. 

153 

154 Create the backend for the patterns. 

155 

156 *name* (:class:`str`) is the name of the backend. 

157 

158 *patterns* (:class:`~collections.abc.Sequence` of :class:`.Pattern`) 

159 contains the compiled patterns. 

160 

161 Returns the backend (:class:`._Backend`). 

162 """ 

163 return make_gitignore_backend(name, patterns)