Implement os.{chdir,rename,rmdir,remove} using Win32 directly.

This commit is contained in:
Martin v. Löwis 2006-05-04 10:08:42 +00:00
parent 777367103c
commit 8e0d494e41
4 changed files with 178 additions and 96 deletions

View File

@ -5,6 +5,7 @@
import os import os
import unittest import unittest
import warnings import warnings
import sys
from test import test_support from test import test_support
warnings.filterwarnings("ignore", "tempnam", RuntimeWarning, __name__) warnings.filterwarnings("ignore", "tempnam", RuntimeWarning, __name__)
@ -364,6 +365,20 @@ class URandomTests (unittest.TestCase):
except NotImplementedError: except NotImplementedError:
pass pass
class Win32ErrorTests(unittest.TestCase):
def test_rename(self):
self.assertRaises(WindowsError, os.rename, test_support.TESTFN, test_support.TESTFN+".bak")
def test_remove(self):
self.assertRaises(WindowsError, os.remove, test_support.TESTFN)
def test_chdir(self):
self.assertRaises(WindowsError, os.chdir, test_support.TESTFN)
if sys.platform != 'win32':
class Win32ErrorTests(unittest.TestCase):
pass
def test_main(): def test_main():
test_support.run_unittest( test_support.run_unittest(
TemporaryFileTests, TemporaryFileTests,
@ -372,7 +387,8 @@ def test_main():
WalkTests, WalkTests,
MakedirTests, MakedirTests,
DevNullTests, DevNullTests,
URandomTests URandomTests,
Win32ErrorTests
) )
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -48,12 +48,12 @@ class TestShutil(unittest.TestCase):
if self.errorState == 0: if self.errorState == 0:
self.assertEqual(func, os.remove) self.assertEqual(func, os.remove)
self.assertEqual(arg, self.childpath) self.assertEqual(arg, self.childpath)
self.assertEqual(exc[0], OSError) self.failUnless(issubclass(exc[0], OSError))
self.errorState = 1 self.errorState = 1
else: else:
self.assertEqual(func, os.rmdir) self.assertEqual(func, os.rmdir)
self.assertEqual(arg, TESTFN) self.assertEqual(arg, TESTFN)
self.assertEqual(exc[0], OSError) self.failUnless(issubclass(exc[0], OSError))
self.errorState = 2 self.errorState = 2
def test_rmtree_dont_delete_file(self): def test_rmtree_dont_delete_file(self):

View File

@ -67,6 +67,9 @@ Core and builtins
Extension Modules Extension Modules
----------------- -----------------
- Use Win32 API to implement os.{chdir,rename,rmdir,remove}. As a result,
these functions now raise WindowsError instead of OSError.
- Calling Tk_Init twice is refused if the first call failed as that - Calling Tk_Init twice is refused if the first call failed as that
may deadlock. may deadlock.

View File

