Implement os.{chdir,rename,rmdir,remove} using Win32 directly.
This commit is contained in:
parent
777367103c
commit
8e0d494e41
|
@ -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__":
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue