strconv - Data conversion from/to string¶
Overview¶
While Python types typically support conversion to string via builtin str() function
(and custom __str__ methods), there is no symetric operation that converts string created
by str() back to typed value. This module provides support for such symetric conversion
from/to string for any data type.
Note
Symetric string conversion is used by config module, notably by
ListOption and DataclassOption. You can
extend the range of data types supported by these options by registering convertors
for required data types.
Architecture¶
Module maintains a global registry of convertors associated with a data type. These convertor functions may support conversion for multiple data types, and must have signatures:
# Conversion to string
def value2str(value: Any) -> str:
...
# Conversion from string
def str2value(cls: Type, value: str) -> Any:
...
Convertors should raise ValueError when conversion fails.
Convertors must be registered using register_convertor() function, or reassigned using
update_convertor().
There are two methods for use of registered convertors:
By using functions
convert_to_str()andconvert_from_str(). These functions use convertor that is registered for particular data type. If there is no such convertor, they use first convertor registered for any base class. The lookup is performed in Method Resolution Order.This method is preferred when you will work with various data types.
Using
get_convertor()function to obtain registry entry with convertors.This method is preferred when you will work with single data type repeatedly.
Important
The convertor registry lookup could be done either for a class, or class name.
Lookup for a class is first performed for specified class. If there is no such entry, all bases classes in Method Resolution Order are used for lookup, and the first entry found is returned.
Lookup for a class name could be performed only for a specified class. Because some types
could be converted using convertors registered for their base class, such lookup will
not find the required convertor entry. To circumvent this issue, it’s necessary to
register a class for name lookup using register_class() function. The registry lookup
uses this class registry to use the actual type instead type name.
Registered data types¶
Module registers convertor functions for next data types:
bool. UsesTRUE_STRandFALSE_STRlist for conversion from string (the case is NOT significant). Conversion to string always uses first item in these lists.EnumandFlag. Supports conversion for all descendants ofEnum,IntEnum,FlagandIntFlag.
Tip
Functions any2str() and str2any() could be used to register conversion for data
types that support symetric conversion via __str__() method and class constructor
(__init__() must accept one positional argument that could be a string and all other
arguments must be keyword arguments with default values).
Types for annotations¶
- firebird.base.strconv.TConvertToStr¶
Function that converts typed value to its string representation.
Globals¶
Functions¶
- firebird.base.strconv.convert_to_str(value: Any) str[source]¶
Converts a value to its string representation using its registered convertor.
Looks up the convertor based on the value’s class (
value.__class__). If there is no direct convertor registered for the value’s specific class, it searches the Method Resolution Order (MRO) for a convertor registered for a base class.- Parameters:
value (Any) – The value to be converted to a string.
- Raises:
TypeError – If no convertor is found for the value’s class or any of its base classes in the MRO.
- Return type:
Example
from decimal import Decimal from uuid import uuid4 from firebird.base.strconv import convert_to_str, register_convertor print(convert_to_str(123)) # Output: '123' print(convert_to_str(Decimal('1.2'))) # Output: '1.2' print(convert_to_str(True)) # Output: 'yes' my_uuid = uuid4() print(convert_to_str(my_uuid)) # Output: UUID string representation class MyBase: pass class MyDerived(MyBase): pass register_convertor(MyBase, to_str=lambda v: "BaseStr") instance = MyDerived() print(convert_to_str(instance)) # Output: 'BaseStr' (uses MyBase convertor)
- firebird.base.strconv.convert_from_str(cls: type | str, value: str) Any[source]¶
Converts a string representation back to a typed value using a registered convertor.
- Parameters:
- Return type:
Note
When
clsis a type name:If the class name is NOT registered via
register_class(), MRO lookup for base class convertors is not possible if an exact name match isn’t found.If a simple class name is provided and is ambiguous (multiple registered classes with the same name), the first match found is used. Use full names (‘module.ClassName’) for clarity in such cases.
- Raises:
TypeError – If no convertor is found for
clsor any of its base classes (when MRO lookup is possible).ValueError – Often raised by the underlying
from_strfunction if the stringvalueis not in the expected format for the target type (e.g., converting ‘abc’ to int).
- Parameters:
- Return type:
Example
from decimal import Decimal from uuid import UUID from firebird.base.strconv import convert_from_str num = convert_from_str(int, '123') # Output: 123 (int) dec = convert_from_str(Decimal, '1.2') # Output: Decimal('1.2') flag = convert_from_str(bool, 'off') # Output: False (bool) uid = convert_from_str(UUID, '...') # Output: UUID object # Using string name dec_from_name = convert_from_str('Decimal', '3.14') # Output: Decimal('3.14') try: convert_from_str(int, 'not-a-number') except ValueError as e: print(e) # Example: invalid literal for int() with base 10: 'not-a-number'
- firebird.base.strconv.register_convertor(cls: type, *, to_str: ~collections.abc.Callable[[~typing.Any], str] = <function any2str>, from_str: ~collections.abc.Callable[[type, str], ~typing.Any] = <function str2any>) None[source]¶
Registers convertor function(s) for a specific data type.
If
to_strorfrom_strare not provided, default convertors (any2str,str2any) based onstr()andcls()are used.- Parameters:
- Return type:
None
Example
from datetime import date from firebird.base.strconv import register_convertor, convert_to_str, convert_from_str # Register custom convertors for date def date_to_iso(value: date) -> str: return value.isoformat() def iso_to_date(cls: type, value: str) -> date: return cls.fromisoformat(value) register_convertor(date, to_str=date_to_iso, from_str=iso_to_date) d = date(2023, 10, 27) s = convert_to_str(d) # Uses date_to_iso -> '2023-10-27' d2 = convert_from_str(date, s) # Uses iso_to_date -> date(2023, 10, 27)
- firebird.base.strconv.update_convertor(cls: type | str, *, to_str: Callable[[Any], str] | None = None, from_str: Callable[[type, str], Any] | None = None) None[source]¶
Update the
to_strand/orfrom_strfunctions for an existing convertor.- Parameters:
- Raises:
TypeError – If the data type (or its name) has no registered convertor.
- Return type:
None
Example
from firebird.base.strconv import update_convertor, convert_to_str # Assume BoolConvertor exists and uses 'yes'/'no' # Change bool to output 'TRUE'/'FALSE' update_convertor(bool, to_str=lambda v: 'TRUE' if v else 'FALSE') print(convert_to_str(True)) # Output: TRUE
- firebird.base.strconv.register_class(cls: type) None[source]¶
Registers a class name for lookup, primarily for string-based conversions.
This allows functions like
has_convertor,get_convertor, andconvert_from_strto find the correct convertor when given a simple class name (e.g., “MyClass”) as a string, instead of the class object itself. Registration is particularly useful when:Performing lookups based on class names stored as strings.
Resolving potential ambiguity if multiple classes with the same simple name exist in different modules (though using full names like ‘module.MyClass’ is generally safer in such cases).
Enabling MRO (Method Resolution Order) lookup for base class convertors when the lookup starts with a string name.
See also
- firebird.base.strconv.has_convertor(cls: type | str) bool[source]¶
Returns True if a convertor is registered for the class or its bases.
- Parameters:
cls (type | str) – Type object or type name. The name could be a simple class name (e.g., “MyClass”) or a full name including the module (e.g., “my_module.MyClass”).
- Return type:
Note
When
clsis a name:If the class name is NOT registered via
register_class(), it’s not possible to perform MRO lookup for base class convertors. Only an exact match on the name (simple or full) will work.If a simple class name is provided and multiple classes of the same name but from different modules have registered convertors (or been registered via
register_class), the lookup might be ambiguous. Using full names is recommended in such scenarios.
Example
from decimal import Decimal from firebird.base.strconv import register_convertor, has_convertor, register_class print(has_convertor(Decimal)) # Output: True (built-in) print(has_convertor('Decimal')) # Output: True (built-in, simple name works) print(has_convertor('decimal.Decimal')) # Output: True (full name) class MyData: pass class MySubData(MyData): pass register_convertor(MyData) register_class(MySubData) # Register subclass name print(has_convertor(MySubData)) # Output: True (finds MyData via MRO) print(has_convertor('MySubData')) # Output: True (finds MyData via MRO because name is registered) print(has_convertor('NonExistent')) # Output: False
- firebird.base.strconv.get_convertor(cls: type | str) Convertor[source]¶
“Returns the Convertor object registered for a data type or its bases.
This function performs the lookup based on the type or type name, including MRO search for base classes if necessary and possible. It is used internally by
convert_to_strandconvert_from_str, but can be called directly if you need access to theConvertorinstance itself, for example, for introspection or direct access to theto_str/from_strfunctions.- Parameters:
cls (type | str) – Type object or type name. The name could be a simple class name (e.g., “MyClass”) or a full name including the module (e.g., “my_module.MyClass”).
- Return type:
Note
When
clsis a name:If the class name is NOT registered via
register_class(), MRO lookup for base class convertors is not possible if an exact name match isn’t found.If a simple class name is provided and is ambiguous (multiple registered classes with the same name), the first match found is used. Use full names for clarity.
- Raises:
TypeError – If no convertor is found for
clsor any of its base classes.- Parameters:
- Return type:
Example
from decimal import Decimal from firebird.base.strconv import get_convertor decimal_conv = get_convertor(Decimal) print(decimal_conv.name) # Output: Decimal print(decimal_conv.to_str(Decimal('9.87'))) # Output: 9.87 bool_conv = get_convertor('bool') # Lookup by name print(bool_conv.from_str(bool, 'TRUE')) # Output: True
- firebird.base.strconv.any2str(value: Any) str[source]¶
Converts value to string using
str(value).This is the default
to_strconvertor function.
Dataclasses¶
- class firebird.base.strconv.Convertor(cls: type, to_str: Callable[[Any], str], from_str: Callable[[type, str], Any])[source]¶
Bases:
DistinctData convertor registry entry.
Holds the functions responsible for converting a specific data type to and from its string representation. Instances of this class are stored in the internal registry.
- Parameters: