or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/pypi-erlang_py

Erlang Binary Term Format for Python with encoding/decoding of Erlang/Elixir data structures

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/erlang-py@2.0.x

To install, run

npx @tessl/cli install tessl/pypi-erlang_py@2.0.0

0

# Erlang_py

1

2

A Python library that provides complete encoding and decoding functionality for the Erlang Binary Term Format (ETF). This library enables Python applications to communicate with Erlang/Elixir systems via binary protocols, message passing, and data serialization, with enhanced Elixir compatibility features including symmetrical encoding/decoding and proper None/nil conversion.

3

4

## Package Information

5

6

- **Package Name**: erlang_py

7

- **Language**: Python

8

- **Installation**: `pip install erlang_py`

9

10

## Core Imports

11

12

```python

13

import erlang

14

```

15

16

## Basic Usage

17

18

```python

19

import erlang

20

21

# Encode Python data to Erlang binary format

22

data = {'key': 'value', 'number': 42, 'list': [1, 2, 3]}

23

binary_data = erlang.term_to_binary(data)

24

25

# Decode Erlang binary back to Python

26

decoded_data = erlang.binary_to_term(binary_data)

27

print(decoded_data) # {'key': 'value', 'number': 42, 'list': [1, 2, 3]}

28

29

# Working with Erlang-specific types

30

atom = erlang.OtpErlangAtom('hello')

31

binary_atom = erlang.term_to_binary(atom)

32

decoded_atom = erlang.binary_to_term(binary_atom)

33

34

# Compressed encoding for large data

35

large_data = [i for i in range(10000)]

36

compressed_binary = erlang.term_to_binary(large_data, compressed=True)

37

decompressed_data = erlang.binary_to_term(compressed_binary)

38

```

39

40

## Architecture

41

42

The Erlang Binary Term Format (ETF) is a standardized binary protocol used by Erlang and Elixir for serializing data structures. This library provides a complete Python implementation that enables seamless interoperability between Python applications and Erlang/Elixir systems.

43

44

**Key Design Concepts:**

45

- **Symmetric encoding/decoding**: Data can be round-trip converted between Python and Erlang without loss

46

- **Type preservation**: Erlang-specific types (atoms, PIDs, references) are preserved using wrapper classes

47

- **Compression support**: Large data structures can be compressed using zlib to reduce network overhead

48

- **Binary compatibility**: Full compliance with the official Erlang External Term Format specification

49

50

This architecture makes the library suitable for distributed systems, message queues, and any scenario requiring Python-Erlang data exchange.

51

52

## Capabilities

53

54

### Binary Term Format Conversion

55

56

Core functionality for encoding Python data to Erlang Binary Term Format and decoding Erlang terms back to Python objects.

57

58

```python { .api }

59

def binary_to_term(data):

60

"""

61

Decode Erlang terms within binary data into Python types.

62

63

Parameters:

64

- data: bytes - Binary data containing Erlang terms

65

66

Returns:

67

Decoded Python object (any type)

68

69

Raises:

70

- ParseException: If data is invalid or cannot be parsed

71

"""

72

73

def term_to_binary(term, compressed=False):

74

"""

75

Encode Python types into Erlang terms in binary data.

76

77

Parameters:

78

- term: Any - Python object to encode

79

- compressed: bool or int - Compression level (False=no compression, True=level 6, 0-9=specific level)

80

81

Returns:

82

Binary data (bytes) containing encoded Erlang terms

83

84

Raises:

85

- InputException: If compression level is invalid (must be in [0..9])

86

- OutputException: If encoding fails (e.g., uint32 overflow)

87

"""

88

```

89

90

### Erlang Data Type Wrappers

91

92

Specialized classes representing Erlang-specific data types that don't have direct Python equivalents.

93

94

