Module psdi_data_conversion.database

@file psdi_data_conversion/database.py

Created 2025-02-03 by Bryan Gillis.

Python module provide utilities for accessing the converter database

Global variables

var D_FORMAT_PROPERTY_ATTRS

A dict of attrs of this class which describe properties that a format may or may not have

Functions

def calc_conversion_conv_weight(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo) ‑> int
Expand source code
def calc_conversion_conv_weight(converter: str | int | UUID | ConverterInfo,
                                in_format: str | int | UUID | FormatInfo,
                                out_format: str | int | UUID | FormatInfo) -> int:
    """Get the converter weight for a conversion from `in_format_info` to `out_format_info` with converter
    `converter_info` (not including the offset applied to it when stored in the total weight)

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The converter, specified by its name or ID
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format

    Returns
    -------
    int
        64-bit bit weight, representing the weight based on the converter
    """
    # Get the info for all input
    converter_info = get_converter_info(converter)
    return converter_info.weight

Get the converter weight for a conversion from in_format_info to out_format_info with converter converter_info (not including the offset applied to it when stored in the total weight)

Parameters

converter : str | int | UUID | ConverterInfo
The converter, specified by its name or ID
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format

Returns

int
64-bit bit weight, representing the weight based on the converter
def calc_conversion_format_weight(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo) ‑> int
Expand source code
def calc_conversion_format_weight(converter: str | int | UUID | ConverterInfo,
                                  in_format: str | int | UUID | FormatInfo,
                                  out_format: str | int | UUID | FormatInfo) -> int:
    """Get the format weight for a conversion from `in_format_info` to `out_format_info` (not including the offset
    applied to it when stored in the total weight)

    TODO: Implement properly

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The converter, specified by its name or ID
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format

    Returns
    -------
    int
        64-bit bit weight, representing the weight based on how reliable the format is for conversions from experience
    """
    out_weight = out_format.weight
    if out_weight:
        return out_weight
    return FORMAT_WEIGHT_UNKNOWN

Get the format weight for a conversion from in_format_info to out_format_info (not including the offset applied to it when stored in the total weight)

TODO: Implement properly

Parameters

converter : str | int | UUID | ConverterInfo
The converter, specified by its name or ID
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format

Returns

int
64-bit bit weight, representing the weight based on how reliable the format is for conversions from experience
def calc_conversion_prec_weight(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo) ‑> int
Expand source code
def calc_conversion_prec_weight(converter: str | int | UUID | ConverterInfo,
                                in_format: str | int | UUID | FormatInfo,
                                out_format: str | int | UUID | FormatInfo) -> int:
    """Get the precision weight for a conversion from `in_format_info` to `out_format_info` (not including the offset
    applied to it when stored in the total weight).

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The converter, specified by its name or ID
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format

    Returns
    -------
    int
        32-bit bit weight, where the bit 2N is set to 1, with N being the number of decimal places of precision lost,
        bound to 0 <= N <= 12
    """
    # Get the info for all input
    in_format_info, out_format_info = disambiguate_formats(converter, in_format, out_format)

    # Calculate the precision loss, defaulting to the maximum if unknown
    prec_loss = PREC_MAX_DIGIT_LOSS
    if in_format_info.precision is not None and out_format_info.precision is not None:
        prec_loss = in_format_info.precision - out_format_info.precision
    prec_loss = min(max(prec_loss, PREC_MIN_DIGIT_LOSS), PREC_MAX_DIGIT_LOSS)

    return 1 << PREC_GAP_BITS*prec_loss

Get the precision weight for a conversion from in_format_info to out_format_info (not including the offset applied to it when stored in the total weight).

Parameters

converter : str | int | UUID | ConverterInfo
The converter, specified by its name or ID
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format

Returns

int
32-bit bit weight, where the bit 2N is set to 1, with N being the number of decimal places of precision lost, bound to 0 <= N <= 12
def calc_conversion_prop_weight(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo) ‑> int
Expand source code
def calc_conversion_prop_weight(converter: str | int | UUID | ConverterInfo,
                                in_format: str | int | UUID | FormatInfo,
                                out_format: str | int | UUID | FormatInfo) -> int:
    """Get the property weight for a conversion from `in_format_info` to `out_format_info` (not including the offset
    applied to it when stored in the total weight).

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The converter, specified by its name or ID
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format

    Returns
    -------
    int
        64-bit bit weight, where bits set to 1 indicate the properties lost or potentially in this conversion
    """

    # Get the info for all input
    in_format_info, out_format_info = disambiguate_formats(converter, in_format, out_format)

    # Start the weight as the minimum weight for any conversion. We'll turn on bits for each property potentially lost
    prop_weight = 0

    for prop, bit in D_PROP_BITS.items():
        in_prop: bool | None = getattr(in_format_info, prop)
        out_prop: bool | None = getattr(out_format_info, prop)

        # Add a weight for this conversion if the input property status is True/Unknown and output is False/Unknown, to
        # be maximally conservative
        if (in_prop is True or in_prop is None) and not out_prop:
            prop_weight |= 1 << bit

    return prop_weight

Get the property weight for a conversion from in_format_info to out_format_info (not including the offset applied to it when stored in the total weight).

Parameters

converter : str | int | UUID | ConverterInfo
The converter, specified by its name or ID
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format

Returns

int
64-bit bit weight, where bits set to 1 indicate the properties lost or potentially in this conversion
def calc_conversion_weight(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo) ‑> int
Expand source code
def calc_conversion_weight(converter: str | int | UUID | ConverterInfo,
                           in_format: str | int | UUID | FormatInfo,
                           out_format: str | int | UUID | FormatInfo) -> int:
    """Get the combined weight for a conversion

    Parameters
    ----------
    converter_info : ConverterInfo
        The converter used
    in_format_info : FormatInfo
        The source format for the conversion
    out_format_info : FormatInfo
        The output format for the conversion

    Returns
    -------
    int
        64-bit weight
    """
    # Get the info for all input
    converter_info = get_converter_info(converter)
    in_format_info, out_format_info = disambiguate_formats(converter, in_format, out_format)

    return combine_conversion_weight(calc_conversion_prop_weight(converter_info, in_format_info, out_format_info),
                                     calc_conversion_prec_weight(converter_info, in_format_info, out_format_info),
                                     calc_conversion_format_weight(converter_info, in_format_info, out_format_info),
                                     calc_conversion_conv_weight(converter_info, in_format_info, out_format_info))

Get the combined weight for a conversion

Parameters

converter_info : ConverterInfo
The converter used
in_format_info : FormatInfo
The source format for the conversion
out_format_info : FormatInfo
The output format for the conversion

Returns

int
64-bit weight
def combine_conversion_weight(prop_weight: int, prec_weight: int, format_weight: int, conv_weight: int)
Expand source code
def combine_conversion_weight(prop_weight: int, prec_weight: int, format_weight: int, conv_weight: int):
    """Calculate the combined weight for a conversion from its component weights. The weights must be in the provided
    range, or else the output will have undefined behaviour

    Parameters
    ----------
    prop_weight : int
        The conversion property weight, in the range 0 <= prop_weight < 2**16
    prec_weight : int
        The conversion precision weight, in the range 0 <= prec_weight < 2**32
    format_weight : int
        The conversion format weight, in the range 0 <= format_weight < 2**8
    conv_weight : int
        The converter weight, in the range 0 <= conv_weight < 2**8

    Returns
    -------
    int
        The combined conversion weight
    """
    return ((prop_weight << PROP_WEIGHT_BIT_OFFSET) +
            (prec_weight << PREC_WEIGHT_BIT_OFFSET) +
            (format_weight << FORMAT_WEIGHT_BIT_OFFSET) +
            (conv_weight << CONV_WEIGHT_BIT_OFFSET))

Calculate the combined weight for a conversion from its component weights. The weights must be in the provided range, or else the output will have undefined behaviour

Parameters

prop_weight : int
The conversion property weight, in the range 0 <= prop_weight < 2**16
prec_weight : int
The conversion precision weight, in the range 0 <= prec_weight < 2**32
format_weight : int
The conversion format weight, in the range 0 <= format_weight < 2**8
conv_weight : int
The converter weight, in the range 0 <= conv_weight < 2**8

Returns

int
The combined conversion weight
def disambiguate_formats(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
**kwargs) ‑> tuple[FormatInfo, FormatInfo]
Expand source code
def disambiguate_formats(converter: str | int | UUID | ConverterInfo,
                         in_format: str | int | UUID | FormatInfo,
                         out_format: str | int | UUID | FormatInfo,
                         **kwargs) -> tuple[FormatInfo, FormatInfo]:
    """Try to disambiguate formats by seeing if there's only one possible conversion between formats matching those
    provided.

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The converter, specified by its name or ID
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format

    Returns
    -------
    tuple[FormatInfo, FormatInfo]
        The input and output format for this conversion, if only one combination is possible

    Raises
    ------
    FileConverterDatabaseException
        If more than one format combination is possible for this conversion, or no conversion is possible
    """
    # Check for deprecated kwargs
    if "converter_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                      f"{tc.CODE}`disambiguate_formats`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "accepts the converter name, ID, or info", DeprecationWarning)
        converter = kwargs["converter_name"]

    # Check if both formats are already supplied unambiguously
    if not isinstance(in_format, str) and not isinstance(out_format, str):
        return get_format_info(in_format), get_format_info(out_format)

    # Get the converter/format info for all input, as possible
    converter_info = get_converter_info(converter)
    l_in_format_info = get_format_info(in_format, "all")
    if len(l_in_format_info) == 1:
        in_format_name = l_in_format_info[0].format_word()
    else:
        in_format_name = f"{tc.MESSAGE}'{in_format}'{tc.OFF}"
    l_out_format_info = get_format_info(out_format, "all")
    if len(l_out_format_info) == 1:
        out_format_name = l_out_format_info[0].format_word()
    else:
        out_format_name = f"{tc.MESSAGE}'{out_format}'{tc.OFF}"

    # Get all possible conversions, and see if we only have one for this converter
    l_possible_conversions = [x for x in get_possible_conversions(in_format, out_format)
                              if x[0] is converter_info]

    if len(l_possible_conversions) == 1:
        return l_possible_conversions[0][1], l_possible_conversions[0][2]
    elif len(l_possible_conversions) == 0:
        raise FileConverterDatabaseException(f"Conversion from {in_format_name} to "
                                             f"{out_format_name} with converter "
                                             f"{converter_info.format_word()} is not supported", help=True)
    else:

        converter_name = converter.format_word() if isinstance(
            converter, ConverterInfo) else f"{tc.PATH}'{converter}'{tc.OFF}"

        msg = (f"Conversion from {in_format_name} to {out_format_name} with converter "
               f"{converter_name} is ambiguous. Please Use the ID or disambiguated name (listed below) "
               "of the desired conversion. Possible matching conversions are:\n")
        for _, possible_in_format, possible_out_format in l_possible_conversions:
            msg += (f"    {possible_in_format.format_inline()} to {possible_out_format.format_inline()}\n")
        # Trim the final newline from the message
        msg = msg[:-1]
        raise FileConverterDatabaseException(msg, help=True)

Try to disambiguate formats by seeing if there's only one possible conversion between formats matching those provided.

Parameters

converter : str | int | UUID | ConverterInfo
The converter, specified by its name or ID
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format

Returns

tuple[FormatInfo, FormatInfo]
The input and output format for this conversion, if only one combination is possible

Raises

FileConverterDatabaseException
If more than one format combination is possible for this conversion, or no conversion is possible
def format_weight(weight: int, color='\x1b[96m')
Expand source code
def format_weight(weight: int, color=tc.NUMBER):
    """Formats a weight integer into a more convenient format"""

    l_hex_parts: list[str] = []
    for weight_part, bit_borders in zip(split_conversion_weight(weight), pairwise(L_WEIGHT_BIT_BORDERS)):

        weight_str = _simple_hex(weight_part)

        # We want to pad the hex string to the maximum size it could possibly be, for consistent sizing of each
        # component. We get the total size in base 2from the difference between highest and lowest bit for each
        # component. Divide this by 4 and take the ceiling to get the total size in hex
        total_hex_len = math.ceil((bit_borders[0]-bit_borders[1])/4)

        # Format it padded with zeros on the left up to this total length
        l_hex_parts.append(f"{weight_str:0>{total_hex_len}}")

    hex_weight_str = "-".join(l_hex_parts)

    return f"{color}{hex_weight_str}{tc.OFF}"

Formats a weight integer into a more convenient format

def get_conversion_pathway(in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
only: "Literal['all'] | Literal['supported'] | Literal['registered']" = 'registered') ‑> ConversionPath | None
Expand source code
def get_conversion_pathway(in_format: str | int | UUID | FormatInfo,
                           out_format: str | int | UUID | FormatInfo,
                           only: Literal["all"] | Literal["supported"] | Literal["registered"] = "registered"
                           ) -> ConversionPath | None:
    """Get a list of conversions that can be performed to convert one format to another. This is primarily used when a
    direct conversion is not supported by any individual converter. Only one possible pathway will be returned,
    prioritising pathways which do not lose lose and then re-extrapolate any information stored by some formats and not
    others along the path.

    Parameters
    ----------
    in_format : str | int | UUID | FormatInfo
        The input file format. For this function, the format must be defined uniquely, either by using a disambiguated
        extension, ID, or FormatInfo
    out_format : str | int | UUID | FormatInfo
        The output file format. For this function, the format must be defined uniquely, either by using a disambiguated
        extension, ID, or FormatInfo
    only : Literal["all"] | Literal["supported"] | Literal["registered"], optional
        Which converters to limit the pathway search to:
        - "all": All known converters
        - "supported": Only converters supported by this utility, even if not currently available (e.g. they don't work
          on your OS)
        - "registered" (default): Only converters supported by this utility and currently available

    Returns
    -------
    ConversionPath | None
        Will return `None` if no conversion pathway is possible or if the input and output formats are the same.
        Otherwise, will return a `ConversionPath`, which is a list of `Conversion` steps in the pathway, each being a
        `NamedTuple` of:

        converter : ConverterInfo
            Info on the converter used to perform this step
        in_format : FormatInfo
            Input format for this step (if the first step, will be the input format to this function, otherwise will be
            the output format of the previous step)
        out_format : FormatInfo
            Output format from this step (if the last step, will be the output format for this function, otherwise will
            be the input format of the next step)
    """

    return get_database().conversions_table.get_conversion_pathway(in_format=in_format,
                                                                   out_format=out_format,
                                                                   only=only)

Get a list of conversions that can be performed to convert one format to another. This is primarily used when a direct conversion is not supported by any individual converter. Only one possible pathway will be returned, prioritising pathways which do not lose lose and then re-extrapolate any information stored by some formats and not others along the path.

Parameters

in_format : str | int | UUID | FormatInfo
The input file format. For this function, the format must be defined uniquely, either by using a disambiguated extension, ID, or FormatInfo
out_format : str | int | UUID | FormatInfo
The output file format. For this function, the format must be defined uniquely, either by using a disambiguated extension, ID, or FormatInfo
only : Literal["all"] | Literal["supported"] | Literal["registered"], optional
Which converters to limit the pathway search to: - "all": All known converters - "supported": Only converters supported by this utility, even if not currently available (e.g. they don't work on your OS) - "registered" (default): Only converters supported by this utility and currently available

Returns

ConversionPath | None

Will return None if no conversion pathway is possible or if the input and output formats are the same. Otherwise, will return a ConversionPath, which is a list of Conversion steps in the pathway, each being a NamedTuple of:

converter : ConverterInfo Info on the converter used to perform this step in_format : FormatInfo Input format for this step (if the first step, will be the input format to this function, otherwise will be the output format of the previous step) out_format : FormatInfo Output format from this step (if the last step, will be the output format for this function, otherwise will be the input format of the next step)

def get_conversion_quality(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
**kwargs) ‑> ConversionQualityInfo | None
Expand source code
def get_conversion_quality(converter: str | int | UUID | ConverterInfo,
                           in_format: str | int | UUID | FormatInfo,
                           out_format: str | int | UUID | FormatInfo,
                           **kwargs) -> ConversionQualityInfo | None:
    """Get an indication of the quality of a conversion from one format to another, or if it's not possible

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo,
        The converter, specified through its name or ID
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format

    Returns
    -------
    ConversionQualityInfo | None
        If the conversion is not possible, returns None. If the conversion is possible, returns a
        `ConversionQualityInfo` object with info on the conversion
    """
    if "converter_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                      f"{tc.CODE}`get_conversion_quality`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "accepts the converter name, ID, or info", DeprecationWarning)
        converter = kwargs["converter_name"]

    return get_database().conversions_table.get_conversion_quality(converter=converter,
                                                                   in_format=in_format,
                                                                   out_format=out_format)

Get an indication of the quality of a conversion from one format to another, or if it's not possible

Parameters

converter : str | int | UUID | ConverterInfo,
The converter, specified through its name or ID
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format

Returns

ConversionQualityInfo | None
If the conversion is not possible, returns None. If the conversion is possible, returns a ConversionQualityInfo object with info on the conversion
def get_conversion_weight(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo)
Expand source code
def get_conversion_weight(converter: str | int | UUID | ConverterInfo,
                          in_format: str | int | UUID | FormatInfo,
                          out_format: str | int | UUID | FormatInfo):
    """Get the weight for a desired conversion.

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The name, ID, or info of the converter used for this conversion
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format

    Returns
    -------
    int
        The 64-bit combined weight of this conversion

    Raises
    ------
    FileConverterDatabaseException
        If the requested conversion is not possible
    """
    return get_database().conversions_table.get_conversion_weight(converter, in_format, out_format)

Get the weight for a desired conversion.

Parameters

converter : str | int | UUID | ConverterInfo
The name, ID, or info of the converter used for this conversion
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format

Returns

int
The 64-bit combined weight of this conversion

Raises

FileConverterDatabaseException
If the requested conversion is not possible
def get_converter_info(converter: str | int | UUID | ConverterInfo | None = None,
**kwargs) ‑> ConverterInfo | list[ConverterInfo]
Expand source code
def get_converter_info(converter: str | int | UUID | ConverterInfo | None = None,
                       **kwargs) -> ConverterInfo | list[ConverterInfo]:
    """Gets the information on converters or a given converter stored in the database

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo | None
        The name or UUID of the converter to get info for. Default None, which results in a list being returned of the
        info for all converters in the database

    Returns
    -------
    ConverterInfo | list[ConverterInfo]
        If `converter` is provided, will return a single `ConverterInfo` (or raise an exception if the name
        is invalid). If not provided, a list of all `ConverterInfo` objects in the database will be returned

    Raises
    ------
    FileConverterDatabaseException
        If `name` is provided but does not match the name of a converter in the database
    """

    # Check for deprecated kwargs
    if "converter_name_or_id" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name_or_id`{tc.OFF} for the method "
                      f"{tc.CODE}`get_converter_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "has equivalent functionality and is being used now to normalise function signatures across "
                      "the package.", DeprecationWarning)
        if converter is None:
            converter = get_converter_info(kwargs["converter_name_or_id"])
    return get_database().get_converter_info(converter)

Gets the information on converters or a given converter stored in the database

Parameters

converter : str | int | UUID | ConverterInfo | None
The name or UUID of the converter to get info for. Default None, which results in a list being returned of the info for all converters in the database

Returns

ConverterInfo | list[ConverterInfo]
If converter is provided, will return a single ConverterInfo (or raise an exception if the name is invalid). If not provided, a list of all ConverterInfo objects in the database will be returned

Raises

FileConverterDatabaseException
If name is provided but does not match the name of a converter in the database
def get_database() ‑> DataConversionDatabase
Expand source code
def get_database() -> DataConversionDatabase:
    """Gets the global database object, loading it in first if necessary. Since it's computationally expensive to load
    the database, it's best treated as an immutable singleton.

    Returns
    -------
    DataConversionDatabase
        The global database object
    """
    global _database
    if _database is None:
        # Create the database object and store it globally
        _database = load_database()
    return _database

Gets the global database object, loading it in first if necessary. Since it's computationally expensive to load the database, it's best treated as an immutable singleton.

Returns

DataConversionDatabase
The global database object
def get_database_path() ‑> pathlib.Path
Expand source code
def get_database_path() -> Path:
    """Get the absolute path to the database file
    """
    qualified_database_filename = get_package_path() / const.DATABASE_FILENAME
    return qualified_database_filename

Get the absolute path to the database file

def get_format_info(file_format: str | int | UUID | FormatInfo,
which: "int | Literal['all'] | None" = None,
**kwargs) ‑> FormatInfo | list[FormatInfo]
Expand source code
def get_format_info(file_format: str | int | UUID | FormatInfo,
                    which: int | Literal["all"] | None = None,
                    **kwargs) -> FormatInfo | list[FormatInfo]:
    """Gets the information on a given file format stored in the database

    Parameters
    ----------
    file_format : str | int | UUID | FormatInfo
        The name (extension) of the format, or its ID. In the case of ambiguous extensions which could apply to multiple
        formats, the ID must be used here or a FileConverterDatabaseException will be raised. This also allows passing a
        FormatInfo to this, in which case that object will be silently returned, to allow normalising the input to
        always be a FormatInfo when output from this
    which : int | None
        In the case that an extension string is provided which turns out to be ambiguous, which of the listed
        possibilities to use from the zero-indexed list. Default None, which raises an exception for an ambiguous
        format. 0 may be used to select the first in the database, which is often a good default choice. The literal
        string "all" may be used to request all possibilites, in which case this method will return a list (even if
        there are zero or one possibilities)

    Returns
    -------
    FormatInfo | list[FormatInfo]
    """

    # Check for deprecated kwargs
    if "format_name_or_id" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`format_name_or_id`{tc.OFF} for the method "
                      f"{tc.CODE}`get_format_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`file_format`{tc.OFF} instead, "
                      "which has equivalent functionality and is being used now to normalise function signatures "
                      "across the package.", DeprecationWarning)
        file_format = kwargs["format_name_or_id"]

    return get_database().get_format_info(file_format, which)

Gets the information on a given file format stored in the database

Parameters

file_format : str | int | UUID | FormatInfo
The name (extension) of the format, or its ID. In the case of ambiguous extensions which could apply to multiple formats, the ID must be used here or a FileConverterDatabaseException will be raised. This also allows passing a FormatInfo to this, in which case that object will be silently returned, to allow normalising the input to always be a FormatInfo when output from this
which : int | None
In the case that an extension string is provided which turns out to be ambiguous, which of the listed possibilities to use from the zero-indexed list. Default None, which raises an exception for an ambiguous format. 0 may be used to select the first in the database, which is often a good default choice. The literal string "all" may be used to request all possibilites, in which case this method will return a list (even if there are zero or one possibilities)

Returns

FormatInfo | list[FormatInfo]
 
def get_format_pretty_name(file_format: str | int | UUID | FormatInfo)
Expand source code
def get_format_pretty_name(file_format: str | int | UUID | FormatInfo):
    """Gets a string for the name of a format which may or may not be ambiguous at this point

    Parameters
    ----------
    file_format : str | int | UUID | FormatInfo
        The name (extension) of the format, or its ID. In the case of ambiguous extensions which could apply to multiple
        formats, the ID must be used here or a FileConverterDatabaseException will be raised. This also allows passing a
        FormatInfo to this, in which case that object will be silently returned, to allow normalising the input to
        always be a FormatInfo when output from this

    Returns
    -------
    str
    """
    l_possible_format_info = get_format_info(file_format, "all")
    if len(l_possible_format_info) == 1:
        return l_possible_format_info[0].format_word()
    return f"{tc.MESSAGE}'{file_format}'{tc.OFF}"

Gets a string for the name of a format which may or may not be ambiguous at this point

Parameters

