Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/chardet/universaldetector.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
| author | shellac |
|---|---|
| date | Sat, 02 May 2020 07:14:21 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:26e78fe6e8c4 |
|---|---|
| 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 """ | |
| 29 Module containing the UniversalDetector detector class, which is the primary | |
| 30 class a user of ``chardet`` should use. | |
| 31 | |
| 32 :author: Mark Pilgrim (initial port to Python) | |
| 33 :author: Shy Shalom (original C code) | |
| 34 :author: Dan Blanchard (major refactoring for 3.0) | |
| 35 :author: Ian Cordasco | |
| 36 """ | |
| 37 | |
| 38 | |
| 39 import codecs | |
| 40 import logging | |
| 41 import re | |
| 42 | |
| 43 from .charsetgroupprober import CharSetGroupProber | |
| 44 from .enums import InputState, LanguageFilter, ProbingState | |
| 45 from .escprober import EscCharSetProber | |
| 46 from .latin1prober import Latin1Prober | |
| 47 from .mbcsgroupprober import MBCSGroupProber | |
| 48 from .sbcsgroupprober import SBCSGroupProber | |
| 49 | |
| 50 | |
| 51 class UniversalDetector(object): | |
| 52 """ | |
| 53 The ``UniversalDetector`` class underlies the ``chardet.detect`` function | |
| 54 and coordinates all of the different charset probers. | |
| 55 | |
| 56 To get a ``dict`` containing an encoding and its confidence, you can simply | |
| 57 run: | |
| 58 | |
| 59 .. code:: | |
| 60 | |
| 61 u = UniversalDetector() | |
| 62 u.feed(some_bytes) | |
| 63 u.close() | |
| 64 detected = u.result | |
| 65 | |
| 66 """ | |
| 67 | |
| 68 MINIMUM_THRESHOLD = 0.20 | |
| 69 HIGH_BYTE_DETECTOR = re.compile(b'[\x80-\xFF]') | |
| 70 ESC_DETECTOR = re.compile(b'(\033|~{)') | |
| 71 WIN_BYTE_DETECTOR = re.compile(b'[\x80-\x9F]') | |
| 72 ISO_WIN_MAP = {'iso-8859-1': 'Windows-1252', | |
| 73 'iso-8859-2': 'Windows-1250', | |
| 74 'iso-8859-5': 'Windows-1251', | |
| 75 'iso-8859-6': 'Windows-1256', | |
| 76 'iso-8859-7': 'Windows-1253', | |
| 77 'iso-8859-8': 'Windows-1255', | |
| 78 'iso-8859-9': 'Windows-1254', | |
| 79 'iso-8859-13': 'Windows-1257'} | |
| 80 | |
| 81 def __init__(self, lang_filter=LanguageFilter.ALL): | |
| 82 self._esc_charset_prober = None | |
| 83 self._charset_probers = [] | |
| 84 self.result = None | |
| 85 self.done = None | |
| 86 self._got_data = None | |
| 87 self._input_state = None | |
| 88 self._last_char = None | |
| 89 self.lang_filter = lang_filter | |
| 90 self.logger = logging.getLogger(__name__) | |
| 91 self._has_win_bytes = None | |
| 92 self.reset() | |
| 93 | |
| 94 def reset(self): | |
| 95 """ | |
| 96 Reset the UniversalDetector and all of its probers back to their | |
| 97 initial states. This is called by ``__init__``, so you only need to | |
| 98 call this directly in between analyses of different documents. | |
| 99 """ | |
| 100 self.result = {'encoding': None, 'confidence': 0.0, 'language': None} | |
| 101 self.done = False | |
| 102 self._got_data = False | |
| 103 self._has_win_bytes = False | |
| 104 self._input_state = InputState.PURE_ASCII | |
| 105 self._last_char = b'' | |
| 106 if self._esc_charset_prober: | |
| 107 self._esc_charset_prober.reset() | |
| 108 for prober in self._charset_probers: | |
| 109 prober.reset() | |
| 110 | |
| 111 def feed(self, byte_str): | |
| 112 """ | |
| 113 Takes a chunk of a document and feeds it through all of the relevant | |
| 114 charset probers. | |
| 115 | |
| 116 After calling ``feed``, you can check the value of the ``done`` | |
| 117 attribute to see if you need to continue feeding the | |
| 118 ``UniversalDetector`` more data, or if it has made a prediction | |
| 119 (in the ``result`` attribute). | |
| 120 | |
| 121 .. note:: | |
| 122 You should always call ``close`` when you're done feeding in your | |
| 123 document if ``done`` is not already ``True``. | |
| 124 """ | |
| 125 if self.done: | |
| 126 return | |
| 127 | |
| 128 if not len(byte_str): | |
| 129 return | |
| 130 | |
| 131 if not isinstance(byte_str, bytearray): | |
| 132 byte_str = bytearray(byte_str) | |
| 133 | |
| 134 # First check for known BOMs, since these are guaranteed to be correct | |
| 135 if not self._got_data: | |
| 136 # If the data starts with BOM, we know it is UTF | |
| 137 if byte_str.startswith(codecs.BOM_UTF8): | |
| 138 # EF BB BF UTF-8 with BOM | |
| 139 self.result = {'encoding': "UTF-8-SIG", | |
| 140 'confidence': 1.0, | |
| 141 'language': ''} | |
| 142 elif byte_str.startswith((codecs.BOM_UTF32_LE, | |
| 143 codecs.BOM_UTF32_BE)): | |
| 144 # FF FE 00 00 UTF-32, little-endian BOM | |
| 145 # 00 00 FE FF UTF-32, big-endian BOM | |
| 146 self.result = {'encoding': "UTF-32", | |
| 147 'confidence': 1.0, | |
| 148 'language': ''} | |
| 149 elif byte_str.startswith(b'\xFE\xFF\x00\x00'): | |
| 150 # FE FF 00 00 UCS-4, unusual octet order BOM (3412) | |
| 151 self.result = {'encoding': "X-ISO-10646-UCS-4-3412", | |
| 152 'confidence': 1.0, | |
| 153 'language': ''} | |
| 154 elif byte_str.startswith(b'\x00\x00\xFF\xFE'): | |
| 155 # 00 00 FF FE UCS-4, unusual octet order BOM (2143) | |
| 156 self.result = {'encoding': "X-ISO-10646-UCS-4-2143", | |
| 157 'confidence': 1.0, | |
| 158 'language': ''} | |
| 159 elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)): | |
| 160 # FF FE UTF-16, little endian BOM | |
| 161 # FE FF UTF-16, big endian BOM | |
| 162 self.result = {'encoding': "UTF-16", | |
| 163 'confidence': 1.0, | |
| 164 'language': ''} | |
| 165 | |
| 166 self._got_data = True | |
| 167 if self.result['encoding'] is not None: | |
| 168 self.done = True | |
| 169 return | |
| 170 | |
| 171 # If none of those matched and we've only see ASCII so far, check | |
| 172 # for high bytes and escape sequences | |
| 173 if self._input_state == InputState.PURE_ASCII: | |
| 174 if self.HIGH_BYTE_DETECTOR.search(byte_str): | |
| 175 self._input_state = InputState.HIGH_BYTE | |
| 176 elif self._input_state == InputState.PURE_ASCII and \ | |
| 177 self.ESC_DETECTOR.search(self._last_char + byte_str): | |
| 178 self._input_state = InputState.ESC_ASCII | |
| 179 | |
| 180 self._last_char = byte_str[-1:] | |
| 181 | |
| 182 # If we've seen escape sequences, use the EscCharSetProber, which | |
| 183 # uses a simple state machine to check for known escape sequences in | |
| 184 # HZ and ISO-2022 encodings, since those are the only encodings that | |
| 185 # use such sequences. | |
| 186 if self._input_state == InputState.ESC_ASCII: | |
| 187 if not self._esc_charset_prober: | |
| 188 self._esc_charset_prober = EscCharSetProber(self.lang_filter) | |
| 189 if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT: | |
| 190 self.result = {'encoding': | |
| 191 self._esc_charset_prober.charset_name, | |
| 192 'confidence': | |
| 193 self._esc_charset_prober.get_confidence(), | |
| 194 'language': | |
| 195 self._esc_charset_prober.language} | |
| 196 self.done = True | |
| 197 # If we've seen high bytes (i.e., those with values greater than 127), | |
| 198 # we need to do more complicated checks using all our multi-byte and | |
| 199 # single-byte probers that are left. The single-byte probers | |
| 200 # use character bigram distributions to determine the encoding, whereas | |
| 201 # the multi-byte probers use a combination of character unigram and | |
| 202 # bigram distributions. | |
| 203 elif self._input_state == InputState.HIGH_BYTE: | |
| 204 if not self._charset_probers: | |
| 205 self._charset_probers = [MBCSGroupProber(self.lang_filter)] | |
| 206 # If we're checking non-CJK encodings, use single-byte prober | |
| 207 if self.lang_filter & LanguageFilter.NON_CJK: | |
| 208 self._charset_probers.append(SBCSGroupProber()) | |
| 209 self._charset_probers.append(Latin1Prober()) | |
| 210 for prober in self._charset_probers: | |
| 211 if prober.feed(byte_str) == ProbingState.FOUND_IT: | |
| 212 self.result = {'encoding': prober.charset_name, | |
| 213 'confidence': prober.get_confidence(), | |
| 214 'language': prober.language} | |
| 215 self.done = True | |
| 216 break | |
| 217 if self.WIN_BYTE_DETECTOR.search(byte_str): | |
| 218 self._has_win_bytes = True | |
| 219 | |
| 220 def close(self): | |
| 221 """ | |
| 222 Stop analyzing the current document and come up with a final | |
| 223 prediction. | |
| 224 | |
| 225 :returns: The ``result`` attribute, a ``dict`` with the keys | |
| 226 `encoding`, `confidence`, and `language`. | |
| 227 """ | |
| 228 # Don't bother with checks if we're already done | |
| 229 if self.done: | |
| 230 return self.result | |
| 231 self.done = True | |
| 232 | |
| 233 if not self._got_data: | |
| 234 self.logger.debug('no data received!') | |
| 235 | |
| 236 # Default to ASCII if it is all we've seen so far | |
| 237 elif self._input_state == InputState.PURE_ASCII: | |
| 238 self.result = {'encoding': 'ascii', | |
| 239 'confidence': 1.0, | |
| 240 'language': ''} | |
| 241 | |
| 242 # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD | |
| 243 elif self._input_state == InputState.HIGH_BYTE: | |
| 244 prober_confidence = None | |
| 245 max_prober_confidence = 0.0 | |
| 246 max_prober = None | |
| 247 for prober in self._charset_probers: | |
| 248 if not prober: | |
| 249 continue | |
| 250 prober_confidence = prober.get_confidence() | |
| 251 if prober_confidence > max_prober_confidence: | |
| 252 max_prober_confidence = prober_confidence | |
| 253 max_prober = prober | |
| 254 if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD): | |
| 255 charset_name = max_prober.charset_name | |
| 256 lower_charset_name = max_prober.charset_name.lower() | |
| 257 confidence = max_prober.get_confidence() | |
| 258 # Use Windows encoding name instead of ISO-8859 if we saw any | |
| 259 # extra Windows-specific bytes | |
| 260 if lower_charset_name.startswith('iso-8859'): | |
| 261 if self._has_win_bytes: | |
| 262 charset_name = self.ISO_WIN_MAP.get(lower_charset_name, | |
| 263 charset_name) | |
| 264 self.result = {'encoding': charset_name, | |
| 265 'confidence': confidence, | |
| 266 'language': max_prober.language} | |
| 267 | |
| 268 # Log all prober confidences if none met MINIMUM_THRESHOLD | |
| 269 if self.logger.getEffectiveLevel() == logging.DEBUG: | |
| 270 if self.result['encoding'] is None: | |
| 271 self.logger.debug('no probers hit minimum threshold') | |
| 272 for group_prober in self._charset_probers: | |
| 273 if not group_prober: | |
| 274 continue | |
| 275 if isinstance(group_prober, CharSetGroupProber): | |
| 276 for prober in group_prober.probers: | |
| 277 self.logger.debug('%s %s confidence = %s', | |
| 278 prober.charset_name, | |
| 279 prober.language, | |
| 280 prober.get_confidence()) | |
| 281 else: | |
| 282 self.logger.debug('%s %s confidence = %s', | |
| 283 prober.charset_name, | |
| 284 prober.language, | |
| 285 prober.get_confidence()) | |
| 286 return self.result |
