Codecs¶
Though the default registry can be customized by making additonal coder
registrations or by unregistering existing coders (see Registry),
sometimes a user might wish to create their own registry entirely. In that
case, they can still use the usual API for encoding and decoding values (see
Encoding and Decoding) with their own registry by using the
ABICodec
or ABIEncoder
class.
Using a Custom Registry¶
Here’s an example of how you might add support for a simple “null” type using a custom registry while continuining to use the porcelain encoding and decoding API:
from eth_abi.codec import ABICodec
from eth_abi.exceptions import EncodingError, DecodingError
from eth_abi.registry import ABIRegistry
# Define and register the coders
NULL_ENCODING = b'\x00' * 32
def encode_null(x):
if x is not None:
raise EncodingError('Unsupported value')
return NULL_ENCODING
def decode_null(stream):
if stream.read(32) != NULL_ENCODING:
raise DecodingError('Not enough data or wrong data')
return None
registry = ABIRegistry()
registry.register('null', encode_null, decode_null)
# Try them out
codec = ABICodec(registry)
assert codec.encode(['null'], [None]) == NULL_ENCODING
(decoded_null_val,) = codec.decode(['null'], NULL_ENCODING)
assert decoded_null_val is None
In the above example, we define two coder callables and register them to handle
exact matches against the 'null'
type string in a custom registry. For
more information about coder registrations, see
Adding Simple Types.
We then create a custom codec object with our custom registry and use this to encode and decode byte sequences. This allows us to continue using the porcelain API (described in the Encoding and Decoding sections) with our custom registry.
Copying an Existing Registry¶
Sometimes, it’s more convenient to use an existing registry but with only one or two small modifications. This can be done via a registry’s copying or cloning capability coupled with the use of a custom codec:
from eth_abi.codec import ABICodec
from eth_abi.registry import registry as default_registry
registry = default_registry.copy()
registry.unregister('address')
codec = ABICodec(registry)
try:
codec.encode(['address'], [None])
except ValueError:
pass
else:
# We shouldn't reach this since the above code will cause an exception
raise Exception('unreachable')
default_codec = ABICodec(default_registry)
# The default registry is unaffected since a copy was made
assert (
default_codec.encode(['address'], ['0x' + 'ff' * 20]) ==
b'\x00' * 12 + b'\xff' * 20
)
Using a Custom Stream Class¶
If a user wishes to customize the behavior of the internal stream class used for decoding, they can do the following:
from eth_abi.codec import ABIEncoder, ABIDecoder
from eth_abi.registry import registry
class MyStream:
# Custom behavior...
pass
class MyDecoder(ABIDecoder):
stream_class = MyStream
class MyCodec(ABIEncoder, MyDecoder):
pass
codec = MyCodec(registry)