Skip to main content

Packify

This is a simple package that allows one to serialize and deserialize practically any data structure to and from bytes.

Installation

pip install packify

Status

Primary development is complete. Further development will be tracked in the issues. Historical changes can be found in the changelog.

Usage

Usage is simple: import the package and call the pack/unpack functions.

from packify import pack, unpack
from decimal import Decimal

data = {
    123: 432.1,
    "abc": "cba",
    b"abc": b"cba",
    "None": None,
    "true": True,
    "false": False,
    'list': [
        '123',
        123,
        b'123',
        True,
    ],
    'tuple': (
        '123',
        123,
        b'123',
        False,
    ),
    'set': {
        '123',
        123,
        b'123',
    },
    'Decimal': Decimal('123.321'),
}

packed = pack(data)
unpacked = unpack(packed)
assert type(packed) is bytes
assert unpacked == data

The following types are supported:

  • int
  • bool
  • float
  • Decimal
  • str
  • bytes
  • bytearray
  • NoneType
  • list
  • tuple
  • set
  • dict
Using with named tuples

If you want to use named tuples, you can do so by packing as a regular tuple and then unpacking it into the named tuple class.

from packify import pack, unpack
from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])

original = Point(1, 2)
packed = pack(tuple(original))
unpacked = Point(*unpack(packed))
assert unpacked == original

The recursive features of pack and unpack will not work with named tuples nested within data structures. Using a Packable implementation is a better approach if you need custom data types nested within data structures (see below).


Additionally, a simple duck-type interface/protocol, Packable, is included. Any more complex data structure can be handled if it implements the Packable interface. Packable is defined as follows:

@runtime_checkable
class Packable(Protocol):
    def pack(self) -> bytes:
        """Packs the instance into bytes."""
        ...

    @classmethod
    def unpack(cls, data: bytes, inject: dict = {}) -> Packable:
        """Unpacks an instance from bytes. Must accept dependency
            injection to unpack other Packable types.
        """
        ...

If a class that implements Packable is used, then it needs to be included in the inject parameter for calls to unpack.

Example
from dataclasses import dataclass, field
from packify import pack, unpack

@dataclass
class Thing:
    data: str = field()
    amount: int = field()
    fraction: float = field()
    parts: list = field()
    def __eq__(self, other) -> bool:
        return type(self) is type(other) and self.pack() == other.pack()
    def pack(self) -> bytes:
        return pack((self.data, self.amount, self.fraction, self.parts))
    @classmethod
    def unpack(cls, data: bytes, /, *, inject: dict = {}):
        return cls(*unpack(data, inject={**globals(), **inject}))

thing = Thing("hello world", 123, 420.69, ['a', b'b', 3])
packed = pack(thing)
unpacked = unpack(packed, inject={'Thing': Thing})
assert unpacked == thing
# alternately, the easier but less specific method is to copy globals
unpacked = unpack(packed, inject={**globals()})
assert unpacked == thing

As long as the class implements the Packable protocol, it can be included in lists, sets, tuples, and dicts (assuming it is hashable for set or to be used as a dict key), and it will just work.

The pack function will raise a TypeError if the data is not serializable and an OverflowError if a serialized value would exceed the 32-bit length limit, and the unpack function will raise a ValueError if the data is truncated or corrupt, or a DependencyError if it is unable to find or use a Packable class to unpack the relevant item. The MissingDependencyError and InvalidDependencyError subclasses distinguish a missing dependency from an invalid one.

For convenience/use in annotations, a SerializableType is exported which includes the above type information.

Additional documentation can be found in dox.md, which was generated automagically by autodox.

CLI

The packify package includes a CLI for exporting the agent skill to various AI coding environments:

# Print version
packify --version

# Print skill to stdout
packify skill

# Save skill to a specific directory
packify skill --output path/to/skills

# Install skill for specific AI tools
packify opencode   # .opencode/skills/packify/SKILL.md
packify claude     # .claude/skills/packify/SKILL.md
packify cursor     # .cursor/skills/packify/SKILL.md
packify codex      # .agents/skills/packify/SKILL.md

This makes the packify skill available to AI agents so they can understand and use the library effectively.

More Resources

Check out the Pycelium discord server. If you experience a problem, please discuss it on the Discord server. All suggestions for improvement are also welcome, and the best place for that is also Discord. If you experience a bug and do not use Discord, open an issue on Github.

Tests

There are only 40 tests, and they consist of e2e tests of both the pack and unpack functions, edge case/error detection unit tests, a few tests covering reported bugs, and 2 fuzz tests to broaden the coverage of the test suite added after the v0.3.0 refactor. To run the tests, clone the repository and use the following:

python -m unittest discover tests/
# or
python tests/test_serialization.py
python tests/test_fuzzy.py

Benchmarks

scripts/benchmark.py measures pack and unpack throughput over a deterministic corpus and reports the minimum and median nanoseconds per operation for each workload:

python scripts/benchmark.py

Use --direction to benchmark only one operation, and repeat --rev to compare revisions. Each revision's packify package is loaded in an isolated subprocess, so before and after comparisons are reproducible without touching the working tree:

python scripts/benchmark.py --direction both
python scripts/benchmark.py --rev 7f20e8e --rev 434e9f7

Run python scripts/benchmark.py --help for all options.

License

Copyright (c) 2026 Jonathan Voss (k98kurz)

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Metadata

Release files for packify 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for packify 0.4.0
File Size Uploaded Provenance
packify-0.4.0.tar.gz 13.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for packify 0.4.0
File Interpreter ABI Platform
packify-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.4 kB

Release files / packify-0.4.0.tar.gz

Download URL packify-0.4.0.tar.gz
Size 13.5 kB
Tags Source
SHA-256 checksum
How to use checksums
897acefd757e0337473455d48b6dd3572b0b1f25105ec0a66d00870e08d6ddcc
BLAKE2b-256 checksum
How to use checksums
fd8f0c4fbff3ed69454ebf9d92a2c43efd42f7aa7e4c5b0c039c58c4a87ec79c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.17

Release files / packify-0.4.0-py3-none-any.whl

Download URL packify-0.4.0-py3-none-any.whl
Size 13.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7e4653c6d8a5e2d7fa1ad04d0d08a79c82ef4e0ad0917eb6bb564bb38737d182
BLAKE2b-256 checksum
How to use checksums
b68979e46fcce0be6bd2fe00a554cbbaa2a32c15a18d2f64b31d5dbb0f9b290a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.17

Security for packify 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page