Skip to content

Commit 04f0bbf

Browse files
DinoVYhg1s
authored andcommitted
bpo-38075: Port _randommodule.c to PEP-384 (GH-15798)
- Migrate `Random_Type` to `PyType_FromSpec` - To simulate an old use of `PyLong_Type.tp_as_number->nb_absolute`, I added code to the module init function to stash `int.__abs__` for later use. Ideally we'd use `PyType_GetSlot()` instead, but it doesn't currently work for static types in CPython, and implementing it just for this case doesn't seem worth it. - Do exact check for long and dispatch to PyNumber_Absolute, use vector call when not exact.
1 parent 42671ae commit 04f0bbf

File tree

2 files changed

+93
-57
lines changed

2 files changed

+93
-57
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The random module is now PEP-384 compatible

Modules/_randommodule.c

Lines changed: 92 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,23 @@
7979
#define UPPER_MASK 0x80000000U /* most significant w-r bits */
8080
#define LOWER_MASK 0x7fffffffU /* least significant r bits */
8181

82+
typedef struct {
83+
PyObject *Random_Type;
84+
PyObject *Long___abs__;
85+
} _randomstate;
86+
87+
#define _randomstate(o) ((_randomstate *)PyModule_GetState(o))
88+
89+
static struct PyModuleDef _randommodule;
90+
91+
#define _randomstate_global _randomstate(PyState_FindModule(&_randommodule))
92+
8293
typedef struct {
8394
PyObject_HEAD
8495
int index;
8596
uint32_t state[N];
8697
} RandomObject;
8798

88-
static PyTypeObject Random_Type;
89-
90-
#define RandomObject_Check(v) (Py_TYPE(v) == &Random_Type)
9199

92100
#include "clinic/_randommodule.c.h"
93101

@@ -256,6 +264,7 @@ random_seed(RandomObject *self, PyObject *arg)
256264
uint32_t *key = NULL;
257265
size_t bits, keyused;
258266
int res;
267+
PyObject *args[1];
259268

