# TSRKit Types
Performant Python Typings library for type-safe binary serialization, JSON encoding, and data validation with zero dependencies.
Perfect for network protocols, game state serialization, configuration files, and any application requiring efficient, validated data handling.
## Features
- **🔒 Type Safe**: Strong typing with runtime validation and type hints
- **âš¡ High Performance**: Efficient binary encoding with zero-copy operations where possible
- **📦 Zero Dependencies**: No external runtime dependencies
- **🔄 Dual Serialization**: Both binary and JSON serialization support
- **🧩 Generic Support**: Parameterized types for flexible, reusable code
- **🎯 Memory Efficient**: Minimal overhead, extends built-in Python types
- **🚀 Easy to Use**: Intuitive API with comprehensive type system
## Installation
```bash
pip install tsrkit-types
```
## Type Categories
### Integer Types (extension of Python int)
#### Unsigned Integers (`Uint`)
The `Uint` class provides both fixed-size and variable-size unsigned integers.
**Fixed-Size Integers:**
```python
from tsrkit_types.integers import Uint
# Pre-defined types
value = Uint[8](255) # 8-bit unsigned integer (0-255)
value = Uint[16](65535) # 16-bit unsigned integer (0-65535)
value = Uint[32](42949) # 32-bit unsigned integer
value = Uint[64](1844674) # 64-bit unsigned integer
# Dynamic size specification
U128 = Uint[128] # 128-bit unsigned integer
value = U128(123456789)
# Encoding/Decoding
encoded = value.encode() # Encode to bytes
decoded = U8.decode(encoded) # Decode from bytes
size = value.encode_size() # Get encoded size
```
**Variable-Size General Integers:**
```python
# General integers (supports up to 2^64 - 1 with variable encoding)
num = Uint(1000) # Variable-length encoding
encoded = num.encode()
decoded = Uint.decode(encoded)
# Arithmetic operations preserve type
a = Uint[8](10)
b = Uint[8](20)
result = a + b # result is Uint[8](30)
```
**Encoding Details:**
- Fixed-size integers use little-endian encoding
- Variable-size integers use a compact encoding scheme that optimizes for smaller values
- Values < 2^7 are encoded in 1 byte
- Larger values use a variable-length prefix encoding
### String Types (extension of Python str)
#### UTF-8 Strings (`String`)
```python
from tsrkit_types.string import String
# Creation
text = String("Hello, World!")
text = String("Unicode: 🚀🔥")
# Properties
length = len(text) # Character count
text_str = str(text) # Convert to Python str
# Encoding/Decoding
encoded = text.encode() # [length][utf8_bytes]
decoded = String.decode(encoded)
# JSON serialization
json_data = text.to_json() # Returns the string value
restored = String.from_json(json_data)
```
**Encoding Format:**
- Length prefix (variable-length `Uint`) followed by UTF-8 bytes
- String length is measured in UTF-16 code units (like Python strings)
### Boolean Types
#### Boolean (`Bool`)
```python
from tsrkit_types.bool import Bool
# Creation
true_val = Bool(True)
false_val = Bool(False)
# Usage
if true_val: # Supports truthiness testing
print("It's true!")
# Encoding/Decoding
encoded = true_val.encode() # 1 byte: 0x01 or 0x00
decoded = Bool.decode(encoded)
# JSON serialization
json_str = true_val.to_json() # "true" or "false"
restored = Bool.from_json("true")
```
### Null Types
```python
from tsrkit_types.null import Null
# Null type
null_val = Null # Singleton null value
```
### Choice and Option Types
#### Choice (Union Types)
```python
from tsrkit_types.choice import Choice
from tsrkit_types.integers import U8, U16
from tsrkit_types.string import String
# Anonymous choice
IntOrString = Choice[U8, String]
value = IntOrString(U8(42))
value = IntOrString(String("hello"))
# Switch the choice
value.set(String("world"))
inner = value.unwrap() # Get the inner value
# Named choice with custom keys
class Result(Choice):
success: String
error: U8
result = Result(String("OK"))
result = Result(U8(404), key="error")
# JSON serialization
json_data = result.to_json() # {"success": "OK"} or {"error": 404}
restored = Result.from_json(json_data)
# Encoding
encoded = result.encode() # [variant_index][value]
decoded = Result.decode(encoded)
```
#### Option (Optional Value - T | Null)
```python
from tsrkit_types.option import Option
from tsrkit_types.integers import U32
# Optional value
opt_val = Option[U32](U32(100)) # Some value
empty_opt = Option[U32]() # None/Null
# Check if has value
if opt_val: # Truthiness test
value = opt_val.unwrap() # Get the U32 value
# Encoding (more efficient than general Choice)
encoded = opt_val.encode() # 1 byte tag + optional value
decoded = Option[U32].decode(encoded)
```
### Container Types
#### Sequences (Extension of Python list)
```python
from tsrkit_types.sequences import Array, Vector, TypedArray, TypedVector
from tsrkit_types.integers import U16
# Fixed-size array
FixedArray = Array[10] # Array of exactly 10 elements
arr = FixedArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# Typed fixed-size array
TypedFixedArray = TypedArray[U16, 5] # 5 U16 elements
typed_arr = TypedFixedArray([U16(1), U16(2), U16(3), U16(4), U16(5)])
# Variable-size vector
DynamicVector = Vector[100] # Vector with max 100 elements
vec = DynamicVector([1, 2, 3])
# Typed variable-size vector
TypedDynamicVector = TypedVector[U16] # Vector of U16 elements
typed_vec = TypedDynamicVector([U16(1), U16(2)])
# Operations
vec.append(4) # Add element with validation
vec.extend([5, 6, 7]) # Add multiple elements
vec[0] = 100 # Set element with validation
# Encoding
encoded = typed_vec.encode() # [length?][element1][element2]...
decoded = TypedDynamicVector.decode(encoded)
```
**Sequence Types:**
- `Array[N]`: Fixed size, any element type
- `Vector`: Variable size, any element type
- `TypedArray[T, N]`: Fixed size with typed elements
- `TypedVector[T]`: Variable size with typed elements
- `BoundedVector[min, max]`: Size constrained vector
- `TypedBoundedVector[T, min, max]`: Typed and size constrained
#### Dictionary
```python
from tsrkit_types.dictionary import Dictionary
from tsrkit_types.string import String
from tsrkit_types.integers import U32
# Create dictionary type
StringToInt = Dictionary[String, U32]
data = StringToInt({
String("key1"): U32(100),
String("key2"): U32(200)
})
# Operations
data[String("key3")] = U32(300) # Add entry
value = data[String("key1")] # Get value
del data[String("key2")] # Remove entry
# Iteration
for key, value in data.items():
print(f"{key}: {value}")
# Encoding
encoded = data.encode() # [length][key1][value1][key2][value2]...
decoded = StringToInt.decode(encoded)
# JSON serialization
json_data = data.to_json() # {"key1": 100, "key3": 300}
restored = StringToInt.from_json(json_data)
```
### Bytes Types
The library provides two complementary byte array types: immutable `Bytes` (extending Python's built-in `bytes`) and mutable `ByteArray` (extending Python's `bytearray`). Both types share common functionality through a mixin architecture for bit conversion, JSON serialization, and binary encoding.
#### Bytes (Immutable - extension of Python bytes)
```python
from tsrkit_types.bytes import Bytes
# Creation
data = Bytes(b"Hello, binary world!")
data = Bytes([0x01, 0x02, 0x03, 0x04])
data = Bytes("48656c6c6f") # From hex string
# Shared Operations (via BytesMixin)
bits = data.to_bits() # Convert to bit list [True, False, True, ...]
data2 = Bytes.from_bits(bits) # Create from bit list
# Properties
length = len(data) # Byte length
raw_bytes = bytes(data) # Convert to Python bytes
# Encoding/Decoding
encoded = data.encode() # [length][raw_bytes]
decoded = Bytes.decode(encoded)
# JSON serialization (hex encoded)
json_str = data.to_json() # "48656c6c6f2c2062696e61727920776f726c6421"
restored = Bytes.from_json(json_str)
restored2 = Bytes.from_json("0x48656c6c6f") # Supports 0x prefix
```
#### ByteArray (Mutable - extension of Python bytearray)
```python
from tsrkit_types.bytearray import ByteArray
# Creation
data = ByteArray(b"Hello, binary world!")
data = ByteArray([0x01, 0x02, 0x03, 0x04])
data = ByteArray("48656c6c6f") # From hex string
# Mutable Operations
data.append(0xFF) # Add single byte
data.extend([0xAB, 0xCD]) # Add multiple bytes
data.insert(0, 0x00) # Insert byte at position
data.pop() # Remove and return last byte
data.remove(0xFF) # Remove first occurrence
data.clear() # Remove all bytes
data.reverse() # Reverse in-place
# Indexing and Slicing (mutable)
data[0] = 0x42 # Set byte at index
data[1:3] = [0x43, 0x44] # Set slice
del data[0] # Delete byte at index
# Shared Operations (via BytesMixin) - same as Bytes
bits = data.to_bits() # Convert to bit list
data2 = ByteArray.from_bits(bits) # Create from bit list
# Properties and Conversion
length = len(data) # Byte length
raw_bytes = bytes(data) # Convert to immutable bytes
immutable = Bytes(data) # Convert to immutable Bytes
# Encoding/Decoding (same interface as Bytes)
encoded = data.encode() # [length][raw_bytes]
decoded = ByteArray.decode(encoded)
# JSON serialization (same interface as Bytes)
json_str = data.to_json() # "48656c6c6f..."
restored = ByteArray.from_json(json_str)
```
**Key Differences:**
- **Bytes**: Immutable, extends `bytes`, memory-efficient for read-only data
- **ByteArray**: Mutable, extends `bytearray`, suitable for dynamic byte manipulation
- **Shared Functionality**: Both support identical bit conversion, JSON serialization, and binary encoding through `BytesMixin`
**Common Features (Both Types):**
- Bit-level conversion with MSB/LSB support
- Hex string JSON serialization with 0x prefix support
- Efficient binary encoding with length prefix
- String representation and validation
- Memory-efficient operations
#### Bits (Bit Arrays - Sequence of bool)
```python
from tsrkit_types.bits import Bits
# Creation
bits = Bits([True, False, True, True, False])
bits = Bits.from_hex("1A3F") # From hex string
bits = Bits.from_int(42, 8) # From integer with bit width
# Fixed-size parameterized bits
FixedBits = Bits[8] # Exactly 8 bits
fixed = FixedBits([True, False, True, True, False, False, True, False])
# Operations
bit_val = bits[0] # Get bit at index
bits[1] = True # Set bit at index
bits.append(False) # Add bit
bits.extend([True, False]) # Add multiple bits
# Conversion
hex_str = bits.to_hex() # Convert to hex string
int_val = bits.to_int() # Convert to integer
# Bit order specification
bits_msb = Bits([True, False], bit_order="MSB") # Most significant bit first
bits_lsb = Bits([True, False], bit_order="LSB") # Least significant bit first
# Encoding
encoded = bits.encode() # [length][packed_bits]
decoded = Bits.decode(encoded)
# JSON serialization
json_str = bits.to_json() # Hex string representation
restored = Bits.from_json(json_str)
```
### Enum (Extension of Python Enum, with Codable + JSON support)
```python
from tsrkit_types.enum import Enum
# Define enum
class Color(Enum):
RED = 0
GREEN = 1
BLUE = 2
# Usage
color = Color.RED
color_val = Color(1) # Color.GREEN
# String conversion
name = color.name # "RED"
value = color.value # 0
# Encoding
encoded = color.encode() # Variable-length uint encoding
decoded = Color.decode(encoded)
# JSON serialization
json_data = color.to_json() # "RED"
restored = Color.from_json("GREEN")
```
### Structured Types
#### Struct Decorator (Extension of dataclasses)
```python
from tsrkit_types.struct import struct
from tsrkit_types.string import String
from tsrkit_types.integers import U8, U32
from dataclasses import field
@structure
class Person:
name: String
age: U8
@structure
class Employee:
person: Person
employee_id: U32
department: String = field(metadata={"default": String("Unknown")})
# Creation
person = Person(name=String("John Doe"), age=U8(30))
employee = Employee(
person=person,
employee_id=U32(12345),
department=String("Engineering")
)
# Access fields
print(employee.person.name) # "John Doe"
print(employee.employee_id) # 12345
# Encoding/Decoding
encoded = employee.encode() # Concatenated field encodings
decoded = Employee.decode(encoded)
# JSON serialization with custom field names
@structure
class CustomPerson:
name: String = field(metadata={"name": "full_name"})
age: U8
person = CustomPerson(name=String("Jane"), age=U8(25))
json_data = person.to_json() # {"full_name": "Jane", "age": 25}
restored = CustomPerson.from_json({"full_name": "Jane", "age": 25})
```
**Struct Features:**
- Automatic `Codable` implementation
- Field validation and type checking
- Default values via metadata
- Custom JSON field mapping
- Inheritance support
- Frozen/immutable variants
## Advanced Usage
### Custom Types
Implement your own `Codable` types:
```python
from tsrkit_types.itf.codable import Codable
from typing import Tuple, Union
class Point3D(Codable):
def __init__(self, x: float, y: float, z: float):
self.x, self.y, self.z = x, y, z
def encode_size(self) -> int:
return 24 # 3 doubles = 24 bytes
def encode_into(self, buffer: bytearray, offset: int = 0) -> int:
import struct
struct.pack_into('<ddd', buffer, offset, self.x, self.y, self.z)
return 24
@classmethod
def decode_from(cls, buffer: Union[bytes, bytearray, memoryview],
offset: int = 0) -> Tuple['Point3D', int]:
import struct
x, y, z = struct.unpack_from('<ddd', buffer, offset)
return cls(x, y, z), 24
```
### Configuration and Optimization
```python
# Optimize for specific use cases
@structure(frozen=True) # Immutable structs
class ImmutableData:
value: U64
# Memory-efficient sequences
CompactArray = TypedArray[U8, 1000] # 1000 bytes exactly
data = CompactArray([0] * 1000)
# Bounded containers for validation
BoundedList = BoundedVector[10, 100] # Between 10 and 100 elements
safe_list = BoundedList([0] * 50)
```
### Error Handling
```python
from tsrkit_types.integers import U8
try:
# Value out of range
invalid = U8(256) # Raises ValueError
except ValueError as e:
print(f"Range error: {e}")
try:
# Type mismatch in Choice
choice = Choice[U8, String](42) # Raises TypeError
except TypeError as e:
print(f"Type error: {e}")
try:
# Buffer too small for decoding
corrupted = b"\x01"
U32.decode(corrupted) # Raises ValueError
except ValueError as e:
print(f"Decode error: {e}")
```
## Core Interface
All types implement the `Codable` interface:
```python
from tsrkit_types.itf.codable import Codable
class MyType(Codable):
def encode_size(self) -> int: ... # Size needed for encoding
def encode_into(self, buffer: bytearray, offset: int = 0) -> int: ... # Encode into buffer
def encode(self) -> bytes: ... # Encode to new bytes object
@classmethod
def decode_from(cls, buffer: bytes, offset: int = 0) -> Tuple[T, int]: ... # Decode from buffer
@classmethod
def decode(cls, buffer: bytes, offset: int = 0) -> T: ... # Decode from buffer (convenience)
```
## Performance Considerations
### Encoding Efficiency
- **Fixed-size types** (U8, U16, etc.) have constant encoding size
- **Variable-size types** (general Uint) optimize for smaller values
- **Sequences** encode length only when necessary (variable-size)
- **Strings** use UTF-8 encoding with variable-length prefix
### Memory Usage
- Types extend built-in Python types where possible (int, str, list, dict)
- Zero-copy operations where feasible
- Minimal overhead for type metadata
### Best Practices
```python
# Prefer fixed-size types when range is known
user_id = U32(123456) # Better than Uint(123456)
# Use typed containers for homogeneous data
scores = TypedVector[U16]([100, 95, 87, 92])
# Batch operations for better performance
data = TypedArray[U8, 1000]([0] * 1000)
encoded = data.encode() # Single operation vs. encoding each element
# Reuse buffer for multiple encodings
buffer = bytearray(1024)
offset = 0
offset += value1.encode_into(buffer, offset)
offset += value2.encode_into(buffer, offset)
# Choose appropriate byte type for your use case
# Use Bytes for immutable binary data (memory efficient, read-only)
config_data = Bytes(b"static configuration")
# Use ByteArray for dynamic binary buffers (mutable, growing data)
dynamic_buffer = ByteArray()
dynamic_buffer.extend([0x01, 0x02])
dynamic_buffer.append(0x03)
dynamic_buffer.insert(0, 0x00) # Result: [0x00, 0x01, 0x02, 0x03]
```
## Examples
### Network Protocol
```**python**
@structure
class NetworkPacket:
packet_type: U8
session_id: U32
payload_length: U16
payload: Bytes
# Create packet
packet = NetworkPacket(
packet_type=U8(1),
session_id=U32(0x12345678),
payload_length=U16(13),
payload=Bytes(b"Hello, World!")
)
# Serialize for transmission
wire_data = packet.encode()
# Deserialize on receiver
received_packet = NetworkPacket.decode(wire_data)
```
### Configuration File
```python
@structure
class DatabaseConfig:
host: String
port: U16
username: String
password: String
max_connections: U8 = field(metadata={"default": U8(10)})
ssl_enabled: Bool = field(metadata={"default": Bool(True)})
# Create config
config = DatabaseConfig(
host=String("localhost"),
port=U16(5432),
username=String("admin"),
password=String("secret")
)
# Save to JSON
import json
with open("db_config.json", "w") as f:
json.dump(config.to_json(), f)
# Load from JSON
with open("db_config.json", "r") as f:
data = json.load(f)
config = DatabaseConfig.from_json(data)
```
### Game State Serialization
```python
class GameEntityType(Enum):
PLAYER = 0
ENEMY = 1
ITEM = 2
@structure
class Position:
x: U16
y: U16
@structure
class GameEntity:
entity_type: GameEntityType
position: Position
health: U8
name: String
@structure
class GameState:
level: U8
score: U32
entities: TypedVector[GameEntity]
# Create game state
state = GameState(
level=U8(1),
score=U32(1500),
entities=TypedVector[GameEntity]([
GameEntity(
entity_type=GameEntityType.PLAYER,
position=Position(x=U16(100), y=U16(200)),
health=U8(100),
name=String("Hero")
)
])
)
# Save/load game
save_data = state.encode()
loaded_state = GameState.decode(save_data)
```
## Development
### Setup
1. **Clone the repository:**
```bash
git clone https://github.com/chainscore/tsrkit-types.git
cd tsrkit-types
```
2. **Install development dependencies:**
```bash
pip install -e ".[dev]"
```
### Running Tests
**Run the full test suite:**
```bash
pytest
```
**Run tests with coverage:**
```bash
pytest --cov=tsrkit_types --cov-report=html
```
**Run tests in parallel (faster):**
```bash
pytest -n auto
```
**Run specific test categories:**
```bash
pytest tests/test_integers.py # Integer type tests
pytest tests/test_strings.py # String type tests
pytest tests/test_containers.py # Container type tests
pytest tests/test_structs.py # Struct tests
pytest tests/test_network.py # Network protocol tests
```
**Run tests with verbose output:**
```bash
pytest -v
```
**Skip slow tests:**
```bash
pytest -m "not slow"
```
### Build and Publish
1. Build the package:
```bash
python3 -m build --wheel
```
2. Publish the wheels:
```bash
twine upload dist/*
```
### Test Coverage
View the test coverage report:
```bash
# Generate HTML coverage report
pytest --cov=tsrkit_types --cov-report=html
open htmlcov/index.html # macOS
# or xdg-open htmlcov/index.html # Linux
```
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Contributing
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
## Requirements
- **Python**: >= 3.11
- **Runtime Dependencies**: None (zero dependencies!)
- **Development Dependencies**: pytest and plugins (see `pyproject.toml`)
Raw data
{
"_id": null,
"home_page": null,
"name": "tsrkit-types",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "serialization, binary, encoding, types, codable, json, validation, data-types, type-safety, zero-copy, protocol, struct, networking, performance, bytes, integers, strings",
"author": null,
"author_email": "chainscore-labs <hello@chainscore.finance>, prasad-kumkar <prasad@chainscore.finance>",
"download_url": null,
"platform": null,
"description": "# TSRKit Types\n\nPerformant Python Typings library for type-safe binary serialization, JSON encoding, and data validation with zero dependencies.\n\nPerfect for network protocols, game state serialization, configuration files, and any application requiring efficient, validated data handling.\n\n## Features\n\n- **\ud83d\udd12 Type Safe**: Strong typing with runtime validation and type hints\n- **\u26a1 High Performance**: Efficient binary encoding with zero-copy operations where possible \n- **\ud83d\udce6 Zero Dependencies**: No external runtime dependencies\n- **\ud83d\udd04 Dual Serialization**: Both binary and JSON serialization support\n- **\ud83e\udde9 Generic Support**: Parameterized types for flexible, reusable code\n- **\ud83c\udfaf Memory Efficient**: Minimal overhead, extends built-in Python types\n- **\ud83d\ude80 Easy to Use**: Intuitive API with comprehensive type system\n\n## Installation\n\n```bash\npip install tsrkit-types\n```\n\n## Type Categories\n\n### Integer Types (extension of Python int)\n\n#### Unsigned Integers (`Uint`)\n\nThe `Uint` class provides both fixed-size and variable-size unsigned integers.\n\n**Fixed-Size Integers:**\n```python\nfrom tsrkit_types.integers import Uint\n\n# Pre-defined types\nvalue = Uint[8](255) # 8-bit unsigned integer (0-255)\nvalue = Uint[16](65535) # 16-bit unsigned integer (0-65535) \nvalue = Uint[32](42949) # 32-bit unsigned integer\nvalue = Uint[64](1844674) # 64-bit unsigned integer\n\n# Dynamic size specification\nU128 = Uint[128] # 128-bit unsigned integer\nvalue = U128(123456789)\n\n# Encoding/Decoding\nencoded = value.encode() # Encode to bytes\ndecoded = U8.decode(encoded) # Decode from bytes\nsize = value.encode_size() # Get encoded size\n```\n\n**Variable-Size General Integers:**\n```python\n# General integers (supports up to 2^64 - 1 with variable encoding)\nnum = Uint(1000) # Variable-length encoding\nencoded = num.encode()\ndecoded = Uint.decode(encoded)\n\n# Arithmetic operations preserve type\na = Uint[8](10)\nb = Uint[8](20)\nresult = a + b # result is Uint[8](30)\n```\n\n**Encoding Details:**\n- Fixed-size integers use little-endian encoding\n- Variable-size integers use a compact encoding scheme that optimizes for smaller values\n- Values < 2^7 are encoded in 1 byte\n- Larger values use a variable-length prefix encoding\n\n### String Types (extension of Python str)\n\n#### UTF-8 Strings (`String`)\n\n```python\nfrom tsrkit_types.string import String\n\n# Creation\ntext = String(\"Hello, World!\")\ntext = String(\"Unicode: \ud83d\ude80\ud83d\udd25\")\n\n# Properties\nlength = len(text) # Character count\ntext_str = str(text) # Convert to Python str\n\n# Encoding/Decoding\nencoded = text.encode() # [length][utf8_bytes]\ndecoded = String.decode(encoded)\n\n# JSON serialization\njson_data = text.to_json() # Returns the string value\nrestored = String.from_json(json_data)\n```\n\n**Encoding Format:**\n- Length prefix (variable-length `Uint`) followed by UTF-8 bytes\n- String length is measured in UTF-16 code units (like Python strings)\n\n### Boolean Types\n\n#### Boolean (`Bool`)\n\n```python\nfrom tsrkit_types.bool import Bool\n\n# Creation\ntrue_val = Bool(True)\nfalse_val = Bool(False)\n\n# Usage\nif true_val: # Supports truthiness testing\n print(\"It's true!\")\n\n# Encoding/Decoding\nencoded = true_val.encode() # 1 byte: 0x01 or 0x00\ndecoded = Bool.decode(encoded)\n\n# JSON serialization\njson_str = true_val.to_json() # \"true\" or \"false\"\nrestored = Bool.from_json(\"true\")\n```\n\n### Null Types\n\n```python\nfrom tsrkit_types.null import Null\n\n# Null type\nnull_val = Null # Singleton null value\n```\n\n### Choice and Option Types\n\n#### Choice (Union Types)\n\n```python\nfrom tsrkit_types.choice import Choice\nfrom tsrkit_types.integers import U8, U16\nfrom tsrkit_types.string import String\n\n# Anonymous choice\nIntOrString = Choice[U8, String]\nvalue = IntOrString(U8(42))\nvalue = IntOrString(String(\"hello\"))\n\n# Switch the choice\nvalue.set(String(\"world\"))\ninner = value.unwrap() # Get the inner value\n\n# Named choice with custom keys\nclass Result(Choice):\n success: String\n error: U8\n\nresult = Result(String(\"OK\"))\nresult = Result(U8(404), key=\"error\")\n\n# JSON serialization\njson_data = result.to_json() # {\"success\": \"OK\"} or {\"error\": 404}\nrestored = Result.from_json(json_data)\n\n# Encoding\nencoded = result.encode() # [variant_index][value]\ndecoded = Result.decode(encoded)\n```\n\n#### Option (Optional Value - T | Null)\n\n```python\nfrom tsrkit_types.option import Option\nfrom tsrkit_types.integers import U32\n\n# Optional value\nopt_val = Option[U32](U32(100)) # Some value\nempty_opt = Option[U32]() # None/Null\n\n# Check if has value\nif opt_val: # Truthiness test\n value = opt_val.unwrap() # Get the U32 value\n\n# Encoding (more efficient than general Choice)\nencoded = opt_val.encode() # 1 byte tag + optional value\ndecoded = Option[U32].decode(encoded)\n```\n\n### Container Types\n\n#### Sequences (Extension of Python list)\n\n```python\nfrom tsrkit_types.sequences import Array, Vector, TypedArray, TypedVector\nfrom tsrkit_types.integers import U16\n\n# Fixed-size array\nFixedArray = Array[10] # Array of exactly 10 elements\narr = FixedArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n\n# Typed fixed-size array \nTypedFixedArray = TypedArray[U16, 5] # 5 U16 elements\ntyped_arr = TypedFixedArray([U16(1), U16(2), U16(3), U16(4), U16(5)])\n\n# Variable-size vector\nDynamicVector = Vector[100] # Vector with max 100 elements\nvec = DynamicVector([1, 2, 3])\n\n# Typed variable-size vector\nTypedDynamicVector = TypedVector[U16] # Vector of U16 elements\ntyped_vec = TypedDynamicVector([U16(1), U16(2)])\n\n# Operations\nvec.append(4) # Add element with validation\nvec.extend([5, 6, 7]) # Add multiple elements\nvec[0] = 100 # Set element with validation\n\n# Encoding\nencoded = typed_vec.encode() # [length?][element1][element2]...\ndecoded = TypedDynamicVector.decode(encoded)\n```\n\n**Sequence Types:**\n- `Array[N]`: Fixed size, any element type\n- `Vector`: Variable size, any element type \n- `TypedArray[T, N]`: Fixed size with typed elements\n- `TypedVector[T]`: Variable size with typed elements\n- `BoundedVector[min, max]`: Size constrained vector\n- `TypedBoundedVector[T, min, max]`: Typed and size constrained\n\n#### Dictionary\n\n```python\nfrom tsrkit_types.dictionary import Dictionary\nfrom tsrkit_types.string import String\nfrom tsrkit_types.integers import U32\n\n# Create dictionary type\nStringToInt = Dictionary[String, U32]\ndata = StringToInt({\n String(\"key1\"): U32(100),\n String(\"key2\"): U32(200)\n})\n\n# Operations\ndata[String(\"key3\")] = U32(300) # Add entry\nvalue = data[String(\"key1\")] # Get value\ndel data[String(\"key2\")] # Remove entry\n\n# Iteration\nfor key, value in data.items():\n print(f\"{key}: {value}\")\n\n# Encoding\nencoded = data.encode() # [length][key1][value1][key2][value2]...\ndecoded = StringToInt.decode(encoded)\n\n# JSON serialization\njson_data = data.to_json() # {\"key1\": 100, \"key3\": 300}\nrestored = StringToInt.from_json(json_data)\n```\n\n### Bytes Types\n\nThe library provides two complementary byte array types: immutable `Bytes` (extending Python's built-in `bytes`) and mutable `ByteArray` (extending Python's `bytearray`). Both types share common functionality through a mixin architecture for bit conversion, JSON serialization, and binary encoding.\n\n#### Bytes (Immutable - extension of Python bytes)\n\n```python\nfrom tsrkit_types.bytes import Bytes\n\n# Creation\ndata = Bytes(b\"Hello, binary world!\")\ndata = Bytes([0x01, 0x02, 0x03, 0x04])\ndata = Bytes(\"48656c6c6f\") # From hex string\n\n# Shared Operations (via BytesMixin)\nbits = data.to_bits() # Convert to bit list [True, False, True, ...]\ndata2 = Bytes.from_bits(bits) # Create from bit list\n\n# Properties\nlength = len(data) # Byte length\nraw_bytes = bytes(data) # Convert to Python bytes\n\n# Encoding/Decoding\nencoded = data.encode() # [length][raw_bytes]\ndecoded = Bytes.decode(encoded)\n\n# JSON serialization (hex encoded)\njson_str = data.to_json() # \"48656c6c6f2c2062696e61727920776f726c6421\"\nrestored = Bytes.from_json(json_str)\nrestored2 = Bytes.from_json(\"0x48656c6c6f\") # Supports 0x prefix\n```\n\n#### ByteArray (Mutable - extension of Python bytearray)\n\n```python\nfrom tsrkit_types.bytearray import ByteArray\n\n# Creation\ndata = ByteArray(b\"Hello, binary world!\")\ndata = ByteArray([0x01, 0x02, 0x03, 0x04])\ndata = ByteArray(\"48656c6c6f\") # From hex string\n\n# Mutable Operations\ndata.append(0xFF) # Add single byte\ndata.extend([0xAB, 0xCD]) # Add multiple bytes\ndata.insert(0, 0x00) # Insert byte at position\ndata.pop() # Remove and return last byte\ndata.remove(0xFF) # Remove first occurrence\ndata.clear() # Remove all bytes\ndata.reverse() # Reverse in-place\n\n# Indexing and Slicing (mutable)\ndata[0] = 0x42 # Set byte at index\ndata[1:3] = [0x43, 0x44] # Set slice\ndel data[0] # Delete byte at index\n\n# Shared Operations (via BytesMixin) - same as Bytes\nbits = data.to_bits() # Convert to bit list\ndata2 = ByteArray.from_bits(bits) # Create from bit list\n\n# Properties and Conversion\nlength = len(data) # Byte length \nraw_bytes = bytes(data) # Convert to immutable bytes\nimmutable = Bytes(data) # Convert to immutable Bytes\n\n# Encoding/Decoding (same interface as Bytes)\nencoded = data.encode() # [length][raw_bytes]\ndecoded = ByteArray.decode(encoded)\n\n# JSON serialization (same interface as Bytes)\njson_str = data.to_json() # \"48656c6c6f...\"\nrestored = ByteArray.from_json(json_str)\n```\n\n**Key Differences:**\n- **Bytes**: Immutable, extends `bytes`, memory-efficient for read-only data\n- **ByteArray**: Mutable, extends `bytearray`, suitable for dynamic byte manipulation\n- **Shared Functionality**: Both support identical bit conversion, JSON serialization, and binary encoding through `BytesMixin`\n\n**Common Features (Both Types):**\n- Bit-level conversion with MSB/LSB support\n- Hex string JSON serialization with 0x prefix support\n- Efficient binary encoding with length prefix\n- String representation and validation\n- Memory-efficient operations\n\n#### Bits (Bit Arrays - Sequence of bool)\n\n```python\nfrom tsrkit_types.bits import Bits\n\n# Creation\nbits = Bits([True, False, True, True, False])\nbits = Bits.from_hex(\"1A3F\") # From hex string\nbits = Bits.from_int(42, 8) # From integer with bit width\n\n# Fixed-size parameterized bits\nFixedBits = Bits[8] # Exactly 8 bits\nfixed = FixedBits([True, False, True, True, False, False, True, False])\n\n# Operations\nbit_val = bits[0] # Get bit at index\nbits[1] = True # Set bit at index\nbits.append(False) # Add bit\nbits.extend([True, False]) # Add multiple bits\n\n# Conversion\nhex_str = bits.to_hex() # Convert to hex string\nint_val = bits.to_int() # Convert to integer\n\n# Bit order specification\nbits_msb = Bits([True, False], bit_order=\"MSB\") # Most significant bit first\nbits_lsb = Bits([True, False], bit_order=\"LSB\") # Least significant bit first\n\n# Encoding\nencoded = bits.encode() # [length][packed_bits]\ndecoded = Bits.decode(encoded)\n\n# JSON serialization\njson_str = bits.to_json() # Hex string representation\nrestored = Bits.from_json(json_str)\n```\n\n### Enum (Extension of Python Enum, with Codable + JSON support)\n\n```python\nfrom tsrkit_types.enum import Enum\n\n# Define enum\nclass Color(Enum):\n RED = 0\n GREEN = 1\n BLUE = 2\n\n# Usage\ncolor = Color.RED\ncolor_val = Color(1) # Color.GREEN\n\n# String conversion\nname = color.name # \"RED\"\nvalue = color.value # 0\n\n# Encoding\nencoded = color.encode() # Variable-length uint encoding\ndecoded = Color.decode(encoded)\n\n# JSON serialization\njson_data = color.to_json() # \"RED\"\nrestored = Color.from_json(\"GREEN\")\n```\n\n### Structured Types\n\n#### Struct Decorator (Extension of dataclasses)\n\n```python\nfrom tsrkit_types.struct import struct\nfrom tsrkit_types.string import String\nfrom tsrkit_types.integers import U8, U32\nfrom dataclasses import field\n\n@structure\nclass Person:\n name: String\n age: U8\n \n@structure \nclass Employee:\n person: Person\n employee_id: U32\n department: String = field(metadata={\"default\": String(\"Unknown\")})\n\n# Creation\nperson = Person(name=String(\"John Doe\"), age=U8(30))\nemployee = Employee(\n person=person,\n employee_id=U32(12345),\n department=String(\"Engineering\")\n)\n\n# Access fields\nprint(employee.person.name) # \"John Doe\"\nprint(employee.employee_id) # 12345\n\n# Encoding/Decoding\nencoded = employee.encode() # Concatenated field encodings\ndecoded = Employee.decode(encoded)\n\n# JSON serialization with custom field names\n@structure\nclass CustomPerson:\n name: String = field(metadata={\"name\": \"full_name\"})\n age: U8\n\nperson = CustomPerson(name=String(\"Jane\"), age=U8(25))\njson_data = person.to_json() # {\"full_name\": \"Jane\", \"age\": 25}\nrestored = CustomPerson.from_json({\"full_name\": \"Jane\", \"age\": 25})\n```\n\n**Struct Features:**\n- Automatic `Codable` implementation\n- Field validation and type checking\n- Default values via metadata\n- Custom JSON field mapping\n- Inheritance support\n- Frozen/immutable variants\n\n## Advanced Usage\n\n### Custom Types\n\nImplement your own `Codable` types:\n\n```python\nfrom tsrkit_types.itf.codable import Codable\nfrom typing import Tuple, Union\n\nclass Point3D(Codable):\n def __init__(self, x: float, y: float, z: float):\n self.x, self.y, self.z = x, y, z\n \n def encode_size(self) -> int:\n return 24 # 3 doubles = 24 bytes\n \n def encode_into(self, buffer: bytearray, offset: int = 0) -> int:\n import struct\n struct.pack_into('<ddd', buffer, offset, self.x, self.y, self.z)\n return 24\n \n @classmethod\n def decode_from(cls, buffer: Union[bytes, bytearray, memoryview], \n offset: int = 0) -> Tuple['Point3D', int]:\n import struct\n x, y, z = struct.unpack_from('<ddd', buffer, offset)\n return cls(x, y, z), 24\n```\n\n### Configuration and Optimization\n\n```python\n# Optimize for specific use cases\n@structure(frozen=True) # Immutable structs\nclass ImmutableData:\n value: U64\n\n# Memory-efficient sequences\nCompactArray = TypedArray[U8, 1000] # 1000 bytes exactly\ndata = CompactArray([0] * 1000)\n\n# Bounded containers for validation\nBoundedList = BoundedVector[10, 100] # Between 10 and 100 elements\nsafe_list = BoundedList([0] * 50)\n```\n\n### Error Handling\n\n```python\nfrom tsrkit_types.integers import U8\n\ntry:\n # Value out of range\n invalid = U8(256) # Raises ValueError\nexcept ValueError as e:\n print(f\"Range error: {e}\")\n\ntry:\n # Type mismatch in Choice\n choice = Choice[U8, String](42) # Raises TypeError\nexcept TypeError as e:\n print(f\"Type error: {e}\")\n\ntry:\n # Buffer too small for decoding\n corrupted = b\"\\x01\"\n U32.decode(corrupted) # Raises ValueError\nexcept ValueError as e:\n print(f\"Decode error: {e}\")\n```\n\n\n## Core Interface\n\nAll types implement the `Codable` interface:\n\n```python\nfrom tsrkit_types.itf.codable import Codable\n\nclass MyType(Codable):\n def encode_size(self) -> int: ... # Size needed for encoding\n def encode_into(self, buffer: bytearray, offset: int = 0) -> int: ... # Encode into buffer\n def encode(self) -> bytes: ... # Encode to new bytes object\n \n @classmethod\n def decode_from(cls, buffer: bytes, offset: int = 0) -> Tuple[T, int]: ... # Decode from buffer\n @classmethod\n def decode(cls, buffer: bytes, offset: int = 0) -> T: ... # Decode from buffer (convenience)\n```\n\n## Performance Considerations\n\n### Encoding Efficiency\n\n- **Fixed-size types** (U8, U16, etc.) have constant encoding size\n- **Variable-size types** (general Uint) optimize for smaller values\n- **Sequences** encode length only when necessary (variable-size)\n- **Strings** use UTF-8 encoding with variable-length prefix\n\n### Memory Usage\n\n- Types extend built-in Python types where possible (int, str, list, dict)\n- Zero-copy operations where feasible\n- Minimal overhead for type metadata\n\n### Best Practices\n\n```python\n# Prefer fixed-size types when range is known\nuser_id = U32(123456) # Better than Uint(123456)\n\n# Use typed containers for homogeneous data\nscores = TypedVector[U16]([100, 95, 87, 92])\n\n# Batch operations for better performance\ndata = TypedArray[U8, 1000]([0] * 1000)\nencoded = data.encode() # Single operation vs. encoding each element\n\n# Reuse buffer for multiple encodings\nbuffer = bytearray(1024)\noffset = 0\noffset += value1.encode_into(buffer, offset)\noffset += value2.encode_into(buffer, offset)\n\n# Choose appropriate byte type for your use case\n# Use Bytes for immutable binary data (memory efficient, read-only)\nconfig_data = Bytes(b\"static configuration\")\n\n# Use ByteArray for dynamic binary buffers (mutable, growing data)\ndynamic_buffer = ByteArray()\ndynamic_buffer.extend([0x01, 0x02])\ndynamic_buffer.append(0x03)\ndynamic_buffer.insert(0, 0x00) # Result: [0x00, 0x01, 0x02, 0x03]\n```\n\n## Examples\n\n### Network Protocol\n\n```**python**\n@structure\nclass NetworkPacket:\n packet_type: U8\n session_id: U32\n payload_length: U16\n payload: Bytes\n\n# Create packet\npacket = NetworkPacket(\n packet_type=U8(1),\n session_id=U32(0x12345678),\n payload_length=U16(13),\n payload=Bytes(b\"Hello, World!\")\n)\n\n# Serialize for transmission\nwire_data = packet.encode()\n\n# Deserialize on receiver\nreceived_packet = NetworkPacket.decode(wire_data)\n```\n\n### Configuration File\n\n```python\n@structure\nclass DatabaseConfig:\n host: String\n port: U16\n username: String \n password: String\n max_connections: U8 = field(metadata={\"default\": U8(10)})\n ssl_enabled: Bool = field(metadata={\"default\": Bool(True)})\n\n# Create config\nconfig = DatabaseConfig(\n host=String(\"localhost\"),\n port=U16(5432),\n username=String(\"admin\"),\n password=String(\"secret\")\n)\n\n# Save to JSON\nimport json\nwith open(\"db_config.json\", \"w\") as f:\n json.dump(config.to_json(), f)\n\n# Load from JSON\nwith open(\"db_config.json\", \"r\") as f:\n data = json.load(f)\n config = DatabaseConfig.from_json(data)\n```\n\n### Game State Serialization\n\n```python\nclass GameEntityType(Enum):\n PLAYER = 0\n ENEMY = 1\n ITEM = 2\n\n@structure\nclass Position:\n x: U16\n y: U16\n\n@structure \nclass GameEntity:\n entity_type: GameEntityType\n position: Position\n health: U8\n name: String\n\n@structure\nclass GameState:\n level: U8\n score: U32\n entities: TypedVector[GameEntity]\n\n# Create game state\nstate = GameState(\n level=U8(1),\n score=U32(1500),\n entities=TypedVector[GameEntity]([\n GameEntity(\n entity_type=GameEntityType.PLAYER,\n position=Position(x=U16(100), y=U16(200)),\n health=U8(100),\n name=String(\"Hero\")\n )\n ])\n)\n\n# Save/load game\nsave_data = state.encode()\nloaded_state = GameState.decode(save_data)\n```\n\n## Development\n\n### Setup\n\n1. **Clone the repository:**\n ```bash\n git clone https://github.com/chainscore/tsrkit-types.git\n cd tsrkit-types\n ```\n\n2. **Install development dependencies:**\n ```bash\n pip install -e \".[dev]\"\n ```\n\n### Running Tests\n\n**Run the full test suite:**\n```bash\npytest\n```\n\n**Run tests with coverage:**\n```bash\npytest --cov=tsrkit_types --cov-report=html\n```\n\n**Run tests in parallel (faster):**\n```bash\npytest -n auto\n```\n\n**Run specific test categories:**\n```bash\npytest tests/test_integers.py # Integer type tests\npytest tests/test_strings.py # String type tests \npytest tests/test_containers.py # Container type tests\npytest tests/test_structs.py # Struct tests\npytest tests/test_network.py # Network protocol tests\n```\n\n**Run tests with verbose output:**\n```bash\npytest -v\n```\n\n**Skip slow tests:**\n```bash\npytest -m \"not slow\"\n```\n\n### Build and Publish\n\n1. Build the package:\n\n```bash\npython3 -m build --wheel\n```\n\n2. Publish the wheels:\n\n```bash\ntwine upload dist/*\n```\n\n### Test Coverage\n\nView the test coverage report:\n```bash\n# Generate HTML coverage report\npytest --cov=tsrkit_types --cov-report=html\nopen htmlcov/index.html # macOS\n# or xdg-open htmlcov/index.html # Linux\n```\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## Requirements\n\n- **Python**: >= 3.11\n- **Runtime Dependencies**: None (zero dependencies!)\n- **Development Dependencies**: pytest and plugins (see `pyproject.toml`) \n",
"bugtrack_url": null,
"license": null,
"summary": "Performant Python Typings library for type-safe binary serialization, JSON encoding, and data validation with zero dependencies",
"version": "0.1.8",
"project_urls": {
"Changelog": "https://github.com/chainscore/tsrkit-types/blob/main/CHANGELOG.md",
"Documentation": "https://github.com/chainscore/tsrkit-types#readme",
"Homepage": "https://github.com/chainscore/tsrkit-types",
"Issues": "https://github.com/chainscore/tsrkit-types/issues",
"Repository": "https://github.com/chainscore/tsrkit-types"
},
"split_keywords": [
"serialization",
" binary",
" encoding",
" types",
" codable",
" json",
" validation",
" data-types",
" type-safety",
" zero-copy",
" protocol",
" struct",
" networking",
" performance",
" bytes",
" integers",
" strings"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "88667f87bb24f00d18a4d526f4f511d685d0bba584795b04e158b490ed1cd98a",
"md5": "19bc768455bc47890ea449cb5fe18205",
"sha256": "5cf4f86aba2a356ec354e7a47b8504e1efe921ca793e904094cb42cb07cd57ee"
},
"downloads": -1,
"filename": "tsrkit_types-0.1.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "19bc768455bc47890ea449cb5fe18205",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 28873,
"upload_time": "2025-07-11T06:47:43",
"upload_time_iso_8601": "2025-07-11T06:47:43.093188Z",
"url": "https://files.pythonhosted.org/packages/88/66/7f87bb24f00d18a4d526f4f511d685d0bba584795b04e158b490ed1cd98a/tsrkit_types-0.1.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-11 06:47:43",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "chainscore",
"github_project": "tsrkit-types",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "tsrkit-types"
}