detextive 3.0__tar.gz → 3.2__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.0 → detextive-3.2}/.gitignore +2 -0
- {detextive-3.0 → detextive-3.2}/PKG-INFO +19 -17
- {detextive-3.0 → detextive-3.2}/README.rst +15 -11
- {detextive-3.0 → detextive-3.2}/pyproject.toml +15 -11
- {detextive-3.0 → detextive-3.2}/sources/detextive/__init__.py +1 -1
- {detextive-3.0 → detextive-3.2}/sources/detextive/charsets.py +95 -22
- {detextive-3.0 → detextive-3.2}/sources/detextive/core.py +9 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/decoders.py +10 -9
- {detextive-3.0 → detextive-3.2}/sources/detextive/detectors.py +3 -5
- {detextive-3.0 → detextive-3.2}/LICENSE.txt +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/__/__init__.py +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/__/imports.py +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/__/nomina.py +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/_typedecls/puremagic/__init__.pyi +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/exceptions.py +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/inference.py +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/lineseparators.py +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/mimetypes.py +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/nomina.py +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/py.typed +0 -0
- {detextive-3.0 → detextive-3.2}/sources/detextive/validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: detextive
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2
|
|
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
|
|
@@ -15,15 +15,13 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
17
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
20
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
22
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
24
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
25
23
|
Classifier: Topic :: Software Development
|
|
26
|
-
Requires-Python: >=3.
|
|
24
|
+
Requires-Python: >=3.12
|
|
27
25
|
Requires-Dist: absence~=1.1
|
|
28
26
|
Requires-Dist: accretive~=4.1
|
|
29
27
|
Requires-Dist: chardet
|
|
@@ -113,6 +111,8 @@ Key Features ⭐
|
|
|
113
111
|
using control character and printability heuristics.
|
|
114
112
|
|
|
115
113
|
|
|
114
|
+
|
|
115
|
+
|
|
116
116
|
Installation 📦
|
|
117
117
|
===============================================================================
|
|
118
118
|
|
|
@@ -229,12 +229,14 @@ For complete bytes-to-text processing with automatic charset detection and valid
|
|
|
229
229
|
print( f"Decoded text: {text}" )
|
|
230
230
|
|
|
231
231
|
|
|
232
|
+
|
|
233
|
+
|
|
232
234
|
Contribution 🤝
|
|
233
235
|
===============================================================================
|
|
234
236
|
|
|
235
237
|
Contribution to this project is welcome! However, it must follow the `code of
|
|
236
238
|
conduct
|
|
237
|
-
<https://emcd.github.io/
|
|
239
|
+
<https://emcd.github.io/documentation-common/v1/sphinx-html/common/conduct.html>`_
|
|
238
240
|
for the project.
|
|
239
241
|
|
|
240
242
|
Please file bug reports and feature requests in the `issue tracker
|
|
@@ -286,9 +288,15 @@ Other Projects by This Author 🌟
|
|
|
286
288
|
===============================================================================
|
|
287
289
|
|
|
288
290
|
|
|
289
|
-
* `
|
|
291
|
+
* `agentmux <https://github.com/emcd/agentmux>`_ (`agentmux <https://crates.io/crates/agentmux/>`_ on Crates.io)
|
|
290
292
|
|
|
291
|
-
|
|
293
|
+
📡 Provides **relays for inter-agent communication** and a single pane of glass for managing multiple agent harness sessions.
|
|
294
|
+
* `cistella <https://github.com/emcd/cistella>`_ (`cistella <https://crates.io/crates/cistella/>`_ on Crates.io)
|
|
295
|
+
|
|
296
|
+
🧱 A **sandboxing application** which **isolates agent sessions** via various technologies.
|
|
297
|
+
* `nb-mcp-server <https://github.com/emcd/nb-mcp-server>`_ (`nb-mcp-server <https://crates.io/crates/nb-mcp-server/>`_ on Crates.io)
|
|
298
|
+
|
|
299
|
+
🗒️ An MCP server wrapping the ``nb`` CLI for **LLM-friendly note-taking**.
|
|
292
300
|
* `python-accretive <https://github.com/emcd/python-accretive>`_ (`accretive <https://pypi.org/project/accretive/>`_ on PyPI)
|
|
293
301
|
|
|
294
302
|
🌌 A Python library package which provides **accretive data structures** - collections which can grow but never shrink.
|
|
@@ -304,12 +312,6 @@ Other Projects by This Author 🌟
|
|
|
304
312
|
* `python-frigid <https://github.com/emcd/python-frigid>`_ (`frigid <https://pypi.org/project/frigid/>`_ on PyPI)
|
|
305
313
|
|
|
306
314
|
🔒 A Python library package which provides **immutable data structures** - collections which cannot be modified after creation.
|
|
307
|
-
* `python-
|
|
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.
|
|
313
|
-
* `python-mimeogram <https://github.com/emcd/python-mimeogram>`_ (`mimeogram <https://pypi.org/project/mimeogram/>`_ on PyPI)
|
|
315
|
+
* `python-ictr <https://github.com/emcd/python-ictr>`_ (`ictr <https://pypi.org/project/ictr/>`_ on PyPI)
|
|
314
316
|
|
|
315
|
-
|
|
317
|
+
🍦 **Non-Intrusive Debugging** - A Python library which provides flavored traces, configuration hierarchies, customized outputs, and ready-made recipes for logging and debug printing.
|
|
@@ -69,6 +69,8 @@ Key Features ⭐
|
|
|
69
69
|
using control character and printability heuristics.
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
|
|
73
|
+
|
|
72
74
|
Installation 📦
|
|
73
75
|
===============================================================================
|
|
74
76
|
|
|
@@ -185,12 +187,14 @@ For complete bytes-to-text processing with automatic charset detection and valid
|
|
|
185
187
|
print( f"Decoded text: {text}" )
|
|
186
188
|
|
|
187
189
|
|
|
190
|
+
|
|
191
|
+
|
|
188
192
|
Contribution 🤝
|
|
189
193
|
===============================================================================
|
|
190
194
|
|
|
191
195
|
Contribution to this project is welcome! However, it must follow the `code of
|
|
192
196
|
conduct
|
|
193
|
-
<https://emcd.github.io/
|
|
197
|
+
<https://emcd.github.io/documentation-common/v1/sphinx-html/common/conduct.html>`_
|
|
194
198
|
for the project.
|
|
195
199
|
|
|
196
200
|
Please file bug reports and feature requests in the `issue tracker
|
|
@@ -242,9 +246,15 @@ Other Projects by This Author 🌟
|
|
|
242
246
|
===============================================================================
|
|
243
247
|
|
|
244
248
|
|
|
245
|
-
* `
|
|
249
|
+
* `agentmux <https://github.com/emcd/agentmux>`_ (`agentmux <https://crates.io/crates/agentmux/>`_ on Crates.io)
|
|
246
250
|
|
|
247
|
-
|
|
251
|
+
📡 Provides **relays for inter-agent communication** and a single pane of glass for managing multiple agent harness sessions.
|
|
252
|
+
* `cistella <https://github.com/emcd/cistella>`_ (`cistella <https://crates.io/crates/cistella/>`_ on Crates.io)
|
|
253
|
+
|
|
254
|
+
🧱 A **sandboxing application** which **isolates agent sessions** via various technologies.
|
|
255
|
+
* `nb-mcp-server <https://github.com/emcd/nb-mcp-server>`_ (`nb-mcp-server <https://crates.io/crates/nb-mcp-server/>`_ on Crates.io)
|
|
256
|
+
|
|
257
|
+
🗒️ An MCP server wrapping the ``nb`` CLI for **LLM-friendly note-taking**.
|
|
248
258
|
* `python-accretive <https://github.com/emcd/python-accretive>`_ (`accretive <https://pypi.org/project/accretive/>`_ on PyPI)
|
|
249
259
|
|
|
250
260
|
🌌 A Python library package which provides **accretive data structures** - collections which can grow but never shrink.
|
|
@@ -260,12 +270,6 @@ Other Projects by This Author 🌟
|
|
|
260
270
|
* `python-frigid <https://github.com/emcd/python-frigid>`_ (`frigid <https://pypi.org/project/frigid/>`_ on PyPI)
|
|
261
271
|
|
|
262
272
|
🔒 A Python library package which provides **immutable data structures** - collections which cannot be modified after creation.
|
|
263
|
-
* `python-
|
|
264
|
-
|
|
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.
|
|
269
|
-
* `python-mimeogram <https://github.com/emcd/python-mimeogram>`_ (`mimeogram <https://pypi.org/project/mimeogram/>`_ on PyPI)
|
|
273
|
+
* `python-ictr <https://github.com/emcd/python-ictr>`_ (`ictr <https://pypi.org/project/ictr/>`_ on PyPI)
|
|
270
274
|
|
|
271
|
-
|
|
275
|
+
🍦 **Non-Intrusive Debugging** - A Python library which provides flavored traces, configuration hierarchies, customized outputs, and ready-made recipes for logging and debug printing.
|
|
@@ -13,7 +13,7 @@ description = 'Detects textual content.'
|
|
|
13
13
|
dynamic = [ 'version' ]
|
|
14
14
|
license = 'Apache-2.0'
|
|
15
15
|
readme = { 'file' = 'README.rst', 'content-type' = 'text/x-rst' }
|
|
16
|
-
requires-python = '>= 3.
|
|
16
|
+
requires-python = '>= 3.12'
|
|
17
17
|
dependencies = [
|
|
18
18
|
'accretive~=4.1',
|
|
19
19
|
'chardet',
|
|
@@ -31,13 +31,11 @@ classifiers = [ # https://pypi.org/classifiers
|
|
|
31
31
|
'License :: OSI Approved :: Apache Software License',
|
|
32
32
|
'Programming Language :: Python :: 3 :: Only',
|
|
33
33
|
# --- BEGIN: Injected by Copier ---
|
|
34
|
-
'Programming Language :: Python :: 3.10',
|
|
35
|
-
'Programming Language :: Python :: 3.11',
|
|
36
34
|
'Programming Language :: Python :: 3.12',
|
|
37
35
|
'Programming Language :: Python :: 3.13',
|
|
38
36
|
'Programming Language :: Python :: 3.14',
|
|
37
|
+
'Programming Language :: Python :: 3.15',
|
|
39
38
|
'Programming Language :: Python :: Implementation :: CPython',
|
|
40
|
-
'Programming Language :: Python :: Implementation :: PyPy',
|
|
41
39
|
# --- END: Injected by Copier ---
|
|
42
40
|
'Topic :: Software Development',
|
|
43
41
|
]
|
|
@@ -103,7 +101,7 @@ strict-naming = false
|
|
|
103
101
|
# --- BEGIN: Injected by Copier ---
|
|
104
102
|
# --- END: Injected by Copier ---
|
|
105
103
|
[tool.hatch.envs.default]
|
|
106
|
-
python = '3.
|
|
104
|
+
python = '3.12'
|
|
107
105
|
[tool.hatch.envs.develop]
|
|
108
106
|
description = ''' Development environment. '''
|
|
109
107
|
builder = true
|
|
@@ -113,7 +111,6 @@ dependencies = [
|
|
|
113
111
|
'furo',
|
|
114
112
|
'isort',
|
|
115
113
|
'myst-parser',
|
|
116
|
-
'pre-commit',
|
|
117
114
|
'pyright',
|
|
118
115
|
'pytest',
|
|
119
116
|
'ruff',
|
|
@@ -141,13 +138,23 @@ docsgen = [
|
|
|
141
138
|
"""sphinx-build -a -d .auxiliary/caches/sphinx --quiet \
|
|
142
139
|
documentation .auxiliary/artifacts/sphinx-html""",
|
|
143
140
|
]
|
|
141
|
+
fragments = [
|
|
142
|
+
"""python .auxiliary/scripts/validate-fragments.py""",
|
|
143
|
+
]
|
|
144
|
+
module-sizes = [
|
|
145
|
+
"""python .auxiliary/scripts/validate-module-sizes.py""",
|
|
146
|
+
]
|
|
144
147
|
linters = [
|
|
145
148
|
"""ruff check --quiet sources documentation tests""",
|
|
146
|
-
|
|
149
|
+
# External install only (e.g. uv tool install): a develop dependency
|
|
150
|
+
# cycles for packages below emcd-vibe-linter in the stack.
|
|
151
|
+
#"""vibelinter check""",
|
|
147
152
|
# --- BEGIN: Injected by Copier ---
|
|
148
153
|
# --- END: Injected by Copier ---
|
|
149
154
|
"""isort --check-only --diff sources tests""",
|
|
150
155
|
"""pyright sources""",
|
|
156
|
+
'fragments',
|
|
157
|
+
'module-sizes',
|
|
151
158
|
]
|
|
152
159
|
packagers = [
|
|
153
160
|
"""hatch build""",
|
|
@@ -184,13 +191,10 @@ description = ''' Quality assurance environment. '''
|
|
|
184
191
|
template = 'develop'
|
|
185
192
|
[[tool.hatch.envs.qa.matrix]]
|
|
186
193
|
python = [
|
|
187
|
-
'3.10',
|
|
188
|
-
'3.11',
|
|
189
194
|
'3.12',
|
|
190
195
|
'3.13',
|
|
191
196
|
'3.14',
|
|
192
|
-
'
|
|
193
|
-
'pypy3.11',
|
|
197
|
+
'3.15',
|
|
194
198
|
]
|
|
195
199
|
[tool.hatch.version]
|
|
196
200
|
path = 'sources/detextive/__init__.py'
|
|
@@ -35,7 +35,7 @@ from .core import ( # isort: skip
|
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def attempt_decodes(
|
|
38
|
+
def attempt_decodes( # noqa: PLR0913
|
|
39
39
|
content: _nomina.Content, /, *,
|
|
40
40
|
behaviors: _Behaviors = _BEHAVIORS_DEFAULT,
|
|
41
41
|
inference: __.Absential[ str ] = __.absent,
|
|
@@ -52,29 +52,20 @@ def attempt_decodes( # noqa: C901,PLR0912,PLR0913,PLR0915
|
|
|
52
52
|
'''
|
|
53
53
|
confidence = _core.confidence_from_bytes_quantity(
|
|
54
54
|
content, behaviors = behaviors )
|
|
55
|
-
|
|
55
|
+
trial_codecs = _collect_trial_codecs(
|
|
56
|
+
content,
|
|
57
|
+
behaviors = behaviors,
|
|
58
|
+
inference = inference,
|
|
59
|
+
supplement = supplement )
|
|
56
60
|
trials: set[ str ] = set( )
|
|
57
|
-
for
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if __.is_absent( inference ): continue
|
|
61
|
-
charset = inference
|
|
62
|
-
case _CodecSpecifiers.OsDefault:
|
|
63
|
-
charset = discover_os_charset_default( )
|
|
64
|
-
case _CodecSpecifiers.PythonDefault:
|
|
65
|
-
charset = __.locale.getpreferredencoding( )
|
|
66
|
-
case _CodecSpecifiers.UserSupplement:
|
|
67
|
-
if __.is_absent( supplement ): continue
|
|
68
|
-
charset = supplement
|
|
69
|
-
case str( ): charset = codec
|
|
70
|
-
case _: continue
|
|
71
|
-
charset = normalize_charset(
|
|
72
|
-
charset, bom_cognizant = behaviors.remove_bom )
|
|
73
|
-
if charset in trials: continue
|
|
74
|
-
try: text = content.decode( charset, errors = on_decode_error )
|
|
61
|
+
for trial_codec in trial_codecs:
|
|
62
|
+
try: text = content.decode(
|
|
63
|
+
trial_codec, errors = behaviors.on_decode_error )
|
|
75
64
|
except UnicodeDecodeError: continue
|
|
76
|
-
finally: trials.add(
|
|
77
|
-
result = _CharsetResult(
|
|
65
|
+
finally: trials.add( trial_codec )
|
|
66
|
+
result = _CharsetResult(
|
|
67
|
+
charset = normalize_charset_for_content( content, trial_codec ),
|
|
68
|
+
confidence = confidence )
|
|
78
69
|
if not __.is_absent( validator ):
|
|
79
70
|
try: validator( text, result )
|
|
80
71
|
except _exceptions.TextInvalidity: continue
|
|
@@ -97,6 +88,23 @@ def normalize_charset( charset: str, bom_cognizant: bool = False ) -> str:
|
|
|
97
88
|
return charset_
|
|
98
89
|
|
|
99
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
|
+
|
|
100
108
|
def trial_decode_as_confident( # noqa: PLR0913
|
|
101
109
|
content: _nomina.Content, /, *,
|
|
102
110
|
behaviors: _Behaviors = _BEHAVIORS_DEFAULT,
|
|
@@ -126,3 +134,68 @@ def trial_decode_as_confident( # noqa: PLR0913
|
|
|
126
134
|
if __.is_absent( inference ):
|
|
127
135
|
raise _exceptions.CharsetDetectFailure( location = location )
|
|
128
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
|
|
@@ -128,6 +128,12 @@ class Behaviors( __.immut.DataclassObject ):
|
|
|
128
128
|
trial_decode_confidence: __.typx.Annotated[
|
|
129
129
|
float, __.ddoc.Doc( ''' Minimum confidence to skip trial decode. ''')
|
|
130
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
|
|
131
137
|
|
|
132
138
|
def __post_init__( self ) -> None:
|
|
133
139
|
if not isinstance( self.charset_detect, bool ):
|
|
@@ -136,6 +142,9 @@ class Behaviors( __.immut.DataclassObject ):
|
|
|
136
142
|
if not isinstance( self.mimetype_detect, bool ):
|
|
137
143
|
raise _exceptions.BehaviorsInvalidity(
|
|
138
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' )
|
|
139
148
|
|
|
140
149
|
|
|
141
150
|
BehaviorsArgument: __.typx.TypeAlias = __.typx.Annotated[
|
|
@@ -160,15 +160,16 @@ def _decode_content_charset_result( # noqa: PLR0913
|
|
|
160
160
|
if not __.is_absent( httpct_result ): return httpct_result
|
|
161
161
|
behaviors_ = __.dcls.replace(
|
|
162
162
|
behaviors, trial_decode = _BehaviorTristate.Never )
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
163
|
+
if behaviors.charset_detect:
|
|
164
|
+
with __.ctxl.suppress( _exceptions.CharsetDetectFailure ):
|
|
165
|
+
result = _detectors.detect_charset_confidence(
|
|
166
|
+
content,
|
|
167
|
+
behaviors = behaviors_,
|
|
168
|
+
supplement = charset_supplement,
|
|
169
|
+
location = location )
|
|
170
|
+
if ( result.charset
|
|
171
|
+
and result.confidence >= behaviors.trial_decode_confidence
|
|
172
|
+
): charset = result.charset
|
|
172
173
|
validator = __.funct.partial(
|
|
173
174
|
_validate_text_in_decode_attempt,
|
|
174
175
|
behaviors = behaviors,
|
|
@@ -340,11 +340,9 @@ mimetype_detectors[ 'puremagic' ] = _detect_via_puremagic
|
|
|
340
340
|
|
|
341
341
|
|
|
342
342
|
def _normalize_charset_detection(
|
|
343
|
-
content: _nomina.Content,
|
|
343
|
+
content: _nomina.Content, _behaviors: _Behaviors, result: _CharsetResult
|
|
344
344
|
) -> _CharsetResult:
|
|
345
345
|
if result.charset is None: return result # pragma: no cover
|
|
346
|
-
charset = _charsets.
|
|
347
|
-
|
|
348
|
-
if charset == 'utf-8-sig' and not content.startswith( __.codecs.BOM ):
|
|
349
|
-
charset = 'utf-8'
|
|
346
|
+
charset = _charsets.normalize_charset_for_content(
|
|
347
|
+
content, result.charset )
|
|
350
348
|
return _CharsetResult( charset = charset, confidence = result.confidence )
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|