litestar-org/litestar-skills/msgspec

Claude Code and agent skill by litestar-org/litestar-skills

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Auto-activate for msgspec, Struct, Meta, msgspec.json, msgspec.msgpack, tagged unions, enc_hook, dec_hook, convert(), or Litestar DTO shapes. Not for Pydantic or ORM models — use their stack-specific skill.

Install

npx vskill@latest install litestar-org/litestar-skills/msgspec

SKILL.md

msgspec Skill

msgspec is a high-performance Python library for serialization, deserialization, and typed validation. This guidance targets the 0.22.0 release.

Code Style Rules

  • Use PEP 604 for unions: T | None (not Optional[T])
  • `from __future__ import annotations` rule — Library/shared modules that define runtime-introspected msgspec.Struct subclasses should avoid postponed annotations unless the consuming tool resolves them. Consumer modules that only use Structs MAY use future annotations.
  • Annotate every serialized field; only annotated attributes become Struct fields
  • Use kw_only=True for Structs with more than 2 fields
  • Put wire-name configuration on msgspec.field(name=...) or the Struct's rename=

option; msgspec.Meta defines constraints and JSON Schema metadata, not field aliases

Quick Reference

Struct Definition and Options
python
import msgspec
# Basic struct
class User(msgspec.Struct):
id: int
name: str
email: str | None = None
# Performance options
class Event(msgspec.Struct, frozen=True, gc=False, cache_hash=True):
"""frozen=True: immutable + hashable. gc=False: skip GC tracking."""
event_type: str
payload: dict[str, object]
# Keyword-only + omit defaults on wire (recommended for >2 fields / compact payloads)
class Config(msgspec.Struct, kw_only=True, omit_defaults=True, repr_omit_defaults=True):
host: str
port: int = 5432
ssl: bool = False
# Array-like encoding (tuple encoding, more compact)
class Point(msgspec.Struct, array_like=True):
x: float
y: float
# Rename fields for serialization ("lower", "upper", "camel", "pascal", "kebab", callable, or mapping)
class ApiResponse(msgspec.Struct, rename="camel"):
user_id: int # serialized as "userId"
created_at: str # serialized as "createdAt"
# Per-field wire alias and default_factory via msgspec.field()
class Resource(msgspec.Struct):
resource_id: int = msgspec.field(name="id")
tags: list[str] = msgspec.field(default_factory=list)
# Reject unknown fields at API boundaries
class StrictInput(msgspec.Struct, forbid_unknown_fields=True):
name: str
value: int
Struct / defstruct OptionDefaultPurpose
kw_onlyFalseMake all fields keyword-only in __init__
frozenFalseImmutable instances; adds __hash__
cache_hashFalsePrecompute and cache hash on frozen=True Structs
gcTrueSet False to omit Cyclic GC header on short-lived, non-circular Structs
omit_defaultsFalseOmit fields equal to their default (or empty built-in list/dict/set/bytearray default_factory; custom default_factory callables are never omitted) when encoding or calling to_builtins()
repr_omit_defaultsFalseOmit fields equal to their default in repr()
forbid_unknown_fieldsFalseRaise ValidationError on unknown keys during decoding / convert()
renameNone"lower", "upper", "camel", "pascal", "kebab", `Callable[[str], str \None], or Mapping[str, str] (leading _ is preserved for "camel"/"pascal"`)
array_likeFalseEncode/decode Struct as a positional array ([x, y]) instead of an object
tag / tag_fieldNoneDiscriminated union tag (True, False, str, int, or `Callable[[str], str \int]) and discriminator key (defaults to "type" when tag` is set)
eq / orderTrue / FalseGenerate ==/!= and </<=/>/>= comparison methods
weakref / dictFalse / FalseAdd __weakref__ slot or __dict__ attribute storage (neither dict=True nor weakref=True/inherited __weakref__ can be combined with gc=False in 0.22.0+)
Partial Updates with UNSET and UnsetType

Use msgspec.UNSET (msgspec.UnsetType) to distinguish omitted fields from explicit null (None). bool(msgspec.UNSET) is False. Fields set to UNSET are automatically omitted when encoding or calling msgspec.to_builtins().

