Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/tomlkit/source.py: 98%

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

98 statements  

1from __future__ import annotations 

2 

3from copy import copy 

4from typing import Any 

5 

6from tomlkit.exceptions import ParseError 

7from tomlkit.exceptions import UnexpectedCharError 

8from tomlkit.toml_char import TOMLChar 

9 

10 

11class _State: 

12 def __init__( 

13 self, 

14 source: Source, 

15 save_marker: bool | None = False, 

16 restore: bool | None = False, 

17 ) -> None: 

18 self._source = source 

19 self._save_marker = save_marker 

20 self.restore = restore 

21 

22 def __enter__(self) -> _State: 

23 # Entering this context manager - save the state 

24 self._chars = copy(self._source._chars) 

25 self._idx = self._source._idx 

26 self._current = self._source._current 

27 self._marker = self._source._marker 

28 

29 return self 

30 

31 def __exit__(self, exception_type, exception_val, trace): 

32 # Exiting this context manager - restore the prior state 

33 if self.restore or exception_type: 

34 self._source._chars = self._chars 

35 self._source._idx = self._idx 

36 self._source._current = self._current 

37 if self._save_marker: 

38 self._source._marker = self._marker 

39 

40 

41class _StateHandler: 

42 """ 

43 State preserver for the Parser. 

44 """ 

45 

46 def __init__(self, source: Source) -> None: 

47 self._source = source 

48 self._states = [] 

49 

50 def __call__(self, *args, **kwargs): 

51 return _State(self._source, *args, **kwargs) 

52 

53 def __enter__(self) -> _State: 

54 state = self() 

55 self._states.append(state) 

56 return state.__enter__() 

57 

58 def __exit__(self, exception_type, exception_val, trace): 

59 state = self._states.pop() 

60 return state.__exit__(exception_type, exception_val, trace) 

61 

62 

63class Source(str): 

64 EOF = TOMLChar("\0") 

65 

66 def __init__(self, _: str) -> None: 

67 super().__init__() 

68 

69 # Collection of TOMLChars 

70 self._chars = iter([(i, TOMLChar(c)) for i, c in enumerate(self)]) 

71 

72 self._idx = 0 

73 self._marker = 0 

74 self._current = TOMLChar("") 

75 

76 self._state = _StateHandler(self) 

77 

78 self.inc() 

79 

80 def reset(self): 

81 # initialize both idx and current 

82 self.inc() 

83 

84 # reset marker 

85 self.mark() 

86 

87 @property 

88 def state(self) -> _StateHandler: 

89 return self._state 

90 

91 @property 

92 def idx(self) -> int: 

93 return self._idx 

94 

95 @property 

96 def current(self) -> TOMLChar: 

97 return self._current 

98 

99 @property 

100 def marker(self) -> int: 

101 return self._marker 

102 

103 def extract(self) -> str: 

104 """ 

105 Extracts the value between marker and index 

106 """ 

107 return self[self._marker : self._idx] 

108 

109 def inc(self, exception: type[ParseError] | None = None) -> bool: 

110 """ 

111 Increments the parser if the end of the input has not been reached. 

112 Returns whether or not it was able to advance. 

113 """ 

114 try: 

115 self._idx, self._current = next(self._chars) 

116 

117 return True 

118 except StopIteration: 

119 self._idx = len(self) 

120 self._current = self.EOF 

121 if exception: 

122 raise self.parse_error(exception) from None 

123 

124 return False 

125 

126 def inc_n(self, n: int, exception: type[ParseError] | None = None) -> bool: 

127 """ 

128 Increments the parser by n characters 

129 if the end of the input has not been reached. 

130 """ 

131 return all(self.inc(exception=exception) for _ in range(n)) 

132 

133 def consume(self, chars, min=0, max=-1): 

134 """ 

135 Consume chars until min/max is satisfied is valid. 

136 """ 

137 while self.current in chars and max != 0: 

138 min -= 1 

139 max -= 1 

140 if not self.inc(): 

141 break 

142 

143 # failed to consume minimum number of characters 

144 if min > 0: 

145 raise self.parse_error(UnexpectedCharError, self.current) 

146 

147 def end(self) -> bool: 

148 """ 

149 Returns True if the parser has reached the end of the input. 

150 """ 

151 return self._current is self.EOF 

152 

153 def mark(self) -> None: 

154 """ 

155 Sets the marker to the index's current position 

156 """ 

157 self._marker = self._idx 

158 

159 def parse_error( 

160 self, 

161 exception: type[ParseError] = ParseError, 

162 *args: Any, 

163 **kwargs: Any, 

164 ) -> ParseError: 

165 """ 

166 Creates a generic "parse error" at the current position. 

167 """ 

168 line, col = self._to_linecol() 

169 

170 return exception(line, col, *args, **kwargs) 

171 

172 def _to_linecol(self) -> tuple[int, int]: 

173 cur = 0 

174 for i, line in enumerate(self.splitlines()): 

175 if cur + len(line) + 1 > self.idx: 

176 return (i + 1, self.idx - cur) 

177 

178 cur += len(line) + 1 

179 

180 return len(self.splitlines()), 0