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

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: Created 1 year, 3 months ago
Right Patch Set: Using low leve accessors Created 1 year, 3 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 unit = self.buildfromunit(self) 374 """Returns a copy of the this unit that can be used independently.
375 #XXX: adding missing data 375 """
376 unit = pounit()
377 unit.source = self.source
378 unit.target = self.target
379 unit.markfuzzy(self.isfuzzy())
376 if self.isobsolete(): 380 if self.isobsolete():
377 unit.makeobsolete() 381 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)
378 context = gpo.po_message_msgctxt(self._gpo_message) 393 context = gpo.po_message_msgctxt(self._gpo_message)
379 if context: 394 if context:
380 unit.setcontext(context) 395 unit.setcontext(context)
381 comments = self._extract_msgidcomments() 396 comments = self._extract_msgidcomments()
382 if comments: 397 if comments:
383 print 'HAS COMMENTS', comments
384 unit.setmsgidcomment(comments) 398 unit.setmsgidcomment(comments)
385 return unit 399 return unit
386 400
387 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ): 401 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ):
388 """Merges the otherpo (with the same msgid) into this one. 402 """Merges the otherpo (with the same msgid) into this one.
389 403
390 Overwrite non-blank self.msgstr only if overwrite is True 404 Overwrite non-blank self.msgstr only if overwrite is True
391 merge comments only if comments is True 405 merge comments only if comments is True
392 406
393 """ 407 """
394 408
395 if not isinstance(otherpo, pounit): 409 if not isinstance(otherpo, pounit):
396 super(pounit, self).merge(otherpo, overwrite, comments) 410 super(pounit, self).merge(otherpo, overwrite, comments)
397 return 411 return
398 if comments: 412 if comments:
399 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge") 413 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge")
400 # FIXME mergelists(self.typecomments, otherpo.typecomments) 414 # FIXME mergelists(self.typecomments, otherpo.typecomments)
401 if not authoritative: 415 if not authoritative:
402 # We don't bring across otherpo.automaticcomments as we consider ourself 416 # We don't bring across otherpo.automaticcomments as we consider ourself
403 # to be the the authority. Same applies to otherpo.msgidcomment s 417 # to be the the authority. Same applies to otherpo.msgidcomment s
404 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge") 418 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge")
405 self.msgidcomment = otherpo._extract_msgidcomments() or None 419 self.msgidcomment = otherpo._extract_msgidcomments() or None
406 self.addlocations(otherpo.getlocations()) 420 self.addlocations(otherpo.getlocations())
407 if not self.istranslated() or overwrite: 421 if not self.istranslated() or overwrite:
408 # Remove kde-style comments from the translation (if any). 422 # Remove kde-style comments from the translation (if any).
409 if self._extract_msgidcomments(otherpo.target): 423 if self._extract_msgidcomments(otherpo.target):
410 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '') 424 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '')
411 self.target = otherpo.target 425 self.target = otherpo.target
412 if self.source != otherpo.source: 426 if self.source != otherpo.source:
413 self.markfuzzy() 427 self.markfuzzy()
414 else: 428 else:
415 self.markfuzzy(otherpo.isfuzzy()) 429 self.markfuzzy(otherpo.isfuzzy())
416 elif not otherpo.istranslated(): 430 elif not otherpo.istranslated():
417 if self.source != otherpo.source: 431 if self.source != otherpo.source:
418 self.markfuzzy() 432 self.markfuzzy()
419 else: 433 else:
420 if self.target != otherpo.target: 434 if self.target != otherpo.target:
421 self.markfuzzy() 435 self.markfuzzy()
422 436
423 def isheader(self): 437 def isheader(self):
424 #return self.source == u"" and self.target != u"" 438 #return self.source == u"" and self.target != u""
425 # we really want to make sure that there is no msgidcomment or msgctxt 439 # we really want to make sure that there is no msgidcomment or msgctxt
426 return self.getid() == "" and len(self.target) > 0 440 return self.getid() == "" and len(self.target) > 0
427 441
428 def isblank(self): 442 def isblank(self):
429 return len(self.source) == 0 and len(self.target) == 0 443 return len(self.source) == 0 and len(self.target) == 0
430 444
431 def hastypecomment(self, typecomment): 445 def hastypecomment(self, typecomment):
432 return gpo.po_message_is_format(self._gpo_message, typecomment) 446 return gpo.po_message_is_format(self._gpo_message, typecomment)
433 447
(...skipping 199 matching lines...) Show 10 above Show 10 below
633 # Skip the first unit if it is a header. 647 # Skip the first unit if it is a header.
634 if self.units[0].isheader(): 648 if self.units[0].isheader():
635 units = self.units[1:] 649 units = self.units[1:]
636 else: 650 else:
637 units = self.units 651 units = self.units
638 652
639 for unit in units: 653 for unit in units:
640 if not unit.isblank() and not unit.isobsolete(): 654 if not unit.isblank() and not unit.isobsolete():
641 return False 655 return False
642 return True 656 return True
643 657
644 def parse(self, input): 658 def parse(self, input):
645 if hasattr(input, 'name'): 659 if hasattr(input, 'name'):
646 self.filename = input.name 660 self.filename = input.name
647 elif not getattr(self, 'filename', ''): 661 elif not getattr(self, 'filename', ''):
648 self.filename = '' 662 self.filename = ''
649 663
650 if hasattr(input, "read"): 664 if hasattr(input, "read"):
651 posrc = input.read() 665 posrc = input.read()
652 input.close() 666 input.close()
653 input = posrc 667 input = posrc
654 668
655 needtmpfile = not os.path.isfile(input) 669 needtmpfile = not os.path.isfile(input)
656 if needtmpfile: 670 if needtmpfile:
657 # This is not a file - we write the string to a temporary file 671 # This is not a file - we write the string to a temporary file
658 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 672 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po')
659 os.write(fd, input) 673 os.write(fd, input)
660 input = fname 674 input = fname
661 os.close(fd) 675 os.close(fd)
662 676
663 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler) 677 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler)
664 if self._gpo_memory_file is None: 678 if self._gpo_memory_file is None:
665 print >> sys.stderr, "Error:" 679 print >> sys.stderr, "Error:"
666 680
667 if needtmpfile: 681 if needtmpfile:
668 os.remove(input) 682 os.remove(input)
669 683
670 # Handle xerrors here 684 # Handle xerrors here
671 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None) 685 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None)
672 if self._header: 686 if self._header:
673 charset = gpo.po_header_field(self._header, "Content-Type") 687 charset = gpo.po_header_field(self._header, "Content-Type")
674 if charset: 688 if charset:
675 charset = re.search("charset=([^\\s]+)", charset).group(1) 689 charset = re.search("charset=([^\\s]+)", charset).group(1)
676 self._encoding = encodingToUse(charset) 690 self._encoding = encodingToUse(charset)
677 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_fi le, None) 691 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_fi le, None)
678 newmessage = gpo.po_next_message(self._gpo_message_iterator) 692 newmessage = gpo.po_next_message(self._gpo_message_iterator)
679 while newmessage: 693 while newmessage:
680 newunit = pounit(gpo_message=newmessage) 694 newunit = pounit(gpo_message=newmessage)
681 self.units.append(newunit) 695 self.units.append(newunit)
682 newmessage = gpo.po_next_message(self._gpo_message_iterator) 696 newmessage = gpo.po_next_message(self._gpo_message_iterator)
683 #self._free_iterator()
684 697
685 def __del__(self): 698 def __del__(self):
686 #print 'DELL CALLED', id(self)
687 # We currently disable this while we still get segmentation faults. 699 # We currently disable this while we still get segmentation faults.
688 # Note that this is definitely leaking memory because of this. 700 # Note that this is definitely leaking memory because of this.
689 #return
690 self._free_iterator() 701 self._free_iterator()
691 if self._gpo_memory_file is not None: 702 if self._gpo_memory_file is not None:
692 gpo.po_file_free(self._gpo_memory_file) 703 gpo.po_file_free(self._gpo_memory_file)
693 self._gpo_memory_file = None 704 self._gpo_memory_file = None
694 705
695 def _free_iterator(self): 706 def _free_iterator(self):
696 #print 'ITERATOR CALLED', id(self)
697 # We currently disable this while we still get segmentation faults. 707 # We currently disable this while we still get segmentation faults.
698 # Note that this is definitely leaking memory because of this. 708 # Note that this is definitely leaking memory because of this.
699 #return
700 if self._gpo_message_iterator is not None: 709 if self._gpo_message_iterator is not None:
701 gpo.po_message_iterator_free(self._gpo_message_iterator) 710 gpo.po_message_iterator_free(self._gpo_message_iterator)
702 self._gpo_message_iterator = None 711 self._gpo_message_iterator = None
LEFTRIGHT

Powered by Google App Engine
This is Rietveld r159