260269
if (arg == NULL || arg == Py_None) {
261270
if (random_seed_urandom(self) < 0) {
@@ -272,10 +281,14 @@ random_seed(RandomObject *self, PyObject *arg)
272281
* So: if the arg is a PyLong, use its absolute value.
273282
* Otherwise use its hash value, cast to unsigned.
274283
*/
275-
if (PyLong_Check(arg)) {
284+
if (PyLong_CheckExact(arg)) {
285+
n = PyNumber_Absolute(arg);
286+
} else if (PyLong_Check(arg)) {
276287
/* Calling int.__abs__() prevents calling arg.__abs__(), which might
277288
return an invalid value. See issue #31478. */
278-
n = PyLong_Type.tp_as_number->nb_absolute(arg);
289+
args[0] = arg;
290+
n = _PyObject_Vectorcall(_randomstate_global->Long___abs__, args, 0,
291+
NULL);
279292
}
280293
else {
281294
Py_hash_t hash = PyObject_Hash(arg);
@@ -500,10 +513,12 @@ random_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
500513
RandomObject *self;
501514
PyObject *tmp;
502515

503-
if (type == &Random_Type && !_PyArg_NoKeywords("Random", kwds))
516+
if (type == (PyTypeObject*)_randomstate_global->Random_Type &&
517+
!_PyArg_NoKeywords("Random()", kwds)) {
504518
return NULL;
519+
}
505520

506-
self = (RandomObject *)type->tp_alloc(type, 0);
521+
self = (RandomObject *)PyType_GenericAlloc(type, 0);
507522
if (self == NULL)
508523
return NULL;
509524
tmp = random_seed(self, args);
@@ -527,77 +542,97 @@ static PyMethodDef random_methods[] = {
527542
PyDoc_STRVAR(random_doc,
528543
"Random() -> create a random number generator with its own internal state.");
529544

530-
static PyTypeObject Random_Type = {
531-
PyVarObject_HEAD_INIT(NULL, 0)
532-
"_random.Random", /*tp_name*/
533-
sizeof(RandomObject), /*tp_basicsize*/
534-
0, /*tp_itemsize*/
535-
/* methods */
536-
0, /*tp_dealloc*/
537-
0, /*tp_vectorcall_offset*/
538-
0, /*tp_getattr*/
539-
0, /*tp_setattr*/
540-
0, /*tp_as_async*/
541-
0, /*tp_repr*/
542-
0, /*tp_as_number*/
543-
0, /*tp_as_sequence*/
544-
0, /*tp_as_mapping*/
545-
0, /*tp_hash*/
546-
0, /*tp_call*/
547-
0, /*tp_str*/
548-
PyObject_GenericGetAttr, /*tp_getattro*/
549-
0, /*tp_setattro*/
550-
0, /*tp_as_buffer*/
551-
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
552-
random_doc, /*tp_doc*/
553-
0, /*tp_traverse*/
554-
0, /*tp_clear*/
555-
0, /*tp_richcompare*/
556-
0, /*tp_weaklistoffset*/
557-
0, /*tp_iter*/
558-
0, /*tp_iternext*/
559-
random_methods, /*tp_methods*/
560-
0, /*tp_members*/
561-
0, /*tp_getset*/
562-
0, /*tp_base*/
563-
0, /*tp_dict*/
564-
0, /*tp_descr_get*/
565-
0, /*tp_descr_set*/
566-
0, /*tp_dictoffset*/
567-
0, /*tp_init*/
568-
0, /*tp_alloc*/
569-
random_new, /*tp_new*/
570-
PyObject_Free, /*tp_free*/
571-
0, /*tp_is_gc*/
545+
static PyType_Slot Random_Type_slots[] = {
546+
{Py_tp_doc, random_doc},
547+
{Py_tp_methods, random_methods},
548+
{Py_tp_new, random_new},
549+
{Py_tp_free, PyObject_Free},
550+
{0, 0},
551+
};
552+
553+
static PyType_Spec Random_Type_spec = {
554+
"_random.Random",
555+
sizeof(RandomObject),
556+
0,
557+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
558+
Random_Type_slots
572559
};
573560

574561
PyDoc_STRVAR(module_doc,
575562
"Module implements the Mersenne Twister random number generator.");
576563

564+
static int
565+
_random_traverse(PyObject *module, visitproc visit, void *arg)
566+
{
567+
Py_VISIT(_randomstate(module)->Random_Type);
568+
return 0;
569+
}
570+
571+
static int
572+
_random_clear(PyObject *module)
573+
{
574+
Py_CLEAR(_randomstate(module)->Random_Type);
575+
return 0;
576+
}
577+
578+
static void
579+
_random_free(void *module)
580+
{
581+
_random_clear((PyObject *)module);
582+
}
577583

578584
static struct PyModuleDef _randommodule = {
579585
PyModuleDef_HEAD_INIT,
580586
"_random",
581587
module_doc,
582-
-1,
583-
NULL,
588+
sizeof(_randomstate),
584589
NULL,
585590
NULL,
586-
NULL,
587-
NULL
591+
_random_traverse,
592+
_random_clear,
593+
_random_free,
588594
};
589595

590596
PyMODINIT_FUNC
591597
PyInit__random(void)
592598
{
593599
PyObject *m;
594600

595-
if (PyType_Ready(&Random_Type) < 0)
601+
PyObject *Random_Type = PyType_FromSpec(&Random_Type_spec);
602+
if (Random_Type == NULL) {
596603
return NULL;
604+
}
605+
597606
m = PyModule_Create(&_randommodule);
598-
if (m == NULL)
607+
if (m == NULL) {
608+
Py_DECREF(Random_Type);
599609
return NULL;
600-
Py_INCREF(&Random_Type);
601-
PyModule_AddObject(m, "Random", (PyObject *)&Random_Type);
610+
}
611+
_randomstate(m)->Random_Type = Random_Type;
612+
613+
Py_INCREF(Random_Type);
614+
PyModule_AddObject(m, "Random", Random_Type);
615+
616+
/* Look up and save int.__abs__, which is needed in random_seed(). */
617+
PyObject *longval = NULL, *longtype = NULL;
618+
longval = PyLong_FromLong(0);
619+
if (longval == NULL) goto fail;
620+
621+
longtype = PyObject_Type(longval);
622+
if (longtype == NULL) goto fail;
623+
624+
PyObject *abs = PyObject_GetAttrString(longtype, "__abs__");
625+
if (abs == NULL) goto fail;
626+
627+
Py_DECREF(longtype);
628+
Py_DECREF(longval);
629+
_randomstate(m)->Long___abs__ = abs;
630+
602631
return m;
632+
633+
fail:
634+
Py_XDECREF(longtype);
635+
Py_XDECREF(longval);
636+
Py_DECREF(m);
637+
return NULL;
603638
}

0 commit comments

Comments
 (0)