Skip to content. | Skip to navigation

Personal tools
You are here: Home Knowledge Downloads fmt_migrate Python library for migrating to str.format()

fmt_migrate Python library for migrating to str.format()

Code that accompanies my PyCon 2010 talk on str.format()

fmt_migrate.py — Python Source, 4 kB (4738 bytes)

File contents

########################################################################
# A library for migrating libraries using %-formatting to instead use
#  str.format(). Presented at PyCon 2010: http://trueblade.com/pycon2010
#
# Author: Eric V. Smith
#
# Copyright: 2010, True Blade Systems, Inc.
#
# Notes:
#  I'm still working on refining the guessing.
#
# Change history:
#  20100215: eric: Initial version.
########################################################################

########################################################################
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA
########################################################################

def expand_str_mapping(fmt, kwargs):
    '''fmt is either a %-formatting format string, or a
       str.format() format string. We guess which one and call
       the appropriate routine.
       kwargs is a mapping (probably a dictionary) used to expand
       the format string.'''

    t = _is_definitely_percent_mapping(fmt)
    if t is True:
        # It's definitely a %-formatting format string
        return fmt % kwargs
    if t is False:
        # It's definitely a str.format() format string
        return fmt.format(**kwargs)

    # We'll need to guess. There are pathological cases like
    #  "{abc:%(abc)s}" that are valid for both, so in general
    #  it's impossible to get this exactly correct. But we can
    #  do pretty good.
    t = _is_probably_percent_mapping(fmt, kwargs)
    if t is True:
        return fmt % kwargs
    if t is False:
        return fmt.format(**kwargs)

    # We really have to guess. Guess str.format first.
    try:
        return fmt.format(**kwargs)
    except TypeError:
        return fmt % kwargs


def _is_definitely_percent_mapping(fmt):
    if '%(' in fmt and '{' not in fmt:
        return True
    if '{' in fmt and '%(' not in fmt:
        return False
    # Has both, need to get fancier to figure it out
    return None


def _is_probably_percent_mapping(fmt, kwargs):
    # These counts and tests are approximate, unless we go through the
    #  hassle of actually parsing the format string. Let's see how good
    #  we can get without doing that.

    # Count the number of each type of placeholder, less the escaped ones
    n_percent = fmt.count('%(') - fmt.count('%%(')
    n_brace = fmt.count('{') - fmt.count('{{')

    # Make a decision based on the number of them
    if n_percent > n_brace:
        if n_percent == len(kwargs):
            return True
    elif n_brace > n_percent:
        if n_brace == len(kwargs):
            return False

    return None


if __name__ == '__main__':
    import unittest

    import datetime

    class TestMapping(unittest.TestCase):
        def test_percent(self):
            self.assertEqual(expand_str_mapping('%(foo)s', {'foo' : 1}), '1')

        def test_braces(self):
            self.assertEqual(expand_str_mapping('{foo}', {'foo' : 1}), '1')

            d = datetime.datetime(2010, 2, 14)
            self.assertEqual(expand_str_mapping('{d:%Y}', {'d':d}), '2010')
            self.assertEqual(expand_str_mapping('{d:%(Y}', {'d':d}), '(Y')

    class TestMappingDefiniteGuesser(unittest.TestCase):
        def test_definite_precent(self):
            f = _is_definitely_percent_mapping
            self.assertIs(f('%(foo)s'), True)
            self.assertIs(f('-- %(foo)s --}'), True)

        def test_definite_braces(self):
            f = _is_definitely_percent_mapping
            self.assertIs(f('{foo}'), False)
            self.assertIs(f('-- %(foo)s --}'), True)

        def test_is_not_definite(self):
            f = _is_definitely_percent_mapping
            self.assertIs(f('test'), None)
            self.assertIs(f('-- %s --}'), None)
            self.assertIs(f(''), None)

    class TestMappingProbablyGuesser(unittest.TestCase):
        def test_braces(self):
            f = _is_probably_percent_mapping
            self.assertIs(f('{:%Y}', {1:1}), False)


    class TestTuple(unittest.TestCase):
        def test_percent(self):
            pass


    unittest.main()
Document Actions
« May 2017 »
May
SuMoTuWeThFrSa
123456
78910111213
14151617181920
21222324252627
28293031