```python { .api }

95

class OtpErlangAtom:

96

"""Represents an Erlang atom."""

97

98

def __init__(self, value):

99

"""

100

Create an Erlang atom.

101

102

Parameters:

103

- value: int | str | bytes - Integer for atom cache reference,

104

string or bytes for atom name

105

"""

106

107

def binary(self):

108

"""Return encoded representation of the atom."""

109

110

class OtpErlangBinary:

111

"""Represents an Erlang binary with optional bit-level precision."""

112

113

def __init__(self, value, bits=8):

114

"""

115

Create an Erlang binary.

116

117

Parameters:

118

- value: bytes - Binary value

119

- bits: int - Number of bits in last byte (default: 8)

120

"""

121

122

def binary(self):

123

"""Return encoded representation of the binary."""

124

125

class OtpErlangFunction:

126

"""Represents an Erlang function."""

127

128

def __init__(self, tag, value):

129

"""

130

Create an Erlang function.

131

132

Parameters:

133

- tag: Function tag identifier

134

- value: Function value/data

135

"""

136

137

def binary(self):

138

"""Return encoded representation of the function."""

139

140

class OtpErlangList:

141

"""Represents an Erlang list with optional improper list support."""

142

143

def __init__(self, value, improper=False):

144

"""

145

Create an Erlang list.

146

147

Parameters:

148

- value: list - List contents

149

- improper: bool - Whether list has no empty list tail (default: False)

150

"""

151

152

def binary(self):

153

"""Return encoded representation of the list."""

154

155

class OtpErlangPid:

156

"""Represents an Erlang process identifier (PID)."""

157

158

def __init__(self, node, id_value, serial, creation):

159

"""

160

Create an Erlang PID.

161

162

Parameters:

163

- node: OtpErlangAtom - Node atom where the process resides

164

- id_value: bytes - Process ID value

165

- serial: bytes - Serial number

166

- creation: bytes - Creation identifier

167

"""

168

169

def binary(self):

170

"""Return encoded representation of the PID."""

171

172

class OtpErlangPort:

173

"""Represents an Erlang port."""

174

175

def __init__(self, node, id_value, creation):

176

"""

177

Create an Erlang port.

178

179

Parameters:

180

- node: OtpErlangAtom - Node atom where the port resides

181

- id_value: bytes - Port ID value

182

- creation: bytes - Creation identifier

183

"""

184

185

def binary(self):

186

"""Return encoded representation of the port."""

187

188

class OtpErlangReference:

189

"""Represents an Erlang reference."""

190

191

def __init__(self, node, id_value, creation):

192

"""

193

Create an Erlang reference.

194

195

Parameters:

196

- node: OtpErlangAtom - Node atom where the reference was created

197

- id_value: bytes - Reference ID value

198

- creation: bytes - Creation identifier

199

"""

200

201

def binary(self):

202

"""Return encoded representation of the reference."""

203

```

204

205

### Exception Handling

206

207

Exception classes for different error conditions during encoding/decoding operations.

208

209

```python { .api }

210

class InputException(ValueError):

211

"""

212

InputError describes problems with function input parameters.

213

214

Extends ValueError.

215

"""

216

217

def __init__(self, s):

218

"""

219

Parameters:

220

- s: Error message string

221

"""

222

223

class OutputException(TypeError):

224

"""

225

OutputError describes problems with creating function output data.

226

227

Extends TypeError.

228

"""

229

230

def __init__(self, s):

231

"""

232

Parameters:

233

- s: Error message string

234

"""

235

236

class ParseException(SyntaxError):

237

"""

238

ParseError provides specific parsing failure information.

239

240

Extends SyntaxError.

241

"""

242

243

def __init__(self, s):

244

"""

245

Parameters:

246

- s: Error message string

247

"""

248

```

249

250

### Additional Capabilities

251

252

Advanced functionality that extends beyond the core binary term format conversion.

253

254

```python { .api }

255

def consult(string_in):

256

"""

257

Provide file:consult/1 functionality with Python types.

258

259

Parse textual Erlang data representation into Python objects,

260

avoiding external dependencies for simple data parsing scenarios.

261

262

Parameters:

263

- string_in: str - String containing textual Erlang data

264

265

Returns:

266

Python object representation of the parsed Erlang data

267

268

Note: This function is not in __all__ and should be considered

269

internal/advanced API. Use with caution in production code.

270

271

Raises:

272

- ParseException: If the string cannot be parsed as valid Erlang data

273

"""

274

```

275

276

## Types

277

278

