Skip to content

Commit 5fb1575

Browse files
committed
Final fix for bpo-9741, with tests proving it.
Also show that it is now possible to build multiple po files in one single script call.
1 parent 73b5ac8 commit 5fb1575

File tree

2 files changed

+76
-28
lines changed

2 files changed

+76
-28
lines changed

Lib/test/test_tools/test_i18n.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,35 @@ def test_both_with_outputfile(self):
424424
'file1_fr.po', 'file2_fr.po')
425425
with open('file1.mo', 'rb') as fin:
426426
self.assertEqual(self.file12_fr_mo, fin.read())
427+
428+
def test_both_without_outputfile(self):
429+
"""Test script without -o option and 2 input files"""
430+
# msgfmt.py version 1.2 behaviour was that second mo file
431+
# also merged previous po files
432+
with temp_cwd(None):
433+
with open("file1_fr.po", "wb") as out:
434+
out.write(self.file1_fr_po)
435+
with open("file2_fr.po", "wb") as out:
436+
out.write(self.file2_fr_po)
437+
assert_python_ok(self.script, 'file1_fr.po', 'file2_fr.po')
438+
with open('file1_fr.mo', 'rb') as fin:
439+
self.assertEqual(self.file1_fr_mo, fin.read())
440+
with open('file2_fr.mo', 'rb') as fin:
441+
self.assertEqual(self.file2_fr_mo, fin.read())
442+
443+
def test_consecutive_make_calls(self):
444+
"""Directly calls make twice to prove bpo-9741 is fixed"""
445+
sys.path.append(os.path.join(toolsdir,'i18n'))
446+
from msgfmt import make
447+
with temp_cwd(None):
448+
with open("file1_fr.po", "wb") as out:
449+
out.write(self.file1_fr_po)
450+
with open("file2_fr.po", "wb") as out:
451+
out.write(self.file2_fr_po)
452+
make("file1_fr.po", "file1_fr.mo")
453+
make("file2_fr.po", "file2_fr.mo")
454+
with open('file1_fr.mo', 'rb') as fin:
455+
self.assertEqual(self.file1_fr_mo, fin.read())
456+
with open('file2_fr.mo', 'rb') as fin:
457+
self.assertEqual(self.file2_fr_mo, fin.read())
458+
sys.path.pop()

