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

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: Using low leve accessors Created 1 year, 4 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"""
(...skipping 273 matching lines...) Show 10 above Show 10 below
324 else: 324 else:
325 raise ValueError("Comment type not valid") 325 raise ValueError("Comment type not valid")
326 326
327 if comments and get_libgettextpo_version() < (0, 17, 0): 327 if comments and get_libgettextpo_version() < (0, 17, 0):
328 comments = "\n".join([line.strip() for line in comments.split("\n")] ) 328 comments = "\n".join([line.strip() for line in comments.split("\n")] )
329 # Let's drop the last newline 329 # Let's drop the last newline
330 return comments[:-1].decode(self._encoding) 330 return comments[:-1].decode(self._encoding)
331 331
332 def addnote(self, text, origin=None, position="append"): 332 def addnote(self, text, origin=None, position="append"):
333 # ignore empty strings and strings without non-space characters 333 # ignore empty strings and strings without non-space characters
334 if (not text) or (not text.strip()): 334 if (not text) or (not text.strip()):
335 return 335 return
336 text = data.forceunicode(text) 336 text = data.forceunicode(text)
337 oldnotes = self.getnotes(origin) 337 oldnotes = self.getnotes(origin)
338 newnotes = None 338 newnotes = None
339 if oldnotes: 339 if oldnotes:
340 if position == "append": 340 if position == "append":
341 newnotes = oldnotes + "\n" + text 341 newnotes = oldnotes + "\n" + text
342 elif position == "merge": 342 elif position == "merge":
343 if oldnotes != text: 343 if oldnotes != text:
344 oldnoteslist = oldnotes.split("\n") 344 oldnoteslist = oldnotes.split("\n")
345 for newline in text.split("\n"): 345 for newline in text.split("\n"):
346 newline = newline.rstrip() 346 newline = newline.rstrip()
347 # avoid duplicate comment lines (this might cause some p roblems) 347 # avoid duplicate comment lines (this might cause some p roblems)
348 if newline not in oldnotes or len(newline) < 5: 348 if newline not in oldnotes or len(newline) < 5:
349 oldnoteslist.append(newline) 349 oldnoteslist.append(newline)
350 newnotes = "\n".join(oldnoteslist) 350 newnotes = "\n".join(oldnoteslist)
351 else: 351 else:
352 newnotes = text + '\n' + oldnotes 352 newnotes = text + '\n' + oldnotes
353 else: 353 else:
354 newnotes = "\n".join([line.rstrip() for line in text.split("\n")]) 354 newnotes = "\n".join([line.rstrip() for line in text.split("\n")])
355 355
356 if newnotes: 356 if newnotes:
357 newlines = [] 357 newlines = []
358 needs_space = get_libgettextpo_version() < (0, 17, 0) 358 needs_space = get_libgettextpo_version() < (0, 17, 0)
359 for line in newnotes.split("\n"): 359 for line in newnotes.split("\n"):
360 if line and needs_space: 360 if line and needs_space:
361 newlines.append(" " + line) 361 newlines.append(" " + line)
362 else: 362 else:
363 newlines.append(line) 363 newlines.append(line)
364 newnotes = "\n".join(newlines) 364 newnotes = "\n".join(newlines)
365 if origin in ["programmer", "developer", "source code"]: 365 if origin in ["programmer", "developer", "source code"]:
366 gpo.po_message_set_extracted_comments(self._gpo_message, newnote s) 366 gpo.po_message_set_extracted_comments(self._gpo_message, newnote s)
367 else: 367 else:
368 gpo.po_message_set_comments(self._gpo_message, newnotes) 368 gpo.po_message_set_comments(self._gpo_message, newnotes)
369 369
370 def removenotes(self): 370 def removenotes(self):
371 gpo.po_message_set_comments(self._gpo_message, "") 371 gpo.po_message_set_comments(self._gpo_message, "")
372 372
373 def copy(self): 373 def copy(self):
374 """Returns a copy of the this unit that can be used independently. 374 unit = self.buildfromunit(self)
375 """ 375 #XXX: adding missing data
376 unit = pounit()
377 unit.source = self.source
378 unit.target = self.target
379 unit.markfuzzy(self.isfuzzy())
380 if self.isobsolete(): 376 if self.isobsolete():
381 unit.makeobsolete() 377 unit.makeobsolete()
382 for location in self.getlocations():
383 unit.addlocation(location)
384 notes = []
385 origins = ['translator', 'developer', 'programmer', 'source code']
386 for origin in origins:
387 note = self.getnotes(origin=origin)
388 if note in notes:
389 continue
390 if note:
391 unit.addnote(note, origin=origin)
392 notes.append(note)
393 context = gpo.po_message_msgctxt(self._gpo_message) 378 context = gpo.po_message_msgctxt(self._gpo_message)
394 if context: 379 if context:
395 unit.setcontext(context) 380 unit.setcontext(context)
396 comments = self._extract_msgidcomments() 381 comments = self._extract_msgidcomments()
397 if comments: 382 if comments:
383 print 'HAS COMMENTS', comments
398 unit.setmsgidcomment(comments) 384 unit.setmsgidcomment(comments)
399 return unit 385 return unit
400 386
401 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ): 387 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ):
402 """Merges the otherpo (with the same msgid) into this one. 388 """Merges the otherpo (with the same msgid) into this one.
403 389
404 Overwrite non-blank self.msgstr only if overwrite is True 390 Overwrite non-blank self.msgstr only if overwrite is True
405 merge comments only if comments is True 391 merge comments only if comments is True
406 392
407 """ 393 """
408 394
409 if not isinstance(otherpo, pounit): 395 if not isinstance(otherpo, pounit):
410 super(pounit, self).merge(otherpo, overwrite, comments) 396 super(pounit, self).merge(otherpo, overwrite, comments)
411 return 397 return
412 if comments: 398 if comments:
413 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge") 399 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge")
414 # FIXME mergelists(self.typecomments, otherpo.typecomments) 400 # FIXME mergelists(self.typecomments, otherpo.typecomments)
415 if not authoritative: 401 if not authoritative:
416 # We don't bring across otherpo.automaticcomments as we consider ourself 402 # We don't bring across otherpo.automaticcomments as we consider ourself
417 # to be the the authority. Same applies to otherpo.msgidcomment s 403 # to be the the authority. Same applies to otherpo.msgidcomment s
418 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge") 404 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge")
419 self.msgidcomment = otherpo._extract_msgidcomments() or None 405 self.msgidcomment = otherpo._extract_msgidcomments() or None
420 self.addlocations(otherpo.getlocations()) 406 self.addlocations(otherpo.getlocations())
421 if not self.istranslated() or overwrite: 407 if not self.istranslated() or overwrite:
422 # Remove kde-style comments from the translation (if any). 408 # Remove kde-style comments from the translation (if any).
423 if self._extract_msgidcomments(otherpo.target): 409 if self._extract_msgidcomments(otherpo.target):
424 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '') 410 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '')
425 self.target = otherpo.target 411 self.target = otherpo.target
426 if self.source != otherpo.source: 412 if self.source != otherpo.source:
427 self.markfuzzy() 413 self.markfuzzy()
428 else: 414 else:
429 self.markfuzzy(otherpo.isfuzzy()) 415 self.markfuzzy(otherpo.isfuzzy())
430 elif not otherpo.istranslated(): 416 elif not otherpo.istranslated():
431 if self.source != otherpo.source: 417 if self.source != otherpo.source:
432 self.markfuzzy() 418 self.markfuzzy()
433 else: 419 else:
434 if self.target != otherpo.target: 420 if self.target != otherpo.target:
435 self.markfuzzy() 421 self.markfuzzy()
436 422
437 def isheader(self): 423 def isheader(self):
438 #return self.source == u"" and self.target != u"" 424 #return self.source == u"" and self.target != u""
439 # 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
440 return self.getid() == "" and len(self.target) > 0 426 return self.getid() == "" and len(self.target) > 0
441 427
442 def isblank(self): 428 def isblank(self):
443 return len(self.source) == 0 and len(self.target) == 0 429 return len(self.source) == 0 and len(self.target) == 0
444 430
445 def hastypecomment(self, typecomment): 431 def hastypecomment(self, typecomment):
446 return gpo.po_message_is_format(self._gpo_message, typecomment) 432 return gpo.po_message_is_format(self._gpo_message, typecomment)
447 433
(...skipping 199 matching lines...) Show 10 above Show 10 below
647 # Skip the first unit if it is a header. 633 # Skip the first unit if it is a header.
648 if self.units[0].isheader(): 634 if self.units[0].isheader():
649 units = self.units[1:] 635 units = self.units[1:]
650 else: 636 else:
651 units = self.units 637 units = self.units
652 638
653 for unit in units: 639 for unit in units:
654 if not unit.isblank() and not unit.isobsolete(): 640 if not unit.isblank() and not unit.isobsolete():
655 return False 641 return False
656 return True 642 return True
657 643
658 def parse(self, input): 644 def parse(self, input):
659 if hasattr(input, 'name'): 645 if hasattr(input, 'name'):
660 self.filename = input.name 646 self.filename = input.name
661 elif not getattr(self, 'filename', ''): 647 elif not getattr(self, 'filename', ''):
662 self.filename = '' 648 self.filename = ''
663 649
664 if hasattr(input, "read"): 650 if hasattr(input, "read"):
665 posrc = input.read() 651 posrc = input.read()
666 input.close() 652 input.close()
667 input = posrc 653 input = posrc
668 654
669 needtmpfile = not os.path.isfile(input) 655 needtmpfile = not os.path.isfile(input)
670 if needtmpfile: 656 if needtmpfile:
671 # 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
672 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 658 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po')
673 os.write(fd, input) 659 os.write(fd, input)
674 input = fname 660 input = fname
675 os.close(fd) 661 os.close(fd)
676 662
677 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)
678 if self._gpo_memory_file is None: 664 if self._gpo_memory_file is None:
679 print >> sys.stderr, "Error:" 665 print >> sys.stderr, "Error:"
680 666
681 if needtmpfile: 667 if needtmpfile:
682 os.remove(input) 668 os.remove(input)
683 669
684 # Handle xerrors here 670 # Handle xerrors here
685 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)
686 if self._header: 672 if self._header:
687 charset = gpo.po_header_field(self._header, "Content-Type") 673 charset = gpo.po_header_field(self._header, "Content-Type")
688 if charset: 674 if charset:
689 charset = re.search("charset=([^\\s]+)", charset).group(1) 675 charset = re.search("charset=([^\\s]+)", charset).group(1)
690 self._encoding = encodingToUse(charset) 676 self._encoding = encodingToUse(charset)
691 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)
692 newmessage = gpo.po_next_message(self._gpo_message_iterator) 678 newmessage = gpo.po_next_message(self._gpo_message_iterator)
693 while newmessage: 679 while newmessage:
694 newunit = pounit(gpo_message=newmessage) 680 newunit = pounit(gpo_message=newmessage)
695 self.units.append(newunit) 681 self.units.append(newunit)
696 newmessage = gpo.po_next_message(self._gpo_message_iterator) 682 newmessage = gpo.po_next_message(self._gpo_message_iterator)
683 #self._free_iterator()
697 684
698 def __del__(self): 685 def __del__(self):
686 #print 'DELL CALLED', id(self)
699 # We currently disable this while we still get segmentation faults. 687 # We currently disable this while we still get segmentation faults.
700 # Note that this is definitely leaking memory because of this. 688 # Note that this is definitely leaking memory because of this.
689 #return
701 self._free_iterator() 690 self._free_iterator()
702 if self._gpo_memory_file is not None: 691 if self._gpo_memory_file is not None:
703 gpo.po_file_free(self._gpo_memory_file) 692 gpo.po_file_free(self._gpo_memory_file)
704 self._gpo_memory_file = None 693 self._gpo_memory_file = None
705 694
706 def _free_iterator(self): 695 def _free_iterator(self):
696 #print 'ITERATOR CALLED', id(self)
707 # We currently disable this while we still get segmentation faults. 697 # We currently disable this while we still get segmentation faults.
708 # Note that this is definitely leaking memory because of this. 698 # Note that this is definitely leaking memory because of this.
699 #return
709 if self._gpo_message_iterator is not None: 700 if self._gpo_message_iterator is not None:
710 gpo.po_message_iterator_free(self._gpo_message_iterator) 701 gpo.po_message_iterator_free(self._gpo_message_iterator)
711 self._gpo_message_iterator = None 702 self._gpo_message_iterator = None
LEFTRIGHT

Powered by Google App Engine
This is Rietveld r159