python
import msgspec
class UserPatch(msgspec.Struct, kw_only=True, forbid_unknown_fields=True):
name: str | msgspec.UnsetType = msgspec.UNSET
email: str | None | msgspec.UnsetType = msgspec.UNSET
patch = msgspec.json.decode(b'{"email": null}', type=UserPatch)
assert patch.name is msgspec.UNSET # omitted by client
assert patch.email is None # explicitly cleared by client
assert msgspec.json.encode(patch) == b'{"email":null}'
Validation Constraints
python
from datetime import datetime
from typing import Annotated
import msgspec
from msgspec import Meta
class Product(msgspec.Struct):
name: Annotated[str, Meta(min_length=1, max_length=100)]
price: Annotated[float, Meta(gt=0)]
quantity: Annotated[int, Meta(ge=0, le=10_000)]
sku: Annotated[str, Meta(pattern=r"^[A-Z]{2}-\d{4}$")]
batch_size: Annotated[int, Meta(multiple_of=5)]
expires_at: Annotated[datetime, Meta(tz=True)]
# Reusable constraint aliases
PositiveInt = Annotated[int, Meta(gt=0)]
NonEmptyStr = Annotated[str, Meta(min_length=1)]
Percentage = Annotated[float, Meta(ge=0.0, le=100.0)]
class Order(msgspec.Struct):
id: PositiveInt
label: NonEmptyStr
discount: Percentage = 0.0
Serialization (json, msgpack, yaml, toml) and Raw
python
import msgspec
# JSON -- singleton encoder/decoder (cache these!)
# Encoder kwargs: enc_hook=None, decimal_format="string"|"number"|Callable[[Decimal], Any], uuid_format="canonical"|"hex", order=None|"deterministic"|"sorted"
# Decoder kwargs: type=Any, *, strict=True, dec_hook=None, float_hook=None
encoder = msgspec.json.Encoder()
decoder = msgspec.json.Decoder(User)
data = encoder.encode(user) # bytes
user = decoder.decode(b'{"id":1,"name":"Alice"}')
# Newline-delimited JSON (NDJSON) and zero-allocation buffer writes
ndjson = encoder.encode_lines([user])
users = decoder.decode_lines(ndjson)
buf = bytearray()
encoder.encode_into(user, buf, 0)
pretty = msgspec.json.format(data, indent=2)
# Functional API (convenience, slightly slower)
data = msgspec.json.encode(user, order="deterministic")
user = msgspec.json.decode(data, type=User)
# MessagePack (binary, more compact; uuid_format also accepts "bytes"; Decoder accepts ext_hook and msgspec.msgpack.Ext)
mp_data = msgspec.msgpack.encode(user)
user = msgspec.msgpack.decode(mp_data, type=User)
# YAML (requires msgspec[yaml] / PyYAML) and TOML (requires msgspec[toml] / tomli_w + tomllib/tomli)
yaml_bytes = msgspec.yaml.encode(user)
user_from_yaml = msgspec.yaml.decode(yaml_bytes, type=User)
toml_bytes = msgspec.toml.encode(user)
user_from_toml = msgspec.toml.decode(toml_bytes, type=User)
# Deferred decoding / zero-copy payload slicing with msgspec.Raw
class Envelope(msgspec.Struct):
kind: str
payload: msgspec.Raw
env = msgspec.json.decode(b'{"kind":"user","payload":{"id":1,"name":"Alice"}}', type=Envelope)
detached_raw = env.payload.copy() # detach from input buffer if retaining long-term
inner_user = msgspec.json.decode(detached_raw, type=User)
# Hooks are only for unsupported custom types. datetime, date, time, timedelta,
# UUID, Decimal, and Enum are already supported natively.
def enc_hook(obj: object) -> object:
if isinstance(obj, complex):
return (obj.real, obj.imag)
raise NotImplementedError(f"Unsupported type: {type(obj)}")
def dec_hook(target_type: type, obj: object) -> object:
if target_type is complex:
real, imag = obj
return complex(real, imag)
raise NotImplementedError(f"Unsupported type: {target_type}")
custom_encoder = msgspec.json.Encoder(enc_hook=enc_hook)
custom_decoder = msgspec.json.Decoder(MyStruct, dec_hook=dec_hook)

