Skip to content

Commit a45e25e

Browse files
authored
Replace %/.format/concatenation with f-strings where feasible (#927)
Original conversion suggestions via flynt, edited by hand.
1 parent 896c2ea commit a45e25e

24 files changed

Lines changed: 161 additions & 204 deletions

babel/core.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def __init__(self, identifier):
9898
9999
:param identifier: the identifier string of the unsupported locale
100100
"""
101-
Exception.__init__(self, 'unknown locale %r' % identifier)
101+
Exception.__init__(self, f"unknown locale {identifier!r}")
102102

103103
#: The identifier of the locale that could not be found.
104104
self.identifier = identifier
@@ -262,7 +262,7 @@ def parse(cls, identifier, sep='_', resolve_likely_subtags=True):
262262
elif isinstance(identifier, Locale):
263263
return identifier
264264
elif not isinstance(identifier, str):
265-
raise TypeError('Unexpected value for identifier: %r' % (identifier,))
265+
raise TypeError(f"Unexpected value for identifier: {identifier!r}")
266266

267267
parts = parse_locale(identifier, sep=sep)
268268
input_id = get_locale_identifier(parts)
@@ -349,9 +349,8 @@ def __repr__(self):
349349
for key in ('territory', 'script', 'variant'):
350350
value = getattr(self, key)
351351
if value is not None:
352-
parameters.append('%s=%r' % (key, value))
353-
parameter_string = '%r' % self.language + ', '.join(parameters)
354-
return 'Locale(%s)' % parameter_string
352+
parameters.append(f"{key}={value!r}")
353+
return f"Locale({self.language!r}{', '.join(parameters)})"
355354

356355
def __str__(self):
357356
return get_locale_identifier((self.language, self.territory,
@@ -388,7 +387,7 @@ def get_display_name(self, locale=None):
388387
details.append(locale.variants.get(self.variant))
389388
details = filter(None, details)
390389
if details:
391-
retval += ' (%s)' % u', '.join(details)
390+
retval += f" ({', '.join(details)})"
392391
return retval
393392

394393
display_name = property(get_display_name, doc="""\
@@ -1120,7 +1119,7 @@ def parse_locale(identifier, sep='_'):
11201119
parts = identifier.split(sep)
11211120
lang = parts.pop(0).lower()
11221121
if not lang.isalpha():
1123-
raise ValueError('expected only letters, got %r' % lang)
1122+
raise ValueError(f"expected only letters, got {lang!r}")
11241123

11251124
script = territory = variant = None
11261125
if parts:
@@ -1139,7 +1138,7 @@ def parse_locale(identifier, sep='_'):
11391138
variant = parts.pop().upper()
11401139

11411140
if parts:
1142-
raise ValueError('%r is not a valid locale identifier' % identifier)
1141+
raise ValueError(f"{identifier!r} is not a valid locale identifier")
11431142

11441143
return lang, territory, script, variant
11451144

babel/dates.py

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def get_timezone(zone=None):
203203
try:
204204
return _pytz.timezone(zone)
205205
except _pytz.UnknownTimeZoneError:
206-
raise LookupError('Unknown timezone %s' % zone)
206+
raise LookupError(f"Unknown timezone {zone}")
207207

208208

209209
def get_next_timezone_transition(zone=None, dt=None):
@@ -312,11 +312,7 @@ def to_offset(self):
312312
return int(self.to_tzinfo._utcoffset.total_seconds())
313313

314314
def __repr__(self):
315-
return '<TimezoneTransition %s -> %s (%s)>' % (
316-
self.from_tz,
317-
self.to_tz,
318-
self.activates,
319-
)
315+
return f"<TimezoneTransition {self.from_tz} -> {self.to_tz} ({self.activates})>"
320316

321317

322318
def get_period_names(width='wide', context='stand-alone', locale=LC_TIME):
@@ -958,7 +954,7 @@ def _iter_patterns(a_unit):
958954
yield unit_rel_patterns['future']
959955
else:
960956
yield unit_rel_patterns['past']
961-
a_unit = 'duration-' + a_unit
957+
a_unit = f"duration-{a_unit}"
962958
yield locale._data['unit_patterns'].get(a_unit, {}).get(format)
963959

964960
for unit, secs_per_unit in TIMEDELTA_UNITS:
@@ -1293,7 +1289,7 @@ def __init__(self, pattern, format):
12931289
self.format = format
12941290

12951291
def __repr__(self):
1296-
return '<%s %r>' % (type(self).__name__, self.pattern)
1292+
return f"<{type(self).__name__} {self.pattern!r}>"
12971293

12981294
def __str__(self):
12991295
pat = self.pattern
@@ -1365,7 +1361,7 @@ def __getitem__(self, name):
13651361
elif char in ('z', 'Z', 'v', 'V', 'x', 'X', 'O'):
13661362
return self.format_timezone(char, num)
13671363
else:
1368-
raise KeyError('Unsupported date/time field %r' % char)
1364+
raise KeyError(f"Unsupported date/time field {char!r}")
13691365

13701366
def extract(self, char):
13711367
char = str(char)[0]
@@ -1384,7 +1380,7 @@ def extract(self, char):
13841380
elif char == 'a':
13851381
return int(self.value.hour >= 12) # 0 for am, 1 for pm
13861382
else:
1387-
raise NotImplementedError("Not implemented: extracting %r from %r" % (char, self.value))
1383+
raise NotImplementedError(f"Not implemented: extracting {char!r} from {self.value!r}")
13881384

13891385
def format_era(self, char, num):
13901386
width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)]
@@ -1429,7 +1425,7 @@ def format_week(self, char, num):
14291425
if week == 0:
14301426
date = self.value - timedelta(days=self.value.day)
14311427
week = self.get_week_number(date.day, date.weekday())
1432-
return '%d' % week
1428+
return str(week)
14331429

