Package translate :: Package storage :: Module cpo
[hide private]
[frames] | no frames]

Source Code for Module translate.storage.cpo

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # Copyright 2002-2007 Zuza Software Foundation 
  5  # 
  6  # This file is part of translate. 
  7  # 
  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 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12  # 
 13  # translate is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with translate; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 21   
 22  """Classes that hold units of .po files (pounit) or entire files (pofile). 
 23   
 24  Gettext-style .po (or .pot) files are used in translations for KDE, GNOME and 
 25  many other projects. 
 26   
 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 
 29  to have a look at gettext-tools/libgettextpo/gettext-po.h from the gettext 
 30  package for the public API of the library. 
 31  """ 
 32   
 33  from translate.misc.multistring import multistring 
 34  from translate.storage import pocommon 
 35  from translate.storage import pypo 
 36  from translate.storage.pocommon import encodingToUse 
 37  from translate.lang import data 
 38  from ctypes import c_int, c_uint, c_char_p, c_long, CFUNCTYPE, POINTER 
 39  from ctypes import Structure, cdll 
 40  import ctypes.util 
 41  import os 
 42  import re 
 43  import sys 
 44  import tempfile 
 45   
 46  lsep = " " 
 47  """Seperator for #: entries""" 
 48   
 49  STRING = c_char_p 
 50   
 51   
 52  # Structures 
