datafog 4.9.0a2__tar.gz → 4.10.0__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.
Files changed (91) hide show
  1. {datafog-4.9.0a2 → datafog-4.10.0}/PKG-INFO +43 -17
  2. {datafog-4.9.0a2 → datafog-4.10.0}/README.md +39 -14
  3. datafog-4.10.0/datafog/__about__.py +1 -0
  4. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/__init__.py +13 -3
  5. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/_core_capabilities.py +7 -6
  6. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/_legacy_retirement.py +5 -1
  7. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/compat/v4.py +8 -7
  8. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/engine.py +193 -33
  9. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/exceptions.py +4 -0
  10. datafog-4.10.0/datafog/native.py +60 -0
  11. {datafog-4.9.0a2 → datafog-4.10.0}/datafog.egg-info/PKG-INFO +43 -17
  12. {datafog-4.9.0a2 → datafog-4.10.0}/datafog.egg-info/SOURCES.txt +2 -0
  13. {datafog-4.9.0a2 → datafog-4.10.0}/datafog.egg-info/requires.txt +2 -1
  14. {datafog-4.9.0a2 → datafog-4.10.0}/setup.py +6 -2
  15. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_allowlist.py +19 -1
  16. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_api_bridge_49.py +1 -1
  17. datafog-4.10.0/tests/test_backend_resolution.py +114 -0
  18. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_contract_481.py +17 -4
  19. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_core04_integration.py +25 -12
  20. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_de_pii_regex.py +13 -4
  21. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_detection_accuracy.py +105 -16
  22. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_legacy_retirement.py +7 -4
  23. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_rust_backend.py +11 -4
  24. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_rust_contract.py +3 -1
  25. datafog-4.9.0a2/datafog/__about__.py +0 -1
  26. {datafog-4.9.0a2 → datafog-4.10.0}/LICENSE +0 -0
  27. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/__init___lean.py +0 -0
  28. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/__init___original.py +0 -0
  29. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/agent.py +0 -0
  30. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/client.py +0 -0
  31. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/compat/__init__.py +0 -0
  32. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/config.py +0 -0
  33. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/core.py +0 -0
  34. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/integrations/__init__.py +0 -0
  35. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/integrations/claude_code.py +0 -0
  36. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/integrations/litellm_guardrail.py +0 -0
  37. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/main.py +0 -0
  38. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/main_lean.py +0 -0
  39. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/main_original.py +0 -0
  40. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/models/__init__.py +0 -0
  41. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/models/annotator.py +0 -0
  42. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/models/anonymizer.py +0 -0
  43. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/models/common.py +0 -0
  44. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/models/spacy_nlp.py +0 -0
  45. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/__init__.py +0 -0
  46. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/image_processing/__init__.py +0 -0
  47. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/image_processing/donut_processor.py +0 -0
  48. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/image_processing/image_downloader.py +0 -0
  49. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/image_processing/pytesseract_processor.py +0 -0
  50. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/spark_processing/__init__.py +0 -0
  51. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/spark_processing/pyspark_udfs.py +0 -0
  52. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/text_processing/__init__.py +0 -0
  53. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/text_processing/gliner_annotator.py +0 -0
  54. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/text_processing/regex_annotator/__init__.py +0 -0
  55. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/text_processing/regex_annotator/regex_annotator.py +0 -0
  56. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/processing/text_processing/spacy_pii_annotator.py +0 -0
  57. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/py.typed +0 -0
  58. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/services/__init__.py +0 -0
  59. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/services/image_service.py +0 -0
  60. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/services/spark_service.py +0 -0
  61. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/services/text_service.py +0 -0
  62. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/services/text_service_lean.py +0 -0
  63. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/services/text_service_original.py +0 -0
  64. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/telemetry.py +0 -0
  65. {datafog-4.9.0a2 → datafog-4.10.0}/datafog/v5.py +0 -0
  66. {datafog-4.9.0a2 → datafog-4.10.0}/datafog.egg-info/dependency_links.txt +0 -0
  67. {datafog-4.9.0a2 → datafog-4.10.0}/datafog.egg-info/entry_points.txt +0 -0
  68. {datafog-4.9.0a2 → datafog-4.10.0}/datafog.egg-info/top_level.txt +0 -0
  69. {datafog-4.9.0a2 → datafog-4.10.0}/setup.cfg +0 -0
  70. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_agent_api.py +0 -0
  71. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_anonymizer.py +0 -0
  72. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_claude_code_hook.py +0 -0
  73. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_cli_smoke.py +0 -0
  74. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_client.py +0 -0
  75. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_core_capability_adapter.py +0 -0
  76. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_donut_lazy_import.py +0 -0
  77. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_engine_api.py +0 -0
  78. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_gliner_annotator.py +0 -0
  79. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_image_service.py +0 -0
  80. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_install_profiles.py +0 -0
  81. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_litellm_guardrail.py +0 -0
  82. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_main.py +0 -0
  83. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_no_network_core.py +0 -0
  84. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_ocr_integration.py +0 -0
  85. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_regex_annotator.py +0 -0
  86. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_runtime_dependency_safety.py +0 -0
  87. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_spark_integration.py +0 -0
  88. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_telemetry.py +0 -0
  89. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_text_service.py +0 -0
  90. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_text_service_integration.py +0 -0
  91. {datafog-4.9.0a2 → datafog-4.10.0}/tests/test_v44_bridge_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datafog