14341430
def format_weekday(self, char='E', num=4):
14351431
"""
@@ -1475,7 +1471,7 @@ def format_day_of_year(self, num):
14751471
return self.format(self.get_day_of_year(), num)
14761472

14771473
def format_day_of_week_in_month(self):
1478-
return '%d' % ((self.value.day - 1) // 7 + 1)
1474+
return str((self.value.day - 1) // 7 + 1)
14791475

14801476
def format_period(self, char, num):
14811477
"""
@@ -1517,7 +1513,7 @@ def format_period(self, char, num):
15171513
period_names = get_period_names(context=context, width=width, locale=self.locale)
15181514
if period in period_names:
15191515
return period_names[period]
1520-
raise ValueError('Could not format period %s in %s' % (period, self.locale))
1516+
raise ValueError(f"Could not format period {period} in {self.locale}")
15211517

15221518
def format_frac_seconds(self, num):
15231519
""" Return fractional seconds.
@@ -1689,11 +1685,10 @@ def parse_pattern(pattern):
16891685
fieldchar, fieldnum = tok_value
16901686
limit = PATTERN_CHARS[fieldchar]
16911687
if limit and fieldnum not in limit:
1692-
raise ValueError('Invalid length for field: %r'
1693-
% (fieldchar * fieldnum))
1688+
raise ValueError(f"Invalid length for field: {fieldchar * fieldnum!r}")
16941689
result.append('%%(%s)s' % (fieldchar * fieldnum))
16951690
else:
1696-
raise NotImplementedError("Unknown token type: %s" % tok_type)
1691+
raise NotImplementedError(f"Unknown token type: {tok_type}")
16971692

16981693
_pattern_cache[pattern] = pat = DateTimePattern(pattern, u''.join(result))
16991694
return pat

babel/lists.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,10 @@ def format_list(lst, style='standard', locale=DEFAULT_LOCALE):
6868
return lst[0]
6969

7070
if style not in locale.list_patterns:
71-
raise ValueError('Locale %s does not support list formatting style %r (supported are %s)' % (
72-
locale,
73-
style,
74-
list(sorted(locale.list_patterns)),
75-
))
71+
raise ValueError(
72+
f'Locale {locale} does not support list formatting style {style!r} '
73+
f'(supported are {sorted(locale.list_patterns)})'
74+
)
7675
patterns = locale.list_patterns[style]
7776

7877
if len(lst) == 2:

babel/localedata.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ def resolve_locale_filename(name):
5050

5151
# Ensure we're not left with one of the Windows reserved names.
5252
if sys.platform == "win32" and _windows_reserved_name_re.match(os.path.splitext(name)[0]):
53-
raise ValueError("Name %s is invalid on Windows" % name)
53+
raise ValueError(f"Name {name} is invalid on Windows")
5454

5555
# Build the path.
56-
return os.path.join(_dirname, '%s.dat' % name)
56+
return os.path.join(_dirname, f"{name}.dat")
5757

5858

5959
def exists(name):
@@ -194,7 +194,7 @@ def __init__(self, keys):
194194
self.keys = tuple(keys)
195195

196196
def __repr__(self):
197-
return '<%s %r>' % (type(self).__name__, self.keys)
197+
return f"<{type(self).__name__} {self.keys!r}>"
198198

199199
def resolve(self, data):
200200
"""Resolve the alias based on the given data.

