nutools/pyulib/src/ulib/tasks/simple.py

147 lines
4.6 KiB
Python

# -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
import ulib.base.i_need_py23
__all__ = ('SimpleStore',)
import os, re
from os import path
from ulib.base.uio import _s, _u
from ulib.base.lines import Lines
from ulib.base.paths import mkdirof
from ulib.base.tmpfiles import actemp, cltemp
from ulib.base.flock import rdopen, wropen
from base import *
from consts import *
from store import AbstractStore, UnsavedModificationError
LASTID = 'lastid'
class SimpleStore(AbstractStore):
GENERIC_PROPERTIES = AbstractStore.GENERIC_PROPERTIES + [LASTID]
def __init__(self, inf=None):
AbstractStore.__init__(self)
self.__reset()
if inf is not None: self.load(inf)
def __reset(self):
self._tasks = []
self._lastid = 0
self._tids = {}
_tasks, tasks, get_tasks = make_sprop('_tasks', None, setter=False, deleter=False)
_lastid, lastid, get_lastid, set_lastid, del_lastid = make_suprop('_lastid', 0)
_tids = None
def _nextid(self):
_u = self._uio.u
nextid = int(self._lastid) + 1
while self._tids.has_key(_u(nextid)):
nextid += 1
self._lastid = nextid
return nextid
def _ensure_tid(self, task):
if task._tid is None: task.tid = self._nextid()
def _task_attached(self, task, validate=False):
self._ensure_tid(task)
if not self._tids.has_key(task._tid):
self._tids[task._tid] = task
AbstractStore._task_attached(self, task, validate)
def remove(self, task):
if task not in self._tasks: return False
del self._tids[task._tid]
self._tasks.remove(task)
self._modified = True
return True
_fdn = None
_fd = None
_fn = None
def get_name(self):
u"""Retourner le nom de ce store
"""
return _u(self._fn)
name = property(get_name)
RE_SPROPS = re.compile(r'##\s+sprops:')
def load(self, infdn=None, force=False, **ignored):
if self.is_modified() and not force:
raise UnsavedModificationError
if infdn is None:
infdn = self._fdn
if infdn is None: raise ValueError("Source file is required")
else:
self._fdn = path.abspath(infdn)
self._fd, self._fn = path.split(self._fdn)
self.__reset()
self._valid = False
lines = Lines()
if path.exists(infdn):
lines.readlines(infdn, open_func=rdopen)
for line in lines:
if self.RE_SPROPS.match(line) is not None:
self.set_props(**self._line2props(line, False))
elif not line.strip() or line.startswith(u'#'):
# ignorer lignes vides et commençant par "#"
pass
else:
task = self._tf.build_task(**self._line2props(line))
self.tasks.append(task)
self._task_attached(task, True)
self._modified = False
self._valid = True
def __is_sprop(self, line):
return self.RE_SPROPS.match(line) is not None
def should_reload(self):
infdn = self._fdn
if infdn is None: return False
if not path.exists(infdn): return False
lines = Lines()
lines.readlines(infdn, open_func=rdopen, until=self.__is_sprop)
props = {}
for line in lines:
if self.RE_SPROPS.match(line) is not None:
props = self._line2props(line, False)
break
return self.serial != props.get(SERIAL, None)
ENCODING = u"""## -*- coding: %(encoding)s -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=%(encoding)s"""
SPROP = u"""## sprops:"""
def save(self, outfdn=None, force=False):
if not force and not self.is_modified(): return False
if outfdn is None: outfdn = self._fdn
if outfdn is None: raise ValueError("Destination file is required")
lines = self.get_lines(int(self.serial or 0) + 1)
outf = wropen(outfdn)
try:
tmpfdn = outfdn + '.tmp'
mkdirof(tmpfdn)
li = actemp(tmpfdn)
try:
lines.writelines(tmpfdn)
try:
os.rename(tmpfdn, outfdn)
except OSError:
os.remove(outfdn)
os.rename(tmpfdn, outfdn)
finally:
cltemp(li)
finally:
outf.close()
for task in self._tasks: task.modified = False
self._modified = False
return True