summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2021-04-30 14:56:27 +0200
committerGitHub <noreply@github.com>2021-04-30 14:56:27 +0200
commit4908fae3d57f68694cf006e89fd7761f45003447 (patch)
treeb4168c26f8bc1017606f2308b34ae8bd1f573999
parent0cad068ec174bbe33fb80460da56eb413f3b9359 (diff)
downloadcpython-git-4908fae3d57f68694cf006e89fd7761f45003447.tar.gz
bpo-43916: PyStdPrinter_Type uses Py_TPFLAGS_DISALLOW_INSTANTIATION (GH-25749)
The PyStdPrinter_Type type now uses the Py_TPFLAGS_DISALLOW_INSTANTIATION flag to disallow instantiation, rather than seting a tp_init method which always fail. Write also unit tests for PyStdPrinter_Type.
-rw-r--r--Lib/test/test_embed.py56
-rw-r--r--Objects/fileobject.c31
-rw-r--r--Python/sysmodule.c2
3 files changed, 61 insertions, 28 deletions
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 646cd0632e..23cf297d4a 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -1473,11 +1473,67 @@ class AuditingTests(EmbeddingTestsMixin, unittest.TestCase):
timeout=support.SHORT_TIMEOUT,
returncode=1)
+
class MiscTests(EmbeddingTestsMixin, unittest.TestCase):
def test_unicode_id_init(self):
# bpo-42882: Test that _PyUnicode_FromId() works
# when Python is initialized multiples times.
self.run_embedded_interpreter("test_unicode_id_init")
+
+class StdPrinterTests(EmbeddingTestsMixin, unittest.TestCase):
+ # Test PyStdPrinter_Type which is used by _PySys_SetPreliminaryStderr():
+ # "Set up a preliminary stderr printer until we have enough
+ # infrastructure for the io module in place."
+
+ def get_stdout_fd(self):
+ return sys.__stdout__.fileno()
+
+ def create_printer(self, fd):
+ ctypes = import_helper.import_module('ctypes')
+ PyFile_NewStdPrinter = ctypes.pythonapi.PyFile_NewStdPrinter
+ PyFile_NewStdPrinter.argtypes = (ctypes.c_int,)
+ PyFile_NewStdPrinter.restype = ctypes.py_object
+ return PyFile_NewStdPrinter(fd)
+
+ def test_write(self):
+ message = "unicode:\xe9-\u20ac-\udc80!\n"
+
+ stdout_fd = self.get_stdout_fd()
+ stdout_fd_copy = os.dup(stdout_fd)
+ self.addCleanup(os.close, stdout_fd_copy)
+
+ rfd, wfd = os.pipe()
+ self.addCleanup(os.close, rfd)
+ self.addCleanup(os.close, wfd)
+ try:
+ # PyFile_NewStdPrinter() only accepts fileno(stdout)
+ # or fileno(stderr) file descriptor.
+ os.dup2(wfd, stdout_fd)
+
+ printer = self.create_printer(stdout_fd)
+ printer.write(message)
+ finally:
+ os.dup2(stdout_fd_copy, stdout_fd)
+
+ data = os.read(rfd, 100)
+ self.assertEqual(data, message.encode('utf8', 'backslashreplace'))
+
+ def test_methods(self):
+ fd = self.get_stdout_fd()
+ printer = self.create_printer(fd)
+ self.assertEqual(printer.fileno(), fd)
+ self.assertEqual(printer.isatty(), os.isatty(fd))
+ printer.flush() # noop
+ printer.close() # noop
+
+ def test_disallow_instantiation(self):
+ fd = self.get_stdout_fd()
+ printer = self.create_printer(fd)
+ PyStdPrinter_Type = type(printer)
+ with self.assertRaises(TypeError):
+ PyStdPrinter_Type(fd)
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/Objects/fileobject.c b/Objects/fileobject.c
index 9b89448006..5a2816f552 100644
--- a/Objects/fileobject.c
+++ b/Objects/fileobject.c
@@ -325,29 +325,6 @@ typedef struct {
int fd;
} PyStdPrinter_Object;
-static PyObject *
-stdprinter_new(PyTypeObject *type, PyObject *args, PyObject *kews)
-{
- PyStdPrinter_Object *self;
-
- assert(type != NULL && type->tp_alloc != NULL);
-
- self = (PyStdPrinter_Object *) type->tp_alloc(type, 0);
- if (self != NULL) {
- self->fd = -1;
- }
-
- return (PyObject *) self;
-}
-
-static int
-stdprinter_init(PyObject *self, PyObject *args, PyObject *kwds)
-{
- PyErr_SetString(PyExc_TypeError,
- "cannot create 'stderrprinter' instances");
- return -1;
-}
-
PyObject *
PyFile_NewStdPrinter(int fd)
{
@@ -390,7 +367,7 @@ stdprinter_write(PyStdPrinter_Object *self, PyObject *args)
return NULL;
}
- /* Encode Unicode to UTF-8/surrogateescape */
+ /* Encode Unicode to UTF-8/backslashreplace */
str = PyUnicode_AsUTF8AndSize(unicode, &n);
if (str == NULL) {
PyErr_Clear();
@@ -507,7 +484,7 @@ PyTypeObject PyStdPrinter_Type = {
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
@@ -523,9 +500,9 @@ PyTypeObject PyStdPrinter_Type = {
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
- stdprinter_init, /* tp_init */
+ 0, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
- stdprinter_new, /* tp_new */
+ 0, /* tp_new */
PyObject_Del, /* tp_free */
};
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 36297ff82e..2190bbf37f 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -3007,7 +3007,7 @@ err_occurred:
/* Set up a preliminary stderr printer until we have enough
infrastructure for the io module in place.
- Use UTF-8/surrogateescape and ignore EAGAIN errors. */
+ Use UTF-8/backslashreplace and ignore EAGAIN errors. */
static PyStatus
_PySys_SetPreliminaryStderr(PyObject *sysdict)
{