Commit 43fec9b4 authored by Steve Dower's avatar Steve Dower

Merge issue #28164 and issue #29409

parents 38dbaced 722e3e27
...@@ -9,7 +9,8 @@ from array import array ...@@ -9,7 +9,8 @@ from array import array
from weakref import proxy from weakref import proxy
from functools import wraps from functools import wraps
from test.support import TESTFN, check_warnings, run_unittest, make_bad_fd, cpython_only from test.support import (TESTFN, TESTFN_UNICODE, check_warnings, run_unittest,
make_bad_fd, cpython_only)
from collections import UserList from collections import UserList
import _io # C implementation of io import _io # C implementation of io
...@@ -432,6 +433,23 @@ class OtherFileTests: ...@@ -432,6 +433,23 @@ class OtherFileTests:
finally: finally:
os.unlink(TESTFN) os.unlink(TESTFN)
@unittest.skipIf(sys.getfilesystemencoding() != 'utf-8',
"test only works for utf-8 filesystems")
def testUtf8BytesOpen(self):
# Opening a UTF-8 bytes filename
try:
fn = TESTFN_UNICODE.encode("utf-8")
except UnicodeEncodeError:
self.skipTest('could not encode %r to utf-8' % TESTFN_UNICODE)
f = self.FileIO(fn, "w")
try:
f.write(b"abc")
f.close()
with open(TESTFN_UNICODE, "rb") as f:
self.assertEqual(f.read(), b"abc")
finally:
os.unlink(TESTFN_UNICODE)
def testConstructorHandlesNULChars(self): def testConstructorHandlesNULChars(self):
fn_with_NUL = 'foo\0bar' fn_with_NUL = 'foo\0bar'
self.assertRaises(ValueError, self.FileIO, fn_with_NUL, 'w') self.assertRaises(ValueError, self.FileIO, fn_with_NUL, 'w')
......
'''Tests for WindowsConsoleIO '''Tests for WindowsConsoleIO
''' '''
import os
import io import io
import unittest
import sys import sys
import unittest
import tempfile
if sys.platform != 'win32': if sys.platform != 'win32':
raise unittest.SkipTest("test only relevant on win32") raise unittest.SkipTest("test only relevant on win32")
...@@ -19,6 +21,16 @@ class WindowsConsoleIOTests(unittest.TestCase): ...@@ -19,6 +21,16 @@ class WindowsConsoleIOTests(unittest.TestCase):
self.assertFalse(issubclass(ConIO, io.TextIOBase)) self.assertFalse(issubclass(ConIO, io.TextIOBase))
def test_open_fd(self): def test_open_fd(self):
self.assertRaisesRegex(ValueError,
"negative file descriptor", ConIO, -1)
fd, _ = tempfile.mkstemp()
try:
self.assertRaisesRegex(ValueError,
"Cannot open non-console file", ConIO, fd)
finally:
os.close(fd)
try: try:
f = ConIO(0) f = ConIO(0)
except ValueError: except ValueError:
...@@ -56,6 +68,20 @@ class WindowsConsoleIOTests(unittest.TestCase): ...@@ -56,6 +68,20 @@ class WindowsConsoleIOTests(unittest.TestCase):
f.close() f.close()
def test_open_name(self): def test_open_name(self):
self.assertRaises(ValueError, ConIO, sys.executable)
f = open('C:/con', 'rb', buffering=0)
self.assertIsInstance(f, ConIO)
f.close()
f = open(r'\\.\conin$', 'rb', buffering=0)
self.assertIsInstance(f, ConIO)
f.close()
f = open('//?/conout$', 'wb', buffering=0)
self.assertIsInstance(f, ConIO)
f.close()
f = ConIO("CON") f = ConIO("CON")
self.assertTrue(f.readable()) self.assertTrue(f.readable())
self.assertFalse(f.writable()) self.assertFalse(f.writable())
......
...@@ -871,6 +871,10 @@ Library ...@@ -871,6 +871,10 @@ Library
Windows Windows
------- -------
- Issue #28164: Correctly handle special console filenames (patch by Eryk Sun)
- Issue #29409: Implement PEP 529 for io.FileIO (Patch by Eryk Sun)
- Issue #29392: Prevent crash when passing invalid arguments into msvcrt module. - Issue #29392: Prevent crash when passing invalid arguments into msvcrt module.
- Issue #25778: winreg does not truncate string correctly (Patch by Eryk Sun) - Issue #25778: winreg does not truncate string correctly (Patch by Eryk Sun)
......
...@@ -230,12 +230,13 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, ...@@ -230,12 +230,13 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
int closefd, PyObject *opener) int closefd, PyObject *opener)
/*[clinic end generated code: output=23413f68e6484bbd input=193164e293d6c097]*/ /*[clinic end generated code: output=23413f68e6484bbd input=193164e293d6c097]*/
{ {
const char *name = NULL;
PyObject *stringobj = NULL;
const char *s;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
Py_UNICODE *widename = NULL; Py_UNICODE *widename = NULL;
#else
const char *name = NULL;
#endif #endif
PyObject *stringobj = NULL;
const char *s;
int ret = 0; int ret = 0;
int rwa = 0, plus = 0; int rwa = 0, plus = 0;
int flags = 0; int flags = 0;
...@@ -277,24 +278,21 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, ...@@ -277,24 +278,21 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
PyErr_Clear(); PyErr_Clear();
} }
if (fd < 0) {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (PyUnicode_Check(nameobj)) {
Py_ssize_t length; Py_ssize_t length;
widename = PyUnicode_AsUnicodeAndSize(nameobj, &length); if (!PyUnicode_FSDecoder(nameobj, &stringobj)) {
if (widename == NULL)
return -1;
if (wcslen(widename) != length) {
PyErr_SetString(PyExc_ValueError, "embedded null character");
return -1; return -1;
} }
} else widename = PyUnicode_AsUnicodeAndSize(stringobj, &length);
#endif if (widename == NULL)
if (fd < 0) return -1;
{ #else
if (!PyUnicode_FSConverter(nameobj, &stringobj)) { if (!PyUnicode_FSConverter(nameobj, &stringobj)) {
return -1; return -1;
} }
name = PyBytes_AS_STRING(stringobj); name = PyBytes_AS_STRING(stringobj);
#endif
} }
s = mode; s = mode;
...@@ -386,11 +384,10 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode, ...@@ -386,11 +384,10 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
do { do {
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (widename != NULL)
self->fd = _wopen(widename, flags, 0666); self->fd = _wopen(widename, flags, 0666);
else #else
#endif
self->fd = open(name, flags, 0666); self->fd = open(name, flags, 0666);
#endif
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} while (self->fd < 0 && errno == EINTR && } while (self->fd < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals())); !(async_err = PyErr_CheckSignals()));
......
...@@ -60,51 +60,68 @@ char _get_console_type(HANDLE handle) { ...@@ -60,51 +60,68 @@ char _get_console_type(HANDLE handle) {
} }
char _PyIO_get_console_type(PyObject *path_or_fd) { char _PyIO_get_console_type(PyObject *path_or_fd) {
int fd; int fd = PyLong_AsLong(path_or_fd);
fd = PyLong_AsLong(path_or_fd);
PyErr_Clear(); PyErr_Clear();
if (fd >= 0) { if (fd >= 0) {
HANDLE handle; HANDLE handle;
_Py_BEGIN_SUPPRESS_IPH _Py_BEGIN_SUPPRESS_IPH
handle = (HANDLE)_get_osfhandle(fd); handle = (HANDLE)_get_osfhandle(fd);
_Py_END_SUPPRESS_IPH _Py_END_SUPPRESS_IPH
if (!handle) if (handle == INVALID_HANDLE_VALUE)
return '\0'; return '\0';
return _get_console_type(handle); return _get_console_type(handle);
} }
PyObject *decoded, *decoded_upper; PyObject *decoded;
wchar_t *decoded_wstr;
int d = PyUnicode_FSDecoder(path_or_fd, &decoded); if (!PyUnicode_FSDecoder(path_or_fd, &decoded)) {
if (!d) {
PyErr_Clear(); PyErr_Clear();
return '\0'; return '\0';
} }
if (!PyUnicode_Check(decoded)) { decoded_wstr = PyUnicode_AsWideCharString(decoded, NULL);
Py_CLEAR(decoded);
return '\0';
}
decoded_upper = PyObject_CallMethod(decoded, "upper", NULL);
Py_CLEAR(decoded); Py_CLEAR(decoded);
if (!decoded_upper) { if (!decoded_wstr) {
PyErr_Clear(); PyErr_Clear();
return '\0'; return '\0';
} }
DWORD length;
wchar_t name_buf[MAX_PATH], *pname_buf = name_buf;
length = GetFullPathNameW(decoded_wstr, MAX_PATH, pname_buf, NULL);
if (length > MAX_PATH) {
pname_buf = PyMem_New(wchar_t, length);
if (pname_buf)
length = GetFullPathNameW(decoded_wstr, length, pname_buf, NULL);
else
length = 0;
}
PyMem_Free(decoded_wstr);
char m = '\0'; char m = '\0';
if (_PyUnicode_EqualToASCIIString(decoded_upper, "CONIN$")) { if (length) {
wchar_t *name = pname_buf;
if (length >= 4 && name[3] == L'\\' &&
(name[2] == L'.' || name[2] == L'?') &&
name[1] == L'\\' && name[0] == L'\\') {
name += 4;
}
if (!_wcsicmp(name, L"CONIN$")) {
m = 'r'; m = 'r';
} else if (_PyUnicode_EqualToASCIIString(decoded_upper, "CONOUT$")) { } else if (!_wcsicmp(name, L"CONOUT$")) {
m = 'w'; m = 'w';
} else if (_PyUnicode_EqualToASCIIString(decoded_upper, "CON")) { } else if (!_wcsicmp(name, L"CON")) {
m = 'x'; m = 'x';
} }
}
Py_CLEAR(decoded_upper); if (pname_buf != name_buf)
PyMem_Free(pname_buf);
return m; return m;
} }
/*[clinic input] /*[clinic input]
module _io module _io
class _io._WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type" class _io._WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment