cloakrs 0.1.0a1__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 (61) hide show
  1. cloakrs-0.1.0a1/Cargo.toml +46 -0
  2. cloakrs-0.1.0a1/LICENSE.md +21 -0
  3. cloakrs-0.1.0a1/PKG-INFO +190 -0
  4. cloakrs-0.1.0a1/README.md +169 -0
  5. cloakrs-0.1.0a1/bindings/python/CHANGELOG.md +13 -0
  6. cloakrs-0.1.0a1/bindings/python/Cargo.lock +542 -0
  7. cloakrs-0.1.0a1/bindings/python/Cargo.toml +23 -0
  8. cloakrs-0.1.0a1/bindings/python/LICENSE.md +21 -0
  9. cloakrs-0.1.0a1/bindings/python/README.md +169 -0
  10. cloakrs-0.1.0a1/bindings/python/release.py +144 -0
  11. cloakrs-0.1.0a1/bindings/python/src/lib.rs +383 -0
  12. cloakrs-0.1.0a1/bindings/python/tests/installed_smoke.py +44 -0
  13. cloakrs-0.1.0a1/bindings/python/tests/test_sanitizer.py +301 -0
  14. cloakrs-0.1.0a1/bindings/python/tests/test_scanner.py +175 -0
  15. cloakrs-0.1.0a1/bindings/python/tests/typing_smoke.py +25 -0
  16. cloakrs-0.1.0a1/crates/cloakrs-core/Cargo.toml +26 -0
  17. cloakrs-0.1.0a1/crates/cloakrs-core/README.md +192 -0
  18. cloakrs-0.1.0a1/crates/cloakrs-core/src/context.rs +244 -0
  19. cloakrs-0.1.0a1/crates/cloakrs-core/src/error.rs +51 -0
  20. cloakrs-0.1.0a1/crates/cloakrs-core/src/finding.rs +380 -0
  21. cloakrs-0.1.0a1/crates/cloakrs-core/src/lib.rs +47 -0
  22. cloakrs-0.1.0a1/crates/cloakrs-core/src/masker.rs +923 -0
  23. cloakrs-0.1.0a1/crates/cloakrs-core/src/prompt.rs +986 -0
  24. cloakrs-0.1.0a1/crates/cloakrs-core/src/recognizer.rs +200 -0
  25. cloakrs-0.1.0a1/crates/cloakrs-core/src/scanner.rs +594 -0
  26. cloakrs-0.1.0a1/crates/cloakrs-locales/Cargo.toml +19 -0
  27. cloakrs-0.1.0a1/crates/cloakrs-locales/README.md +192 -0
  28. cloakrs-0.1.0a1/crates/cloakrs-locales/src/br_br.rs +404 -0
  29. cloakrs-0.1.0a1/crates/cloakrs-locales/src/common.rs +45 -0
  30. cloakrs-0.1.0a1/crates/cloakrs-locales/src/de_de.rs +256 -0
  31. cloakrs-0.1.0a1/crates/cloakrs-locales/src/en_gb.rs +418 -0
  32. cloakrs-0.1.0a1/crates/cloakrs-locales/src/eu.rs +59 -0
  33. cloakrs-0.1.0a1/crates/cloakrs-locales/src/fr_fr.rs +285 -0
  34. cloakrs-0.1.0a1/crates/cloakrs-locales/src/in_in.rs +404 -0
  35. cloakrs-0.1.0a1/crates/cloakrs-locales/src/lib.rs +78 -0
  36. cloakrs-0.1.0a1/crates/cloakrs-locales/src/nl_nl.rs +182 -0
  37. cloakrs-0.1.0a1/crates/cloakrs-patterns/Cargo.toml +18 -0
  38. cloakrs-0.1.0a1/crates/cloakrs-patterns/README.md +192 -0
  39. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/api_key.rs +434 -0
  40. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/common.rs +51 -0
  41. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/credit_card.rs +281 -0
  42. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/crypto.rs +296 -0
  43. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/date_of_birth.rs +376 -0
  44. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/email.rs +219 -0
  45. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/hostname.rs +363 -0
  46. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/iban.rs +302 -0
  47. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/ip_address.rs +250 -0
  48. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/lib.rs +93 -0
  49. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/mac_address.rs +204 -0
  50. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/person_name.rs +331 -0
  51. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/phone.rs +270 -0
  52. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/physical_address.rs +262 -0
  53. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/ssn.rs +169 -0
  54. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/url.rs +643 -0
  55. cloakrs-0.1.0a1/crates/cloakrs-patterns/src/user_path.rs +384 -0
  56. cloakrs-0.1.0a1/crates/cloakrs-patterns/tests/false_positive.rs +77 -0
  57. cloakrs-0.1.0a1/crates/cloakrs-patterns/tests/scanner_integration.rs +359 -0
  58. cloakrs-0.1.0a1/pyproject.toml +41 -0
  59. cloakrs-0.1.0a1/python/cloakrs/__init__.py +231 -0
  60. cloakrs-0.1.0a1/python/cloakrs/_native.pyi +38 -0
  61. cloakrs-0.1.0a1/python/cloakrs/py.typed +0 -0
