use a lock file to check if only one feed2toot process runs at a given time

This commit is contained in:
Carl Chenet 2019-08-17 15:26:49 +02:00
parent 04d5441be4
commit 8dbf986b34
5 changed files with 127 additions and 0 deletions

View File

@ -52,8 +52,14 @@ class CliParse:
help='tweet all RSS items, regardless of cache') help='tweet all RSS items, regardless of cache')
parser.add_argument('-l', '--limit', dest='limit', default=10, type=int, parser.add_argument('-l', '--limit', dest='limit', default=10, type=int,
help='tweet only LIMIT items (default: %(default)s)') help='tweet only LIMIT items (default: %(default)s)')
parser.add_argument('-t', '--lock-timeout', dest='locktimeout', default=3600, type=int,
help='lock timeout in seconds after which feed2toot can removes the lock itself')
parser.add_argument('--cachefile', dest='cachefile', parser.add_argument('--cachefile', dest='cachefile',
help='location of the cache file (default: %(default)s)') help='location of the cache file (default: %(default)s)')
parser.add_argument('--lockfile', dest='lockfile',
default=os.path.join(os.getenv('XDG_CONFIG_HOME', '~/.config'),
'feed2toot.lock'),
help='location of the lock file (default: %(default)s)')
parser.add_argument('-n', '--dry-run', dest='dryrun', parser.add_argument('-n', '--dry-run', dest='dryrun',
action='store_true', default=False, action='store_true', default=False,
help='Do not actually post tweets') help='Do not actually post tweets')

View File

@ -31,6 +31,7 @@ import feedparser
from feed2toot.confparsers.cache import parsecache from feed2toot.confparsers.cache import parsecache
from feed2toot.confparsers.hashtaglist import parsehashtaglist from feed2toot.confparsers.hashtaglist import parsehashtaglist
from feed2toot.confparsers.feedparser import parsefeedparser from feed2toot.confparsers.feedparser import parsefeedparser
from feed2toot.confparsers.lock import parselock
from feed2toot.confparsers.media import parsemedia from feed2toot.confparsers.media import parsemedia
from feed2toot.confparsers.plugins import parseplugins from feed2toot.confparsers.plugins import parseplugins
from feed2toot.confparsers.rss.pattern import parsepattern from feed2toot.confparsers.rss.pattern import parsepattern
@ -69,6 +70,10 @@ class ConfParse:
# pattern and patter_case_sensitive format option # pattern and patter_case_sensitive format option
################################################# #################################################
options['patterns'], options['patternscasesensitive'] = parsepattern(config) options['patterns'], options['patternscasesensitive'] = parsepattern(config)
#################################################
# lock file options
#################################################
options['lockfile'], options['locktimeout'] = parselock(self.clioptions.lockfile, self.clioptions.locktimeout, config)
############################### ###############################
# addtags option, default: True # addtags option, default: True
############################### ###############################

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright © 2015-2019 Carl Chenet <carl.chenet@ohmytux.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/
# Get values of the lock section
'''Get values of the lock section'''
# standard library imports
import os.path
import sys
def parselock(lockfile, locktimeout, config):
'''Parse configuration values and get values of the hashtaglist section'''
lockfile = lockfile
locktimeout = locktimeout
section = 'lock'
##################
# lockfile option
##################
confoption = 'lock_file'
if config.has_section(section):
lockfile = config.get(section, confoption)
lockfile = os.path.expanduser(lockfile)
lockfileparent = os.path.dirname(lockfile)
if lockfileparent and not os.path.exists(lockfileparent):
sys.exit('The parent directory of the cache file does not exist: {lockfileparent}'.format(lockfileparent=lockfileparent))
######################
# lock_timeout option
######################
if config.has_section(section):
confoption = 'lock_timeout'
if config.has_option(section, confoption):
try:
locktimeout = int(config.get(section, confoption))
except ValueError as err:
sys.exit('Error in configuration with the {confoption} parameter in [{section}]: {err}'.format(confoption=confoption, section=section, err=err))
return lockfile, locktimeout

63
feed2toot/lock.py Normal file
View File

@ -0,0 +1,63 @@
# vim:ts=4:sw=4:ft=python:fileencoding=utf-8
# Copyright © 2015-2019 Carl Chenet <carl.chenet@ohmytux.com>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
'''Manage a lock file'''
# standard libraires imports
import datetime
import logging
import os
import os.path
import sys
class LockFile:
'''LockFile object'''
def __init__(self, lockfile, locktimeout):
'''check the lockfile and the locktimeout'''
self.lockfile = lockfile
ltimeout = datetime.timedelta(seconds=locktimeout)
self.lfdateformat = '%Y-%m-%d_%H-%M-%S'
# if a lock file exists
if os.path.exists(self.lockfile):
if os.path.isfile(self.lockfile):
with open(self.lockfile, 'r') as lf:
lfcontent = lf.read().rstrip()
# lfcontent should be a datetime
logging.debug('Check if lock file is older than timeout ({timeout} secs)'.format(timeout=locktimeout))
locktime = datetime.datetime.strptime(lfcontent, self.lfdateformat)
if locktime < (datetime.datetime.now() - ltimeout):
# remove the lock file
logging.debug('Found an expired lock file')
self.release()
self.create_lock()
else:
# quit because another feed2toot process is running
logging.debug('Found a valid lock file. Exiting immediately.')
sys.exit(0)
else:
# no lock file. Creating one
self.create_lock()
def create_lock(self):
'''Create a lock file'''
with open(self.lockfile, 'w') as lf:
currentdatestring = datetime.datetime.now().strftime(self.lfdateformat)
lf.write(currentdatestring)
logging.debug('lockfile {lockfile} created.'.format(lockfile=self.lockfile))
def release(self):
'''Release the lockfile'''
os.remove(self.lockfile)
logging.debug('Removed lock file.')

View File

@ -32,6 +32,7 @@ from feed2toot.filterentry import FilterEntry
from feed2toot.removeduplicates import RemoveDuplicates from feed2toot.removeduplicates import RemoveDuplicates
from feed2toot.tootpost import TootPost from feed2toot.tootpost import TootPost
from feed2toot.feedcache import FeedCache from feed2toot.feedcache import FeedCache
from feed2toot.lock import LockFile
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
class Main: class Main:
@ -71,6 +72,8 @@ class Main:
tweetformat = conf[2] tweetformat = conf[2]
feeds = conf[3] feeds = conf[3]
plugins = conf[4] plugins = conf[4]
# check the logfile and logtimeout
lockfile = LockFile(options['lockfile'], options['locktimeout'])
# create link to the persistent list # create link to the persistent list
cache = FeedCache(options) cache = FeedCache(options)
if 'hashtaglist' in options and options['hashtaglist']: if 'hashtaglist' in options and options['hashtaglist']:
@ -221,3 +224,5 @@ class Main:
print(err) print(err)
# do not forget to close cache (shelf object) # do not forget to close cache (shelf object)
cache.close() cache.close()
# release the lock file
lockfile.release()