8000 bpo-34284: NullNew for tuples of `sys` · python/cpython@a64abba · GitHub
[go: up one dir, main page]

Skip to content

Commit a64abba

Browse files
committed
bpo-34284: NullNew for tuples of sys
1 parent 06f3144 commit a64abba

File tree

3 files changed

+74
-25
lines changed

3 files changed

+74
-25
lines changed

Lib/test/test_types.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import types
1010
import unittest.mock
1111
import weakref
12+
import re
1213

1314
class TypesTests(unittest.TestCase):
1415

@@ -1652,5 +1653,63 @@ def coro():
16521653
'close', 'throw'}))
16531654

16541655

1656+
class ObjectCreationTests(unittest.TestCase):
1657+
def test_simple_create(self):
1658+
class C: pass
1659+
self.assertIsInstance(C(), C)
1660+
self.assertIsInstance(C.__new__(C), C)
1661+
self.assertIsInstance(object.__new__(C), C)
1662+
1663+
def test_create_static_class(self):
1664+
C = dict
1665+
self.assertIsInstance(C(), C)
1666+
self.assertIsInstance(C.__new__(C), C)
1667+
1668+
message = re.escape(
1669+
'object.__new__(dict) is not safe, '
1670+
'use dict.__new__()'
1671+
)
1672+
with self.assertRaisesRegex(TypeError, message):
1673+
object.__new__(C), C
1674+
1675+
def test_create_uncreatable(self):
1676+
C = types.GeneratorType
1677+
1678+
cant_create_message = re.escape("cannot create 'generator' instances")
1679+
not_safe_message = re.escape(
1680+
'object.__new__(generator) is not safe, '
1681+
'use generator.__new__()'
1682+
)
1683+
1684+
with self.assertRaisesRegex(TypeError, cant_create_message):
1685+
C()
1686+
with self.assertRaisesRegex(TypeError, cant_create_message):
1687+
C.__new__(C)
1688+
with self.assertRaisesRegex(TypeError, not_safe_message):
1689+
object.__new__(C)
1690+
1691+
def test_create_sys_tuples(self):
1692+
C = type(sys.flags)
1693+
1694+
cant_create_message = re.escape("cannot create 'sys.flags' instances")
1695+
object_message = re.escape(
1696+
'object.__new__(sys.flags) is not safe, '
1697+
'use sys.flags.__new__()'
1698+
)
1699+
tuple_message = re.escape(
1700+
'tuple.__new__(sys.flags) is not safe, '
1701+
'use sys.flags.__new__()'
1702+
)
1703+
1704+
with self.assertRaisesRegex(TypeError, cant_create_message):
1705+
C()
1706+
with self.assertRaisesRegex(TypeError, cant_create_message):
1707+
C.__new__(C)
1708+
with self.assertRaisesRegex(TypeError, object_message):
1709+
object.__new__(C)
1710+
with self.assertRaisesRegex(TypeError, tuple_message):
1711+
tuple.__new__(C)
1712+
1713+
16551714
if __name__ == '__main__':
16561715
unittest.main()
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
NullNew for old extension types instead of just null
1+
Types that cannot be instantiated now don't have `tp_new` equal to NULL,
2+
but equal to a common helper function that raises TypeError instead.
3+
This is applied to all old types without `tp_new` and types
4+
in the `sys` module that are derived from `tuple`.

Python/sysmodule.c

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,6 +2273,14 @@ static struct PyModuleDef sysmodule = {
22732273
} while (0)
22742274

22752275

2276+
static void
2277+
prevent_user_creation(PyTypeObject *type)
2278+
{
2279+
/* prevent user from creating new instances */
2280+
type->tp_init = NULL;
2281+
type->tp_new = PyType_NullNew;
2282+
}
2283+
22762284
_PyInitError
22772285
_PySys_BeginInit(PyObject **sysmod)
22782286
{
@@ -2370,12 +2378,7 @@ _PySys_BeginInit(PyObject **sysmod)
23702378
}
23712379
version_info = make_version_info();
23722380
SET_SYS_FROM_STRING("version_info", version_info);
2373-
/* prevent user from creating new instances */
2374-
VersionInfoType.tp_init = NULL;
2375-
VersionInfoType.tp_new = NULL;
2376-
res = PyDict_DelItemString(VersionInfoType.tp_dict, "__new__");
2377-
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
2378-
PyErr_Clear();
2381+
prevent_user_creation(&VersionInfoType);
23792382

23802383
/* implementation */
23812384
SET_SYS_FROM_STRING("implementation", make_impl_info(version_info));
@@ -2396,14 +2399,7 @@ _PySys_BeginInit(PyObject **sysmod)
23962399
&windows_version_desc) < 0) {
23972400
goto type_init_failed;
23982401
}
2399-
/* prevent user from creating new instances */
2400-
WindowsVersionType.tp_init = NULL;
2401-
WindowsVersionType.tp_new = NULL;
2402-
assert(!PyErr_Occurred());
2403-
res = PyDict_DelItemString(WindowsVersionType.tp_dict, "__new__");
2404-
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
2405-
PyErr_Clear();
2406-
}
2402+
prevent_user_creation(&WindowsVersionType);
24072403
#endif
24082404

24092405
/* float repr style: 0.03 (short) vs 0.029999999999999999 (legacy) */
@@ -2493,16 +2489,7 @@ _PySys_EndInit(PyObject *sysdict, _PyMainInterpreterConfig *config)
24932489

24942490
/* Set flags to their final values */
24952491
SET_SYS_FROM_STRING_INT_RESULT("flags", make_flags());
2496-
/* prevent user from creating new instances */
2497-
FlagsType.tp_init = NULL;
2498-
FlagsType.tp_new = NULL;
2499-
res = PyDict_DelItemString(FlagsType.tp_dict, "__new__");
2500-
if (res < 0) {
2501-
if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
2502-
return res;
2503-
}
2504-
PyErr_Clear();
2505-
}
2492+
prevent_user_creation(&FlagsType);
25062493

25072494
SET_SYS_FROM_STRING_INT_RESULT("dont_write_bytecode",
25082495
PyBool_FromLong(Py_DontWriteBytecodeFlag));

0 commit comments

Comments
 (0)
0