use module-bound exception
PiperOrigin-RevId: 812739918
diff --git a/python/_brotli.c b/python/_brotli.c
index 1f9150e..0307347 100644
--- a/python/_brotli.c
+++ b/python/_brotli.c
@@ -1,6 +1,6 @@
#include <assert.h>
-#include <stdio.h>
#include <stddef.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PY_SSIZE_T_CLEAN 1
@@ -18,7 +18,12 @@
#define PY_GET_TYPE(Obj) ((Obj)->ob_type)
#endif
+static const char kErrorAttr[] = "error";
+#if PY_MAJOR_VERSION >= 3
+static const char kModuleAttr[] = "_module";
+#else
static PyObject* BrotliError;
+#endif
static const char kInvalidBufferError[] =
"brotli: data must be a C-contiguous buffer";
@@ -195,8 +200,36 @@
"Implementation module for the Brotli library.");
/* clang-format on */
-static void set_brotli_exception(const char* msg) {
+static void set_brotli_exception(PyObject* t, const char* msg) {
+#if PY_MAJOR_VERSION >= 3
+ PyObject* error = NULL;
+ PyObject* module = NULL;
+ assert(t != NULL);
+ assert(PyType_Check(t));
+ module = PyObject_GetAttrString(t, kModuleAttr);
+ if (!module) return; /* AttributeError raised. */
+ error = PyObject_GetAttrString(module, kErrorAttr);
+ Py_DECREF(module);
+ if (error == NULL) return; /* AttributeError raised. */
+ PyErr_SetString(error, msg);
+ Py_DECREF(error);
+#else
PyErr_SetString(BrotliError, msg);
+#endif
+}
+
+static void set_brotli_exception_from_module(PyObject* m, const char* msg) {
+#if PY_MAJOR_VERSION >= 3
+ PyObject* error = NULL;
+ assert(m != NULL);
+ assert(PyModule_Check(m));
+ error = PyObject_GetAttrString(m, kErrorAttr);
+ if (error == NULL) return; /* AttributeError raised. */
+ PyErr_SetString(error, msg);
+ Py_DECREF(error);
+#else
+ PyErr_SetString(BrotliError, msg);
+#endif
}
/*
@@ -316,7 +349,7 @@
result = PyBytes_FromStringAndSize(NULL, len);
if (result == NULL) {
- PyErr_Clear(); /* OOM exception will be raised by callers. */
+ PyErr_Clear(); /* OOM exception will be raised by callers. */
return NULL;
}
if (len == 0) return result;
@@ -347,6 +380,7 @@
PyObject* keywds) {
/* `tp_itemsize` is 0, so `nitems` could be 0. */
PyBrotli_Compressor* self = (PyBrotli_Compressor*)type->tp_alloc(type, 0);
+ PyObject* self_type = (PyObject*)type;
if (self == NULL) return NULL;
@@ -354,7 +388,7 @@
self->processing = 0;
self->enc = BrotliEncoderCreateInstance(0, 0, 0);
if (self->enc == NULL) {
- set_brotli_exception(kCompressCreateError);
+ set_brotli_exception(self_type, kCompressCreateError);
PY_GET_TYPE(self)->tp_free((PyObject*)self);
return NULL;
}
@@ -367,6 +401,7 @@
PyObject* keywds) {
static const char* kwlist[] = {"mode", "quality", "lgwin", "lgblock", NULL};
+ PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
unsigned char mode = BROTLI_DEFAULT_MODE;
unsigned char quality = BROTLI_DEFAULT_QUALITY;
unsigned char lgwin = BROTLI_DEFAULT_WINDOW;
@@ -386,7 +421,7 @@
if ((mode == 0) || (mode == 1) || (mode == 2)) {
BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_MODE, (uint32_t)mode);
} else {
- set_brotli_exception(kInvalidModeError);
+ set_brotli_exception(self_type, kInvalidModeError);
self->healthy = 0;
return -1;
}
@@ -394,14 +429,14 @@
BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_QUALITY,
(uint32_t)quality);
} else {
- set_brotli_exception(kInvalidQualityError);
+ set_brotli_exception(self_type, kInvalidQualityError);
self->healthy = 0;
return -1;
}
if ((10 <= lgwin) && (lgwin <= 24)) {
BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGWIN, (uint32_t)lgwin);
} else {
- set_brotli_exception(kInvalidLgwinError);
+ set_brotli_exception(self_type, kInvalidLgwinError);
self->healthy = 0;
return -1;
}
@@ -409,7 +444,7 @@
BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGBLOCK,
(uint32_t)lgblock);
} else {
- set_brotli_exception(kInvalidLgblockError);
+ set_brotli_exception(self_type, kInvalidLgblockError);
self->healthy = 0;
return -1;
}
@@ -431,6 +466,7 @@
static PyObject* compress_stream(PyBrotli_Compressor* self,
BrotliEncoderOperation op, uint8_t* input,
size_t input_length) {
+ PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
size_t available_in = input_length;
const uint8_t* next_in = input;
Buffer buffer;
@@ -470,8 +506,8 @@
if (ok) {
ret = Buffer_Finish(&buffer);
if (ret == NULL) oom = 1;
- } else { /* Not ok */
- set_brotli_exception(kCompressError);
+ } else { /* Not ok */
+ set_brotli_exception(self_type, kCompressError);
}
error:
@@ -490,16 +526,17 @@
static PyObject* brotli_Compressor_process(PyBrotli_Compressor* self,
PyObject* args) {
+ PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
PyObject* ret = NULL;
PyObject* input_object = NULL;
Py_buffer input;
if (self->healthy == 0) {
- set_brotli_exception(kCompressUnhealthyError);
+ set_brotli_exception(self_type, kCompressUnhealthyError);
return NULL;
}
if (self->processing != 0) {
- set_brotli_exception(kCompressConcurrentError);
+ set_brotli_exception(self_type, kCompressConcurrentError);
return NULL;
}
@@ -519,14 +556,15 @@
}
static PyObject* brotli_Compressor_flush(PyBrotli_Compressor* self) {
+ PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
PyObject* ret = NULL;
if (self->healthy == 0) {
- set_brotli_exception(kCompressUnhealthyError);
+ set_brotli_exception(self_type, kCompressUnhealthyError);
return NULL;
}
if (self->processing != 0) {
- set_brotli_exception(kCompressConcurrentError);
+ set_brotli_exception(self_type, kCompressConcurrentError);
return NULL;
}
@@ -537,14 +575,15 @@
}
static PyObject* brotli_Compressor_finish(PyBrotli_Compressor* self) {
+ PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
PyObject* ret = NULL;
if (self->healthy == 0) {
- set_brotli_exception(kCompressUnhealthyError);
+ set_brotli_exception(self_type, kCompressUnhealthyError);
return NULL;
}
if (self->processing != 0) {
- set_brotli_exception(kCompressConcurrentError);
+ set_brotli_exception(self_type, kCompressConcurrentError);
return NULL;
}
@@ -571,6 +610,7 @@
PyObject* keywds) {
/* `tp_itemsize` is 0, so `nitems` could be 0. */
PyBrotli_Decompressor* self = (PyBrotli_Decompressor*)type->tp_alloc(type, 0);
+ PyObject* self_type = (PyObject*)type;
if (self == NULL) return NULL;
self->healthy = 0;
@@ -578,7 +618,7 @@
self->dec = BrotliDecoderCreateInstance(0, 0, 0);
if (self->dec == NULL) {
- set_brotli_exception(kDecompressCreateError);
+ set_brotli_exception(self_type, kDecompressCreateError);
PY_GET_TYPE(self)->tp_free((PyObject*)self);
return NULL;
}
@@ -620,6 +660,7 @@
PyObject* args, PyObject* keywds) {
static const char* kwlist[] = {"", "output_buffer_limit", NULL};
+ PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
PyObject* ret = NULL;
PyObject* input_object = NULL;
Py_buffer input;
@@ -633,11 +674,11 @@
int oom = 0;
if (self->healthy == 0) {
- set_brotli_exception(kDecompressUnhealthyError);
+ set_brotli_exception(self_type, kDecompressUnhealthyError);
return NULL;
}
if (self->processing != 0) {
- set_brotli_exception(kDecompressConcurrentError);
+ set_brotli_exception(self_type, kDecompressConcurrentError);
return NULL;
}
@@ -654,7 +695,7 @@
if (self->unconsumed_data_length > 0) {
if (input.len > 0) {
- set_brotli_exception(kDecompressSinkError);
+ set_brotli_exception(self_type, kDecompressSinkError);
goto finally;
}
next_in = self->unconsumed_data;
@@ -693,7 +734,7 @@
if (oom) {
goto finally;
} else if (result == BROTLI_DECODER_RESULT_ERROR) {
- set_brotli_exception(kDecompressError);
+ set_brotli_exception(self_type, kDecompressError);
goto finally;
}
@@ -712,7 +753,7 @@
if ((result == BROTLI_DECODER_RESULT_SUCCESS) && (avail_in > 0)) {
/* TODO(eustas): Add API to ignore / fetch unused "tail"? */
- set_brotli_exception(kDecompressError);
+ set_brotli_exception(self_type, kDecompressError);
goto finally;
}
@@ -741,12 +782,13 @@
}
static PyObject* brotli_Decompressor_is_finished(PyBrotli_Decompressor* self) {
+ PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
if (self->healthy == 0) {
- set_brotli_exception(kDecompressUnhealthyError);
+ set_brotli_exception(self_type, kDecompressUnhealthyError);
return NULL;
}
if (self->processing != 0) {
- set_brotli_exception(kDecompressConcurrentError);
+ set_brotli_exception(self_type, kDecompressConcurrentError);
return NULL;
}
if (BrotliDecoderIsFinished(self->dec)) {
@@ -758,12 +800,13 @@
static PyObject* brotli_Decompressor_can_accept_more_data(
PyBrotli_Decompressor* self) {
+ PyObject* self_type = (PyObject*)PY_GET_TYPE((PyObject*)self);
if (self->healthy == 0) {
- set_brotli_exception(kDecompressUnhealthyError);
+ set_brotli_exception(self_type, kDecompressUnhealthyError);
return NULL;
}
if (self->processing != 0) {
- set_brotli_exception(kDecompressConcurrentError);
+ set_brotli_exception(self_type, kDecompressConcurrentError);
return NULL;
}
if (self->unconsumed_data_length > 0) {
@@ -775,7 +818,7 @@
/* --- Module functions --- */
-static PyObject* brotli_decompress(PyObject* self, PyObject* args,
+static PyObject* brotli_decompress(PyObject* m, PyObject* args,
PyObject* keywds) {
static const char* kwlist[] = {"string", NULL};
@@ -832,7 +875,7 @@
if (oom) {
goto finally;
} else if (result != BROTLI_DECODER_RESULT_SUCCESS || available_in > 0) {
- set_brotli_exception(kDecompressError);
+ set_brotli_exception_from_module(m, kDecompressError);
goto finally;
}
@@ -1039,27 +1082,35 @@
brotli_error_doc, NULL, NULL);
if (error_type == NULL) goto error;
- if (RegisterObject(m, "error", error_type) < 0) goto error;
+ if (RegisterObject(m, kErrorAttr, error_type) < 0) goto error;
+#if PY_MAJOR_VERSION < 3
/* Assumption: pointer is used only while module is alive and well. */
BrotliError = error_type;
+#endif
error_type = NULL;
#if PY_MAJOR_VERSION >= 3
compressor_type = PyType_FromSpec(&brotli_Compressor_spec);
decompressor_type = PyType_FromSpec(&brotli_Decompressor_spec);
#else
- compressor_type = &brotli_CompressorType;
+ compressor_type = (PyObject*)&brotli_CompressorType;
Py_INCREF(compressor_type);
- decompressor_type = &brotli_DecompressorType;
+ decompressor_type = (PyObject*)&brotli_DecompressorType;
Py_INCREF(decompressor_type);
#endif
if (compressor_type == NULL) goto error;
if (PyType_Ready((PyTypeObject*)compressor_type) < 0) goto error;
+#if PY_MAJOR_VERSION >= 3
+ if (PyObject_SetAttrString(compressor_type, kModuleAttr, m) < 0) goto error;
+#endif
if (RegisterObject(m, "Compressor", compressor_type) < 0) goto error;
compressor_type = NULL;
if (decompressor_type == NULL) goto error;
if (PyType_Ready((PyTypeObject*)decompressor_type) < 0) goto error;
+#if PY_MAJOR_VERSION >= 3
+ if (PyObject_SetAttrString(decompressor_type, kModuleAttr, m) < 0) goto error;
+#endif
if (RegisterObject(m, "Decompressor", decompressor_type) < 0) goto error;
decompressor_type = NULL;