From dd573b095cd136c764cef7bcb240ab805384e5ad Mon Sep 17 00:00:00 2001 From: Eddy Xu Date: Fri, 30 Jan 2026 01:18:14 -0500 Subject: [PATCH] ADD WARN for len(str(random.random)) --- Include/cpython/floatobject.h | 2 ++ Include/cpython/unicodeobject.h | 8 +++++- Lib/test/test_py2xwarn.py | 13 +++++---- Lib/test/test_sys.py | 6 +++- Modules/_randommodule.c | 6 +++- Objects/bytesobject.c | 51 --------------------------------- Objects/floatobject.c | 17 +++++++++-- Objects/unicodeobject.c | 18 +++++++++++- 8 files changed, 59 insertions(+), 62 deletions(-) diff --git a/Include/cpython/floatobject.h b/Include/cpython/floatobject.h index 7795d9f83f0..b6b02d64f8e 100644 --- a/Include/cpython/floatobject.h +++ b/Include/cpython/floatobject.h @@ -5,12 +5,14 @@ typedef struct { PyObject_HEAD double ob_fval; + _Bool ob_is_from_random; } PyFloatObject; // Macro version of PyFloat_AsDouble() trading safety for speed. // It doesn't check if op is a double object. #define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval) +PyAPI_FUNC(PyObject *) _PyFloat_FromDoubleWithFlags(double x, unsigned char flags); PyAPI_FUNC(int) PyFloat_Pack2(double x, char *p, int le); PyAPI_FUNC(int) PyFloat_Pack4(double x, char *p, int le); diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h index 758aaff2d77..403d04c1e54 100644 --- a/Include/cpython/unicodeobject.h +++ b/Include/cpython/unicodeobject.h @@ -135,7 +135,8 @@ typedef struct { unsigned int ascii:1; /* Padding to ensure that PyUnicode_DATA() is always aligned to 4 bytes (see issue #19537 on m68k). */ - unsigned int :25; + unsigned int ob_is_from_random:1; + unsigned int :24; } state; } PyASCIIObject; @@ -500,6 +501,11 @@ PyAPI_FUNC(PyObject*) _PyUnicode_FromASCII( const char *buffer, Py_ssize_t size); +PyAPI_FUNC(PyObject*) _PyUnicode_FromASCII_withRandomFlag( + const char *buffer, + Py_ssize_t size, + unsigned char flag); + /* Compute the maximum character of the substring unicode[start:end]. Return 127 for an empty string. */ PyAPI_FUNC(Py_UCS4) _PyUnicode_FindMaxChar ( diff --git a/Lib/test/test_py2xwarn.py b/Lib/test/test_py2xwarn.py index 37e9c6a8a2a..1e482fcf700 100644 --- a/Lib/test/test_py2xwarn.py +++ b/Lib/test/test_py2xwarn.py @@ -38,6 +38,14 @@ def test_next(self): w.reset() self.assertNoWarning(iterator_marks.__next__(), w) + def test_random(self): + expected = "String repr of random.random() is longer in 3.x, change code accordingly" + import random + with check_py2x_warnings(("", Py2xWarning)) as w: + self.assertWarning(len(str(random.random())), w, expected) + w.reset() + self.assertNoWarning(len(str(0.123456)), w) + def test_truncate0(self): expected = "Calling truncate(0) on text stream without seek(0)" + \ " may produce inconsistent results. Use seek(0) before truncate(0)" @@ -46,11 +54,6 @@ def test_truncate0(self): f.write("test") f.truncate(0) self.assertWarning((), w, expected) - - def test_byteformat(self): - expected = "bytes.format() is not supported in Python 3, use str.format() and encode() instead." - with check_py2x_warnings(("", Py2xWarning)) as w: - self.assertWarning(b"te{}".format("st"), w, expected) if __name__ == '__main__': diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 630de3c7570..d5d0effcf30 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1390,7 +1390,11 @@ class C(object): pass # reverse check(reversed(''), size('nP')) # float - check(float(0), size('d')) + if sys.py2x_warning: + # plus 8 to reflect struct change for unsigned char ob_is_from_random + check(float(0), size('d')+8) + else: + check(float(0), size('d')+8) # sys.floatinfo check(sys.float_info, vsize('') + self.P * len(sys.float_info)) # frame diff --git a/Modules/_randommodule.c b/Modules/_randommodule.c index d96c0371ec7..96f13df0a09 100644 --- a/Modules/_randommodule.c +++ b/Modules/_randommodule.c @@ -177,7 +177,11 @@ _random_Random_random_impl(RandomObject *self) /*[clinic end generated code: output=117ff99ee53d755c input=afb2a59cbbb00349]*/ { uint32_t a=genrand_uint32(self)>>5, b=genrand_uint32(self)>>6; - return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0)); + double x = (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); + if (Py_Py2xWarningFlag) + return _PyFloat_FromDoubleWithFlags(x, 1); + else + return PyFloat_FromDouble(x); } /* initializes mt[N] with a seed */ diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 0b71dd8a3dc..1dfa1d57cf6 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -115,55 +115,6 @@ _Py_COMP_DIAG_POP return (PyObject *) op; } -static PyObject * -bytes_format(PyObject *self, PyObject *args, PyObject *kwargs) -{ - if (Py_Py2xWarningFlag){ - if (PyErr_WarnEx( - PyExc_Py2xWarning, - "bytes.format() is not supported in Python 3, " - "use str.format() and encode() instead.", - 1) < 0) { - return NULL; - } - - if (!PyBytes_Check(self)) { - PyErr_SetString(PyExc_TypeError, "expected bytes as input"); - return NULL; - } - - Py_ssize_t size = PyBytes_GET_SIZE(self); - const char *buf = PyBytes_AS_STRING(self); - - PyObject *fmt_str = PyUnicode_DecodeASCII(buf, size, "surrogateescape"); - PyObject *format_method = PyObject_GetAttrString(fmt_str, "format"); - - PyObject *result_str = PyObject_Call(format_method, args, kwargs); - Py_DECREF(format_method); - Py_DECREF(fmt_str); - - PyObject *result_bytes = PyUnicode_AsEncodedString( - result_str, - "ascii", - "surrogateescape" - ); - - if (!PyBytes_Check(result_bytes)) { - PyErr_SetString(PyExc_TypeError, "expected bytes as output"); - Py_DECREF(result_str); - return NULL; - } - - Py_DECREF(result_str); - return result_bytes; - } - else{ - return PyErr_Format(PyExc_AttributeError, - "'%s' object has no attribute 'format'", - Py_TYPE(self)->tp_name); - } -} - PyObject * PyBytes_FromStringAndSize(const char *str, Py_ssize_t size) { @@ -2602,8 +2553,6 @@ bytes_methods[] = { BYTES_TRANSLATE_METHODDEF {"upper", stringlib_upper, METH_NOARGS, _Py_upper__doc__}, STRINGLIB_ZFILL_METHODDEF - {"format", (PyCFunction)bytes_format, METH_VARARGS | METH_KEYWORDS, - PyDoc_STR("format(*args, **kwargs) -> bytes")}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 86861b7e28d..bcb1105faff 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -153,9 +153,20 @@ PyFloat_FromDouble(double fval) } _PyObject_Init((PyObject*)op, &PyFloat_Type); op->ob_fval = fval; + op->ob_is_from_random = 0; return (PyObject *) op; } +PyObject * +_PyFloat_FromDoubleWithFlags(double x, unsigned char flag) +{ + PyObject *o = PyFloat_FromDouble(x); + if (o == NULL) + return NULL; + ((PyFloatObject *)o)->ob_is_from_random = flag; + return o; +} + static PyObject * float_from_string_inner(const char *s, Py_ssize_t len, void *obj) { @@ -376,14 +387,16 @@ float_repr(PyFloatObject *v) { PyObject *result; char *buf; - buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v), 'r', 0, Py_DTSF_ADD_DOT_0, NULL); if (!buf) return PyErr_NoMemory(); - result = _PyUnicode_FromASCII(buf, strlen(buf)); + if (Py_Py2xWarningFlag) + result = _PyUnicode_FromASCII_withRandomFlag(buf, strlen(buf), v->ob_is_from_random); + else + result = _PyUnicode_FromASCII(buf, strlen(buf)); PyMem_Free(buf); return result; } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index e9358290724..8c46976f33a 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -1189,6 +1189,7 @@ PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar) _PyUnicode_STATE(unicode).kind = kind; _PyUnicode_STATE(unicode).compact = 1; _PyUnicode_STATE(unicode).ascii = is_ascii; + _PyUnicode_STATE(unicode).ob_is_from_random = 0; if (is_ascii) { ((char*)data)[size] = 0; } @@ -1935,6 +1936,16 @@ _PyUnicode_FromASCII(const char *buffer, Py_ssize_t size) return unicode; } +PyObject* +_PyUnicode_FromASCII_withRandomFlag(const char *buffer, Py_ssize_t size, unsigned char flag) +{ + PyObject *o = _PyUnicode_FromASCII(buffer, size); + if (o == NULL) + return NULL; + ((PyASCIIObject *)o)->state.ob_is_from_random = flag; + return o; +} + static Py_UCS4 kind_maxchar_limit(int kind) { @@ -11605,7 +11616,12 @@ unicode_join(PyObject *self, PyObject *iterable) static Py_ssize_t unicode_length(PyObject *self) -{ +{ + if(Py_Py2xWarningFlag && PyUnicode_Check(self) && ((PyASCIIObject *)self)->state.ob_is_from_random == 1) + PyErr_WarnEx( + PyExc_Py2xWarning, + "String repr of random.random() is longer in 3.x, change code accordingly", + 1); return PyUnicode_GET_LENGTH(self); }