pydantic-anonymizer 0.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 drawiks
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,270 @@
1
+ Metadata-Version: 2.4
2
+ Name: pydantic-anonymizer
3
+ Version: 0.1.0
4
+ Summary: anonymize sensitive data in pydantic models
5
+ Author-email: drawiks <drawksr@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/drawiks/pydantic-anonymizer
8
+ Project-URL: Repository, https://github.com/drawiks/pydantic-anonymizer
9
+ Project-URL: Issues, https://github.com/drawiks/pydantic-anonymizer/issues
10
+ Keywords: pydantic,anonymizer,masking,privacy,gdpr
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: pydantic>=2.0
23
+ Requires-Dist: phonenumbers>=8.13
24
+ Dynamic: license-file
25
+
26
+ <div align="center">
27
+ <h1>πŸ”’ pydantic-anonymizer</h1>
28
+ <a href="https://pypi.org/project/pydantic-anonymizer/">
29
+ <img alt="PyPI version" src="https://img.shields.io/pypi/v/pydantic-anonymizer?color=blue">
30
+ </a>
31
+ <img height="20" alt="Python 3.9+" src="https://img.shields.io/badge/python-3.9+-blue">
32
+ <img height="20" alt="License MIT" src="https://img.shields.io/badge/license-MIT-green">
33
+ <img height="20" alt="Status" src="https://img.shields.io/badge/status-stable-brightgreen">
34
+ <p><strong>mask sensitive data in Pydantic models</strong></p>
35
+ <p><a href="README.md">Русский</a></p>
36
+ </div>
37
+
38
+ ---
39
+
40
+ ```
41
+ ,--. ,--. ,--.
42
+ ,---.,--. ,--.,-| | ,--,--.,--,--, ,-' '-.`--' ,---.
43
+ | .-. |\ ' /' .-. |' ,-. || \'-. .-',--.| .--'
44
+ | '-' ' \ ' \ `-' |\ '-' || || | | | | |\ `--.
45
+ | |-'.-' / `---' `--`--'`--''--' `--' `--' `---'
46
+ `--' `---'
47
+
48
+ ,--.
49
+ ,--,--.,--,--, ,---. ,--,--, ,--. ,--.,--,--,--.`--',-----. ,---. ,--.--.
50
+ ' ,-. || \| .-. || \ \ ' / | |,--.`-. / | .-. :| .--'
51
+ \ '-' || || |' '-' '| || | \ ' | | | || | / `-.\ --.| |
52
+ `--`--'`--''--' `---' `--''--'.-' / `--`--`--'`--'`-----' `----'`--'
53
+ `---'
54
+ ```
55
+
56
+ ## **πŸ“¦ installation**
57
+
58
+ ```bash
59
+ pip install pydantic-anonymizer
60
+ ```
61
+
62
+ ---
63
+
64
+ ## **πŸ“‘ quick start**
65
+
66
+ ```python
67
+ from pydantic import BaseModel, Field
68
+ from pydantic_anonymizer import Anonymizer
69
+
70
+ class UserProfile(BaseModel, Anonymizer):
71
+ username: str
72
+ email: str = Field(json_schema_extra={"anonymize": True})
73
+ card_number: str = Field(json_schema_extra={"anonymize": "card"})
74
+ phone_number: str = Field(json_schema_extra={"anonymize": "phone"})
75
+
76
+ user = UserProfile(
77
+ username="ivan_dev",
78
+ email="ivan@mail.com",
79
+ card_number="4242111122223333",
80
+ phone_number="+380500223785"
81
+ )
82
+
83
+ print(user.model_dump())
84
+ # {'username': 'ivan_dev', 'email': 'ivan@mail.com', 'card_number': '4242111122223333', 'phone_number': '+380500223785'}
85
+
86
+ print(user.model_dump_anonymized())
87
+ # {'username': 'ivan_dev', 'email': 'i***@***.com', 'card_number': '4242-****-****-3333', 'phone_number': '+380 (***) ***-**-85'}
88
+ ```
89
+
90
+ ---
91
+
92
+ ## **🧩 features**
93
+
94
+ - πŸ” **automatic masking** - configure via `json_schema_extra` in model fields
95
+ - πŸ“§ **generic masking** - partial mask for emails and text (`i***@***.com`)
96
+ - πŸ’³ **card masking** - format `4242-****-****-3333`
97
+ - πŸ“± **phone masking** - correct country code parsing with [phonenumbers](https://github.com/daviddryden/python-phonenumbers)
98
+ - πŸ—οΈ **nested models** - recursive processing of nested Pydantic models
99
+ - πŸ“‹ **lists** - support for `list[Model]` with masking of each element
100
+ - πŸ› οΈ **custom strategies** - your own masking functions via `MaskRegistry`
101
+ - βœ… **reliable** - 27 tests covering all scenarios
102
+ - πŸͺΆ **minimal dependencies** - only `pydantic>=2.0` and `phonenumbers>=8.13`
103
+
104
+ ---
105
+
106
+ ## **πŸ“– usage**
107
+
108
+ ### basic usage
109
+
110
+ ```python
111
+ from pydantic import BaseModel, Field
112
+ from pydantic_anonymizer import Anonymizer
113
+
114
+ class User(BaseModel, Anonymizer):
115
+ name: str
116
+ email: str = Field(json_schema_extra={"anonymize": True})
117
+
118
+ user = User(name="ivan", email="ivan@mail.com")
119
+
120
+ # original data
121
+ user.model_dump() # {'name': 'ivan', 'email': 'ivan@mail.com'}
122
+
123
+ # masked data
124
+ user.model_dump_anonymized() # {'name': 'ivan', 'email': 'i***@***.com'}
125
+
126
+ # JSON string
127
+ user.model_dump_json_anonymized() # '{"name": "ivan", "email": "i***@***.com"}'
128
+ ```
129
+
130
+ ### nested models
131
+
132
+ ```python
133
+ from pydantic import BaseModel, Field
134
+ from pydantic_anonymizer import Anonymizer
135
+
136
+ class Address(BaseModel, Anonymizer):
137
+ city: str
138
+ street: str = Field(json_schema_extra={"anonymize": True})
139
+
140
+ class UserProfile(BaseModel, Anonymizer):
141
+ name: str
142
+ address: Address
143
+
144
+ user = UserProfile(
145
+ name="ivan",
146
+ address=Address(city="Moscow", street="Lenina 1")
147
+ )
148
+
149
+ result = user.model_dump_anonymized()
150
+ # {'name': 'ivan', 'address': {'city': 'Moscow', 'street': 'L***a 1'}}
151
+ ```
152
+
153
+ ### lists of models
154
+
155
+ ```python
156
+ from pydantic import BaseModel, Field
157
+ from pydantic_anonymizer import Anonymizer
158
+
159
+ class Card(BaseModel, Anonymizer):
160
+ number: str = Field(json_schema_extra={"anonymize": "card"})
161
+
162
+ class Wallet(BaseModel, Anonymizer):
163
+ cards: list[Card]
164
+
165
+ wallet = Wallet(cards=[
166
+ Card(number="4242111122223333"),
167
+ Card(number="5555666677778888")
168
+ ])
169
+
170
+ result = wallet.model_dump_anonymized()
171
+ # {'cards': [{'number': '4242-****-****-3333'}, {'number': '5555-****-****-8888'}]}
172
+ ```
173
+
174
+ ### custom strategies
175
+
176
+ ```python
177
+ from pydantic import BaseModel, Field
178
+ from pydantic_anonymizer import Anonymizer, MaskRegistry
179
+
180
+ # register your own masking function
181
+ def mask_ssn(value: str) -> str:
182
+ return "***-**-" + value[-4:]
183
+
184
+ MaskRegistry.register("ssn", mask_ssn)
185
+
186
+ class Person(BaseModel, Anonymizer):
187
+ ssn: str = Field(json_schema_extra={"anonymize": "ssn"})
188
+
189
+ person = Person(ssn="123-45-6789")
190
+ person.model_dump_anonymized() # {'ssn': '***-**-6789'}
191
+ ```
192
+
193
+ ---
194
+
195
+ ## **🎭 built-in strategies**
196
+
197
+ | Strategy | Field | Input | Output |
198
+ |----------|-------|-------|--------|
199
+ | `True` (generic) | email | `ivan@mail.com` | `i***@***.com` |
200
+ | `"card"` | card number | `4242111122223333` | `4242-****-****-3333` |
201
+ | `"phone"` | phone | `+380500223785` | `+380 (***) ***-**-85` |
202
+
203
+ ### country code support
204
+
205
+ phone masking works correctly with any country codes:
206
+
207
+ | Country | Code | Example |
208
+ |---------|------|---------|
209
+ | Ukraine | +380 | `+380500223785` β†’ `+380 (***) ***-**-85` |
210
+ | USA | +1 | `+14155552671` β†’ `+1 (***) ***-**-71` |
211
+ | UK | +44 | `+447911123456` β†’ `+44 (***) ***-**-56` |
212
+ | Russia | +7 | `+79161234567` β†’ `+7 (***) ***-**-67` |
213
+ | Germany | +49 | `+4915112345678` β†’ `+49 (***) ***-**-78` |
214
+ | China | +86 | `+8613812345678` β†’ `+86 (***) ***-**-78` |
215
+
216
+ ---
217
+
218
+ ## **πŸ“ examples**
219
+
220
+ ### FastAPI logging
221
+
222
+ ```python
223
+ from fastapi import FastAPI
224
+ from pydantic import BaseModel, Field
225
+ from pydantic_anonymizer import Anonymizer
226
+
227
+ app = FastAPI()
228
+
229
+ class UserCreate(BaseModel, Anonymizer):
230
+ username: str
231
+ email: str = Field(json_schema_extra={"anonymize": True})
232
+ card_number: str = Field(json_schema_extra={"anonymize": "card"})
233
+
234
+ @app.post("/users")
235
+ def create_user(user: UserCreate):
236
+ # log with masking
237
+ print(user.model_dump_anonymized())
238
+ # {'username': 'admin', 'email': 'a***@***.com', 'card_number': '4242-****-****-3333'}
239
+
240
+ # original data for processing
241
+ return user.model_dump()
242
+ ```
243
+
244
+ ### safe logging
245
+
246
+ ```python
247
+ import logging
248
+ from pydantic import BaseModel, Field
249
+ from pydantic_anonymizer import Anonymizer
250
+
251
+ logging.basicConfig(level=logging.INFO)
252
+ logger = logging.getLogger(__name__)
253
+
254
+ class Payment(BaseModel, Anonymizer):
255
+ card_number: str = Field(json_schema_extra={"anonymize": "card"})
256
+ amount: float
257
+
258
+ def process_payment(payment: Payment):
259
+ # safe to log - card number is masked
260
+ logger.info("payment: %s", payment.model_dump_anonymized())
261
+
262
+ # work with original data
263
+ charge_card(payment.card_number, payment.amount)
264
+ ```
265
+
266
+ ---
267
+
268
+ ## **πŸ“œ license**
269
+
270
+ [MIT](https://github.com/drawiks/pydantic-anonymizer/blob/main/LICENSE)
@@ -0,0 +1,245 @@
1
+ <div align="center">
2
+ <h1>πŸ”’ pydantic-anonymizer</h1>
3
+ <a href="https://pypi.org/project/pydantic-anonymizer/">
4
+ <img alt="PyPI version" src="https://img.shields.io/pypi/v/pydantic-anonymizer?color=blue">
5
+ </a>
6
+ <img height="20" alt="Python 3.9+" src="https://img.shields.io/badge/python-3.9+-blue">
7
+ <img height="20" alt="License MIT" src="https://img.shields.io/badge/license-MIT-green">
8
+ <img height="20" alt="Status" src="https://img.shields.io/badge/status-stable-brightgreen">
9
+ <p><strong>маскированиС ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ… Π² Pydantic модСлях</strong></p>
10
+ <p><a href="README_en.md">English</a></p>
11
+ </div>
12
+
13
+ ---
14
+
15
+ ```
16
+ ,--. ,--. ,--.
17
+ ,---.,--. ,--.,-| | ,--,--.,--,--, ,-' '-.`--' ,---.
18
+ | .-. |\ ' /' .-. |' ,-. || \'-. .-',--.| .--'
19
+ | '-' ' \ ' \ `-' |\ '-' || || | | | | |\ `--.
20
+ | |-'.-' / `---' `--`--'`--''--' `--' `--' `---'
21
+ `--' `---'
22
+
23
+ ,--.
24
+ ,--,--.,--,--, ,---. ,--,--, ,--. ,--.,--,--,--.`--',-----. ,---. ,--.--.
25
+ ' ,-. || \| .-. || \ \ ' / | |,--.`-. / | .-. :| .--'
26
+ \ '-' || || |' '-' '| || | \ ' | | | || | / `-.\ --.| |
27
+ `--`--'`--''--' `---' `--''--'.-' / `--`--`--'`--'`-----' `----'`--'
28
+ `---'
29
+ ```
30
+
31
+ ## **πŸ“¦ установка**
32
+
33
+ ```bash
34
+ pip install pydantic-anonymizer
35
+ ```
36
+
37
+ ---
38
+
39
+ ## **πŸ“‘ быстрый старт**
40
+
41
+ ```python
42
+ from pydantic import BaseModel, Field
43
+ from pydantic_anonymizer import Anonymizer
44
+
45
+ class UserProfile(BaseModel, Anonymizer):
46
+ username: str
47
+ email: str = Field(json_schema_extra={"anonymize": True})
48
+ card_number: str = Field(json_schema_extra={"anonymize": "card"})
49
+ phone_number: str = Field(json_schema_extra={"anonymize": "phone"})
50
+
51
+ user = UserProfile(
52
+ username="ivan_dev",
53
+ email="ivan@mail.com",
54
+ card_number="4242111122223333",
55
+ phone_number="+380500223785"
56
+ )
57
+
58
+ print(user.model_dump())
59
+ # {'username': 'ivan_dev', 'email': 'ivan@mail.com', 'card_number': '4242111122223333', 'phone_number': '+380500223785'}
60
+
61
+ print(user.model_dump_anonymized())
62
+ # {'username': 'ivan_dev', 'email': 'i***@***.com', 'card_number': '4242-****-****-3333', 'phone_number': '+380 (***) ***-**-85'}
63
+ ```
64
+
65
+ ---
66
+
67
+ ## **🧩 возмоТности**
68
+
69
+ - πŸ” **автоматичСская маскировка** - настройка Ρ‡Π΅Ρ€Π΅Π· `json_schema_extra` Π² полях ΠΌΠΎΠ΄Π΅Π»ΠΈ
70
+ - πŸ“§ **generic маскированиС** - частичная маска для email ΠΈ тСкста (`i***@***.com`)
71
+ - πŸ’³ **маскированиС ΠΊΠ°Ρ€Ρ‚** - Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ `4242-****-****-3333`
72
+ - πŸ“± **маскированиС Ρ‚Π΅Π»Π΅Ρ„ΠΎΠ½ΠΎΠ²** - ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹ΠΉ парсинг ΠΊΠΎΠ΄ΠΎΠ² стран благодаря [phonenumbers](https://github.com/daviddryden/python-phonenumbers)
73
+ - πŸ—οΈ **Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Π΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ** - рСкурсивная ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… Pydantic ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ
74
+ - πŸ“‹ **списки** - ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° `list[Model]` с маскированиСм ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
75
+ - πŸ› οΈ **кастомныС стратСгии** - собствСнныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ маскирования Ρ‡Π΅Ρ€Π΅Π· `MaskRegistry`
76
+ - βœ… **Π½Π°Π΄Ρ‘ΠΆΠ½ΠΎΡΡ‚ΡŒ** - 27 тСстов ΠΏΠΎΠΊΡ€Ρ‹Π²Π°ΡŽΡ‚ всС сцСнарии
77
+ - πŸͺΆ **ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ зависимостСй** - Ρ‚ΠΎΠ»ΡŒΠΊΠΎ `pydantic>=2.0` ΠΈ `phonenumbers>=8.13`
78
+
79
+ ---
80
+
81
+ ## **πŸ“– использованиС**
82
+
83
+ ### базовоС использованиС
84
+
85
+ ```python
86
+ from pydantic import BaseModel, Field
87
+ from pydantic_anonymizer import Anonymizer
88
+
89
+ class User(BaseModel, Anonymizer):
90
+ name: str
91
+ email: str = Field(json_schema_extra={"anonymize": True})
92
+
93
+ user = User(name="ivan", email="ivan@mail.com")
94
+
95
+ # ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅
96
+ user.model_dump() # {'name': 'ivan', 'email': 'ivan@mail.com'}
97
+
98
+ # замаскированныС Π΄Π°Π½Π½Ρ‹Π΅
99
+ user.model_dump_anonymized() # {'name': 'ivan', 'email': 'i***@***.com'}
100
+
101
+ # JSON строка
102
+ user.model_dump_json_anonymized() # '{"name": "ivan", "email": "i***@***.com"}'
103
+ ```
104
+
105
+ ### Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Π΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ
106
+
107
+ ```python
108
+ from pydantic import BaseModel, Field
109
+ from pydantic_anonymizer import Anonymizer
110
+
111
+ class Address(BaseModel, Anonymizer):
112
+ city: str
113
+ street: str = Field(json_schema_extra={"anonymize": True})
114
+
115
+ class UserProfile(BaseModel, Anonymizer):
116
+ name: str
117
+ address: Address
118
+
119
+ user = UserProfile(
120
+ name="ivan",
121
+ address=Address(city="Moscow", street="Lenina 1")
122
+ )
123
+
124
+ result = user.model_dump_anonymized()
125
+ # {'name': 'ivan', 'address': {'city': 'Moscow', 'street': 'L***a 1'}}
126
+ ```
127
+
128
+ ### списки модСлСй
129
+
130
+ ```python
131
+ from pydantic import BaseModel, Field
132
+ from pydantic_anonymizer import Anonymizer
133
+
134
+ class Card(BaseModel, Anonymizer):
135
+ number: str = Field(json_schema_extra={"anonymize": "card"})
136
+
137
+ class Wallet(BaseModel, Anonymizer):
138
+ cards: list[Card]
139
+
140
+ wallet = Wallet(cards=[
141
+ Card(number="4242111122223333"),
142
+ Card(number="5555666677778888")
143
+ ])
144
+
145
+ result = wallet.model_dump_anonymized()
146
+ # {'cards': [{'number': '4242-****-****-3333'}, {'number': '5555-****-****-8888'}]}
147
+ ```
148
+
149
+ ### кастомныС стратСгии
150
+
151
+ ```python
152
+ from pydantic import BaseModel, Field
153
+ from pydantic_anonymizer import Anonymizer, MaskRegistry
154
+
155
+ # рСгистрацияθ‡ͺε·±ηš„ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ маскирования
156
+ def mask_ssn(value: str) -> str:
157
+ return "***-**-" + value[-4:]
158
+
159
+ MaskRegistry.register("ssn", mask_ssn)
160
+
161
+ class Person(BaseModel, Anonymizer):
162
+ ssn: str = Field(json_schema_extra={"anonymize": "ssn"})
163
+
164
+ person = Person(ssn="123-45-6789")
165
+ person.model_dump_anonymized() # {'ssn': '***-**-6789'}
166
+ ```
167
+
168
+ ---
169
+
170
+ ## **🎭 встроСнныС стратСгии**
171
+
172
+ | БтратСгия | ПолС | Π’Ρ…ΠΎΠ΄ | Π’Ρ‹Ρ…ΠΎΠ΄ |
173
+ |-----------|------|------|-------|
174
+ | `True` (generic) | email | `ivan@mail.com` | `i***@***.com` |
175
+ | `"card"` | Π½ΠΎΠΌΠ΅Ρ€ ΠΊΠ°Ρ€Ρ‚Ρ‹ | `4242111122223333` | `4242-****-****-3333` |
176
+ | `"phone"` | Ρ‚Π΅Π»Π΅Ρ„ΠΎΠ½ | `+380500223785` | `+380 (***) ***-**-85` |
177
+
178
+ ### ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° ΠΊΠΎΠ΄ΠΎΠ² стран
179
+
180
+ маскированиС Ρ‚Π΅Π»Π΅Ρ„ΠΎΠ½ΠΎΠ² ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ с Π»ΡŽΠ±Ρ‹ΠΌΠΈ ΠΊΠΎΠ΄Π°ΠΌΠΈ стран:
181
+
182
+ | Π‘Ρ‚Ρ€Π°Π½Π° | Код | ΠŸΡ€ΠΈΠΌΠ΅Ρ€ |
183
+ |--------|-----|--------|
184
+ | Π£ΠΊΡ€Π°ΠΈΠ½Π° | +380 | `+380500223785` β†’ `+380 (***) ***-**-85` |
185
+ | БША | +1 | `+14155552671` β†’ `+1 (***) ***-**-71` |
186
+ | ВСликобритания | +44 | `+447911123456` β†’ `+44 (***) ***-**-56` |
187
+ | Россия | +7 | `+79161234567` β†’ `+7 (***) ***-**-67` |
188
+ | ГСрмания | +49 | `+4915112345678` β†’ `+49 (***) ***-**-78` |
189
+ | ΠšΠΈΡ‚Π°ΠΉ | +86 | `+8613812345678` β†’ `+86 (***) ***-**-78` |
190
+
191
+ ---
192
+
193
+ ## **πŸ“ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹**
194
+
195
+ ### Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π² FastAPI
196
+
197
+ ```python
198
+ from fastapi import FastAPI
199
+ from pydantic import BaseModel, Field
200
+ from pydantic_anonymizer import Anonymizer
201
+
202
+ app = FastAPI()
203
+
204
+ class UserCreate(BaseModel, Anonymizer):
205
+ username: str
206
+ email: str = Field(json_schema_extra={"anonymize": True})
207
+ card_number: str = Field(json_schema_extra={"anonymize": "card"})
208
+
209
+ @app.post("/users")
210
+ def create_user(user: UserCreate):
211
+ # Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ с маскированиСм
212
+ print(user.model_dump_anonymized())
213
+ # {'username': 'admin', 'email': 'a***@***.com', 'card_number': '4242-****-****-3333'}
214
+
215
+ # ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ
216
+ return user.model_dump()
217
+ ```
218
+
219
+ ### бСзопасноС Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅
220
+
221
+ ```python
222
+ import logging
223
+ from pydantic import BaseModel, Field
224
+ from pydantic_anonymizer import Anonymizer
225
+
226
+ logging.basicConfig(level=logging.INFO)
227
+ logger = logging.getLogger(__name__)
228
+
229
+ class Payment(BaseModel, Anonymizer):
230
+ card_number: str = Field(json_schema_extra={"anonymize": "card"})
231
+ amount: float
232
+
233
+ def process_payment(payment: Payment):
234
+ # бСзопасно Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ - Π½ΠΎΠΌΠ΅Ρ€ ΠΊΠ°Ρ€Ρ‚Ρ‹ замаскирован
235
+ logger.info("ΠΏΠ»Π°Ρ‚Ρ‘ΠΆ: %s", payment.model_dump_anonymized())
236
+
237
+ # Ρ€Π°Π±ΠΎΡ‚Π°Π΅ΠΌ с ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹ΠΌΠΈ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ
238
+ charge_card(payment.card_number, payment.amount)
239
+ ```
240
+
241
+ ---
242
+
243
+ ## **πŸ“œ лицСнзия**
244
+
245
+ [MIT](https://github.com/drawiks/pydantic-anonymizer/blob/main/LICENSE)