dec_hook runs only for unsupported custom annotations. TypeError and ValueError raised by the hook become path-aware ValidationErrors. ValidationError and DecodeError raised by the hook propagate directly without being wrapped in another ValidationError (0.21.1+).

Exception hierarchy: MsgspecError(Exception) is the base class for EncodeError and DecodeError. ValidationError is a subclass of DecodeError (ValidationError -> DecodeError -> MsgspecError). Always catch ValidationError before DecodeError when distinguishing schema validation errors from malformed wire syntax.

Canonical Litestar serializers (match-your-stack)

Litestar apps typically need to_json(value, as_bytes=True) that handles UUID / datetime / Enum / Decimal for Channels broadcasts, log contexts, and JSONB writes. Pick the branch that matches your project.

Branch A — sqlspec is in-stack. Re-export sqlspec's serializer; it already installs an enc_hook covering UUID, datetime, Enum, Decimal, Pydantic, dataclasses, attrs, and msgspec.Struct.

python
# myapp/utils/serialization.py
from sqlspec.utils.serializers import from_json, to_json
__all__ = ("from_json", "to_json")

Usage:

python
from myapp.utils.serialization import to_json
payload = to_json(order, as_bytes=True)
await backend.publish(payload, channels=[f"orders:{order.id}:events"])

Branch B — sqlspec is not in-stack. Use a plain msgspec Encoder; the package natively handles UUID, datetime, date, time, timedelta, Decimal, Enum, dataclasses, attrs classes, and Structs.

python
# myapp/utils/serialization.py
from typing import Any
import msgspec
_encoder = msgspec.json.Encoder()
def to_json(value: Any) -> bytes:
if isinstance(value, bytes):
return value
return _encoder.encode(value)
Type Coercion with convert() and to_builtins()
python
import msgspec
raw = {"id": "42", "name": "Alice"} # id is a string
# Strict mode (default): raises on type mismatch
user = msgspec.convert(raw, User) # ValidationError: id must be int
# Lax mode: coerces compatible types (e.g. str -> int, 0/1 -> bool, epoch int/float -> datetime)
user = msgspec.convert(raw, User, strict=False) # id coerced to 42
# str_keys: dict keys are strings (useful for JSON/TOML-loaded dicts)
data = {"1": "Alice", "2": "Bob"}
result = msgspec.convert(data, dict[int, str], str_keys=True)
# Convert with dec_hook or custom builtin_types passthrough
measurement = msgspec.convert(raw_measurement, Measurement, dec_hook=dec_hook, builtin_types=(bytes,))
# Convert a dataclass, attrs instance, or ORM model to a Struct by reading attributes
from dataclasses import dataclass
@dataclass
class LegacyUser:
id: int
name: str
legacy = LegacyUser(id=1, name="Alice")
user = msgspec.convert(legacy, User, from_attributes=True)
# Convert a Struct or supported type graph back to Python builtin types
builtins_dict = msgspec.to_builtins(user, str_keys=True, order="deterministic")

from_attributes=False is the default. Plain mappings convert to object-like output types without this option; dataclass, attrs, ORM, and other objects require from_attributes=True. msgspec.Raw instances pass through convert() when the target field/type is msgspec.Raw; to parse a Raw buffer into a Struct, use msgspec.json.decode(raw, type=Struct) (or msgpack.decode).

Struct Helpers (msgspec.structs) and Inspection (msgspec.inspect)
python
import copy
import msgspec
# Shallow conversions (keyed by Python attribute names; preserves nested Structs and UNSET values)
field_map = msgspec.structs.asdict(user)
field_tuple = msgspec.structs.astuple(user)
# Copy-and-replace (both call __post_init__ as of msgspec 0.21.0)
updated = msgspec.structs.replace(user, name="Bob")
updated_std = copy.replace(user, name="Bob")
# Mutate a frozen=True Struct (e.g. inside __post_init__ to normalize a field)
msgspec.structs.force_setattr(event, "event_type", event.event_type.lower())

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
16
7-day trend score
0

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/litestar-org/litestar-skills/msgspec/badge)](https://verified-skill.com/skills/litestar-org/litestar-skills/msgspec)

Something wrong with this skill or its page? Tell us.