Skip to content

Commit

Permalink
Merge pull request #2 from sayanarijit/dev
Browse files Browse the repository at this point in the history
v2
  • Loading branch information
sayanarijit committed May 22, 2018
2 parents e6acb35 + 010d644 commit 5ea8c47
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 35 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ python:
install:
- python setup.py install
script:
- python setup.py test
- python ./pidlock.py --version
- python ./pidlock.py --name test --command pwd --lockdir ~/.pidlock --verbose
- python ./pidlock.py --name test --command pwd --lockdir ~/.pidlock --verbose -w 1 -m .05
- python ./pidlock.py -n test -c pwd -l ~/.pidlock -v -w 1 -m .05
- python ./pidlock.py -n test -c "sleep 10" -v &
- python ./pidlock.py -n test -c "pwd" -v -w 3; [ $? -eq 1 ] || false
- python ./pidlock.py -n test -c "pwd" -v -w 7 -m .5
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,19 @@ pidlock --name sleepy_script --command 'sleep 10'

### Customization:

* You can pass PID file location and verbosity as arguments
* You can pass PID file location, verbosity, time limit and minimum interval as arguments

```
# Python Usage
locker = PIDLock(lockdir='~/.pidlock', verbose=True)
with locker.lock('sleepy_script', wait=10, mininterval=1):
time.sleep(10)
```
```
# Commandline usage
pidlock -n sleepy_script -c 'sleep 10' -l ~/.pidlock -v
pidlock -n sleepy_script -c 'sleep 10' -l ~/.pidlock -v -w 10 -m 1
# Same as
pidlock --name sleepy_script --command 'sleep 10' --lockdir ~/.pidlock --verbose
pidlock --name sleepy_script --command 'sleep 10' --lockdir ~/.pidlock --verbose --wait 10 --mininterval 1
```
93 changes: 64 additions & 29 deletions pidlock.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
# -*-coding: utf-8 -*-
'''
Author: Arijit Basu
Email: sayanarijit@gmail.com
Documentation: https://github.com/sayanarijit/pidlock
Author: Arijit Basu <sayanarijit@gmail.com>
Documentation: https://github.com/sayanarijit/pidlock#pidlock
'''

from __future__ import print_function
import os
import sys
import time
import psutil
from os import path
from codecs import open
from subprocess import Popen
from contextlib import contextmanager


VERSION = 'v1.1.0'
VERSION = 'v2.0.0'


class PIDLockedException(Exception):
Expand All @@ -23,25 +24,33 @@ def __init__(self, name, pid):


class PIDLock:
"""
PARAMETERS:
lockdir: str: (default: ~/.pidlock) Where to create/store PID lock files
verbose: bool: (default: True) Verbosity
RAISES:
PIDLockedException
EXAMPLE:
import time
from pidlock import PIDLock
locker = PIDLock()
with locker.lock('sleepy_script'):
time.sleep(10)
"""PIDLock class
Args:
lockdir: (str) Where to create/store PID lock files. Default is '~/.pidlock'
verbose: (bool) Prints verbose outputs. Default is False
Example:
>>> from pidlock import PIDLock
>>> locker = PIDLock(lockdir='~/.pidlock', verbose=True)
"""
def __init__(self, lockdir='~/.pidlock', verbose=True):
self.lockdir = path.expanduser(lockdir)
self.verbose = verbose

@contextmanager
def lock(self, name):
def lock(self, name, wait=0, mininterval=1):
"""Locks process till codes inside "with" block is executed
Args:
name: (str) Name of the file to be used as lock
wait: (float) How long (sec.) should it wait for lock to be released. Default is 0
mininterval: (float) Minimum interval (sec.) to balance between performance and resource usage. Default is 1
Raises:
PIDLockedException: PID file is locked by another process
Example:
>>> with locker.lock(name='sleepy_script', wait=0, mininterval=1):
>>> time.sleep(10)
"""
if not path.isdir(self.lockdir): os.mkdir(self.lockdir)

pidfile = path.join(self.lockdir, name + ".pid")
Expand All @@ -52,17 +61,37 @@ def lock(self, name):
f = open(pidfile, encoding='utf-8')
xpid = int(f.read())
f.close()
# If pid exists, quit
# If pid exists
pids = psutil.pids()
pids.remove(pid)
if xpid in pids:
raise PIDLockedException(name, xpid)
if self.verbose:
print('PID file is locked with PID:', xpid)
print('Waiting for lock to be released...')

# Wait till it's released or time limit is over
now = time.time()
while time.time() - now < wait and xpid in pids:
if self.verbose:
print('Still waiting...')
time.sleep(mininterval)
pids = psutil.pids()
pids.remove(pid)

if xpid in pids:
# Time limit is over, raise exception
raise PIDLockedException(name, xpid)

# Else remove lock
os.remove(pidfile)
else:
if self.verbose:
print('Removing old pidfile...')
os.remove(pidfile)

# Else create lock
if self.verbose:
print('Locking "{}" with PID: {}'.format(name, pid))

f = open(pidfile, "w", encoding='utf-8')
f.write(str(pid))
f.close()
Expand All @@ -72,18 +101,20 @@ def lock(self, name):
yield
finally:
# Finally close the PID file
os.remove(pidfile)
if self.verbose:
print('Released lock...')
os.remove(pidfile)


def pidlock_cli():
"""
CLI interface for PID based locking
USAGE:
"""CLI interface for PID based locking
Usage:
pidlock [-h] -n NAME -c COMMAND [--version]
EXAMPLE:
pidlock -n sleepy_script -c 'sleep 10'
Returns:
Returns the returncode returned by the executed command
Example:
$ pidlock -n sleepy_script -c 'sleep 10'
"""
import argparse

Expand All @@ -99,17 +130,21 @@ def pidlock_cli():
help='directory to keep lock files. (default: ~/.pidlock)', default='~/.pidlock')
parser.add_argument('-v', '--verbose',
action='store_true', help='use this flag to make it verbose. (default: False)', default=False)
parser.add_argument('-w', '--wait',
type=float, help='how long (sec.) to wait for PID file to be released. (default: 0)', default=0)
parser.add_argument('-m', '--mininterval',
type=float, help='minimum interval (sec.) to balance between performance and resource usage. (default: 1)', default=1)
parser.add_argument('-V', '--version',
action='version', version='pidlock '+VERSION)

parsed = parser.parse_args()

locker = PIDLock(parsed.lockdir, parsed.verbose)
locker = PIDLock(lockdir=parsed.lockdir, verbose=parsed.verbose)
try:
with locker.lock(parsed.name):
with locker.lock(name=parsed.name, wait=parsed.wait, mininterval=parsed.mininterval):
if parsed.verbose:
print("Running command:", parsed.command)
quit(os.system(parsed.command))
quit(Popen(parsed.command, shell=True).wait())
except PIDLockedException as e:
print('pidlock:', e, file=sys.stderr)
quit(1)
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
argparse==1.4.0
psutil==5.4.3
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from codecs import open
from os import path

VERSION = 'v1.1.5'
VERSION = 'v2.0.0'

here = path.abspath(path.dirname(__file__))

Expand Down

0 comments on commit 5ea8c47

Please sign in to comment.