Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(77)

Delta Between Two Patch Sets: translate/storage/cpo.py

Issue 81: Fix CPO memory leak SVN Base: https://translate.svn.sourceforge.net/svnroot/translate/src/trunk/
Left Patch Set: Fix addlocation method Created 1 year, 3 months ago
Right Patch Set: Created 1 year, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: View regular side by side diff
Right: View regular side by side diff
LEFTRIGHT
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 # 3 #
4 # Copyright 2002-2007 Zuza Software Foundation 4 # Copyright 2002-2007 Zuza Software Foundation
5 # 5 #
6 # This file is part of translate. 6 # This file is part of translate.
7 # 7 #
8 # translate is free software; you can redistribute it and/or modify 8 # translate is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by 9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or 10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version. 11 # (at your option) any later version.
12 # 12 #
13 # translate is distributed in the hope that it will be useful, 13 # translate is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details. 16 # GNU General Public License for more details.
17 # 17 #
18 # You should have received a copy of the GNU General Public License 18 # You should have received a copy of the GNU General Public License
19 # along with translate; if not, write to the Free Software 19 # along with translate; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 21
22 """Classes that hold units of .po files (pounit) or entire files (pofile). 22 """Classes that hold units of .po files (pounit) or entire files (pofile).
23 23
24 Gettext-style .po (or .pot) files are used in translations for KDE, GNOME and 24 Gettext-style .po (or .pot) files are used in translations for KDE, GNOME and
25 many other projects. 25 many other projects.
26 26
27 This uses libgettextpo from the gettext package. Any version before 0.17 will 27 This uses libgettextpo from the gettext package. Any version before 0.17 will
28 at least cause some subtle bugs or may not work at all. Developers might want 28 at least cause some subtle bugs or may not work at all. Developers might want
29 to have a look at gettext-tools/libgettextpo/gettext-po.h from the gettext 29 to have a look at gettext-tools/libgettextpo/gettext-po.h from the gettext
30 package for the public API of the library. 30 package for the public API of the library.
31 """ 31 """
32 32
33 from translate.misc.multistring import multistring 33 from translate.misc.multistring import multistring
34 from translate.storage import pocommon 34 from translate.storage import pocommon
35 from translate.misc import quote 35 from translate.misc import quote
36 from translate.lang import data 36 from translate.lang import data
37 from ctypes import * 37 from ctypes import *
38 import ctypes.util 38 import ctypes.util
39 try: 39 try:
40 import cStringIO as StringIO 40 import cStringIO as StringIO
41 except ImportError: 41 except ImportError:
42 import StringIO 42 import StringIO
43 import os 43 import os
44 import pypo 44 import pypo
45 import re 45 import re
46 import sys 46 import sys
47 import tempfile 47 import tempfile
48 48
49 lsep = " " 49 lsep = " "
50 """Seperator for #: entries""" 50 """Seperator for #: entries"""
51 51
52 STRING = c_char_p 52 STRING = c_char_p
53 53
54 # Structures 54 # Structures
55 class po_message(Structure): 55 class po_message(Structure):
56 _fields_ = [] 56 _fields_ = []
57 57
58 # Function prototypes 58 # Function prototypes
59 xerror_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c _uint, c_int, STRING) 59 xerror_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c _uint, c_int, STRING)
60 xerror2_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRING, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRIN G) 60 xerror2_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRING, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRIN G)
61 61
62 62
63 # Structures (error handler) 63 # Structures (error handler)
64 class po_xerror_handler(Structure): 64 class po_xerror_handler(Structure):
65 _fields_ = [('xerror', xerror_prototype), 65 _fields_ = [('xerror', xerror_prototype),
66 ('xerror2', xerror2_prototype)] 66 ('xerror2', xerror2_prototype)]
67 67
68 class po_error_handler(Structure): 68 class po_error_handler(Structure):
69 _fields_ = [ 69 _fields_ = [
70 ('error', CFUNCTYPE(None, c_int, c_int, STRING)), 70 ('error', CFUNCTYPE(None, c_int, c_int, STRING)),
71 ('error_at_line', CFUNCTYPE(None, c_int, c_int, STRING, c_uint, STRING)), 71 ('error_at_line', CFUNCTYPE(None, c_int, c_int, STRING, c_uint, STRING)),
72 ('multiline_warning', CFUNCTYPE(None, STRING, STRING)), 72 ('multiline_warning', CFUNCTYPE(None, STRING, STRING)),
73 ('multiline_error', CFUNCTYPE(None, STRING, STRING)), 73 ('multiline_error', CFUNCTYPE(None, STRING, STRING)),
74 ] 74 ]
75 75
76 # Callback functions for po_xerror_handler 76 # Callback functions for po_xerror_handler
77 def xerror_cb(severity, message, filename, lineno, column, multilint_p, message_ text): 77 def xerror_cb(severity, message, filename, lineno, column, multilint_p, message_ text):
78 print >> sys.stderr, "xerror_cb", severity, message, filename, lineno, colum n, multilint_p, message_text 78 print >> sys.stderr, "xerror_cb", severity, message, filename, lineno, colum n, multilint_p, message_text
79 if severity >= 1: 79 if severity >= 1:
80 raise ValueError(message_text) 80 raise ValueError(message_text)
81 81
82 def xerror2_cb(severity, message1, filename1, lineno1, column1, multiline_p1, me ssage_text1, message2, filename2, lineno2, column2, multiline_p2, message_text2) : 82 def xerror2_cb(severity, message1, filename1, lineno1, column1, multiline_p1, me ssage_text1, message2, filename2, lineno2, column2, multiline_p2, message_text2) :
83 print >> sys.stderr, "xerror2_cb", severity, message1, filename1, lineno1, c olumn1, multiline_p1, message_text1, message2, filename2, lineno2, column2, mult iline_p2, message_text2 83 print >> sys.stderr, "xerror2_cb", severity, message1, filename1, lineno1, c olumn1, multiline_p1, message_text1, message2, filename2, lineno2, column2, mult iline_p2, message_text2
84 if severity >= 1: 84 if severity >= 1:
85 raise ValueError(message_text1) 85 raise ValueError(message_text1)
86 86
87 87
88 88
89 # Load libgettextpo 89 # Load libgettextpo
90 gpo = None 90 gpo = None
91 # 'gettextpo' is recognised on Unix, while only 'libgettextpo' is recognised on 91 # 'gettextpo' is recognised on Unix, while only 'libgettextpo' is recognised on
92 # windows. Therefore we test both. 92 # windows. Therefore we test both.
93 names = ['gettextpo', 'libgettextpo'] 93 names = ['gettextpo', 'libgettextpo']
94 for name in names: 94 for name in names:
95 lib_location = ctypes.util.find_library(name) 95 lib_location = ctypes.util.find_library(name)
96 if lib_location: 96 if lib_location:
97 gpo = cdll.LoadLibrary(lib_location) 97 gpo = cdll.LoadLibrary(lib_location)
98 if gpo: 98 if gpo:
99 break 99 break
100 else: 100 else:
101 # Now we are getting desperate, so let's guess a unix type DLL that might 101 # Now we are getting desperate, so let's guess a unix type DLL that might
102 # be in LD_LIBRARY_PATH or loaded with LD_PRELOAD 102 # be in LD_LIBRARY_PATH or loaded with LD_PRELOAD
103 try: 103 try:
104 gpo = cdll.LoadLibrary('libgettextpo.so') 104 gpo = cdll.LoadLibrary('libgettextpo.so')
105 except OSError, e: 105 except OSError, e:
106 raise ImportError("gettext PO library not found") 106 raise ImportError("gettext PO library not found")
107 107
108 # Setup return and paramater types 108 # Setup return and paramater types
109 # File access 109 # File access
110 gpo.po_file_read_v3.argtypes = [STRING, POINTER(po_xerror_handler)] 110 gpo.po_file_read_v3.argtypes = [STRING, POINTER(po_xerror_handler)]
111 gpo.po_file_write_v2.argtypes = [c_int, STRING, POINTER(po_xerror_handler)] 111 gpo.po_file_write_v2.argtypes = [c_int, STRING, POINTER(po_xerror_handler)]
112 gpo.po_file_write_v2.retype = c_int 112 gpo.po_file_write_v2.retype = c_int
113 113
114 # Header 114 # Header
115 gpo.po_file_domain_header.restype = STRING 115 gpo.po_file_domain_header.restype = STRING
116 gpo.po_header_field.restype = STRING 116 gpo.po_header_field.restype = STRING
117 gpo.po_header_field.argtypes = [STRING, STRING] 117 gpo.po_header_field.argtypes = [STRING, STRING]
118 118
119 # Locations (filepos) 119 # Locations (filepos)
120 gpo.po_filepos_file.restype = STRING 120 gpo.po_filepos_file.restype = STRING
121 gpo.po_message_filepos.restype = c_int 121 gpo.po_message_filepos.restype = c_int
122 gpo.po_message_filepos.argtypes = [c_int, c_int] 122 gpo.po_message_filepos.argtypes = [c_int, c_int]
123 gpo.po_message_add_filepos.argtypes = [c_int, STRING, c_size_t] 123 gpo.po_message_add_filepos.argtypes = [c_int, STRING, c_int]
124 124
125 # Message (get methods) 125 # Message (get methods)
126 gpo.po_message_comments.restype = STRING 126 gpo.po_message_comments.restype = STRING
127 gpo.po_message_extracted_comments.restype = STRING 127 gpo.po_message_extracted_comments.restype = STRING
128 gpo.po_message_prev_msgctxt.restype = STRING 128 gpo.po_message_prev_msgctxt.restype = STRING
129 gpo.po_message_prev_msgid.restype = STRING 129 gpo.po_message_prev_msgid.restype = STRING
130 gpo.po_message_prev_msgid_plural.restype = STRING 130 gpo.po_message_prev_msgid_plural.restype = STRING
131 gpo.po_message_is_format.restype = c_int 131 gpo.po_message_is_format.restype = c_int
132 gpo.po_message_msgctxt.restype = STRING 132 gpo.po_message_msgctxt.restype = STRING
133 gpo.po_message_msgid.restype = STRING 133 gpo.po_message_msgid.restype = STRING
134 gpo.po_message_msgid_plural.restype = STRING 134 gpo.po_message_msgid_plural.restype = STRING
135 gpo.po_message_msgstr.restype = STRING 135 gpo.po_message_msgstr.restype = STRING
136 gpo.po_message_msgstr_plural.restype = STRING 136 gpo.po_message_msgstr_plural.restype = STRING
137 137
138 # Message (set methods) 138 # Message (set methods)
139 gpo.po_message_set_comments.argtypes = [c_int, STRING] 139 gpo.po_message_set_comments.argtypes = [c_int, STRING]
140 gpo.po_message_set_extracted_comments.argtypes = [c_int, STRING] 140 gpo.po_message_set_extracted_comments.argtypes = [c_int, STRING]
141 gpo.po_message_set_fuzzy.argtypes = [c_int, c_int] 141 gpo.po_message_set_fuzzy.argtypes = [c_int, c_int]
142 gpo.po_message_set_msgctxt.argtypes = [c_int, STRING] 142 gpo.po_message_set_msgctxt.argtypes = [c_int, STRING]
143 gpo.po_message_set_format.argtypes = [c_int, STRING, c_int]
dwaynebailey 2008/08/29 13:19:25 Not sure what this has to do with addlocations? Y
144 143
145 # Setup the po_xerror_handler 144 # Setup the po_xerror_handler
146 xerror_handler = po_xerror_handler() 145 xerror_handler = po_xerror_handler()
147 xerror_handler.xerror = xerror_prototype(xerror_cb) 146 xerror_handler.xerror = xerror_prototype(xerror_cb)
148 xerror_handler.xerror2 = xerror2_prototype(xerror2_cb) 147 xerror_handler.xerror2 = xerror2_prototype(xerror2_cb)
149 148
150 def escapeforpo(text): 149 def escapeforpo(text):
151 return pypo.escapeforpo(text) 150 return pypo.escapeforpo(text)
152 151
153 def quoteforpo(text): 152 def quoteforpo(text):
154 return pypo.quoteforpo(text) 153 return pypo.quoteforpo(text)
155 154
156 def unquotefrompo(postr, joinwithlinebreak=False): 155 def unquotefrompo(postr, joinwithlinebreak=False):
157 return pypo.unquotefrompo(postr, joinwithlinebreak) 156 return pypo.unquotefrompo(postr, joinwithlinebreak)
158 157
159 def encodingToUse(encoding): 158 def encodingToUse(encoding):
160 return pypo.encodingToUse(encoding) 159 return pypo.encodingToUse(encoding)
161 160
162 def get_libgettextpo_version(): 161 def get_libgettextpo_version():
163 """Returns the libgettextpo version 162 """Returns the libgettextpo version
164 163
165 @return: a three-value tuple containing the libgettextpo version in the 164 @return: a three-value tuple containing the libgettextpo version in the
166 following format: 165 following format:
167 (major version, minor version, subminor version) 166 (major version, minor version, subminor version)
168 """ 167 """
169 libversion = c_long.in_dll(gpo, 'libgettextpo_version') 168 libversion = c_long.in_dll(gpo, 'libgettextpo_version')
170 major = libversion.value >> 16 169 major = libversion.value >> 16
171 minor = libversion.value >> 8 170 minor = libversion.value >> 8
172 subminor = libversion.value - (major << 16) - (minor << 8) 171 subminor = libversion.value - (major << 16) - (minor << 8)
173 return major, minor, subminor 172 return major, minor, subminor
174 173
175 174
176 class pounit(pocommon.pounit): 175 class pounit(pocommon.pounit):
177 def __init__(self, source=None, encoding='utf-8', gpo_message=None): 176 def __init__(self, source=None, encoding='utf-8', gpo_message=None):
178 self._encoding = encoding 177 self._encoding = encoding
179 if not gpo_message: 178 if not gpo_message:
180 self._gpo_message = gpo.po_message_create() 179 self._gpo_message = gpo.po_message_create()
181 if source or source == "": 180 if source or source == "":
182 self.source = source 181 self.source = source
183 self.target = "" 182 self.target = ""
184 elif gpo_message: 183 elif gpo_message:
185 self._gpo_message = gpo_message 184 self._gpo_message = gpo_message
186 185
187 def setmsgidcomment(self, msgidcomment): 186 def setmsgidcomment(self, msgidcomment):
188 if msgidcomment: 187 if msgidcomment:
189 newsource = "_: " + msgidcomment + "\n" + self.source 188 newsource = "_: " + msgidcomment + "\n" + self.source
190 self.source = newsource 189 self.source = newsource
191 msgidcomment = property(None, setmsgidcomment) 190 msgidcomment = property(None, setmsgidcomment)
192 191
193 def setmsgid_plural(self, msgid_plural): 192 def setmsgid_plural(self, msgid_plural):
(...skipping 131 matching lines...) Show 10 above Show 10 below
325 else: 324 else:
326 raise ValueError("Comment type not valid") 325 raise ValueError("Comment type not valid")
327 326
328 if comments and get_libgettextpo_version() < (0, 17, 0): 327 if comments and get_libgettextpo_version() < (0, 17, 0):
329 comments = "\n".join([line.strip() for line in comments.split("\n")] ) 328 comments = "\n".join([line.strip() for line in comments.split("\n")] )
330 # Let's drop the last newline 329 # Let's drop the last newline
331 return comments[:-1].decode(self._encoding) 330 return comments[:-1].decode(self._encoding)
332 331
333 def addnote(self, text, origin=None, position="append"): 332 def addnote(self, text, origin=None, position="append"):
334 # ignore empty strings and strings without non-space characters 333 # ignore empty strings and strings without non-space characters
335 if (not text) or (not text.strip()): 334 if (not text) or (not text.strip()):
336 return 335 return
337 text = data.forceunicode(text) 336 text = data.forceunicode(text)
338 oldnotes = self.getnotes(origin) 337 oldnotes = self.getnotes(origin)
339 newnotes = None 338 newnotes = None
340 if oldnotes: 339 if oldnotes:
341 if position == "append": 340 if position == "append":
342 newnotes = oldnotes + "\n" + text 341 newnotes = oldnotes + "\n" + text
343 elif position == "merge": 342 elif position == "merge":
344 if oldnotes != text: 343 if oldnotes != text:
345 oldnoteslist = oldnotes.split("\n") 344 oldnoteslist = oldnotes.split("\n")
346 for newline in text.split("\n"): 345 for newline in text.split("\n"):
347 newline = newline.rstrip() 346 newline = newline.rstrip()
348 # avoid duplicate comment lines (this might cause some p roblems) 347 # avoid duplicate comment lines (this might cause some p roblems)
349 if newline not in oldnotes or len(newline) < 5: 348 if newline not in oldnotes or len(newline) < 5:
350 oldnoteslist.append(newline) 349 oldnoteslist.append(newline)
351 newnotes = "\n".join(oldnoteslist) 350 newnotes = "\n".join(oldnoteslist)
352 else: 351 else:
353 newnotes = text + '\n' + oldnotes 352 newnotes = text + '\n' + oldnotes
354 else: 353 else:
355 newnotes = "\n".join([line.rstrip() for line in text.split("\n")]) 354 newnotes = "\n".join([line.rstrip() for line in text.split("\n")])
356 355
357 if newnotes: 356 if newnotes:
358 newlines = [] 357 newlines = []
359 needs_space = get_libgettextpo_version() < (0, 17, 0) 358 needs_space = get_libgettextpo_version() < (0, 17, 0)
360 for line in newnotes.split("\n"): 359 for line in newnotes.split("\n"):
361 if line and needs_space: 360 if line and needs_space:
362 newlines.append(" " + line) 361 newlines.append(" " + line)
363 else: 362 else:
364 newlines.append(line) 363 newlines.append(line)
365 newnotes = "\n".join(newlines) 364 newnotes = "\n".join(newlines)
366 if origin in ["programmer", "developer", "source code"]: 365 if origin in ["programmer", "developer", "source code"]:
367 gpo.po_message_set_extracted_comments(self._gpo_message, newnote s) 366 gpo.po_message_set_extracted_comments(self._gpo_message, newnote s)
368 else: 367 else:
369 gpo.po_message_set_comments(self._gpo_message, newnotes) 368 gpo.po_message_set_comments(self._gpo_message, newnotes)
370 369
371 def removenotes(self): 370 def removenotes(self):
372 gpo.po_message_set_comments(self._gpo_message, "") 371 gpo.po_message_set_comments(self._gpo_message, "")
373 372
374 def copy(self): 373 def copy(self):
375 """Returns a copy of the this unit that can be used independently. 374 unit = self.buildfromunit(self)
376 """ 375 #XXX: adding missing data
377 unit = pounit()
378 unit.source = self.source
379 unit.target = self.target
380 unit.markfuzzy(self.isfuzzy())
381 if self.isobsolete(): 376 if self.isobsolete():
382 unit.makeobsolete() 377 unit.makeobsolete()
383 for location in self.getlocations():
384 unit.addlocation(location)
385 notes = []
386 origins = ['translator', 'developer', 'programmer', 'source code']
dwaynebailey 2008/08/29 13:19:25 Maybe call them note_types
387 for origin in origins:
388 note = self.getnotes(origin=origin)
389 if note in notes:
390 continue
391 else:
392 unit.addnote(note, origin=origin)
393 notes.append(note)
394 context = gpo.po_message_msgctxt(self._gpo_message) 378 context = gpo.po_message_msgctxt(self._gpo_message)
395 if context: 379 if context:
396 unit.setcontext(context) 380 unit.setcontext(context)
397 comments = self._extract_msgidcomments() 381 comments = self._extract_msgidcomments()
398 if comments: 382 if comments:
383 print 'HAS COMMENTS', comments
399 unit.setmsgidcomment(comments) 384 unit.setmsgidcomment(comments)
400 for typecomment in self.gettypecomments():
401 unit.addtypecomment(typecomment)
402 return unit 385 return unit
dwaynebailey 2008/08/29 13:19:25 Generally I think this part looks good
403 386
404 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ): 387 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ):
405 """Merges the otherpo (with the same msgid) into this one. 388 """Merges the otherpo (with the same msgid) into this one.
406 389
407 Overwrite non-blank self.msgstr only if overwrite is True 390 Overwrite non-blank self.msgstr only if overwrite is True
408 merge comments only if comments is True 391 merge comments only if comments is True
409 392
410 """ 393 """
411 394
412 if not isinstance(otherpo, pounit): 395 if not isinstance(otherpo, pounit):
413 super(pounit, self).merge(otherpo, overwrite, comments) 396 super(pounit, self).merge(otherpo, overwrite, comments)
414 return 397 return
415 if comments: 398 if comments:
416 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge") 399 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge")
417 # FIXME mergelists(self.typecomments, otherpo.typecomments) 400 # FIXME mergelists(self.typecomments, otherpo.typecomments)
418 if not authoritative: 401 if not authoritative:
419 # We don't bring across otherpo.automaticcomments as we consider ourself 402 # We don't bring across otherpo.automaticcomments as we consider ourself
420 # to be the the authority. Same applies to otherpo.msgidcomment s 403 # to be the the authority. Same applies to otherpo.msgidcomment s
421 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge") 404 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge")
422 self.msgidcomment = otherpo._extract_msgidcomments() or None 405 self.msgidcomment = otherpo._extract_msgidcomments() or None
423 self.addlocations(otherpo.getlocations()) 406 self.addlocations(otherpo.getlocations())
424 if not self.istranslated() or overwrite: 407 if not self.istranslated() or overwrite:
425 # Remove kde-style comments from the translation (if any). 408 # Remove kde-style comments from the translation (if any).
426 if self._extract_msgidcomments(otherpo.target): 409 if self._extract_msgidcomments(otherpo.target):
427 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '') 410 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '')
428 self.target = otherpo.target 411 self.target = otherpo.target
429 if self.source != otherpo.source: 412 if self.source != otherpo.source:
430 self.markfuzzy() 413 self.markfuzzy()
431 else: 414 else:
432 self.markfuzzy(otherpo.isfuzzy()) 415 self.markfuzzy(otherpo.isfuzzy())
433 elif not otherpo.istranslated(): 416 elif not otherpo.istranslated():
434 if self.source != otherpo.source: 417 if self.source != otherpo.source:
435 self.markfuzzy() 418 self.markfuzzy()
436 else: 419 else:
437 if self.target != otherpo.target: 420 if self.target != otherpo.target:
438 self.markfuzzy() 421 self.markfuzzy()
439 422
440 def isheader(self): 423 def isheader(self):
441 #return self.source == u"" and self.target != u"" 424 #return self.source == u"" and self.target != u""
442 # we really want to make sure that there is no msgidcomment or msgctxt 425 # we really want to make sure that there is no msgidcomment or msgctxt
443 return self.getid() == "" and len(self.target) > 0 426 return self.getid() == "" and len(self.target) > 0
444 427
445 def isblank(self): 428 def isblank(self):
446 return len(self.source) == 0 and len(self.target) == 0 429 return len(self.source) == 0 and len(self.target) == 0
447 430
448 def hastypecomment(self, typecomment): 431 def hastypecomment(self, typecomment):
449 return gpo.po_message_is_format(self._gpo_message, typecomment) 432 return gpo.po_message_is_format(self._gpo_message, typecomment)
450 433
451 def gettypecomments(self):
452 """Returns the type comments of a unit, if any.
453
454 @return: a list containing the type comments of the unit
455 """
456 #XXX: brute force
457 formats = [
458 "c", "objc", "sh", "python", "lisp", "elisp", "librep", "scheme",
459 "smalltalk", "java", "csharp", "awk", "object-pascal", "ycp", "tcl",
460 "perl", "perl-brace", "php", "gcc-internal", "qt", "kde", "boost",
461 ]
dwaynebailey 2008/08/29 13:19:25 I thought we could get a list of valid formats fro
462 comments = []
463 for format in formats:
464 format += '-format'
465 if self.hastypecomment(format):
466 comments.append(format)
467 return comments
468
469 def addtypecomment(self, typecomment):
470 """Adds a type comment to the unit
471
472 @oaram typecomment: the type comment to be added, for example
473 "c-format".
474 """
475 gpo.po_message_set_format(self._gpo_message, typecomment, 1)
476
477 def hasmarkedcomment(self, commentmarker): 434 def hasmarkedcomment(self, commentmarker):
478 commentmarker = "(%s)" % commentmarker 435 commentmarker = "(%s)" % commentmarker
479 for comment in self.getnotes("translator").split("\n"): 436 for comment in self.getnotes("translator").split("\n"):
480 if comment.startswith(commentmarker): 437 if comment.startswith(commentmarker):
481 return True 438 return True
482 return False 439 return False
483 440
484 def istranslated(self): 441 def istranslated(self):
485 return super(pounit, self).istranslated() and not self.isobsolete() 442 return super(pounit, self).istranslated() and not self.isobsolete()
486 443
487 def istranslatable(self): 444 def istranslatable(self):
488 return not (self.isheader() or self.isblank() or self.isobsolete()) 445 return not (self.isheader() or self.isblank() or self.isobsolete())
489 446
490 def isfuzzy(self): 447 def isfuzzy(self):
491 return gpo.po_message_is_fuzzy(self._gpo_message) 448 return gpo.po_message_is_fuzzy(self._gpo_message)
492 449
493 def markfuzzy(self, present=True): 450 def markfuzzy(self, present=True):
494 gpo.po_message_set_fuzzy(self._gpo_message, present) 451 gpo.po_message_set_fuzzy(self._gpo_message, present)
495 452
496 def isreview(self): 453 def isreview(self):
497 return self.hasmarkedcomment("review") or self.hasmarkedcomment("pofilte r") 454 return self.hasmarkedcomment("review") or self.hasmarkedcomment("pofilte r")
498 455
499 def isobsolete(self): 456 def isobsolete(self):
500 return gpo.po_message_is_obsolete(self._gpo_message) 457 return gpo.po_message_is_obsolete(self._gpo_message)
501 458
502 def makeobsolete(self): 459 def makeobsolete(self):
503 # FIXME: libgettexpo currently does not reset other data, we probably wa nt to do that 460 # FIXME: libgettexpo currently does not reset other data, we probably wa nt to do that
504 # but a better solution would be for libgettextpo to output correct data on serialisation 461 # but a better solution would be for libgettextpo to output correct data on serialisation
505 gpo.po_message_set_obsolete(self._gpo_message, True) 462 gpo.po_message_set_obsolete(self._gpo_message, True)
506 463
507 def resurrect(self): 464 def resurrect(self):
508 gpo.po_message_set_obsolete(self._gpo_message, False) 465 gpo.po_message_set_obsolete(self._gpo_message, False)
509 466
510 def hasplural(self): 467 def hasplural(self):
511 return gpo.po_message_msgid_plural(self._gpo_message) is not None 468 return gpo.po_message_msgid_plural(self._gpo_message) is not None
512 469
513 def _extract_msgidcomments(self, text=None): 470 def _extract_msgidcomments(self, text=None):
514 """Extract KDE style msgid comments from the unit. 471 """Extract KDE style msgid comments from the unit.
515 472
516 @rtype: String 473 @rtype: String
517 @return: Returns the extracted msgidcomments found in this unit's msgid. 474 @return: Returns the extracted msgidcomments found in this unit's msgid.
518 475
519 """ 476 """
520 477
521 if not text: 478 if not text:
522 text = gpo.po_message_msgid(self._gpo_message) 479 text = gpo.po_message_msgid(self._gpo_message)
523 if text: 480 if text:
524 msgidcomment = re.search("_: (.*)\n", text) 481 msgidcomment = re.search("_: (.*)\n", text)
525 if msgidcomment: 482 if msgidcomment:
526 return msgidcomment.group(1).decode(self._encoding) 483 return msgidcomment.group(1).decode(self._encoding)
527 return u"" 484 return u""
528 485
529 def __str__(self): 486 def __str__(self):
530 pf = pofile() 487 pf = pofile()
531 unit = self.copy() 488 unit = self.copy()
532 pf.addunit(unit) 489 pf.addunit(unit)
533 return str(pf) 490 return str(pf)
534 491
535 def getlocations(self): 492 def getlocations(self):
536 locations = [] 493 locations = []
537 i = 0 494 i = 0
538 location = gpo.po_message_filepos(self._gpo_message, i) 495 location = gpo.po_message_filepos(self._gpo_message, i)
539 while location: 496 while location:
540 locname = gpo.po_filepos_file(location) 497 locname = gpo.po_filepos_file(location)
541 locline = gpo.po_filepos_start_line(location) 498 locline = gpo.po_filepos_start_line(location)
542 if locline == -1: 499 if locline == -1:
543 locstring = locname 500 locstring = locname
544 else: 501 else:
545 locstring = locname + ":" + str(locline) 502 locstring = locname + ":" + str(locline)
546 locations.append(locstring) 503 locations.append(locstring)
547 i += 1 504 i += 1
548 location = gpo.po_message_filepos(self._gpo_message, i) 505 location = gpo.po_message_filepos(self._gpo_message, i)
549 return locations 506 return locations
550 507
551 def addlocation(self, location): 508 def addlocation(self, location):
552 for loc in location.split(): 509 for loc in location.split():
553 parts = loc.split(":") 510 parts = loc.split(":")
554 file = parts[0] 511 file = parts[0]
555 if len(parts) == 2: 512 if len(parts) == 2:
556 line = int(parts[1]) 513 line = int(parts[1])
557 else: 514 else:
558 # libgettextpo requires a (size_t)(-1) if no file 515 line = -1
559 # location is available
560 line = c_size_t(-1)
561 gpo.po_message_add_filepos(self._gpo_message, file, line) 516 gpo.po_message_add_filepos(self._gpo_message, file, line)
562 517
563 def setcontext(self, context): 518 def setcontext(self, context):
564 """Sets the context message""" 519 """Sets the context message"""
520 #FIXME: not complete
565 gpo.po_message_set_msgctxt(self._gpo_message, context) 521 gpo.po_message_set_msgctxt(self._gpo_message, context)
dwaynebailey 2008/08/29 13:19:25 Again one piece at a time. If you can separate ou
566 522
567 def getcontext(self): 523 def getcontext(self):
568 msgctxt = gpo.po_message_msgctxt(self._gpo_message) 524 msgctxt = gpo.po_message_msgctxt(self._gpo_message)
569 msgidcomment = self._extract_msgidcomments() 525 msgidcomment = self._extract_msgidcomments()
570 if msgctxt: 526 if msgctxt:
571 return msgctxt + msgidcomment 527 return msgctxt + msgidcomment
572 else: 528 else:
573 return msgidcomment 529 return msgidcomment
574 530
575 class pofile(pocommon.pofile): 531 class pofile(pocommon.pofile):
576 UnitClass = pounit 532 UnitClass = pounit
577 def __init__(self, inputfile=None, encoding=None, unitclass=pounit): 533 def __init__(self, inputfile=None, encoding=None, unitclass=pounit):
578 self.UnitClass = unitclass 534 self.UnitClass = unitclass
579 pocommon.pofile.__init__(self, unitclass=unitclass) 535 pocommon.pofile.__init__(self, unitclass=unitclass)
580 self._gpo_memory_file = None 536 self._gpo_memory_file = None
581 self._gpo_message_iterator = None 537 self._gpo_message_iterator = None
582 self._encoding = encodingToUse(encoding) 538 self._encoding = encodingToUse(encoding)
583 if inputfile is not None: 539 if inputfile is not None:
584 self.parse(inputfile) 540 self.parse(inputfile)
585 else: 541 else:
586 self._gpo_memory_file = gpo.po_file_create() 542 self._gpo_memory_file = gpo.po_file_create()
587 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memor y_file, None) 543 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memor y_file, None)
588 544
589 def addunit(self, unit): 545 def addunit(self, unit):
590 unitcopy = unit.copy() 546 unitcopy = unit.copy()
591 gpo.po_message_insert(self._gpo_message_iterator, unitcopy._gpo_message) 547 gpo.po_message_insert(self._gpo_message_iterator, unitcopy._gpo_message)
592 self.units.append(unitcopy) 548 self.units.append(unitcopy)
593
594 def addsourceunit(self, source):
595 self.addunit(self.UnitClass(source))
596 unit = self.findunit(source)
597 return unit
598 549
599 def removeduplicates(self, duplicatestyle="merge"): 550 def removeduplicates(self, duplicatestyle="merge"):
600 """make sure each msgid is unique ; merge comments etc from duplicates i nto original""" 551 """make sure each msgid is unique ; merge comments etc from duplicates i nto original"""
601 msgiddict = {} 552 msgiddict = {}
602 uniqueunits = [] 553 uniqueunits = []
603 # we sometimes need to keep track of what has been marked 554 # we sometimes need to keep track of what has been marked
604 # TODO: this is using a list as the pos aren't hashable, but this is slo w... 555 # TODO: this is using a list as the pos aren't hashable, but this is slo w...
605 markedpos = [] 556 markedpos = []
606 def addcomment(thepo): 557 def addcomment(thepo):
607 thepo.msgidcomment = " ".join(thepo.getlocations()) 558 thepo.msgidcomment = " ".join(thepo.getlocations())
608 markedpos.append(thepo) 559 markedpos.append(thepo)
609 for thepo in self.units: 560 for thepo in self.units:
610 if thepo.isheader(): 561 if thepo.isheader():
611 uniqueunits.append(thepo) 562 uniqueunits.append(thepo)
612 continue 563 continue
613 if duplicatestyle.startswith("msgid_comment"): 564 if duplicatestyle.startswith("msgid_comment"):
614 msgid = thepo._extract_msgidcomments() + thepo.source 565 msgid = thepo._extract_msgidcomments() + thepo.source
615 else: 566 else:
616 msgid = thepo.source 567 msgid = thepo.source
617 if duplicatestyle == "msgid_comment_all": 568 if duplicatestyle == "msgid_comment_all":
618 addcomment(thepo) 569 addcomment(thepo)
619 uniqueunits.append(thepo) 570 uniqueunits.append(thepo)
620 elif msgid in msgiddict: 571 elif msgid in msgiddict:
621 if duplicatestyle == "merge": 572 if duplicatestyle == "merge":
622 if msgid: 573 if msgid:
623 msgiddict[msgid].merge(thepo) 574 msgiddict[msgid].merge(thepo)
624 else: 575 else:
625 addcomment(thepo) 576 addcomment(thepo)
626 uniqueunits.append(thepo) 577 uniqueunits.append(thepo)
627 elif duplicatestyle == "keep": 578 elif duplicatestyle == "keep":
628 uniqueunits.append(thepo) 579 uniqueunits.append(thepo)
629 elif duplicatestyle == "msgid_comment": 580 elif duplicatestyle == "msgid_comment":
630 origpo = msgiddict[msgid] 581 origpo = msgiddict[msgid]
631 if origpo not in markedpos: 582 if origpo not in markedpos:
632 addcomment(origpo) 583 addcomment(origpo)
633 addcomment(thepo) 584 addcomment(thepo)
634 uniqueunits.append(thepo) 585 uniqueunits.append(thepo)
635 elif duplicatestyle == "msgctxt": 586 elif duplicatestyle == "msgctxt":
636 origpo = msgiddict[msgid] 587 origpo = msgiddict[msgid]
637 if origpo not in markedpos: 588 if origpo not in markedpos:
638 gpo.po_message_set_msgctxt(origpo._gpo_message, " ".join (origpo.getlocations())) 589 gpo.po_message_set_msgctxt(origpo._gpo_message, " ".join (origpo.getlocations()))
639 markedpos.append(thepo) 590 markedpos.append(thepo)
640 gpo.po_message_set_msgctxt(thepo._gpo_message, " ".join(thep o.getlocations())) 591 gpo.po_message_set_msgctxt(thepo._gpo_message, " ".join(thep o.getlocations()))
641 uniqueunits.append(thepo) 592 uniqueunits.append(thepo)
642 else: 593 else:
643 if not msgid and duplicatestyle != "keep": 594 if not msgid and duplicatestyle != "keep":
644 addcomment(thepo) 595 addcomment(thepo)
645 msgiddict[msgid] = thepo 596 msgiddict[msgid] = thepo
646 uniqueunits.append(thepo) 597 uniqueunits.append(thepo)
647 new_gpo_memory_file = gpo.po_file_create() 598 new_gpo_memory_file = gpo.po_file_create()
648 new_gpo_message_iterator = gpo.po_message_iterator(new_gpo_memory_file, None) 599 new_gpo_message_iterator = gpo.po_message_iterator(new_gpo_memory_file, None)
649 for unit in uniqueunits: 600 for unit in uniqueunits:
650 gpo.po_message_insert(new_gpo_message_iterator, unit._gpo_message) 601 gpo.po_message_insert(new_gpo_message_iterator, unit._gpo_message)
651 gpo.po_message_iterator_free(self._gpo_message_iterator) 602 gpo.po_message_iterator_free(self._gpo_message_iterator)
652 self._gpo_message_iterator = new_gpo_message_iterator 603 self._gpo_message_iterator = new_gpo_message_iterator
653 self._gpo_memory_file = new_gpo_memory_file 604 self._gpo_memory_file = new_gpo_memory_file
654 self.units = uniqueunits 605 self.units = uniqueunits
655 606
656 def __str__(self): 607 def __str__(self):
657 def obsolete_workaround(): 608 def obsolete_workaround():
658 # Remove all items that are not output by msgmerge when a unit is ob solete. This is a work 609 # Remove all items that are not output by msgmerge when a unit is ob solete. This is a work
659 # around for bug in libgettextpo 610 # around for bug in libgettextpo
660 # FIXME Do version test in case they fix this bug 611 # FIXME Do version test in case they fix this bug
661 for unit in self.units: 612 for unit in self.units:
662 if unit.isobsolete(): 613 if unit.isobsolete():
663 gpo.po_message_set_extracted_comments(unit._gpo_message, "") 614 gpo.po_message_set_extracted_comments(unit._gpo_message, "")
664 location = gpo.po_message_filepos(unit._gpo_message, 0) 615 location = gpo.po_message_filepos(unit._gpo_message, 0)
665 while location: 616 while location:
666 gpo.po_message_remove_filepos(unit._gpo_message, 0) 617 gpo.po_message_remove_filepos(unit._gpo_message, 0)
667 location = gpo.po_message_filepos(unit._gpo_message, 0) 618 location = gpo.po_message_filepos(unit._gpo_message, 0)
668 outputstring = "" 619 outputstring = ""
669 if self._gpo_memory_file: 620 if self._gpo_memory_file:
670 obsolete_workaround() 621 obsolete_workaround()
671 f = tempfile.NamedTemporaryFile(prefix='translate', suffix='.po') 622 f = tempfile.NamedTemporaryFile(prefix='translate', suffix='.po')
672 self._gpo_memory_file = gpo.po_file_write_v2(self._gpo_memory_file, f.name, xerror_handler) 623 self._gpo_memory_file = gpo.po_file_write_v2(self._gpo_memory_file, f.name, xerror_handler)
673 f.seek(0) 624 f.seek(0)
674 outputstring = f.read() 625 outputstring = f.read()
675 f.close() 626 f.close()
676 return outputstring 627 return outputstring
677 628
678 def isempty(self): 629 def isempty(self):
679 """Returns True if the object doesn't contain any translation units.""" 630 """Returns True if the object doesn't contain any translation units."""
680 if len(self.units) == 0: 631 if len(self.units) == 0:
681 return True 632 return True
682 # Skip the first unit if it is a header. 633 # Skip the first unit if it is a header.
683 if self.units[0].isheader(): 634 if self.units[0].isheader():
684 units = self.units[1:] 635 units = self.units[1:]
685 else: 636 else:
686 units = self.units 637 units = self.units
687 638
688 for unit in units: 639 for unit in units:
689 if not unit.isblank() and not unit.isobsolete(): 640 if not unit.isblank() and not unit.isobsolete():
690 return False 641 return False
691 return True 642 return True
692 643
693 def parse(self, input): 644 def parse(self, input):
694 if hasattr(input, 'name'): 645 if hasattr(input, 'name'):
695 self.filename = input.name 646 self.filename = input.name
696 elif not getattr(self, 'filename', ''): 647 elif not getattr(self, 'filename', ''):
697 self.filename = '' 648 self.filename = ''
698 649
699 if hasattr(input, "read"): 650 if hasattr(input, "read"):
700 posrc = input.read() 651 posrc = input.read()
701 input.close() 652 input.close()
702 input = posrc 653 input = posrc
703 654
704 needtmpfile = not os.path.isfile(input) 655 needtmpfile = not os.path.isfile(input)
705 if needtmpfile: 656 if needtmpfile:
706 # This is not a file - we write the string to a temporary file 657 # This is not a file - we write the string to a temporary file
707 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 658 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po')
708 os.write(fd, input) 659 os.write(fd, input)
709 input = fname 660 input = fname
710 os.close(fd) 661 os.close(fd)
711 662
712 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler) 663 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler)
713 if self._gpo_memory_file is None: 664 if self._gpo_memory_file is None:
714 print >> sys.stderr, "Error:" 665 print >> sys.stderr, "Error:"
715 666
716 if needtmpfile: 667 if needtmpfile:
717 os.remove(input) 668 os.remove(input)
718 669
719 # Handle xerrors here 670 # Handle xerrors here
720 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None) 671 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None)
721 if self._header: 672 if self._header:
722 charset = gpo.po_header_field(self._header, "Content-Type") 673 charset = gpo.po_header_field(self._header, "Content-Type")
723 if charset: 674 if charset:
724 charset = re.search("charset=([^\\s]+)", charset).group(1) 675 charset = re.search("charset=([^\\s]+)", charset).group(1)
725 self._encoding = encodingToUse(charset) 676 self._encoding = encodingToUse(charset)
726 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_fi le, None) 677 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_fi le, None)
727 newmessage = gpo.po_next_message(self._gpo_message_iterator) 678 newmessage = gpo.po_next_message(self._gpo_message_iterator)
728 while newmessage: 679 while newmessage:
729 newunit = pounit(gpo_message=newmessage) 680 newunit = pounit(gpo_message=newmessage)
730 self.units.append(newunit) 681 self.units.append(newunit)
731 newmessage = gpo.po_next_message(self._gpo_message_iterator) 682 newmessage = gpo.po_next_message(self._gpo_message_iterator)
683 #self._free_iterator()
732 684
733 def __del__(self): 685 def __del__(self):
686 #print 'DELL CALLED', id(self)
734 # We currently disable this while we still get segmentation faults. 687 # We currently disable this while we still get segmentation faults.
735 # Note that this is definitely leaking memory because of this. 688 # Note that this is definitely leaking memory because of this.
689 #return
736 self._free_iterator() 690 self._free_iterator()
737 if self._gpo_memory_file is not None: 691 if self._gpo_memory_file is not None:
738 gpo.po_file_free(self._gpo_memory_file) 692 gpo.po_file_free(self._gpo_memory_file)
739 self._gpo_memory_file = None 693 self._gpo_memory_file = None
740 694
741 def _free_iterator(self): 695 def _free_iterator(self):
696 #print 'ITERATOR CALLED', id(self)
742 # We currently disable this while we still get segmentation faults. 697 # We currently disable this while we still get segmentation faults.
743 # Note that this is definitely leaking memory because of this. 698 # Note that this is definitely leaking memory because of this.
699 #return
744 if self._gpo_message_iterator is not None: 700 if self._gpo_message_iterator is not None:
745 gpo.po_message_iterator_free(self._gpo_message_iterator) 701 gpo.po_message_iterator_free(self._gpo_message_iterator)
746 self._gpo_message_iterator = None 702 self._gpo_message_iterator = None
LEFTRIGHT

Powered by Google App Engine
This is Rietveld r159