mrkeyoor.com_
Wed 23 Sept 00:34 UTC
PyPIUtilsupdated 19 Sept 2026

dacite review

dacite 1.9.2 turns a dictionary into an instance of a standard Python dataclass. `from_dict()` follows annotations through nested dataclasses, collections, optionals, unions, generics, and forward references. Configuration covers type hooks, constructor casts, key conversion, extra-key rejection, and ambiguous unions. The current release fixes Protocol type declarations and avoids writing to read-only typing internals. Our install was pure Python, included `py.typed`, and imported successfully, but dacite still draws a hard boundary: it constructs objects and does not validate business rules or serialize them.

Verdict

dacite 1.9.2 installed in 0.2 seconds, occupied 1 MB, imported in 0.14 seconds, and had 0 known vulnerabilities in our sandbox. Install it for trusted dictionary-to-dataclass construction; use a validation or serialization library when the input boundary needs more than annotated type checks.

We installed it

Lab card: what happened when we installed daciteScreenshot of dacite documentation
Install✓ · 0.2s1 package on disk · 1 MB
Importimport dacite in 0.14s · pure Python · py.typed · requires Python >=3.7
Known vulns0(pip-audit)

Answers from our run

Does dacite install cleanly?

Yes. In a fresh container with an empty cache, pip install dacite finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does dacite need to run?

Python >=3.7, and nothing compiled: it is pure Python. In our run import dacite succeeded in 0.14s, and the package ships py.typed for type checkers.

dacite or cattrs: which should you use?

cattrs: Choose it when dataclasses or attrs classes must be converted in both directions. dacite 1.9.2 installed in 0.2 seconds, occupied 1 MB, imported in 0.14 seconds, and had 0 known vulnerabilities in our sandbox.

When should you not use dacite?

The dictionary comes straight from an untrusted request. The README says dacite is not a validation library, so ranges, formats, and cross-field rules need another layer.

API stability5/5One `from_dict()` function and the 7 documented `Config` options define nearly the whole public surface. Release 1.9 added generics, forward references, and key conversion without changing the core call shape. Version 1.9.2 then limited itself to fixes for Protocol declarations and read-only typing internals, which suggests low migration cost within the 1.x line.
Docs4/5The README shows nested objects, optional fields, unions, collections, generics, type hooks, casts, forward references, strict modes, key conversion, and each exception with code. It also states plainly that construction is not validation. The weak spot is operational detail: there is no versioned documentation site, discriminator recipe, alias table, or full error-handling example for an external API boundary.
Maintenance3/5PyPI lists 1.9.2 from February 5, 2025, and GitHub reports the last push on March 17, 2025. The repository is not archived, has 2,063 stars, and shows 69 open issues and pull requests. The latest release fixed two specific typing problems, but the year-plus gap since any repository push makes quick support for new Python typing behavior uncertain.
Ecosystem4/5The package records 5,875,451 weekly downloads and works directly with `dataclasses` plus standard `typing` constructs. Its pure-Python wheel and `py.typed` marker fit ordinary Python tooling. The tradeoff is deliberate isolation: validation, schema production, JSON output, settings, and field-specific aliases come from separate packages with separate configuration and error models.

Use it if

  • Your application already uses standard-library dataclasses and receives trusted dictionaries that need nested object construction.
  • A single `from_dict()` call plus a small `Config` is preferable to adding a second model system.
  • Type-wide hooks, enum casts, and one field-name conversion function cover the input format.
  • You want field-path exceptions for missing values, wrong runtime types, unexpected keys, and union failures.
Skip it if

Setup reality

We installed dacite 1.9.2 in a fresh Python 3.12 Bookworm sandbox. Installation finished in 0.2 seconds and left 1 package using 1 MB on disk. The measured metadata declared 9 direct dependencies and Python 3.7 or newer. import dacite completed in 0.14 seconds. The wheel is pure Python, ships py.typed, and pip-audit found 0 known vulnerabilities.

The first decision is whether unknown input keys should disappear. They are ignored unless Config(strict=True) is set. Runtime type checking is enabled, but it only compares values with annotations. A negative age, malformed email, or invalid relationship between 2 fields can still reach the dataclass. Validate those rules before construction and translate DaciteFieldError subclasses at the boundary.

Conversions have broad scope. A hook registered for datetime runs for every matching field, and cast=[Enum] calls each enum constructor. convert_key receives the dataclass field name and returns the dictionary key, so a camel-case adapter runs in the opposite direction many developers first assume. Forward references that Python cannot resolve need an explicit forward_references mapping.

