Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pathspec/_backends/hyperscan/_base.py: 73%

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

49 statements  

1""" 

2This module provides private data for the base implementation for the 

3:module:`hyperscan` library. 

4 

5WARNING: The *pathspec._backends.hyperscan* package is not part of the public 

6API. Its contents and structure are likely to change. 

7""" 

8from __future__ import annotations 

9 

10import re 

11from dataclasses import ( 

12 dataclass) 

13from typing import ( 

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

15 cast) 

16 

17try: 

18 import hyperscan 

19except ModuleNotFoundError: 

20 hyperscan = None # type: ignore[assignment] 

21 HS_FLAGS = 0 

22 HS_VERSION = (-1, -1, -1) 

23else: 

24 HS_FLAGS = hyperscan.HS_FLAG_SINGLEMATCH | hyperscan.HS_FLAG_UTF8 

25 HS_VERSION = cast(tuple[int, int, int], tuple(map(int, hyperscan.__version__.split('.')[:3]))) # type: ignore[attr-defined] 

26 

27_COMPAT_CONVERT_UNICODE = re.compile(r'\\(.)|([^\x00-\x7F])', re.DOTALL) 

28""" 

29Regex to match non-ASCII characters, and escaped characters. 

30""" 

31 

32HS_FLAGS: int # type: ignore[no-redef] 

33""" 

34The hyperscan flags to use: 

35 

36- HS_FLAG_SINGLEMATCH is needed to ensure the partial patterns only match once. 

37 

38- HS_FLAG_UTF8 is required to support unicode paths. 

39""" 

40 

41HS_VERSION: tuple[int, int, int] # type: ignore[no-redef] 

42""" 

43The hyperscan version tuple. 

44""" 

45 

46HS_VERSION_0_9_1 = (0, 9, 1) 

47""" 

48The hyperscan version tuple for version 0.9.1. 

49""" 

50 

51 

52def compat_convert_utf8(regex: str) -> bytes: 

53 """ 

54 Encode unicode characters to an encoded representation. This is needed because 

55 hyperscan versions before 0.9.1 on Linux fail on multibyte UTF-8 characters. 

56 

57 See <https://github.com/darvid/python-hyperscan/issues/274>. 

58 

59 *regex* (:class:`str`) is the unicode regex. 

60 

61 Returns the ASCII regex (:class:`bytes`). 

62 """ 

63 return _COMPAT_CONVERT_UNICODE.sub(_replace_unicode, regex).encode('ascii') 

64 

65 

66def _encode_uchar(uchar: str) -> str: 

67 """ 

68 Encode the unicode character. 

69 

70 *uchar* (:class:`str`) is the unicode character. 

71 

72 Returns the encoded string (:class:`str`). 

73 """ 

74 cp = ord(uchar) 

75 if cp <= 0xFF: 

76 return f'\\x{{{cp:02X}}}' 

77 else: 

78 return f'\\x{{{cp:04X}}}' 

79 

80 

81def _replace_unicode(match: re.Match) -> str: 

82 """ 

83 Replaces the matched unicode character with an encoded representation, handling 

84 potential invalid byte sequences gracefully by substituting them with the 

85 replacement character. 

86 

87 *match* (:class:`re.Match`) is the match object. 

88 

89 Returns the string replacement (:class:`str`). 

90 """ 

91 escaped, uchar = match.groups() 

92 if uchar is not None: 

93 # Unicode character. 

94 return _encode_uchar(uchar) 

95 elif escaped.isascii(): 

96 # Escaped ASCII character. 

97 return match[0] 

98 else: 

99 # Escaped unicode character. 

100 return _encode_uchar(escaped) 

101 

102 

103@dataclass(frozen=True) 

104class HyperscanExprDat(object): 

105 """ 

106 The :class:`HyperscanExprDat` class is used to store data related to an 

107 expression. 

108 """ 

109 

110 # The slots argument is not supported until Python 3.10. 

111 __slots__ = [ 

112 'include', 

113 'index', 

114 'is_dir_pattern', 

115 ] 

116 

117 include: bool 

118 """ 

119 *include* (:class:`bool`) is whether is whether the matched files should be 

120 included (:data:`True`), or excluded (:data:`False`). 

121 """ 

122 

123 index: int 

124 """ 

125 *index* (:class:`int`) is the pattern index. 

126 """ 

127 

128 is_dir_pattern: bool 

129 """ 

130 *is_dir_pattern* (:class:`bool`) is whether the pattern is a directory 

131 pattern for gitignore. 

132 """ 

133 

134 

135@dataclass(frozen=True) 

136class HyperscanExprDebug(HyperscanExprDat): 

137 """ 

138 The :class:`HyperscanExprDebug` class stores additional debug information 

139 related to an expression. 

140 """ 

141 

142 # The slots argument is not supported until Python 3.10. 

143 __slots__ = ['regex'] 

144 

145 regex: Union[str, bytes] 

146 """ 

147 *regex* (:class:`str` or :class:`bytes`) is the regular expression. 

148 """