file_format : str | int | UUID | FormatInfo
The name (extension) of the format, or its ID. In the case of ambiguous extensions which could apply to multiple formats, the ID must be used here or a FileConverterDatabaseException will be raised. This also allows passing a FormatInfo to this, in which case that object will be silently returned, to allow normalising the input to always be a FormatInfo when output from this

Returns

str
 
def get_in_format_args(converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
arg: str | None = None,
**kwargs) ‑> tuple[list[FlagInfo], list[OptionInfo]] | ArgInfo
Expand source code
def get_in_format_args(converter: str | int | UUID | ConverterInfo,
                       in_format: str | int | UUID | FormatInfo,
                       arg: str | None = None,
                       **kwargs) -> tuple[list[FlagInfo], list[OptionInfo]] | ArgInfo:
    """Get the input flags and options supported by a given converter for a given format (provided as its extension).
    Optionally will provide information on just a single flag or option if its value is provided as an optional argument

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The converter name
    in_format : str | int | UUID | FormatInfo
        The file format name (extension)
    arg : str | None
        If provided, only information on this flag or option will be provided

    Returns
    -------
    tuple[list[FlagInfo], list[OptionInfo]]
        (if `arg` not provided) A list of info for the allowed flags, and a list of info for the allowed options. Each
        list is sorted by the ID of the flag or option.
    ArgInfo
        (if `arg` provided) Info on the provided flag or option
    """

    # Check for deprecated kwargs
    if "converter_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                      f"{tc.CODE}`get_in_format_args`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "accepts the converter name, ID, or info", DeprecationWarning)
        converter_info = get_converter_info(kwargs["converter_name"])
    else:
        converter_info = get_converter_info(converter)
    if "format_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`format_name`{tc.OFF} for the method "
                      f"{tc.CODE}`get_in_format_args`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`in_format`{tc.OFF} instead, which "
                      "accepts the format name, ID, or info", DeprecationWarning)
        in_format_info = get_converter_info(kwargs["format_name"])
    else:
        in_format_info = get_format_info(in_format)

    tl_args = converter_info.get_in_format_args(in_format_info)
    if not arg:
        return tl_args
    return _find_arg(tl_args, arg)

Get the input flags and options supported by a given converter for a given format (provided as its extension). Optionally will provide information on just a single flag or option if its value is provided as an optional argument

Parameters

converter : str | int | UUID | ConverterInfo
The converter name
in_format : str | int | UUID | FormatInfo
The file format name (extension)
arg : str | None
If provided, only information on this flag or option will be provided

Returns

tuple[list[FlagInfo], list[OptionInfo]]
(if arg not provided) A list of info for the allowed flags, and a list of info for the allowed options. Each list is sorted by the ID of the flag or option.
ArgInfo
(if arg provided) Info on the provided flag or option
def get_out_format_args(converter: str | int | UUID | ConverterInfo,
out_format: str | int | UUID | FormatInfo,
arg: str | None = None,
**kwargs) ‑> tuple[list[FlagInfo], list[OptionInfo]] | ArgInfo
Expand source code
def get_out_format_args(converter: str | int | UUID | ConverterInfo,
                        out_format: str | int | UUID | FormatInfo,
                        arg: str | None = None,
                        **kwargs) -> tuple[list[FlagInfo], list[OptionInfo]] | ArgInfo:
    """Get the input flags and options supported by a given converter for a given format (provided as its extension).
    Optionally will provide information on just a single flag or option if its value is provided as an optional argument

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The converter name
    out_format : str | int | UUID | FormatInfo
        The file format name (extension)
    arg : str | None
        If provided, only information on this flag or option will be provided

    Returns
    -------
    tuple[list[FlagInfo], list[OptionInfo]]
        (if `arg` not provided) A list of info for the allowed flags, and a list of info for the allowed options. Each
        list is sorted by the ID of the flag or option.
    ArgInfo
        (if `arg` provided) Info on the provided flag or option
    """

    # Check for deprecated kwargs
    if "converter_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                      f"{tc.CODE}`get_out_format_args`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "accepts the converter name, ID, or info", DeprecationWarning)
        converter_info = get_converter_info(kwargs["converter_name"])
    else:
        converter_info = get_converter_info(converter)
    if "format_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`format_name`{tc.OFF} for the method "
                      f"{tc.CODE}`get_out_format_args`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`out_format`{tc.OFF} instead, which "
                      "accepts the format name, ID, or info", DeprecationWarning)
        out_format_info = get_converter_info(kwargs["format_name"])
    else:
        out_format_info = get_format_info(out_format)

    tl_args = converter_info.get_out_format_args(out_format_info)
    if not arg:
        return tl_args
    return _find_arg(tl_args, arg)

Get the input flags and options supported by a given converter for a given format (provided as its extension). Optionally will provide information on just a single flag or option if its value is provided as an optional argument

Parameters

converter : str | int | UUID | ConverterInfo
The converter name
out_format : str | int | UUID | FormatInfo
The file format name (extension)
arg : str | None
If provided, only information on this flag or option will be provided

Returns

tuple[list[FlagInfo], list[OptionInfo]]
(if arg not provided) A list of info for the allowed flags, and a list of info for the allowed options. Each list is sorted by the ID of the flag or option.
ArgInfo
(if arg provided) Info on the provided flag or option
def get_possible_conversion_pathways(in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
only: "Literal['all'] | Literal['supported'] | Literal['registered']" = 'registered',
include: "Literal['best'] | Literal['shortest']" = 'best') ‑> list[ConversionPath]
Expand source code
def get_possible_conversion_pathways(in_format: str | int | UUID | FormatInfo,
                                     out_format: str | int | UUID | FormatInfo,
                                     only: Literal["all"] | Literal["supported"] | Literal["registered"] = "registered",
                                     include: Literal["best"] | Literal["shortest"] = "best"
                                     ) -> list[ConversionPath]:
    """As `get_conversion_pathway`, but instead of returning just one pathway, returns a list of pathways meeting the
    `include` criterion:

    "best": Include all pathways that are assessed as equally best based on the pathfinding weight criteria (which
        takes into account data and precision loss in each step of the conversion chain)

    "shortest": Include all pathways with an equally low number of steps
    """

    return get_database().conversions_table.get_possible_conversion_pathways(in_format=in_format,
                                                                             out_format=out_format,
                                                                             only=only,
                                                                             include=include)

As get_conversion_pathway(), but instead of returning just one pathway, returns a list of pathways meeting the include criterion:

"best": Include all pathways that are assessed as equally best based on the pathfinding weight criteria (which takes into account data and precision loss in each step of the conversion chain)

"shortest": Include all pathways with an equally low number of steps

def get_possible_conversions(in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo) ‑> list[Conversion]
Expand source code
def get_possible_conversions(in_format: str | int | UUID | FormatInfo,
                             out_format: str | int | UUID | FormatInfo) -> list[Conversion]:
    """Get a list of converters which can perform a conversion from one format to another and disambiguate in the case
    of ambiguous input/output formats

    Parameters
    ----------
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format

    Returns
    -------
    list[Conversion]
        A list of tuples, where each tuple's first item is the ConverterInfo of a converter which can perform a matching
        conversion, the second is the info of the input format for this conversion, and the third is the info of the
        output format
    """

    return get_database().conversions_table.get_possible_conversions(in_format=in_format,
                                                                     out_format=out_format)

Get a list of converters which can perform a conversion from one format to another and disambiguate in the case of ambiguous input/output formats

Parameters

in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format

Returns

list[Conversion]
A list of tuples, where each tuple's first item is the ConverterInfo of a converter which can perform a matching conversion, the second is the info of the input format for this conversion, and the third is the info of the output format
def get_possible_formats(converter: str | int | UUID | ConverterInfo,
**kwargs) ‑> tuple[list[FormatInfo], list[FormatInfo]]
Expand source code
def get_possible_formats(converter: str | int | UUID | ConverterInfo,
                         **kwargs) -> tuple[list[FormatInfo], list[FormatInfo]]:
    """Get a list of input and output formats that a given converter supports

    Parameters
    ----------
    converter: str | int | UUID | ConverterInfo
        The converter, specified by its name or ID

    Returns
    -------
    tuple[list[FormatInfo], list[FormatInfo]]
        A tuple of a list of the supported input formats and a list of the supported output formats
    """
    # Check for deprecated kwargs
    if "converter_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                      f"{tc.CODE}`disambiguate_formats`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "accepts the converter name, ID, or info", DeprecationWarning)
        converter = kwargs["converter_name"]
    return get_database().conversions_table.get_possible_formats(converter)

Get a list of input and output formats that a given converter supports

Parameters

converter : str | int | UUID | ConverterInfo
The converter, specified by its name or ID

Returns

tuple[list[FormatInfo], list[FormatInfo]]
A tuple of a list of the supported input formats and a list of the supported output formats
def load_database(prune=True) ‑> DataConversionDatabase
Expand source code
def load_database(prune=True) -> DataConversionDatabase:
    """Load and return a new instance of the data conversion database from the JSON database file in this package. This
    function should not be called directly unless you specifically need a new instance of the database object and can't
    deepcopy the database returned by `get_database()` or you need an unpruned database, as it's expensive to load it
    in.

    Parameters
    ----------
    prune : bool
        Whether or not to prune from the database any formats which have no supported conversions, default True

    Returns
    -------
    DataConversionDatabase
    """

    # Find and load the database JSON file
    d_data: dict = json.load(open(get_database_path(), "r"))

    return DataConversionDatabase(d_data, prune)

Load and return a new instance of the data conversion database from the JSON database file in this package. This function should not be called directly unless you specifically need a new instance of the database object and can't deepcopy the database returned by get_database() or you need an unpruned database, as it's expensive to load it in.

Parameters

prune : bool
Whether or not to prune from the database any formats which have no supported conversions, default True

Returns

DataConversionDatabase
 
def split_conversion_weight(conversion_weight: int)
Expand source code
def split_conversion_weight(conversion_weight: int):
    """Splits the total conversion weight into the parts for each component weight

    Parameters
    ----------
    conversion_weight : int
        The total conversion weight

    Returns
    -------
    ConversionWeightParts
        NamedTuple of prop_weight, prec_weight, format_weight, and conv_weight
    """

    prop_weight = conversion_weight >> PROP_WEIGHT_BIT_OFFSET
    conversion_weight -= prop_weight << PROP_WEIGHT_BIT_OFFSET

    prec_weight = conversion_weight >> PREC_WEIGHT_BIT_OFFSET
    conversion_weight -= prec_weight << PREC_WEIGHT_BIT_OFFSET

    format_weight = conversion_weight >> FORMAT_WEIGHT_BIT_OFFSET
    conversion_weight -= format_weight << FORMAT_WEIGHT_BIT_OFFSET

    conv_weight = conversion_weight >> CONV_WEIGHT_BIT_OFFSET

    return ConversionWeightParts(prop_weight, prec_weight, format_weight, conv_weight)

Splits the total conversion weight into the parts for each component weight

Parameters

conversion_weight : int
The total conversion weight

Returns

ConversionWeightParts
NamedTuple of prop_weight, prec_weight, format_weight, and conv_weight

Classes

class ArgInfo (id: int = <property object>,
name: str = <property object>,
description: str = <property object>,
info: str = <property object>,
parent: DataConversionDatabase | DBInfo | None = None,
s_in_formats: set[int] = <factory>,
s_out_formats: set[int] = <factory>)
Expand source code
@dataclass
class ArgInfo(DBInfo):
    """Class providing information on an argument accepted by a converter (whether it accepts a value or not)
    """

    s_in_formats: set[int] = field(default_factory=set)
    s_out_formats: set[int] = field(default_factory=set)

    # __hash__ needs to be inherited explicitly for dataclasses since they redefine __eq__
    __hash__ = DBInfo.__hash__

    @property
    def flag(self):
        """DEPRECATED: Now known as `name`
        """
        warnings.warn(f"The {tc.CODE}`flag`{tc.OFF} property of the {tc.CODE}`ArgInfo`{tc.OFF} class has been renamed "
                      f"to {tc.CODE}`name`{tc.OFF} as of version 0.4.0 and is due to be removed in a future version.",
                      DeprecationWarning)
        return self.name

Class providing information on an argument accepted by a converter (whether it accepts a value or not)

Ancestors

Subclasses

Instance variables

prop flag
Expand source code
@property
def flag(self):
    """DEPRECATED: Now known as `name`
    """
    warnings.warn(f"The {tc.CODE}`flag`{tc.OFF} property of the {tc.CODE}`ArgInfo`{tc.OFF} class has been renamed "
                  f"to {tc.CODE}`name`{tc.OFF} as of version 0.4.0 and is due to be removed in a future version.",
                  DeprecationWarning)
    return self.name

DEPRECATED: Now known as name

var s_in_formats : set[int]
var s_out_formats : set[int]

Inherited members