Union matching is ordered. With Cat | Dog, dacite accepts the first member whose annotations fit unless strict union matching is enabled. Version 1.9.2 fixed Protocol declarations and read-only typing internals, following the generic and forward-reference work in 1.9.0. Resolved type information is cached process-wide; the cache defaults to 2,048 entries and can be resized or cleared when an application creates dataclass types dynamically.

Patterns

Build a dataclass from a dictionary construct-dataclass

from dataclasses import dataclass
from dacite import from_dict

@dataclass
class User:
    name: str
    age: int

user = from_dict(User, {'name': 'Ada', 'age': 37})

`from_dict()` checks the annotated runtime types, but 1 integer check does not enforce a nonnegative age.

Create nested dataclasses construct-nested-model

from dataclasses import dataclass
from dacite import from_dict

@dataclass
class Address:
    city: str

@dataclass
class Customer:
    address: Address

customer = from_dict(Customer, {'address': {'city': 'London'}})

The `Address` annotation makes dacite turn 1 nested dictionary into an `Address` instance.

Convert a list of records construct-list-items

@dataclass
class Line:
    sku: str

@dataclass
class Order:
    lines: list[Line]

order = from_dict(Order, {'lines': [{'sku': 'A1'}]})

`list[Line]` supplies the item type; a bare `list` gives dacite no dataclass to construct.

Allow a missing optional field handle-optional-field

@dataclass
class Profile:
    handle: str
    bio: str | None

profile = from_dict(Profile, {'handle': 'ada'})
assert profile.bio is None

An omitted optional field becomes `None`; 1 omitted required field without a default raises `MissingValueError`.

Parse timestamps with a type hook parse-datetime

from datetime import datetime
from dacite import Config

config = Config(type_hooks={datetime: datetime.fromisoformat})
event = from_dict(Event, {'occurred_at': '2026-08-22T12:30:00+00:00'}, config)

The 1 `datetime` hook runs for every field with that target type, and parsing failures come from the hook.

Construct enum values cast-enum

from enum import Enum
from dacite import Config

class State(Enum):
    OPEN = 'open'
    CLOSED = 'closed'

ticket = from_dict(Ticket, {'state': 'open'}, Config(cast=[Enum]))

Casting the `Enum` base class affects every enum field; an unknown value raises from its constructor.

Reject unknown input keys reject-extra-keys

from dacite import Config, UnexpectedDataError

try:
    user = from_dict(User, payload, Config(strict=True))
except UnexpectedDataError as error:
    raise ValueError(sorted(error.keys)) from error

Strict mode defaults to false, so 1 misspelled payload key is otherwise ignored.

Require one union match reject-ambiguous-union

config = Config(strict_unions_match=True)
pet = from_dict(Pet, {'animal': {'name': 'Milo'}}, config)

If 2 union members accept this shape, strict matching raises instead of choosing by annotation order.

Read camel-case dictionary keys convert-input-keys

def to_camel(field: str) -> str:
    first, *rest = field.split('_')
    return first + ''.join(part.title() for part in rest)

config = Config(convert_key=to_camel)
person = from_dict(Person, {'firstName': 'Ada'}, config)

`convert_key` maps each dataclass field name to 1 source dictionary key, rather than converting incoming keys first.

Resolve a forward-referenced type resolve-forward-reference

config = Config(forward_references={'Leaf': Leaf})
node = from_dict(Node, {'child': {'value': 'x'}}, config)

The 1-entry namespace lets Python resolve `Leaf`; an unresolved name raises `ForwardReferenceError`.

Expose the failing field path translate-field-error

from dacite import DaciteFieldError

try:
    order = from_dict(Order, payload)
except DaciteFieldError as error:
    raise ValueError(error.field_path) from error

Field-aware exceptions include a nested path; `UnexpectedDataError` and `ForwardReferenceError` need separate handlers.

Clear cached type resolution manage-type-cache

from dacite import clear_cache, get_cache_size, set_cache_size

assert get_cache_size() == 2048
set_cache_size(4096)
try:
    run_dynamic_imports()
finally:
    clear_cache()

The cache is process-wide and defaults to 2,048 entries; resize it only after measuring dynamic dataclass workloads.

Alternatives

PackageRegistryPick it when
cattrsPyPIChoose it when dataclasses or attrs classes must be converted in both directions.
mashumaroPyPIChoose it for generated serialization methods and multiple wire formats on dataclasses.
pydanticPyPIChoose it when parsing, constraints, aliases, errors, schemas, and serialization belong in one model layer.
dataclasses-jsonPyPIChoose it when JSON load and dump methods attached to dataclasses fit the project better than a standalone mapper.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.