#!/usr/bin/env python2.7
# pylint:disable=missing-docstring,invalid-name,redefined-outer-name,too-few-public-methods
"""\
usage: pgctl-fuser file [file ...]

Shows the pids (of the current user) that have this file opened.
This is useful for finding which processes hold a file lock (flock).
This has the same behavior as `lsof -t file`, but is *much* faster.
"""
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

from .debug import trace


def stat(path):
    from os import stat
    try:
        path = stat(path)
    except EnvironmentError as error:
        trace('fuser suppressed: %s', error)
        return None
    else:
        return (path.st_ino, path.st_dev)


def listdir(path):
    from os import listdir
    try:
        return listdir(path)
    except EnvironmentError as error:
        trace('fuser suppressed: %s', error)
        return ()


def fuser(path):
    """Return the list of pids that have 'path' open, for the current user"""
    search = stat(path)
    if search is None:
        return

    from glob import glob
    for fddir in glob('/proc/*/fd/'):
        try:
            pid = int(fddir.split('/', 3)[2])
        except ValueError:
            continue

        fds = listdir(fddir)
        for fd in fds:
            from os.path import join
            fd = join(fddir, fd)
            found = stat(fd)
            if found == search:
                yield pid
                break


def main():
    from sys import argv
    try:
        path = argv[1]
    except IndexError:
        return __doc__

    for pid in fuser(path):
        print(pid)


if __name__ == '__main__':
    exit(main())

#!/usr/bin/env python2.7
# pylint:disable=missing-docstring,invalid-name,redefined-outer-name,too-few-public-methods
"""\
usage: pgctl-fuser [-d] file [file ...]

Shows the pids (of the current user) that have this file opened.
This is useful for finding which processes hold a file lock (flock).
This has the same behavior as `lsof -t file`, but is *much* faster.
"""
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

from collections import namedtuple

from .debug import trace

StatTuple = namedtuple('StatTuple', ('st_ino', 'std_dev', 'st_nlink'))


def stat(path):
    from os import stat
    try:
        path = stat(path)
    except EnvironmentError as error:
        trace('fuser suppressed: %s', error)
        return None
    else:
        return StatTuple(path.st_ino, path.st_dev, path.st_nlink)


def listdir(path):
    from os import listdir
    try:
        return listdir(path)
    except EnvironmentError as error:
        trace('fuser suppressed: %s', error)
        return ()


def fuser(path, allow_deleted=False):
    """Return the list of pids that have 'path' open, for the current user"""
    search = stat(path)
    if search is None and not allow_deleted:
        return

    from glob import glob
    for fddir in glob('/proc/*/fd/'):
        try:
            pid = int(fddir.split('/', 3)[2])
        except ValueError:
            continue

        fds = listdir(fddir)
        for fd in fds:
            from os.path import join
            fd = join(fddir, fd)
            found = stat(fd)
            if found == search:
                yield pid
                break

            if allow_deleted and found.st_nlink == 0:
                from os import readlink
                if readlink(fd) == path + ' (deleted)':
                    yield pid
                    break


def main(args=None):
    from argparse import ArgumentParser
    from sys import argv
    args = args or argv

    parser = ArgumentParser(description=__doc__)
    parser.add_argument('-d', '--allow-deleted', action='store_true', help='allow deleted files')
    parser.add_argument('file', nargs='+')
    args = parser.parse_args(args[1:])
    if not args.file:
        parser.print_help()

    for f in args.file:
        for pid in fuser(f, allow_deleted=args.allow_deleted):
            print(pid)


if __name__ == '__main__':
    exit(main())

add allow-deleted option to fuser