blob: e77e24bbeac4aba2df9bf41fe85a96a28e883db3 [file] [view]
---
title: Python Native Serialization
sidebar_position: 2
id: native
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---
Python native serialization is the Python-only wire mode selected with `xlang=False`. Use it when
every writer and reader is Python and the payload should follow Python's object model instead of
the portable xlang type system.
Use [Cross-Language Interoperability](basic-serialization.md#cross-language-interoperability), the default Python mode, when bytes must be read
by Java, C++, Go, Rust, JavaScript/TypeScript, C#, Swift, Dart, Scala, Kotlin,
or another non-Python Fory implementation.
## When To Use Native Serialization
Use native serialization when:
- A payload is produced and consumed only by Python applications.
- You are replacing `pickle` or `cloudpickle` for Python-only object graphs.
- The data model includes functions, lambdas, local classes, methods, or Python reduction hooks.
- The graph can contain shared objects or cycles that need Python reference tracking.
- You need pickle protocol 5-style out-of-band buffers for large Python data objects.
Native mode can serialize Python-specific values such as global functions, local functions, lambdas,
local classes, methods, and objects customized with `__getstate__`, `__setstate__`, `__reduce__`,
or `__reduce_ex__`. Those values are not valid xlang payloads.
## Create a Native-Mode Fory Instance
Create `Fory` with `xlang=False`:
```python
import pyfory
fory = pyfory.Fory(xlang=False, ref=False, strict=True)
```
Keep `strict=True` for registered, trusted type surfaces. Use `strict=False` only when native-mode
payloads need dynamic Python types such as functions, local classes, or objects reconstructed by
reduction hooks.
## Common Usage
```python
import pyfory
fory = pyfory.Fory(xlang=False, ref=True, strict=False)
data = fory.dumps({"name": "Alice", "age": 30, "scores": [95, 87, 92]})
print(fory.loads(data))
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
person = Person("Bob", 25)
data = fory.dumps(person)
print(fory.loads(data)) # Person(name='Bob', age=25)
```
Use `dumps`/`loads` for pickle-style APIs, or `serialize`/`deserialize` when matching the xlang
API shape in code that switches modes explicitly.
## Security And Dynamic Types
Native mode can reconstruct Python objects that execute import and construction logic during
deserialization. Treat untrusted native-mode bytes the same way you would treat untrusted pickle
bytes.
- Keep `strict=True` when deserializing data that should contain only registered or built-in types.
- Use `strict=False` only for trusted payloads that require dynamic Python classes or functions.
- Provide a `policy=` deserialization policy when dynamic types are required but the accepted type
surface should still be restricted.
- Do not use xlang/native mode choice as a security control. Apply strict mode, policies,
registration, and resource limits based on the payload source.
## Python-specific values and hooks
See [Functions, Classes, and Methods](functions-classes-methods.md) for callable and type values, then [Serialization Hooks](serialization-hooks.md) for reduction, state, construction, and pickle/cloudpickle migration.
## References And Cycles
Enable `ref=True` when object identity, shared references, or cycles must round-trip:
```python
import pyfory
fory = pyfory.Fory(xlang=False, ref=True, strict=True)
node = {}
node["self"] = node
data = fory.dumps(node)
decoded = fory.loads(data)
assert decoded["self"] is decoded
```
Disable reference tracking for value-shaped payloads that do not need identity preservation. It
keeps the payload smaller and the hot path simpler.
## Out-of-Band Buffers
Python native mode can use pickle protocol 5-style out-of-band buffers for large binary payloads
and data structures backed by external memory:
```python
import pickle
import pyfory
data = b"Large binary data"
pickle_buffer = pickle.PickleBuffer(data)
buffer_objects = []
fory = pyfory.Fory(xlang=False, ref=True, strict=False)
serialized = fory.dumps(pickle_buffer, buffer_callback=buffer_objects.append)
buffers = [obj.getbuffer() for obj in buffer_objects]
decoded = fory.loads(serialized, buffers=buffers)
assert bytes(decoded.raw()) == data
```
Use this when the payload stays in Python and large buffers should avoid extra copies. See
[Out-of-Band Serialization](out-of-band.md).
## Native And Xlang Comparison
| Requirement | Use native serialization | Use xlang serialization |
| ------------------------------------------ | ------------------------ | ----------------------- |
| Python-only payloads | Yes | Optional |
| Non-Python readers or writers | No | Yes |
| Functions, lambdas, local classes | Yes | No |
| `__reduce__` / `__getstate__` object hooks | Yes | No |
| Pickle/cloudpickle replacement | Yes | No |
| Portable type mapping across languages | No | Yes |
## Performance Comparison
```python
import pyfory
import pickle
import timeit
fory = pyfory.Fory(xlang=False, ref=True, strict=False)
obj = {f"key{i}": f"value{i}" for i in range(10000)}
print(f"Fory: {timeit.timeit(lambda: fory.dumps(obj), number=1000):.3f}s")
print(f"Pickle: {timeit.timeit(lambda: pickle.dumps(obj), number=1000):.3f}s")
```
## Troubleshooting
### Another language cannot read the payload
The writer is using native serialization. Rebuild it with `xlang=True`, register portable schemas
on every peer, and avoid Python-only values such as lambdas or local classes.
### A dynamic class or function fails to deserialize
Use `strict=False` for trusted payloads and provide a deserialization `policy=` when only selected
dynamic types should be accepted.
### A cycle does not round-trip
Create the `Fory` instance with `ref=True`.
### A value depends on pickle hooks
Keep the payload in native mode. Xlang mode does not execute Python `__reduce__`,
`__reduce_ex__`, `__getstate__`, or `__setstate__` object reconstruction hooks.
## Related Topics
- [Cross-Language Interoperability](basic-serialization.md#cross-language-interoperability) - Cross-language Python payloads
- [Configuration](configuration.md) - Python `Fory` options
- [Out-of-Band Serialization](out-of-band.md) - Zero-copy buffer support
- [Configuration](configuration.md#security) - Deserialization policies