@ -458,21 +458,29 @@ win32_error_unicode(char* function, Py_UNICODE* filename)
static PyObject *_PyUnicode_FromFileSystemEncodedObject(register PyObject *obj) static PyObject *_PyUnicode_FromFileSystemEncodedObject(register PyObject *obj)
{ {
/* XXX Perhaps we should make this API an alias of }
PyObject_Unicode() instead ?! */
if (PyUnicode_CheckExact(obj)) { /* Function suitable for O& conversion */
Py_INCREF(obj); static int
return obj; convert_to_unicode(PyObject *arg, void* _param)
} {
if (PyUnicode_Check(obj)) { PyObject **param = (PyObject**)_param;
if (PyUnicode_CheckExact(arg)) {
Py_INCREF(arg);
*param = arg;
}
else if (PyUnicode_Check(arg)) {
/* For a Unicode subtype that's not a Unicode object, /* For a Unicode subtype that's not a Unicode object,
return a true Unicode object with the same data. */ return a true Unicode object with the same data. */
return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(obj), *param = PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(arg),
PyUnicode_GET_SIZE(obj)); PyUnicode_GET_SIZE(arg));
return *param != NULL;
} }
return PyUnicode_FromEncodedObject(obj, else
Py_FileSystemDefaultEncoding, *param = PyUnicode_FromEncodedObject(arg,
"strict"); Py_FileSystemDefaultEncoding,
"strict");
return (*param) != NULL;
} }
#endif /* Py_WIN_WIDE_FILENAMES */ #endif /* Py_WIN_WIDE_FILENAMES */
@ -589,35 +597,10 @@ unicode_file_names(void)
#endif #endif
static PyObject * static PyObject *
posix_1str(PyObject *args, char *format, int (*func)(const char*), posix_1str(PyObject *args, char *format, int (*func)(const char*))
char *wformat, int (*wfunc)(const Py_UNICODE*))
{ {
char *path1 = NULL; char *path1 = NULL;
int res; int res;
#ifdef Py_WIN_WIDE_FILENAMES
if (unicode_file_names()) {
PyUnicodeObject *po;
if (PyArg_ParseTuple(args, wformat, &po)) {
Py_BEGIN_ALLOW_THREADS
/* PyUnicode_AS_UNICODE OK without thread
lock as it is a simple dereference. */
res = (*wfunc)(PyUnicode_AS_UNICODE(po));
Py_END_ALLOW_THREADS
if (res < 0)
return posix_error_with_unicode_filename(PyUnicode_AS_UNICODE(po));
Py_INCREF(Py_None);
return Py_None;
}
/* Drop the argument parsing error as narrow
strings are also valid. */
PyErr_Clear();
}
#else
/* Platforms that don't support Unicode filenames
shouldn't be passing these extra params */
assert(wformat==NULL && wfunc == NULL);
#endif
if (!PyArg_ParseTuple(args, format, if (!PyArg_ParseTuple(args, format,
Py_FileSystemDefaultEncoding, &path1)) Py_FileSystemDefaultEncoding, &path1))
return NULL; return NULL;
@ -634,52 +617,10 @@ posix_1str(PyObject *args, char *format, int (*func)(const char*),
static PyObject * static PyObject *
posix_2str(PyObject *args, posix_2str(PyObject *args,
char *format, char *format,
int (*func)(const char *, const char *), int (*func)(const char *, const char *))
char *wformat,
int (*wfunc)(const Py_UNICODE *, const Py_UNICODE *))
{ {
char *path1 = NULL, *path2 = NULL; char *path1 = NULL, *path2 = NULL;
int res; int res;
#ifdef Py_WIN_WIDE_FILENAMES
if (unicode_file_names()) {
PyObject *po1;
PyObject *po2;
if (PyArg_ParseTuple(args, wformat, &po1, &po2)) {
if (PyUnicode_Check(po1) || PyUnicode_Check(po2)) {
PyObject *wpath1;
PyObject *wpath2;
wpath1 = _PyUnicode_FromFileSystemEncodedObject(po1);
wpath2 = _PyUnicode_FromFileSystemEncodedObject(po2);
if (!wpath1 || !wpath2) {
Py_XDECREF(wpath1);
Py_XDECREF(wpath2);
return NULL;
}
Py_BEGIN_ALLOW_THREADS
/* PyUnicode_AS_UNICODE OK without thread
lock as it is a simple dereference. */
res = (*wfunc)(PyUnicode_AS_UNICODE(wpath1),
PyUnicode_AS_UNICODE(wpath2));
Py_END_ALLOW_THREADS
Py_XDECREF(wpath1);
Py_XDECREF(wpath2);
if (res != 0)
return posix_error();
Py_INCREF(Py_None);
return Py_None;
}
/* Else flow through as neither is Unicode. */
}
/* Drop the argument parsing error as narrow
strings are also valid. */
PyErr_Clear();
}
#else
/* Platforms that don't support Unicode filenames
shouldn't be passing these extra params */
assert(wformat==NULL && wfunc == NULL);
#endif
if (!PyArg_ParseTuple(args, format, if (!PyArg_ParseTuple(args, format,
Py_FileSystemDefaultEncoding, &path1, Py_FileSystemDefaultEncoding, &path1,
Py_FileSystemDefaultEncoding, &path2)) Py_FileSystemDefaultEncoding, &path2))
@ -696,6 +637,101 @@ posix_2str(PyObject *args,
return Py_None; return Py_None;
} }
#ifdef Py_WIN_WIDE_FILENAMES
static PyObject*
win32_1str(PyObject* args, char* func,
char* format, BOOL (__stdcall *funcA)(LPCSTR),
char* wformat, BOOL (__stdcall *funcW)(LPWSTR))
{
PyObject *uni;
char *ansi;
BOOL result;
if (unicode_file_names()) {
if (!PyArg_ParseTuple(args, wformat, &uni))
PyErr_Clear();
else {
Py_BEGIN_ALLOW_THREADS
result = funcW(PyUnicode_AsUnicode(uni));
Py_END_ALLOW_THREADS
if (!result)
return win32_error_unicode(func, PyUnicode_AsUnicode(uni));
Py_INCREF(Py_None);
return Py_None;
}
}
if (!PyArg_ParseTuple(args, format, &ansi))
return NULL;
Py_BEGIN_ALLOW_THREADS
result = funcA(ansi);
Py_END_ALLOW_THREADS
if (!result)
return win32_error(func, ansi);
Py_INCREF(Py_None);
return Py_None;
}
/* This is a reimplementation of the C library's chdir function,
but one that produces Win32 errors instead of DOS error codes.
chdir is essentially a wrapper around SetCurrentDirectory; however,
it also needs to set "magic" environment variables indicating
the per-drive current directory, which are of the form =<drive>: */
BOOL __stdcall
win32_chdir(LPCSTR path)
{
char new_path[MAX_PATH+1];
int result;
char env[4] = "=x:";
if(!SetCurrentDirectoryA(path))
return FALSE;
result = GetCurrentDirectoryA(MAX_PATH+1, new_path);
if (!result)
return FALSE;
/* In the ANSI API, there should not be any paths longer
than MAX_PATH. */
assert(result <= MAX_PATH+1);
if (strncmp(new_path, "\\\\", 2) == 0 ||
strncmp(new_path, "//", 2) == 0)
/* UNC path, nothing to do. */
return TRUE;
env[1] = new_path[0];
return SetEnvironmentVariableA(env, new_path);
}
/* The Unicode version differs from the ANSI version
since the current directory might exceed MAX_PATH characters */
BOOL __stdcall
win32_wchdir(LPCWSTR path)
{
wchar_t _new_path[MAX_PATH+1], *new_path = _new_path;
int result;
wchar_t env[4] = L"=x:";
if(!SetCurrentDirectoryW(path))
return FALSE;
result = GetCurrentDirectoryW(MAX_PATH+1, new_path);
if (!result)
return FALSE;
if (result > MAX_PATH+1) {
new_path = malloc(result);
if (!new_path) {
SetLastError(ERROR_OUTOFMEMORY);
return FALSE;
}
}
if (wcsncmp(new_path, L"\\\\", 2) == 0 ||
wcsncmp(new_path, L"//", 2) == 0)
/* UNC path, nothing to do. */
return TRUE;
env[1] = new_path[0];
result = SetEnvironmentVariableW(env, new_path);
if (new_path != _new_path)
free(new_path);
return result;
}
#endif
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
/* The CRT of Windows has a number of flaws wrt. its stat() implementation: /* The CRT of Windows has a number of flaws wrt. its stat() implementation:
- time stamps are restricted to second resolution - time stamps are restricted to second resolution
@ -1410,14 +1446,13 @@ static PyObject *
posix_chdir(PyObject *self, PyObject *args) posix_chdir(PyObject *self, PyObject *args)
{ {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
return posix_1str(args, "et:chdir", chdir, "U:chdir", _wchdir); return win32_1str(args, "chdir", "s:chdir", win32_chdir, "U:chdir", win32_wchdir);
#elif defined(PYOS_OS2) && defined(PYCC_GCC) #elif defined(PYOS_OS2) && defined(PYCC_GCC)
return posix_1str(args, "et:chdir", _chdir2, NULL, NULL); return posix_1str(args, "et:chdir", _chdir2);
#elif defined(__VMS) #elif defined(__VMS)
return posix_1str(args, "et:chdir", (int (*)(const char *))chdir, return posix_1str(args, "et:chdir", (int (*)(const char *))chdir);
NULL, NULL);
#else #else
return posix_1str(args, "et:chdir", chdir, NULL, NULL); return posix_1str(args, "et:chdir", chdir);
#endif #endif
} }
@ -1485,7 +1520,7 @@ Change root directory to path.");
static PyObject * static PyObject *
posix_chroot(PyObject *self, PyObject *args) posix_chroot(PyObject *self, PyObject *args)
{ {
return posix_1str(args, "et:chroot", chroot, NULL, NULL); return posix_1str(args, "et:chroot", chroot);
} }
#endif #endif
@ -2071,7 +2106,6 @@ posix_nice(PyObject *self, PyObject *args)
} }
#endif /* HAVE_NICE */ #endif /* HAVE_NICE */
PyDoc_STRVAR(posix_rename__doc__, PyDoc_STRVAR(posix_rename__doc__,
"rename(old, new)\n\n\ "rename(old, new)\n\n\
Rename a file or directory."); Rename a file or directory.");
@ -2080,7 +2114,36 @@ static PyObject *
posix_rename(PyObject *self, PyObject *args) posix_rename(PyObject *self, PyObject *args)
{ {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
return posix_2str(args, "etet:rename", rename, "OO:rename", _wrename); PyObject *o1, *o2;
char *p1, *p2;
BOOL result;
if (unicode_file_names()) {
if (!PyArg_ParseTuple(args, "O&O&:rename",
convert_to_unicode, &o1,
convert_to_unicode, &o2))
PyErr_Clear();
else {
Py_BEGIN_ALLOW_THREADS
result = MoveFileW(PyUnicode_AsUnicode(o1),
PyUnicode_AsUnicode(o2));
Py_END_ALLOW_THREADS
Py_DECREF(o1);
Py_DECREF(o2);
if (!result)
return win32_error("rename", NULL);
Py_INCREF(Py_None);
return Py_None;
}
}
if (!PyArg_ParseTuple(args, "ss:rename", &p1, &p2))
return NULL;
Py_BEGIN_ALLOW_THREADS
result = MoveFileA(p1, p2);
Py_END_ALLOW_THREADS
if (!result)
return win32_error("rename", NULL);
Py_INCREF(Py_None);
return Py_None;
#else #else
return posix_2str(args, "etet:rename", rename, NULL, NULL); return posix_2str(args, "etet:rename", rename, NULL, NULL);
#endif #endif
@ -2095,9 +2158,9 @@ static PyObject *
posix_rmdir(PyObject *self, PyObject *args) posix_rmdir(PyObject *self, PyObject *args)
{ {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
return posix_1str(args, "et:rmdir", rmdir, "U:rmdir", _wrmdir); return win32_1str(args, "rmdir", "s:rmdir", RemoveDirectoryA, "U:rmdir", RemoveDirectoryW);
#else #else
return posix_1str(args, "et:rmdir", rmdir, NULL, NULL); return posix_1str(args, "et:rmdir", rmdir);
#endif #endif
} }
@ -2166,9 +2229,9 @@ static PyObject *
posix_unlink(PyObject *self, PyObject *args) posix_unlink(PyObject *self, PyObject *args)
{ {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
return posix_1str(args, "et:remove", unlink, "U:remove", _wunlink); return win32_1str(args, "remove", "s:remove", DeleteFileA, "U:remove", DeleteFileW);
#else #else
return posix_1str(args, "et:remove", unlink, NULL, NULL); return posix_1str(args, "et:remove", unlink);
#endif #endif
} }