Skip to main content

Pure Python decoder for Clojure Nippy-encoded data

Project description

nippy-decoder

License: MIT Python 3.8+

pure python decoder for clojure nippy serialized data.

zero dependencies. uses only python standard library.


what and why

nippy is clojure's fast serialization format. if you have clojure/java services storing data as nippy-encoded bytes (in postgres, redis, kafka, etc.), you need a way to decode it in python.

this decoder:

  • reads nippy bytes into python dicts, lists, strings, etc.
  • has zero dependencies (just stdlib)
  • is simple: 200 lines of code
  • is validated against official clojure taoensso/nippy

installation

pip install nippy-decoder

how to use

basic decoding

from nippy_decoder import NippyDecoder

decoder = NippyDecoder()

# from anywhere: database, file, api, kafka...
nippy_bytes = b'NPY\x00...'

result = decoder.decode(nippy_bytes)
# => {'name': 'alice', 'age': 30, 'active': True}

from database

postgres with psycopg2:

import psycopg2
from nippy_decoder import NippyDecoder

decoder = NippyDecoder()

conn = psycopg2.connect("dbname=mydb")
cur = conn.cursor()

# query nippy-encoded column
cur.execute("""
    SELECT id, data
    FROM facts
    WHERE created_at > %s
""", (since_date,))

for fact_id, nippy_bytes in cur:
    decoded = decoder.decode(nippy_bytes)
    print(f"{fact_id}: {decoded}")

sqlite:

import sqlite3
from nippy_decoder import NippyDecoder

decoder = NippyDecoder()

conn = sqlite3.connect('/path/to/db.sqlite')
cur = conn.cursor()

cur.execute("SELECT id, nippy_data FROM records")

for row_id, nippy_bytes in cur:
    decoded = decoder.decode(nippy_bytes)
    # work with python dict/list/etc

from api response

import requests
from nippy_decoder import NippyDecoder

decoder = NippyDecoder()

response = requests.get("https://api.example.com/facts/123")
decoded = decoder.decode(response.content)

print(decoded['status'])  # => 'active'

error handling

from nippy_decoder import NippyDecoder

decoder = NippyDecoder()

try:
    result = decoder.decode(data)
except ValueError as e:
    print(f"decode failed: {e}")
    # "invalid nippy header"
    # "unsupported nippy version: 5"
    # "unsupported type: 200"

type mapping

clojure python example
nil None
true / false True / False
integer int 4242
float / double float 3.143.14
string str "hello""hello"
keyword str :status"status"
vector list [1 2 3][1, 2, 3]
map dict {:a 1 :b 2}{"a": 1, "b": 2}
set set #{1 2 3}{1, 2, 3}
uuid str uuid → "550e8400-..."
bytes bytes or dict auto-parses json if detected

note on keywords: clojure keywords like :status become python strings "status" (without the :).

note on byte arrays: if a byte array starts with { or [, the decoder attempts to parse it as json and returns a dict/list. otherwise returns raw bytes.

what's supported

  • primitives: null, boolean, integers (8-64 bit), floats, doubles
  • strings: utf-8 strings with 1-4 byte length prefixes (types 11-14, 105)
  • keywords: clojure keywords (types 33-35, 106)
  • collections: vectors (types 19-23), maps (types 24-26, 112), sets (types 27-29), lists (types 30-32)
  • binary: byte arrays (types 15-18), uuids (type 36)
  • metadata: type 37 (skipped, value returned)
  • extended: legacy collection types (44-127)

validated against nippy version 0 (standard format since 2014).

what's not

  • encoding - this is decode-only
  • nippy v1+ - only v0 supported (but v0 is the standard)
  • custom types - custom nippy extensions not supported
  • types > 127 - reserved/future types not implemented

if you need encoding, use the official taoensso/nippy in clojure.

examples

decode primitives:

decoder = NippyDecoder()

# integer
decoder.decode(b'NPY\x00\x07\x00\x00\x00\x2a')  # => 42

# string
decoder.decode(b'NPY\x00\x0c\x05hello')  # => "hello"

# keyword
decoder.decode(b'NPY\x00\x21\x06status')  # => "status"

decode collections:

# vector
decoder.decode(b'NPY\x00\x15\x03...')  # => [1, 2, 3]

# map
result = decoder.decode(b'NPY\x00\x18\x02...')
# => {"name": "test", "age": 25}

# nested
result = decoder.decode(b'NPY\x00\x18\x01...')
# => {"user": {"name": "alice", "active": True}}

decode uuid:

result = decoder.decode(b'NPY\x00\x24...')
# => "ba8feab4-9efb-4635-97d2-be648a141fb4"

complex nested data:

# decode nested maps with multiple data types
fact_bytes = get_from_database(record_id)
record = decoder.decode(fact_bytes)

print(record)
# {
#   'record-id': 'ba8feab4-9efb-4635-97d2-be648a141fb4',
#   'status': 'PENDING',
#   'priority': 1,
#   'metadata': {
#     'contribution': 179.03,
#     'interest': 50.25
#   }
# }

development

# clone
git clone https://github.com/HariprasathSankaraiyan/nippy-decoder
cd nippy-decoder

# install in dev mode
pip install -e .

# run examples
python examples/basic_usage.py

# run tests (requires pytest)
pip install pytest
pytest tests/ -v

structure:

nippy-decoder/
├── src/nippy_decoder/
│   ├── decoder.py      # core logic
│   └── __init__.py
├── tests/              # pytest suite
│   ├── test_primitives.py
│   ├── test_collections.py
│   └── test_uuid.py
├── examples/
│   └── basic_usage.py  # usage examples
├── pyproject.toml      # package metadata
├── README.md
└── LICENSE

license

MIT License

Copyright © 2025 nippy-decoder contributors


issues and prs welcome!

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

nippy_decoder-0.1.0.tar.gz (262.5 kB view details)

Uploaded Source

Built Distribution

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

nippy_decoder-0.1.0-py3-none-any.whl (6.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for nippy_decoder-0.1.0.tar.gz
Algorithm Hash digest
SHA256 86d0c9ac60e3b9303956e9b57dc11c8a35fe2a5caf5bdd9143f65327a6c6f365
MD5 7faf545766fbb43bc75392dc8ec1a946
BLAKE2b-256 08e5a107aa38bc14837fae232fb5ff3d93f1007ffbe098464b829c9e6245cf45

See more details on using hashes here.

File details

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

File metadata

  • Download URL: nippy_decoder-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 6.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.4

File hashes

Hashes for nippy_decoder-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7528ccb61bed7ce5764223173edf7a9adae8689e04b0adcc682ef58b71b37aa8
MD5 d0adb6c837d0eb2dbd49ec97e3bbe520
BLAKE2b-256 1756a8e2d3fed7d9604c4ea3dfdffd65fcf738ed5c7cc9dbff2542cf4caaf981

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