Add a couple more win32file functions

This commit is contained in:
Kovid Goyal 2020-10-08 15:01:25 +05:30
parent 216c5a3629
commit 1754c87256
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 123 additions and 9 deletions

View File

@ -147,6 +147,7 @@ class BuildTest(unittest.TestCase):
@unittest.skipUnless(iswindows, 'winutil is windows only')
def test_winutil(self):
import tempfile
from calibre.constants import plugins
from calibre import strftime
winutil = plugins['winutil'][0]
@ -213,6 +214,41 @@ class BuildTest(unittest.TestCase):
self.assertRaises(OSError, winutil.delete_file, npath)
winutil.set_file_attributes(npath, winutil.FILE_ATTRIBUTE_NORMAL)
winutil.delete_file(npath)
self.assertGreater(min(winutil.get_disk_free_space(None)), 0)
open(path, 'wb').close()
open(npath, 'wb').close()
winutil.move_file(path, npath, winutil.MOVEFILE_WRITE_THROUGH | winutil.MOVEFILE_REPLACE_EXISTING)
self.assertFalse(os.path.exists(path))
os.remove(npath)
dpath = tempfile.mkdtemp(dir=os.path.dirname(path))
dh = winutil.create_file(
dpath, winutil.FILE_LIST_DIRECTORY, winutil.FILE_SHARE_READ, winutil.OPEN_EXISTING, winutil.FILE_FLAG_BACKUP_SEMANTICS,
)
from threading import Thread
events = []
def read_changes():
buffer = b'0' * 8192
events.extend(winutil.read_directory_changes(
dh, buffer, True,
winutil.FILE_NOTIFY_CHANGE_FILE_NAME |
winutil.FILE_NOTIFY_CHANGE_DIR_NAME |
winutil.FILE_NOTIFY_CHANGE_ATTRIBUTES |
winutil.FILE_NOTIFY_CHANGE_SIZE |
winutil.FILE_NOTIFY_CHANGE_LAST_WRITE |
winutil.FILE_NOTIFY_CHANGE_SECURITY
))
t = Thread(target=read_changes, daemon=True)
t.start()
testp = os.path.join(dpath, 'test')
open(testp, 'w').close()
t.join(2)
self.assertTrue(events)
for actions, path in events:
self.assertEqual(os.path.join(dpath, path), testp)
winutil.close_handle(dh)
os.remove(testp)
os.rmdir(dpath)
def test_sqlite(self):
import sqlite3

View File

