1
|
|
|
#! /usr/bin/env python |
2
|
|
|
|
3
|
|
|
import sys |
4
|
|
|
import os |
5
|
|
|
import tempfile |
6
|
|
|
|
7
|
|
|
|
8
|
|
|
class SingleInstanceException(BaseException): |
9
|
|
|
pass |
10
|
|
|
|
11
|
|
|
|
12
|
|
|
class SingleInstance: |
13
|
|
|
|
14
|
|
|
""" |
15
|
|
|
If you want to prevent your script from running in parallel just instantiate SingleInstance() class. |
16
|
|
|
If is there another instance already running it will throw a `SingleInstanceException`. |
17
|
|
|
>>> import tendo |
18
|
|
|
... me = SingleInstance() |
19
|
|
|
This option is very useful if you have scripts executed by crontab at small amounts of time. |
20
|
|
|
Remember that this works by creating a lock file with a filename based on the full path to the script file. |
21
|
|
|
Providing a flavor_id will augment the filename with the provided flavor_id, allowing you to create |
22
|
|
|
multiple singleton instances from the same file. This is particularly useful if you want specific |
23
|
|
|
functions to have their own singleton instances. |
24
|
|
|
""" |
25
|
|
|
|
26
|
|
|
def __init__(self, flavor_id=""): |
27
|
|
|
import sys |
28
|
|
|
self.initialized = False |
29
|
|
|
basename = os.path.splitext(os.path.abspath(sys.argv[0]))[0].replace( |
30
|
|
|
"/", "-").replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock' |
31
|
|
|
# os.path.splitext(os.path.abspath(sys.modules['__main__'].__file__))[0].replace("/", "-"). |
32
|
|
|
# replace(":", "").replace("\\", "-") + '-%s' % flavor_id + '.lock' |
33
|
|
|
self.lockfile = os.path.normpath( |
34
|
|
|
tempfile.gettempdir() + '/' + basename) |
35
|
|
|
|
36
|
|
|
if sys.platform == 'win32': |
37
|
|
|
try: |
38
|
|
|
# file already exists, we try to remove (in case previous |
39
|
|
|
# execution was interrupted) |
40
|
|
|
if os.path.exists(self.lockfile): |
41
|
|
|
os.unlink(self.lockfile) |
42
|
|
|
self.fd = os.open( |
43
|
|
|
self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR) |
44
|
|
|
except OSError: |
45
|
|
|
type, e, tb = sys.exc_info() |
46
|
|
|
if e.errno == 13: |
47
|
|
|
return |
48
|
|
|
else: # non Windows |
49
|
|
|
import fcntl |
50
|
|
|
self.fp = open(self.lockfile, 'w') |
51
|
|
|
self.fp.flush() |
52
|
|
|
try: |
53
|
|
|
fcntl.lockf(self.fp, fcntl.LOCK_EX | fcntl.LOCK_NB) |
54
|
|
|
except IOError: |
55
|
|
|
return |
56
|
|
|
self.initialized = True |
57
|
|
|
|
58
|
|
|
def __del__(self): |
59
|
|
|
import sys |
60
|
|
|
import os |
61
|
|
|
if not self.initialized: |
62
|
|
|
return |
63
|
|
|
try: |
64
|
|
|
if sys.platform == 'win32': |
65
|
|
|
if hasattr(self, 'fd'): |
66
|
|
|
os.close(self.fd) |
67
|
|
|
os.unlink(self.lockfile) |
68
|
|
|
else: |
69
|
|
|
import fcntl |
70
|
|
|
fcntl.lockf(self.fp, fcntl.LOCK_UN) |
71
|
|
|
# os.close(self.fp) |
72
|
|
|
if os.path.isfile(self.lockfile): |
73
|
|
|
os.unlink(self.lockfile) |
74
|
|
|
except Exception as e: |
75
|
|
|
raise |
76
|
|
|
|
77
|
|
|
|
78
|
|
|
def f(name): |
79
|
|
|
# tmp = logger.level |
80
|
|
|
# logger.setLevel(logging.CRITICAL) # we do not want to see the warning |
81
|
|
|
try: |
82
|
|
|
me2 = SingleInstance(flavor_id=name) # noqa |
83
|
|
|
except SingleInstanceException: |
84
|
|
|
sys.exit(-1) |
85
|
|
|
# logger.setLevel(tmp) |
86
|
|
|
pass |
87
|
|
|
|