config2py.codecs#

Extension-based codec registries for configuration file parsing.

This module provides a flexible pattern for encoding and decoding configuration files based on their file extensions. It includes codecs for bytes <-> JSON-friendly Python types.

Examples

>>> # Basic usage
>>> data = {'name': 'config2py', 'version': '1.0'}
>>>
>>> # Encode to bytes
>>> encoded = encode_by_extension('config.json', data)
>>> assert isinstance(encoded, bytes)
>>>
>>> # Decode from bytes
>>> decoded = decode_by_extension('config.json', encoded)
>>> assert decoded == data
>>>
>>> # Register custom codec
>>> @register_decoder('.custom')
... def decode_custom(data: bytes) -> dict:
...     return {'custom': data.decode()}
>>>
>>> @register_encoder('.custom')
... def encode_custom(obj: dict) -> bytes:
...     return obj.get('custom', '').encode()

The module automatically registers codecs for standard formats (json, toml, ini, etc.) and conditionally registers codecs that require third-party libraries (yaml, json5, etc.).

Security warning: the .pkl and .pickle extensions decode with pickle.loads, which can execute arbitrary code. Never call decode_by_extension with those extensions on bytes you do not fully trust (for example, bytes fetched from a remote or shared store). See i2mint/config2py#29.

Functions

decode_by_extension(key, data)

Decode data based on key's extension.

encode_by_extension(key, obj)

Encode object based on key's extension.

get_extension(key)

Extract extension from a key (filename, path, etc.).

register_codec(extension, *[, encoder, ...])

Register encoder and/or decoder for an extension.

register_decoder(extension, *[, overwrite])

Decorator to register a decoder function.

register_encoder(extension, *[, overwrite])

Decorator to register an encoder function.

list_registered_extensions()

List all registered extensions.

is_extension_registered(extension)

Check if an extension has any codec registered.

get_codec_info(extension)

Get information about a registered codec.

config2py.codecs.decode_by_extension(key, data)[source]#

Decode data based on key’s extension.

Parameters:
  • key (str) – Key or filename with extension

  • data (bytes) – Bytes to decode

Return type:

Any

Returns:

Decoded Python object

Raises:

ValueError – If no decoder registered for extension

Warning

The decoder is chosen by the key’s extension alone. .pkl and .pickle map to pickle.loads, which can execute arbitrary code, so never decode untrusted bytes under those extensions.

Examples

>>> data = b'{"key": "value"}'
>>> decode_by_extension('config.json', data)
{'key': 'value'}
config2py.codecs.encode_by_extension(key, obj)[source]#

Encode object based on key’s extension.

Parameters:
  • key (str) – Key or filename with extension

  • obj (Any) – Python object to encode

Return type:

bytes

Returns:

Encoded bytes

Raises:

ValueError – If no encoder registered for extension

Examples

>>> obj = {'key': 'value'}
>>> encoded = encode_by_extension('config.json', obj)
>>> assert b'"key"' in encoded
config2py.codecs.get_codec_info(extension)[source]#

Get information about a registered codec.

Parameters:

extension (str) – File extension (with or without leading dot)

Return type:

dict[str, Any]

Returns:

Dictionary with codec information

Examples

>>> info = get_codec_info('.json')
>>> info['has_encoder']
True
>>> info['has_decoder']
True
config2py.codecs.get_extension(key)[source]#

Extract extension from a key (filename, path, etc.).

Parameters:

key (str) – A string that may contain a file extension

Return type:

str

Returns:

Extension without the dot, or empty string if no extension found

Examples

>>> get_extension('config.json')
'json'
>>> get_extension('/path/to/data.yaml')
'yaml'
>>> get_extension('no_extension')
''
>>> get_extension('.env')
'env'
>>> get_extension('/path/to/.env')
'env'
config2py.codecs.is_extension_registered(extension)[source]#

Check if an extension has any codec registered.

Parameters:

extension (str) – File extension (with or without leading dot)

Return type:

bool

Returns:

True if decoder or encoder is registered

Examples

>>> is_extension_registered('.json')
True
>>> is_extension_registered('.nonexistent')
False
config2py.codecs.list_registered_extensions()[source]#

List all registered extensions.

Return type:

list[str]

Returns:

Sorted list of registered extensions

Examples

>>> extensions = list_registered_extensions()
>>> '.json' in extensions
True
config2py.codecs.register_codec(extension, *, encoder=None, decoder=None, overwrite=False, dependency=None)[source]#

Register encoder and/or decoder for an extension.

Parameters:
  • extension (str) – File extension (with or without leading dot)

  • encoder (Optional[Callable[[Any], bytes]]) – Function to encode objects to bytes

  • decoder (Optional[Callable[[bytes], Any]]) – Function to decode bytes to objects

  • overwrite (bool) – Whether to overwrite existing codec

  • dependency (Optional[str]) – Optional package name required for this codec

Raises:

ValueError – If codec already registered and overwrite=False

Examples

>>> def my_encoder(obj): return str(obj).encode()
>>> def my_decoder(data): return data.decode()
>>> register_codec('.custom', encoder=my_encoder, decoder=my_decoder, overwrite=True)
config2py.codecs.register_decoder(extension, *, overwrite=False)[source]#

Decorator to register a decoder function.

Parameters:
  • extension (str) – File extension (with or without leading dot)

  • overwrite (bool) – Whether to overwrite existing decoder

Returns:

Decorator function

Examples

>>> @register_decoder('.custom', overwrite=True)
... def decode_custom(data: bytes) -> dict:
...     return {'data': data.decode()}
config2py.codecs.register_encoder(extension, *, overwrite=False)[source]#

Decorator to register an encoder function.

Parameters:
  • extension (str) – File extension (with or without leading dot)

  • overwrite (bool) – Whether to overwrite existing encoder

Returns:

Decorator function

Examples

>>> @register_encoder('.custom', overwrite=True)
... def encode_custom(obj: dict) -> bytes:
...     return obj.get('data', '').encode()