@ -225,15 +225,6 @@ winutil_set_max_stdio(PyObject *self, PyObject *args) {
Py_RETURN_NONE;
}
static PyObject*
winutil_move_file(PyObject *self, PyObject *args) {
Py_UNICODE *a, *b;
unsigned int flags = MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH;
if (!PyArg_ParseTuple(args, "uu|I", &a, &b, &flags)) return NULL;
if (!MoveFileExW(a, b, flags)) { PyErr_SetFromWindowsErr(0); return NULL; }
Py_RETURN_NONE;
}
static PyObject *
winutil_username(PyObject *self) {
wchar_t buf[UNLEN + 1] = {0};
@ -392,6 +383,9 @@ extern PyObject *winutil_set_file_attributes(PyObject *self, PyObject *args);
extern PyObject *winutil_get_file_size(PyObject *self, PyObject *args);
extern PyObject *winutil_set_file_pointer(PyObject *self, PyObject *args);
extern PyObject *winutil_read_file(PyObject *self, PyObject *args);
extern PyObject *winutil_get_disk_free_space(PyObject *self, PyObject *args);
extern PyObject *winutil_move_file(PyObject *self, PyObject *args);
extern PyObject *winutil_read_directory_changes(PyObject *self, PyObject *args);
static PyMethodDef winutil_methods[] = {
{"special_folder_path", winutil_folder_path, METH_VARARGS,
@ -503,6 +497,10 @@ be a unicode string. Returns unicode strings."
"set_file_pointer(handle, chunk_size=16KB)\n\nWrapper for ReadFile"
},
{"get_disk_free_space", (PyCFunction)winutil_get_disk_free_space, METH_VARARGS,
"get_disk_free_space(path)\n\nWrapper for GetDiskFreeSpaceEx"
},
{"close_handle", (PyCFunction)winutil_close_handle, METH_O,
"close_handle(handle)\n\nWrapper for CloseHandle"
},
@ -523,6 +521,14 @@ be a unicode string. Returns unicode strings."
"set_file_attributes(path, attrs)\n\nWrapper for SetFileAttributes"
},
{"move_file", (PyCFunction)winutil_move_file, METH_VARARGS,
"move_file(a, b, flags)\n\nWrapper for MoveFileEx"
},
{"read_directory_changes", (PyCFunction)winutil_read_directory_changes, METH_VARARGS,
"read_directory_changes(handle, buffer, subtree, flags)\n\nWrapper for ReadDirectoryChangesW"
},
{NULL, NULL, 0, NULL}
};
@ -586,6 +592,27 @@ CALIBRE_MODINIT_FUNC PyInit_winutil(void) {
PyModule_AddIntConstant(m, "FILE_BEGIN", FILE_BEGIN);
PyModule_AddIntConstant(m, "FILE_CURRENT", FILE_CURRENT);
PyModule_AddIntConstant(m, "FILE_END", FILE_END);
PyModule_AddIntConstant(m, "MOVEFILE_COPY_ALLOWED", MOVEFILE_COPY_ALLOWED);
PyModule_AddIntConstant(m, "MOVEFILE_CREATE_HARDLINK", MOVEFILE_CREATE_HARDLINK);
PyModule_AddIntConstant(m, "MOVEFILE_DELAY_UNTIL_REBOOT", MOVEFILE_DELAY_UNTIL_REBOOT);
PyModule_AddIntConstant(m, "MOVEFILE_FAIL_IF_NOT_TRACKABLE", MOVEFILE_FAIL_IF_NOT_TRACKABLE);
PyModule_AddIntConstant(m, "MOVEFILE_REPLACE_EXISTING", MOVEFILE_REPLACE_EXISTING);
PyModule_AddIntConstant(m, "MOVEFILE_WRITE_THROUGH", MOVEFILE_WRITE_THROUGH);
PyModule_AddIntConstant(m, "FILE_NOTIFY_CHANGE_FILE_NAME", FILE_NOTIFY_CHANGE_FILE_NAME);
PyModule_AddIntConstant(m, "FILE_NOTIFY_CHANGE_DIR_NAME", FILE_NOTIFY_CHANGE_DIR_NAME);
PyModule_AddIntConstant(m, "FILE_NOTIFY_CHANGE_ATTRIBUTES", FILE_NOTIFY_CHANGE_ATTRIBUTES);
PyModule_AddIntConstant(m, "FILE_NOTIFY_CHANGE_SIZE", FILE_NOTIFY_CHANGE_SIZE);
PyModule_AddIntConstant(m, "FILE_NOTIFY_CHANGE_LAST_WRITE", FILE_NOTIFY_CHANGE_LAST_WRITE);
PyModule_AddIntConstant(m, "FILE_NOTIFY_CHANGE_LAST_ACCESS", FILE_NOTIFY_CHANGE_LAST_ACCESS);
PyModule_AddIntConstant(m, "FILE_NOTIFY_CHANGE_CREATION", FILE_NOTIFY_CHANGE_CREATION);
PyModule_AddIntConstant(m, "FILE_NOTIFY_CHANGE_SECURITY", FILE_NOTIFY_CHANGE_SECURITY);
PyModule_AddIntConstant(m, "FILE_ACTION_ADDED", FILE_ACTION_ADDED);
PyModule_AddIntConstant(m, "FILE_ACTION_REMOVED", FILE_ACTION_REMOVED);
PyModule_AddIntConstant(m, "FILE_ACTION_MODIFIED", FILE_ACTION_MODIFIED);
PyModule_AddIntConstant(m, "FILE_ACTION_RENAMED_OLD_NAME", FILE_ACTION_RENAMED_OLD_NAME);
PyModule_AddIntConstant(m, "FILE_ACTION_RENAMED_NEW_NAME", FILE_ACTION_RENAMED_NEW_NAME);
PyModule_AddIntConstant(m, "FILE_LIST_DIRECTORY", FILE_LIST_DIRECTORY);
PyModule_AddIntConstant(m, "FILE_FLAG_BACKUP_SEMANTICS", FILE_FLAG_BACKUP_SEMANTICS);
return m;
}