3
- Version: 4.9.0a2
3
+ Version: 4.10.0
4
4
  Summary: Lightning-fast PII detection and anonymization library, 100x+ faster than NER-based detection (see benchmarks/)
5
5
  Author: Sid Mohan
6
6
  Author-email: sid@datafog.ai
@@ -10,7 +10,7 @@ Project-URL: Discord, https://discord.gg/bzDth394R4
10
10
  Project-URL: Twitter, https://twitter.com/datafoginc
11
11
  Project-URL: GitHub, https://github.com/datafog/datafog-python
12
12
  Keywords: pii detection anonymization privacy regex performance
13
- Classifier: Development Status :: 4 - Beta
13
+ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: Apache Software License
16
16
  Classifier: Programming Language :: Python :: 3
@@ -25,11 +25,12 @@ Classifier: Topic :: Security
25
25
  Requires-Python: >=3.10
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
+ Requires-Dist: datafog-core<2,>=1.3.0
28
29
  Requires-Dist: pydantic<3.0,>=2.0
29
30
  Requires-Dist: pydantic-settings>=2.0.0
30
31
  Requires-Dist: typing-extensions>=4.0
31
32
  Provides-Extra: rust
32
- Requires-Dist: datafog-core<0.5,>=0.4.0; extra == "rust"
33
+ Requires-Dist: datafog-core<2,>=1.3.0; extra == "rust"
33
34
  Provides-Extra: nlp
34
35
  Requires-Dist: click<9.0,>=8.0; extra == "nlp"
35
36
  Requires-Dist: spacy<4.0,>=3.7.0; extra == "nlp"
@@ -133,8 +134,9 @@ values never echoed into logs or transcripts:
133
134
 
134
135
  - **Claude Code hook** (`datafog-hook`): gates agent tool calls (shell
135
136
  commands, web requests, file writes, MCP tools) and warns the model when
