Python: module pour créer un fichier de verrouillage basé sur PID?

J'écris un script Python qui peut ou non (selon un tas de choses) fonctionner pendant longtemps, et je voudrais m'assurer que plusieurs instances (démarrées via cron) ne marchent pas les unes sur les autres. La façon logique de le faire semble être un fichier de verrouillage basé sur PID... mais je ne veux pas réinventer la roue s'il y a déjà du code pour le faire.

Alors, Existe-t-il un module Python qui gérera les détails d'un fichier de verrouillage PID?

22
demandé sur David Wolever 2009-09-18 18:10:52

6 réponses

Si vous pouvez utiliser GPLv2, Mercurial a un module pour cela:

Http://bitbucket.org/mirror/mercurial/src/tip/mercurial/lock.py

Exemple d'utilisation:

from mercurial import error, lock

try:
    l = lock.lock("/path/to/lock", timeout=600) # wait at most 10 minutes
    # do something
except error.LockHeld:
     # couldn't take the lock
else:
    l.release()
8
répondu tonfa 2009-09-18 14:35:05

Cela peut vous aider: lockfile

11
répondu ennuikiller 2009-09-18 14:18:47

Je crois que vous trouverez les informations nécessaires ici. La page en question fait référence à un paquet pour construire des démons en python: ce processus implique la création d'un fichier de verrouillage PID.

4
répondu jldupont 2009-09-18 14:20:16

Il existe une recette sur ActiveState lors de la création de fichiers de verrouillage.

Pour générer le nom de fichier, vous pouvez utiliser os.getpid () {[2] } pour obtenir le PID.

2
répondu Andre Miller 2009-09-18 14:23:15

Je sais que c'est un vieux thread, mais j'ai aussi créé un verrou simple qui ne repose que sur des bibliothèques natives python:

import fcntl
import errno


class FileLock:
    def __init__(self, filename=None):
        self.filename = os.path.expanduser('~') + '/LOCK_FILE' if filename is None else filename
        self.lock_file = open(self.filename, 'w+')

    def unlock(self):
        fcntl.flock(self.lock_file, fcntl.LOCK_UN)

    def lock(self, maximum_wait=300):
        waited = 0
        while True:
            try:
                fcntl.flock(self.lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
                return True
            except IOError as e:
                if e.errno != errno.EAGAIN:
                    raise e
                else:
                    time.sleep(1)
                    waited += 1
                    if waited >= maximum_wait:
                        return False
2
répondu shahins 2017-03-23 04:31:03

J'ai été assez mécontent de tout cela, alors j'ai écrit ceci:

class Pidfile():
    def __init__(self, path, log=sys.stdout.write, warn=sys.stderr.write):
        self.pidfile = path
        self.log = log
        self.warn = warn

    def __enter__(self):
        try:
            self.pidfd = os.open(self.pidfile, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
            self.log('locked pidfile %s' % self.pidfile)
        except OSError as e:
            if e.errno == errno.EEXIST:
                pid = self._check()
                if pid:
                    self.pidfd = None
                    raise ProcessRunningException('process already running in %s as pid %s' % (self.pidfile, pid));
                else:
                    os.remove(self.pidfile)
                    self.warn('removed staled lockfile %s' % (self.pidfile))
                    self.pidfd = os.open(self.pidfile, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
            else:
                raise

        os.write(self.pidfd, str(os.getpid()))
        os.close(self.pidfd)
        return self

    def __exit__(self, t, e, tb):
        # return false to raise, true to pass
        if t is None:
            # normal condition, no exception
            self._remove()
            return True
        elif t is PidfileProcessRunningException:
            # do not remove the other process lockfile
            return False
        else:
            # other exception
            if self.pidfd:
                # this was our lockfile, removing
                self._remove()
            return False

    def _remove(self):
        self.log('removed pidfile %s' % self.pidfile)
        os.remove(self.pidfile)

    def _check(self):
        """check if a process is still running

the process id is expected to be in pidfile, which should exist.

if it is still running, returns the pid, if not, return False."""
        with open(self.pidfile, 'r') as f:
            try:
                pidstr = f.read()
                pid = int(pidstr)
            except ValueError:
                # not an integer
                self.log("not an integer: %s" % pidstr)
                return False
            try:
                os.kill(pid, 0)
            except OSError:
                self.log("can't deliver signal to %s" % pid)
                return False
            else:
                return pid

class ProcessRunningException(BaseException):
    pass

Pour être utilisé quelque chose comme ceci:

try:
    with Pidfile(args.pidfile):
        process(args)
except ProcessRunningException:
    print "the pid file is in use, oops."
1
répondu anarcat 2014-05-23 19:23:50