class Conversion (converter: ForwardRef('ConverterInfo'),
in_format: ForwardRef('FormatInfo'),
out_format: ForwardRef('FormatInfo'))
Expand source code
class Conversion(NamedTuple):
    """NamedTuple giving the basic details of a conversion - the input format, output format, and converter"""

    converter: ConverterInfo
    """The info object for the converter"""

    in_format: FormatInfo
    """The info object for the input file format"""

    out_format: FormatInfo
    """The info object for the output file format"""

    def is_valid(self):
        """Whether or not this represents a valid conversion"""
        return bool(disambiguate_formats(*self))

    def get_weight(self,
                   bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
        """The weight of this conversion, representing the amount of potential data loss"""
        return get_database().conversions_table.get_conversion_weight(*self, bits=bits)

    def format_oneline(self):
        """Formats the conversion as a string"""
        return MSG_CONVERSION_ONELINE.format(self.in_format.format_word(), self.out_format.format_word(),
                                             self.converter.format_word(), format_weight(self.get_weight()))

NamedTuple giving the basic details of a conversion - the input format, output format, and converter

Ancestors

  • builtins.tuple

Instance variables

var converter : ConverterInfo
Expand source code
class Conversion(NamedTuple):
    """NamedTuple giving the basic details of a conversion - the input format, output format, and converter"""

    converter: ConverterInfo
    """The info object for the converter"""

    in_format: FormatInfo
    """The info object for the input file format"""

    out_format: FormatInfo
    """The info object for the output file format"""

    def is_valid(self):
        """Whether or not this represents a valid conversion"""
        return bool(disambiguate_formats(*self))

    def get_weight(self,
                   bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
        """The weight of this conversion, representing the amount of potential data loss"""
        return get_database().conversions_table.get_conversion_weight(*self, bits=bits)

    def format_oneline(self):
        """Formats the conversion as a string"""
        return MSG_CONVERSION_ONELINE.format(self.in_format.format_word(), self.out_format.format_word(),
                                             self.converter.format_word(), format_weight(self.get_weight()))

The info object for the converter

var in_format : FormatInfo
Expand source code
class Conversion(NamedTuple):
    """NamedTuple giving the basic details of a conversion - the input format, output format, and converter"""

    converter: ConverterInfo
    """The info object for the converter"""

    in_format: FormatInfo
    """The info object for the input file format"""

    out_format: FormatInfo
    """The info object for the output file format"""

    def is_valid(self):
        """Whether or not this represents a valid conversion"""
        return bool(disambiguate_formats(*self))

    def get_weight(self,
                   bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
        """The weight of this conversion, representing the amount of potential data loss"""
        return get_database().conversions_table.get_conversion_weight(*self, bits=bits)

    def format_oneline(self):
        """Formats the conversion as a string"""
        return MSG_CONVERSION_ONELINE.format(self.in_format.format_word(), self.out_format.format_word(),
                                             self.converter.format_word(), format_weight(self.get_weight()))

The info object for the input file format

var out_format : FormatInfo
Expand source code
class Conversion(NamedTuple):
    """NamedTuple giving the basic details of a conversion - the input format, output format, and converter"""

    converter: ConverterInfo
    """The info object for the converter"""

    in_format: FormatInfo
    """The info object for the input file format"""

    out_format: FormatInfo
    """The info object for the output file format"""

    def is_valid(self):
        """Whether or not this represents a valid conversion"""
        return bool(disambiguate_formats(*self))

    def get_weight(self,
                   bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
        """The weight of this conversion, representing the amount of potential data loss"""
        return get_database().conversions_table.get_conversion_weight(*self, bits=bits)

    def format_oneline(self):
        """Formats the conversion as a string"""
        return MSG_CONVERSION_ONELINE.format(self.in_format.format_word(), self.out_format.format_word(),
                                             self.converter.format_word(), format_weight(self.get_weight()))

The info object for the output file format

Methods

def format_oneline(self)
Expand source code
def format_oneline(self):
    """Formats the conversion as a string"""
    return MSG_CONVERSION_ONELINE.format(self.in_format.format_word(), self.out_format.format_word(),
                                         self.converter.format_word(), format_weight(self.get_weight()))

Formats the conversion as a string

def get_weight(self, bits: "Literal['all'] | Literal['top'] | Literal['bottom']" = 'all')
Expand source code
def get_weight(self,
               bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
    """The weight of this conversion, representing the amount of potential data loss"""
    return get_database().conversions_table.get_conversion_weight(*self, bits=bits)

The weight of this conversion, representing the amount of potential data loss

def is_valid(self)
Expand source code
def is_valid(self):
    """Whether or not this represents a valid conversion"""
    return bool(disambiguate_formats(*self))

Whether or not this represents a valid conversion

class ConversionPath (iterable=None)
Expand source code
class ConversionPath(list[Conversion]):
    """A path of conversion steps"""

    def __init__(self, iterable=None):
        if not iterable:
            super().__init__()
            return
        super().__init__(iterable)
        # Check that all items are of the correct type
        if not all([isinstance(x, Conversion) for x in self]):
            raise TypeError(f"{tc.CODE}`ConversionPath`{tc.OFF} must be initialised with an iterable of {tc.CODE}`"
                            f"Conversion`{tc.OFF} objects")

    def __setitem__(self, key, value):
        if not isinstance(value, Conversion):
            raise TypeError(f"{tc.CODE}`ConversionPath`{tc.OFF} items must be set to {tc.CODE}`Conversion`{tc.OFF} "
                            "objects")
        return super().__setitem__(key, value)

    def __hash__(self):
        return hash(tuple([hash(step) for step in self]))

    def get_weight(self,
                   bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
        """The weight for the full path"""
        return sum([x.get_weight(bits=bits) for x in self])

    def is_valid(self):
        """Whether or not the path represents a valid series of steps"""

        # Check that all individual steps are valid
        if not all([x.is_valid() for x in self]):
            return False

        # Output format of each step should match input of next
        if not all([self[i].out_format is self[i+1].in_format for i in range(len(self)-1)]):
            return False

        return True

    def get_name(self):
        """Get a string representing the path"""
        if len(self) == 0:
            return ""
        name = self[0].in_format.disambiguated_name
        for step in self:
            name += f"-{step.out_format.disambiguated_name}"
        return name

    @staticmethod
    def _align_step_strs(l_step_raw_strs: list[str]):

        # We want to format all steps to align them, so split them into parts
        ll_step_parts: list[list[str]] = []
        L_CONVERSION_ONELINE_SPLIT_POINTS = [x for x in MSG_CONVERSION_ONELINE.split("{}") if x]
        for step_raw_str in l_step_raw_strs:
            l_step_parts: list[str] = []
            tail = step_raw_str
            for split_point in L_CONVERSION_ONELINE_SPLIT_POINTS:
                head, tail = tail.split(split_point)
                l_step_parts.append(head)
            l_step_parts.append(tail)
            ll_step_parts.append(l_step_parts)

        # Get the maximum length of each part
        l_part_max_lens: list[int] = []
        for i in range(len(L_CONVERSION_ONELINE_SPLIT_POINTS)+1):
            l_part_max_lens.append(max([len(l_step_parts[i]) for l_step_parts in ll_step_parts]))

        # Recompile the strings with each part padded to the maximum length
        l_step_strs: list[str] = []
        for l_step_parts in ll_step_parts:
            step_str = ""
            for i, part in enumerate(l_step_parts):
                step_str += f"{part:<{l_part_max_lens[i]}}"
                if i < len(L_CONVERSION_ONELINE_SPLIT_POINTS):
                    step_str += L_CONVERSION_ONELINE_SPLIT_POINTS[i]
            l_step_strs.append(step_str)

        return l_step_strs

    @staticmethod
    def _format_total_weight_strs(l_total_weights: list[int], aligned_step_str: str):
        weight_split_point = L_CONVERSION_ONELINE_SPLIT_POINTS[-1]
        len_before_weight = displaylen(aligned_step_str.split(weight_split_point)[0]) + len(weight_split_point)
        l_total_weight_strs: list[str] = []

        for total_weight in l_total_weights:
            l_total_weight_strs.append(f"{MSG_TOTAL_WEIGHT.split('{}')[0]:>{len_before_weight}}" +
                                       format_weight(total_weight, color=tc.DARKNUMBER))
        return l_total_weight_strs

    def _get_step_detail_strs(self, align=True):

        if len(self) == 0:
            raise ValueError("Conversion pathway is empty")

        l_step_strs = [f"{i+1}) {step.format_oneline()}" for i, step in enumerate(self)]
        if align:
            l_step_strs = self._align_step_strs(l_step_strs)

        return l_step_strs

    def _get_detail_lines(self):

        l_step_strs = self._get_step_detail_strs()

        weight_str = self._format_total_weight_strs([self.get_weight()], l_step_strs[0])[0]

        return l_step_strs, weight_str

    def _format_path_details(self, l_step_strs: list[str], weight_str: str, show_command: bool):
        msg = "\n".join(l_step_strs) + f"\n{weight_str}"
        if show_command:
            msg += f"\nInvoke with: {tc.CODE}`--path"

            for i, step in enumerate(self):
                if i == 0:
                    msg += f" {step.in_format.id}"
                msg += f" {step.converter.name}"
                msg += f" {step.out_format.id}"

            msg += f"`{tc.OFF}"
        return msg

    def format_detailed(self, show_command=False):
        """Format the full details of the path as a string"""

        l_step_strs, weight_str = self._get_detail_lines()

        return self._format_path_details(l_step_strs, weight_str, show_command=show_command)

    @staticmethod
    def format_multiple_detailed(l_paths: list[ConversionPath], show_command=False):
        """Format details of a list of paths, aligning them all"""

        # We first get a list of all step detail strings across all paths, and align them
        all_step_strs: list[str] = []
        l_weights: list[int] = []
        for path in l_paths:
            all_step_strs += path._get_step_detail_strs(align=False)
            l_weights.append(path.get_weight())
        l_aligned_step_strs = ConversionPath._align_step_strs(all_step_strs)

        # Get the weight string for each path
        l_weight_strs = ConversionPath._format_total_weight_strs(l_weights, l_aligned_step_strs[0])

        # Now construct the details string for each path
        l_path_strs = [""]*len(l_paths)

        # As we iterate over steps in paths again, keep track of the corresponding index in the list of aligned strings
        global_step_index = 0

        for path_index in range(len(l_paths)):
            path = l_paths[path_index]
            l_step_strs = [""]*len(path)
            for local_step_index in range(len(path)):
                l_step_strs[local_step_index] = l_aligned_step_strs[global_step_index]
                global_step_index += 1
            l_path_strs[path_index] = path._format_path_details(l_step_strs, l_weight_strs[path_index],
                                                                show_command=show_command)

        return "\n\n".join(l_path_strs)

A path of conversion steps

Ancestors

  • builtins.list

Static methods

def format_multiple_detailed(l_paths: list[ConversionPath],
show_command=False)
Expand source code
@staticmethod
def format_multiple_detailed(l_paths: list[ConversionPath], show_command=False):
    """Format details of a list of paths, aligning them all"""

    # We first get a list of all step detail strings across all paths, and align them
    all_step_strs: list[str] = []
    l_weights: list[int] = []
    for path in l_paths:
        all_step_strs += path._get_step_detail_strs(align=False)
        l_weights.append(path.get_weight())
    l_aligned_step_strs = ConversionPath._align_step_strs(all_step_strs)

    # Get the weight string for each path
    l_weight_strs = ConversionPath._format_total_weight_strs(l_weights, l_aligned_step_strs[0])

    # Now construct the details string for each path
    l_path_strs = [""]*len(l_paths)

    # As we iterate over steps in paths again, keep track of the corresponding index in the list of aligned strings
    global_step_index = 0

    for path_index in range(len(l_paths)):
        path = l_paths[path_index]
        l_step_strs = [""]*len(path)
        for local_step_index in range(len(path)):
            l_step_strs[local_step_index] = l_aligned_step_strs[global_step_index]
            global_step_index += 1
        l_path_strs[path_index] = path._format_path_details(l_step_strs, l_weight_strs[path_index],
                                                            show_command=show_command)

    return "\n\n".join(l_path_strs)

Format details of a list of paths, aligning them all

Methods

def format_detailed(self, show_command=False)
Expand source code
def format_detailed(self, show_command=False):
    """Format the full details of the path as a string"""

    l_step_strs, weight_str = self._get_detail_lines()

    return self._format_path_details(l_step_strs, weight_str, show_command=show_command)

Format the full details of the path as a string

def get_name(self)
Expand source code
def get_name(self):
    """Get a string representing the path"""
    if len(self) == 0:
        return ""
    name = self[0].in_format.disambiguated_name
    for step in self:
        name += f"-{step.out_format.disambiguated_name}"
    return name

Get a string representing the path

def get_weight(self, bits: "Literal['all'] | Literal['top'] | Literal['bottom']" = 'all')
Expand source code
def get_weight(self,
               bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
    """The weight for the full path"""
    return sum([x.get_weight(bits=bits) for x in self])

The weight for the full path

def is_valid(self)
Expand source code
def is_valid(self):
    """Whether or not the path represents a valid series of steps"""

    # Check that all individual steps are valid
    if not all([x.is_valid() for x in self]):
        return False

    # Output format of each step should match input of next
    if not all([self[i].out_format is self[i+1].in_format for i in range(len(self)-1)]):
        return False

    return True

Whether or not the path represents a valid series of steps

class ConversionQualityInfo (conversion: Conversion,
qual_str: str,
details: str,
d_prop_conversion_info: dict[str, PropertyConversionInfo])
Expand source code
@dataclass
class ConversionQualityInfo:
    """Class describing the quality of a conversion from one format to another with a given converter.
    """

    conversion: Conversion
    """The basic information of the conversion as a Conversion NamedTuple"""

    qual_str: str
    """A string describing the quality of the conversion"""

    details: str
    """A string providing details on any possible issues with the conversion"""

    d_prop_conversion_info: dict[str, PropertyConversionInfo]
    """A dict of PropertyConversionInfo objects, which provide information on each property's support in the
    input and output file formats and a note on the implications
    """

    prop_weight: int | None = field(init=False, repr=False, default=None)
    """The property weight for the conversion, based on how many format properties are/might be lost"""

    prec_weight: int | None = field(init=False, repr=False, default=None)
    """The precision weight for the conversion, based on how much precision is/might be lost"""

    format_weight: int | None = field(init=False, repr=False, default=None)
    """The format weight for the conversion, based on how reliable formats are in conversions from experience"""

    conv_weight: int | None = field(init=False, repr=False, default=None)
    """The converter for the conversion, based on how well-supported the converter is (roughly)"""

    def __post_init__(self):
        """Finalise setting up the class"""
        self.prop_weight, self.prec_weight, self.format_weight, self.conv_weight = split_conversion_weight(self.weight)

    @property
    def converter(self):
        """The info object for the converter"""
        return self.conversion.converter

    @property
    def converter_name(self):
        return self.conversion.converter.name

    @property
    def in_format(self):
        """The info object for the input file format"""
        return self.conversion.in_format

    @property
    def out_format(self):
        """The info object for the output file format"""
        return self.conversion.out_format

    @cached_property
    def valid(self):
        """The full weight for the conversion, for the purpose of determining optimal conversion pathways"""
        return self.conversion.is_valid()

    @cached_property
    def weight(self,
               bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
        """The full weight for the conversion, for the purpose of determining optimal conversion pathways"""
        return self.conversion.get_weight(bits=bits)

    @staticmethod
    def factory(converter: ConverterInfo, in_format: FormatInfo, out_format: FormatInfo, *args, **kwargs):
        """Construct the object from the basic info, making the `Conversion` NamedTuple it contains"""
        return ConversionQualityInfo(Conversion(converter, in_format, out_format), *args, **kwargs)

Class describing the quality of a conversion from one format to another with a given converter.

Static methods

def factory(converter: ConverterInfo,
in_format: FormatInfo,
out_format: FormatInfo,
*args,
**kwargs)
Expand source code
@staticmethod
def factory(converter: ConverterInfo, in_format: FormatInfo, out_format: FormatInfo, *args, **kwargs):
    """Construct the object from the basic info, making the `Conversion` NamedTuple it contains"""
    return ConversionQualityInfo(Conversion(converter, in_format, out_format), *args, **kwargs)

Construct the object from the basic info, making the Conversion NamedTuple it contains

Instance variables

var conv_weight : int | None

The converter for the conversion, based on how well-supported the converter is (roughly)

var conversion : Conversion

The basic information of the conversion as a Conversion NamedTuple

prop converter
Expand source code
@property
def converter(self):
    """The info object for the converter"""
    return self.conversion.converter

The info object for the converter

prop converter_name
Expand source code
@property
def converter_name(self):
    return self.conversion.converter.name
var d_prop_conversion_info : dict[str, PropertyConversionInfo]

A dict of PropertyConversionInfo objects, which provide information on each property's support in the input and output file formats and a note on the implications

var details : str

A string providing details on any possible issues with the conversion

var format_weight : int | None

The format weight for the conversion, based on how reliable formats are in conversions from experience

prop in_format
Expand source code
@property
def in_format(self):
    """The info object for the input file format"""
    return self.conversion.in_format

The info object for the input file format

prop out_format
Expand source code
@property
def out_format(self):
    """The info object for the output file format"""
    return self.conversion.out_format

The info object for the output file format

var prec_weight : int | None

The precision weight for the conversion, based on how much precision is/might be lost

var prop_weight : int | None

The property weight for the conversion, based on how many format properties are/might be lost

var qual_str : str

A string describing the quality of the conversion

var valid
Expand source code
@cached_property
def valid(self):
    """The full weight for the conversion, for the purpose of determining optimal conversion pathways"""
    return self.conversion.is_valid()

The full weight for the conversion, for the purpose of determining optimal conversion pathways

var weight
Expand source code
@cached_property
def weight(self,
           bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all"):
    """The full weight for the conversion, for the purpose of determining optimal conversion pathways"""
    return self.conversion.get_weight(bits=bits)

The full weight for the conversion, for the purpose of determining optimal conversion pathways

class ConversionWeightParts (prop_weight: ForwardRef('int'),
prec_weight: ForwardRef('int'),
format_weight: ForwardRef('int'),
conv_weight: ForwardRef('int'))
Expand source code
class ConversionWeightParts(NamedTuple):
    prop_weight: int
    prec_weight: int
    format_weight: int
    conv_weight: int

ConversionWeightParts(prop_weight, prec_weight, format_weight, conv_weight)

Ancestors

  • builtins.tuple

Instance variables

var conv_weight : int
Expand source code
class ConversionWeightParts(NamedTuple):
    prop_weight: int
    prec_weight: int
    format_weight: int
    conv_weight: int

Alias for field number 3

var format_weight : int
Expand source code
class ConversionWeightParts(NamedTuple):
    prop_weight: int
    prec_weight: int
    format_weight: int
    conv_weight: int

Alias for field number 2

var prec_weight : int
Expand source code
class ConversionWeightParts(NamedTuple):
    prop_weight: int
    prec_weight: int
    format_weight: int
    conv_weight: int

Alias for field number 1

var prop_weight : int
Expand source code
class ConversionWeightParts(NamedTuple):
    prop_weight: int
    prec_weight: int
    format_weight: int
    conv_weight: int

Alias for field number 0

class ConversionsTable (l_converts_to: list[dict[str, bool | int | str | None]],
d_format_id_aliases: dict[int, set[int]],
parent: DataConversionDatabase)
Expand source code
class ConversionsTable:
    """Class providing information on available file format conversions
    """

    def __init__(self,
                 l_converts_to: list[dict[str, bool | int | str | None]],
                 d_format_id_aliases: dict[int, set[int]],
                 parent: DataConversionDatabase):
        """Set up the class - this will be initialised within a `DataConversionDatabase`, which we set as the parent

        Parameters
        ----------
        l_converts_to : list[dict[str, bool  |  int  |  str  |  None]]
            The list of dicts in the database providing information on possible conversions
        d_format_id_aliases : Iterable[int]
            A dict of primary format IDs listing the aliases for each
        parent : DataConversionDatabase
            The database which this belongs to

        Raises
        ------
        FileConverterDatabaseException
        """

        self.parent = parent

        # Build the conversion graphs - each format is a vertex, each conversion is an edge

        num_formats = len(d_format_id_aliases)

        # igraph doesn't support int128s (used for UUIDs) for indices, so associate each format with an index
        self.d_indices_from_uuids: dict[int, int] = {}
        self.d_uuids_from_indices: dict[int, int] = {}

        for i, (prim_id, s_alias_ids) in enumerate(d_format_id_aliases.items()):
            for format_id in s_alias_ids:
                self.d_indices_from_uuids[format_id] = i
            self.d_uuids_from_indices[i] = prim_id

        # Trim down the conversions list to only conversions involving primary formats
        l_converts_to = [x for x in l_converts_to if x[DB_IN_ID_KEY] in d_format_id_aliases and
                         x[DB_OUT_ID_KEY] in d_format_id_aliases]

        l_supported_conversions = [x for x in l_converts_to if
                                   self.parent.get_converter_info(x[DB_CONV_ID_KEY]).name in L_SUPPORTED_CONVERTERS]
        l_registered_conversions = [x for x in l_supported_conversions if
                                    self.parent.get_converter_info(x[DB_CONV_ID_KEY]).name in L_REGISTERED_CONVERTERS]

        # We make separate graphs for all known conversions, all supported conversions, and all registered conversions
        self.graph: ig.Graph
        self.supported_graph: ig.Graph
        self.registered_graph: ig.Graph

        for support_type, l_conversions in (("", l_converts_to),
                                            ("supported_", l_supported_conversions),
                                            ("registered_", l_registered_conversions)):

            # Calculate conversion weights if they aren't already stored in the database
            l_conv_weights = [x[DB_WEIGHT_KEY] if x.get(DB_WEIGHT_KEY) else
                              calc_conversion_weight(self.parent.get_converter_info(x[DB_CONV_ID_KEY]),
                                                     self.parent.get_format_info(x[DB_IN_ID_KEY]),
                                                     self.parent.get_format_info(x[DB_OUT_ID_KEY]))
                              for x in l_conversions]
            # So as to not overload igraph, we also store split the 64-bit weights into the top and lower sections of
            # bits. We add 1 to each weight to ensure that all weights are non-zero
            l_conv_weights_top = [(x >> CONV_WEIGHT_SPLIT_BIT) + 1 for x in l_conv_weights]
            l_conv_weights_bottom = [x-(x_top-1 << CONV_WEIGHT_SPLIT_BIT)+1
                                     for x, x_top in zip(l_conv_weights, l_conv_weights_top)]

            graph = ig.Graph(n=num_formats,
                             directed=True,
                             # Each vertex stores the ID of the primary format
                             vertex_attrs={DB_ID_KEY: [self.d_uuids_from_indices[i] for i in range(num_formats)]},
                             edges=[(self.d_indices_from_uuids[x[DB_IN_ID_KEY]],
                                    self.d_indices_from_uuids[x[DB_OUT_ID_KEY]]) for x in l_conversions],
                             # Each edge stores the id and name of the converter used for the conversion
                             edge_attrs={DB_CONV_ID_KEY: [x[DB_CONV_ID_KEY] for x in l_conversions],
                                         DB_NAME_KEY: [self.parent.get_converter_info(x[DB_CONV_ID_KEY]).name
                                                       for x in l_conversions],
                                         CONV_WEIGHT_KEY: l_conv_weights,
                                         CONV_WEIGHT_TOP_KEY: l_conv_weights_top,
                                         CONV_WEIGHT_BOTTOM_KEY: l_conv_weights_bottom})

            setattr(self, f"{support_type}graph", graph)

    def _get_desired_graph(self, only: Literal["all"] | Literal["supported"] | Literal["registered"] = "registered"
                           ) -> ig.Graph:
        if only == "all":
            support_type = ""
        elif only == "supported":
            support_type = "supported_"
        elif only == "registered":
            support_type = "registered_"
        else:
            raise ValueError(f"Invalue value '{only}' passed to `only` kwarg of `_get_desired_graph`")

        return getattr(self, f"{support_type}graph")

    def _get_possible_converters(self, in_format_info: FormatInfo, out_format_info: FormatInfo,
                                 only: Literal["all"] | Literal["supported"] | Literal["registered"] = "all"):
        """Get a list of all converters which can convert from one format to another
        """
        graph = self._get_desired_graph(only)
        l_edges = graph.es.select(_source=self.d_indices_from_uuids[in_format_info.id],
                                  _target=self.d_indices_from_uuids[out_format_info.id])
        return [x[DB_NAME_KEY] for x in l_edges]

    @lru_cache(maxsize=None)
    def get_conversion_quality(self,
                               converter: str | int | UUID | ConverterInfo,
                               in_format: str | int | UUID | FormatInfo,
                               out_format: str | int | UUID | FormatInfo,
                               **kwargs) -> ConversionQualityInfo | None:
        """Get an indication of the quality of a conversion from one format to another, or if it's not possible

        Parameters
        ----------
        converter : str | int | UUID | ConverterInfo
            The converter, specified by its name, ID, or info
        in_format : str | int | UUID | FormatInfo
            The extension, ID, or info of the input file format
        out_format : str | int | UUID | FormatInfo
            The extension, ID, or info of the output file format

        Returns
        -------
        ConversionQualityInfo | None
            If the conversion is not possible, returns None. If the conversion is possible, returns a
            `ConversionQualityInfo` object with info on the conversion
        """

        # Check for deprecated kwargs
        if "converter_name" in kwargs:
            warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                          f"{tc.CODE}`get_conversion_quality`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                          f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                          "accepts the converter name, ID, or info", DeprecationWarning)
            converter_info = get_converter_info(kwargs["converter_name"])
        else:
            converter_info = get_converter_info(converter)

        # Get all possible format infos for each format
        l_in_format_info = self.parent.get_format_info(in_format, "all")
        l_out_format_info = self.parent.get_format_info(out_format, "all")

        # First check if the conversion is possible for at least one combination
        l_found_combinations: list[tuple[FormatInfo, FormatInfo]] = []
        for in_format_info, out_format_info in product(l_in_format_info, l_out_format_info):
            if converter_info.name in self._get_possible_converters(in_format_info, out_format_info):
                l_found_combinations.append((in_format_info, out_format_info))
        if len(l_found_combinations) == 0:
            return None

        # Check if the conversion is ambiguous
        if len(l_found_combinations) > 1:

            converter_name = converter.format_word() if isinstance(
                converter, ConverterInfo) else f"{tc.PATH}'{converter}'{tc.OFF}"
            in_format_name = in_format.format_word() if isinstance(
                in_format, FormatInfo) else f"{tc.PATH}'{in_format}'{tc.OFF}"
            out_format_name = out_format.format_word() if isinstance(
                out_format, FormatInfo) else f"{tc.PATH}'{out_format}'{tc.OFF}"

            msg = (f"Conversion from {in_format_name} to {out_format_name} with converter "
                   f"{converter_name} is ambiguous. Please use the ID or disambiguated name (listed below) "
                   "of the desired conversion. Possible matching conversions are:\n")
            for possible_in_format, possible_out_format in l_found_combinations:
                msg += (f"    {possible_in_format.format_inline()} to {possible_out_format.format_inline()}\n")
            # Trim the final newline from the message
            msg = msg[:-1]
            raise FileConverterDatabaseException(msg, help=True)

        in_format_info, out_format_info = l_found_combinations[0]

        # The conversion is possible. Now determine how many properties of the output format are not in the input
        # format and might end up being extrapolated
        num_out_props = 0
        num_new_props = 0
        any_unknown = False
        d_prop_conversion_info: dict[str, PropertyConversionInfo] = {}
        for prop in D_FORMAT_PROPERTY_ATTRS:
            in_prop: bool | None = getattr(in_format_info, prop)
            out_prop: bool | None = getattr(out_format_info, prop)

            d_prop_conversion_info[prop] = PropertyConversionInfo(prop, in_prop, out_prop)

            # Check for None, indicating we don't have full information on both formats
            if in_prop is None or out_prop is None:
                any_unknown = True
            elif out_prop:
                num_out_props += 1
                if not in_prop:
                    num_new_props += 1

        # Determine the conversion quality
        if num_out_props > 0:
            qual_ratio = 1 - num_new_props/num_out_props
        else:
            qual_ratio = 1

        if any_unknown:
            qual_str = const.QUAL_UNKNOWN
        elif num_out_props == 0 or qual_ratio >= 0.8:
            qual_str = const.QUAL_VERYGOOD
        elif qual_ratio >= 0.6:
            qual_str = const.QUAL_GOOD
        elif qual_ratio >= 0.4:
            qual_str = const.QUAL_OKAY
        elif qual_ratio >= 0.2:
            qual_str = const.QUAL_POOR
        else:
            qual_str = const.QUAL_VERYPOOR

        # Construct the details string for info on possible issues with the conversion

        # Sort the keys by label alphabetically
        l_props: list[str] = list(d_prop_conversion_info.keys())
        l_props.sort(key=lambda x: d_prop_conversion_info[x].label)

        details = "\n".join(
            [d_prop_conversion_info[x].description for x in l_props if d_prop_conversion_info[x].description])

        return ConversionQualityInfo.factory(converter=converter_info,
                                             in_format=in_format_info,
                                             out_format=out_format_info,
                                             qual_str=qual_str,
                                             details=details,
                                             d_prop_conversion_info=d_prop_conversion_info)

    def get_conversion_weight(self,
                              converter: str | int | UUID | ConverterInfo,
                              in_format: str | int | UUID | FormatInfo,
                              out_format: str | int | UUID | FormatInfo,
                              bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all") -> int:
        """Get the weight for a desired conversion.

        Parameters
        ----------
        converter : str | int | UUID | ConverterInfo
            The name, ID, or info of the converter used for this conversion
        in_format : str | int | UUID | FormatInfo
            The extension, ID, or info of the converter of the input file format
        out_format : str | int | UUID | FormatInfo
            The extension, ID, or info of the converter of the output file format
        bits : Literal["all"] | Literal["top"] | Literal["bottom"]
            (Used when 64-bit ints are too large for some purposes) Whether to get the full weight ("all"), just the top
            half of the bits ("top"), or just the lower half of the bits ("bottom")

        Returns
        -------
        int
            The 64-bit combined weight of this conversion (unless bits=="top" or bits=="bottom", in which case the top
            or bottom 32 bits of the weight will be returned)

        Raises
        ------
        FileConverterDatabaseException
            If the requested conversion is not possible
        """
        converter_info = self.parent.get_converter_info(converter)
        in_format_info = self.parent.get_format_info(in_format)
        out_format_info = self.parent.get_format_info(out_format)

        l_edges = self.graph.es.select(_source=self.d_indices_from_uuids[in_format_info.id],
                                       _target=self.d_indices_from_uuids[out_format_info.id],
                                       **{DB_CONV_ID_KEY: converter_info.id})

        if len(l_edges) == 0:
            raise FileConverterDatabaseException(f"Conversion from {in_format_info.name} to {out_format_info.name} "
                                                 f"with converter {converter_info.pretty_name} is not supported",
                                                 help=True)

        if bits == "top":
            return l_edges[0][CONV_WEIGHT_TOP_KEY]
        elif bits == "bottom":
            return l_edges[0][CONV_WEIGHT_BOTTOM_KEY]
        else:
            return l_edges[0][CONV_WEIGHT_KEY]

    def get_possible_conversions(self,
                                 in_format: str | int | UUID | FormatInfo,
                                 out_format: str | int | UUID | FormatInfo,
                                 only: Literal["all"] | Literal["supported"] | Literal["registered"] = "all"
                                 ) -> list[Conversion]:
        """Get a list of converters which can perform a conversion from one format to another, disambiguating in the
        case of ambiguous formats and providing IDs for input/output formats for possible conversions

        Parameters
        ----------
        in_format : str | int | UUID | FormatInfo
            The extension, ID, or info of the converter of the input file format
        out_format : str | int | UUID | FormatInfo
            The extension, ID, or info of the converter of the output file format
        only : Literal["all"] | Literal["supported"] | Literal["registered"], optional
            Which converters to limit the search to:
            - "all": All known converters
            - "supported": Only converters supported by this utility, even if not currently available (e.g. they don't
            work on your OS)
            - "registered" (default): Only converters supported by this utility and currently available

        Returns
        -------
        list[tuple[ConverterInfo, FormatInfo, FormatInfo]]
            A list of tuples, where each tuple's first item is the ConverterInfo of a converter which can perform a
            matching conversion, the second is the info of the input format for this conversion, and the third is the
            info of the output format
        """
        l_in_format_infos = self.parent.get_format_info(in_format, which="all")
        l_out_format_infos = self.parent.get_format_info(out_format, which="all")

        # Start a list of all possible conversions
        l_possible_conversions = []

        # Iterate over all possible combinations of input and output formats
        for in_format_info, out_format_info in product(l_in_format_infos, l_out_format_infos):

            # Filter for converters which can perform this conversion
            l_converter_names = self._get_possible_converters(in_format_info, out_format_info, only=only)

            for converter_name in l_converter_names:
                l_possible_conversions.append(Conversion(self.parent.get_converter_info(converter_name),
                                                         in_format_info, out_format_info))

        return l_possible_conversions

    def _get_l_paths(self,
                     in_format_info: FormatInfo,
                     out_format_info: FormatInfo,
                     only: Literal["all"] | Literal["supported"] | Literal["registered"] = "registered",
                     include: Literal["best"] | Literal["shortest"] = "best"):
        """Get a raw list of paths from the graph of conversion pathways"""

        # Check if the formats are the same
        if in_format_info is out_format_info:
            return []

        graph: ig.Graph = self._get_desired_graph(only=only)

        # Query the graph for the shortest paths to perform this conversion. If no conversions are possible, igraph
        # will print a warning, which we catch and suppress here
        with catch_warnings(record=True) as l_warnings:
            l_raw_paths: list[list[int]] = (
                graph.get_all_shortest_paths(self.d_indices_from_uuids[in_format_info.id],
                                             to=self.d_indices_from_uuids[out_format_info.id],
                                             weights=CONV_WEIGHT_TOP_KEY
                                             if include == "best" else None))
            for warning in l_warnings:
                if ("Couldn't reach some vertices" not in str(warning.message) and
                        "Couldn't reach some of the requested target vertices" not in str(warning.message)):
                    print(warning, file=sys.stderr)

        l_paths = [self._format_path(x, in_format_info, out_format_info, only) for x in l_raw_paths]

        # If we're just getting the shortest paths, we have all we need now, so return them
        if include == "shortest":
            return l_paths

        # If no paths are possible, return here
        if not l_paths:
            return l_paths

        # igraph can't handle full 64-bit weights, so the shortest paths will only be based on the top half of the bits.
        # We now thus want to filter the paths to just those with the lowest weights

        # Sort paths by their weight
        l_paths.sort(key=lambda x: x.get_weight())

        # Filter to only the paths with the equally-lowest weight
        lowest_weight = l_paths[0].get_weight()
        l_lowest_weight_paths = [x for x in l_paths if x.get_weight() == lowest_weight]

        return l_lowest_weight_paths

    def _format_path(self,
                     raw_path,
                     in_format_info: FormatInfo,
                     out_format_info: FormatInfo,
                     only: Literal["all"] | Literal["supported"] | Literal["registered"] = "registered"
                     ):
        """Format a raw path into the `ConversionPath` output format"""

        graph: ig.Graph = self._get_desired_graph(only)

        path = ConversionPath()
        for i in range(len(raw_path)-1):
            source_index = raw_path[i]
            source_id: int = self.d_uuids_from_indices[source_index]
            target_index: int = raw_path[i+1]
            target_id: int = self.d_uuids_from_indices[target_index]
            converter_name: str = graph.es.select(_source=source_index, _target=target_index)[0][DB_NAME_KEY]
            path.append(Conversion(get_converter_info(converter_name),
                                   self.parent.get_format_info(source_id),
                                   self.parent.get_format_info(target_id)))

        # If the input or output format is an alias, make sure the path references that particular alias
        if not in_format_info.is_primary:
            path[0] = path[0]._replace(in_format=in_format_info)
        if not out_format_info.is_primary:
            path[-1] = path[-1]._replace(out_format=out_format_info)

        return path

    def get_conversion_pathway(self,
                               in_format: str | int | UUID | FormatInfo,
                               out_format: str | int | UUID | FormatInfo,
                               only: Literal["all"] | Literal["supported"] | Literal["registered"] = "registered"
                               ) -> ConversionPath | None:
        """Gets a pathway to convert from one format to another
        """

        in_format_info = self.parent.get_format_info(in_format)
        out_format_info = self.parent.get_format_info(out_format)

        l_paths = self._get_l_paths(in_format_info, out_format_info, only=only)

        # Check if any paths are possible
        if not l_paths or not l_paths[0]:
            return None

        # Any paths returned here are equally valid. For stability, pick the first alphabetically by name
        l_paths.sort(key=lambda x: x.get_name())
        return l_paths[0]

    def get_possible_conversion_pathways(self,
                                         in_format: str | int | UUID | FormatInfo,
                                         out_format: str | int | UUID | FormatInfo,
                                         only: (Literal["all"] | Literal["supported"] |
                                                Literal["registered"]) = "registered",
                                         include: Literal["best"] | Literal["shortest"] = "best"
                                         ) -> list[ConversionPath]:
        """As `get_conversion_pathway`, but instead of returning just one pathway, returns a list of pathways meeting
        the `include` criterion:

        "best": Include all pathways that are assessed as equally best based on the pathfinding weight criteria (which
            takes into account data and precision loss in each step of the conversion chain)

        "shortest": Include all pathways with an equally low number of steps
        """

        in_format_info = self.parent.get_format_info(in_format)
        out_format_info = self.parent.get_format_info(out_format)

        l_paths = self._get_l_paths(in_format_info, out_format_info, only=only, include=include)

        # Check if any paths are possible
        if not l_paths or not l_paths[0]:
            return []

        # Sort the list by weight, then by name
        l_paths.sort(key=lambda x: (x.get_weight(), x.get_name()))

        return l_paths

    def get_possible_formats(self,
                             converter: str | int | UUID | ConverterInfo,
                             **kwargs) -> tuple[list[FormatInfo], list[FormatInfo]]:
        """Get a list of input and output formats that a given converter supports

        Parameters
        ----------
        converter : str | int | UUID | ConverterInfo
            The name, ID, or info of the converter

        Returns
        -------
        tuple[list[FormatInfo], list[FormatInfo]]
            A tuple of a list of the supported input formats and a list of the supported output formats
        """

        # Check for deprecated kwargs
        conv_id: int
        if "converter_name" in kwargs:
            warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                          f"{tc.CODE}`get_possible_formats`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                          f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                          "accepts the converter name, ID, or info", DeprecationWarning)
            conv_id = self.parent.get_converter_info(kwargs["converter_name"]).id
        else:
            conv_id = self.parent.get_converter_info(converter).id

        l_conversion_edges = self.graph.es.select(**{DB_CONV_ID_KEY: conv_id})
        l_possible_in_format_ids = list({self.d_uuids_from_indices[x.source] for x in l_conversion_edges})
        l_possible_out_format_ids = list({self.d_uuids_from_indices[x.target] for x in l_conversion_edges})

        # Get the name for each format ID, and return lists of the names
        return ([self.parent.get_format_info(x) for x in l_possible_in_format_ids],
                [self.parent.get_format_info(x) for x in l_possible_out_format_ids])

Class providing information on available file format conversions

Set up the class - this will be initialised within a DataConversionDatabase, which we set as the parent

Parameters

l_converts_to : list[dict[str, bool | int | str | None]]
The list of dicts in the database providing information on possible conversions
d_format_id_aliases : Iterable[int]
A dict of primary format IDs listing the aliases for each
parent : DataConversionDatabase
The database which this belongs to

Raises

FileConverterDatabaseException
 

Methods

def get_conversion_pathway(self,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
only: "Literal['all'] | Literal['supported'] | Literal['registered']" = 'registered') ‑> ConversionPath | None
Expand source code
def get_conversion_pathway(self,
                           in_format: str | int | UUID | FormatInfo,
                           out_format: str | int | UUID | FormatInfo,
                           only: Literal["all"] | Literal["supported"] | Literal["registered"] = "registered"
                           ) -> ConversionPath | None:
    """Gets a pathway to convert from one format to another
    """

    in_format_info = self.parent.get_format_info(in_format)
    out_format_info = self.parent.get_format_info(out_format)

    l_paths = self._get_l_paths(in_format_info, out_format_info, only=only)

    # Check if any paths are possible
    if not l_paths or not l_paths[0]:
        return None

    # Any paths returned here are equally valid. For stability, pick the first alphabetically by name
    l_paths.sort(key=lambda x: x.get_name())
    return l_paths[0]

Gets a pathway to convert from one format to another

def get_conversion_quality(self,
converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
**kwargs) ‑> ConversionQualityInfo | None
Expand source code
@lru_cache(maxsize=None)
def get_conversion_quality(self,
                           converter: str | int | UUID | ConverterInfo,
                           in_format: str | int | UUID | FormatInfo,
                           out_format: str | int | UUID | FormatInfo,
                           **kwargs) -> ConversionQualityInfo | None:
    """Get an indication of the quality of a conversion from one format to another, or if it's not possible

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The converter, specified by its name, ID, or info
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the output file format

    Returns
    -------
    ConversionQualityInfo | None
        If the conversion is not possible, returns None. If the conversion is possible, returns a
        `ConversionQualityInfo` object with info on the conversion
    """

    # Check for deprecated kwargs
    if "converter_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                      f"{tc.CODE}`get_conversion_quality`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "accepts the converter name, ID, or info", DeprecationWarning)
        converter_info = get_converter_info(kwargs["converter_name"])
    else:
        converter_info = get_converter_info(converter)

    # Get all possible format infos for each format
    l_in_format_info = self.parent.get_format_info(in_format, "all")
    l_out_format_info = self.parent.get_format_info(out_format, "all")

    # First check if the conversion is possible for at least one combination
    l_found_combinations: list[tuple[FormatInfo, FormatInfo]] = []
    for in_format_info, out_format_info in product(l_in_format_info, l_out_format_info):
        if converter_info.name in self._get_possible_converters(in_format_info, out_format_info):
            l_found_combinations.append((in_format_info, out_format_info))
    if len(l_found_combinations) == 0:
        return None

    # Check if the conversion is ambiguous
    if len(l_found_combinations) > 1:

        converter_name = converter.format_word() if isinstance(
            converter, ConverterInfo) else f"{tc.PATH}'{converter}'{tc.OFF}"
        in_format_name = in_format.format_word() if isinstance(
            in_format, FormatInfo) else f"{tc.PATH}'{in_format}'{tc.OFF}"
        out_format_name = out_format.format_word() if isinstance(
            out_format, FormatInfo) else f"{tc.PATH}'{out_format}'{tc.OFF}"

        msg = (f"Conversion from {in_format_name} to {out_format_name} with converter "
               f"{converter_name} is ambiguous. Please use the ID or disambiguated name (listed below) "
               "of the desired conversion. Possible matching conversions are:\n")
        for possible_in_format, possible_out_format in l_found_combinations:
            msg += (f"    {possible_in_format.format_inline()} to {possible_out_format.format_inline()}\n")
        # Trim the final newline from the message
        msg = msg[:-1]
        raise FileConverterDatabaseException(msg, help=True)

    in_format_info, out_format_info = l_found_combinations[0]

    # The conversion is possible. Now determine how many properties of the output format are not in the input
    # format and might end up being extrapolated
    num_out_props = 0
    num_new_props = 0
    any_unknown = False
    d_prop_conversion_info: dict[str, PropertyConversionInfo] = {}
    for prop in D_FORMAT_PROPERTY_ATTRS:
        in_prop: bool | None = getattr(in_format_info, prop)
        out_prop: bool | None = getattr(out_format_info, prop)

        d_prop_conversion_info[prop] = PropertyConversionInfo(prop, in_prop, out_prop)

        # Check for None, indicating we don't have full information on both formats
        if in_prop is None or out_prop is None:
            any_unknown = True
        elif out_prop:
            num_out_props += 1
            if not in_prop:
                num_new_props += 1

    # Determine the conversion quality
    if num_out_props > 0:
        qual_ratio = 1 - num_new_props/num_out_props
    else:
        qual_ratio = 1

    if any_unknown:
        qual_str = const.QUAL_UNKNOWN
    elif num_out_props == 0 or qual_ratio >= 0.8:
        qual_str = const.QUAL_VERYGOOD
    elif qual_ratio >= 0.6:
        qual_str = const.QUAL_GOOD
    elif qual_ratio >= 0.4:
        qual_str = const.QUAL_OKAY
    elif qual_ratio >= 0.2:
        qual_str = const.QUAL_POOR
    else:
        qual_str = const.QUAL_VERYPOOR

    # Construct the details string for info on possible issues with the conversion

    # Sort the keys by label alphabetically
    l_props: list[str] = list(d_prop_conversion_info.keys())
    l_props.sort(key=lambda x: d_prop_conversion_info[x].label)

    details = "\n".join(
        [d_prop_conversion_info[x].description for x in l_props if d_prop_conversion_info[x].description])

    return ConversionQualityInfo.factory(converter=converter_info,
                                         in_format=in_format_info,
                                         out_format=out_format_info,
                                         qual_str=qual_str,
                                         details=details,
                                         d_prop_conversion_info=d_prop_conversion_info)

Get an indication of the quality of a conversion from one format to another, or if it's not possible

Parameters

converter : str | int | UUID | ConverterInfo
The converter, specified by its name, ID, or info
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the output file format

Returns

ConversionQualityInfo | None
If the conversion is not possible, returns None. If the conversion is possible, returns a ConversionQualityInfo object with info on the conversion
def get_conversion_weight(self,
converter: str | int | UUID | ConverterInfo,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
bits: "Literal['all'] | Literal['top'] | Literal['bottom']" = 'all') ‑> int
Expand source code
def get_conversion_weight(self,
                          converter: str | int | UUID | ConverterInfo,
                          in_format: str | int | UUID | FormatInfo,
                          out_format: str | int | UUID | FormatInfo,
                          bits: Literal["all"] | Literal["top"] | Literal["bottom"] = "all") -> int:
    """Get the weight for a desired conversion.

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The name, ID, or info of the converter used for this conversion
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format
    bits : Literal["all"] | Literal["top"] | Literal["bottom"]
        (Used when 64-bit ints are too large for some purposes) Whether to get the full weight ("all"), just the top
        half of the bits ("top"), or just the lower half of the bits ("bottom")

    Returns
    -------
    int
        The 64-bit combined weight of this conversion (unless bits=="top" or bits=="bottom", in which case the top
        or bottom 32 bits of the weight will be returned)

    Raises
    ------
    FileConverterDatabaseException
        If the requested conversion is not possible
    """
    converter_info = self.parent.get_converter_info(converter)
    in_format_info = self.parent.get_format_info(in_format)
    out_format_info = self.parent.get_format_info(out_format)

    l_edges = self.graph.es.select(_source=self.d_indices_from_uuids[in_format_info.id],
                                   _target=self.d_indices_from_uuids[out_format_info.id],
                                   **{DB_CONV_ID_KEY: converter_info.id})

    if len(l_edges) == 0:
        raise FileConverterDatabaseException(f"Conversion from {in_format_info.name} to {out_format_info.name} "
                                             f"with converter {converter_info.pretty_name} is not supported",
                                             help=True)

    if bits == "top":
        return l_edges[0][CONV_WEIGHT_TOP_KEY]
    elif bits == "bottom":
        return l_edges[0][CONV_WEIGHT_BOTTOM_KEY]
    else:
        return l_edges[0][CONV_WEIGHT_KEY]

Get the weight for a desired conversion.

Parameters

converter : str | int | UUID | ConverterInfo
The name, ID, or info of the converter used for this conversion
in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format
bits : Literal["all"] | Literal["top"] | Literal["bottom"]
(Used when 64-bit ints are too large for some purposes) Whether to get the full weight ("all"), just the top half of the bits ("top"), or just the lower half of the bits ("bottom")

Returns

int
The 64-bit combined weight of this conversion (unless bits=="top" or bits=="bottom", in which case the top or bottom 32 bits of the weight will be returned)

Raises

FileConverterDatabaseException
If the requested conversion is not possible
def get_possible_conversion_pathways(self,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
only: "Literal['all'] | Literal['supported'] | Literal['registered']" = 'registered',
include: "Literal['best'] | Literal['shortest']" = 'best') ‑> list[ConversionPath]
Expand source code
def get_possible_conversion_pathways(self,
                                     in_format: str | int | UUID | FormatInfo,
                                     out_format: str | int | UUID | FormatInfo,
                                     only: (Literal["all"] | Literal["supported"] |
                                            Literal["registered"]) = "registered",
                                     include: Literal["best"] | Literal["shortest"] = "best"
                                     ) -> list[ConversionPath]:
    """As `get_conversion_pathway`, but instead of returning just one pathway, returns a list of pathways meeting
    the `include` criterion:

    "best": Include all pathways that are assessed as equally best based on the pathfinding weight criteria (which
        takes into account data and precision loss in each step of the conversion chain)

    "shortest": Include all pathways with an equally low number of steps
    """

    in_format_info = self.parent.get_format_info(in_format)
    out_format_info = self.parent.get_format_info(out_format)

    l_paths = self._get_l_paths(in_format_info, out_format_info, only=only, include=include)

    # Check if any paths are possible
    if not l_paths or not l_paths[0]:
        return []

    # Sort the list by weight, then by name
    l_paths.sort(key=lambda x: (x.get_weight(), x.get_name()))

    return l_paths

As get_conversion_pathway(), but instead of returning just one pathway, returns a list of pathways meeting the include criterion:

"best": Include all pathways that are assessed as equally best based on the pathfinding weight criteria (which takes into account data and precision loss in each step of the conversion chain)

"shortest": Include all pathways with an equally low number of steps

def get_possible_conversions(self,
in_format: str | int | UUID | FormatInfo,
out_format: str | int | UUID | FormatInfo,
only: "Literal['all'] | Literal['supported'] | Literal['registered']" = 'all') ‑> list[Conversion]
Expand source code
def get_possible_conversions(self,
                             in_format: str | int | UUID | FormatInfo,
                             out_format: str | int | UUID | FormatInfo,
                             only: Literal["all"] | Literal["supported"] | Literal["registered"] = "all"
                             ) -> list[Conversion]:
    """Get a list of converters which can perform a conversion from one format to another, disambiguating in the
    case of ambiguous formats and providing IDs for input/output formats for possible conversions

    Parameters
    ----------
    in_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the input file format
    out_format : str | int | UUID | FormatInfo
        The extension, ID, or info of the converter of the output file format
    only : Literal["all"] | Literal["supported"] | Literal["registered"], optional
        Which converters to limit the search to:
        - "all": All known converters
        - "supported": Only converters supported by this utility, even if not currently available (e.g. they don't
        work on your OS)
        - "registered" (default): Only converters supported by this utility and currently available

    Returns
    -------
    list[tuple[ConverterInfo, FormatInfo, FormatInfo]]
        A list of tuples, where each tuple's first item is the ConverterInfo of a converter which can perform a
        matching conversion, the second is the info of the input format for this conversion, and the third is the
        info of the output format
    """
    l_in_format_infos = self.parent.get_format_info(in_format, which="all")
    l_out_format_infos = self.parent.get_format_info(out_format, which="all")

    # Start a list of all possible conversions
    l_possible_conversions = []

    # Iterate over all possible combinations of input and output formats
    for in_format_info, out_format_info in product(l_in_format_infos, l_out_format_infos):

        # Filter for converters which can perform this conversion
        l_converter_names = self._get_possible_converters(in_format_info, out_format_info, only=only)

        for converter_name in l_converter_names:
            l_possible_conversions.append(Conversion(self.parent.get_converter_info(converter_name),
                                                     in_format_info, out_format_info))

    return l_possible_conversions

Get a list of converters which can perform a conversion from one format to another, disambiguating in the case of ambiguous formats and providing IDs for input/output formats for possible conversions

Parameters

in_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the input file format
out_format : str | int | UUID | FormatInfo
The extension, ID, or info of the converter of the output file format
only : Literal["all"] | Literal["supported"] | Literal["registered"], optional
Which converters to limit the search to: - "all": All known converters - "supported": Only converters supported by this utility, even if not currently available (e.g. they don't work on your OS) - "registered" (default): Only converters supported by this utility and currently available

Returns

list[tuple[ConverterInfo, FormatInfo, FormatInfo]]
A list of tuples, where each tuple's first item is the ConverterInfo of a converter which can perform a matching conversion, the second is the info of the input format for this conversion, and the third is the info of the output format
def get_possible_formats(self,
converter: str | int | UUID | ConverterInfo,
**kwargs) ‑> tuple[list[FormatInfo], list[FormatInfo]]
Expand source code
def get_possible_formats(self,
                         converter: str | int | UUID | ConverterInfo,
                         **kwargs) -> tuple[list[FormatInfo], list[FormatInfo]]:
    """Get a list of input and output formats that a given converter supports

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo
        The name, ID, or info of the converter

    Returns
    -------
    tuple[list[FormatInfo], list[FormatInfo]]
        A tuple of a list of the supported input formats and a list of the supported output formats
    """

    # Check for deprecated kwargs
    conv_id: int
    if "converter_name" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name`{tc.OFF} for the method "
                      f"{tc.CODE}`get_possible_formats`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "accepts the converter name, ID, or info", DeprecationWarning)
        conv_id = self.parent.get_converter_info(kwargs["converter_name"]).id
    else:
        conv_id = self.parent.get_converter_info(converter).id

    l_conversion_edges = self.graph.es.select(**{DB_CONV_ID_KEY: conv_id})
    l_possible_in_format_ids = list({self.d_uuids_from_indices[x.source] for x in l_conversion_edges})
    l_possible_out_format_ids = list({self.d_uuids_from_indices[x.target] for x in l_conversion_edges})

    # Get the name for each format ID, and return lists of the names
    return ([self.parent.get_format_info(x) for x in l_possible_in_format_ids],
            [self.parent.get_format_info(x) for x in l_possible_out_format_ids])

Get a list of input and output formats that a given converter supports

Parameters

converter : str | int | UUID | ConverterInfo
The name, ID, or info of the converter

Returns

tuple[list[FormatInfo], list[FormatInfo]]
A tuple of a list of the supported input formats and a list of the supported output formats
class ConverterInfo (id: int = <property object>,
name: str = <property object>,
description: str = <property object>,
info: str = <property object>,
parent: DataConversionDatabase | DBInfo | None = None,
url: str = '',
weight: int = 64,
supported: bool = False,
registered: bool = False,
converter_class: type[FileConverter] = psdi_data_conversion.converters.base.FileConverter,
pretty_name: str = '')
Expand source code
@dataclass
class ConverterInfo(DBInfo):
    """Class providing information on a converter stored in the PSDI Data Conversion database
    """

    url: str = ""
    weight: int = CONV_WEIGHT_DEFAULT
    supported: bool = False
    registered: bool = False

    converter_class: type[FileConverter] = FileConverter
    """The class used to perform conversions with this converter"""

    pretty_name: str = ""
    """The name of the converter, properly spaced and capitalized"""

    _key_prefix: str = ""
    _arg_info: dict[str, list[dict[str, int | str]]] = field(default_factory=dict)

    # Placeholders for members that are generated when needed
    _d_in_flag_info: dict[int, FlagInfo] | None = field(init=False, repr=False, default=None)
    _l_unsorted_in_flag_info: list[FlagInfo] | None = field(init=False, repr=False, default=None)
    _d_out_flag_info: dict[int, FlagInfo] | None = field(init=False, repr=False, default=None)
    _l_unsorted_out_flag_info: list[FlagInfo] | None = field(init=False, repr=False, default=None)
    _d_in_option_info: dict[int, OptionInfo] | None = field(init=False, repr=False, default=None)
    _l_unsorted_in_option_info: list[FlagInfo] | None = field(init=False, repr=False, default=None)
    _d_out_option_info: dict[int, OptionInfo] | None = field(init=False, repr=False, default=None)
    _l_unsorted_out_option_info: list[FlagInfo] | None = field(init=False, repr=False, default=None)

    # __hash__ needs to be inherited explicitly for dataclasses since they redefine __eq__
    __hash__ = DBInfo.__hash__

    def format_word(self):
        """Use the pretty name for the formatted name"""
        return f"{tc.BOLD}{self.pretty_name}{tc.OFF}"

    def format_detailed(self):
        """Include extra information in the detailed description"""
        msg = super().format_detailed()
        if self.url:
            msg += f"\nURL: {tc.LINK}{self.url}{tc.OFF}"
        return msg

    @staticmethod
    def from_db(parent: DataConversionDatabase,
                d_single_converter_info: dict[str, int | str],
                d_data: dict[str, Any]):
        """Factory function to set up the class

        Parameters
        ----------
        parent : DataConversionDatabase
            The database which this belongs to
        d_single_converter_info : dict[str, int | str]
            The dict within the database file which describes this converter
        d_data : dict[str, Any]
            The loaded database dict
        """

        # Get info about the converter from the database

        name = regularize_name(d_single_converter_info[DB_NAME_KEY])
        weight: int | None = d_single_converter_info.get(DB_WEIGHT_KEY)

        try:
            converter_class = get_registered_converter_class(name)
            pretty_name = converter_class.meta.name
        except KeyError:
            converter_class = FileConverter
            pretty_name = d_single_converter_info[DB_NAME_KEY]

        # Use the default weight if the key is absent or the value is None
        if not weight:
            weight = ConverterInfo.weight

        try:
            _key_prefix = get_registered_converter_class(name).meta.database_key_prefix
        except KeyError:
            # We'll get a KeyError for converters in the database that don't yet have their own class, which we can
            # safely ignore
            _key_prefix = None

        # If the converter class has no defined key prefix, don't add any extra info for it
        _arg_info: dict[str, list[dict[str, int | str]]] = {}
        if _key_prefix is not None:
            for key_base in (DB_IN_FLAGS_KEY_BASE,
                             DB_OUT_FLAGS_KEY_BASE,
                             DB_IN_OPTIONS_KEY_BASE,
                             DB_OUT_OPTIONS_KEY_BASE,
                             DB_IN_FLAGS_FORMATS_KEY_BASE,
                             DB_OUT_FLAGS_FORMATS_KEY_BASE,
                             DB_IN_OPTIONS_FORMATS_KEY_BASE,
                             DB_OUT_OPTIONS_FORMATS_KEY_BASE):
                _arg_info[key_base] = d_data.get(_key_prefix + key_base, [])

        return ConverterInfo(id=d_single_converter_info.get(DB_ID_KEY, DEFAULT_ID),
                             name=regularize_name(name),
                             description=d_single_converter_info.get(DB_DESCRIPTION_KEY, ""),
                             info=d_single_converter_info.get(DB_INFO_KEY, ""),
                             parent=parent,
                             url=d_single_converter_info.get(DB_URL_KEY, ""),
                             weight=weight,
                             supported=name in L_SUPPORTED_CONVERTERS,
                             registered=name in L_REGISTERED_CONVERTERS,
                             converter_class=converter_class,
                             pretty_name=pretty_name,
                             _key_prefix=_key_prefix,
                             _arg_info=_arg_info)

    @property
    def uuid(self):
        """Returns the ID as a UUID object
        """
        return UUID(int=self.id)

    def _create_d_arg_info(self, subclass: type[ArgInfo]):
        """Creates either the flag or option info dicts when needed
        """

        # Set values based on whether we're working with flags or options
        if issubclass(subclass, FlagInfo):
            in_key_base = DB_IN_FLAGS_KEY_BASE
            out_key_base = DB_OUT_FLAGS_KEY_BASE
            in_formats_key_base = DB_IN_FLAGS_FORMATS_KEY_BASE
            in_args_id_key_base = DB_IN_FLAGS_ID_KEY_BASE
            out_formats_key_base = DB_OUT_FLAGS_FORMATS_KEY_BASE
            out_args_id_key_base = DB_OUT_FLAGS_ID_KEY_BASE
        elif issubclass(subclass, OptionInfo):
            in_key_base = DB_IN_OPTIONS_KEY_BASE
            out_key_base = DB_OUT_OPTIONS_KEY_BASE
            in_formats_key_base = DB_IN_OPTIONS_FORMATS_KEY_BASE
            in_args_id_key_base = DB_IN_OPTIONS_ID_KEY_BASE
            out_formats_key_base = DB_OUT_OPTIONS_FORMATS_KEY_BASE
            out_args_id_key_base = DB_OUT_OPTIONS_ID_KEY_BASE
        else:
            raise FileConverterDatabaseException(f"Unrecognised subclass passed to {tc.CODE}`_create_d_arg_info"
                                                 f"`{tc.OFF}: {tc.CODE}{subclass}{tc.OFF}")

        for key_base, in_or_out in ((in_key_base, "in"),
                                    (out_key_base, "out")):

            d_arg_info: dict[int, ArgInfo] = {}
            for d_single_arg_info in self._arg_info[key_base]:
                name: str = d_single_arg_info[DB_FLAG_KEY]
                arg_id: int = d_single_arg_info[DB_ID_KEY]
                brief = d_single_arg_info.get(DB_BRIEF_KEY)
                optional_arg_info_kwargs = {}
                if brief is not None:
                    optional_arg_info_kwargs["brief"] = brief
                arg_info = subclass(
                    id=arg_id,
                    name=name,
                    description=d_single_arg_info[DB_DESCRIPTION_KEY],
                    info=d_single_arg_info[DB_FURTHER_INFO_KEY],
                    parent=self,
                    **optional_arg_info_kwargs)
                d_arg_info[arg_id] = arg_info

                # Get a list of all in and formats applicable to this flag, and add them to the flag info's sets
                if in_or_out == "in":
                    l_in_formats = [x[DB_FORMAT_ID_KEY]
                                    for x in self._arg_info[in_formats_key_base]
                                    if x[self._key_prefix + in_args_id_key_base] == arg_id]
                    arg_info.s_in_formats.update(l_in_formats)
                else:
                    l_out_formats = [x[DB_FORMAT_ID_KEY]
                                     for x in self._arg_info[out_formats_key_base]
                                     if x[self._key_prefix + out_args_id_key_base] == arg_id]
                    arg_info.s_out_formats.update(l_out_formats)

            if in_or_out == "in" and issubclass(subclass, FlagInfo):
                self._d_in_flag_info = d_arg_info
                self._l_unsorted_in_flag_info = list(d_arg_info.values())
            elif in_or_out == "out" and issubclass(subclass, FlagInfo):
                self._d_out_flag_info = d_arg_info
                self._l_unsorted_out_flag_info = list(d_arg_info.values())
            elif in_or_out == "in" and issubclass(subclass, OptionInfo):
                self._d_in_option_info = d_arg_info
                self._l_unsorted_in_option_info = list(d_arg_info.values())
            elif in_or_out == "out" and issubclass(subclass, OptionInfo):
                self._d_out_option_info = d_arg_info
                self._l_unsorted_out_option_info = list(d_arg_info.values())
            else:
                raise FileConverterDatabaseException(f"Unrecognised subclass passed to {tc.CODE}`_create_d_arg_info"
                                                     f"`{tc.OFF}: {tc.CODE}{subclass}{tc.OFF}")

        return

    @property
    def d_in_flag_info(self) -> dict[int, FlagInfo] | None:
        """Generate the input flag info dict (indexed by ID) when needed. Returns None if the converter has no flag info
        in the database
        """
        if self._d_in_flag_info is None and self._key_prefix is not None:
            self._create_d_arg_info(FlagInfo)
        return self._d_in_flag_info

    @property
    def l_in_flag_info(self):
        """DEPRECATED: Generate the input flag info list (indexed by ID) when needed. Returns None if the converter has
        no flag info in the database
        """
        deprecation_msg = (f"{tc.CODE}`l_in_flag_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be removed "
                           f"in a future release. To get an input {tc.CODE}FlagInfo{tc.OFF} from the format UUID, use "
                           f"{tc.CODE}`d_in_flag_info`{tc.OFF}. To get an unsorted list of input "
                           f"{tc.CODE}FlagInfo{tc.OFF}, use {tc.CODE}`l_unsorted_in_flag_info`{tc.OFF}.")
        warnings.warn(deprecation_msg, DeprecationWarning)
        raise AttributeError(deprecation_msg)

    @property
    def l_unsorted_in_flag_info(self) -> list[FlagInfo] | None:
        """Generate the unsorted input flag info list when needed. Returns None if the converter has
        no flag info in the database
        """
        if self._l_unsorted_in_flag_info is None and self._key_prefix is not None:
            self._create_d_arg_info(FlagInfo)
        return self._l_unsorted_in_flag_info

    @property
    def d_out_flag_info(self) -> dict[int, FlagInfo] | None:
        """Generate the input flag info dict (indexed by ID) when needed. Returns None if the converter has no flag info
        in the database
        """
        if self._d_out_flag_info is None and self._key_prefix is not None:
            self._create_d_arg_info(FlagInfo)
        return self._d_out_flag_info

    @property
    def l_out_flag_info(self):
        """DEPRECATED: Generate the input flag info list (indexed by ID) when needed. Returns None if the converter has
        no flag info in the database
        """
        deprecation_msg = (f"{tc.CODE}`l_out_flag_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                           f"removed in a future release. To get an input {tc.CODE}FlagInfo{tc.OFF} from the format "
                           f"UUID, use {tc.CODE}`d_out_flag_info`{tc.OFF}. To get an unsorted list of input "
                           f"{tc.CODE}FlagInfo{tc.OFF}, use {tc.CODE}`l_unsorted_out_flag_info`{tc.OFF}.")
        warnings.warn(deprecation_msg, DeprecationWarning)
        raise AttributeError(deprecation_msg)

    @property
    def l_unsorted_out_flag_info(self) -> list[FlagInfo] | None:
        """Generate the unsorted input flag info list when needed. Returns None if the converter has
        no flag info in the database
        """
        if self._l_unsorted_out_flag_info is None and self._key_prefix is not None:
            self._create_d_arg_info(FlagInfo)
        return self._l_unsorted_out_flag_info

    @property
    def d_in_option_info(self) -> dict[int, OptionInfo] | None:
        """Generate the input option info dict (indexed by ID) when needed. Returns None if the converter has no option
        info in the database
        """
        if self._d_in_option_info is None and self._key_prefix is not None:
            self._create_d_arg_info(OptionInfo)
        return self._d_in_option_info

    @property
    def l_in_option_info(self):
        """DEPRECATED: Generate the input option info list (indexed by ID) when needed. Returns None if the converter
        has no option info in the database
        """
        deprecation_msg = (f"{tc.CODE}`l_in_option_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                           f"removed in a future release. To get an input {tc.CODE}OptionInfo{tc.OFF} from the format "
                           f"UUID, use {tc.CODE}`d_in_option_info`{tc.OFF}. To get an unsorted list of input "
                           f"{tc.CODE}OptionInfo{tc.OFF}, use {tc.CODE}`l_unsorted_in_option_info`{tc.OFF}.")
        warnings.warn(deprecation_msg, DeprecationWarning)
        raise AttributeError(deprecation_msg)

    @property
    def l_unsorted_in_option_info(self) -> list[OptionInfo] | None:
        """Generate the unsorted input option info list when needed. Returns None if the converter has
        no option info in the database
        """
        if self._l_unsorted_in_option_info is None and self._key_prefix is not None:
            self._create_d_arg_info(OptionInfo)
        return self._l_unsorted_in_option_info

    @property
    def d_out_option_info(self) -> dict[int, OptionInfo] | None:
        """Generate the input option info dict (indexed by ID) when needed. Returns None if the converter has no option
        info in the database
        """
        if self._d_out_option_info is None and self._key_prefix is not None:
            self._create_d_arg_info(OptionInfo)
        return self._d_out_option_info

    @property
    def l_out_option_info(self):
        """DEPRECATED: Generate the input option info list (indexed by ID) when needed. Returns None if the converter
        has no option info in the database
        """
        deprecation_msg = (f"{tc.CODE}`l_out_option_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                           f"removed in a future release. To get an input {tc.CODE}OptionInfo{tc.OFF} from the format "
                           f"UUID, use {tc.CODE}`d_out_option_info`{tc.OFF}. To get an unsorted list of input "
                           f"{tc.CODE}OptionInfo{tc.OFF}, use {tc.CODE}`l_unsorted_out_option_info`{tc.OFF}.")
        warnings.warn(deprecation_msg, DeprecationWarning)
        raise AttributeError(deprecation_msg)

    @property
    def l_unsorted_out_option_info(self) -> list[OptionInfo] | None:
        """Generate the unsorted input option info list when needed. Returns None if the converter has
        no option info in the database
        """
        if self._l_unsorted_out_option_info is None and self._key_prefix is not None:
            self._create_d_arg_info(OptionInfo)
        return self._l_unsorted_out_option_info

    def _create_d_format_args(self,
                              subclass: type[ArgInfo],
                              in_or_out: str) -> dict[str | int, set[int]]:
        """Creates either the flag or option format args dict
        """

        if in_or_out not in ("in", "out"):
            raise FileConverterDatabaseException(f"Unrecognised {tc.CODE}`in_or_out`{tc.OFF} value passed to "
                                                 f"{tc.CODE}`_create_d_format_args`{tc.OFF}: "
                                                 f"{tc.PATH}'{in_or_out}'{tc.OFF}")

        # Set values based on whether we're working with flags or options, and input or output
        if issubclass(subclass, FlagInfo):
            l_arg_info = self.l_unsorted_in_flag_info if in_or_out == "in" else self.l_unsorted_out_flag_info
        elif issubclass(subclass, OptionInfo):
            l_arg_info = self.l_unsorted_in_option_info if in_or_out == "in" else self.l_unsorted_out_option_info
        else:
            raise FileConverterDatabaseException(
                f"Unrecognised subclass passed to {tc.CODE}`_create_d_format_args`{tc.OFF}: {subclass}")

        d_format_args: dict[str | int, set[ArgInfo]] = {}
        d_parent_format_info_from_id = self.parent.d_format_info_from_id

        # If the converter doesn't provide argument info, set l_arg_info to an empty list so it can be iterated in
        # the next step, rather than None
        if not l_arg_info:
            l_arg_info = []

        for arg_info in l_arg_info:

            if arg_info is None:
                continue

            if in_or_out == "in":
                s_formats = arg_info.s_in_formats
            else:
                s_formats = arg_info.s_out_formats
            l_format_info = [d_parent_format_info_from_id[format_id] for format_id in s_formats]
            for format_info in l_format_info:
                format_name = format_info.name
                format_id = format_info.id

                # Add an empty set for this format to the dict if it isn't yet there, otherwise add to the set
                if format_name not in d_format_args:
                    d_format_args[format_name] = set()
                    # Keying by ID will point to the same set as keying by name
                    d_format_args[format_id] = d_format_args[format_name]

                d_format_args[format_name].add(arg_info.id)

        return d_format_args

    @cached_property
    def d_in_format_flags(self) -> dict[str | int, set[int]]:
        """Generate the dict of flags for an input format (keyed by format name/extension or format ID) when needed.
        The format will not be in the dict if no flags are accepted
        """
        return self._create_d_format_args(FlagInfo, "in")

    @cached_property
    def d_out_format_flags(self) -> dict[str | int, set[int]]:
        """Generate the dict of flags for an output format (keyed by format name/extension or format ID) when needed.
        The format will not be in the dict if no options are accepted
        """
        return self._create_d_format_args(FlagInfo, "out")

    @cached_property
    def d_in_format_options(self) -> dict[str | int, set[int]]:
        """Generate the dict of options for an input format (keyed by format name/extension or format ID) when needed.
        The format will not be in the dict if no options are accepted
        """
        return self._create_d_format_args(OptionInfo, "in")

    @cached_property
    def d_out_format_options(self) -> dict[str | int, set[int]]:
        """Generate the dict of options for an output format (keyed by format name/extension or format ID) when needed.
        The format will not be in the dict if no options are accepted
        """
        return self._create_d_format_args(OptionInfo, "out")

    def get_in_format_args(self, in_format: str | int | UUID | FormatInfo) -> tuple[list[FlagInfo], list[OptionInfo]]:
        """Get the input flags and options supported for a given format (provided as its extension)

        Parameters
        ----------
        in_format : str | int | UUID | FormatInfo
            The file format name (extension), ID, or FormatInfo

        Returns
        -------
        tuple[set[FlagInfo], set[OptionInfo]]
            A set of info for the allowed flags, and a set of info for the allowed options
        """

        l_in_format_infos = get_format_info(in_format, which="all")
        s_flag_ids = set()
        s_option_ids = set()

        for in_format_info in l_in_format_infos:
            in_format_id = in_format_info.id

            s_flag_ids.update(self.d_in_format_flags.get(in_format_id, set()))
            s_option_ids.update(self.d_in_format_options.get(in_format_id, set()))

        l_flag_ids = list(s_flag_ids)
        l_flag_ids.sort()
        l_flag_info = [self.d_in_flag_info[x] for x in l_flag_ids]

        l_option_ids = list(s_option_ids)
        l_option_ids.sort()
        l_option_info = [self.d_in_option_info[x] for x in l_option_ids]

        return l_flag_info, l_option_info

    def get_out_format_args(self, out_format: str | int | UUID | FormatInfo) -> tuple[list[FlagInfo], list[OptionInfo]]:
        """Get the output flags and options supported for a given format (provided as its extension)

        Parameters
        ----------
        out_format : str | int | UUID | FormatInfo
            The file format name (extension), ID, or FormatInfo

        Returns
        -------
        tuple[set[FlagInfo], set[OptionInfo]]
            A set of info for the allowed flags, and a set of info for the allowed options
        """

        l_out_format_infos = get_format_info(out_format, which="all")
        s_flag_ids = set()
        s_option_ids = set()

        for out_format_info in l_out_format_infos:
            out_format_id = out_format_info.id

            s_flag_ids.update(self.d_out_format_flags.get(out_format_id, set()))
            s_option_ids.update(self.d_out_format_options.get(out_format_id, set()))

        l_flag_ids = list(s_flag_ids)
        l_flag_ids.sort()
        l_flag_info = [self.d_out_flag_info[x] for x in l_flag_ids]

        l_option_ids = list(s_option_ids)
        l_option_ids.sort()
        l_option_info = [self.d_out_option_info[x] for x in l_option_ids]

        return l_flag_info, l_option_info

Class providing information on a converter stored in the PSDI Data Conversion database

Ancestors

Static methods

def from_db(parent: DataConversionDatabase,
d_single_converter_info: dict[str, int | str],
d_data: dict[str, Any])
Expand source code
@staticmethod
def from_db(parent: DataConversionDatabase,
            d_single_converter_info: dict[str, int | str],
            d_data: dict[str, Any]):
    """Factory function to set up the class

    Parameters
    ----------
    parent : DataConversionDatabase
        The database which this belongs to
    d_single_converter_info : dict[str, int | str]
        The dict within the database file which describes this converter
    d_data : dict[str, Any]
        The loaded database dict
    """

    # Get info about the converter from the database

    name = regularize_name(d_single_converter_info[DB_NAME_KEY])
    weight: int | None = d_single_converter_info.get(DB_WEIGHT_KEY)

    try:
        converter_class = get_registered_converter_class(name)
        pretty_name = converter_class.meta.name
    except KeyError:
        converter_class = FileConverter
        pretty_name = d_single_converter_info[DB_NAME_KEY]

    # Use the default weight if the key is absent or the value is None
    if not weight:
        weight = ConverterInfo.weight

    try:
        _key_prefix = get_registered_converter_class(name).meta.database_key_prefix
    except KeyError:
        # We'll get a KeyError for converters in the database that don't yet have their own class, which we can
        # safely ignore
        _key_prefix = None

    # If the converter class has no defined key prefix, don't add any extra info for it
    _arg_info: dict[str, list[dict[str, int | str]]] = {}
    if _key_prefix is not None:
        for key_base in (DB_IN_FLAGS_KEY_BASE,
                         DB_OUT_FLAGS_KEY_BASE,
                         DB_IN_OPTIONS_KEY_BASE,
                         DB_OUT_OPTIONS_KEY_BASE,
                         DB_IN_FLAGS_FORMATS_KEY_BASE,
                         DB_OUT_FLAGS_FORMATS_KEY_BASE,
                         DB_IN_OPTIONS_FORMATS_KEY_BASE,
                         DB_OUT_OPTIONS_FORMATS_KEY_BASE):
            _arg_info[key_base] = d_data.get(_key_prefix + key_base, [])

    return ConverterInfo(id=d_single_converter_info.get(DB_ID_KEY, DEFAULT_ID),
                         name=regularize_name(name),
                         description=d_single_converter_info.get(DB_DESCRIPTION_KEY, ""),
                         info=d_single_converter_info.get(DB_INFO_KEY, ""),
                         parent=parent,
                         url=d_single_converter_info.get(DB_URL_KEY, ""),
                         weight=weight,
                         supported=name in L_SUPPORTED_CONVERTERS,
                         registered=name in L_REGISTERED_CONVERTERS,
                         converter_class=converter_class,
                         pretty_name=pretty_name,
                         _key_prefix=_key_prefix,
                         _arg_info=_arg_info)

Factory function to set up the class

Parameters

parent : DataConversionDatabase
The database which this belongs to
d_single_converter_info : dict[str, int | str]
The dict within the database file which describes this converter
d_data : dict[str, Any]
The loaded database dict

Instance variables

var converter_class : type[FileConverter]

The class used to perform conversions with this converter

prop d_in_flag_info : dict[int, FlagInfo] | None
Expand source code
@property
def d_in_flag_info(self) -> dict[int, FlagInfo] | None:
    """Generate the input flag info dict (indexed by ID) when needed. Returns None if the converter has no flag info
    in the database
    """
    if self._d_in_flag_info is None and self._key_prefix is not None:
        self._create_d_arg_info(FlagInfo)
    return self._d_in_flag_info

Generate the input flag info dict (indexed by ID) when needed. Returns None if the converter has no flag info in the database

var d_in_format_flags : dict[str | int, set[int]]
Expand source code
@cached_property
def d_in_format_flags(self) -> dict[str | int, set[int]]:
    """Generate the dict of flags for an input format (keyed by format name/extension or format ID) when needed.
    The format will not be in the dict if no flags are accepted
    """
    return self._create_d_format_args(FlagInfo, "in")

Generate the dict of flags for an input format (keyed by format name/extension or format ID) when needed. The format will not be in the dict if no flags are accepted

var d_in_format_options : dict[str | int, set[int]]
Expand source code
@cached_property
def d_in_format_options(self) -> dict[str | int, set[int]]:
    """Generate the dict of options for an input format (keyed by format name/extension or format ID) when needed.
    The format will not be in the dict if no options are accepted
    """
    return self._create_d_format_args(OptionInfo, "in")

Generate the dict of options for an input format (keyed by format name/extension or format ID) when needed. The format will not be in the dict if no options are accepted

prop d_in_option_info : dict[int, OptionInfo] | None
Expand source code
@property
def d_in_option_info(self) -> dict[int, OptionInfo] | None:
    """Generate the input option info dict (indexed by ID) when needed. Returns None if the converter has no option
    info in the database
    """
    if self._d_in_option_info is None and self._key_prefix is not None:
        self._create_d_arg_info(OptionInfo)
    return self._d_in_option_info

Generate the input option info dict (indexed by ID) when needed. Returns None if the converter has no option info in the database

prop d_out_flag_info : dict[int, FlagInfo] | None
Expand source code
@property
def d_out_flag_info(self) -> dict[int, FlagInfo] | None:
    """Generate the input flag info dict (indexed by ID) when needed. Returns None if the converter has no flag info
    in the database
    """
    if self._d_out_flag_info is None and self._key_prefix is not None:
        self._create_d_arg_info(FlagInfo)
    return self._d_out_flag_info

Generate the input flag info dict (indexed by ID) when needed. Returns None if the converter has no flag info in the database

var d_out_format_flags : dict[str | int, set[int]]
Expand source code
@cached_property
def d_out_format_flags(self) -> dict[str | int, set[int]]:
    """Generate the dict of flags for an output format (keyed by format name/extension or format ID) when needed.
    The format will not be in the dict if no options are accepted
    """
    return self._create_d_format_args(FlagInfo, "out")

Generate the dict of flags for an output format (keyed by format name/extension or format ID) when needed. The format will not be in the dict if no options are accepted

var d_out_format_options : dict[str | int, set[int]]
Expand source code
@cached_property
def d_out_format_options(self) -> dict[str | int, set[int]]:
    """Generate the dict of options for an output format (keyed by format name/extension or format ID) when needed.
    The format will not be in the dict if no options are accepted
    """
    return self._create_d_format_args(OptionInfo, "out")

Generate the dict of options for an output format (keyed by format name/extension or format ID) when needed. The format will not be in the dict if no options are accepted

prop d_out_option_info : dict[int, OptionInfo] | None
Expand source code
@property
def d_out_option_info(self) -> dict[int, OptionInfo] | None:
    """Generate the input option info dict (indexed by ID) when needed. Returns None if the converter has no option
    info in the database
    """
    if self._d_out_option_info is None and self._key_prefix is not None:
        self._create_d_arg_info(OptionInfo)
    return self._d_out_option_info

Generate the input option info dict (indexed by ID) when needed. Returns None if the converter has no option info in the database

prop l_in_flag_info
Expand source code
@property
def l_in_flag_info(self):
    """DEPRECATED: Generate the input flag info list (indexed by ID) when needed. Returns None if the converter has
    no flag info in the database
    """
    deprecation_msg = (f"{tc.CODE}`l_in_flag_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be removed "
                       f"in a future release. To get an input {tc.CODE}FlagInfo{tc.OFF} from the format UUID, use "
                       f"{tc.CODE}`d_in_flag_info`{tc.OFF}. To get an unsorted list of input "
                       f"{tc.CODE}FlagInfo{tc.OFF}, use {tc.CODE}`l_unsorted_in_flag_info`{tc.OFF}.")
    warnings.warn(deprecation_msg, DeprecationWarning)
    raise AttributeError(deprecation_msg)

DEPRECATED: Generate the input flag info list (indexed by ID) when needed. Returns None if the converter has no flag info in the database

prop l_in_option_info
Expand source code
@property
def l_in_option_info(self):
    """DEPRECATED: Generate the input option info list (indexed by ID) when needed. Returns None if the converter
    has no option info in the database
    """
    deprecation_msg = (f"{tc.CODE}`l_in_option_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                       f"removed in a future release. To get an input {tc.CODE}OptionInfo{tc.OFF} from the format "
                       f"UUID, use {tc.CODE}`d_in_option_info`{tc.OFF}. To get an unsorted list of input "
                       f"{tc.CODE}OptionInfo{tc.OFF}, use {tc.CODE}`l_unsorted_in_option_info`{tc.OFF}.")
    warnings.warn(deprecation_msg, DeprecationWarning)
    raise AttributeError(deprecation_msg)

DEPRECATED: Generate the input option info list (indexed by ID) when needed. Returns None if the converter has no option info in the database

prop l_out_flag_info
Expand source code
@property
def l_out_flag_info(self):
    """DEPRECATED: Generate the input flag info list (indexed by ID) when needed. Returns None if the converter has
    no flag info in the database
    """
    deprecation_msg = (f"{tc.CODE}`l_out_flag_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                       f"removed in a future release. To get an input {tc.CODE}FlagInfo{tc.OFF} from the format "
                       f"UUID, use {tc.CODE}`d_out_flag_info`{tc.OFF}. To get an unsorted list of input "
                       f"{tc.CODE}FlagInfo{tc.OFF}, use {tc.CODE}`l_unsorted_out_flag_info`{tc.OFF}.")
    warnings.warn(deprecation_msg, DeprecationWarning)
    raise AttributeError(deprecation_msg)

DEPRECATED: Generate the input flag info list (indexed by ID) when needed. Returns None if the converter has no flag info in the database

prop l_out_option_info
Expand source code
@property
def l_out_option_info(self):
    """DEPRECATED: Generate the input option info list (indexed by ID) when needed. Returns None if the converter
    has no option info in the database
    """
    deprecation_msg = (f"{tc.CODE}`l_out_option_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                       f"removed in a future release. To get an input {tc.CODE}OptionInfo{tc.OFF} from the format "
                       f"UUID, use {tc.CODE}`d_out_option_info`{tc.OFF}. To get an unsorted list of input "
                       f"{tc.CODE}OptionInfo{tc.OFF}, use {tc.CODE}`l_unsorted_out_option_info`{tc.OFF}.")
    warnings.warn(deprecation_msg, DeprecationWarning)
    raise AttributeError(deprecation_msg)

DEPRECATED: Generate the input option info list (indexed by ID) when needed. Returns None if the converter has no option info in the database

prop l_unsorted_in_flag_info : list[FlagInfo] | None
Expand source code
@property
def l_unsorted_in_flag_info(self) -> list[FlagInfo] | None:
    """Generate the unsorted input flag info list when needed. Returns None if the converter has
    no flag info in the database
    """
    if self._l_unsorted_in_flag_info is None and self._key_prefix is not None:
        self._create_d_arg_info(FlagInfo)
    return self._l_unsorted_in_flag_info

Generate the unsorted input flag info list when needed. Returns None if the converter has no flag info in the database

prop l_unsorted_in_option_info : list[OptionInfo] | None
Expand source code
@property
def l_unsorted_in_option_info(self) -> list[OptionInfo] | None:
    """Generate the unsorted input option info list when needed. Returns None if the converter has
    no option info in the database
    """
    if self._l_unsorted_in_option_info is None and self._key_prefix is not None:
        self._create_d_arg_info(OptionInfo)
    return self._l_unsorted_in_option_info

Generate the unsorted input option info list when needed. Returns None if the converter has no option info in the database

prop l_unsorted_out_flag_info : list[FlagInfo] | None
Expand source code
@property
def l_unsorted_out_flag_info(self) -> list[FlagInfo] | None:
    """Generate the unsorted input flag info list when needed. Returns None if the converter has
    no flag info in the database
    """
    if self._l_unsorted_out_flag_info is None and self._key_prefix is not None:
        self._create_d_arg_info(FlagInfo)
    return self._l_unsorted_out_flag_info

Generate the unsorted input flag info list when needed. Returns None if the converter has no flag info in the database

prop l_unsorted_out_option_info : list[OptionInfo] | None
Expand source code
@property
def l_unsorted_out_option_info(self) -> list[OptionInfo] | None:
    """Generate the unsorted input option info list when needed. Returns None if the converter has
    no option info in the database
    """
    if self._l_unsorted_out_option_info is None and self._key_prefix is not None:
        self._create_d_arg_info(OptionInfo)
    return self._l_unsorted_out_option_info

Generate the unsorted input option info list when needed. Returns None if the converter has no option info in the database

var pretty_name : str

The name of the converter, properly spaced and capitalized

var registered : bool
var supported : bool
var url : str
var weight : int

Methods

def format_detailed(self)
Expand source code
def format_detailed(self):
    """Include extra information in the detailed description"""
    msg = super().format_detailed()
    if self.url:
        msg += f"\nURL: {tc.LINK}{self.url}{tc.OFF}"
    return msg

Include extra information in the detailed description

def format_word(self)
Expand source code
def format_word(self):
    """Use the pretty name for the formatted name"""
    return f"{tc.BOLD}{self.pretty_name}{tc.OFF}"

Use the pretty name for the formatted name

def get_in_format_args(self,
in_format: str | int | UUID | FormatInfo) ‑> tuple[list[FlagInfo], list[OptionInfo]]
Expand source code
def get_in_format_args(self, in_format: str | int | UUID | FormatInfo) -> tuple[list[FlagInfo], list[OptionInfo]]:
    """Get the input flags and options supported for a given format (provided as its extension)

    Parameters
    ----------
    in_format : str | int | UUID | FormatInfo
        The file format name (extension), ID, or FormatInfo

    Returns
    -------
    tuple[set[FlagInfo], set[OptionInfo]]
        A set of info for the allowed flags, and a set of info for the allowed options
    """

    l_in_format_infos = get_format_info(in_format, which="all")
    s_flag_ids = set()
    s_option_ids = set()

    for in_format_info in l_in_format_infos:
        in_format_id = in_format_info.id

        s_flag_ids.update(self.d_in_format_flags.get(in_format_id, set()))
        s_option_ids.update(self.d_in_format_options.get(in_format_id, set()))

    l_flag_ids = list(s_flag_ids)
    l_flag_ids.sort()
    l_flag_info = [self.d_in_flag_info[x] for x in l_flag_ids]

    l_option_ids = list(s_option_ids)
    l_option_ids.sort()
    l_option_info = [self.d_in_option_info[x] for x in l_option_ids]

    return l_flag_info, l_option_info

Get the input flags and options supported for a given format (provided as its extension)

Parameters

in_format : str | int | UUID | FormatInfo
The file format name (extension), ID, or FormatInfo

Returns

tuple[set[FlagInfo], set[OptionInfo]]
A set of info for the allowed flags, and a set of info for the allowed options
def get_out_format_args(self,
out_format: str | int | UUID | FormatInfo) ‑> tuple[list[FlagInfo], list[OptionInfo]]
Expand source code
def get_out_format_args(self, out_format: str | int | UUID | FormatInfo) -> tuple[list[FlagInfo], list[OptionInfo]]:
    """Get the output flags and options supported for a given format (provided as its extension)

    Parameters
    ----------
    out_format : str | int | UUID | FormatInfo
        The file format name (extension), ID, or FormatInfo

    Returns
    -------
    tuple[set[FlagInfo], set[OptionInfo]]
        A set of info for the allowed flags, and a set of info for the allowed options
    """

    l_out_format_infos = get_format_info(out_format, which="all")
    s_flag_ids = set()
    s_option_ids = set()

    for out_format_info in l_out_format_infos:
        out_format_id = out_format_info.id

        s_flag_ids.update(self.d_out_format_flags.get(out_format_id, set()))
        s_option_ids.update(self.d_out_format_options.get(out_format_id, set()))

    l_flag_ids = list(s_flag_ids)
    l_flag_ids.sort()
    l_flag_info = [self.d_out_flag_info[x] for x in l_flag_ids]

    l_option_ids = list(s_option_ids)
    l_option_ids.sort()
    l_option_info = [self.d_out_option_info[x] for x in l_option_ids]

    return l_flag_info, l_option_info

Get the output flags and options supported for a given format (provided as its extension)

Parameters

out_format : str | int | UUID | FormatInfo
The file format name (extension), ID, or FormatInfo

Returns

tuple[set[FlagInfo], set[OptionInfo]]
A set of info for the allowed flags, and a set of info for the allowed options

Inherited members

class DBInfo (id: int = <property object>,
name: str = <property object>,
description: str = <property object>,
info: str = <property object>,
parent: DataConversionDatabase | DBInfo | None = None)
Expand source code
@dataclass
class DBInfo:
    """Base for classes providing information from the database"""

    id: int
    _id: int = field(init=False, repr=False, default=DEFAULT_ID)

    name: str
    _name: str = field(init=False, repr=False, default="")

    description: str
    _description: str = field(init=False, repr=False, default="")

    info: str
    _info: str = field(init=False, repr=False, default="")

    parent: DataConversionDatabase | DBInfo | None = field(repr=False, default=None)

    @property
    def id(self):
        """The integer representation of the object's UUID"""
        return self._id

    @id.setter
    def id(self, val: int | property):
        if type(val) is property:
            val = self._id
        self._id = val

    @property
    def name(self):
        """The short name of the object"""
        return self._name

    @name.setter
    def name(self, val: str | property):
        if type(val) is property:
            val = self._name
        self._name = val

    @property
    def description(self):
        """A brief description of the object, which can fit alongside the name and ID on a single line"""
        return self._description

    @description.setter
    def description(self, val: str | property):
        if type(val) is property:
            val = self._description
        self._description = val

    @property
    def info(self):
        """An extended description of the object, which can cover multiple lines"""
        return self._info

    @info.setter
    def info(self, val: str | property):
        if type(val) is property:
            val = self._info
        self._info = val

    @property
    def uuid(self):
        """Returns the ID as a UUID object
        """
        return UUID(int=self.id)

    @cached_property
    def lower_name(self):
        return self.name.lower()

    def format_word(self):
        """Return a formatted representation of this as a single word"""
        return f"{tc.BOLD}{self}{tc.OFF}"

    def format_inline(self):
        """Return a formatted representation of this that can fit inline"""
        return f"{tc.BOLD}{self.format_word()}{tc.OFF} (ID {tc.ID}{self.id}{tc.OFF})"

    def format_oneline(self):
        """Return a formatted description of this that can fit in a single line"""
        return f"{self.format_inline()}: {self.description}"

    def format_detailed(self):
        """Return a multi-line formatted description of this"""

        msg = self.format_oneline()
        if self.info:
            msg += f"\n{self.info}"
        return msg

    def __str__(self):
        """Use the name as the string representation"""
        return self.name

    def __int__(self):
        """Use the ID as the integer representation"""
        return self.id

    def __hash__(self):
        return hash(int(self))

Base for classes providing information from the database

Subclasses

Instance variables

prop description : str
Expand source code
@property
def description(self):
    """A brief description of the object, which can fit alongside the name and ID on a single line"""
    return self._description

A brief description of the object, which can fit alongside the name and ID on a single line

prop id : int
Expand source code
@property
def id(self):
    """The integer representation of the object's UUID"""
    return self._id

The integer representation of the object's UUID

prop info : str
Expand source code
@property
def info(self):
    """An extended description of the object, which can cover multiple lines"""
    return self._info

An extended description of the object, which can cover multiple lines

var lower_name
Expand source code
@cached_property
def lower_name(self):
    return self.name.lower()
prop name : str
Expand source code
@property
def name(self):
    """The short name of the object"""
    return self._name

The short name of the object

var parent : DataConversionDatabase | DBInfo | None
prop uuid
Expand source code
@property
def uuid(self):
    """Returns the ID as a UUID object
    """
    return UUID(int=self.id)

Returns the ID as a UUID object

Methods

def format_detailed(self)
Expand source code
def format_detailed(self):
    """Return a multi-line formatted description of this"""

    msg = self.format_oneline()
    if self.info:
        msg += f"\n{self.info}"
    return msg

Return a multi-line formatted description of this

def format_inline(self)
Expand source code
def format_inline(self):
    """Return a formatted representation of this that can fit inline"""
    return f"{tc.BOLD}{self.format_word()}{tc.OFF} (ID {tc.ID}{self.id}{tc.OFF})"

Return a formatted representation of this that can fit inline

def format_oneline(self)
Expand source code
def format_oneline(self):
    """Return a formatted description of this that can fit in a single line"""
    return f"{self.format_inline()}: {self.description}"

Return a formatted description of this that can fit in a single line

def format_word(self)
Expand source code
def format_word(self):
    """Return a formatted representation of this as a single word"""
    return f"{tc.BOLD}{self}{tc.OFF}"

Return a formatted representation of this as a single word

class DataConversionDatabase (d_data: dict[str, Any], prune=True)
Expand source code
class DataConversionDatabase:
    """Class providing interface for information contained in the PSDI Data Conversion database
    """

    def __init__(self, d_data: dict[str, Any], prune=True):
        """Initialise the DataConversionDatabase object

        Parameters
        ----------
        d_data : dict[str, Any]
            The dict of the database, as loaded in from the JSON file
        prune : bool
            Whether or not any formats with no supported conversions should be pruned from the database, default True
        """

        # Store the database dict internally for debugging purposes
        self._d_data = d_data

        self._prune = prune

        # Store top-level items not tied to a specific converter
        self.formats: list[dict[str, bool | int | str | None]] = d_data[DB_FORMATS_KEY]
        self.converters: list[dict[str, bool | int | str | None]] = d_data[DB_CONVERTERS_KEY]
        self.converts_to: list[dict[str, bool | int | str | None]] = d_data[DB_CONVERTS_TO_KEY]

        # Placeholders for properties that are generated when needed
        self._d_converter_info_from_name: dict[str, ConverterInfo] | None = None
        self._d_converter_info_from_id: dict[int, ConverterInfo] | None = None
        self._l_unsorted_converter_info: list[ConverterInfo] | None = None
        self._d_format_info_from_id: dict[int, FormatInfo] | None = None
        self._d_format_info_from_name: dict[str, FormatInfo] | None = None
        self._l_unsorted_format_info: list[FormatInfo] | None = None
        self._conversions_table: ConversionsTable | None = None

    def _init_converter_info(self):
        """Initialises the private dicts and lists for converter info
        """
        self._d_converter_info_from_name: dict[str, ConverterInfo] = {}
        self._d_converter_info_from_id: dict[int, ConverterInfo] = {}
        self._l_unsorted_converter_info: list[ConverterInfo] = []
        for d_single_converter_info in self.converters:
            name: str = regularize_name(d_single_converter_info[DB_NAME_KEY])
            if name in self._d_converter_info_from_name:
                logger.warning(f"Converter {tc.PATH}'{name}'{tc.OFF} appears more than once in the database. Only "
                               "the first instance will be used.")
                continue

            single_converter_info = ConverterInfo.from_db(parent=self,
                                                          d_single_converter_info=d_single_converter_info,
                                                          d_data=self._d_data)
            self._d_converter_info_from_name[name] = single_converter_info
            self._d_converter_info_from_id[single_converter_info.id] = single_converter_info
            self._l_unsorted_converter_info.append(single_converter_info)

    @property
    def d_converter_info_from_name(self) -> dict[str, ConverterInfo]:
        """Generate the converter info dict (indexed by name) when needed
        """
        if self._d_converter_info_from_name is None:
            self._init_converter_info()

        return self._d_converter_info_from_name

    @property
    def d_converter_info_from_id(self) -> dict[int, ConverterInfo]:
        """Generate the converter info dict (indexed by ID) when needed
        """
        if self._d_converter_info_from_id is None:
            self._init_converter_info()

        return self._d_converter_info_from_id

    @property
    def d_converter_info(self) -> dict[str, ConverterInfo]:
        """DEPRECATED: Get a dict of converter info keyed by name"""
        warnings.warn(f"{tc.CODE}`d_converter_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be removed in "
                      "a future release. To get a {tc.CODE}ConverterInfo{tc.OFF} from the converter name (the previous "
                      f"functionality of this), use {tc.CODE}`d_converter_info_from_name`{tc.OFF}. To get a "
                      f"{tc.CODE}ConverterInfo{tc.OFF} from the format UUID, use "
                      f"{tc.CODE}`d_converter_info_from_id`{tc.OFF}.", DeprecationWarning)
        return self.d_converter_info_from_name

    @property
    def l_unsorted_converter_info(self) -> list[ConverterInfo]:
        """Generate the unsorted converter info list when needed
        """
        if self._l_unsorted_converter_info is None:
            self._init_converter_info()

        return self._l_unsorted_converter_info

    @property
    def l_converter_info(self):
        """DEPRECATED: Get a list of converter info keyed by ID"""
        deprecation_msg = (f"{tc.CODE}`l_converter_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                           f"removed in a future release. To get a {tc.CODE}ConverterInfo{tc.OFF} from the format "
                           f"UUID, use {tc.CODE}`d_converter_info_from_id`{tc.OFF}. To get an unsorted list of "
                           f"{tc.CODE}ConverterInfo{tc.OFF}, use {tc.CODE}`l_unsorted_converter_info`{tc.OFF}.")
        warnings.warn(deprecation_msg, DeprecationWarning)
        raise AttributeError(deprecation_msg)

    @property
    def d_format_info_from_name(self) -> dict[str, list[FormatInfo]]:
        """Generate the format info from format name dict when needed
        """
        if self._d_format_info_from_name is None:
            self._init_formats_and_conversions()

        return self._d_format_info_from_name

    @property
    def d_format_info_from_id(self) -> dict[int, FormatInfo]:
        """Generate the format info from format ID dict when needed
        """
        if self._d_format_info_from_id is None:
            self._init_formats_and_conversions()

        return self._d_format_info_from_id

    @property
    def d_format_info(self) -> dict[str, list[FormatInfo]]:
        """DEPRECATED: Get a dict of format info keyed by format name"""
        warnings.warn(f"{tc.CODE}`d_format_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be removed in a "
                      f"future release. To get a {tc.CODE}FormatInfo{tc.OFF} from the format name (the previous "
                      f"functionality of this), use {tc.CODE}`d_format_info_from_name`{tc.OFF}. To get a "
                      f"{tc.CODE}FormatInfo{tc.OFF} from the format UUID, use "
                      f"{tc.CODE}`d_format_info_from_id`{tc.OFF}.", DeprecationWarning)
        return self.d_format_info_from_name

    @property
    def l_unsorted_format_info(self) -> list[FormatInfo]:
        """Generate the unsorted format info list when needed
        """
        if self._l_unsorted_format_info is None:
            self._init_formats_and_conversions()

        return self._l_unsorted_format_info

    @property
    def l_format_info(self):
        """DEPRECATED: Get a list of format info keyed by ID"""
        deprecation_msg = (f"{tc.CODE}`l_format_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                           f"removed in a future release. To get a {tc.CODE}FormatInfo{tc.OFF} from the format UUID, "
                           f"use {tc.CODE}`d_format_info_from_id`{tc.OFF}. To get an unsorted list of "
                           f"{tc.CODE}FormatInfo{tc.OFF}, use {tc.CODE}`l_unsorted_format_info`{tc.OFF}.")
        warnings.warn(deprecation_msg, DeprecationWarning)
        raise AttributeError(deprecation_msg)

    @property
    def conversions_table(self) -> ConversionsTable:
        """Generates the conversions table when needed
        """

        if self._conversions_table is None:
            self._init_formats_and_conversions()

        return self._conversions_table

    def _init_formats_and_conversions(self):
        """Initializes the format list and dict and the conversions table"""

        # Start by initializing the list of conversions

        # Make the dict of format info keyed by ID
        self._d_format_info_from_id: dict[int, FormatInfo] = {}

        # To sort aliases, we first make a temporary dict of the entries in the database, noting as we go through
        # which ids are for primary formats, and creating a dict listing the alias IDs for each primary ID
        d_format_id_aliases: dict[int, set[int]] = {}
        d_format_dicts: dict[int, JsonDict] = {}
        for d_format_db_info in self.formats:
            format_id: int = d_format_db_info[DB_ID_KEY]
            d_format_dicts[format_id] = d_format_db_info
            if not d_format_db_info.get(DB_FORMAT_ALIAS_OF_KEY):
                if format_id not in d_format_id_aliases:
                    d_format_id_aliases[format_id] = set((format_id,))
                else:
                    d_format_id_aliases[format_id].add(format_id)
            else:
                prim_id: int = d_format_db_info[DB_FORMAT_ALIAS_OF_KEY]
                if prim_id not in d_format_id_aliases:
                    d_format_id_aliases[prim_id] = set((prim_id, format_id))
                else:
                    d_format_id_aliases[prim_id].add(format_id)

        # Now create a FormatInfo object for each primary ID
        for prim_id, s_alias_ids in d_format_id_aliases.items():

            # Collect all the alias extensions for each primary format
            d_alias_exts: dict[int, str] = {x: d_format_dicts[x][DB_FORMAT_EXT_KEY] for x in s_alias_ids}
            for key, val in d_alias_exts.items():
                if val.startswith("."):
                    d_alias_exts[key] = val[1:]

            primary_format_info = FormatInfo.from_db(parent=self,
                                                     d_single_format_info=d_format_dicts[prim_id],
                                                     d_alias_exts=d_alias_exts)

            self._d_format_info_from_id[prim_id] = primary_format_info

            # Create a FormatInfo for every alias as well and also add those to the ID dict
            for alias_id in s_alias_ids:
                if alias_id == prim_id:
                    continue
                self._d_format_info_from_id[alias_id] = FormatInfo(
                    format_common_info=primary_format_info.format_common_info,
                    name=d_alias_exts[alias_id],
                    id=alias_id)

        # Create a temporary version of the unsorted format info list. We'll create a pruned version later, but the
        # unpruned version is needed to create the conversions table, which is needed before we can prune it
        self._l_unsorted_format_info = list(set(self._d_format_info_from_id.values()))

        # Initialize the conversions table now
        self._conversions_table = ConversionsTable(l_converts_to=self.converts_to,
                                                   d_format_id_aliases=d_format_id_aliases,
                                                   parent=self)

        # Use the conversions graph to prune any formats which have no valid conversions

        # Get a slice of the table which only includes supported converters
        supported_graph = self._conversions_table.supported_graph
        d_indices_from_uuids: dict[int, int] = self._conversions_table.d_indices_from_uuids

        if self._prune:
            l_ids_to_remove: list[int] = []
            for format_id, format_info in self._d_format_info_from_id.items():
                if not format_info or supported_graph.degree(d_indices_from_uuids[format_id]) == 0:
                    # The format isn't supported for any conversions, so mark it to be removed from the dict
                    # (Can't remove while we're iterating over the dict)
                    l_ids_to_remove.append(format_id)
            for format_id in l_ids_to_remove:
                del self._d_format_info_from_id[format_id]

        # Now create the formats from name dict
        self._d_format_info_from_name: dict[str, list[FormatInfo]] = {}

        for format_info in self._d_format_info_from_id.values():

            lc_name = format_info.name.lower()

            # Each name may correspond to multiple formats, so we use a list for each entry to list all possible
            # formats for each name
            if lc_name not in self._d_format_info_from_name:
                self._d_format_info_from_name[lc_name] = [format_info]
            elif format_info not in self._d_format_info_from_name[lc_name]:
                self._d_format_info_from_name[lc_name].append(format_info)

        # Sort each list in the format info from name dict by format ID for consistency
        for l_format_info in self._d_format_info_from_name.values():
            l_format_info.sort(key=lambda x: x.id)

        # Finally, create a list of format infos (with arbitrary index)
        self._l_unsorted_format_info = list(set(self._d_format_info_from_id.values()))

    def _get_converter_list(self) -> str:
        return "\n".join([x.format_oneline() for x in self.l_unsorted_converter_info])

    @overload
    def get_converter_info(self, converter: str | int | UUID | ConverterInfo) -> ConverterInfo: ...

    @overload
    def get_converter_info(self, converter: None) -> list[ConverterInfo]: ...

    @overload
    def get_converter_info(self) -> list[ConverterInfo]: ...

    def get_converter_info(self, converter: str | int | UUID | ConverterInfo | None = None,
                           **kwargs) -> (
            ConverterInfo | list[ConverterInfo]):
        """Gets the information on converters or a given converter stored in the database

        Parameters
        ----------
        converter : str | int | UUID | ConverterInfo | None
            The name, ID, or info of the converter to get info for. Default None, which results in a list being
            returned of the info for all converters in the database

        Returns
        -------
        ConverterInfo | list[ConverterInfo]
            If `converter` is provided, will return a single `ConverterInfo` (or raise an exception if the
            name is invalid). If not provided, a list of all `ConverterInfo` objects in the database will be returned

        Raises
        ------
        FileConverterDatabaseException
        If `name` is provided but does not match the name of a converter in the database
        """

        # Check for deprecated kwargs
        if "converter_name_or_id" in kwargs:
            warnings.warn(f"The argument {tc.CODE}`converter_name_or_id`{tc.OFF} for the method "
                          f"{tc.CODE}`get_converter_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                          f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                          "has equivalent functionality and is being used now to normalise function signatures across "
                          "the package.", DeprecationWarning)
            if converter is None:
                converter = get_converter_info(kwargs["converter_name_or_id"])

        if isinstance(converter, str):
            try:
                return self.d_converter_info_from_name[regularize_name(converter)]
            except KeyError:
                try:
                    return self.d_converter_info_from_id[UUID(converter).int]
                except (KeyError, ValueError):
                    raise FileConverterDatabaseException(f"Converter {tc.PATH}'{converter}'{tc.OFF} not "
                                                         "found as a name in the database and/or was not recognised as "
                                                         "a value UUID. Known converters are:\n" +
                                                         self._get_converter_list(),
                                                         help=True)
        elif isinstance(converter, int):
            try:
                return self.d_converter_info_from_id[converter]
            except KeyError:
                raise FileConverterDatabaseException(f"Converter ID '{tc.ID}{converter}'{tc.OFF} not found "
                                                     "in the database. Known converters are:\n" +
                                                     self._get_converter_list(),
                                                     help=True)
        elif isinstance(converter, UUID):
            try:
                return self.d_converter_info_from_id[converter.int]
            except KeyError:
                raise FileConverterDatabaseException(f"Converter ID '{tc.ID}{converter}'{tc.OFF} not found "
                                                     "in the database. Known converters are:\n" +
                                                     self._get_converter_list(),
                                                     help=True)
        elif isinstance(converter, ConverterInfo):
            # Silently return if it's already a ConverterInfo
            return converter
        elif converter is None:
            return self.l_unsorted_converter_info
        else:
            raise FileConverterDatabaseException(f"Invalid key passed to {tc.CODE}`get_converter_info`{tc.OFF}: "
                                                 f"{tc.PATH}'{converter}'{tc.OFF} of type "
                                                 f"{tc.CODE}`{type(converter)}`{tc.OFF}. Type must be "
                                                 f"{tc.CODE}`str`{tc.OFF}, {tc.CODE}`int`{tc.OFF}, or "
                                                 f"{tc.CODE}`UUID`{tc.OFF}")

    @overload
    def get_format_info(self,
                        file_format: str | int | UUID | FormatInfo,
                        which: int | None = None) -> FormatInfo: ...

    @overload
    def get_format_info(self,
                        file_format: str | int | UUID | FormatInfo,
                        which: Literal["all"]) -> list[FormatInfo]: ...

    def get_format_info(self,
                        file_format: str | int | UUID | FormatInfo,
                        which: int | Literal["all"] | None = None,
                        **kwargs) -> FormatInfo | list[FormatInfo]:
        """Gets the information on a given file format stored in the database

        Parameters
        ----------
        file_format : str | int | UUID | FormatInfo
            The name (extension) of the format, or its ID. In the case of ambiguous extensions which could apply to
            multiple formats, the ID must be used here or a `FileConverterDatabaseException` will be raised. This also
            allows passing a FormatInfo to this, in which case that object will be silently returned, to allow
            normalising the input to always be a FormatInfo when output from this
        which : int | None
            In the case that an extension string is provided which turns out to be ambiguous, which of the listed
            possibilities to use from the zero-indexed list. Default None, which raises an exception for an ambiguous
            format. 0 may be used to select the first in the database, which is often a good default choice. The literal
            string "all" may be used to request all possibilites, in which case this method will return a list (even if
            there are zero or one possibilities)

        Returns
        -------
        FormatInfo | list[FormatInfo]
        """

        # Check for deprecated kwargs
        if "format_name_or_id" in kwargs:
            warnings.warn(f"The argument {tc.CODE}`format_name_or_id`{tc.OFF} for the method "
                          f"{tc.CODE}`get_format_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                          f"removed in a future release. Use the argument {tc.CODE}`file_format`{tc.OFF} instead, "
                          "which has equivalent functionality and is being used now to normalise function signatures "
                          "across the package.", DeprecationWarning)
            file_format = kwargs["format_name_or_id"]

        if which == "all":
            return_as_list = True
        else:
            return_as_list = False

        if isinstance(file_format, str):
            # Check first if it's a UUID
            try:
                format_info = self.d_format_info_from_id[UUID(file_format).int]
                if which == "all":
                    return [format_info]
                return format_info
            except KeyError as e:
                if e.args[0] == UUID(file_format).int:
                    raise FileConverterDatabaseException(f"Format ID '{tc.ID}{file_format}'{tc.OFF} not "
                                                         "recognised", help=True)
            except ValueError:
                pass

            # Silently strip leading period
            if file_format.startswith("."):
                file_format = file_format[1:]

            # Convert the format name to lower-case to handle it case-insensitively
            file_format = file_format.lower()

            # Check for a hyphen in the format, which indicates a preference from the user as to which, overriding the
            # `which` kwarg
            if "-" in file_format:
                l_name_segments = file_format.split("-")
                if len(l_name_segments) > 2:
                    raise FileConverterDatabaseException(f"Format name {tc.PATH}'{file_format}'{tc.OFF} is "
                                                         "improperly formatted - It may contain at most one hyphen, "
                                                         "separating the extension from an index indicating which of "
                                                         "the formats with that extension to use, e.g. "
                                                         f"{tc.PATH}'pdb-0'{tc.OFF}, {tc.PATH}'pdb-1'{tc.OFF}, "
                                                         "etc.", help=True)
                file_format = l_name_segments[0]
                which = int(l_name_segments[1])

            l_possible_format_info = self.d_format_info_from_name.get(file_format, [])

            if which == "all":
                return l_possible_format_info

            elif len(l_possible_format_info) == 1:
                format_info = l_possible_format_info[0]

            elif len(l_possible_format_info) == 0:
                raise FileConverterDatabaseException(f"Format name {tc.PATH}'{file_format}'{tc.OFF} not "
                                                     "recognised", help=True)

            elif which is not None and which < len(l_possible_format_info):
                format_info = l_possible_format_info[which]

            else:
                msg = (f"Extension {tc.PATH}'{file_format}'{tc.OFF} is ambiguous and must be defined by "
                       "disambiguated name or ID. Possible formats are:")
                for possible_format_info in l_possible_format_info:
                    msg += f"\n{possible_format_info.format_oneline()}"
                raise FileConverterDatabaseException(msg, help=True)

        elif isinstance(file_format, int):
            try:
                format_info = self.d_format_info_from_id[file_format]
            except KeyError as e:
                if e.args[0] != file_format:
                    raise
                if return_as_list:
                    return []
                raise FileConverterDatabaseException(f"Format ID {tc.PATH}'{file_format}'{tc.OFF} not "
                                                     "recognised", help=True)

        elif isinstance(file_format, UUID):
            try:
                format_info = self.d_format_info_from_id[file_format.int]
            except KeyError as e:
                if e.args[0] != file_format.int:
                    raise
                if return_as_list:
                    return []
                raise FileConverterDatabaseException(f"Format ID {tc.PATH}'{file_format}'{tc.OFF} not "
                                                     "recognised", help=True)

        elif isinstance(file_format, FormatInfo):
            # Silently return the FormatInfo if it was used as a key here
            format_info = file_format

        else:
            raise FileConverterDatabaseException(f"Invalid key passed to {tc.CODE}`get_format_info`{tc.OFF}: "
                                                 f"{tc.PATH}'{file_format}'{tc.OFF} of type "
                                                 f"{tc.CODE}`{type(file_format)}'{tc.OFF}. Type must be "
                                                 f"{tc.CODE}`str`{tc.OFF} or {tc.CODE}`int`{tc.OFF}")
        if return_as_list:
            return [format_info]

        return format_info

Class providing interface for information contained in the PSDI Data Conversion database

Initialise the DataConversionDatabase object

Parameters

d_data : dict[str, Any]
The dict of the database, as loaded in from the JSON file
prune : bool
Whether or not any formats with no supported conversions should be pruned from the database, default True

Instance variables

prop conversions_table : ConversionsTable
Expand source code
@property
def conversions_table(self) -> ConversionsTable:
    """Generates the conversions table when needed
    """

    if self._conversions_table is None:
        self._init_formats_and_conversions()

    return self._conversions_table

Generates the conversions table when needed

prop d_converter_info : dict[str, ConverterInfo]
Expand source code
@property
def d_converter_info(self) -> dict[str, ConverterInfo]:
    """DEPRECATED: Get a dict of converter info keyed by name"""
    warnings.warn(f"{tc.CODE}`d_converter_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be removed in "
                  "a future release. To get a {tc.CODE}ConverterInfo{tc.OFF} from the converter name (the previous "
                  f"functionality of this), use {tc.CODE}`d_converter_info_from_name`{tc.OFF}. To get a "
                  f"{tc.CODE}ConverterInfo{tc.OFF} from the format UUID, use "
                  f"{tc.CODE}`d_converter_info_from_id`{tc.OFF}.", DeprecationWarning)
    return self.d_converter_info_from_name

DEPRECATED: Get a dict of converter info keyed by name

prop d_converter_info_from_id : dict[int, ConverterInfo]
Expand source code
@property
def d_converter_info_from_id(self) -> dict[int, ConverterInfo]:
    """Generate the converter info dict (indexed by ID) when needed
    """
    if self._d_converter_info_from_id is None:
        self._init_converter_info()

    return self._d_converter_info_from_id

Generate the converter info dict (indexed by ID) when needed

prop d_converter_info_from_name : dict[str, ConverterInfo]
Expand source code
@property
def d_converter_info_from_name(self) -> dict[str, ConverterInfo]:
    """Generate the converter info dict (indexed by name) when needed
    """
    if self._d_converter_info_from_name is None:
        self._init_converter_info()

    return self._d_converter_info_from_name

Generate the converter info dict (indexed by name) when needed

prop d_format_info : dict[str, list[FormatInfo]]
Expand source code
@property
def d_format_info(self) -> dict[str, list[FormatInfo]]:
    """DEPRECATED: Get a dict of format info keyed by format name"""
    warnings.warn(f"{tc.CODE}`d_format_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be removed in a "
                  f"future release. To get a {tc.CODE}FormatInfo{tc.OFF} from the format name (the previous "
                  f"functionality of this), use {tc.CODE}`d_format_info_from_name`{tc.OFF}. To get a "
                  f"{tc.CODE}FormatInfo{tc.OFF} from the format UUID, use "
                  f"{tc.CODE}`d_format_info_from_id`{tc.OFF}.", DeprecationWarning)
    return self.d_format_info_from_name

DEPRECATED: Get a dict of format info keyed by format name

prop d_format_info_from_id : dict[int, FormatInfo]
Expand source code
@property
def d_format_info_from_id(self) -> dict[int, FormatInfo]:
    """Generate the format info from format ID dict when needed
    """
    if self._d_format_info_from_id is None:
        self._init_formats_and_conversions()

    return self._d_format_info_from_id

Generate the format info from format ID dict when needed

prop d_format_info_from_name : dict[str, list[FormatInfo]]
Expand source code
@property
def d_format_info_from_name(self) -> dict[str, list[FormatInfo]]:
    """Generate the format info from format name dict when needed
    """
    if self._d_format_info_from_name is None:
        self._init_formats_and_conversions()

    return self._d_format_info_from_name

Generate the format info from format name dict when needed

prop l_converter_info
Expand source code
@property
def l_converter_info(self):
    """DEPRECATED: Get a list of converter info keyed by ID"""
    deprecation_msg = (f"{tc.CODE}`l_converter_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                       f"removed in a future release. To get a {tc.CODE}ConverterInfo{tc.OFF} from the format "
                       f"UUID, use {tc.CODE}`d_converter_info_from_id`{tc.OFF}. To get an unsorted list of "
                       f"{tc.CODE}ConverterInfo{tc.OFF}, use {tc.CODE}`l_unsorted_converter_info`{tc.OFF}.")
    warnings.warn(deprecation_msg, DeprecationWarning)
    raise AttributeError(deprecation_msg)

DEPRECATED: Get a list of converter info keyed by ID

prop l_format_info
Expand source code
@property
def l_format_info(self):
    """DEPRECATED: Get a list of format info keyed by ID"""
    deprecation_msg = (f"{tc.CODE}`l_format_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                       f"removed in a future release. To get a {tc.CODE}FormatInfo{tc.OFF} from the format UUID, "
                       f"use {tc.CODE}`d_format_info_from_id`{tc.OFF}. To get an unsorted list of "
                       f"{tc.CODE}FormatInfo{tc.OFF}, use {tc.CODE}`l_unsorted_format_info`{tc.OFF}.")
    warnings.warn(deprecation_msg, DeprecationWarning)
    raise AttributeError(deprecation_msg)

DEPRECATED: Get a list of format info keyed by ID

prop l_unsorted_converter_info : list[ConverterInfo]
Expand source code
@property
def l_unsorted_converter_info(self) -> list[ConverterInfo]:
    """Generate the unsorted converter info list when needed
    """
    if self._l_unsorted_converter_info is None:
        self._init_converter_info()

    return self._l_unsorted_converter_info

Generate the unsorted converter info list when needed

prop l_unsorted_format_info : list[FormatInfo]
Expand source code
@property
def l_unsorted_format_info(self) -> list[FormatInfo]:
    """Generate the unsorted format info list when needed
    """
    if self._l_unsorted_format_info is None:
        self._init_formats_and_conversions()

    return self._l_unsorted_format_info

Generate the unsorted format info list when needed

Methods

def get_converter_info(self,
converter: str | int | UUID | ConverterInfo | None = None,
**kwargs) ‑> ConverterInfo | list[ConverterInfo]
Expand source code
def get_converter_info(self, converter: str | int | UUID | ConverterInfo | None = None,
                       **kwargs) -> (
        ConverterInfo | list[ConverterInfo]):
    """Gets the information on converters or a given converter stored in the database

    Parameters
    ----------
    converter : str | int | UUID | ConverterInfo | None
        The name, ID, or info of the converter to get info for. Default None, which results in a list being
        returned of the info for all converters in the database

    Returns
    -------
    ConverterInfo | list[ConverterInfo]
        If `converter` is provided, will return a single `ConverterInfo` (or raise an exception if the
        name is invalid). If not provided, a list of all `ConverterInfo` objects in the database will be returned

    Raises
    ------
    FileConverterDatabaseException
    If `name` is provided but does not match the name of a converter in the database
    """

    # Check for deprecated kwargs
    if "converter_name_or_id" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`converter_name_or_id`{tc.OFF} for the method "
                      f"{tc.CODE}`get_converter_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`converter`{tc.OFF} instead, which "
                      "has equivalent functionality and is being used now to normalise function signatures across "
                      "the package.", DeprecationWarning)
        if converter is None:
            converter = get_converter_info(kwargs["converter_name_or_id"])

    if isinstance(converter, str):
        try:
            return self.d_converter_info_from_name[regularize_name(converter)]
        except KeyError:
            try:
                return self.d_converter_info_from_id[UUID(converter).int]
            except (KeyError, ValueError):
                raise FileConverterDatabaseException(f"Converter {tc.PATH}'{converter}'{tc.OFF} not "
                                                     "found as a name in the database and/or was not recognised as "
                                                     "a value UUID. Known converters are:\n" +
                                                     self._get_converter_list(),
                                                     help=True)
    elif isinstance(converter, int):
        try:
            return self.d_converter_info_from_id[converter]
        except KeyError:
            raise FileConverterDatabaseException(f"Converter ID '{tc.ID}{converter}'{tc.OFF} not found "
                                                 "in the database. Known converters are:\n" +
                                                 self._get_converter_list(),
                                                 help=True)
    elif isinstance(converter, UUID):
        try:
            return self.d_converter_info_from_id[converter.int]
        except KeyError:
            raise FileConverterDatabaseException(f"Converter ID '{tc.ID}{converter}'{tc.OFF} not found "
                                                 "in the database. Known converters are:\n" +
                                                 self._get_converter_list(),
                                                 help=True)
    elif isinstance(converter, ConverterInfo):
        # Silently return if it's already a ConverterInfo
        return converter
    elif converter is None:
        return self.l_unsorted_converter_info
    else:
        raise FileConverterDatabaseException(f"Invalid key passed to {tc.CODE}`get_converter_info`{tc.OFF}: "
                                             f"{tc.PATH}'{converter}'{tc.OFF} of type "
                                             f"{tc.CODE}`{type(converter)}`{tc.OFF}. Type must be "
                                             f"{tc.CODE}`str`{tc.OFF}, {tc.CODE}`int`{tc.OFF}, or "
                                             f"{tc.CODE}`UUID`{tc.OFF}")

Gets the information on converters or a given converter stored in the database

Parameters

converter : str | int | UUID | ConverterInfo | None
The name, ID, or info of the converter to get info for. Default None, which results in a list being returned of the info for all converters in the database

Returns

ConverterInfo | list[ConverterInfo]
If converter is provided, will return a single ConverterInfo (or raise an exception if the name is invalid). If not provided, a list of all ConverterInfo objects in the database will be returned

Raises

FileConverterDatabaseException
 

If name is provided but does not match the name of a converter in the database

def get_format_info(self,
file_format: str | int | UUID | FormatInfo,
which: "int | Literal['all'] | None" = None,
**kwargs) ‑> FormatInfo | list[FormatInfo]
Expand source code
def get_format_info(self,
                    file_format: str | int | UUID | FormatInfo,
                    which: int | Literal["all"] | None = None,
                    **kwargs) -> FormatInfo | list[FormatInfo]:
    """Gets the information on a given file format stored in the database

    Parameters
    ----------
    file_format : str | int | UUID | FormatInfo
        The name (extension) of the format, or its ID. In the case of ambiguous extensions which could apply to
        multiple formats, the ID must be used here or a `FileConverterDatabaseException` will be raised. This also
        allows passing a FormatInfo to this, in which case that object will be silently returned, to allow
        normalising the input to always be a FormatInfo when output from this
    which : int | None
        In the case that an extension string is provided which turns out to be ambiguous, which of the listed
        possibilities to use from the zero-indexed list. Default None, which raises an exception for an ambiguous
        format. 0 may be used to select the first in the database, which is often a good default choice. The literal
        string "all" may be used to request all possibilites, in which case this method will return a list (even if
        there are zero or one possibilities)

    Returns
    -------
    FormatInfo | list[FormatInfo]
    """

    # Check for deprecated kwargs
    if "format_name_or_id" in kwargs:
        warnings.warn(f"The argument {tc.CODE}`format_name_or_id`{tc.OFF} for the method "
                      f"{tc.CODE}`get_format_info`{tc.OFF} is deprecated as of version 0.4.0 and due to be "
                      f"removed in a future release. Use the argument {tc.CODE}`file_format`{tc.OFF} instead, "
                      "which has equivalent functionality and is being used now to normalise function signatures "
                      "across the package.", DeprecationWarning)
        file_format = kwargs["format_name_or_id"]

    if which == "all":
        return_as_list = True
    else:
        return_as_list = False

    if isinstance(file_format, str):
        # Check first if it's a UUID
        try:
            format_info = self.d_format_info_from_id[UUID(file_format).int]
            if which == "all":
                return [format_info]
            return format_info
        except KeyError as e:
            if e.args[0] == UUID(file_format).int:
                raise FileConverterDatabaseException(f"Format ID '{tc.ID}{file_format}'{tc.OFF} not "
                                                     "recognised", help=True)
        except ValueError:
            pass

        # Silently strip leading period
        if file_format.startswith("."):
            file_format = file_format[1:]

        # Convert the format name to lower-case to handle it case-insensitively
        file_format = file_format.lower()

        # Check for a hyphen in the format, which indicates a preference from the user as to which, overriding the
        # `which` kwarg
        if "-" in file_format:
            l_name_segments = file_format.split("-")
            if len(l_name_segments) > 2:
                raise FileConverterDatabaseException(f"Format name {tc.PATH}'{file_format}'{tc.OFF} is "
                                                     "improperly formatted - It may contain at most one hyphen, "
                                                     "separating the extension from an index indicating which of "
                                                     "the formats with that extension to use, e.g. "
                                                     f"{tc.PATH}'pdb-0'{tc.OFF}, {tc.PATH}'pdb-1'{tc.OFF}, "
                                                     "etc.", help=True)
            file_format = l_name_segments[0]
            which = int(l_name_segments[1])

        l_possible_format_info = self.d_format_info_from_name.get(file_format, [])

        if which == "all":
            return l_possible_format_info

        elif len(l_possible_format_info) == 1:
            format_info = l_possible_format_info[0]

        elif len(l_possible_format_info) == 0:
            raise FileConverterDatabaseException(f"Format name {tc.PATH}'{file_format}'{tc.OFF} not "
                                                 "recognised", help=True)

        elif which is not None and which < len(l_possible_format_info):
            format_info = l_possible_format_info[which]

        else:
            msg = (f"Extension {tc.PATH}'{file_format}'{tc.OFF} is ambiguous and must be defined by "
                   "disambiguated name or ID. Possible formats are:")
            for possible_format_info in l_possible_format_info:
                msg += f"\n{possible_format_info.format_oneline()}"
            raise FileConverterDatabaseException(msg, help=True)

    elif isinstance(file_format, int):
        try:
            format_info = self.d_format_info_from_id[file_format]
        except KeyError as e:
            if e.args[0] != file_format:
                raise
            if return_as_list:
                return []
            raise FileConverterDatabaseException(f"Format ID {tc.PATH}'{file_format}'{tc.OFF} not "
                                                 "recognised", help=True)

    elif isinstance(file_format, UUID):
        try:
            format_info = self.d_format_info_from_id[file_format.int]
        except KeyError as e:
            if e.args[0] != file_format.int:
                raise
            if return_as_list:
                return []
            raise FileConverterDatabaseException(f"Format ID {tc.PATH}'{file_format}'{tc.OFF} not "
                                                 "recognised", help=True)

    elif isinstance(file_format, FormatInfo):
        # Silently return the FormatInfo if it was used as a key here
        format_info = file_format

    else:
        raise FileConverterDatabaseException(f"Invalid key passed to {tc.CODE}`get_format_info`{tc.OFF}: "
                                             f"{tc.PATH}'{file_format}'{tc.OFF} of type "
                                             f"{tc.CODE}`{type(file_format)}'{tc.OFF}. Type must be "
                                             f"{tc.CODE}`str`{tc.OFF} or {tc.CODE}`int`{tc.OFF}")
    if return_as_list:
        return [format_info]

    return format_info

Gets the information on a given file format stored in the database

Parameters

file_format : str | int | UUID | FormatInfo
The name (extension) of the format, or its ID. In the case of ambiguous extensions which could apply to multiple formats, the ID must be used here or a FileConverterDatabaseException will be raised. This also allows passing a FormatInfo to this, in which case that object will be silently returned, to allow normalising the input to always be a FormatInfo when output from this
which : int | None
In the case that an extension string is provided which turns out to be ambiguous, which of the listed possibilities to use from the zero-indexed list. Default None, which raises an exception for an ambiguous format. 0 may be used to select the first in the database, which is often a good default choice. The literal string "all" may be used to request all possibilites, in which case this method will return a list (even if there are zero or one possibilities)

Returns

FormatInfo | list[FormatInfo]
 
class FileConverterDatabaseException (*args, logged: bool = False, help: bool = False, msg_preformatted: bool = False)
Expand source code
class FileConverterDatabaseException(FileConverterException):
    """Class for any exceptions which arise from issues with the database classes and methods
    """
    pass

Class for any exceptions which arise from issues with the database classes and methods

Ancestors

class FlagInfo (id: int = <property object>,
name: str = <property object>,
description: str = <property object>,
info: str = <property object>,
parent: DataConversionDatabase | DBInfo | None = None,
s_in_formats: set[int] = <factory>,
s_out_formats: set[int] = <factory>)
Expand source code
@dataclass
class FlagInfo(ArgInfo):
    """Class providing information on a flag accepted by a converter (an argument which doesn't accept a value)
    """

    # __hash__ needs to be inherited explicitly for dataclasses since they redefine __eq__
    __hash__ = DBInfo.__hash__

Class providing information on a flag accepted by a converter (an argument which doesn't accept a value)

Ancestors

Inherited members

class FormatCommonInfo (id: int = <property object>,
name: str = <property object>,
description: str = <property object>,
info: str = <property object>,
parent: DataConversionDatabase | DBInfo | None = None,
primary_name: str = '',
primary_id: int = -1,
d_alias_exts: dict[int, str] = <factory>,
d_conv_names: dict[str, str] = <factory>,
composition: bool | None = None,
two_dim: bool | None = None,
three_dim: bool | None = None,
connections: bool | None = None,
precision: int | None = None,
weight: int | None = None)
Expand source code
@dataclass
class FormatCommonInfo(DBInfo):
    """A class representing the common info for a file format (basically, everything except the extension and ID)"""

    primary_name: str = ""
    """The primary name (extension) of this format"""

    primary_id: int = DEFAULT_ID
    """The primary ID of this format"""

    d_alias_exts: dict[int, str] = field(default_factory=dict)
    """Dict of IDs of aliases and their respective extensions"""

    d_conv_names: dict[str, str] = field(default_factory=dict)
    """Dict providing the names of this format by different converters, keyed by their DB prefixes"""

    composition: bool | None = None
    """Whether or not this format stores composition information"""

    two_dim: bool | None = None
    """Whether or not this format stores 2D structural information"""

    three_dim: bool | None = None
    """Whether or not this format stores 3D structural information"""

    connections: bool | None = None
    """Whether or not this format stores connections information"""

    precision: int | None = None
    """The precision of numeric information in the format, as the number of decimal places, or 0 if unknown"""

    weight: int | None = None
    """The weight of the format, based on experience of how reliable it is in conversions"""

    def __post_init__(self):
        """Finish initialising the object"""

        if not self.name:
            self.name = self.primary_name

        if self.id == DEFAULT_ID:
            self.id = self.primary_id

    # __hash__ needs to be inherited explicitly for dataclasses since they redefine __eq__
    __hash__ = DBInfo.__hash__

    @staticmethod
    def from_db(parent: DataConversionDatabase,
                d_single_format_info: dict[str, bool | int | str | None],
                d_alias_exts: dict[int, str] | None = None):
        """Factory function to set up the class - this will be initialised within a `DataConversionDatabase`, which we
        set as the parent

        Parameters
        ----------
        parent : DataConversionDatabase
            The database which this belongs to
        d_single_format_info : dict[str, bool  |  int  |  str  |  None]
            The dict of info on the primary format stored in the database
        d_alias_exts : dict[int, str]
            Dict of IDs of aliases and their respective extensions
        """

        primary_name: str = d_single_format_info.get(DB_FORMAT_EXT_KEY, "")
        primary_id: int = d_single_format_info.get(DB_ID_KEY, DEFAULT_ID)

        if d_alias_exts is None:
            d_alias_exts = {primary_id: primary_name}

        d_conv_names: dict[str, str] = {}
        for key, val in d_single_format_info.items():
            if key.endswith(DB_FORMAT_CONV_NAME_KEY_TAIL):
                d_conv_names[key[:-len(DB_FORMAT_CONV_NAME_KEY_TAIL)]] = val

        format_common_info = FormatCommonInfo(primary_name=primary_name,
                                              primary_id=primary_id,
                                              description=d_single_format_info.get(DB_FORMAT_NOTE_KEY, ""),
                                              parent=parent,
                                              d_alias_exts=d_alias_exts,
                                              d_conv_names=d_conv_names,
                                              composition=d_single_format_info.get(DB_FORMAT_COMP_KEY),
                                              two_dim=d_single_format_info.get(DB_FORMAT_2D_KEY),
                                              three_dim=d_single_format_info.get(DB_FORMAT_3D_KEY),
                                              connections=d_single_format_info.get(DB_FORMAT_CONN_KEY),
                                              precision=d_single_format_info.get(DB_FORMAT_PRECISION_KEY),
                                              weight=d_single_format_info.get(DB_WEIGHT_KEY))
        return format_common_info

    @cached_property
    def disambiguated_name(self) -> str:
        """A unique name for this format which can be used to distinguish it from others which share the same extension,
        by appending the name of each with a unique index"""
        l_formats_with_same_name = self.parent.d_format_info_from_name[self.name.lower()]
        if len(l_formats_with_same_name) == 1:
            return self.lower_name
        else:
            index_of_this = [i for i, x in enumerate(l_formats_with_same_name) if self is x][0]
            return f"{self.lower_name}-{index_of_this}"

A class representing the common info for a file format (basically, everything except the extension and ID)

Ancestors

Static methods

def from_db(parent: DataConversionDatabase,
d_single_format_info: dict[str, bool | int | str | None],
d_alias_exts: dict[int, str] | None = None)
Expand source code
@staticmethod
def from_db(parent: DataConversionDatabase,
            d_single_format_info: dict[str, bool | int | str | None],
            d_alias_exts: dict[int, str] | None = None):
    """Factory function to set up the class - this will be initialised within a `DataConversionDatabase`, which we
    set as the parent

    Parameters
    ----------
    parent : DataConversionDatabase
        The database which this belongs to
    d_single_format_info : dict[str, bool  |  int  |  str  |  None]
        The dict of info on the primary format stored in the database
    d_alias_exts : dict[int, str]
        Dict of IDs of aliases and their respective extensions
    """

    primary_name: str = d_single_format_info.get(DB_FORMAT_EXT_KEY, "")
    primary_id: int = d_single_format_info.get(DB_ID_KEY, DEFAULT_ID)

    if d_alias_exts is None:
        d_alias_exts = {primary_id: primary_name}

    d_conv_names: dict[str, str] = {}
    for key, val in d_single_format_info.items():
        if key.endswith(DB_FORMAT_CONV_NAME_KEY_TAIL):
            d_conv_names[key[:-len(DB_FORMAT_CONV_NAME_KEY_TAIL)]] = val

    format_common_info = FormatCommonInfo(primary_name=primary_name,
                                          primary_id=primary_id,
                                          description=d_single_format_info.get(DB_FORMAT_NOTE_KEY, ""),
                                          parent=parent,
                                          d_alias_exts=d_alias_exts,
                                          d_conv_names=d_conv_names,
                                          composition=d_single_format_info.get(DB_FORMAT_COMP_KEY),
                                          two_dim=d_single_format_info.get(DB_FORMAT_2D_KEY),
                                          three_dim=d_single_format_info.get(DB_FORMAT_3D_KEY),
                                          connections=d_single_format_info.get(DB_FORMAT_CONN_KEY),
                                          precision=d_single_format_info.get(DB_FORMAT_PRECISION_KEY),
                                          weight=d_single_format_info.get(DB_WEIGHT_KEY))
    return format_common_info

Factory function to set up the class - this will be initialised within a DataConversionDatabase, which we set as the parent

Parameters

parent : DataConversionDatabase
The database which this belongs to
d_single_format_info : dict[str, bool | int | str | None]
The dict of info on the primary format stored in the database
d_alias_exts : dict[int, str]
Dict of IDs of aliases and their respective extensions

Instance variables

var composition : bool | None

Whether or not this format stores composition information

var connections : bool | None

Whether or not this format stores connections information

var d_alias_exts : dict[int, str]

Dict of IDs of aliases and their respective extensions

var d_conv_names : dict[str, str]

Dict providing the names of this format by different converters, keyed by their DB prefixes

var disambiguated_name : str
Expand source code
@cached_property
def disambiguated_name(self) -> str:
    """A unique name for this format which can be used to distinguish it from others which share the same extension,
    by appending the name of each with a unique index"""
    l_formats_with_same_name = self.parent.d_format_info_from_name[self.name.lower()]
    if len(l_formats_with_same_name) == 1:
        return self.lower_name
    else:
        index_of_this = [i for i, x in enumerate(l_formats_with_same_name) if self is x][0]
        return f"{self.lower_name}-{index_of_this}"

A unique name for this format which can be used to distinguish it from others which share the same extension, by appending the name of each with a unique index

var precision : int | None

The precision of numeric information in the format, as the number of decimal places, or 0 if unknown

var primary_id : int

The primary ID of this format

var primary_name : str

The primary name (extension) of this format

var three_dim : bool | None

Whether or not this format stores 3D structural information

var two_dim : bool | None

Whether or not this format stores 2D structural information

var weight : int | None

The weight of the format, based on experience of how reliable it is in conversions

Inherited members

class FormatInfo (id: int = <property object>,
name: str = <property object>,
description: str = <property object>,
info: str = <property object>,
parent: DataConversionDatabase | DBInfo | None = None,
format_common_info: FormatCommonInfo = <factory>)
Expand source code
@dataclass
class FormatInfo(DBInfo):
    """Class providing information on a file format from the PSDI Data Conversion database
    """

    format_common_info: FormatCommonInfo = field(default_factory=FormatCommonInfo)
    """The information common to all variants of the format"""

    # __hash__ needs to be inherited explicitly for dataclasses since they redefine __eq__
    __hash__ = DBInfo.__hash__

    def __post_init__(self):
        """Finish setting up the object"""

        if not self.name:
            self.name = self.format_common_info.primary_name

        if self.id == DEFAULT_ID:
            self.id = self.format_common_info.primary_id

        if not self.description:
            self.description = self.format_common_info.description

        if not self.parent:
            self.parent = self.format_common_info.parent

        self.is_primary = self.id == self.format_common_info.primary_id
        """Whether or not this is the primary format for the shared extensions of a format"""

        self._disambiguated_name: str | None = None
        """The disambiguated name of the format"""

    @property
    def primary_name(self):
        """The primary name (extension) of the format"""
        return self.format_common_info.primary_name

    @property
    def primary_id(self):
        """The primary ID of the format"""
        return self.format_common_info.primary_id

    @property
    def d_alias_exts(self):
        """Dict of IDs of aliases and their respective extensions"""
        return self.format_common_info.d_alias_exts

    @property
    def d_conv_names(self):
        """Dict providing the names of this format by different converters, keyed by their DB prefixes"""
        return self.format_common_info.d_conv_names

    @property
    def composition(self):
        """Whether or not this format stores composition information"""
        return self.format_common_info.composition

    @property
    def two_dim(self):
        """Whether or not this format stores 2D structural information"""
        return self.format_common_info.two_dim

    @property
    def three_dim(self):
        """Whether or not this format stores 3D structural information"""
        return self.format_common_info.three_dim

    @property
    def connections(self):
        """Whether or not this format stores connections information"""
        return self.format_common_info.connections

    @property
    def precision(self):
        """The precision of numeric information in the format, as the number of decimal places, or 0 if unknown"""
        return self.format_common_info.precision

    @property
    def weight(self):
        """The weight of the format, based on experience of how reliable it is in conversions"""
        return self.format_common_info.weight

    @cached_property
    def disambiguated_name(self) -> str:
        """A unique name for this format which can be used to distinguish it from others which share the same extension,
        by appending the name of each with a unique index"""
        l_formats_with_same_name = self.parent.d_format_info_from_name[self.name.lower()]
        if len(l_formats_with_same_name) == 1:
            return self.lower_name
        else:
            index_of_this = [i for i, x in enumerate(l_formats_with_same_name) if self is x][0]
            return f"{self.lower_name}-{index_of_this}"

    @property
    def note(self):
        """DEPRECATED: Now known as `description`
        """
        warnings.warn(f"The {tc.CODE}`note`{tc.OFF} property of the {tc.CODE}`FormatInfo`{tc.OFF} class has been "
                      f"renamed to {tc.CODE}`description`{tc.OFF} as of version 0.4.0 and is due to be removed in a "
                      "future version.", DeprecationWarning)
        return self.description

    def format_word(self):
        """Use the disambiguated name for the formatted name"""
        return f"{tc.BOLD}{self.disambiguated_name}{tc.OFF}"

    def format_detailed(self):
        """Detail any aliases the format has and what properties it supports"""
        msg = super().format_detailed()

        # Print whether or not it supports each possible property
        for attr, label in D_FORMAT_PROPERTY_ATTRS.items():
            support_str = label
            if getattr(self, attr):
                support_str += " supported"
            elif getattr(self, attr) is False:
                support_str += " not supported"
            else:
                support_str += " unknown whether or not to be supported"
            msg += f"\n- {support_str}"

        # List any aliases the format has
        l_alias_ids = list(self.format_common_info.d_alias_exts.keys())
        if len(l_alias_ids) > 1:
            l_other_alias_ids = [x for x in l_alias_ids if x != self.id]
            if len(l_other_alias_ids) == 1:
                msg += f"\nAlias: {get_format_info(l_other_alias_ids[0]).format_inline()}"
            else:
                msg += "\nAliases: " + ", ".join([get_format_info(x).format_inline() for x in l_other_alias_ids])

        return msg

    @staticmethod
    def from_db(parent: DataConversionDatabase,
                d_single_format_info: dict[str, bool | int | str | None],
                d_alias_exts: dict[int, str] | None = None):
        """Factory function to easily set up the class. This should only be used for primary formats, and each alias
        format should be set up using the class initializer and the `format_common_info` of the primary format.

        Parameters
        ----------
        parent : DataConversionDatabase
            The database which this belongs to
        d_single_format_info : dict[str, bool  |  int  |  str  |  None]
            The dict of info on the format stored in the database
        d_alias_exts : dict[int, str] | None
            Dict of IDs of aliases and their respective extensions
        """

        format_common_info = FormatCommonInfo.from_db(parent, d_single_format_info, d_alias_exts)
        format_info = FormatInfo(format_common_info=format_common_info)
        return format_info

Class providing information on a file format from the PSDI Data Conversion database

Ancestors

Static methods

def from_db(parent: DataConversionDatabase,
d_single_format_info: dict[str, bool | int | str | None],
d_alias_exts: dict[int, str] | None = None)
Expand source code
@staticmethod
def from_db(parent: DataConversionDatabase,
            d_single_format_info: dict[str, bool | int | str | None],
            d_alias_exts: dict[int, str] | None = None):
    """Factory function to easily set up the class. This should only be used for primary formats, and each alias
    format should be set up using the class initializer and the `format_common_info` of the primary format.

    Parameters
    ----------
    parent : DataConversionDatabase
        The database which this belongs to
    d_single_format_info : dict[str, bool  |  int  |  str  |  None]
        The dict of info on the format stored in the database
    d_alias_exts : dict[int, str] | None
        Dict of IDs of aliases and their respective extensions
    """

    format_common_info = FormatCommonInfo.from_db(parent, d_single_format_info, d_alias_exts)
    format_info = FormatInfo(format_common_info=format_common_info)
    return format_info

Factory function to easily set up the class. This should only be used for primary formats, and each alias format should be set up using the class initializer and the format_common_info of the primary format.

Parameters

parent : DataConversionDatabase
The database which this belongs to
d_single_format_info : dict[str, bool | int | str | None]
The dict of info on the format stored in the database
d_alias_exts : dict[int, str] | None
Dict of IDs of aliases and their respective extensions

Instance variables

prop composition
Expand source code
@property
def composition(self):
    """Whether or not this format stores composition information"""
    return self.format_common_info.composition

Whether or not this format stores composition information

prop connections
Expand source code
@property
def connections(self):
    """Whether or not this format stores connections information"""
    return self.format_common_info.connections

Whether or not this format stores connections information

prop d_alias_exts
Expand source code
@property
def d_alias_exts(self):
    """Dict of IDs of aliases and their respective extensions"""
    return self.format_common_info.d_alias_exts

Dict of IDs of aliases and their respective extensions

prop d_conv_names
Expand source code
@property
def d_conv_names(self):
    """Dict providing the names of this format by different converters, keyed by their DB prefixes"""
    return self.format_common_info.d_conv_names

Dict providing the names of this format by different converters, keyed by their DB prefixes

var disambiguated_name : str
Expand source code
@cached_property
def disambiguated_name(self) -> str:
    """A unique name for this format which can be used to distinguish it from others which share the same extension,
    by appending the name of each with a unique index"""
    l_formats_with_same_name = self.parent.d_format_info_from_name[self.name.lower()]
    if len(l_formats_with_same_name) == 1:
        return self.lower_name
    else:
        index_of_this = [i for i, x in enumerate(l_formats_with_same_name) if self is x][0]
        return f"{self.lower_name}-{index_of_this}"

A unique name for this format which can be used to distinguish it from others which share the same extension, by appending the name of each with a unique index

var format_common_info : FormatCommonInfo

The information common to all variants of the format

prop note
Expand source code
@property
def note(self):
    """DEPRECATED: Now known as `description`
    """
    warnings.warn(f"The {tc.CODE}`note`{tc.OFF} property of the {tc.CODE}`FormatInfo`{tc.OFF} class has been "
                  f"renamed to {tc.CODE}`description`{tc.OFF} as of version 0.4.0 and is due to be removed in a "
                  "future version.", DeprecationWarning)
    return self.description

DEPRECATED: Now known as description

prop precision
Expand source code
@property
def precision(self):
    """The precision of numeric information in the format, as the number of decimal places, or 0 if unknown"""
    return self.format_common_info.precision

The precision of numeric information in the format, as the number of decimal places, or 0 if unknown

prop primary_id
Expand source code
@property
def primary_id(self):
    """The primary ID of the format"""
    return self.format_common_info.primary_id

The primary ID of the format

prop primary_name
Expand source code
@property
def primary_name(self):
    """The primary name (extension) of the format"""
    return self.format_common_info.primary_name

The primary name (extension) of the format

prop three_dim
Expand source code
@property
def three_dim(self):
    """Whether or not this format stores 3D structural information"""
    return self.format_common_info.three_dim

Whether or not this format stores 3D structural information

prop two_dim
Expand source code
@property
def two_dim(self):
    """Whether or not this format stores 2D structural information"""
    return self.format_common_info.two_dim

Whether or not this format stores 2D structural information

prop weight
Expand source code
@property
def weight(self):
    """The weight of the format, based on experience of how reliable it is in conversions"""
    return self.format_common_info.weight

The weight of the format, based on experience of how reliable it is in conversions

Methods

def format_detailed(self)
Expand source code
def format_detailed(self):
    """Detail any aliases the format has and what properties it supports"""
    msg = super().format_detailed()

    # Print whether or not it supports each possible property
    for attr, label in D_FORMAT_PROPERTY_ATTRS.items():
        support_str = label
        if getattr(self, attr):
            support_str += " supported"
        elif getattr(self, attr) is False:
            support_str += " not supported"
        else:
            support_str += " unknown whether or not to be supported"
        msg += f"\n- {support_str}"

    # List any aliases the format has
    l_alias_ids = list(self.format_common_info.d_alias_exts.keys())
    if len(l_alias_ids) > 1:
        l_other_alias_ids = [x for x in l_alias_ids if x != self.id]
        if len(l_other_alias_ids) == 1:
            msg += f"\nAlias: {get_format_info(l_other_alias_ids[0]).format_inline()}"
        else:
            msg += "\nAliases: " + ", ".join([get_format_info(x).format_inline() for x in l_other_alias_ids])

    return msg

Detail any aliases the format has and what properties it supports

def format_word(self)
Expand source code
def format_word(self):
    """Use the disambiguated name for the formatted name"""
    return f"{tc.BOLD}{self.disambiguated_name}{tc.OFF}"

Use the disambiguated name for the formatted name

Inherited members

class OptionInfo (id: int = <property object>,
name: str = <property object>,
description: str = <property object>,
info: str = <property object>,
parent: DataConversionDatabase | DBInfo | None = None,
s_in_formats: set[int] = <factory>,
s_out_formats: set[int] = <factory>,
brief: str = '')
Expand source code
@dataclass
class OptionInfo(ArgInfo):
    """Class providing information on an option accepted by a converter (an argument accepts a value)
    """
    # We need to provide a default argument here, since it will come after the sets with default arguments in ArgInfo
    brief: str = ""

    # __hash__ needs to be inherited explicitly for dataclasses since they redefine __eq__
    __hash__ = DBInfo.__hash__

Class providing information on an option accepted by a converter (an argument accepts a value)

Ancestors

Instance variables

var brief : str

Inherited members

class PropertyConversionInfo (key: str, input_supported: bool | None, output_supported: bool | None)
Expand source code
@dataclass
class PropertyConversionInfo:
    """Class representing whether a given property is present in the input and/out output file formats, and a note on
    what its presence or absence means
    """
    key: str
    input_supported: bool | None
    output_supported: bool | None
    label: str = field(init=False)
    description: str = field(init=False)

    def __post_init__(self):
        """Set the label and note based on input/output status
        """
        self.label = D_FORMAT_PROPERTY_ATTRS[self.key]

        if self.input_supported is None and self.output_supported is None:
            self.description = const.QUAL_NOTE_BOTH_UNKNOWN
        elif self.input_supported is None and self.output_supported is not None:
            self.description = const.QUAL_NOTE_IN_UNKNOWN
        elif self.input_supported is not None and self.output_supported is None:
            self.description = const.QUAL_NOTE_OUT_UNKNOWN
        elif self.input_supported == self.output_supported:
            self.description = ""
        elif self.input_supported:
            self.description = const.QUAL_NOTE_OUT_MISSING
        else:
            self.description = const.QUAL_NOTE_IN_MISSING

        if self.description:
            self.description = self.description.format(self.label)

Class representing whether a given property is present in the input and/out output file formats, and a note on what its presence or absence means

Instance variables

var description : str
var input_supported : bool | None
var key : str
var label : str
var output_supported : bool | None