mrkeyoor.com_
Sun 20 Sept 17:51 UTC
PyPIUtilsupdated 20 Sept 2026

cbor2 review

cbor2 6.1.4 is a Rust-backed Python encoder and decoder for CBOR, the binary data format defined by RFC 8949. Its `dump`, `dumps`, `load`, and `loads` entry points feel familiar to users of the standard `json` module, but CBOR can carry byte strings, integer map keys, semantic tags, UUIDs, and Decimal values without private JSON wrappers. Version 6.1.4 rejects malformed bignums and incomplete indefinite maps, fixes a quadratic hash-collision case involving immutable maps, and corrects reference tracking for `bytearray`. Our installed wheel had no direct dependencies, included `py.typed`, and contained compiled `.so` files.

Verdict

cbor2 6.1.4 installed in 0.2 seconds as one 2 MB package with no direct dependencies or audit findings in our sandbox, making it an easy choice when a protocol already specifies CBOR. Keep JSON for human-facing data, and avoid optional reference tags until every peer proves it can decode them.

We installed it

Lab card: what happened when we installed cbor2Screenshot of cbor2 documentation
Install✓ · 0.2s1 package on disk · 2 MB
Importimport cbor2 in 0.07s · compiled extensions · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does cbor2 install cleanly?

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

What does cbor2 need to run?

Python >=3.10, and a platform wheel with compiled extensions. In our run import cbor2 succeeded in 0.07s, and the package ships py.typed for type checkers.

cbor2 or msgpack: which should you use?

msgpack: Use it when the protocol is MessagePack or its wider peer support matters more than CBOR semantic tags. cbor2 6.1.4 installed in 0.2 seconds as one 2 MB package with no direct dependencies or audit findings in our sandbox, making it an easy choice when a protocol already specifies CBOR.

When should you not use cbor2?

People routinely inspect or edit payloads with text tools. CBOR is binary, and the project's JSON diagnostic command explicitly loses type information.

API stability3/5The four basic file and bytes functions remain close to Python's `json` interface, so ordinary encode and decode calls have little surface area to break. Advanced integrations had real migration work in version 6: the implementation moved to Rust, callback arguments changed, old encoder and decoder submodules disappeared, and several public names moved. Custom tags and hooks therefore need version-specific tests even though simple `dumps` and `loads` code remains recognizable.
Docs4/5The official site documents supported Python types, registered tags, custom encoding and decoding, shared values, string references, command-line inspection, and every release. Its README directly says the JSON view is lossy and that malicious input has not been tested. The missing piece is a single defensive-decoding recipe: users must combine body limits, depth settings, duplicate-key policy, and protocol-specific tag decisions themselves.
Maintenance5/5GitHub reports an unarchived repository with 304 stars, 18 open issues and pull requests, and a push on August 16, 2026. Version 6.1.4 shipped on August 1, 2026 with four precise correctness fixes covering malformed maps, invalid bignums, string-reference desynchronization, and quadratic hashing behavior. The prior two releases also concentrated on parser correctness and reference tracking rather than cosmetic churn.
Ecosystem4/5The supplied weekly snapshot records 20,940,954 PyPI downloads. CBOR is named by standards used in identity, cryptography, constrained devices, and web authentication, and cbor2 handles both registered tags and application-defined ones. Its surrounding tools are smaller than JSON's, while shared values and string references are optional extensions that must be confirmed separately for every implementation on the other end.

Use it if

  • An external specification already requires CBOR, as happens with COSE, CWT, WebAuthn, CoAP, and some device protocols.
  • Messages must retain raw bytes, non-string keys, or registered semantic tags that would need an application-specific wrapper in JSON.
  • You need canonical serialized bytes for a signature, digest, or byte-for-byte comparison.
  • Every non-Python peer has been tested against the same tag, reference, and datetime choices.
Skip it if

Setup reality

We installed cbor2 6.1.4 in a fresh unprivileged Python 3.12 container. pip finished in 0.2 seconds, and the single installed package used 2 MB. import cbor2 took 0.07 seconds. It has zero direct dependencies, requires Python 3.10+, ships py.typed, and includes compiled .so extensions. pip-audit returned zero known vulnerabilities. The package metadata in our run did not state a license.

There are no credentials or configuration files. The format options are the configuration. Set canonical=True when signatures or hashes depend on stable bytes. value_sharing and string_referencing emit reference tags, so they belong in a documented protocol contract rather than a local optimization switch. A tag_hook should return unknown tags intact unless the application has an explicit rejection rule.

