diff -r b116489d31ff Doc/library/shutil.rst --- a/Doc/library/shutil.rst Thu Aug 21 19:16:17 2014 -0400 +++ b/Doc/library/shutil.rst Fri Aug 22 00:24:34 2014 -0500 @@ -580,6 +580,12 @@ formats, by using :func:`register_unpack_format`. +.. function:: wait_until_deleted(path_name, [timeout]) + + Returns Boolean, True if path_name is deleted. False if timeout + expires. Otherwise blocks indefinetly. + + .. _shutil-archiving-example: Archiving example diff -r b116489d31ff Lib/shutil.py --- a/Lib/shutil.py Thu Aug 21 19:16:17 2014 -0400 +++ b/Lib/shutil.py Fri Aug 22 00:24:34 2014 -0500 @@ -14,6 +14,11 @@ import tarfile try: + from _shutil import wait_until_deleted +except ImportError: + pass + +try: import bz2 del bz2 _BZ2_SUPPORTED = True diff -r b116489d31ff Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py Thu Aug 21 19:16:17 2014 -0400 +++ b/Lib/test/test_shutil.py Fri Aug 22 00:24:34 2014 -0500 @@ -1,4 +1,4 @@ -# Copyright (C) 2003 Python Software Foundation +z Copyright (C) 2003 Python Software Foundation import unittest import shutil @@ -1621,6 +1621,13 @@ shutil.move(self.src_dir, self.dst_dir, copy_function=_copy) self.assertEqual(len(moved), 3) + def test_wait_until_deleted_returns_false(self): + if hasattr(shutil, 'wait_until_deleted'): + handle, path = tempfile.mkstemp() + os.close(handle) + self.assertTrue(shutil.wait_until_deleted(path)) + os.remove(path) + class TestCopyFile(unittest.TestCase): diff -r b116489d31ff Modules/_shutil.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_shutil.c Fri Aug 22 00:24:34 2014 -0500 @@ -0,0 +1,156 @@ +/* shutil.h interface + * + * This module provudes C interface to inotify directory notifications. + * + */ + +#define PY_SSIZE_T_CLEAN +#include "Python.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef MS_WINDOWS +#include +typedef unsigned short mode_t; +#endif + +static int initialized; + +typedef struct inotify_event inotify_event_t; + +PyDoc_STRVAR(wait_doc, +"wait_until_deleted(path_name, timeout)\n\ +\n\ +Watches path_name for deletion. returns True\n\ +when path_name is deleted, or False when timeout \n\ +in seconds expires. Raises FileNotFoundError if \n\ +path_name does not exist."); + +static PyObject * +shutil_wait_until_deleted(PyObject *self, PyObject *args, PyObject *kwds) +{ + + _PyTime_timeval pytime; + static char *kwlist[] = {"path_name", "timeout", NULL }; + + char *path_name; + int res, fd, wd = 0; + long timeout = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|l:wait_until_deleted", kwlist, &path_name, &timeout)) + return NULL; + + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, "timeout must be positive"); + return NULL; + } + + pytime.tv_sec= timeout; + pytime.tv_usec = 0; + + fd = inotify_init(); + wd = inotify_add_watch(fd, path_name, IN_DELETE_SELF); + if (wd < 0) { + PyErr_SetString(PyExc_FileNotFoundError, path_name); + return NULL; + } + + res = read_inotify(fd, wd, &pytime, path_name); + if (res == -1) { + return PyBool_FromLong(0); + } else if (res == -2) { + PyErr_SetString(PyExc_Exception, "Error"); + return NULL; + } + + return PyBool_FromLong((long)res); +} + +int read_inotify(int fd, int wd, _PyTime_timeval *pytime, char *path_name) { + + inotify_event_t *event; + int read_length, x = 0; + int buffer_len = sizeof(event) * 1024 + 16; + char buffer[buffer_len]; + + if (pytime->tv_sec > 0) { + if (read_with_timeout(fd, wd, pytime) <= 0) { + return -1; + } + } + + for (x=0;xname[0] == '\0' && event->wd == wd && IN_DELETE_SELF * event->mask) { + inotify_rm_watch(fd, wd); + return 1; + } + } + + return -2; +} + +int read_with_timeout(int fd, int wd, _PyTime_timeval *pytime) { + fd_set fset; + int rval = 0; + + FD_ZERO(&fset); /* clear the set */ + FD_SET(fd, &fset); + FD_SET(wd, &fset); + + if (wd > fd) { + rval = select(wd + 1, &fset, NULL, NULL, pytime); + } else { + rval = select(fd + 1, &fset, NULL, NULL, pytime); + } + return rval; +} + +/* List of functions */ + +static struct PyMethodDef +shutil_methods[] = { + {"wait_until_deleted", (PyCFunction)shutil_wait_until_deleted, METH_KEYWORDS | METH_VARARGS, wait_doc}, + {NULL, NULL} /* sentinel */ +}; + +static struct PyModuleDef _shutilmodule = { + PyModuleDef_HEAD_INIT, + "shutil", + NULL, + -1, + shutil_methods, + NULL, + NULL, + NULL, + NULL +}; + + +PyMODINIT_FUNC +PyInit__shutil(void) +{ + PyObject *m; + + /* Create the module and add the functions */ + m = PyModule_Create(&_shutilmodule); + if (m == NULL) + return NULL; + + + initialized = 1; + return m; +} diff -r b116489d31ff setup.py --- a/setup.py Thu Aug 21 19:16:17 2014 -0400 +++ b/setup.py Fri Aug 22 00:24:34 2014 -0500 @@ -1542,6 +1542,7 @@ # Platform-specific libraries if host_platform.startswith(('linux', 'freebsd', 'gnukfreebsd')): exts.append( Extension('ossaudiodev', ['ossaudiodev.c']) ) + exts.append( Extension('_shutil', ['_shutil.c'])) else: missing.append('ossaudiodev')