detextive 2.0__tar.gz → 3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- detextive-3.1/.gitignore +7 -0
- {detextive-2.0 → detextive-3.1}/PKG-INFO +6 -2
- {detextive-2.0 → detextive-3.1}/README.rst +3 -0
- {detextive-2.0 → detextive-3.1}/pyproject.toml +14 -6
- {detextive-2.0 → detextive-3.1}/sources/detextive/__/imports.py +7 -3
- {detextive-2.0 → detextive-3.1}/sources/detextive/__/nomina.py +0 -5
- {detextive-2.0 → detextive-3.1}/sources/detextive/__init__.py +1 -1
- detextive-3.1/sources/detextive/charsets.py +201 -0
- {detextive-2.0 → detextive-3.1}/sources/detextive/core.py +34 -23
- detextive-3.1/sources/detextive/decoders.py +254 -0
- {detextive-2.0 → detextive-3.1}/sources/detextive/detectors.py +41 -20
- {detextive-2.0 → detextive-3.1}/sources/detextive/exceptions.py +10 -7
- {detextive-2.0 → detextive-3.1}/sources/detextive/inference.py +49 -43
- {detextive-2.0 → detextive-3.1}/sources/detextive/nomina.py +7 -4
- detextive-2.0/.gitignore +0 -12
- detextive-2.0/sources/detextive/charsets.py +0 -118
- detextive-2.0/sources/detextive/decoders.py +0 -98
- {detextive-2.0 → detextive-3.1}/LICENSE.txt +0 -0
- {detextive-2.0 → detextive-3.1}/sources/detextive/__/__init__.py +0 -0
- {detextive-2.0 → detextive-3.1}/sources/detextive/_typedecls/puremagic/__init__.pyi +0 -0
- {detextive-2.0 → detextive-3.1}/sources/detextive/lineseparators.py +0 -0
- {detextive-2.0 → detextive-3.1}/sources/detextive/mimetypes.py +0 -0
- {detextive-2.0 → detextive-3.1}/sources/detextive/py.typed +0 -0
- {detextive-2.0 → detextive-3.1}/sources/detextive/validation.py +0 -0
detextive-3.1/.gitignore
ADDED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: detextive
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.1
|
|
4
4
|
Summary: Detects textual content.
|
|
5
5
|
Project-URL: Homepage, https://github.com/emcd/python-detextive
|
|
6
6
|
Project-URL: Documentation, https://emcd.github.io/python-detextive
|
|
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
23
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
24
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
24
25
|
Classifier: Topic :: Software Development
|
|
@@ -27,7 +28,7 @@ Requires-Dist: absence~=1.1
|
|
|
27
28
|
Requires-Dist: accretive~=4.1
|
|
28
29
|
Requires-Dist: chardet
|
|
29
30
|
Requires-Dist: dynadoc~=1.4
|
|
30
|
-
Requires-Dist: frigid~=4.
|
|
31
|
+
Requires-Dist: frigid~=4.2
|
|
31
32
|
Requires-Dist: puremagic
|
|
32
33
|
Requires-Dist: typing-extensions
|
|
33
34
|
Provides-Extra: all
|
|
@@ -306,6 +307,9 @@ Other Projects by This Author 🌟
|
|
|
306
307
|
* `python-icecream-truck <https://github.com/emcd/python-icecream-truck>`_ (`icecream-truck <https://pypi.org/project/icecream-truck/>`_ on PyPI)
|
|
307
308
|
|
|
308
309
|
🍦 **Flavorful Debugging** - A Python library which enhances the powerful and well-known ``icecream`` package with flavored traces, configuration hierarchies, customized outputs, ready-made recipes, and more.
|
|
310
|
+
* `python-librovore <https://github.com/emcd/python-librovore>`_ (`librovore <https://pypi.org/project/librovore/>`_ on PyPI)
|
|
311
|
+
|
|
312
|
+
🐲 **Documentation Search Engine** - An intelligent documentation search and extraction tool that provides both a command-line interface for humans and an MCP (Model Context Protocol) server for AI agents. Search across Sphinx and MkDocs sites with fuzzy matching, extract clean markdown content, and integrate seamlessly with AI development workflows.
|
|
309
313
|
* `python-mimeogram <https://github.com/emcd/python-mimeogram>`_ (`mimeogram <https://pypi.org/project/mimeogram/>`_ on PyPI)
|
|
310
314
|
|
|
311
315
|
📨 A command-line tool for **exchanging collections of files with Large Language Models** - bundle multiple files into a single clipboard-ready document while preserving directory structure and metadata... good for code reviews, project sharing, and LLM interactions.
|
|
@@ -263,6 +263,9 @@ Other Projects by This Author 🌟
|
|
|
263
263
|
* `python-icecream-truck <https://github.com/emcd/python-icecream-truck>`_ (`icecream-truck <https://pypi.org/project/icecream-truck/>`_ on PyPI)
|
|
264
264
|
|
|
265
265
|
🍦 **Flavorful Debugging** - A Python library which enhances the powerful and well-known ``icecream`` package with flavored traces, configuration hierarchies, customized outputs, ready-made recipes, and more.
|
|
266
|
+
* `python-librovore <https://github.com/emcd/python-librovore>`_ (`librovore <https://pypi.org/project/librovore/>`_ on PyPI)
|
|
267
|
+
|
|
268
|
+
🐲 **Documentation Search Engine** - An intelligent documentation search and extraction tool that provides both a command-line interface for humans and an MCP (Model Context Protocol) server for AI agents. Search across Sphinx and MkDocs sites with fuzzy matching, extract clean markdown content, and integrate seamlessly with AI development workflows.
|
|
266
269
|
* `python-mimeogram <https://github.com/emcd/python-mimeogram>`_ (`mimeogram <https://pypi.org/project/mimeogram/>`_ on PyPI)
|
|
267
270
|
|
|
268
271
|
📨 A command-line tool for **exchanging collections of files with Large Language Models** - bundle multiple files into a single clipboard-ready document while preserving directory structure and metadata... good for code reviews, project sharing, and LLM interactions.
|
|
@@ -15,14 +15,14 @@ license = 'Apache-2.0'
|
|
|
15
15
|
readme = { 'file' = 'README.rst', 'content-type' = 'text/x-rst' }
|
|
16
16
|
requires-python = '>= 3.10'
|
|
17
17
|
dependencies = [
|
|
18
|
-
'absence~=1.1',
|
|
19
18
|
'accretive~=4.1',
|
|
20
19
|
'chardet',
|
|
21
|
-
'dynadoc~=1.4',
|
|
22
|
-
'frigid~=4.1',
|
|
23
20
|
'puremagic',
|
|
24
21
|
'typing-extensions',
|
|
25
22
|
# --- BEGIN: Injected by Copier ---
|
|
23
|
+
'absence~=1.1',
|
|
24
|
+
'dynadoc~=1.4',
|
|
25
|
+
'frigid~=4.2',
|
|
26
26
|
# --- END: Injected by Copier ---
|
|
27
27
|
]
|
|
28
28
|
classifiers = [ # https://pypi.org/classifiers
|
|
@@ -35,6 +35,7 @@ classifiers = [ # https://pypi.org/classifiers
|
|
|
35
35
|
'Programming Language :: Python :: 3.11',
|
|
36
36
|
'Programming Language :: Python :: 3.12',
|
|
37
37
|
'Programming Language :: Python :: 3.13',
|
|
38
|
+
'Programming Language :: Python :: 3.14',
|
|
38
39
|
'Programming Language :: Python :: Implementation :: CPython',
|
|
39
40
|
'Programming Language :: Python :: Implementation :: PyPy',
|
|
40
41
|
# --- END: Injected by Copier ---
|
|
@@ -105,13 +106,13 @@ strict-naming = false
|
|
|
105
106
|
python = '3.10'
|
|
106
107
|
[tool.hatch.envs.develop]
|
|
107
108
|
description = ''' Development environment. '''
|
|
109
|
+
builder = true
|
|
108
110
|
dependencies = [
|
|
109
|
-
'Jinja2',
|
|
110
111
|
'coverage[toml]',
|
|
111
112
|
'detextive[all]',
|
|
112
113
|
'furo',
|
|
113
114
|
'isort',
|
|
114
|
-
'
|
|
115
|
+
'myst-parser',
|
|
115
116
|
'pre-commit',
|
|
116
117
|
'pyright',
|
|
117
118
|
'pytest',
|
|
@@ -129,6 +130,7 @@ post-install-commands = [
|
|
|
129
130
|
# --- END: Injected by Copier ---
|
|
130
131
|
]
|
|
131
132
|
[tool.hatch.envs.develop.env-vars]
|
|
133
|
+
PYTHONIOENCODING = 'utf-8' # TODO: Only for coverage/doctest.
|
|
132
134
|
PYTHONUNBUFFERED = 'TRUE' # TODO: Only for coverage/pytest.
|
|
133
135
|
# --- BEGIN: Injected by Copier ---
|
|
134
136
|
# --- END: Injected by Copier ---
|
|
@@ -141,10 +143,10 @@ docsgen = [
|
|
|
141
143
|
]
|
|
142
144
|
linters = [
|
|
143
145
|
"""ruff check --quiet sources documentation tests""",
|
|
146
|
+
"""vibelinter check""",
|
|
144
147
|
# --- BEGIN: Injected by Copier ---
|
|
145
148
|
# --- END: Injected by Copier ---
|
|
146
149
|
"""isort --check-only --diff sources tests""",
|
|
147
|
-
"""vulture""",
|
|
148
150
|
"""pyright sources""",
|
|
149
151
|
]
|
|
150
152
|
packagers = [
|
|
@@ -186,7 +188,9 @@ python = [
|
|
|
186
188
|
'3.11',
|
|
187
189
|
'3.12',
|
|
188
190
|
'3.13',
|
|
191
|
+
'3.14',
|
|
189
192
|
'pypy3.10',
|
|
193
|
+
'pypy3.11',
|
|
190
194
|
]
|
|
191
195
|
[tool.hatch.version]
|
|
192
196
|
path = 'sources/detextive/__init__.py'
|
|
@@ -359,6 +363,10 @@ directory = 'repair'
|
|
|
359
363
|
name = 'Repairs'
|
|
360
364
|
showcontent = true
|
|
361
365
|
|
|
366
|
+
[tool.vibelinter]
|
|
367
|
+
context = 3
|
|
368
|
+
exclude_paths = [ '.auxiliary/**', '.venv/**', 'tests/**', ]
|
|
369
|
+
|
|
362
370
|
[tool.vulture]
|
|
363
371
|
paths = [ '.auxiliary/configuration/vulturefood.py', 'sources' ]
|
|
364
372
|
min_confidence = 60
|
|
@@ -24,8 +24,10 @@
|
|
|
24
24
|
|
|
25
25
|
import collections.abc as cabc
|
|
26
26
|
import codecs
|
|
27
|
+
import contextlib as ctxl
|
|
27
28
|
import dataclasses as dcls
|
|
28
29
|
import enum
|
|
30
|
+
import functools as funct
|
|
29
31
|
import locale
|
|
30
32
|
import mimetypes
|
|
31
33
|
import os
|
|
@@ -36,11 +38,13 @@ import unicodedata
|
|
|
36
38
|
from pathlib import Path
|
|
37
39
|
|
|
38
40
|
import accretive as accret
|
|
39
|
-
import dynadoc as ddoc
|
|
40
|
-
import frigid as immut
|
|
41
41
|
import typing_extensions as typx
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
# --- BEGIN: Injected by Copier ---
|
|
44
|
+
import dynadoc as ddoc
|
|
45
|
+
import frigid as immut
|
|
46
|
+
# --- END: Injected by Copier ---
|
|
44
47
|
|
|
45
48
|
# --- BEGIN: Injected by Copier ---
|
|
49
|
+
from absence import Absential, absent, is_absent
|
|
46
50
|
# --- END: Injected by Copier ---
|
|
@@ -30,8 +30,3 @@ PositionalArguments: __.typx.TypeAlias = __.cabc.Sequence[ __.typx.Any ]
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
package_name = __name__.split( '.', maxsplit = 1 )[ 0 ]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def is_public_identifier( name: str ) -> bool:
|
|
36
|
-
''' Is Python identifier public? '''
|
|
37
|
-
return not name.startswith( '_' )
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# vim: set filetype=python fileencoding=utf-8:
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
#============================================================================#
|
|
5
|
+
# #
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
|
7
|
+
# you may not use this file except in compliance with the License. #
|
|
8
|
+
# You may obtain a copy of the License at #
|
|
9
|
+
# #
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0 #
|
|
11
|
+
# #
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software #
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS, #
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
|
15
|
+
# See the License for the specific language governing permissions and #
|
|
16
|
+
# limitations under the License. #
|
|
17
|
+
# #
|
|
18
|
+
#============================================================================#
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
''' Management of bytes array decoding via trial character sets. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from . import __
|
|
25
|
+
from . import core as _core
|
|
26
|
+
from . import exceptions as _exceptions
|
|
27
|
+
from . import nomina as _nomina
|
|
28
|
+
|
|
29
|
+
from .core import ( # isort: skip
|
|
30
|
+
BEHAVIORS_DEFAULT as _BEHAVIORS_DEFAULT,
|
|
31
|
+
BehaviorTristate as _BehaviorTristate,
|
|
32
|
+
Behaviors as _Behaviors,
|
|
33
|
+
CharsetResult as _CharsetResult,
|
|
34
|
+
CodecSpecifiers as _CodecSpecifiers,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def attempt_decodes( # noqa: PLR0913
|
|
39
|
+
content: _nomina.Content, /, *,
|
|
40
|
+
behaviors: _Behaviors = _BEHAVIORS_DEFAULT,
|
|
41
|
+
inference: __.Absential[ str ] = __.absent,
|
|
42
|
+
supplement: __.Absential[ str ] = __.absent,
|
|
43
|
+
location: __.Absential[ _nomina.Location ] = __.absent,
|
|
44
|
+
validator: __.Absential[
|
|
45
|
+
__.cabc.Callable[ [ str, _CharsetResult ], None ]
|
|
46
|
+
] = __.absent,
|
|
47
|
+
) -> tuple[ str, _CharsetResult ]:
|
|
48
|
+
''' Attempts to decode content with various character sets.
|
|
49
|
+
|
|
50
|
+
Will try character sets in the order specified by the trial codecs
|
|
51
|
+
listed on the behaviors object.
|
|
52
|
+
'''
|
|
53
|
+
confidence = _core.confidence_from_bytes_quantity(
|
|
54
|
+
content, behaviors = behaviors )
|
|
55
|
+
trial_codecs = _collect_trial_codecs(
|
|
56
|
+
content,
|
|
57
|
+
behaviors = behaviors,
|
|
58
|
+
inference = inference,
|
|
59
|
+
supplement = supplement )
|
|
60
|
+
trials: set[ str ] = set( )
|
|
61
|
+
for trial_codec in trial_codecs:
|
|
62
|
+
try: text = content.decode(
|
|
63
|
+
trial_codec, errors = behaviors.on_decode_error )
|
|
64
|
+
except UnicodeDecodeError: continue
|
|
65
|
+
finally: trials.add( trial_codec )
|
|
66
|
+
result = _CharsetResult(
|
|
67
|
+
charset = normalize_charset_for_content( content, trial_codec ),
|
|
68
|
+
confidence = confidence )
|
|
69
|
+
if not __.is_absent( validator ):
|
|
70
|
+
try: validator( text, result )
|
|
71
|
+
except _exceptions.TextInvalidity: continue
|
|
72
|
+
return text, result
|
|
73
|
+
raise _exceptions.ContentDecodeFailure(
|
|
74
|
+
charset = tuple( trials ), location = location )
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def discover_os_charset_default( ) -> str:
|
|
78
|
+
''' Discovers default character set encoding from operating system. '''
|
|
79
|
+
discoverer = getattr(
|
|
80
|
+
__.locale, 'getencoding', __.sys.getfilesystemencoding )
|
|
81
|
+
return normalize_charset( discoverer( ) )
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def normalize_charset( charset: str, bom_cognizant: bool = False ) -> str:
|
|
85
|
+
''' Normalizes character set encoding names. '''
|
|
86
|
+
charset_ = __.codecs.lookup( charset ).name
|
|
87
|
+
if bom_cognizant and charset_ == 'utf-8': return 'utf-8-sig'
|
|
88
|
+
return charset_
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def normalize_charset_for_content(
|
|
92
|
+
content: _nomina.Content, charset: str
|
|
93
|
+
) -> str:
|
|
94
|
+
''' Normalizes charset reporting based on byte-order mark provenance. '''
|
|
95
|
+
charset_ = normalize_charset( charset )
|
|
96
|
+
bom_charset = _discover_utf_bom_charset( content )
|
|
97
|
+
if charset_ in ( 'utf-8', 'utf-8-sig' ):
|
|
98
|
+
if bom_charset == 'utf-8-sig': return 'utf-8-sig'
|
|
99
|
+
return 'utf-8'
|
|
100
|
+
for family in ( 'utf-16', 'utf-32' ):
|
|
101
|
+
if not charset_.startswith( family ): continue
|
|
102
|
+
if bom_charset in ( f"{family}-le", f"{family}-be" ):
|
|
103
|
+
return family
|
|
104
|
+
return charset_
|
|
105
|
+
return charset_
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def trial_decode_as_confident( # noqa: PLR0913
|
|
109
|
+
content: _nomina.Content, /, *,
|
|
110
|
+
behaviors: _Behaviors = _BEHAVIORS_DEFAULT,
|
|
111
|
+
inference: __.Absential[ str ] = __.absent,
|
|
112
|
+
confidence: float = 0.0,
|
|
113
|
+
supplement: __.Absential[ str ] = __.absent,
|
|
114
|
+
location: __.Absential[ _nomina.Location ] = __.absent,
|
|
115
|
+
) -> _CharsetResult:
|
|
116
|
+
''' Performs trial decode of content.
|
|
117
|
+
|
|
118
|
+
Considers desired trial decode behavior and detection confidence.
|
|
119
|
+
'''
|
|
120
|
+
nomargs: __.NominativeArguments = dict(
|
|
121
|
+
behaviors = behaviors,
|
|
122
|
+
inference = inference,
|
|
123
|
+
supplement = supplement,
|
|
124
|
+
location = location )
|
|
125
|
+
should_decode = False
|
|
126
|
+
match behaviors.trial_decode:
|
|
127
|
+
case _BehaviorTristate.Always: should_decode = True
|
|
128
|
+
case _BehaviorTristate.AsNeeded:
|
|
129
|
+
should_decode = confidence < behaviors.trial_decode_confidence
|
|
130
|
+
case _BehaviorTristate.Never: pass
|
|
131
|
+
if should_decode:
|
|
132
|
+
_, result = attempt_decodes( content, **nomargs )
|
|
133
|
+
return result
|
|
134
|
+
if __.is_absent( inference ):
|
|
135
|
+
raise _exceptions.CharsetDetectFailure( location = location )
|
|
136
|
+
return _CharsetResult( charset = inference, confidence = confidence )
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _collect_trial_codecs(
|
|
140
|
+
content: _nomina.Content, /, *,
|
|
141
|
+
behaviors: _Behaviors,
|
|
142
|
+
inference: __.Absential[ str ],
|
|
143
|
+
supplement: __.Absential[ str ],
|
|
144
|
+
) -> tuple[ str, ... ]:
|
|
145
|
+
codecs: list[ str ] = [ ] # No set needed; this candidate list is tiny.
|
|
146
|
+
for codec in behaviors.trial_codecs:
|
|
147
|
+
charset = _resolve_trial_codec(
|
|
148
|
+
codec, inference = inference, supplement = supplement )
|
|
149
|
+
if __.is_absent( charset ): continue
|
|
150
|
+
charset = normalize_charset( charset )
|
|
151
|
+
if _is_ambiguous_utf_trial( content, charset, behaviors ): continue
|
|
152
|
+
if behaviors.remove_bom and charset == 'utf-8': charset = 'utf-8-sig'
|
|
153
|
+
if charset in codecs: continue
|
|
154
|
+
codecs.append( charset )
|
|
155
|
+
return tuple( codecs )
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _discover_utf_bom_charset(
|
|
159
|
+
content: _nomina.Content
|
|
160
|
+
) -> __.typx.Optional[ str ]:
|
|
161
|
+
# Must check UTF-32 markers first, since they prefix-match UTF-16 markers.
|
|
162
|
+
if content.startswith( __.codecs.BOM_UTF32_LE ): return 'utf-32-le'
|
|
163
|
+
if content.startswith( __.codecs.BOM_UTF32_BE ): return 'utf-32-be'
|
|
164
|
+
if content.startswith( __.codecs.BOM_UTF8 ): return 'utf-8-sig'
|
|
165
|
+
if content.startswith( __.codecs.BOM_UTF16_LE ): return 'utf-16-le'
|
|
166
|
+
if content.startswith( __.codecs.BOM_UTF16_BE ): return 'utf-16-be'
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _is_ambiguous_utf_trial(
|
|
171
|
+
content: _nomina.Content, charset: str, behaviors: _Behaviors
|
|
172
|
+
) -> bool:
|
|
173
|
+
if not behaviors.utf_16_32_requires_byte_order: return False
|
|
174
|
+
match charset:
|
|
175
|
+
case 'utf-16':
|
|
176
|
+
bom_charset = _discover_utf_bom_charset( content )
|
|
177
|
+
return bom_charset not in ( 'utf-16-le', 'utf-16-be' )
|
|
178
|
+
case 'utf-32':
|
|
179
|
+
bom_charset = _discover_utf_bom_charset( content )
|
|
180
|
+
return bom_charset not in ( 'utf-32-le', 'utf-32-be' )
|
|
181
|
+
case _: return False
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _resolve_trial_codec(
|
|
185
|
+
codec: __.typx.Any, /, *,
|
|
186
|
+
inference: __.Absential[ str ],
|
|
187
|
+
supplement: __.Absential[ str ],
|
|
188
|
+
) -> __.Absential[ str ]:
|
|
189
|
+
charset: __.Absential[ str ] = __.absent
|
|
190
|
+
match codec:
|
|
191
|
+
case _CodecSpecifiers.FromInference:
|
|
192
|
+
if not __.is_absent( inference ): charset = inference
|
|
193
|
+
case _CodecSpecifiers.OsDefault:
|
|
194
|
+
charset = discover_os_charset_default( )
|
|
195
|
+
case _CodecSpecifiers.PythonDefault:
|
|
196
|
+
charset = __.locale.getpreferredencoding( )
|
|
197
|
+
case _CodecSpecifiers.UserSupplement:
|
|
198
|
+
if not __.is_absent( supplement ): charset = supplement
|
|
199
|
+
case str( ): charset = codec
|
|
200
|
+
case _: pass
|
|
201
|
+
return charset
|
|
@@ -22,15 +22,10 @@
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
from . import __
|
|
25
|
+
from . import exceptions as _exceptions
|
|
25
26
|
from . import nomina as _nomina
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
_STANDARD_CHARSET_PROMOTIONS = (
|
|
29
|
-
( 'ascii', 'utf-8-sig' ),
|
|
30
|
-
( 'utf-8', 'utf-8-sig' ),
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
|
|
34
29
|
CHARSET_DEFAULT = 'utf-8'
|
|
35
30
|
MIMETYPE_DEFAULT = 'application/octet-stream'
|
|
36
31
|
|
|
@@ -68,9 +63,9 @@ class Behaviors( __.immut.DataclassObject ):
|
|
|
68
63
|
''' Minimum number of bytes for full detection confidence. ''' ),
|
|
69
64
|
] = 1024
|
|
70
65
|
charset_detect: __.typx.Annotated[
|
|
71
|
-
|
|
72
|
-
__.ddoc.Doc( '''
|
|
73
|
-
] =
|
|
66
|
+
bool,
|
|
67
|
+
__.ddoc.Doc( ''' Whether to detect charset from content. ''' ),
|
|
68
|
+
] = True
|
|
74
69
|
charset_detectors_order: __.typx.Annotated[
|
|
75
70
|
__.cabc.Sequence[ str ],
|
|
76
71
|
__.ddoc.Doc(
|
|
@@ -80,20 +75,10 @@ class Behaviors( __.immut.DataclassObject ):
|
|
|
80
75
|
DetectFailureActions,
|
|
81
76
|
__.ddoc.Doc( ''' Action to take on charset detection failure. ''' ),
|
|
82
77
|
] = DetectFailureActions.Default
|
|
83
|
-
charset_promotions: __.typx.Annotated[
|
|
84
|
-
__.cabc.Mapping[ str, str ],
|
|
85
|
-
__.ddoc.Doc(
|
|
86
|
-
''' Which detected charsets to promote to other charsets.
|
|
87
|
-
|
|
88
|
-
E.g., 7-bit ASCII to UTF-8.
|
|
89
|
-
''' ),
|
|
90
|
-
] = __.dcls.field(
|
|
91
|
-
default_factory = (
|
|
92
|
-
lambda: __.immut.Dictionary( _STANDARD_CHARSET_PROMOTIONS ) ) )
|
|
93
78
|
mimetype_detect: __.typx.Annotated[
|
|
94
|
-
|
|
95
|
-
__.ddoc.Doc( '''
|
|
96
|
-
] =
|
|
79
|
+
bool,
|
|
80
|
+
__.ddoc.Doc( ''' Whether to detect MIME type from content. ''' ),
|
|
81
|
+
] = True
|
|
97
82
|
mimetype_detectors_order: __.typx.Annotated[
|
|
98
83
|
__.cabc.Sequence[ str ],
|
|
99
84
|
__.ddoc.Doc(
|
|
@@ -114,6 +99,9 @@ class Behaviors( __.immut.DataclassObject ):
|
|
|
114
99
|
'codecs' module.
|
|
115
100
|
''' ),
|
|
116
101
|
] = 'strict'
|
|
102
|
+
remove_bom: __.typx.Annotated[
|
|
103
|
+
bool, __.ddoc.Doc( ''' Remove byte-ordering mark? ''' )
|
|
104
|
+
] = True
|
|
117
105
|
text_validate: __.typx.Annotated[
|
|
118
106
|
BehaviorTristate,
|
|
119
107
|
__.ddoc.Doc( ''' When to validate text. ''' ),
|
|
@@ -125,7 +113,13 @@ class Behaviors( __.immut.DataclassObject ):
|
|
|
125
113
|
trial_codecs: __.typx.Annotated[
|
|
126
114
|
__.cabc.Sequence[ str | CodecSpecifiers ],
|
|
127
115
|
__.ddoc.Doc( ''' Sequence of codec names or specifiers. ''' ),
|
|
128
|
-
] = (
|
|
116
|
+
] = (
|
|
117
|
+
CodecSpecifiers.UserSupplement,
|
|
118
|
+
'utf-8',
|
|
119
|
+
CodecSpecifiers.FromInference,
|
|
120
|
+
CodecSpecifiers.OsDefault,
|
|
121
|
+
CodecSpecifiers.PythonDefault,
|
|
122
|
+
)
|
|
129
123
|
trial_decode: __.typx.Annotated[
|
|
130
124
|
BehaviorTristate,
|
|
131
125
|
__.ddoc.Doc(
|
|
@@ -134,6 +128,23 @@ class Behaviors( __.immut.DataclassObject ):
|
|
|
134
128
|
trial_decode_confidence: __.typx.Annotated[
|
|
135
129
|
float, __.ddoc.Doc( ''' Minimum confidence to skip trial decode. ''')
|
|
136
130
|
] = 0.80
|
|
131
|
+
utf_16_32_requires_byte_order: __.typx.Annotated[
|
|
132
|
+
bool,
|
|
133
|
+
__.ddoc.Doc(
|
|
134
|
+
''' Require explicit byte order for BOM-less generic UTF-16/32? '''
|
|
135
|
+
),
|
|
136
|
+
] = False
|
|
137
|
+
|
|
138
|
+
def __post_init__( self ) -> None:
|
|
139
|
+
if not isinstance( self.charset_detect, bool ):
|
|
140
|
+
raise _exceptions.BehaviorsInvalidity(
|
|
141
|
+
'charset_detect', 'a boolean' )
|
|
142
|
+
if not isinstance( self.mimetype_detect, bool ):
|
|
143
|
+
raise _exceptions.BehaviorsInvalidity(
|
|
144
|
+
'mimetype_detect', 'a boolean' )
|
|
145
|
+
if not isinstance( self.utf_16_32_requires_byte_order, bool ):
|
|
146
|
+
raise _exceptions.BehaviorsInvalidity(
|
|
147
|
+
'utf_16_32_requires_byte_order', 'a boolean' )
|
|
137
148
|
|
|
138
149
|
|
|
139
150
|
BehaviorsArgument: __.typx.TypeAlias = __.typx.Annotated[
|