镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions packages/google-crc32c/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

[build-system]
requires = ["setuptools", "wheel"]
requires = ["setuptools >= 77.0.3"]
build-backend = "setuptools.build_meta"

[project]
Expand All @@ -22,5 +22,26 @@ version = "1.9.0"
description = "A python wrapper of the C library 'Google CRC32C'"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "Apache-2.0"}
dynamic = ["classifiers"]
authors = [{name = "Google LLC", email = "googleapis-packages@google.com"}]
urls = {Homepage = "https://github.com/googleapis/google-cloud-python/tree/main/packages/google-crc32c"}
optional-dependencies = {testing = ["pytest"]}
license = "Apache-2.0"
license-files = ["LICENSE"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
]

[tool.setuptools]
include-package-data = false
package-data = {google_crc32c = ["py.typed"]}
platforms = ["Posix", "MacOS X", "Windows"]
zip-safe = true
48 changes: 0 additions & 48 deletions packages/google-crc32c/setup.cfg

This file was deleted.

85 changes: 60 additions & 25 deletions packages/google-crc32c/src/google_crc32c/_crc32c.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,30 @@
/* The minimum buffer size in bytes (1MB) required to justify the overhead of releasing the GIL. */
static const Py_ssize_t gil_threshold = 1024 * 1024;

static int
_should_release_gil(Py_ssize_t length, PyObject *chunk_obj)
{
/* Checks if the chunk is immutable (bytes) to prevent concurrent modification,
* and large enough to benefit from releasing the GIL. */
return (length >= gil_threshold && PyBytes_Check(chunk_obj));
}

static PyObject *
_crc32c_extend(PyObject *self, PyObject *args)
{
unsigned long crc_input;
uint32_t crc;
const char *chunk;
Py_ssize_t length;
PyThreadState *save = NULL;
Py_buffer buffer;

if (!PyArg_ParseTuple(args, "ky#", &crc_input, &chunk, &length))
if (!PyArg_ParseTuple(args, "ky*", &crc_input, &buffer))
return NULL;

if (_should_release_gil(length, PyTuple_GET_ITEM(args, 1))) {
if (buffer.len >= gil_threshold) {
save = PyEval_SaveThread();
}

crc = crc32c_extend((uint32_t)crc_input, (const uint8_t*)chunk, length);
crc = crc32c_extend((uint32_t)crc_input, (const uint8_t*)buffer.buf, buffer.len);

if (save) {
PyEval_RestoreThread(save);
}

PyBuffer_Release(&buffer);

return PyLong_FromUnsignedLong(crc);
}

Expand All @@ -43,23 +37,24 @@ static PyObject *
_crc32c_value(PyObject *self, PyObject *args)
{
uint32_t crc;
const char *chunk;
Py_ssize_t length;
Py_buffer buffer;
PyThreadState *save = NULL;

if (!PyArg_ParseTuple(args, "y#", &chunk, &length))
if (!PyArg_ParseTuple(args, "y*", &buffer))
return NULL;

if (_should_release_gil(length, PyTuple_GET_ITEM(args, 0))) {
if (buffer.len >= gil_threshold) {
save = PyEval_SaveThread();
}

crc = crc32c_value((const uint8_t*)chunk, length);
crc = crc32c_value((const uint8_t*)buffer.buf, buffer.len);

if (save) {
PyEval_RestoreThread(save);
}

PyBuffer_Release(&buffer);

return PyLong_FromUnsignedLong(crc);
}

Expand All @@ -72,19 +67,59 @@ static PyMethodDef Crc32cMethods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};

#if PY_VERSION_HEX >= 0x030f0000
PyABIInfo_VAR(abi_info);
static PySlot Crc32cSlots[] = {
PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
PySlot_STATIC_DATA(Py_mod_name, "_crc32c"),
PySlot_STATIC_DATA(Py_mod_methods, Crc32cMethods),
PySlot_SIZE(Py_mod_state_size, 0),
PySlot_UINT64(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
PySlot_UINT64(Py_mod_gil, Py_MOD_GIL_NOT_USED),
PySlot_END
};

static struct PyModuleDef crc32cmodule = {
PyModuleDef_HEAD_INIT,
"_crc32c", /* name of module */
NULL, /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module,
or -1 if the module keeps state in global variables. */
Crc32cMethods
PyMODEXPORT_FUNC
PyModExport__crc32c()
{
return Crc32cSlots;
}

PyMODINIT_FUNC
PyInit__crc32c(void)
{
/* workaround for setuptools on Python 3.15+ */
/* https://github.com/pypa/distutils/issues/387 */
return NULL;
}

#else

static PyModuleDef_Slot Crc32cSlots[] = {
#ifdef Py_mod_multiple_interpreters
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
#endif
#ifdef Py_mod_gil
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
#endif
{0, NULL}
};

static struct PyModuleDef crc32cmodule = {
PyModuleDef_HEAD_INIT, /* m_base */
"_crc32c", /* m_name: name of module */
NULL, /* m_doc: module documentation, may be NULL */
0, /* m_size: size of per-interpreter state of the module */
Crc32cMethods, /* m_methods */
Crc32cSlots, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};

PyMODINIT_FUNC
PyInit__crc32c(void)
{
return PyModule_Create(&crc32cmodule);
return PyModuleDef_Init(&crc32cmodule);
}
#endif
Comment on lines +70 to +125

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Speculative support for Python 3.15+ using unreleased/non-standard C APIs (PySlot, PyMODEXPORT_FUNC, etc.) should be avoided. Python 3.13 and 3.14 are fully supported by the standard multi-phase initialization slots (Py_mod_gil and Py_mod_multiple_interpreters). We should simplify the module initialization by removing the speculative 0x030f0000 block and using the standard multi-phase initialization for all supported Python versions.

static PyModuleDef_Slot Crc32cSlots[] = {\n#ifdef Py_mod_multiple_interpreters\n    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},\n#endif\n#ifdef Py_mod_gil\n    {Py_mod_gil, Py_MOD_GIL_NOT_USED},\n#endif\n    {0, NULL}\n};\n\nstatic struct PyModuleDef crc32cmodule = {\n    PyModuleDef_HEAD_INIT, /* m_base */\n    \"_crc32c\",             /* m_name: name of module */\n    NULL,                  /* m_doc: module documentation, may be NULL */\n    0,                     /* m_size: size of per-interpreter state of the module */\n    Crc32cMethods,         /* m_methods */\n    Crc32cSlots,           /* m_slots */\n    NULL,                  /* m_traverse */\n    NULL,                  /* m_clear */\n    NULL,                  /* m_free */\n};\n\nPyMODINIT_FUNC\nPyInit__crc32c(void)\n{\n    return PyModuleDef_Init(&crc32cmodule);\n}

@mayeut mayeut Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's nothing speculative at this point. we're at 3.15.0rc3, final release coming this week and API/ABI has been standardized and frozen at at 3.15.0rc1.
That being said, using the PyModExport entry point (PEP 793) could be held back while waiting for proper setuptools support of abi3t.

Per https://peps.python.org/pep-0803/:

Virtually all extensions will need to switch the new export hook added in PEP 793 (PyModExport_modulename()) to support abi3t.

Loading