Independent BSON codec for Python that doesn't depend on MongoDB
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Explicit integer type wrappers providing precise control over BSON integer encoding. These classes ensure specific bit widths and signedness for integer values, supporting the full range of BSON integer types.
Wrapper for 32-bit signed integers with range validation and explicit BSON encoding control.
class Int32:
def __init__(self, value):
"""
Create 32-bit signed integer wrapper.
Parameters:
- value: int, must be in range [-2^31, 2^31-1]
Raises:
ValueError: If value outside 32-bit signed integer range
"""
def get_value(self):
"""
Get the wrapped integer value.
Returns:
int: The original integer value
"""
def __str__(self):
"""String representation of the value"""Usage example:
from bson.types import Int32
import bson
# Create 32-bit signed integers
small_int = Int32(100)
negative_int = Int32(-2147483648) # Minimum 32-bit signed value
max_int = Int32(2147483647) # Maximum 32-bit signed value
print(small_int.get_value()) # 100
print(str(small_int)) # "100"
# Use in BSON serialization for explicit type control
data = {
"explicit_int32": Int32(42),
"regular_int": 42 # May be encoded as different BSON integer type
}
bson_data = bson.dumps(data)
restored = bson.loads(bson_data)
print(restored) # Both values appear as regular Python ints after deserialization
# Range validation
try:
too_big = Int32(2147483648) # 2^31, exceeds range
except ValueError as e:
print(f"Range error: {e}")
try:
too_small = Int32(-2147483649) # Below -2^31, exceeds range
except ValueError as e:
print(f"Range error: {e}")Wrapper for 64-bit signed integers supporting the full range of signed 64-bit values.
class Int64:
def __init__(self, value):
"""
Create 64-bit signed integer wrapper.
Parameters:
- value: int, must be in range [-2^63, 2^63-1]
Raises:
ValueError: If value outside 64-bit signed integer range
"""
def get_value(self):
"""
Get the wrapped integer value.
Returns:
int: The original integer value
"""
def __str__(self):
"""String representation of the value"""Usage example:
from bson.types import Int64
import bson
# Create 64-bit signed integers
big_int = Int64(9223372036854775807) # Maximum 64-bit signed value
negative_big = Int64(-9223372036854775808) # Minimum 64-bit signed value
print(big_int.get_value()) # 9223372036854775807
print(str(big_int)) # "9223372036854775807"
# Use in BSON for large integers
data = {
"timestamp_ns": Int64(1634567890123456789), # Nanosecond timestamp
"large_id": Int64(-1000000000000000000)
}
bson_data = bson.dumps(data)
restored = bson.loads(bson_data)
# Range validation
try:
too_big = Int64(9223372036854775808) # 2^63, exceeds range
except ValueError as e:
print(f"Range error: {e}")Wrapper for 64-bit unsigned integers supporting values from 0 to 2^64-1.
class UInt64:
def __init__(self, value):
"""
Create 64-bit unsigned integer wrapper.
Parameters:
- value: int, must be in range [0, 2^64-1]
Raises:
ValueError: If value outside 64-bit unsigned integer range
"""
def get_value(self):
"""
Get the wrapped integer value.
Returns:
int: The original integer value
"""
def __str__(self):
"""String representation of the value"""Usage example:
from bson.types import UInt64
import bson
# Create 64-bit unsigned integers
max_uint64 = UInt64(18446744073709551615) # Maximum 64-bit unsigned value (2^64-1)
zero_uint64 = UInt64(0) # Minimum value
print(max_uint64.get_value()) # 18446744073709551615
print(str(max_uint64)) # "18446744073709551615"
# Use for large positive values
data = {
"file_size": UInt64(17179869184000), # 16 TB in bytes
"counter": UInt64(18446744073709551615)
}
bson_data = bson.dumps(data)
restored = bson.loads(bson_data)
# Range validation
try:
negative = UInt64(-1) # Negative values not allowed
except ValueError as e:
print(f"Range error: {e}")
try:
too_big = UInt64(18446744073709551616) # 2^64, exceeds range
except ValueError as e:
print(f"Range error: {e}")When using regular Python integers without explicit type wrappers, the bson package automatically selects the appropriate BSON integer type based on value range:
import bson
data = {
"small": 100, # -> BSON Int32
"medium": 2147483648, # -> BSON Int64 (exceeds Int32 range)
"large": 9223372036854775808, # -> BSON UInt64 (exceeds Int64 range)
"too_large": 18446744073709551616 # -> Exception (exceeds UInt64 range)
}
# Automatic type selection rules:
# [-2^31, 2^31-1] -> Int32
# [-2^63, -2^31-1] ∪ [2^31, 2^63-1] -> Int64
# [2^63, 2^64-1] -> UInt64
# Outside [0, 2^64-1] -> ExceptionUse type wrapper classes when you need explicit control over BSON encoding:
from bson.types import Int32, Int64, UInt64
import bson
# Force specific BSON types regardless of value
data = {
"explicit_int32": Int32(100), # Always encoded as BSON Int32
"explicit_int64": Int64(100), # Always encoded as BSON Int64
"explicit_uint64": UInt64(100), # Always encoded as BSON UInt64
"automatic": 100 # Automatically chosen (Int32 in this case)
}
bson_data = bson.dumps(data)
# All values appear as regular Python ints after deserialization
restored = bson.loads(bson_data)
# But they were encoded with different BSON type codesThe type wrapper classes integrate seamlessly with the BSON encoding system:
from bson.types import Int32, Int64, UInt64
import bson
# Mixed type data
mixed_data = {
"values": [
Int32(42),
Int64(1234567890123),
UInt64(18446744073709551615),
12345 # Regular int, automatically typed
],
"metadata": {
"count": Int32(4),
"timestamp": Int64(1634567890123)
}
}
# Encode with explicit type control
bson_bytes = bson.dumps(mixed_data)
# Decode back to regular Python types
result = bson.loads(bson_bytes)
# All integers appear as regular Python ints in resultType wrappers can be used within custom BSONCoding objects:
from bson.types import Int32, UInt64
from bson.codec import BSONCoding
import bson
class Counter(BSONCoding):
def __init__(self, count=0):
self.count = UInt64(count) if count >= 0 else Int64(count)
def bson_encode(self):
return {"count": self.count}
def bson_init(self, raw_values):
self.count = raw_values["count"]
return self
# Register and use
bson.import_class(Counter)
counter = Counter(18446744073709551615) # Large positive value
data = {"counter_obj": counter}
bson_data = bson.dumps(data)
restored = bson.loads(bson_data)All type wrapper classes validate ranges during construction and raise ValueError with descriptive messages for out-of-range values:
from bson.types import Int32, Int64, UInt64
# Int32 range errors
try:
Int32(2147483648) # 2^31, too large
except ValueError as e:
print(e) # "value 2147483648 cannot be represented in int32"
# Int64 range errors
try:
Int64(9223372036854775808) # 2^63, too large
except ValueError as e:
print(e) # "value 9223372036854775808 cannot be represented in int32" (bug in error message)
# UInt64 range errors
try:
UInt64(-1) # Negative, invalid for unsigned
except ValueError as e:
print(e) # "value -1 cannot be represented in uint32" (bug in error message)
try:
UInt64(18446744073709551616) # 2^64, too large
except ValueError as e:
print(e) # "value 18446744073709551616 cannot be represented in uint32" (bug in error message)Note: The error messages contain bugs where Int64 and UInt64 classes incorrectly reference "int32" and "uint32" respectively.
Install with Tessl CLI
npx tessl i tessl/pypi-bson