Skip to main content

anonymize sensitive data in pydantic models

Project description

🔒 pydantic-anonymizer

PyPI version Python 3.9+ License MIT Status

mask sensitive data in Pydantic models

Русский


                 ,--.                  ,--.  ,--.       
 ,---.,--. ,--.,-|  | ,--,--.,--,--, ,-'  '-.`--' ,---. 
| .-. |\  '  /' .-. |' ,-.  ||      \'-.  .-',--.| .--' 
| '-' ' \   ' \ `-' |\ '-'  ||  ||  |  |  |  |  |\ `--. 
|  |-'.-'  /   `---'  `--`--'`--''--'  `--'  `--' `---' 
`--'  `---'                                             
                                                                            
                                                  ,--.                      
 ,--,--.,--,--,  ,---. ,--,--, ,--. ,--.,--,--,--.`--',-----. ,---. ,--.--. 
' ,-.  ||      \| .-. ||      \ \  '  / |        |,--.`-.  / | .-. :|  .--' 
\ '-'  ||  ||  |' '-' '|  ||  |  \   '  |  |  |  ||  | /  `-.\   --.|  |    
 `--`--'`--''--' `---' `--''--'.-'  /   `--`--`--'`--'`-----' `----'`--'    
                               `---'                                        

📦 installation

pip install pydantic-anonymizer

📑 quick start

from pydantic import BaseModel, Field
from pydantic_anonymizer import Anonymizer

class UserProfile(BaseModel, Anonymizer):
    username: str
    email: str = Field(json_schema_extra={"anonymize": True})
    card_number: str = Field(json_schema_extra={"anonymize": "card"})
    phone_number: str = Field(json_schema_extra={"anonymize": "phone"})

user = UserProfile(
    username="ivan_dev",
    email="ivan@mail.com",
    card_number="4242111122223333",
    phone_number="+380500223785"
)

print(user.model_dump())
# {'username': 'ivan_dev', 'email': 'ivan@mail.com', 'card_number': '4242111122223333', 'phone_number': '+380500223785'}

print(user.model_dump_anonymized())
# {'username': 'ivan_dev', 'email': 'i***@***.com', 'card_number': '4242-****-****-3333', 'phone_number': '+380 (***) ***-**-85'}

🧩 features

  • 🔐 automatic masking - configure via json_schema_extra in model fields
  • 📧 generic masking - partial mask for emails and text (i***@***.com)
  • 💳 card masking - format 4242-****-****-3333
  • 📱 phone masking - correct country code parsing with phonenumbers
  • 🏗️ nested models - recursive processing of nested Pydantic models
  • 📋 lists - support for list[Model] with masking of each element
  • 🛠️ custom strategies - your own masking functions via MaskRegistry
  • reliable - 27 tests covering all scenarios
  • 🪶 minimal dependencies - only pydantic>=2.0 and phonenumbers>=8.13

📖 usage

basic usage

from pydantic import BaseModel, Field
from pydantic_anonymizer import Anonymizer

class User(BaseModel, Anonymizer):
    name: str
    email: str = Field(json_schema_extra={"anonymize": True})

user = User(name="ivan", email="ivan@mail.com")

# original data
user.model_dump()  # {'name': 'ivan', 'email': 'ivan@mail.com'}

# masked data
user.model_dump_anonymized()  # {'name': 'ivan', 'email': 'i***@***.com'}

# JSON string
user.model_dump_json_anonymized()  # '{"name": "ivan", "email": "i***@***.com"}'

nested models

from pydantic import BaseModel, Field
from pydantic_anonymizer import Anonymizer

class Address(BaseModel, Anonymizer):
    city: str
    street: str = Field(json_schema_extra={"anonymize": True})

class UserProfile(BaseModel, Anonymizer):
    name: str
    address: Address

user = UserProfile(
    name="ivan",
    address=Address(city="Moscow", street="Lenina 1")
)

result = user.model_dump_anonymized()
# {'name': 'ivan', 'address': {'city': 'Moscow', 'street': 'L***a 1'}}

lists of models

from pydantic import BaseModel, Field
from pydantic_anonymizer import Anonymizer

class Card(BaseModel, Anonymizer):
    number: str = Field(json_schema_extra={"anonymize": "card"})

class Wallet(BaseModel, Anonymizer):
    cards: list[Card]

wallet = Wallet(cards=[
    Card(number="4242111122223333"),
    Card(number="5555666677778888")
])

result = wallet.model_dump_anonymized()
# {'cards': [{'number': '4242-****-****-3333'}, {'number': '5555-****-****-8888'}]}

custom strategies

from pydantic import BaseModel, Field
from pydantic_anonymizer import Anonymizer, MaskRegistry

# register your own masking function
def mask_ssn(value: str) -> str:
    return "***-**-" + value[-4:]

MaskRegistry.register("ssn", mask_ssn)

class Person(BaseModel, Anonymizer):
    ssn: str = Field(json_schema_extra={"anonymize": "ssn"})

person = Person(ssn="123-45-6789")
person.model_dump_anonymized()  # {'ssn': '***-**-6789'}

🎭 built-in strategies

Strategy Field Input Output
True (generic) email ivan@mail.com i***@***.com
"card" card number 4242111122223333 4242-****-****-3333
"phone" phone +380500223785 +380 (***) ***-**-85

country code support

phone masking works correctly with any country codes:

Country Code Example
Ukraine +380 +380500223785+380 (***) ***-**-85
USA +1 +14155552671+1 (***) ***-**-71
UK +44 +447911123456+44 (***) ***-**-56
Russia +7 +79161234567+7 (***) ***-**-67
Germany +49 +4915112345678+49 (***) ***-**-78
China +86 +8613812345678+86 (***) ***-**-78

📝 examples

FastAPI logging

from fastapi import FastAPI
from pydantic import BaseModel, Field
from pydantic_anonymizer import Anonymizer

app = FastAPI()

class UserCreate(BaseModel, Anonymizer):
    username: str
    email: str = Field(json_schema_extra={"anonymize": True})
    card_number: str = Field(json_schema_extra={"anonymize": "card"})

@app.post("/users")
def create_user(user: UserCreate):
    # log with masking
    print(user.model_dump_anonymized())
    # {'username': 'admin', 'email': 'a***@***.com', 'card_number': '4242-****-****-3333'}

    # original data for processing
    return user.model_dump()

safe logging

import logging
from pydantic import BaseModel, Field
from pydantic_anonymizer import Anonymizer

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class Payment(BaseModel, Anonymizer):
    card_number: str = Field(json_schema_extra={"anonymize": "card"})
    amount: float

def process_payment(payment: Payment):
    # safe to log - card number is masked
    logger.info("payment: %s", payment.model_dump_anonymized())

    # work with original data
    charge_card(payment.card_number, payment.amount)

📜 license

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pydantic_anonymizer-0.1.0.tar.gz (8.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pydantic_anonymizer-0.1.0-py3-none-any.whl (6.9 kB view details)

Uploaded Python 3

File details

Details for the file pydantic_anonymizer-0.1.0.tar.gz.

File metadata

  • Download URL: pydantic_anonymizer-0.1.0.tar.gz
  • Upload date:
  • Size: 8.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for pydantic_anonymizer-0.1.0.tar.gz
Algorithm Hash digest
SHA256 27420861d428ef2653bfaf590933755488d31b91e54733e4d34a5755725fc58c
MD5 8bf84563d1bcefd5f9c6c282c9122142
BLAKE2b-256 ffc4241adf8db481781ae4656f4ee1571b5c66655ceb509ce439fbe3e9f3a8a9

See more details on using hashes here.

File details

Details for the file pydantic_anonymizer-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pydantic_anonymizer-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 760b5fa6acfee5f46a5fb568056bbfee4cee39adc30713e2ac5f8551c5d65696
MD5 31fd6d70e6f96f0690cf0652e87b79ca
BLAKE2b-256 5738a08ccfab8e3cca24680acb0e7b7364eddcf73d81207082d18954ec769936

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page