fmt_migrate Python library for migrating to str.format()
Code that accompanies my PyCon 2010 talk on str.format()
fmt_migrate.py
—
Python Source,
4Kb
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()

