[Python-checkins] bpo-38142: Updated _hashopenssl.c to be PEP 384 compliant (#16071)

Christian Heimes webhook-mailer at python.org
Wed Sep 25 17:03:38 EDT 2019


https://github.com/python/cpython/commit/df69e75edcc08475bc9a57a5a76df8a45bfc3c34
commit: df69e75edcc08475bc9a57a5a76df8a45bfc3c34
branch: master
author: Christian Heimes <christian at python.org>
committer: GitHub <noreply at github.com>
date: 2019-09-25T23:03:30+02:00
summary:

bpo-38142: Updated _hashopenssl.c to be PEP 384 compliant (#16071)

* Updated _hashopenssl.c to be PEP 384 compliant
* Remove refleak test from test_hashlib. The updated type no longer accepts random arguments to __init__.

files:
A Misc/NEWS.d/next/Library/2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst
M Lib/test/test_hashlib.py
M Modules/_hashopenssl.c

diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 9204b44bf4f9..d55de02f9164 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -167,16 +167,6 @@ def hash_constructors(self):
         constructors = self.constructors_to_test.values()
         return itertools.chain.from_iterable(constructors)
 
-    @support.refcount_test
-    @unittest.skipIf(c_hashlib is None, 'Require _hashlib module')
-    def test_refleaks_in_hash___init__(self):
-        gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
-        sha1_hash = c_hashlib.new('sha1')
-        refs_before = gettotalrefcount()
-        for i in range(100):
-            sha1_hash.__init__('sha1')
-        self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10)
-
     def test_hash_array(self):
         a = array.array("b", range(10))
         for cons in self.hash_constructors:
diff --git a/Misc/NEWS.d/next/Library/2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst b/Misc/NEWS.d/next/Library/2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst
new file mode 100644
index 000000000000..740b6f1cf096
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-09-12-18-41-35.bpo-38142.1I0Ch0.rst
@@ -0,0 +1 @@
+The _hashlib OpenSSL wrapper extension module is now PEP-384 compliant.
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index d963c7a57221..48511f72bc63 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -46,6 +46,16 @@
 #define PY_OPENSSL_HAS_BLAKE2 1
 #endif
 
+static PyModuleDef _hashlibmodule;
+
+typedef struct {
+    PyTypeObject *EVPtype;
+} _hashlibstate;
+
+#define _hashlibstate(o) ((_hashlibstate *)PyModule_GetState(o))
+#define _hashlibstate_global ((_hashlibstate *)PyModule_GetState(PyState_FindModule(&_hashlibmodule)))
+
+
 typedef struct {
     PyObject_HEAD
     EVP_MD_CTX          *ctx;   /* OpenSSL message digest context */
@@ -53,14 +63,12 @@ typedef struct {
 } EVPobject;
 
 
-static PyTypeObject EVPtype;
-
 #include "clinic/_hashopenssl.c.h"
 /*[clinic input]
 module _hashlib
-class _hashlib.HASH "EVPobject *" "&EVPtype"
+class _hashlib.HASH "EVPobject *" "((_hashlibstate *)PyModule_GetState(module))->EVPtype"
 [clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a881a5092eecad28]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1adf85e8eb2ab979]*/
 
 
 /* LCOV_EXCL_START */
@@ -231,7 +239,9 @@ py_digest_by_name(const char *name)
 static EVPobject *
 newEVPobject(void)
 {
-    EVPobject *retval = (EVPobject *)PyObject_New(EVPobject, &EVPtype);
+    EVPobject *retval = (EVPobject *)PyObject_New(
+        EVPobject, _hashlibstate_global->EVPtype
+    );
     if (retval == NULL) {
         return NULL;
     }
@@ -273,10 +283,12 @@ EVP_hash(EVPobject *self, const void *vp, Py_ssize_t len)
 static void
 EVP_dealloc(EVPobject *self)
 {
+    PyTypeObject *tp = Py_TYPE(self);
     if (self->lock != NULL)
         PyThread_free_lock(self->lock);
     EVP_MD_CTX_free(self->ctx);
     PyObject_Del(self);
+    Py_DECREF(tp);
 }
 
 static int
@@ -501,46 +513,23 @@ PyDoc_STRVAR(hashtype_doc,
 "name -- the hash algorithm being used by this object\n"
 "digest_size -- number of bytes in this hashes output");
 
-static PyTypeObject EVPtype = {
-    PyVarObject_HEAD_INIT(NULL, 0)
+static PyType_Slot EVPtype_slots[] = {
+    {Py_tp_dealloc, EVP_dealloc},
+    {Py_tp_repr, EVP_repr},
+    {Py_tp_doc, (char *)hashtype_doc},
+    {Py_tp_methods, EVP_methods},
+    {Py_tp_getset, EVP_getseters},
+    {0, 0},
+};
+
+static PyType_Spec EVPtype_spec = {
     "_hashlib.HASH",    /*tp_name*/
     sizeof(EVPobject),  /*tp_basicsize*/
     0,                  /*tp_itemsize*/
-    /* methods */
-    (destructor)EVP_dealloc, /*tp_dealloc*/
-    0,                  /*tp_vectorcall_offset*/
-    0,                  /*tp_getattr*/
-    0,                  /*tp_setattr*/
-    0,                  /*tp_as_async*/
-    (reprfunc)EVP_repr, /*tp_repr*/
-    0,                  /*tp_as_number*/
-    0,                  /*tp_as_sequence*/
-    0,                  /*tp_as_mapping*/
-    0,                  /*tp_hash*/
-    0,                  /*tp_call*/
-    0,                  /*tp_str*/
-    0,                  /*tp_getattro*/
-    0,                  /*tp_setattro*/
-    0,                  /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
-    hashtype_doc,       /*tp_doc*/
-    0,                  /*tp_traverse*/
-    0,                  /*tp_clear*/
-    0,                  /*tp_richcompare*/
-    0,                  /*tp_weaklistoffset*/
-    0,                  /*tp_iter*/
-    0,                  /*tp_iternext*/
-    EVP_methods,        /* tp_methods */
-    NULL,        /* tp_members */
-    EVP_getseters,      /* tp_getset */
-    0,                  /* tp_base */
-    0,                  /* tp_dict */
-    0,                  /* tp_descr_get */
-    0,                  /* tp_descr_set */
-    0,                  /* tp_dictoffset */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+    EVPtype_slots
 };
 
-\
 static PyObject *
 EVPnew(const EVP_MD *digest,
        const unsigned char *cp, Py_ssize_t len, int usedforsecurity)
@@ -1120,17 +1109,39 @@ static struct PyMethodDef EVP_functions[] = {
 
 /* Initialize this module. */
 
+static int
+hashlib_traverse(PyObject *m, visitproc visit, void *arg)
+{
+    _hashlibstate *state = _hashlibstate(m);
+    Py_VISIT(state->EVPtype);
+    return 0;
+}
+
+static int
+hashlib_clear(PyObject *m)
+{
+    _hashlibstate *state = _hashlibstate(m);
+    Py_CLEAR(state->EVPtype);
+    return 0;
+}
+
+static void
+hashlib_free(void *m)
+{
+    hashlib_clear((PyObject *)m);
+}
+
 
 static struct PyModuleDef _hashlibmodule = {
     PyModuleDef_HEAD_INIT,
     "_hashlib",
     NULL,
-    -1,
+    sizeof(_hashlibstate),
     EVP_functions,
     NULL,
-    NULL,
-    NULL,
-    NULL
+    hashlib_traverse,
+    hashlib_clear,
+    hashlib_free
 };
 
 PyMODINIT_FUNC
@@ -1144,19 +1155,21 @@ PyInit__hashlib(void)
     ERR_load_crypto_strings();
 #endif
 
-    /* TODO build EVP_functions openssl_* entries dynamically based
-     * on what hashes are supported rather than listing many
-     * but having some be unsupported.  Only init appropriate
-     * constants. */
-
-    Py_TYPE(&EVPtype) = &PyType_Type;
-    if (PyType_Ready(&EVPtype) < 0)
-        return NULL;
+    m = PyState_FindModule(&_hashlibmodule);
+    if (m != NULL) {
+        Py_INCREF(m);
+        return m;
+    }
 
     m = PyModule_Create(&_hashlibmodule);
     if (m == NULL)
         return NULL;
 
+    PyTypeObject *EVPtype = (PyTypeObject *)PyType_FromSpec(&EVPtype_spec);
+    if (EVPtype == NULL)
+        return NULL;
+    _hashlibstate(m)->EVPtype = EVPtype;
+
     openssl_md_meth_names = generate_hash_name_list();
     if (openssl_md_meth_names == NULL) {
         Py_DECREF(m);
@@ -1167,8 +1180,9 @@ PyInit__hashlib(void)
         return NULL;
     }
 
-    Py_INCREF((PyObject *)&EVPtype);
-    PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype);
+    Py_INCREF((PyObject *)_hashlibstate(m)->EVPtype);
+    PyModule_AddObject(m, "HASH", (PyObject *)_hashlibstate(m)->EVPtype);
 
+    PyState_AddModule(m, &_hashlibmodule);
     return m;
 }



More information about the Python-checkins mailing list