53 -class po_message(Structure):
54 _fields_ = []
55 56 # Function prototypes 57 xerror_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRING) 58 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, STRING) 59 60 61 # Structures (error handler)
62 -class po_xerror_handler(Structure):
63 _fields_ = [('xerror', xerror_prototype), 64 ('xerror2', xerror2_prototype)]
65 66
67 -class po_error_handler(Structure):
68 _fields_ = [ 69 ('error', CFUNCTYPE(None, c_int, c_int, STRING)), 70 ('error_at_line', CFUNCTYPE(None, c_int, c_int, STRING, c_uint, STRING)), 71 ('multiline_warning', CFUNCTYPE(None, STRING, STRING)), 72 ('multiline_error', CFUNCTYPE(None, STRING, STRING)), 73 ]
74 75 76 # Callback functions for po_xerror_handler
77 -def xerror_cb(severity, message, filename, lineno, column, multilint_p, message_text):
78 print >> sys.stderr, "xerror_cb", severity, message, filename, lineno, column, multilint_p, message_text 79 if severity >= 1: 80 raise ValueError(message_text)
81 82
83 -def xerror2_cb(severity, message1, filename1, lineno1, column1, multiline_p1, message_text1, message2, filename2, lineno2, column2, multiline_p2, message_text2):
84 print >> sys.stderr, "xerror2_cb", severity, message1, filename1, lineno1, column1, multiline_p1, message_text1, message2, filename2, lineno2, column2, multiline_p2, message_text2 85 if severity >= 1: 86 raise ValueError(message_text1)
87 88 89 # Load libgettextpo 90 gpo = None 91 # 'gettextpo' is recognised on Unix, while only 'libgettextpo' is recognised on 92 # windows. Therefore we test both. 93 names = ['gettextpo', 'libgettextpo'] 94 for name in names: 95 lib_location = ctypes.util.find_library(name) 96 if lib_location: 97 gpo = cdll.LoadLibrary(lib_location) 98 if gpo: 99 break 100 else: 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 103 try: 104 gpo = cdll.LoadLibrary('libgettextpo.so') 105 except OSError, e: 106 raise ImportError("gettext PO library not found") 107 108 # Setup return and paramater types 109 # File access 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)] 112 gpo.po_file_write_v2.retype = c_int 113 114 # Header 115 gpo.po_file_domain_header.restype = STRING 116 gpo.po_header_field.restype = STRING 117 gpo.po_header_field.argtypes = [STRING, STRING] 118 119 # Locations (filepos) 120 gpo.po_filepos_file.restype = STRING 121 gpo.po_message_filepos.restype = c_int 122 gpo.po_message_filepos.argtypes = [c_int, c_int] 123 gpo.po_message_add_filepos.argtypes = [c_int, STRING, c_int] 124 125 # Message (get methods) 126 gpo.po_message_comments.restype = STRING 127 gpo.po_message_extracted_comments.restype = STRING 128 gpo.po_message_prev_msgctxt.restype = STRING 129 gpo.po_message_prev_msgid.restype = STRING 130 gpo.po_message_prev_msgid_plural.restype = STRING 131 gpo.po_message_is_format.restype = c_int 132 gpo.po_message_is_format.argtypes = [c_int, STRING] 133 gpo.po_message_set_format.argtypes = [c_int, STRING, c_int] 134 gpo.po_message_msgctxt.restype = STRING 135 gpo.po_message_msgid.restype = STRING 136 gpo.po_message_msgid_plural.restype = STRING 137 gpo.po_message_msgstr.restype = STRING 138 gpo.po_message_msgstr_plural.restype = STRING 139 140 # Message (set methods) 141 gpo.po_message_set_comments.argtypes = [c_int, STRING] 142 gpo.po_message_set_extracted_comments.argtypes = [c_int, STRING] 143 gpo.po_message_set_fuzzy.argtypes = [c_int, c_int] 144 gpo.po_message_set_msgctxt.argtypes = [c_int, STRING] 145 146 # Setup the po_xerror_handler 147 xerror_handler = po_xerror_handler() 148 xerror_handler.xerror = xerror_prototype(xerror_cb) 149 xerror_handler.xerror2 = xerror2_prototype(xerror2_cb) 150 151
152 -def escapeforpo(text):
153 return pypo.escapeforpo(text)
154 155
156 -def quoteforpo(text):
157 return pypo.quoteforpo(text)
158 159
160 -def unquotefrompo(postr):
161 return pypo.unquotefrompo(postr)
162 163
164 -def get_libgettextpo_version():
165 """Returns the libgettextpo version 166 167 @rtype: three-value tuple 168 @return: libgettextpo version in the following format:: 169 (major version, minor version, subminor version) 170 """ 171 libversion = c_long.in_dll(gpo, 'libgettextpo_version') 172 major = libversion.value >> 16 173 minor = libversion.value >> 8 174 subminor = libversion.value - (major << 16) - (minor << 8) 175 return major, minor, subminor
176 177
178 -class pounit(pocommon.pounit):
179
180 - def __init__(self, source=None, encoding='utf-8', gpo_message=None):
181 self._rich_source = None 182 self._rich_target = None 183 self._encoding = encoding 184 if not gpo_message: 185 self._gpo_message = gpo.po_message_create() 186 if source or source == "": 187 self.source = source 188 self.target = "" 189 elif gpo_message: 190 self._gpo_message = gpo_message
191
192 - def setmsgid_plural(self, msgid_plural):
193 if isinstance(msgid_plural, list): 194 msgid_plural = "".join(msgid_plural) 195 gpo.po_message_set_msgid_plural(self._gpo_message, msgid_plural)
196 msgid_plural = property(None, setmsgid_plural) 197
198 - def getsource(self):
199 200 def remove_msgid_comments(text): 201 if not text: 202 return text 203 if text.startswith("_:"): 204 remainder = re.search(r"_: .*\n(.*)", text) 205 if remainder: 206 return remainder.group(1) 207 else: 208 return u"" 209 else: 210 return text
211 singular = remove_msgid_comments(gpo.po_message_msgid(self._gpo_message).decode(self._encoding)) 212 if singular: 213 if self.hasplural(): 214 multi = multistring(singular, self._encoding) 215 pluralform = gpo.po_message_msgid_plural(self._gpo_message).decode(self._encoding) 216 multi.strings.append(pluralform) 217 return multi 218 else: 219 return singular 220 else: 221 return u""
222
223 - def setsource(self, source):
224 if isinstance(source, multistring): 225 source = source.strings 226 if isinstance(source, unicode): 227 source = source.encode(self._encoding) 228 if isinstance(source, list): 229 gpo.po_message_set_msgid(self._gpo_message, source[0].encode(self._encoding)) 230 if len(source) > 1: 231 gpo.po_message_set_msgid_plural(self._gpo_message, source[1].encode(self._encoding)) 232 else: 233 gpo.po_message_set_msgid(self._gpo_message, source) 234 gpo.po_message_set_msgid_plural(self._gpo_message, None)
235 source = property(getsource, setsource) 236
237 - def gettarget(self):
238 if self.hasplural(): 239 plurals = [] 240 nplural = 0 241 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural) 242 while plural: 243 plurals.append(plural.decode(self._encoding)) 244 nplural += 1 245 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural) 246 if plurals: 247 multi = multistring(plurals, encoding=self._encoding) 248 else: 249 multi = multistring(u"") 250 else: 251 multi = (gpo.po_message_msgstr(self._gpo_message) or "").decode(self._encoding) 252 return multi
253
254 - def settarget(self, target):
255 # for plural strings: convert 'target' into a list 256 if self.hasplural(): 257 if isinstance(target, multistring): 258 target = target.strings 259 elif isinstance(target, basestring): 260 target = [target] 261 # for non-plurals: check number of items in 'target' 262 elif isinstance(target, (dict, list)): 263 if len(target) == 1: 264 target = target[0] 265 else: 266 raise ValueError("po msgid element has no plural but msgstr has %d elements (%s)" % (len(target), target)) 267 # empty the previous list of messages 268 # TODO: the "pypo" implementation does not remove the previous items of 269 # the target, if self.target == target (essentially: comparing only 270 # the first item of a plural string with the single new string) 271 # Maybe this behaviour should be unified. 272 if isinstance(target, (dict, list)): 273 i = 0 274 message = gpo.po_message_msgstr_plural(self._gpo_message, i) 275 while message is not None: 276 gpo.po_message_set_msgstr_plural(self._gpo_message, i, None) 277 i += 1 278 message = gpo.po_message_msgstr_plural(self._gpo_message, i) 279 # add the items of a list 280 if isinstance(target, list): 281 for i in range(len(target)): 282 targetstring = target[i] 283 if isinstance(targetstring, unicode): 284 targetstring = targetstring.encode(self._encoding) 285 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstring) 286 # add the values of a dict 287 elif isinstance(target, dict): 288 for i, targetstring in enumerate(target.itervalues()): 289 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstring) 290 # add a single string 291 else: 292 if isinstance(target, unicode): 293 target = target.encode(self._encoding) 294 if target is None: 295 gpo.po_message_set_msgstr(self._gpo_message, "") 296 else: 297 gpo.po_message_set_msgstr(self._gpo_message, target)
298 target = property(gettarget, settarget) 299
300 - def getid(self):
301 """The unique identifier for this unit according to the convensions in 302 .mo files.""" 303 id = (gpo.po_message_msgid(self._gpo_message) or "").decode(self._encoding) 304 # Gettext does not consider the plural to determine duplicates, only 305 # the msgid. For generation of .mo files, we might want to use this 306 # code to generate the entry for the hash table, but for now, it is 307 # commented out for conformance to gettext. 308 # plural = gpo.po_message_msgid_plural(self._gpo_message) 309 # if not plural is None: 310 # id = '%s\0%s' % (id, plural) 311 context = gpo.po_message_msgctxt(self._gpo_message) 312 if context: 313 id = u"%s\04%s" % (context.decode(self._encoding), id) 314 return id
315
316 - def getnotes(self, origin=None):
317 if origin == None: 318 comments = gpo.po_message_comments(self._gpo_message) + \ 319 gpo.po_message_extracted_comments(self._gpo_message) 320 elif origin == "translator": 321 comments = gpo.po_message_comments(self._gpo_message) 322 elif origin in ["programmer", "developer", "source code"]: 323 comments = gpo.po_message_extracted_comments(self._gpo_message) 324 else: 325 raise ValueError("Comment type not valid") 326 327 if comments and get_libgettextpo_version() < (0, 17, 0): 328 comments = "\n".join([line.strip() for line in comments.split("\n")]) 329 # Let's drop the last newline 330 return comments[:-1].decode(self._encoding)
331
332 - def addnote(self, text, origin=None, position="append"):
333 # ignore empty strings and strings without non-space characters 334 if not (text and text.strip()): 335 return 336 text = data.forceunicode(text) 337 oldnotes = self.getnotes(origin) 338 newnotes = None 339 if oldnotes: 340 if position == "append": 341 newnotes = oldnotes + "\n" + text 342 elif position == "merge": 343 if oldnotes != text: 344 oldnoteslist = oldnotes.split("\n") 345 for newline in text.split("\n"): 346 newline = newline.rstrip() 347 # avoid duplicate comment lines (this might cause some problems) 348 if newline not in oldnotes or len(newline) < 5: 349 oldnoteslist.append(newline) 350 newnotes = "\n".join(oldnoteslist) 351 else: 352 newnotes = text + '\n' + oldnotes 353 else: 354 newnotes = "\n".join([line.rstrip() for line in text.split("\n")]) 355 356 if newnotes: 357 newlines = [] 358 needs_space = get_libgettextpo_version() < (0, 17, 0) 359 for line in newnotes.split("\n"): 360 if line and needs_space: 361 newlines.append(" " + line) 362 else: 363 newlines.append(line) 364 newnotes = "\n".join(newlines).encode(self._encoding) 365 if origin in ["programmer", "developer", "source code"]: 366 gpo.po_message_set_extracted_comments(self._gpo_message, newnotes) 367 else: 368 gpo.po_message_set_comments(self._gpo_message, newnotes)
369
370 - def removenotes(self):
371 gpo.po_message_set_comments(self._gpo_message, "")
372
373 - def copy(self):
374 newpo = self.__class__() 375 newpo._gpo_message = self._gpo_message 376 return newpo
377
378 - def merge(self, otherpo, overwrite=False, comments=True, authoritative=False):
379 """Merges the otherpo (with the same msgid) into this one. 380 381 Overwrite non-blank self.msgstr only if overwrite is True 382 merge comments only if comments is True 383 """ 384 385 if not isinstance(otherpo, pounit): 386 super(pounit, self).merge(otherpo, overwrite, comments) 387 return 388 if comments: 389 self.addnote(otherpo.getnotes("translator"), origin="translator", position="merge") 390 # FIXME mergelists(self.typecomments, otherpo.typecomments) 391 if not authoritative: 392 # We don't bring across otherpo.automaticcomments as we consider ourself 393 # to be the the authority. Same applies to otherpo.msgidcomments 394 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge") 395 self.msgidcomment = otherpo._extract_msgidcomments() or None 396 self.addlocations(otherpo.getlocations()) 397 if not self.istranslated() or overwrite: 398 # Remove kde-style comments from the translation (if any). 399 if self._extract_msgidcomments(otherpo.target): 400 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract_msgidcomments() + '\n', '') 401 self.target = otherpo.target 402 if self.source != otherpo.source or self.getcontext() != otherpo.getcontext(): 403 self.markfuzzy() 404 else: 405 self.markfuzzy(otherpo.isfuzzy()) 406 elif not otherpo.istranslated(): 407 if self.source != otherpo.source: 408 self.markfuzzy() 409 else: 410 if self.target != otherpo.target: 411 self.markfuzzy()
412
413 - def isheader(self):
414 #return self.source == u"" and self.target != u"" 415 # we really want to make sure that there is no msgidcomment or msgctxt 416 return self.getid() == "" and len(self.target) > 0
417
418 - def isblank(self):
419 return len(self.source) == len(self.target) == len(self.getcontext()) == 0
420
421 - def hastypecomment(self, typecomment):
422 return gpo.po_message_is_format(self._gpo_message, typecomment)
423
424 - def settypecomment(self, typecomment, present=True):
425 gpo.po_message_set_format(self._gpo_message, typecomment, present)
426
427 - def hasmarkedcomment(self, commentmarker):
428 commentmarker = "(%s)" % commentmarker 429 for comment in self.getnotes("translator").split("\n"): 430 if comment.startswith(commentmarker): 431 return True 432 return False
433
434 - def isfuzzy(self):
435 return gpo.po_message_is_fuzzy(self._gpo_message)
436
437 - def markfuzzy(self, present=True):
438 gpo.po_message_set_fuzzy(self._gpo_message, present)
439
440 - def isobsolete(self):
441 return gpo.po_message_is_obsolete(self._gpo_message)
442
443 - def makeobsolete(self):
444 # FIXME: libgettexpo currently does not reset other data, we probably want to do that 445 # but a better solution would be for libgettextpo to output correct data on serialisation 446 gpo.po_message_set_obsolete(self._gpo_message, True)
447
448 - def resurrect(self):
449 gpo.po_message_set_obsolete(self._gpo_message, False)
450
451 - def hasplural(self):
452 return gpo.po_message_msgid_plural(self._gpo_message) is not None
453
454 - def _extract_msgidcomments(self, text=None):
455 """Extract KDE style msgid comments from the unit. 456 457 @rtype: String 458 @return: Returns the extracted msgidcomments found in this unit's msgid. 459 """ 460 if not text: 461 text = gpo.po_message_msgid(self._gpo_message).decode(self._encoding) 462 if text: 463 return pocommon.extract_msgid_comment(text) 464 return u""
465
466 - def setmsgidcomment(self, msgidcomment):
467 if msgidcomment: 468 self.source = u"_: %s\n%s" % (msgidcomment, self.source)
469 msgidcomment = property(_extract_msgidcomments, setmsgidcomment) 470
471 - def __str__(self):
472 pf = pofile() 473 pf.addunit(self) 474 return str(pf)
475
476 - def getlocations(self):
477 locations = [] 478 i = 0 479 location = gpo.po_message_filepos(self._gpo_message, i) 480 while location: 481 locname = gpo.po_filepos_file(location) 482 locline = gpo.po_filepos_start_line(location) 483 if locline == -1: 484 locstring = locname 485 else: 486 locstring = locname + ":" + str(locline) 487 locations.append(locstring) 488 i += 1 489 location = gpo.po_message_filepos(self._gpo_message, i) 490 return locations
491
492 - def addlocation(self, location):
493 for loc in location.split(): 494 parts = loc.split(":") 495 file = parts[0] 496 if len(parts) == 2: 497 line = int(parts[1] or "0") 498 else: 499 line = -1 500 gpo.po_message_add_filepos(self._gpo_message, file, line)
501
502 - def getcontext(self):
503 msgctxt = gpo.po_message_msgctxt(self._gpo_message) 504 if msgctxt: 505 return msgctxt.decode(self._encoding) 506 else: 507 msgidcomment = self._extract_msgidcomments() 508 return msgidcomment
509
510 - def buildfromunit(cls, unit):
511 """Build a native unit from a foreign unit, preserving as much 512 information as possible.""" 513 if type(unit) == cls and hasattr(unit, "copy") and callable(unit.copy): 514 return unit.copy() 515 elif isinstance(unit, pocommon.pounit): 516 newunit = cls(unit.source) 517 newunit.target = unit.target 518 #context 519 newunit.msgidcomment = unit._extract_msgidcomments() 520 context = unit.getcontext() 521 if not newunit.msgidcomment and context: 522 gpo.po_message_set_msgctxt(newunit._gpo_message, context) 523 524 locations = unit.getlocations() 525 if locations: 526 newunit.addlocations(locations) 527 notes = unit.getnotes("developer") 528 if notes: 529 newunit.addnote(notes, "developer") 530 notes = unit.getnotes("translator") 531 if notes: 532 newunit.addnote(notes, "translator") 533 if unit.isobsolete(): 534 newunit.makeobsolete() 535 newunit.markfuzzy(unit.isfuzzy()) 536 for tc in ['python-format', 'c-format', 'php-format']: 537 if unit.hastypecomment(tc): 538 newunit.settypecomment(tc) 539 # We assume/guess/hope that there will only be one 540 break 541 return newunit 542 else: 543 return base.TranslationUnit.buildfromunit(unit)
544 buildfromunit = classmethod(buildfromunit) 545 546
547 -class pofile(pocommon.pofile):
548 UnitClass = pounit 549
550 - def __init__(self, inputfile=None, encoding=None, unitclass=pounit):
551 self._gpo_memory_file = None 552 self._gpo_message_iterator = None 553 super(pofile, self).__init__(inputfile=inputfile, encoding=encoding) 554 if inputfile is None: 555 self._gpo_memory_file = gpo.po_file_create() 556 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_file, None)
557
558 - def addunit(self, unit, new=True):
559 if new: 560 gpo.po_message_insert(self._gpo_message_iterator, unit._gpo_message) 561 super(pofile, self).addunit(unit)
562
563 - def removeduplicates(self, duplicatestyle="merge"):
564 """make sure each msgid is unique ; merge comments etc from duplicates into original""" 565 # TODO: can we handle consecutive calls to removeduplicates()? What 566 # about files already containing msgctxt? - test 567 id_dict = {} 568 uniqueunits = [] 569 # TODO: this is using a list as the pos aren't hashable, but this is slow. 570 # probably not used frequently enough to worry about it, though. 571 markedpos = [] 572 def addcomment(thepo): 573 thepo.msgidcomment = " ".join(thepo.getlocations()) 574 markedpos.append(thepo)
575 for thepo in self.units: 576 id = thepo.getid() 577 if thepo.isheader() and not thepo.getlocations(): 578 # header msgids shouldn't be merged... 579 uniqueunits.append(thepo) 580 elif id in id_dict: 581 if duplicatestyle == "merge": 582 if id: 583 id_dict[id].merge(thepo) 584 else: 585 addcomment(thepo) 586 uniqueunits.append(thepo) 587 elif duplicatestyle == "msgctxt": 588 origpo = id_dict[id] 589 if origpo not in markedpos: 590 gpo.po_message_set_msgctxt(origpo._gpo_message, " ".join(origpo.getlocations())) 591 markedpos.append(thepo) 592 gpo.po_message_set_msgctxt(thepo._gpo_message, " ".join(thepo.getlocations())) 593 uniqueunits.append(thepo) 594 else: 595 if not id: 596 if duplicatestyle == "merge": 597 addcomment(thepo) 598 else: 599 gpo.po_message_set_msgctxt(thepo._gpo_message, " ".join(thepo.getlocations())) 600 id_dict[id] = thepo 601 uniqueunits.append(thepo) 602 new_gpo_memory_file = gpo.po_file_create() 603 new_gpo_message_iterator = gpo.po_message_iterator(new_gpo_memory_file, None) 604 for unit in uniqueunits: 605 gpo.po_message_insert(new_gpo_message_iterator, unit._gpo_message) 606 gpo.po_message_iterator_free(self._gpo_message_iterator) 607 self._gpo_message_iterator = new_gpo_message_iterator 608 self._gpo_memory_file = new_gpo_memory_file 609 self.units = uniqueunits
610
611 - def __str__(self):
612 def obsolete_workaround(): 613 # Remove all items that are not output by msgmerge when a unit is obsolete. This is a work 614 # around for bug in libgettextpo 615 # FIXME Do version test in case they fix this bug 616 for unit in self.units: 617 if unit.isobsolete(): 618 gpo.po_message_set_extracted_comments(unit._gpo_message, "") 619 location = gpo.po_message_filepos(unit._gpo_message, 0) 620 while location: 621 gpo.po_message_remove_filepos(unit._gpo_message, 0) 622 location = gpo.po_message_filepos(unit._gpo_message, 0)
623 outputstring = "" 624 if self._gpo_memory_file: 625 obsolete_workaround() 626 f, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 627 os.close(f) 628 self._gpo_memory_file = gpo.po_file_write_v2(self._gpo_memory_file, fname, xerror_handler) 629 f = open(fname) 630 outputstring = f.read() 631 f.close() 632 os.remove(fname) 633 return outputstring 634
635 - def isempty(self):
636 """Returns True if the object doesn't contain any translation units.""" 637 if len(self.units) == 0: 638 return True 639 # Skip the first unit if it is a header. 640 if self.units[0].isheader(): 641 units = self.units[1:] 642 else: 643 units = self.units 644 645 for unit in units: 646 if not unit.isblank() and not unit.isobsolete(): 647 return False 648 return True
649
650 - def parse(self, input):
651 if hasattr(input, 'name'): 652 self.filename = input.name 653 elif not getattr(self, 'filename', ''): 654 self.filename = '' 655 656 if hasattr(input, "read"): 657 posrc = input.read() 658 input.close() 659 input = posrc 660 661 needtmpfile = not os.path.isfile(input) 662 if needtmpfile: 663 # This is not a file - we write the string to a temporary file 664 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 665 os.write(fd, input) 666 input = fname 667 os.close(fd) 668 669 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler) 670 if self._gpo_memory_file is None: 671 print >> sys.stderr, "Error:" 672 673 if needtmpfile: 674 os.remove(input) 675 676 # Handle xerrors here 677 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None) 678 if self._header: 679 charset = gpo.po_header_field(self._header, "Content-Type") 680 if charset: 681 charset = re.search("charset=([^\\s]+)", charset).group(1) 682 self._encoding = encodingToUse(charset) 683 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_file, None) 684 newmessage = gpo.po_next_message(self._gpo_message_iterator) 685 while newmessage: 686 newunit = pounit(gpo_message=newmessage, encoding=self._encoding) 687 self.addunit(newunit, new=False) 688 newmessage = gpo.po_next_message(self._gpo_message_iterator) 689 self._free_iterator()
690
691 - def __del__(self):
692 # We currently disable this while we still get segmentation faults. 693 # Note that this is definitely leaking memory because of this. 694 return 695 self._free_iterator() 696 if self._gpo_memory_file is not None: 697 gpo.po_file_free(self._gpo_memory_file) 698 self._gpo_memory_file = None
699
700 - def _free_iterator(self):
701 # We currently disable this while we still get segmentation faults. 702 # Note that this is definitely leaking memory because of this. 703 return 704 if self._gpo_message_iterator is not None: 705 gpo.po_message_iterator_free(self._gpo_message_iterator) 706 self._gpo_message_iterator = None
707