How-to: the decimal module and Unicode digit input
Python's Decimal accepts Unicode full-width digits
decimal.Decimal() interprets Unicode full-width digits (U+FF10–U+FF19: 0123456789) as numbers as-is. When a user submits full-width digits through an HTTP API, they can pass straight through a Pydantic str field.
python
from decimal import Decimal
Decimal("123") # → Decimal('123') ← converted normally
Decimal("1.5") # → Decimal('1.5')Problem: unexpected input may slip through
In a financial-calculation API that declares price: str = Field(...), if a client sends "1000" it is processed as Decimal("1000") → Decimal('1000'). That is not an error in itself, but when you need normalized input (logging, DB storage, etc.) perform Unicode normalization before processing.
python
import unicodedata
from decimal import Decimal
def parse_decimal_safe(value: str) -> Decimal:
"""Normalize (NFKC) then convert to Decimal."""
normalized = unicodedata.normalize("NFKC", value.strip())
return Decimal(normalized)unicodedata.normalize("NFKC", ...) converts full-width digits to half-width.
python
unicodedata.normalize("NFKC", "123") # → "123"
unicodedata.normalize("NFKC", "1.5") # → "1.5"Validation with Pydantic
We recommend forcing input normalization with a Pydantic model_validator.
python
from pydantic import BaseModel, Field, model_validator
class PriceRequest(BaseModel):
price: str = Field(max_length=20, description="Price (half-width digits)")
@model_validator(mode="before")
@classmethod
def normalize_unicode(cls, values: dict) -> dict:
import unicodedata
if "price" in values and isinstance(values["price"], str):
values["price"] = unicodedata.normalize("NFKC", values["price"])
return valuesRelated issue
- [FT176] #500: document the full-width Unicode digit acceptance behavior of
parse_decimal_safe()