babel/localtime/_win32.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ def get_localzone_name():
7777
if timezone is None:
7878
# Nope, that didn't work. Try adding 'Standard Time',
7979
# it seems to work a lot of times:
80-
timezone = tz_names.get(tzkeyname + ' Standard Time')
80+
timezone = tz_names.get(f"{tzkeyname} Standard Time")
8181

8282
# Return what we have.
8383
if timezone is None:
84-
raise pytz.UnknownTimeZoneError('Can not find timezone ' + tzkeyname)
84+
raise pytz.UnknownTimeZoneError(f"Can not find timezone {tzkeyname}")
8585

8686
return timezone
8787

babel/messages/catalog.py

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def _parse_datetime_header(value):
4949
hours_offset_s, mins_offset_s = rest[:2], rest[2:]
5050

5151
# Make them all integers
52-
plus_minus = int(plus_minus_s + '1')
52+
plus_minus = int(f"{plus_minus_s}1")
5353
hours_offset = int(hours_offset_s)
5454
mins_offset = int(mins_offset_s)
5555

@@ -108,8 +108,7 @@ def __init__(self, id, string=u'', locations=(), flags=(), auto_comments=(),
108108
self.context = context
109109

110110
def __repr__(self):
111-
return '<%s %r (flags: %r)>' % (type(self).__name__, self.id,
112-
list(self.flags))
111+
return f"<{type(self).__name__} {self.id!r} (flags: {list(self.flags)!r})>"
113112

114113
def __cmp__(self, other):
115114
"""Compare Messages, taking into account plural ids"""
@@ -312,7 +311,7 @@ def _set_locale(self, locale):
312311
self._locale = None
313312
return
314313

315-
raise TypeError('`locale` must be a Locale, a locale identifier string, or None; got %r' % locale)
314+
raise TypeError(f"`locale` must be a Locale, a locale identifier string, or None; got {locale!r}")
316315

317316
def _get_locale(self):
318317
return self._locale
@@ -334,7 +333,7 @@ def _get_header_comment(self):
334333
.replace('ORGANIZATION', self.copyright_holder)
335334
locale_name = (self.locale.english_name if self.locale else self.locale_identifier)
336335
if locale_name:
337-
comment = comment.replace('Translations template', '%s translations' % locale_name)
336+
comment = comment.replace("Translations template", f"{locale_name} translations")
338337
return comment
339338

340339
def _set_header_comment(self, string):
@@ -375,8 +374,7 @@ def _set_header_comment(self, string):
375374

376375
def _get_mime_headers(self):
377376
headers = []
378-
headers.append(('Project-Id-Version',
379-
'%s %s' % (self.project, self.version)))
377+
headers.append(("Project-Id-Version", f"{self.project} {self.version}"))
380378
headers.append(('Report-Msgid-Bugs-To', self.msgid_bugs_address))
381379
headers.append(('POT-Creation-Date',
382380
format_datetime(self.creation_date, 'yyyy-MM-dd HH:mmZ',
@@ -399,10 +397,9 @@ def _get_mime_headers(self):
399397
if self.locale is not None:
400398
headers.append(('Plural-Forms', self.plural_forms))
401399
headers.append(('MIME-Version', '1.0'))
402-
headers.append(('Content-Type',
403-
'text/plain; charset=%s' % self.charset))
400+
headers.append(("Content-Type", f"text/plain; charset={self.charset}"))
404401
headers.append(('Content-Transfer-Encoding', '8bit'))
405-
headers.append(('Generated-By', 'Babel %s\n' % VERSION))
402+
headers.append(("Generated-By", f"Babel {VERSION}\n"))
406403
return headers
407404

408405
def _force_text(self, s, encoding='utf-8', errors='strict'):
@@ -434,7 +431,7 @@ def _set_mime_headers(self, headers):
434431
if 'charset' in params:
435432
self.charset = params['charset'].lower()
436433
elif name == 'plural-forms':
437-
params = parse_separated_header(' ;' + value)
434+
params = parse_separated_header(f" ;{value}")
438435
self._num_plurals = int(params.get('nplurals', 2))
439436
self._plural_expr = params.get('plural', '(n != 1)')
440437
elif name == 'pot-creation-date':
@@ -541,7 +538,7 @@ def plural_forms(self):
541538
'nplurals=2; plural=(n > 1);'
542539
543540
:type: `str`"""
544-
return 'nplurals=%s; plural=%s;' % (self.num_plurals, self.plural_expr)
541+
return f"nplurals={self.num_plurals}; plural={self.plural_expr};"
545542

546543
def __contains__(self, id):
547544
"""Return whether the catalog has a message with the specified ID."""
@@ -560,7 +557,7 @@ def __iter__(self):
560557
:rtype: ``iterator``"""
561558
buf = []
562559
for name, value in self.mime_headers:
563-
buf.append('%s: %s' % (name, value))
560+
buf.append(f"{name}: {value}")
564561
flags = set()
565562
if self.fuzzy:
566563
flags |= {'fuzzy'}
@@ -571,8 +568,8 @@ def __iter__(self):
571568
def __repr__(self):
572569
locale = ''
573570
if self.locale:
574-
locale = ' %s' % self.locale
575-
return '<%s %r%s>' % (type(self).__name__, self.domain, locale)
571+
locale = f" {self.locale}"
572+
return f"<{type(self).__name__} {self.domain!r}{locale}>"
576573

577574
def __delitem__(self, id):
578575
"""Delete the message with the specified ID."""
@@ -626,13 +623,12 @@ def __setitem__(self, id, message):
626623
elif id == '':
627624
# special treatment for the header message
628625
self.mime_headers = message_from_string(message.string).items()
629-
self.header_comment = '\n'.join([('# %s' % c).rstrip() for c
630-
in message.user_comments])
626+
self.header_comment = "\n".join([f"# {c}".rstrip() for c in message.user_comments])
631627
self.fuzzy = message.fuzzy
632628
else:
633629
if isinstance(id, (list, tuple)):
634630
assert isinstance(message.string, (list, tuple)), \
635-
'Expected sequence but got %s' % type(message.string)
631+
f"Expected sequence but got {type(message.string)}"
636632
self._messages[key] = message
637633

638634
def add(self, id, string=None, locations=(), flags=(), auto_comments=(),

babel/messages/checkers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def _check_positional(results):
144144
type_map = dict(a)
145145
for name, typechar in b:
146146
if name not in type_map:
147-
raise TranslationError('unknown named placeholder %r' % name)
147+
raise TranslationError(f'unknown named placeholder {name!r}')
148148
elif not _compatible(typechar, type_map[name]):
149149
raise TranslationError('incompatible format for '
150150
'placeholder %r: '

babel/messages/extract.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@
4242

4343
DEFAULT_MAPPING = [('**.py', 'python')]
4444

45-
empty_msgid_warning = (
46-
'%s: warning: Empty msgid. It is reserved by GNU gettext: gettext("") '
47-
'returns the header entry with meta information, not the empty string.')
4845

4946

5047
def _strip_comment_tags(comments, tags):
@@ -332,7 +329,7 @@ def extract(method, fileobj, keywords=DEFAULT_KEYWORDS, comment_tags=(),
332329
func = builtin.get(method)
333330

334331
if func is None:
335-
raise ValueError('Unknown extraction method %r' % method)
332+
raise ValueError(f"Unknown extraction method {method!r}")
336333

337334
results = func(fileobj, keywords.keys(), comment_tags,
338335
options=options or {})
@@ -377,9 +374,11 @@ def extract(method, fileobj, keywords=DEFAULT_KEYWORDS, comment_tags=(),
377374
first_msg_index = spec[0] - 1
378375
if not messages[first_msg_index]:
379376
# An empty string msgid isn't valid, emit a warning
380-
where = '%s:%i' % (hasattr(fileobj, 'name') and
381-
fileobj.name or '(unknown)', lineno)
382-
sys.stderr.write((empty_msgid_warning % where) + '\n')
377+
filename = (getattr(fileobj, "name", None) or "(unknown)")
378+
sys.stderr.write(
379+
f"{filename}:{lineno}: warning: Empty msgid. It is reserved by GNU gettext: gettext(\"\") "
380+
f"returns the header entry with meta information, not the empty string.\n"
381+
)
383382
continue
384383

385384
messages = tuple(msgs)

0 commit comments

Comments
 (0)