View File

@ -165,6 +165,25 @@ set_error_from_file_handle(HANDLE h) {
extern "C" {
PyObject*
winutil_move_file(PyObject *self, PyObject *args) {
wchar_raii a, b;
unsigned int flags = MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH;
if (!PyArg_ParseTuple(args, "O&O&|I", py_to_wchar_no_none, &a, py_to_wchar_no_none, &b, &flags)) return NULL;
if (!MoveFileExW(a.ptr(), b.ptr(), flags))
return PyErr_SetExcFromWindowsErrWithFilenameObjects(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0), PyTuple_GET_ITEM(args, 1));
Py_RETURN_NONE;
}
PyObject*
winutil_get_disk_free_space(PyObject *self, PyObject *args) {
wchar_raii path;
if (!PyArg_ParseTuple(args, "O&", py_to_wchar, &path)) return NULL;
ULARGE_INTEGER bytes_available_to_caller, total_bytes, total_free_bytes;
if (!GetDiskFreeSpaceEx(path.ptr(), &bytes_available_to_caller, &total_bytes, &total_free_bytes)) return PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, 0, PyTuple_GET_ITEM(args, 0));
return Py_BuildValue("KKK", bytes_available_to_caller.QuadPart, total_bytes.QuadPart, total_free_bytes.QuadPart);
}
PyObject*
winutil_read_file(PyObject *self, PyObject *args) {
unsigned long chunk_size = 16 * 1024;
@ -181,6 +200,38 @@ winutil_read_file(PyObject *self, PyObject *args) {
return ans;
}
PyObject*
winutil_read_directory_changes(PyObject *self, PyObject *args) {
PyObject *buffer, *handle; int watch_subtree; unsigned long filter;
if (!PyArg_ParseTuple(args, "O!O!pk", &PyLong_Type, &handle, &PyBytes_Type, &buffer, &watch_subtree, &filter)) return NULL;
DWORD bytes_returned;
BOOL ok;
Py_BEGIN_ALLOW_THREADS;
ok = ReadDirectoryChangesW(PyLong_AsVoidPtr(handle), PyBytes_AS_STRING(buffer), (DWORD)PyBytes_GET_SIZE(buffer), watch_subtree, filter, &bytes_returned, NULL, NULL);
Py_END_ALLOW_THREADS;
if (!ok) return set_error_from_file_handle(PyLong_AsVoidPtr(handle));
PFILE_NOTIFY_INFORMATION p;
size_t offset = 0;
PyObject *ans = PyList_New(0);
if (!ans) return NULL;
if (bytes_returned) {
do {
p = (PFILE_NOTIFY_INFORMATION)(PyBytes_AS_STRING(buffer) + offset);
offset += p->NextEntryOffset;
if (p->FileNameLength) {
PyObject *temp = Py_BuildValue("ku#", p->Action, p->FileName, p->FileNameLength / sizeof(wchar_t));
if (!temp) { Py_DECREF(ans); return NULL; }
int ret = PyList_Append(ans, temp);
Py_DECREF(temp);
if (ret != 0) { Py_DECREF(ans); return NULL; }
}
} while(p->NextEntryOffset);
} else {
Py_CLEAR(ans);
PyErr_SetString(PyExc_OverflowError, "the change events buffer overflowed, something has changed");
}
return ans;
}
PyObject*
winutil_get_file_size(PyObject *self, PyObject *pyhandle) {