136
- prompts or tool results carry PII. ~70–90ms per invocation including
137
- process startup. Easiest install is the
137
+ prompts or tool results carry PII. ~105–110ms per invocation including
138
+ process startup and the native engine load (`DATAFOG_BACKEND=python` trims
139
+ ~20ms at the cost of detection coverage). Easiest install is the
138
140
  [Claude Code plugin](https://github.com/DataFog/datafog-claude-plugin):
139
141
 
140
142
  ```
@@ -146,8 +148,8 @@ values never echoed into logs or transcripts:
146
148
 
147
149
  - **LiteLLM guardrail** (`DataFogGuardrail`): redacts or blocks PII in
148
150
  requests and responses at the gateway, for any LiteLLM-proxied provider.
149
- In-process (~40µs per message scanned; a request clears the guardrail in
150
- well under a millisecond), no sidecar service. Setup:
151
+ In-process (tens of microseconds per message scanned on the native engine),
152
+ no sidecar service. Setup:
151
153
  [examples/litellm_guardrail/](examples/litellm_guardrail/).
152
154
 
153
155
  Both default to the high-precision entity set (`EMAIL`, `PHONE`,
@@ -165,7 +167,7 @@ NER live in [benchmarks/](benchmarks/).
165
167
  ## Installation
166
168
 
167
169
  ```bash
168
- # Core install (regex engine)
170
+ # Core install: native engine, no extra packages needed
169
171
  pip install datafog
170
172
 
171
173
  # Add spaCy support
@@ -184,9 +186,33 @@ pip install datafog[distributed]
184
186
  pip install datafog[all]
185
187
  ```
186
188
 
189
+ `pip install datafog` pulls `datafog-core`, which is the native engine. Wheels
190
+ exist for Linux (glibc and musl, x86_64/aarch64/s390x/ppc64le), macOS and
191
+ Windows (amd64/arm64); anywhere else the source build needs a Rust toolchain.
192
+ If Core cannot be imported, detection falls back to the pure-Python engine and
193
+ warns once — `DATAFOG_BACKEND=python` selects that path deliberately.
194
+
187
195
  The `nlp-advanced` extra includes SentencePiece and protobuf for GLiNER's
188
196
  multilingual tokenizer; installing the OCR extra is not required for GLiNER.
189
197
 
198
+ ## Backends
199
+
200
+ `backend` defaults to `auto`: the native engine when `datafog-core` is
201
+ importable, the pure-Python regex engine otherwise. `DATAFOG_BACKEND` sets the
202
+ default and `backend="python"` / `backend="rust"` override it per call.
203
+ `auto` never selects the native engine for `engine="spacy"`, `"gliner"` or
204
+ `"smart"`, which stay on the Python ML path.
205
+
206
+ The native engine adds detectors the Python engine never had — `API_KEY`,
207
+ `BEARER_TOKEN`, `JWT`, `PRIVATE_KEY`, `CREDENTIAL_URI`, `ACCOUNT_NUMBER`,
208
+ `US_ROUTING_NUMBER`, `NPI`, `UUID` and a German set — and serves prose `PERSON`
209
+ and `ADDRESS` from an embedded model, so name and address detection needs no
210
+ spaCy and no model download. Findings keep `engine="regex"` provenance and
211
+ `confidence=1.0` as compatibility values, not native confidence estimates.
212
+ Reviewed differences from the Python engine are listed in
213
+ [tests/contracts/rust-1.3.0.json](tests/contracts/rust-1.3.0.json).
214
+ `datafog.native` exposes the native schema directly.
215
+
190
216
  Python 3.13 support is certified for the core SDK, CLI, `nlp`,
191
217
  `nlp-advanced`, and `ocr` install profiles. Donut OCR still requires a model
192
218
  that is available locally before runtime use. `distributed` and `all` remain
@@ -302,10 +328,12 @@ scan/redact helpers, or guardrail helpers.
302
328
  model.
303
329
  - A Java runtime is required by PySpark.
304
330
 
305
- DataFog 4.9.0 deprecates OCR and Spark with visible use-time
306
- warnings; their APIs and extras will be removed in 5.0. They remain functional
307
- in 4.9. Users who need these features can stay on the final 4.x release. See
308
- the [4.9 migration guide](docs/migration-4.9.md) for the transition plan.
331
+ DataFog deprecates OCR and Spark with visible use-time
332
+ warnings; their APIs and extras will be removed in 5.0. The last supported
333
+ version is 4.10.0 — pin it with `pip install "datafog==4.10.0"` — and every
334
+ retired surface is tracked in the [retirement schedule](docs/retirement.rst).
335
+ See the [4.9 migration guide](docs/migration-4.9.md) for the transition plan,
336
+ and [VERSIONING.md](VERSIONING.md) for what a version number promises.
309
337
 
310
338
  ## Backward-Compatible APIs
311
339
 
@@ -375,11 +403,9 @@ The [4.9 migration guide](docs/migration-4.9.md) explains opt-in Rust detection,
375
403
  the native `datafog.v5` preview, and the revised 5.0 retirement schedule for
376
404
  `detect`/`process`, OCR, and Spark. The Python detector remains the default.
377
405
 
378
- The experimental Rust adapter in 4.9.0 requires Core `>=0.4.0,<0.5` and capability
379
- contract 1. Upgrade with `python -m pip install --upgrade "datafog[rust]==4.9.0"`
380
- to evaluate it. Base-only users can install `datafog==4.9.0` without Core;
381
- installing the Rust extra does not change the default backend. See the
382
- [4.9.0 release notes](RELEASE_NOTES_4.9.0.md).
406
+ 4.10.0 requires Core `>=1.3.0,<2` and capability contract 1, and installs it by
407
+ default. See the [4.10.0 release notes](RELEASE_NOTES_4.10.0.md) and the
408
+ [4.10 migration guide](docs/migration-4.10.md).
383
409
  Entity labels, locales, and activation settings come from the installed
384
410
  Core, allowing compatible releases to add detectors without a Python update.
385
411
  German detection is opt-in through locale or entity selection; UUID is opt-in
@@ -19,8 +19,9 @@ values never echoed into logs or transcripts:
19
19
 
20
20
  - **Claude Code hook** (`datafog-hook`): gates agent tool calls (shell
21
21
  commands, web requests, file writes, MCP tools) and warns the model when
22
- prompts or tool results carry PII. ~70–90ms per invocation including
23
- process startup. Easiest install is the
22
+ prompts or tool results carry PII. ~105–110ms per invocation including
23
+ process startup and the native engine load (`DATAFOG_BACKEND=python` trims
24
+ ~20ms at the cost of detection coverage). Easiest install is the
24
25
  [Claude Code plugin](https://github.com/DataFog/datafog-claude-plugin):
25
26
 
26
27
  ```
@@ -32,8 +33,8 @@ values never echoed into logs or transcripts:
32
33
 
33
34
  - **LiteLLM guardrail** (`DataFogGuardrail`): redacts or blocks PII in
34
35
  requests and responses at the gateway, for any LiteLLM-proxied provider.
35
- In-process (~40µs per message scanned; a request clears the guardrail in
36
- well under a millisecond), no sidecar service. Setup:
36
+ In-process (tens of microseconds per message scanned on the native engine),
37
+ no sidecar service. Setup:
37
38
  [examples/litellm_guardrail/](examples/litellm_guardrail/).
38
39
 
39
40
  Both default to the high-precision entity set (`EMAIL`, `PHONE`,
@@ -51,7 +52,7 @@ NER live in [benchmarks/](benchmarks/).
51
52
  ## Installation
52
53
 
53
54
  ```bash
54
- # Core install (regex engine)
55
+ # Core install: native engine, no extra packages needed
55
56
  pip install datafog
56
57
 
57
58
  # Add spaCy support
@@ -70,9 +71,33 @@ pip install datafog[distributed]
70
71
  pip install datafog[all]
71
72
  ```
72
73
 
74
+ `pip install datafog` pulls `datafog-core`, which is the native engine. Wheels
75
+ exist for Linux (glibc and musl, x86_64/aarch64/s390x/ppc64le), macOS and
76
+ Windows (amd64/arm64); anywhere else the source build needs a Rust toolchain.
77
+ If Core cannot be imported, detection falls back to the pure-Python engine and
78
+ warns once — `DATAFOG_BACKEND=python` selects that path deliberately.
79
+
73
80
  The `nlp-advanced` extra includes SentencePiece and protobuf for GLiNER's
74
81
  multilingual tokenizer; installing the OCR extra is not required for GLiNER.
75
82
 
83
+ ## Backends
84
+
85
+ `backend` defaults to `auto`: the native engine when `datafog-core` is
86
+ importable, the pure-Python regex engine otherwise. `DATAFOG_BACKEND` sets the
87
+ default and `backend="python"` / `backend="rust"` override it per call.
88
+ `auto` never selects the native engine for `engine="spacy"`, `"gliner"` or
89
+ `"smart"`, which stay on the Python ML path.
90
+
91
+ The native engine adds detectors the Python engine never had — `API_KEY`,
92
+ `BEARER_TOKEN`, `JWT`, `PRIVATE_KEY`, `CREDENTIAL_URI`, `ACCOUNT_NUMBER`,
93
+ `US_ROUTING_NUMBER`, `NPI`, `UUID` and a German set — and serves prose `PERSON`
94
+ and `ADDRESS` from an embedded model, so name and address detection needs no
95
+ spaCy and no model download. Findings keep `engine="regex"` provenance and
96
+ `confidence=1.0` as compatibility values, not native confidence estimates.
97
+ Reviewed differences from the Python engine are listed in
98
+ [tests/contracts/rust-1.3.0.json](tests/contracts/rust-1.3.0.json).
99
+ `datafog.native` exposes the native schema directly.
100
+
76
101
  Python 3.13 support is certified for the core SDK, CLI, `nlp`,
77
102
  `nlp-advanced`, and `ocr` install profiles. Donut OCR still requires a model
78
103
  that is available locally before runtime use. `distributed` and `all` remain
@@ -188,10 +213,12 @@ scan/redact helpers, or guardrail helpers.
188
213
  model.
189
214
  - A Java runtime is required by PySpark.
190
215
 
191
- DataFog 4.9.0 deprecates OCR and Spark with visible use-time
192
- warnings; their APIs and extras will be removed in 5.0. They remain functional
193
- in 4.9. Users who need these features can stay on the final 4.x release. See
194
- the [4.9 migration guide](docs/migration-4.9.md) for the transition plan.
216
+ DataFog deprecates OCR and Spark with visible use-time
217
+ warnings; their APIs and extras will be removed in 5.0. The last supported
218
+ version is 4.10.0 — pin it with `pip install "datafog==4.10.0"` — and every
219
+ retired surface is tracked in the [retirement schedule](docs/retirement.rst).
220
+ See the [4.9 migration guide](docs/migration-4.9.md) for the transition plan,
221
+ and [VERSIONING.md](VERSIONING.md) for what a version number promises.
195
222
 
196
223
  ## Backward-Compatible APIs
197
224
 
@@ -261,11 +288,9 @@ The [4.9 migration guide](docs/migration-4.9.md) explains opt-in Rust detection,
261
288
  the native `datafog.v5` preview, and the revised 5.0 retirement schedule for
262
289
  `detect`/`process`, OCR, and Spark. The Python detector remains the default.
263
290
 
264
- The experimental Rust adapter in 4.9.0 requires Core `>=0.4.0,<0.5` and capability
265
- contract 1. Upgrade with `python -m pip install --upgrade "datafog[rust]==4.9.0"`
266
- to evaluate it. Base-only users can install `datafog==4.9.0` without Core;
267
- installing the Rust extra does not change the default backend. See the
268
- [4.9.0 release notes](RELEASE_NOTES_4.9.0.md).
291
+ 4.10.0 requires Core `>=1.3.0,<2` and capability contract 1, and installs it by
292
+ default. See the [4.10.0 release notes](RELEASE_NOTES_4.10.0.md) and the
293
+ [4.10 migration guide](docs/migration-4.10.md).
269
294
  Entity labels, locales, and activation settings come from the installed
270
295
  Core, allowing compatible releases to add detectors without a Python update.
271
296
  German detection is opt-in through locale or entity selection; UUID is opt-in
@@ -0,0 +1 @@
1
+ __version__ = "4.10.0"
@@ -12,6 +12,7 @@ Optional extras available for advanced features:
12
12
  import warnings
13
13
 
14
14
  from .__about__ import __version__
15
+ from ._legacy_retirement import LAST_SUPPORTED as _LAST_SUPPORTED
15
16
  from .agent import create_guardrail, filter_output, sanitize, scan_prompt
16
17
 
17
18
  # Core API functions - always available (lightweight)
@@ -81,6 +82,13 @@ def __getattr__(name: str):
81
82
 
82
83
  elif name in _LAZY_EXPORTS:
83
84
  module_path, attr_name, extra_name = _LAZY_EXPORTS[name]
85
+ if attr_name is None:
86
+ # Submodule export (``datafog.native``): return the module itself.
87
+ import importlib
88
+
89
+ value = importlib.import_module(module_path)
90
+ globals()[name] = value
91
+ return value
84
92
  try:
85
93
  module = __import__(module_path, fromlist=[attr_name])
86
94
  value = getattr(module, attr_name)
@@ -104,6 +112,7 @@ def __getattr__(name: str):
104
112
 
105
113
 
106
114
  _LAZY_EXPORTS = {
115
+ "native": ("datafog.native", None, None),
107
116
  "app": ("datafog.client", "app", None),
108
117
  "DataFog": ("datafog.main", "DataFog", None),
109
118
  "TextPIIAnnotator": ("datafog.main", "TextPIIAnnotator", None),
@@ -131,7 +140,8 @@ _LAZY_EXPORTS = {
131
140
  def _warn_v5_replacement(old_api: str, replacement: str) -> None:
132
141
  warnings.warn(
133
142
  f"datafog.{old_api}() is deprecated and will be removed in 5.0. Use {replacement} instead. "
134
- "The earlier promise to retain this shim through 5.x has been revised.",
143
+ f'Last supported version is {_LAST_SUPPORTED}. Pin it with: pip install "datafog=={_LAST_SUPPORTED}". '
144
+ "The earlier promise to retain this shim through 5.x has been revised. See docs/retirement.rst.",
135
145
  FutureWarning,
136
146
  stacklevel=3,
137
147
  )
@@ -145,7 +155,7 @@ def scan(
145
155
  allowlist: list[str] | None = None,
146
156
  allowlist_patterns: list[str] | None = None,
147
157
  *,
148
- backend: str = "python",
158
+ backend: str | None = None,
149
159
  ) -> ScanResult:
150
160
  """Scan using the 4.x compatibility API; Rust detection is opt-in."""
151
161
  return _compat_v4.scan(
@@ -170,7 +180,7 @@ def redact(
170
180
  allowlist: list[str] | None = None,
171
181
  allowlist_patterns: list[str] | None = None,
172
182
  *,
173
- backend: str = "python",
183
+ backend: str | None = None,
174
184
  ) -> RedactResult:
175
185
  """Redact using the 4.x compatibility API and legacy strategies."""
176
186
  return _compat_v4.redact(
@@ -85,7 +85,7 @@ def _requested_locales(locales: Any, lookup: dict[str, str]) -> set[str]:
85
85
  return {_resolve_locale(locale, lookup) for locale in locales or []}
86
86
 
87
87
 
88
- def _activation_config(label: str, metadata: dict) -> dict:
88
+ def _activation_config(label: str, metadata: dict) -> dict | None:
89
89
  details = metadata.get(label)
90
90
  if not isinstance(details, dict):
91
91
  raise _invalid(f"missing entity metadata for {label!r}")
@@ -95,10 +95,9 @@ def _activation_config(label: str, metadata: dict) -> dict:
95
95
  ):
96
96
  raise _invalid(f"invalid scopes for {label!r}")
97
97
  if "text" not in scopes:
98
- raise ValueError(
99
- f"Entity {label!r} is not available for Rust text scanning "
100
- "(structured-only entity)"
101
- )
98
+ # Structured-only labels are served by the caller, usually from the
99
+ # embedded model, so a mixed selection is not an error.
100
+ return None
102
101
  activation = details.get("activation")
103
102
  if not isinstance(activation, dict):
104
103
  raise _invalid(f"missing activation metadata for {label!r}")
@@ -120,13 +119,15 @@ def scan_configs(core: Any, requested: set[str], locales: Any) -> list[dict]:
120
119
  capabilities = getattr(core, "capabilities", None)
121
120
  if not callable(capabilities):
122
121
  raise _invalid(
123
- "contract version 1 is required; install a compatible Core 0.4.x"
122
+ "contract version 1 is required; install a compatible datafog-core"
124
123
  )
125
124
  supported, lookup, metadata = _read_capabilities(capabilities)
126
125
  selected_locales = _requested_locales(locales, lookup)
127
126
  common: dict[str, Any] = {}
128
127
  for label in sorted(requested & supported):
129
128
  config = _activation_config(label, metadata)
129
+ if config is None:
130
+ continue
130
131
  for key, value in config.items():
131
132
  if key == "locale":
132
133
  selected_locales.add(_resolve_locale(value, lookup))
@@ -2,6 +2,9 @@
2
2
 
3
3
  import warnings
4
4
 
5
+ #: Final release on which retired optional surfaces still work.
6
+ LAST_SUPPORTED = "4.10.0"
7
+
5
8
 
6
9
  class LegacySurfaceWarning(FutureWarning):
7
10
  """An optional DataFog surface scheduled for removal in 5.0."""
@@ -11,7 +14,8 @@ def warn_legacy_surface(surface: str) -> None:
11
14
  """Warn at the caller's public API boundary, before optional imports."""
12
15
  warnings.warn(
13
16
  f"DataFog {surface} support is deprecated in 4.9 and will be removed in 5.0. "
14
- "Remain on the final 4.x release if you need continued support.",
17
+ f'Last supported version is {LAST_SUPPORTED}. Pin it with: pip install "datafog=={LAST_SUPPORTED}". '
18
+ "See docs/retirement.rst.",
15
19
  LegacySurfaceWarning,
16
20
  stacklevel=3,
17
21
  )
@@ -6,6 +6,7 @@ not include the deprecated detect/process shims.
6
6
 
7
7
  from ..engine import Entity, RedactResult, ScanResult
8
8
  from ..engine import redact as _redact_entities
9
+ from ..engine import resolve_backend
9
10
  from ..engine import scan as _scan
10
11
  from ..engine import scan_and_redact as _scan_and_redact
11
12
 
@@ -29,13 +30,13 @@ def scan(
29
30
  allowlist: list[str] | None = None,
30
31
  allowlist_patterns: list[str] | None = None,
31
32
  *,
32
- backend: str = "python",
33
+ backend: str | None = None,
33
34
  ) -> ScanResult:
34
35
  """
35
- Scan with the legacy result schema; Rust detection is opt-in.
36
+ Scan with the legacy result schema.
36
37
 
37
- Defaults to the lightweight regex engine so the core install works without
38
- optional dependency fallback warnings.
38
+ Backend selection follows ``datafog.engine.resolve_backend``: native when
39
+ ``datafog-core`` is importable, otherwise Python with a one-time warning.
39
40
 
40
41
  ``allowlist`` exempts exact entity texts (your own support address, doc
41
42
  placeholders); ``allowlist_patterns`` exempts entities whose full text
@@ -64,7 +65,7 @@ def redact(
64
65
  allowlist: list[str] | None = None,
65
66
  allowlist_patterns: list[str] | None = None,
66
67
  *,
67
- backend: str = "python",
68
+ backend: str | None = None,
68
69
  ) -> RedactResult:
69
70
  """
70
71
  Redact with legacy strategies and result schema.
@@ -75,8 +76,8 @@ def redact(
75
76
  full-text regex match respectively); they apply to the scan path and are
76
77
  rejected when explicit ``entities`` are supplied.
77
78
  """
78
- if backend not in ("python", "rust"):
79
- raise ValueError("backend must be one of: python, rust")
79
+ # Validate the backend even when explicit entities skip the scan path.
80
+ resolve_backend(backend, engine)
80
81
 
81
82
  if preset is not None:
82
83
  try: