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

100 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 07:01 +0000

1from copy import copy 

2from typing import Any 

3from typing import Optional 

4from typing import Tuple 

5from typing import Type 

6 

7from tomlkit.exceptions import ParseError 

8from tomlkit.exceptions import UnexpectedCharError 

9from tomlkit.toml_char import TOMLChar 

10 

11 

12class _State: 

13 def __init__( 

14 self, 

15 source: "Source", 

16 save_marker: Optional[bool] = False, 

17 restore: Optional[bool] = False, 

18 ) -> None: 

19 self._source = source 

20 self._save_marker = save_marker 

21 self.restore = restore 

22 

23 def __enter__(self) -> "_State": 

24 # Entering this context manager - save the state 

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

26 self._idx = self._source._idx 

27 self._current = self._source._current 

28 self._marker = self._source._marker 

29 

30 return self 

31 

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

33 # Exiting this context manager - restore the prior state 

34 if self.restore or exception_type: 

35 self._source._chars = self._chars 

36 self._source._idx = self._idx 

37 self._source._current = self._current 

38 if self._save_marker: 

39 self._source._marker = self._marker 

40 

41 

42class _StateHandler: 

43 """ 

44 State preserver for the Parser. 

45 """ 

46 

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

48 self._source = source 

49 self._states = [] 

50 

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

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

53 

54 def __enter__(self) -> None: 

55 state = self() 

56 self._states.append(state) 

57 return state.__enter__() 

58 

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

60 state = self._states.pop() 

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

62 

63 

64class Source(str): 

65 EOF = TOMLChar("\0") 

66 

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

68 super().__init__() 

69 

70 # Collection of TOMLChars 

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

72 

73 self._idx = 0 

74 self._marker = 0 

75 self._current = TOMLChar("") 

76 

77 self._state = _StateHandler(self) 

78 

79 self.inc() 

80 

81 def reset(self): 

82 # initialize both idx and current 

83 self.inc() 

84 

85 # reset marker 

86 self.mark() 

87 

88 @property 

89 def state(self) -> _StateHandler: 

90 return self._state 

91 

92 @property 

93 def idx(self) -> int: 

94 return self._idx 

95 

96 @property 

97 def current(self) -> TOMLChar: 

98 return self._current 

99 

100 @property 

101 def marker(self) -> int: 

102 return self._marker 

103 

104 def extract(self) -> str: 

105 """ 

106 Extracts the value between marker and index 

107 """ 

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

109 

110 def inc(self, exception: Optional[Type[ParseError]] = None) -> bool: 

111 """ 

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

113 Returns whether or not it was able to advance. 

114 """ 

115 try: 

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

117 

118 return True 

119 except StopIteration: 

120 self._idx = len(self) 

121 self._current = self.EOF 

122 if exception: 

123 raise self.parse_error(exception) 

124 

125 return False 

126 

127 def inc_n(self, n: int, exception: Optional[Type[ParseError]] = None) -> bool: 

128 """ 

129 Increments the parser by n characters 

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

131 """ 

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

133 

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

135 """ 

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

137 """ 

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

139 min -= 1 

140 max -= 1 

141 if not self.inc(): 

142 break 

143 

144 # failed to consume minimum number of characters 

145 if min > 0: 

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

147 

148 def end(self) -> bool: 

149 """ 

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

151 """ 

152 return self._current is self.EOF 

153 

154 def mark(self) -> None: 

155 """ 

156 Sets the marker to the index's current position 

157 """ 

158 self._marker = self._idx 

159 

160 def parse_error( 

161 self, 

162 exception: Type[ParseError] = ParseError, 

163 *args: Any, 

164 **kwargs: Any, 

165 ) -> ParseError: 

166 """ 

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

168 """ 

169 line, col = self._to_linecol() 

170 

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

172 

173 def _to_linecol(self) -> Tuple[int, int]: 

174 cur = 0 

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

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

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

178 

179 cur += len(line) + 1 

180 

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