Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/chardet/sbcharsetprober.py: 100%

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

87 statements  

1######################## BEGIN LICENSE BLOCK ######################## 

2# The Original Code is Mozilla Universal charset detector code. 

3# 

4# The Initial Developer of the Original Code is 

5# Netscape Communications Corporation. 

6# Portions created by the Initial Developer are Copyright (C) 2001 

7# the Initial Developer. All Rights Reserved. 

8# 

9# Contributor(s): 

10# Mark Pilgrim - port to Python 

11# Shy Shalom - original C code 

12# 

13# This library is free software; you can redistribute it and/or 

14# modify it under the terms of the GNU Lesser General Public 

15# License as published by the Free Software Foundation; either 

16# version 2.1 of the License, or (at your option) any later version. 

17# 

18# This library is distributed in the hope that it will be useful, 

19# but WITHOUT ANY WARRANTY; without even the implied warranty of 

20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

21# Lesser General Public License for more details. 

22# 

23# You should have received a copy of the GNU Lesser General Public 

24# License along with this library; if not, write to the Free Software 

25# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 

26# 02110-1301 USA 

27######################### END LICENSE BLOCK ######################### 

28 

29from typing import Dict, List, NamedTuple, Optional, Union 

30 

31from .charsetprober import CharSetProber 

32from .enums import CharacterCategory, ProbingState, SequenceLikelihood 

33 

34 

35class SingleByteCharSetModel(NamedTuple): 

36 charset_name: str 

37 language: str 

38 char_to_order_map: Dict[int, int] 

39 language_model: Dict[int, Dict[int, int]] 

40 typical_positive_ratio: float 

41 keep_ascii_letters: bool 

42 alphabet: str 

43 

44 

45class SingleByteCharSetProber(CharSetProber): 

46 SAMPLE_SIZE = 64 

47 SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 

48 POSITIVE_SHORTCUT_THRESHOLD = 0.95 

49 NEGATIVE_SHORTCUT_THRESHOLD = 0.05 

50 

51 def __init__( 

52 self, 

53 model: SingleByteCharSetModel, 

54 is_reversed: bool = False, 

55 name_prober: Optional[CharSetProber] = None, 

56 ) -> None: 

57 super().__init__() 

58 self._model = model 

59 # TRUE if we need to reverse every pair in the model lookup 

60 self._reversed = is_reversed 

61 # Optional auxiliary prober for name decision 

62 self._name_prober = name_prober 

63 self._last_order = 255 

64 self._seq_counters: List[int] = [] 

65 self._total_seqs = 0 

66 self._total_char = 0 

67 self._control_char = 0 

68 self._freq_char = 0 

69 self.reset() 

70 

71 def reset(self) -> None: 

72 super().reset() 

73 # char order of last character 

74 self._last_order = 255 

75 self._seq_counters = [0] * SequenceLikelihood.get_num_categories() 

76 self._total_seqs = 0 

77 self._total_char = 0 

78 self._control_char = 0 

79 # characters that fall in our sampling range 

80 self._freq_char = 0 

81 

82 @property 

83 def charset_name(self) -> Optional[str]: 

84 if self._name_prober: 

85 return self._name_prober.charset_name 

86 return self._model.charset_name 

87 

88 @property 

89 def language(self) -> Optional[str]: 

90 if self._name_prober: 

91 return self._name_prober.language 

92 return self._model.language 

93 

94 def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: 

95 # TODO: Make filter_international_words keep things in self.alphabet 

96 if not self._model.keep_ascii_letters: 

97 byte_str = self.filter_international_words(byte_str) 

98 else: 

99 byte_str = self.remove_xml_tags(byte_str) 

100 if not byte_str: 

101 return self.state 

102 char_to_order_map = self._model.char_to_order_map 

103 language_model = self._model.language_model 

104 for char in byte_str: 

105 order = char_to_order_map.get(char, CharacterCategory.UNDEFINED) 

106 # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but 

107 # CharacterCategory.SYMBOL is actually 253, so we use CONTROL 

108 # to make it closer to the original intent. The only difference 

109 # is whether or not we count digits and control characters for 

110 # _total_char purposes. 

111 if order < CharacterCategory.CONTROL: 

112 self._total_char += 1 

113 if order < self.SAMPLE_SIZE: 

114 self._freq_char += 1 

115 if self._last_order < self.SAMPLE_SIZE: 

116 self._total_seqs += 1 

117 if not self._reversed: 

118 lm_cat = language_model[self._last_order][order] 

119 else: 

120 lm_cat = language_model[order][self._last_order] 

121 self._seq_counters[lm_cat] += 1 

122 self._last_order = order 

123 

124 charset_name = self._model.charset_name 

125 if self.state == ProbingState.DETECTING: 

126 if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: 

127 confidence = self.get_confidence() 

128 if confidence > self.POSITIVE_SHORTCUT_THRESHOLD: 

129 self.logger.debug( 

130 "%s confidence = %s, we have a winner", charset_name, confidence 

131 ) 

132 self._state = ProbingState.FOUND_IT 

133 elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD: 

134 self.logger.debug( 

135 "%s confidence = %s, below negative shortcut threshold %s", 

136 charset_name, 

137 confidence, 

138 self.NEGATIVE_SHORTCUT_THRESHOLD, 

139 ) 

140 self._state = ProbingState.NOT_ME 

141 

142 return self.state 

143 

144 def get_confidence(self) -> float: 

145 r = 0.01 

146 if self._total_seqs > 0: 

147 r = ( 

148 ( 

149 self._seq_counters[SequenceLikelihood.POSITIVE] 

150 + 0.25 * self._seq_counters[SequenceLikelihood.LIKELY] 

151 ) 

152 / self._total_seqs 

153 / self._model.typical_positive_ratio 

154 ) 

155 # The more control characters (proportionnaly to the size 

156 # of the text), the less confident we become in the current 

157 # charset. 

158 r = r * (self._total_char - self._control_char) / self._total_char 

159 r = r * self._freq_char / self._total_char 

160 if r >= 1.0: 

161 r = 0.99 

162 return r