The 0.2-second install used a compatible wheel. If pip cannot find one for the interpreter and platform, it builds the Rust implementation from source; the README names Rust 1.93.0 as the tested toolchain. Use a binary-only install in containers where compilation is forbidden. python -m cbor2.tool helps inspect a capture, but its JSON output cannot round-trip byte strings or non-string mapping keys.

CBOR decoding does not construct executable Python objects as pickle does, yet a parser can still consume excessive memory or CPU. Limit the request body before calling loads, then choose max_depth, duplicate-key handling, and indefinite-item policy from the message schema. Release 6.1.4 closes malformed-map and bignum acceptance bugs plus a quadratic immutable-map collision, so older 6.x pins miss parser fixes that matter on hostile inputs.

Patterns

Round-trip bytes and mapping values round-trip-values

import cbor2

payload = cbor2.dumps({"id": 7, "blob": b"\x00\x01"})
record = cbor2.loads(payload)
assert record == {"id": 7, "blob": b"\x00\x01"}

`dumps()` returns bytes, and a CBOR byte string decodes back to Python `bytes` without a base64 wrapper.

Serialize one item through a binary file read-write-stream

with open("record.cbor", "wb") as target:
    cbor2.dump(record, target)

with open("record.cbor", "rb") as source:
    restored = cbor2.load(source)

Both calls expect binary file objects. `load()` consumes one CBOR item and leaves the stream at the following byte.

Define how a naive datetime is interpreted encode-naive-datetime

from datetime import datetime, timezone
import cbor2

created = datetime(2026, 8, 26, 12, 0)
payload = cbor2.dumps(created, timezone=timezone.utc)
restored = cbor2.loads(payload)

The encoder will not infer a timezone for a naive datetime. `datetime_as_timestamp=True` chooses numeric epoch encoding instead.

Make equal mappings produce equal bytes produce-canonical-bytes

a = cbor2.dumps({"b": 1, "a": 2}, canonical=True)
b = cbor2.dumps({"a": 2, "b": 1}, canonical=True)
assert a == b

Canonical mode controls map order and numeric representation. Use it when serialized bytes are signed or hashed.

Map a class to a private semantic tag encode-application-type

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

def encode_point(encoder, point):
    encoder.encode_semantic(4000, [point.x, point.y])

payload = cbor2.dumps(Point(3, 5), encoders={Point: encode_point})

Private tag 4000 has meaning only by agreement. Document its value shape and coordinate the number with every peer.

Turn one semantic tag into an object decode-application-tag

def tag_hook(tag, immutable):
    if tag.tag == 4000:
        return Point(*tag.value)
    return tag

point = cbor2.loads(payload, tag_hook=tag_hook)

cbor2 6.x passes `(tag, immutable)` to this hook. Returning an unknown tag preserves its number and value for later handling.

Apply parser limits to an incoming message limit-untrusted-input

import io

raw = receive_body(max_bytes=1_000_000)
decoder = cbor2.CBORDecoder(
    io.BytesIO(raw),
    max_depth=32,
    allow_indefinite=False,
    allow_duplicate_keys=False,
)
value = decoder.decode()

A 32-level depth limit does not cap total input. Reject an oversized body before constructing the decoder.

Consume concatenated CBOR values decode-item-sequence

decoder = cbor2.CBORDecoder(stream)
while True:
    try:
        yield decoder.decode()
    except cbor2.CBORDecodeEOF:
        break

`CBORDecodeEOF` is the normal end of a sequence. A different decoding exception means the last value is incomplete or malformed.

Encode a cycle with shared-value tags preserve-cyclic-reference

node = {"name": "root"}
node["self"] = node

payload = cbor2.dumps(node, value_sharing=True)
copy = cbor2.loads(payload)
assert copy["self"] is copy

Shared-value mode adds reference tags to the data. Test this payload with each non-cbor2 decoder before adopting it on a shared wire.

Render a capture for command-line inspection inspect-cbor-file

python -m cbor2.tool --pretty message.cbor

# Input stored as base64
python -m cbor2.tool --decode message.cbor.b64

The tool emits a JSON representation for diagnosis. The conversion loses CBOR types and is unsuitable for a round-trip migration.

Alternatives

PackageRegistryPick it when
msgpackPyPIUse it when the protocol is MessagePack or its wider peer support matters more than CBOR semantic tags.
orjsonPyPIUse it when payloads must remain JSON and encoding or decoding speed is the actual constraint.
msgspecPyPIUse it for typed structures with validation during JSON or MessagePack decoding.
cbor-diagPyPIUse it to work with CBOR diagnostic notation rather than as an application's main codec.

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.