Tools/i18n/msgfmt.py

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#! /usr/bin/env python3
22
# Written by Martin v. Löwis <[email protected]>
3-
3+
# Version 1.3 by s-ball <[email protected]>
44
"""Generate binary message catalog from textual translation description.
55
66
This program converts a textual Uniforum-style message catalog (.po file) into
@@ -33,7 +33,7 @@
3333
import array
3434
from email.parser import HeaderParser
3535

36-
__version__ = "1.2"
36+
__version__ = "1.3"
3737

3838
MESSAGES = {}
3939

@@ -45,29 +45,27 @@ def usage(code, msg=''):
4545
sys.exit(code)
4646

4747

48-
def add(ctxt, id, str, fuzzy):
48+
def add(ctxt, id, str, fuzzy, messages):
4949
"Add a non-fuzzy translation to the dictionary."
50-
global MESSAGES
5150
if not fuzzy and str:
5251
if ctxt is None:
53-
MESSAGES[id] = str
52+
messages[id] = str
5453
else:
55-
MESSAGES[b"%b\x04%b" % (ctxt, id)] = str
54+
messages[b"%b\x04%b" % (ctxt, id)] = str
5655

5756

58-
def generate():
57+
def generate(messages):
5958
"Return the generated output."
60-
global MESSAGES
6159
# the keys are sorted in the .mo file
62-
keys = sorted(MESSAGES.keys())
60+
keys = sorted(messages.keys())
6361
offsets = []
6462
ids = strs = b''
6563
for id in keys:
6664
# For each string, we need size and file offset. Each string is NUL
6765
# terminated; the NUL does not count into the size.
68-
offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id])))
66+
offsets.append((len(ids), len(id), len(strs), len(messages[id])))
6967
ids += id + b'\0'
70-
strs += MESSAGES[id] + b'\0'
68+
strs += messages[id] + b'\0'
7169
output = ''
7270
# The header is 7 32-bit unsigned integers. We don't use hash tables, so
7371
# the keys start right after the index tables.
@@ -96,18 +94,40 @@ def generate():
9694
return output
9795

9896

99-
def make(filename, outfile):
100-
ID = 1
101-
STR = 2
102-
CTXT = 3
97+
def make(filenames, outfile):
98+
messages = {}
99+
if isinstance(filenames, str):
100+
infile, outfile = get_names(filenames, outfile)
101+
process(infile, messages)
102+
elif outfile is None:
103+
for filename in filenames:
104+
infile, outfile = get_names(filename, None)
105+
messages.clear()
106+
process(infile, messages)
107+
output = generate(messages)
108+
writefile(outfile, output)
109+
return
110+
else:
111+
for filename in filenames:
112+
infile, _ = get_names(filename, outfile)
113+
process(infile, messages)
114+
output = generate(messages)
115+
writefile(outfile, output)
103116

117+
def get_names(filename, outfile):
104118
# Compute .mo name from .po name and arguments
105119
if filename.endswith('.po'):
106120
infile = filename
107121
else:
108122
infile = filename + '.po'
109123
if outfile is None:
110124
outfile = os.path.splitext(infile)[0] + '.mo'
125+
return infile, outfile
126+
127+
def process(infile, messages):
128+
ID = 1
129+
STR = 2
130+
CTXT = 3
111131

112132
try:
113133
with open(infile, 'rb') as f:
@@ -130,7 +150,7 @@ def make(filename, outfile):
130150
lno += 1
131151
# If we get a comment line after a msgstr, this is a new entry
132152
if l[0] == '#' and section == STR:
133-
add(msgctxt, msgid, msgstr, fuzzy)
153+
add(msgctxt, msgid, msgstr, fuzzy, messages)
134154
section = msgctxt = None
135155
fuzzy = 0
136156
# Record a fuzzy mark
@@ -142,13 +162,13 @@ def make(filename, outfile):
142162
# Now we are in a msgid or msgctxt section, output previous section
143163
if l.startswith('msgctxt'):
144164
if section == STR:
145-
add(msgctxt, msgid, msgstr, fuzzy)
165+
add(msgctxt, msgid, msgstr, fuzzy, messages)
146166
section = CTXT
147167
l = l[7:]
148168
msgctxt = b''
149169
elif l.startswith('msgid') and not l.startswith('msgid_plural'):
150170
if section == STR:
151-
add(msgctxt, msgid, msgstr, fuzzy)
171+
add(msgctxt, msgid, msgstr, fuzzy, messages)
152172
if not msgid:
153173
# See whether there is an encoding declaration
154174
p = HeaderParser()
@@ -203,21 +223,19 @@ def make(filename, outfile):
203223
sys.exit(1)
204224
# Add last entry
205225
if section == STR:
206-
add(msgctxt, msgid, msgstr, fuzzy)
207-
208-
# Compute output
209-
output = generate()
226+
add(msgctxt, msgid, msgstr, fuzzy, messages)
210227

228+
def writefile(outfile, output):
211229
try:
212230
with open(outfile,"wb") as f:
213231
f.write(output)
214232
except IOError as msg:
215233
print(msg, file=sys.stderr)
216234

217235

218-
def main():
236+
def main(argv):
219237
try:
220-
opts, args = getopt.getopt(sys.argv[1:], 'hVo:',
238+
opts, args = getopt.getopt(argv, 'hVo:',
221239
['help', 'version', 'output-file='])
222240
except getopt.error as msg:
223241
usage(1, msg)
@@ -237,10 +255,8 @@ def main():
237255
print('No input file given', file=sys.stderr)
238256
print("Try `msgfmt --help' for more information.", file=sys.stderr)
239257
return
240-
241-
for filename in args:
242-
make(filename, outfile)
258+
make(args, outfile)
243259

244260

245261
if __name__ == '__main__':
246-
main()
262+
main(sys.argv[1:])

0 commit comments

Comments
 (0)