@@ -0,0 +1,46 @@
1
+ [workspace]
2
+ members = [
3
+ "crates/cloakrs-core",
4
+ "crates/cloakrs-patterns",
5
+ "crates/cloakrs-locales",
6
+ "crates/cloakrs-adapters",
7
+ "crates/cloakrs-tracing",
8
+ "crates/cloakrs-cli",
9
+ ]
10
+ exclude = ["bindings/python"]
11
+ resolver = "2"
12
+
13
+ [workspace.package]
14
+ version = "0.4.0"
15
+ edition = "2021"
16
+ rust-version = "1.75.0"
17
+ license = "MIT"
18
+ repository = "https://github.com/kadir/cloakrs"
19
+ homepage = "https://github.com/kadir/cloakrs"
20
+ documentation = "https://docs.rs/cloakrs-core"
21
+ readme = "README.md"
22
+ keywords = ["pii", "redaction", "privacy", "masking", "cli"]
23
+ categories = ["command-line-utilities", "text-processing"]
24
+
25
+ [workspace.dependencies]
26
+ aes-gcm = "0.10"
27
+ base64 = "0.22"
28
+ clap = { version = "4", features = ["derive", "env"] }
29
+ criterion = "0.5"
30
+ csv = "1"
31
+ indicatif = "0.17"
32
+ ignore = "0.4"
33
+ once_cell = "1"
34
+ proptest = "1"
35
+ rayon = "1"
36
+ regex = "1"
37
+ serde = { version = "1", features = ["derive"] }
38
+ serde_json = "1"
39
+ same-file = "1"
40
+ tempfile = "3"
41
+ sha2 = "0.10"
42
+ thiserror = "2"
43
+ tokio = { version = "1", features = ["full"] }
44
+ toml = "0.8"
45
+ tracing = "0.1"
46
+ tracing-subscriber = "0.3"
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cloakrs contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: cloakrs
3
+ Version: 0.1.0a1
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Programming Language :: Python :: 3
6
+ Classifier: Programming Language :: Python :: 3.11
7
+ Classifier: Programming Language :: Python :: 3.12
8
+ Classifier: Programming Language :: Python :: 3.13
9
+ Classifier: Programming Language :: Python :: 3.14
10
+ Classifier: Programming Language :: Python :: Implementation :: CPython
11
+ Classifier: Programming Language :: Rust
12
+ License-File: LICENSE.md
13
+ Summary: Local PII detection, redaction, and prompt sanitization powered by Rust
14
+ License-Expression: MIT
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
17
+ Project-URL: Homepage, https://github.com/kadir/cloakrs
18
+ Project-URL: Issues, https://github.com/kadir/cloakrs/issues
19
+ Project-URL: Repository, https://github.com/kadir/cloakrs
20
+
21
+ # cloakrs for Python
22
+
23
+ An alpha Python interface to the cloakrs 0.4.0 Rust engine, with local scanning,
24
+ redaction, and reversible prompt sanitization. Input stays in your process;
25
+ the library makes no network requests and does not invoke the CLI.
26
+
27
+ ## Install the prerelease
28
+
29
+ ```sh
30
+ python -m pip install --only-binary=:all: 'cloakrs==0.1.0a1'
31
+ ```
32
+
33
+ Requires regular CPython 3.11–3.14. Wheels include the compiled Rust engine;
34
+ installation needs no Rust compiler or additional Python runtime dependencies.
35
+
36
+ | Platform | Wheel support |
37
+ | --- | --- |
38
+ | Linux with glibc 2.17+ | x86_64 and ARM64 |
39
+ | macOS 11+ | Intel and Apple Silicon |
40
+ | Windows | x86_64 |
41
+
42
+ Alpine/musl, Windows ARM64, free-threaded Python, and alternative interpreters
43
+ do not have validated wheels. A source install requires Rust 1.83 or newer:
44
+ `python -m pip install --no-binary=cloakrs 'cloakrs==0.1.0a1'`.
45
+ This is an early prerelease; evaluate it on representative data before adoption.
46
+
47
+ ## Use
48
+
49
+ ```python
50
+ from cloakrs import Scanner
51
+
52
+ scanner = Scanner(locale="us")
53
+ result = scanner.scan("Email jane@example.com")
54
+ assert result.masked_text == "Email [EMAIL]"
55
+
56
+ finding = result.findings[0]
57
+ assert finding.entity_type == "email"
58
+ assert finding.text == "jane@example.com"
59
+ assert scanner.mask("Email jane@example.com") == "Email [EMAIL]"
60
+ ```
61
+
62
+ `Scanner` and `Sanitizer` accept the same keyword-only options:
63
+
64
+ | Option | Default | Meaning |
65
+ | --- | --- | --- |
66
+ | `locale` | `"universal"` | One of `universal`, `us`, `uk`, `nl`, `de`, `fr`, `in`, `br`, `eu` |
67
+ | `min_confidence` | `0.0` | Finite threshold from 0 to 1, matching the Rust library default |
68
+ | `exclude_entities` | `()` | Sequence of canonical entity names; see `cloakrs.ENTITY_TYPES` |
69
+ | `allow_list` | `()` | Literal values whose overlapping findings are suppressed |
70
+ | `deny_list` | `()` | Literal values added as `deny-list` findings |
71
+
72
+ All options are explicit; no configuration files are discovered. Duplicate
73
+ exclusions have no additional effect. Deny-list rules survive entity exclusions;
74
+ allow-list precedence still applies to deny-list findings. `passport-number` and
75
+ `drivers-license` are reserved entity names without bundled recognizers.
76
+
77
+ ```python
78
+ scanner = Scanner(locale="us", exclude_entities=["url"])
79
+ assert scanner.mask("https://example.com?email=jane%40example.com") == (
80
+ "https://example.com?email=[EMAIL]"
81
+ )
82
+ ```
83
+
84
+ Excluding URLs can leave embedded credentials visible, including userinfo
85
+ passwords and unsupported encoded query secrets. Default URL detection remains
86
+ enabled. Pattern-based detection does not guarantee that arbitrary text contains
87
+ no secrets; choose a locale and check representative input for your application.
88
+
89
+ ## Sanitize a prompt and restore a response
90
+
91
+ ```python
92
+ from cloakrs import Mapping, Sanitizer
93
+
94
+ sanitizer = Sanitizer(locale="us")
95
+ clean, mapping = sanitizer.sanitize("Email jane@example.com")
96
+ assert clean == "Email [EMAIL_1]"
97
+ assert len(mapping) == 1
98
+ assert repr(mapping) == "Mapping(entries=1)"
99
+
100
+ # Send only clean to your model; keep mapping inside your application.
101
+ response = "Reply to [ email_1 ]"
102
+ assert mapping.restore(response) == "Reply to jane@example.com"
103
+ assert mapping.restore(response, strict=True) == response
104
+
105
+ # Explicit export contains original values. Keep this JSON private too.
106
+ mapping_json = mapping.to_json()
107
+ loaded = Mapping.from_json(mapping_json)
108
+ assert loaded.restore(clean) == "Email jane@example.com"
109
+ ```
110
+
111
+ Use `sanitize(text, placeholder_style="braces")` for `{EMAIL_1}` placeholders;
112
+ the default is `"brackets"`. Repeated values of the same entity type share one
113
+ placeholder within a call. Existing placeholders are preserved, with new ones
114
+ using available numbers. Each call returns an independent mapping. Nested
115
+ findings are replaced once at their outermost span.
116
+
117
+ Restoration tolerates ASCII case changes and whitespace immediately inside
118
+ numbered placeholders. `strict=True` requires exact spelling. Unknown
119
+ placeholders remain untouched, and restored values are not expanded recursively.
120
+
121
+ `Mapping` is opaque: its representation shows only the entry count, and pickling
122
+ is disabled. It does not automatically write files or log values. `to_json()`
123
+ explicitly exports sensitive originals using the Rust CLI mapping schema;
124
+ `from_json()` can import files produced by `cloakrs sanitize`. The CLI's
125
+ `cloakrs restore` can read Python exports. Mapping spans remain **UTF-8 byte
126
+ offsets** of the first occurrence, while Python `Finding` indices count code
127
+ points. JSON import rejects invalid schema, empty or ambiguous duplicate
128
+ placeholders, confidence outside 0–1, and spans whose byte lengths do not match
129
+ their originals. Unknown extra JSON fields are ignored, as in the Rust CLI.
130
+
131
+ ## Results and errors
132
+
133
+ `ScanResult` and `Finding` are immutable typed dataclasses. Findings include
134
+ `entity_type`, `start`, `end`, `confidence`, `recognizer_id`, and `text`.
135
+ `source[finding.start:finding.end]` selects the matched original text. Indices
136
+ count Python Unicode code points, not Rust UTF-8 bytes or visual graphemes.
137
+ Nested URL findings can overlap; use `masked_text` for Rust's redaction policy.
138
+
139
+ Representations omit original and masked text. Accessing `.text`, `.masked_text`,
140
+ or explicitly serializing a result can expose sensitive values. Masked text may
141
+ still contain unsupported or intentionally excluded values.
142
+
143
+ Unknown locale, entity, or placeholder-style names and invalid confidence values
144
+ raise `ValueError`; wrong argument types raise `TypeError`. Lone Unicode
145
+ surrogates are rejected with `ValueError`.
146
+ Internal scanning failures raise a generic `RuntimeError` without source values.
147
+
148
+ Reuse scanners and sanitizers for repeated calls. Scanning, sanitization,
149
+ restoration, and mapping JSON processing release the Python interpreter lock
150
+ while Rust runs. Instances can be shared between Python threads.
151
+ `mask()` avoids allocating Python finding objects when only redacted text is needed.
152
+
153
+ ## Build and test from this repository
154
+
155
+ Requires regular CPython 3.11–3.14 and Rust 1.83 or newer for building this binding.
156
+ The Rust library and CLI retain their independent Rust 1.75 minimum. A built wheel
157
+ does not require a Rust compiler or additional Python runtime dependencies.
158
+ Free-threaded Python and alternative interpreters are not validated yet.
159
+
160
+ From the repository root:
161
+
162
+ ```sh
163
+ python3 -m venv .venv-python
164
+ # Linux/macOS; on Windows activate .venv-python\Scripts\Activate.ps1 instead.
165
+ . .venv-python/bin/activate
166
+ python -m pip install 'maturin>=1.15,<2' 'pytest>=8,<10' 'mypy>=1.15,<3'
167
+ cd bindings/python
168
+ maturin build --release --locked --out dist
169
+ python -m pip install --no-deps --force-reinstall dist/*.whl
170
+ cargo build --manifest-path ../../Cargo.toml --locked -p cloakrs-cli
171
+ python -m pytest tests
172
+ python -m mypy python/cloakrs tests/typing_smoke.py
173
+ ```
174
+
175
+ In PowerShell, install the wheel with
176
+ `python -m pip install --no-deps --force-reinstall (Get-ChildItem dist/*.whl)`.
177
+ The test suite runs against the installed wheel and compares all 58 existing
178
+ evaluation cases against the Rust detection snapshot. It also checks exact
179
+ sanitizer round trips in both placeholder styles and exchanges mappings with the
180
+ real CLI in both directions. These tests require the repository checkout and the
181
+ CLI built above. Python binding changes have a separate CI workflow.
182
+
183
+ ## Releasing
184
+
185
+ Python releases use `python-v<version>` tags and the dedicated
186
+ `python-release.yml` workflow with PyPI Trusted Publishing. Branch pushes and
187
+ pull requests validate packages without publishing. See the repository's
188
+ [Python release guide](https://github.com/kadir/cloakrs/blob/master/docs/python-releases.md)
189
+ for account setup, artifact checks, tagging, and verification after upload.
190
+
@@ -0,0 +1,169 @@
1
+ # cloakrs for Python
2
+
3
+ An alpha Python interface to the cloakrs 0.4.0 Rust engine, with local scanning,
4
+ redaction, and reversible prompt sanitization. Input stays in your process;
5
+ the library makes no network requests and does not invoke the CLI.
6
+
7
+ ## Install the prerelease
8
+
9
+ ```sh
10
+ python -m pip install --only-binary=:all: 'cloakrs==0.1.0a1'
11
+ ```
12
+
13
+ Requires regular CPython 3.11–3.14. Wheels include the compiled Rust engine;
14
+ installation needs no Rust compiler or additional Python runtime dependencies.
15
+
16
+ | Platform | Wheel support |
17
+ | --- | --- |
18
+ | Linux with glibc 2.17+ | x86_64 and ARM64 |
19
+ | macOS 11+ | Intel and Apple Silicon |
20
+ | Windows | x86_64 |
21
+
22
+ Alpine/musl, Windows ARM64, free-threaded Python, and alternative interpreters
23
+ do not have validated wheels. A source install requires Rust 1.83 or newer:
24
+ `python -m pip install --no-binary=cloakrs 'cloakrs==0.1.0a1'`.
25
+ This is an early prerelease; evaluate it on representative data before adoption.
26
+
27
+ ## Use
28
+
29
+ ```python
30
+ from cloakrs import Scanner
31
+
32
+ scanner = Scanner(locale="us")
33
+ result = scanner.scan("Email jane@example.com")
34
+ assert result.masked_text == "Email [EMAIL]"
35
+
36
+ finding = result.findings[0]
37
+ assert finding.entity_type == "email"
38
+ assert finding.text == "jane@example.com"
39
+ assert scanner.mask("Email jane@example.com") == "Email [EMAIL]"
40
+ ```
41
+
42
+ `Scanner` and `Sanitizer` accept the same keyword-only options:
43
+
44
+ | Option | Default | Meaning |
45
+ | --- | --- | --- |
46
+ | `locale` | `"universal"` | One of `universal`, `us`, `uk`, `nl`, `de`, `fr`, `in`, `br`, `eu` |
47
+ | `min_confidence` | `0.0` | Finite threshold from 0 to 1, matching the Rust library default |
48
+ | `exclude_entities` | `()` | Sequence of canonical entity names; see `cloakrs.ENTITY_TYPES` |
49
+ | `allow_list` | `()` | Literal values whose overlapping findings are suppressed |
50
+ | `deny_list` | `()` | Literal values added as `deny-list` findings |
51
+
52
+ All options are explicit; no configuration files are discovered. Duplicate
53
+ exclusions have no additional effect. Deny-list rules survive entity exclusions;
54
+ allow-list precedence still applies to deny-list findings. `passport-number` and
55
+ `drivers-license` are reserved entity names without bundled recognizers.
56
+
57
+ ```python
58
+ scanner = Scanner(locale="us", exclude_entities=["url"])
59
+ assert scanner.mask("https://example.com?email=jane%40example.com") == (
60
+ "https://example.com?email=[EMAIL]"
61
+ )
62
+ ```
63
+
64
+ Excluding URLs can leave embedded credentials visible, including userinfo
65
+ passwords and unsupported encoded query secrets. Default URL detection remains
66
+ enabled. Pattern-based detection does not guarantee that arbitrary text contains
67
+ no secrets; choose a locale and check representative input for your application.
68
+
69
+ ## Sanitize a prompt and restore a response
70
+
71
+ ```python
72
+ from cloakrs import Mapping, Sanitizer
73
+
74
+ sanitizer = Sanitizer(locale="us")
75
+ clean, mapping = sanitizer.sanitize("Email jane@example.com")
76
+ assert clean == "Email [EMAIL_1]"
77
+ assert len(mapping) == 1
78
+ assert repr(mapping) == "Mapping(entries=1)"
79
+
80
+ # Send only clean to your model; keep mapping inside your application.
81
+ response = "Reply to [ email_1 ]"
82
+ assert mapping.restore(response) == "Reply to jane@example.com"
83
+ assert mapping.restore(response, strict=True) == response
84
+
85
+ # Explicit export contains original values. Keep this JSON private too.
86
+ mapping_json = mapping.to_json()
87
+ loaded = Mapping.from_json(mapping_json)
88
+ assert loaded.restore(clean) == "Email jane@example.com"
89
+ ```
90
+
91
+ Use `sanitize(text, placeholder_style="braces")` for `{EMAIL_1}` placeholders;
92
+ the default is `"brackets"`. Repeated values of the same entity type share one
93
+ placeholder within a call. Existing placeholders are preserved, with new ones
94
+ using available numbers. Each call returns an independent mapping. Nested
95
+ findings are replaced once at their outermost span.
96
+
97
+ Restoration tolerates ASCII case changes and whitespace immediately inside
98
+ numbered placeholders. `strict=True` requires exact spelling. Unknown
99
+ placeholders remain untouched, and restored values are not expanded recursively.
100
+
101
+ `Mapping` is opaque: its representation shows only the entry count, and pickling
102
+ is disabled. It does not automatically write files or log values. `to_json()`
103
+ explicitly exports sensitive originals using the Rust CLI mapping schema;
104
+ `from_json()` can import files produced by `cloakrs sanitize`. The CLI's
105
+ `cloakrs restore` can read Python exports. Mapping spans remain **UTF-8 byte
106
+ offsets** of the first occurrence, while Python `Finding` indices count code
107
+ points. JSON import rejects invalid schema, empty or ambiguous duplicate
108
+ placeholders, confidence outside 0–1, and spans whose byte lengths do not match
109
+ their originals. Unknown extra JSON fields are ignored, as in the Rust CLI.
110
+
111
+ ## Results and errors
112
+
113
+ `ScanResult` and `Finding` are immutable typed dataclasses. Findings include
114
+ `entity_type`, `start`, `end`, `confidence`, `recognizer_id`, and `text`.
115
+ `source[finding.start:finding.end]` selects the matched original text. Indices
116
+ count Python Unicode code points, not Rust UTF-8 bytes or visual graphemes.
117
+ Nested URL findings can overlap; use `masked_text` for Rust's redaction policy.
118
+
119
+ Representations omit original and masked text. Accessing `.text`, `.masked_text`,
120
+ or explicitly serializing a result can expose sensitive values. Masked text may
121
+ still contain unsupported or intentionally excluded values.
122
+
123
+ Unknown locale, entity, or placeholder-style names and invalid confidence values
124
+ raise `ValueError`; wrong argument types raise `TypeError`. Lone Unicode
125
+ surrogates are rejected with `ValueError`.
126
+ Internal scanning failures raise a generic `RuntimeError` without source values.
127
+
128
+ Reuse scanners and sanitizers for repeated calls. Scanning, sanitization,
129
+ restoration, and mapping JSON processing release the Python interpreter lock
130
+ while Rust runs. Instances can be shared between Python threads.
131
+ `mask()` avoids allocating Python finding objects when only redacted text is needed.
132
+
133
+ ## Build and test from this repository
134
+
135
+ Requires regular CPython 3.11–3.14 and Rust 1.83 or newer for building this binding.
136
+ The Rust library and CLI retain their independent Rust 1.75 minimum. A built wheel
137
+ does not require a Rust compiler or additional Python runtime dependencies.
138
+ Free-threaded Python and alternative interpreters are not validated yet.
139
+
140
+ From the repository root:
141
+
142
+ ```sh
143
+ python3 -m venv .venv-python
144
+ # Linux/macOS; on Windows activate .venv-python\Scripts\Activate.ps1 instead.
145
+ . .venv-python/bin/activate
146
+ python -m pip install 'maturin>=1.15,<2' 'pytest>=8,<10' 'mypy>=1.15,<3'
147
+ cd bindings/python
148
+ maturin build --release --locked --out dist
149
+ python -m pip install --no-deps --force-reinstall dist/*.whl
150
+ cargo build --manifest-path ../../Cargo.toml --locked -p cloakrs-cli
151
+ python -m pytest tests
152
+ python -m mypy python/cloakrs tests/typing_smoke.py
153
+ ```
154
+
155
+ In PowerShell, install the wheel with
156
+ `python -m pip install --no-deps --force-reinstall (Get-ChildItem dist/*.whl)`.
157
+ The test suite runs against the installed wheel and compares all 58 existing
158
+ evaluation cases against the Rust detection snapshot. It also checks exact
159
+ sanitizer round trips in both placeholder styles and exchanges mappings with the
160
+ real CLI in both directions. These tests require the repository checkout and the
161
+ CLI built above. Python binding changes have a separate CI workflow.
162
+
163
+ ## Releasing
164
+
165
+ Python releases use `python-v<version>` tags and the dedicated
166
+ `python-release.yml` workflow with PyPI Trusted Publishing. Branch pushes and
167
+ pull requests validate packages without publishing. See the repository's
168
+ [Python release guide](https://github.com/kadir/cloakrs/blob/master/docs/python-releases.md)
169
+ for account setup, artifact checks, tagging, and verification after upload.
@@ -0,0 +1,13 @@
1
+ # Python changelog
2
+
3
+ ## 0.1.0a1
4
+
5
+ First Python prerelease, powered by the cloakrs 0.4.0 Rust engine.
6
+
7
+ - Local scanning and masking with typed findings and Python string indices.
8
+ - Prompt sanitization with bracket/brace placeholders and tolerant or strict restoration.
9
+ - Explicit mapping JSON import/export interoperable with the Rust CLI.
10
+ - Locale, confidence, entity exclusion, and literal allow/deny options.
11
+ - Sensitive values omitted from mapping representations; automatic mapping pickling disabled.
12
+ - CPython 3.11–3.14 wheels for Linux x86_64/ARM64, macOS Intel/Apple Silicon,
13
+ and Windows x86_64, plus a source distribution.