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.weightGet the converter weight for a conversion from
in_format_infotoout_format_infowith converterconverter_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_UNKNOWNGet the format weight for a conversion from
in_format_infotoout_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_lossGet the precision weight for a conversion from
in_format_infotoout_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_weightGet the property weight for a conversion from
in_format_infotoout_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
Noneif no conversion pathway is possible or if the input and output formats are the same. Otherwise, will return aConversionPath, which is a list ofConversionsteps in the pathway, each being aNamedTupleof: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
ConversionQualityInfoobject 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
converteris provided, will return a singleConverterInfo(or raise an exception if the name is invalid). If not provided, a list of allConverterInfoobjects in the database will be returned
Raises
FileConverterDatabaseException- If
nameis 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 _databaseGets 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_filenameGet 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
argnot 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
argprovided) 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
argnot 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
argprovided) 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 theincludecriterion:"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
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.nameClass 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.nameDEPRECATED: 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 nameGet 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 TrueWhether 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
ConversionNamedTuple 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.converterThe 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_formatThe 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_formatThe 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: intConversionWeightParts(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: intAlias 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: intAlias 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: intAlias 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: intAlias 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 parentParameters
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
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
ConversionQualityInfoobject 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_pathsAs
get_conversion_pathway(), but instead of returning just one pathway, returns a list of pathways meeting theincludecriterion:"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_conversionsGet 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_infoClass 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_infoGenerate 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_infoGenerate 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_infoGenerate 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_infoGenerate 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_infoGenerate 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_infoGenerate 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_infoGenerate 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_infoGenerate 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 : boolvar supported : boolvar url : strvar 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 msgInclude 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_infoGet 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_infoGet 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._descriptionA 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._idThe 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._infoAn 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._nameThe short name of the object
var parent : DataConversionDatabase | DBInfo | Noneprop 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 msgReturn 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_infoClass 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_tableGenerates 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_nameDEPRECATED: 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_idGenerate 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_nameGenerate 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_nameDEPRECATED: 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_idGenerate 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_nameGenerate 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_infoGenerate 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_infoGenerate 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
converteris provided, will return a singleConverterInfo(or raise an exception if the name is invalid). If not provided, a list of allConverterInfoobjects in the database will be returned
Raises
If
nameis 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_infoGets 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
FileConverterDatabaseExceptionwill 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 """ passClass for any exceptions which arise from issues with the database classes and methods
Ancestors
- FileConverterException
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
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_infoFactory function to set up the class - this will be initialised within a
DataConversionDatabase, which we set as the parentParameters
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_infoClass 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_infoFactory 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_infoof 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.compositionWhether 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.connectionsWhether 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_extsDict 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_namesDict 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.descriptionDEPRECATED: 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.precisionThe 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_idThe 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_nameThe 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_dimWhether 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_dimWhether 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.weightThe 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 msgDetail 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 : strvar input_supported : bool | Nonevar key : strvar label : strvar output_supported : bool | None