```python { .api }

279

# All OTP classes support these common methods:

280

def __repr__(self) -> str:

281

"""Return string representation of the object."""

282

283

def __hash__(self) -> int:

284

"""Return hash value for use in sets and dictionaries."""

285

286

def __eq__(self, other) -> bool:

287

"""Test equality with another object."""

288

289

# All exception classes support:

290

def __str__(self) -> str:

291

"""Return string representation of the error message."""

292

293

# Internal types available for advanced usage:

294

class frozendict(dict):

295

"""

296

Immutable dictionary that cannot be modified after creation.

297

Used internally for representing Erlang maps.

298

Available for import but not part of official public API.

299

"""

300

301

def __init__(self, *args, **kw):

302

"""

303

Create immutable dictionary, recursively converting nested dicts.

304

305

Parameters:

306

- *args: Positional arguments passed to dict constructor

307

- **kw: Keyword arguments passed to dict constructor

308

"""

309

310

def __hash__(self) -> int:

311

"""Return hash value based on dictionary contents."""

312

313

def __setitem__(self, key, value):

314

"""Raise TypeError - frozendict is immutable."""

315

316

def __delitem__(self, key):

317

"""Raise TypeError - frozendict is immutable."""

318

319

def clear(self):

320

"""Raise TypeError - frozendict is immutable."""

321

322

def pop(self, key, *args):

323

"""Raise TypeError - frozendict is immutable."""

324

325

def popitem(self):

326

"""Raise TypeError - frozendict is immutable."""

327

328

def setdefault(self, key, default=None):

329

"""Raise TypeError - frozendict is immutable."""

330

331

def update(self, *args, **kw):

332

"""Raise TypeError - frozendict is immutable."""

333

```

334

335

## Usage Examples

336

337

### Working with Atoms

338

339

```python

340

import erlang

341

342

# Create atoms from strings

343

atom1 = erlang.OtpErlangAtom("hello")

344

atom2 = erlang.OtpErlangAtom("world")

345

346

# Encode and decode

347

encoded = erlang.term_to_binary([atom1, atom2])

348

decoded = erlang.binary_to_term(encoded)

349

350

print(decoded) # [OtpErlangAtom('hello'), OtpErlangAtom('world')]

351

```

352

353

### Working with Complex Data Structures

354

355

```python

356

import erlang

357

358

# Mix of Python types and Erlang-specific types

359

complex_data = {

360

'atoms': [erlang.OtpErlangAtom('ok'), erlang.OtpErlangAtom('error')],

361

'binary': erlang.OtpErlangBinary(b'hello world'),

362

'regular_list': [1, 2, 3, 'string'],

363

'erlang_list': erlang.OtpErlangList([1, 2, 3]),

364

'nested': {'inner': {'value': 42}}

365

}

366

367

# Round-trip encoding/decoding

368

encoded = erlang.term_to_binary(complex_data)

369

decoded = erlang.binary_to_term(encoded)

370

371

# Verify round-trip integrity

372

assert decoded == complex_data

373

```

374

375

### Error Handling

376

377

```python

378

import erlang

379

380

try:

381

# Invalid binary data

382

erlang.binary_to_term(b'invalid')

383

except erlang.ParseException as e:

384

print(f"Parse error: {e}")

385

386

try:

387

# Invalid compression level

388

erlang.term_to_binary("test", compressed=15)

389

except erlang.InputException as e:

390

print(f"Input error: {e}")

391

```

392

393

### Compression

394

395

```python

396

import erlang

397

398

large_data = list(range(10000))

399

400

# No compression

401

uncompressed = erlang.term_to_binary(large_data)

402

403

# Default compression (level 6)

404

compressed = erlang.term_to_binary(large_data, compressed=True)

405

406

# Specific compression level

407

highly_compressed = erlang.term_to_binary(large_data, compressed=9)

408

409

print(f"Uncompressed: {len(uncompressed)} bytes")

410

print(f"Compressed: {len(compressed)} bytes")

411

print(f"Highly compressed: {len(highly_compressed)} bytes")

412

413

# All decode to the same data

414

assert erlang.binary_to_term(uncompressed) == large_data

415

assert erlang.binary_to_term(compressed) == large_data

416

assert erlang.binary_to_term(highly_compressed) == large_data

417

```

418

419

### Textual Data Parsing

420

421

```python

422

import erlang

423

424

# Parse textual Erlang data (advanced usage)

425

erlang_text = "{ok, [1, 2, 3]}."

426

try:

427

parsed_data = erlang.consult(erlang_text)

428

print(parsed_data) # {'ok': [1, 2, 3]}

429

except erlang.ParseException as e:

430

print(f"Parse error: {e}")

431

432

# Note: consult is not in __all__ and should be used carefully

433

# For most use cases, prefer binary_to_term/term_to_binary

434

```