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

Side by Side Diff: translate/storage/base.py

Issue 81: Fix CPO memory leak SVN Base: https://translate.svn.sourceforge.net/svnroot/translate/src/trunk/
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:
View unified diff
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 # 3 #
4 # Copyright 2006-2008 Zuza Software Foundation 4 # Copyright 2006-2008 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 """Base classes for storage interfaces. 22 """Base classes for storage interfaces.
23 23
24 @organization: Zuza Software Foundation 24 @organization: Zuza Software Foundation
25 @copyright: 2006-2007 Zuza Software Foundation 25 @copyright: 2006-2007 Zuza Software Foundation
26 @license: U{GPL <http://www.fsf.org/licensing/licenses/gpl.html>} 26 @license: U{GPL <http://www.fsf.org/licensing/licenses/gpl.html>}
27 """ 27 """
28 28
29 try: 29 try:
30 import cPickle as pickle 30 import cPickle as pickle
31 except: 31 except:
32 import pickle 32 import pickle
33 from exceptions import NotImplementedError 33 from exceptions import NotImplementedError
34 34
35 def force_override(method, baseclass): 35 def force_override(method, baseclass):
36 """Forces derived classes to override method.""" 36 """Forces derived classes to override method."""
37 37
38 if type(method.im_self) == type(baseclass): 38 if type(method.im_self) == type(baseclass):
39 # then this is a classmethod and im_self is the actual class 39 # then this is a classmethod and im_self is the actual class
40 actualclass = method.im_self 40 actualclass = method.im_self
41 else: 41 else:
42 actualclass = method.im_class 42 actualclass = method.im_class
43 if actualclass != baseclass: 43 if actualclass != baseclass:
44 raise NotImplementedError("%s does not reimplement %s as required by %s" % (actualclass.__name__, method.__name__, baseclass.__name__)) 44 raise NotImplementedError("%s does not reimplement %s as required by %s" % (actualclass.__name__, method.__name__, baseclass.__name__))
45 45
46 class ParseError(Exception): 46 class ParseError(Exception):
47 pass 47 pass
48 48
49 class TranslationUnit(object): 49 class TranslationUnit(object):
50 """Base class for translation units. 50 """Base class for translation units.
(...skipping 203 matching lines...) Show 10 above Show 10 below
254 def isheader(self): 254 def isheader(self):
255 """Indicates whether this unit is a header.""" 255 """Indicates whether this unit is a header."""
256 256
257 return False 257 return False
258 258
259 def isreview(self): 259 def isreview(self):
260 """Indicates whether this unit needs review.""" 260 """Indicates whether this unit needs review."""
261 return False 261 return False
262 262
263 263
264 def isblank(self): 264 def isblank(self):
265 """Used to see if this unit has no source or target string. 265 """Used to see if this unit has no source or target string.
266 266
267 @note: This is probably used more to find translatable units, 267 @note: This is probably used more to find translatable units,
268 and we might want to move in that direction rather and get rid of this. 268 and we might want to move in that direction rather and get rid of this.
269 269
270 """ 270 """
271 271
272 return not (self.source or self.target) 272 return not (self.source or self.target)
273 273
274 def hasplural(self): 274 def hasplural(self):
275 """Tells whether or not this specific unit has plural strings.""" 275 """Tells whether or not this specific unit has plural strings."""
276 276
277 #TODO: Reconsider 277 #TODO: Reconsider
278 return False 278 return False
279 279
280 def getsourcelanguage(self): 280 def getsourcelanguage(self):
281 return getattr(self._store, "sourcelanguage", "en") 281 return getattr(self._store, "sourcelanguage", "en")
282 282
283 def gettargetlanguage(self): 283 def gettargetlanguage(self):
284 return getattr(self._store, "targetlanguage", None) 284 return getattr(self._store, "targetlanguage", None)
285 285
286 def merge(self, otherunit, overwrite=False, comments=True): 286 def merge(self, otherunit, overwrite=False, comments=True):
287 """Do basic format agnostic merging.""" 287 """Do basic format agnostic merging."""
288 288
289 if self.target == "" or overwrite: 289 if self.target == "" or overwrite:
290 self.target = otherunit.target 290 self.target = otherunit.target
291 291
292 def unit_iter(self): 292 def unit_iter(self):
293 """Iterator that only returns this unit.""" 293 """Iterator that only returns this unit."""
294 yield self 294 yield self
295 295
296 def getunits(self): 296 def getunits(self):
297 """This unit in a list.""" 297 """This unit in a list."""
298 return [self] 298 return [self]
299 299
300 def buildfromunit(cls, unit): 300 def buildfromunit(cls, unit):
301 """Build a native unit from a foreign unit, preserving as much 301 """Build a native unit from a foreign unit, preserving as much
302 information as possible.""" 302 information as possible."""
303 303
304 if type(unit) == cls and hasattr(unit, "copy") and callable(unit.copy): 304 #if type(unit) == cls and hasattr(unit, "copy") and callable(unit.copy):
305 return unit.copy() 305 # return unit.copy()
306 newunit = cls(unit.source) 306 newunit = cls(unit.source)
307 newunit.target = unit.target 307 newunit.target = unit.target
308 newunit.markfuzzy(unit.isfuzzy()) 308 newunit.markfuzzy(unit.isfuzzy())
309 locations = unit.getlocations() 309 locations = unit.getlocations()
310 if locations: 310 if locations:
311 newunit.addlocations(locations) 311 newunit.addlocations(locations)
312 notes = unit.getnotes() 312 notes = unit.getnotes()
313 if notes: 313 if notes:
314 newunit.addnote(notes) 314 newunit.addnote(notes)
315 return newunit 315 return newunit
316 buildfromunit = classmethod(buildfromunit) 316 buildfromunit = classmethod(buildfromunit)
317 317
318 class TranslationStore(object): 318 class TranslationStore(object):
319 """Base class for stores for multiple translation units of type UnitClass."" " 319 """Base class for stores for multiple translation units of type UnitClass."" "
320 320
321 UnitClass = TranslationUnit 321 UnitClass = TranslationUnit
322 Mimetypes = None 322 Mimetypes = None
323 Extensions = None 323 Extensions = None
324 324
325 def __init__(self, unitclass=None): 325 def __init__(self, unitclass=None):
326 """Constructs a blank TranslationStore.""" 326 """Constructs a blank TranslationStore."""
327 327
328 self.units = [] 328 self.units = []
329 self.filepath = None 329 self.filepath = None
330 self.translator = "" 330 self.translator = ""
331 self.date = "" 331 self.date = ""
332 self.sourcelanguage = None 332 self.sourcelanguage = None
333 self.targetlanguage = None 333 self.targetlanguage = None
334 if unitclass: 334 if unitclass:
335 self.UnitClass = unitclass 335 self.UnitClass = unitclass
336 super(TranslationStore, self).__init__() 336 super(TranslationStore, self).__init__()
337 337
338 def setsourcelanguage(self, sourcelanguage): 338 def setsourcelanguage(self, sourcelanguage):
339 """Sets the source language for this store""" 339 """Sets the source language for this store"""
340 self.sourcelanguage = sourcelanguage 340 self.sourcelanguage = sourcelanguage
341 341
342 def settargetlanguage(self, targetlanguage): 342 def settargetlanguage(self, targetlanguage):
343 """Sets the target language for this store""" 343 """Sets the target language for this store"""
344 self.targetlanguage = targetlanguage 344 self.targetlanguage = targetlanguage
345 345
346 def unit_iter(self): 346 def unit_iter(self):
347 """Iterator over all the units in this store.""" 347 """Iterator over all the units in this store."""
348 for unit in self.units: 348 for unit in self.units:
349 yield unit 349 yield unit
350 350
351 def getunits(self): 351 def getunits(self):
352 """Return a list of all units in this store.""" 352 """Return a list of all units in this store."""
353 return [unit for unit in self.unit_iter()] 353 return [unit for unit in self.unit_iter()]
354 354
355 def addunit(self, unit): 355 def addunit(self, unit):
356 """Appends the given unit to the object's list of units. 356 """Appends the given unit to the object's list of units.
357 357
358 This method should always be used rather than trying to modify the 358 This method should always be used rather than trying to modify the
359 list manually. 359 list manually.
360 360
361 @type unit: L{TranslationUnit} 361 @type unit: L{TranslationUnit}
362 @param unit: The unit that will be added. 362 @param unit: The unit that will be added.
363 363
364 """ 364 """
365 unit._store = self 365 unit._store = self
366 self.units.append(unit) 366 self.units.append(unit)
367 367
368 def addsourceunit(self, source): 368 def addsourceunit(self, source):
369 """Adds and returns a new unit with the given source string. 369 """Adds and returns a new unit with the given source string.
370 370
371 @rtype: L{TranslationUnit} 371 @rtype: L{TranslationUnit}
372 372
373 """ 373 """
374 374
375 unit = self.UnitClass(source) 375 unit = self.UnitClass(source)
376 self.addunit(unit) 376 self.addunit(unit)
377 return unit 377 return self.units[-1]
378 378
379 def findunit(self, source): 379 def findunit(self, source):
380 """Finds the unit with the given source string. 380 """Finds the unit with the given source string.
381 381
382 @rtype: L{TranslationUnit} or None 382 @rtype: L{TranslationUnit} or None
383 383
384 """ 384 """
385 385
386 if len(getattr(self, "sourceindex", [])): 386 if len(getattr(self, "sourceindex", [])):
387 if source in self.sourceindex: 387 if source in self.sourceindex:
388 return self.sourceindex[source] 388 return self.sourceindex[source]
389 else: 389 else:
390 for unit in self.units: 390 for unit in self.units:
391 if unit.source == source: 391 if unit.source == source:
392 return unit 392 return unit
393 return None 393 return None
394 394
395 def translate(self, source): 395 def translate(self, source):
396 """Returns the translated string for a given source string. 396 """Returns the translated string for a given source string.
397 397
398 @rtype: String or None 398 @rtype: String or None
399 399
400 """ 400 """
401 401
402 unit = self.findunit(source) 402 unit = self.findunit(source)
403 if unit and unit.target: 403 if unit and unit.target:
404 return unit.target 404 return unit.target
405 else: 405 else:
406 return None 406 return None
407 407
408 def makeindex(self): 408 def makeindex(self):
409 """Indexes the items in this store. At least .sourceindex should be usef ull.""" 409 """Indexes the items in this store. At least .sourceindex should be usef ull."""
410 410
411 self.locationindex = {} 411 self.locationindex = {}
412 self.sourceindex = {} 412 self.sourceindex = {}
413 for unit in self.units: 413 for unit in self.units:
414 # Do we need to test if unit.source exists? 414 # Do we need to test if unit.source exists?
415 self.sourceindex[unit.source] = unit 415 self.sourceindex[unit.source] = unit
416 if unit.hasplural(): 416 if unit.hasplural():
417 for nounform in unit.source.strings[1:]: 417 for nounform in unit.source.strings[1:]:
418 self.sourceindex[nounform] = unit 418 self.sourceindex[nounform] = unit
419 for location in unit.getlocations(): 419 for location in unit.getlocations():
420 if location in self.locationindex: 420 if location in self.locationindex:
421 # if sources aren't unique, don't use them 421 # if sources aren't unique, don't use them
422 self.locationindex[location] = None 422 self.locationindex[location] = None
423 else: 423 else:
424 self.locationindex[location] = unit 424 self.locationindex[location] = unit
425 425
426 def __str__(self): 426 def __str__(self):
427 """Converts to a string representation that can be parsed back using L{p arsestring()}.""" 427 """Converts to a string representation that can be parsed back using L{p arsestring()}."""
428 428
429 # We can't pickle fileobj if it is there, so let's hide it for a while. 429 # We can't pickle fileobj if it is there, so let's hide it for a while.
430 fileobj = getattr(self, "fileobj", None) 430 fileobj = getattr(self, "fileobj", None)
431 self.fileobj = None 431 self.fileobj = None
432 dump = pickle.dumps(self) 432 dump = pickle.dumps(self)
433 self.fileobj = fileobj 433 self.fileobj = fileobj
434 return dump 434 return dump
435 435
436 def isempty(self): 436 def isempty(self):
437 """Returns True if the object doesn't contain any translation units.""" 437 """Returns True if the object doesn't contain any translation units."""
438 438
439 if len(self.units) == 0: 439 if len(self.units) == 0:
440 return True 440 return True
441 for unit in self.units: 441 for unit in self.units:
442 if not (unit.isblank() or unit.isheader()): 442 if not (unit.isblank() or unit.isheader()):
443 return False 443 return False
444 return True 444 return True
445 445
446 def _assignname(self): 446 def _assignname(self):
447 """Tries to work out what the name of the filesystem file is and 447 """Tries to work out what the name of the filesystem file is and
448 assigns it to .filename.""" 448 assigns it to .filename."""
449 fileobj = getattr(self, "fileobj", None) 449 fileobj = getattr(self, "fileobj", None)
450 if fileobj: 450 if fileobj:
451 filename = getattr(fileobj, "name", getattr(fileobj, "filename", Non e)) 451 filename = getattr(fileobj, "name", getattr(fileobj, "filename", Non e))
452 if filename: 452 if filename:
453 self.filename = filename 453 self.filename = filename
454 454
455 def parsestring(cls, storestring): 455 def parsestring(cls, storestring):
456 """Converts the string representation back to an object.""" 456 """Converts the string representation back to an object."""
457 newstore = cls() 457 newstore = cls()
458 if storestring: 458 if storestring:
459 newstore.parse(storestring) 459 newstore.parse(storestring)
460 return newstore 460 return newstore
461 parsestring = classmethod(parsestring) 461 parsestring = classmethod(parsestring)
462 462
463 def parse(self, data): 463 def parse(self, data):
464 """parser to process the given source string""" 464 """parser to process the given source string"""
465 self.units = pickle.loads(data).units 465 self.units = pickle.loads(data).units
466 466
467 def savefile(self, storefile): 467 def savefile(self, storefile):
468 """Writes the string representation to the given file (or filename).""" 468 """Writes the string representation to the given file (or filename)."""
469 if isinstance(storefile, basestring): 469 if isinstance(storefile, basestring):
470 storefile = open(storefile, "w") 470 storefile = open(storefile, "w")
471 self.fileobj = storefile 471 self.fileobj = storefile
472 self._assignname() 472 self._assignname()
473 storestring = str(self) 473 storestring = str(self)
474 storefile.write(storestring) 474 storefile.write(storestring)
475 storefile.close() 475 storefile.close()
476 476
477 def save(self): 477 def save(self):
478 """Save to the file that data was originally read from, if available.""" 478 """Save to the file that data was originally read from, if available."""
479 fileobj = getattr(self, "fileobj", None) 479 fileobj = getattr(self, "fileobj", None)
480 if not fileobj: 480 if not fileobj:
481 filename = getattr(self, "filename", None) 481 filename = getattr(self, "filename", None)
482 if filename: 482 if filename:
483 fileobj = file(filename, "w") 483 fileobj = file(filename, "w")
484 else: 484 else:
485 fileobj.close() 485 fileobj.close()
486 filename = getattr(fileobj, "name", getattr(fileobj, "filename", Non e)) 486 filename = getattr(fileobj, "name", getattr(fileobj, "filename", Non e))
487 if not filename: 487 if not filename:
488 raise ValueError("No file or filename to save to") 488 raise ValueError("No file or filename to save to")
489 fileobj = fileobj.__class__(filename, "w") 489 fileobj = fileobj.__class__(filename, "w")
490 self.savefile(fileobj) 490 self.savefile(fileobj)
491 491
492 def parsefile(cls, storefile): 492 def parsefile(cls, storefile):
493 """Reads the given file (or opens the given filename) and parses back to an object.""" 493 """Reads the given file (or opens the given filename) and parses back to an object."""
494 494
495 if isinstance(storefile, basestring): 495 if isinstance(storefile, basestring):
496 storefile = open(storefile, "r") 496 storefile = open(storefile, "r")
497 mode = getattr(storefile, "mode", "r") 497 mode = getattr(storefile, "mode", "r")
498 #For some reason GzipFile returns 1, so we have to test for that here 498 #For some reason GzipFile returns 1, so we have to test for that here
499 if mode == 1 or "r" in mode: 499 if mode == 1 or "r" in mode:
500 storestring = storefile.read() 500 storestring = storefile.read()
501 storefile.close() 501 storefile.close()
502 else: 502 else:
503 storestring = "" 503 storestring = ""
504 newstore = cls.parsestring(storestring) 504 newstore = cls.parsestring(storestring)
505 newstore.fileobj = storefile 505 newstore.fileobj = storefile
506 newstore._assignname() 506 newstore._assignname()
507 return newstore 507 return newstore
508 parsefile = classmethod(parsefile) 508 parsefile = classmethod(parsefile)
509 509
OLDNEW

Powered by Google App Engine
This is Rietveld r159