Search code examples
pythonpython-3.xformatting

Python: convert old style formatting pattern to new style formatting pattern


Is there some "built-in" way to convert old style formatting pattern strings to new style formatting. Or its better to just use good old regex for it?

For example, we have pattern '%(a)s - %(b)s' and want to convert to '{a} - {b}'


Solution

  • Did not found any already built int solutions for converting formatting. Here is how I implemented using regex.

    import re    
    
    ODD_REPEAT_PATTERN = r'((?<!{c}){c}({c}{c})*(?!{c}))'
    EVEN_REPEAT_PATTERN = r'(?<!{c})({c}{c})+(?!{c})'
    
    def __to_new_format(fmt: str, named=True):
        def to_named_fmt(fmt):
            pattern = rf'{odd_perc_pattern}\((.*?)\)s'
            match = re.search(pattern, fmt)
            while match:
                # Only care about placeholder group here.
                __, __, placeholder = match.groups()
                fmt = fmt.replace(
                    f'%({placeholder})s',
                    f'{{{placeholder}}}'
                )
                match = re.search(pattern, fmt)
            return fmt
    
        def to_pos_fmt(fmt):
            even_perc_pattern = EVEN_REPEAT_PATTERN.format(c='%')
            pattern = rf'{even_perc_pattern}s'
            # When positional placeholder has even amount of percents, it
            # will be treated as not having enough arguments passed.
            if re.search(pattern, fmt):
                raise TypeError(
                    'not all arguments converted during string formatting'
                )
            return fmt.replace('%s', '{}')
    
        odd_perc_pattern = ODD_REPEAT_PATTERN.format(c='%')
        # Escape `{` and `}`, because new formatting uses it.
        fmt = fmt.replace('{', '{{').replace('}', '}}')
        fmt = to_named_fmt(fmt) if named else to_pos_fmt(fmt)
        # If we find odd number of occurring percentage symbols, it means
        # those were not escaped and we can't finish conversion.
        if re.search(odd_perc_pattern, fmt):
            raise ValueError('incomplete format')
        return fmt.replace('%%', '%')
    
    
    def to_new_named_format(fmt: str) -> str:
        """Convert old style named formatting to new style formatting.
    
        For example: '%(x)s - %%%(y)s' -> '{x} - %{y}'
    
        Args:
            fmt: old style formatting to convert.
    
        Returns:
            new style formatting.
    
        """
        return __to_new_format(fmt, named=True)
    
    
    def to_new_pos_format(fmt: str) -> str:
        """Convert old style positional formatting to new style formatting.
    
        For example: '%s - %%%s' -> '{} - %{}'
    
        Args:
            fmt: old style formatting to convert.
    
        Returns:
            new style formatting.
    
        """
        return __to_new_format(fmt, named=False)